Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
dcd7f07
ZED's infection fix and nerf (#6907)
Super7522 Jul 29, 2026
b4fc7a6
Automated changelog update (#6907)
GoobAutomatedBot Jul 29, 2026
4b33b84
Merge this without looking ( apc bandaid ) (#6904)
Super7522 Jul 29, 2026
2afbaf5
Automated changelog update (#6904)
GoobAutomatedBot Jul 29, 2026
d8ab680
Delete .github/workflows/reuse-updater.yml
Aidenkrz Jul 30, 2026
43f193f
Minor northstar tweaks (BOUNTY) (#6829)
Discommunicator Jul 31, 2026
47c8a74
Patch. Who? Fix. (#6932)
shimorioEN Aug 1, 2026
2354dd8
new cc gear consistency (#6653)
BeanyBoi69 Aug 1, 2026
80244f3
Automated changelog update (#6653)
GoobAutomatedBot Aug 1, 2026
8fd4d59
[PORT] Fixes IPC's and people with pAI's not being able to raise shie…
gaiads Aug 1, 2026
71ab42d
Automated changelog update (#6669)
GoobAutomatedBot Aug 1, 2026
530b1e4
Made roundstart larvas invisible (#6671)
JohnOakman Aug 1, 2026
c20db1e
Automated changelog update (#6671)
GoobAutomatedBot Aug 1, 2026
3448155
ACTUALLY re-enable honkops (#6679)
BeanyBoi69 Aug 1, 2026
207ed8b
Automated changelog update (#6679)
GoobAutomatedBot Aug 1, 2026
a2ba0eb
[PORT + BOUNTY] moth shrink-wrapping port + multilayer markings and c…
winkkk-k Aug 1, 2026
d95656d
Automated changelog update (#6682)
GoobAutomatedBot Aug 1, 2026
5b5b8fe
Rebalance meta lavaland items (#6668)
Roudenn Aug 1, 2026
971b995
Automated changelog update (#6668)
GoobAutomatedBot Aug 1, 2026
f875647
small Slasher Spring kit Resprite (#6658)
RedTerrorDark Aug 1, 2026
2150119
Automated changelog update (#6658)
GoobAutomatedBot Aug 1, 2026
7b1db10
Zombie immunity to SOME creatures (#6655)
BeanyBoi69 Aug 1, 2026
229e725
Automated changelog update (#6655)
GoobAutomatedBot Aug 1, 2026
f41dae9
Hunger thirst rebalance (#6704)
N0lim Aug 1, 2026
f2630bf
Automated changelog update (#6704)
GoobAutomatedBot Aug 1, 2026
3929b9d
fix: cobra gland works after harp flight ends (#6717)
lucapgiannotti Aug 1, 2026
1569232
Automated changelog update (#6717)
GoobAutomatedBot Aug 1, 2026
acec019
Remove guerrilla gloves from NUKIE uplink (#6715)
BeanyBoi69 Aug 1, 2026
9352a9d
Automated changelog update (#6715)
GoobAutomatedBot Aug 1, 2026
7f4325b
Merge remote-tracking branch 'goob'
UmbiMax Aug 1, 2026
71e8e2c
Revert "Remove guerrilla gloves from NUKIE uplink (#6715)"
UmbiMax Aug 1, 2026
a991e28
Merge pull request
UmbiMax Aug 1, 2026
5eb697b
Revert "Zombie immunity to SOME creatures" (#6948)
Sarahon Aug 1, 2026
c29904f
Admin logs fixes (#6901)
Androidonator Aug 2, 2026
ad11ba2
nerf shitters (#6529)
BeanyBoi69 Aug 2, 2026
dfc5002
Automated changelog update (#6529)
GoobAutomatedBot Aug 2, 2026
419efa6
AI can destroy cyborg (#6924)
Kenkaitos Aug 2, 2026
afa69ef
Automated changelog update (#6924)
GoobAutomatedBot Aug 2, 2026
a9f6940
Trinkets grouping and normalisation (#6489)
Swino4ka Aug 2, 2026
699b59e
Automated changelog update (#6489)
GoobAutomatedBot Aug 2, 2026
946adf0
there can be only one tennis ball [fix?] (#6833)
Hardbassman Aug 2, 2026
5519c01
Automated changelog update (#6833)
GoobAutomatedBot Aug 2, 2026
466e64b
Jetpacks to salvage point vendor (#6750)
BeanyBoi69 Aug 2, 2026
da711d6
Automated changelog update (#6750)
GoobAutomatedBot Aug 2, 2026
a58402a
Colossus Jumpscare ( tweak ) (#6937)
Super7522 Aug 2, 2026
65b5e2d
Automated changelog update (#6937)
GoobAutomatedBot Aug 2, 2026
e67e196
checkbox for license larp (#6929)
NotActuallyMarty Aug 2, 2026
1534351
Merge remote-tracking branch 'goob' into Upstreaaam
UmbiMax Aug 3, 2026
1b671e1
Upstream fix 1
UmbiMax Aug 3, 2026
645bc44
Merge branch 'master' into Upstreaaam
UmbiMax Aug 3, 2026
aa39501
Fix Amber
UmbiMax Aug 3, 2026
d6cd863
Fix Box
UmbiMax Aug 3, 2026
8e45b40
Merge branch 'master' into Upstreaaam
UmbiMax Aug 3, 2026
2e8a592
Box FIX
UmbiMax Aug 3, 2026
ae540c4
Merge branch 'Upstreaaam' of https://github.com/OSAS-corp/Local-OldOr…
UmbiMax Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 0 additions & 116 deletions .github/workflows/reuse-updater.yml

This file was deleted.

149 changes: 98 additions & 51 deletions Content.Client/Humanoid/HumanoidAppearanceSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Numerics;
using Content.Client.DisplacementMap;
using Content.Shared.CCVar;
using Content.Shared.CCVar;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
Expand Down Expand Up @@ -177,7 +176,7 @@ public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile? profil

var customBaseLayers = new Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo>();

var speciesPrototype = _prototypeManager.Index<SpeciesPrototype>(profile.Species);
var speciesPrototype = _prototypeManager.Index(profile.Species); // Floof
var markings = new MarkingSet(speciesPrototype.MarkingPoints, _markingManager, _prototypeManager);

// Add markings that doesn't need coloring. We store them until we add all other markings that doesn't need it.
Expand Down Expand Up @@ -378,50 +377,109 @@ private void ApplyMarking(MarkingPrototype markingPrototype,
var humanoid = entity.Comp1;
var sprite = entity.Comp2;

if (!_sprite.LayerMapTryGet((entity.Owner, sprite), markingPrototype.BodyPart, out var targetLayer, false))
// FLOOF ADD START
// make a handy dict of filename -> colors
// cus we might need to access it by filename to link
// one sprite's colors to another
var colorDict = new Dictionary<string, Color>();
for (var i = 0; i < markingPrototype.Sprites.Count; i++)
{
return;
}

visible &= !IsHidden(humanoid, markingPrototype.BodyPart);
visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
&& setting.AllowsMarkings;
var spriteName = markingPrototype.Sprites[i] switch
{
SpriteSpecifier.Rsi rsi => rsi.RsiState,
SpriteSpecifier.Texture texture => texture.TexturePath.Filename,
_ => null
};

if (spriteName != null)
{
if (colors != null && i < colors.Count)
colorDict.Add(spriteName, colors[i]);
else
colorDict.Add(spriteName, Color.White);
}
}
Comment on lines +384 to +401

@coderabbitai coderabbitai Bot Aug 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python3 - <<'EOF'
import re, pathlib
for p in pathlib.Path('Resources/Prototypes').rglob('*.yml'):
    txt = p.read_text(errors='ignore')
    for block in re.split(r'(?m)^- type:\s*marking\s*$', txt)[1:]:
        block = block.split('\n- type:')[0]
        states = re.findall(r'^\s*state:\s*(\S+)', block, re.M)
        dup = sorted({s for s in states if states.count(s) > 1})
        if dup:
            ident = re.search(r'^\s*id:\s*(\S+)', block, re.M)
            print(p, ident.group(1) if ident else '?', dup)
EOF

Repository: ArcaneSS14/arcane

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file exists and line count =="
wc -l Content.Client/Humanoid/HumanoidAppearanceSystem.cs || exit 0

echo "== relevant source =="
sed -n '340,505p' Content.Client/Humanoid/HumanoidAppearanceSystem.cs

echo "== find MarkingPrototype definition/usages =="
rg -n "class MarkingPrototype|struct MarkingPrototype|MarkingPrototype|Sprites|Colors" -S --glob '*.cs'

Repository: ArcaneSS14/arcane

Length of output: 7165


🌐 Web query:

RobustContent HumanoidAppearanceSystem ApplyMarking MarkingPrototype MarkingManager Marking rsi.RsiState colorDict Add Dictionary

💡 Result:

The terms mentioned are related to the codebase of Space Station 14 (SS14), a multiplayer game built on the RobustToolbox engine, with the exception of RobustContent, which appears to be a confusion with the "Robust" accessibility principle from the Web Content Accessibility Guidelines (WCAG) [1][2][3]. In the context of SS14's humanoid appearance system: 1. HumanoidAppearanceSystem: This was a core system in SS14 responsible for managing the appearance of humanoid entities (such as species, markings, and colors) [4][5]. Note that in recent versions of the game, this has undergone significant refactoring (e.g., the "nubody" refactor), moving logic toward SharedVisualBodySystem and components like HumanoidProfileComponent [6]. 2. ApplyMarking / MarkingManager / MarkingPrototype: These are part of the game's marking system. MarkingManager handles the logic for markings across both in-simulation entities and out-of-simulation profile editing [7]. MarkingPrototype defines specific marking data (like textures or sponsor-only status) [7][8]. Marking manipulation is often performed via methods that manage layers and groups [6][7]. 3. RSI / RsiState: RSI (Robust Station Image) is the proprietary file format used by the RobustToolbox engine to store sprites, animations, and associated metadata [9]. RsiState represents an individual "state" (an icon or animation sequence) within an RSI file, containing data such as frame delays, directions, and metadata flags [9][10][11]. 4. colorDict: This usually refers to a Dictionary object (often Dictionary<string, Color> or similar structures) used in the code to map specific color identifiers to color values, commonly used when applying colors to markings or character features [4][6]. "RobustContent" is not a specific class or system in this codebase; it is a fundamental principle of web accessibility (WCAG), which dictates that web content must be robust enough to be interpreted reliably by a wide variety of user agents and assistive technologies [1][12][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tracked files around HumanoidAppearanceSystem.cs =="
git ls-files | rg '(^|/)HumanoidAppearanceSystem\.cs$|MarkingPrototype|Marking' | sed -n '1,120p'

echo "== exact references to ApplyMarking and colorDict in repository =="
rg -n "ApplyMarking|colorDict|MarkingPrototype|Sprites|Colors|ColorLinks|Layering|LayerSetColor" -S --glob '*.cs' .

echo "== YAML-like rsi/state occurrences in prototype content =="
python3 - <<'EOF'
import pathlib, re
roots = [p for p in pathlib.Path('.').iterdir() if p.is_dir() and p.name in ('Content.Client','Content.Server','Content.Shared','Resources')] + [p for p in pathlib.Path('.').iterdir() if p.is_dir()]
seen = set()
for p in pathlib.Path('.').rglob('*'):
    if p.is_file():
        try:
            s = p.read_text(errors='ignore')
        except Exception:
            continue
        print(f'FILE {p}')
        for m in re.finditer(r'(?:state:\s*([^\s\n]+)|RSI:\s*([^\s\n]+)|- type:\s*marking)', s):
            print(m.group(0))
            if len(s) > 200000 or len(seen) > 200:
                break
EOF

Repository: ArcaneSS14/arcane

Length of output: 50374


Не меняйте порядок добавления цветов в colorDict.

Два спрайта в одном marking с одинаковым RsiState/именем текстуры до сих пор вызывают Dictionary.Add и крашат клиент. Учтите путь RSI или используйте colorDict[spriteName] = ..., чтобы семантика была явной: последний спрайт задает цвет.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Client/Humanoid/HumanoidAppearanceSystem.cs` around lines 384 - 401,
Update the color population logic in the marking sprite loop to avoid
Dictionary.Add failures when duplicate RSI states or texture filenames occur.
Use assignment semantics for colorDict so duplicate spriteName entries overwrite
earlier values, preserving the intended behavior that the last sprite determines
the color.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это с апстрима. Как можно кратко пофиксить?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@UmbiMax, замените оба вызова colorDict.Add(...) на одно присваивание. Последний спрайт с одинаковым именем сохранит свой цвет, а клиент не упадёт.

colorDict[spriteName] = colors != null && i < colors.Count ? colors[i] : Color.White; // Arcane-Edit

То есть замените текущий блок:

if (colors != null && i < colors.Count)
    colorDict.Add(spriteName, colors[i]);
else
    colorDict.Add(spriteName, Color.White);

Порядок обхода Sprites не меняется.


🧠 Learnings used
Learnt from: UmbiMax
Repo: ArcaneSS14/arcane PR: 133
File: Content.Server/Speech/EntitySystems/StutteringSystem.cs:28-29
Timestamp: 2026-07-28T16:30:37.419Z
Learning: При внесении одиночных fork-правок в репозитории ArcaneSS14/arcane вне путей `_Arcane` добавляйте в конец каждой изменённой строки комментарий-маркер `// Arcane-Edit`. Это нужно для упрощения последующего поиска/обновления подобных правок при код-ревью и синхронизациях.

You are interacting with an AI system.

// now, rearrange them, copying any parented colors to children set to
// inherit them
if (markingPrototype.ColorLinks != null)
{
foreach (var (child, parent) in markingPrototype.ColorLinks)
{
if (colorDict.TryGetValue(parent, out var color))
{
colorDict[child] = color;
}
}
}
// and, since we can't rely on the iterator knowing where the heck to put
// each sprite when we have one marking setting multiple layers,
// lets just kinda sorta do that ourselves
var layerDict = new Dictionary<string, int>();
// FLOOF ADD END
for (var j = 0; j < markingPrototype.Sprites.Count; j++)
{
// FLOOF CHANGE START
var markingSprite = markingPrototype.Sprites[j];

if (markingSprite is not SpriteSpecifier.Rsi rsi)
{
continue;
}

var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
var layerSlot = markingPrototype.BodyPart;
// first, try to see if there are any custom layers for this marking
if (markingPrototype.Layering != null && markingPrototype.Layering.TryGetValue(rsi.RsiState, out var layerName)) // Arcane-Edit: Bugfix
{
// Arcane-Edit-Start
if (!Enum.TryParse<HumanoidVisualLayers>(layerName, out var parsedLayer))
{
Log.Error($"Marking {markingPrototype.ID} references unknown visual layer {layerName}");
continue;
}
// Arcane-Edit-End
layerSlot = parsedLayer; // Arcane
}
Comment thread
UmbiMax marked this conversation as resolved.
// update the layerDict
// if it doesnt have this, add it at 0, otherwise increment it
if (layerDict.TryGetValue(layerSlot.ToString(), out var layerIndex))
{
layerDict[layerSlot.ToString()] = layerIndex + 1;
}
else
{
layerDict.Add(layerSlot.ToString(), 0);
}

if (!_sprite.LayerMapTryGet((entity.Owner, sprite), layerId, out var layer, false)) // Goob edit
if (!sprite.LayerMapTryGet(layerSlot, out var targetLayer))
{
layer = _sprite.AddLayer((entity.Owner, sprite), markingSprite, targetLayer + j + 1); // Goob edit
_sprite.LayerMapSet((entity.Owner, sprite), layerId, layer);
_sprite.LayerSetSprite((entity.Owner, sprite), layerId, rsi);
continue;
}

var hasInfo = humanoid.CustomBaseLayers.TryGetValue(markingPrototype.BodyPart, out var info); // Goobstation
// impstation edit begin - check if there's a shader defined in the markingPrototype's shader datafield, and if there is...
visible &= !IsHidden(humanoid, markingPrototype.BodyPart);
visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
&& setting.AllowsMarkings;

var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
// FLOOF CHANGE END

if (!sprite.LayerMapTryGet(layerId, out _))
{
// for layers that are supposed to be behind everything,
// adding 1 to the layer index makes it not be behind
// everything. fun! FLOOF ADD =3
// var targLayerAdj = targetLayer == 0 ? 0 + j : targetLayer + j + 1;
var targLayerAdj = targetLayer + layerDict[layerSlot.ToString()] + 1;
var layer = sprite.AddLayer(markingSprite, targLayerAdj);
sprite.LayerMapSet(layerId, layer);
sprite.LayerSetSprite(layerId, rsi);
}
// imp special via beck. check if there's a shader defined in the markingPrototype's shader datafield, and if there is...
if (markingPrototype.Shader != null)
{
// use spriteComponent's layersetshader function to set the layer's shader to that which is specified.
sprite.LayerSetShader(layer, markingPrototype.Shader); // Goob edit
sprite.LayerSetShader(layerId, markingPrototype.Shader);
}
else // Goobstation
{
if (hasInfo && info.Shader != null)
sprite.LayerSetShader(layer, info.Shader);
else
sprite.LayerSetShader(layer, null, null);
}
// impstation edit end

_sprite.LayerSetVisible((entity.Owner, sprite), layerId, visible);
// end imp special
sprite.LayerSetVisible(layerId, visible);
Comment on lines +452 to +482

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

RobustToolbox SpriteComponent LayerMapSet AddLayer LayerSetShader obsolete SpriteSystem migration

💡 Result:

In RobustToolbox, recent architectural changes have deprecated several methods on the SpriteComponent in favor of using the SpriteSystem. This migration is part of a broader effort to move logic out of components and into their corresponding systems [1][2]. Key Migration Points: Obsolete SpriteComponent Methods: Methods such as LayerMapSet, LayerMapRemove, and LayerMapTryGet (among others) on SpriteComponent have been marked as [Obsolete] and now advise using the equivalent methods provided by SpriteSystem [1]. Using SpriteSystem: Instead of calling methods directly on the SpriteComponent instance, you should now pass the Entity (containing the SpriteComponent) to the SpriteSystem [1][3]. For example, when working with layer maps, you would use: - SpriteSystem.LayerMapSet(entity, key, layerIndex) [1] - SpriteSystem.LayerMapRemove(entity, key) [1] - SpriteSystem.LayerMapTryGet(entity, key, out layerIndex) [1] Layer Reservation: If you were using LayerMapReserveBlank, the modern equivalent is SpriteSystem.LayerMapReserve(entity, key). Note that this method has been updated to be more resilient; it no longer throws an exception if the specified layer already exists, effectively behaving like the former LayerMapReserveBlank [3][2]. Other Methods: The migration also affects layer management and shader operations. Where you previously might have called LayerSetShader directly on a component or layer object, you should look for the corresponding methods in SpriteSystem [1]. Best Practice: When migrating, verify if your code interacts with the SpriteComponent directly and shift those interactions to the SpriteSystem, injecting the SpriteSystem dependency into your own system if necessary [2]. Always refer to the specific [Obsolete] attribute message in your IDE, as it typically points to the exact SpriteSystem method you should use as a replacement [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate game files =="
git ls-files | rg '(^|/)Content\.Client/Humanoid/HumanoidAppearanceSystem\.cs$|package\.lock|packages\.lock\.json|\.csproj$|Directory\.Build\.props$' || true

echo
echo "== Relevant snippet =="
if [ -f Content.Client/Humanoid/HumanoidAppearanceSystem.cs ]; then
  nl -ba Content.Client/Humanoid/HumanoidAppearanceSystem.cs | sed -n '1,90p;430,515p'
fi

echo
echo "== SpriteComponent/SpriteSystem usages in file =="
rg -n "(_sprite|SpriteSystem|LayerMapTryGet|AddLayer|LayerMapSet|LayerSetSprite|LayerSetShader|LayerSetVisible|LayerSetColor|RemoveLayer|ResolveLayers)" Content.Client/H g -2 --iglob '*.cs' --iglob '*.xml' 2>/dev/null | head -n 200 || true

Repository: ArcaneSS14/arcane

Length of output: 1377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Relevant HumanoidAppearanceSystem areas =="
awk '{printf "%4d\t%s\n", NR, $0}' Content.Client/Humanoid/HumanoidAppearanceSystem.cs | sed -n '1,100p;420,515p'

echo
echo "== SpriteComponent direct layer calls in HumanoidAppearanceSystem =="
rg -n "LayerMapTryGet|AddLayer|LayerMapSet|LayerSetSprite|LayerSetShader|LayerSetVisible|LayerSetColor|RemoveLayer|ResolveLayer|_sprite" Content.Client/Humanoid/HumanoidAppearanceSystem.cs Content.Client/Humanoid 2>/dev/null || true

echo
echo "== Package refs likely affecting robust refs =="
rg -n "RobustToolbox|SpaceWizards|RobustClient|Robust.Shared" Content.Client/Content.Client.csproj Directory.Build.props . 2>/dev/null | head -n 80 || true

echo
echo "== Find project/package files =="
find . -maxdepth 3 \( -name 'packages.lock.json' -o -name 'package.lock' -o -name '*.resx' \) -print 2>/dev/null | sort | head -n 50 || true

Repository: ArcaneSS14/arcane

Length of output: 24430


Переведите этот блок на _sprite и удалите закомментированные фрагменты.

Здесь смешаны вызовы SpriteComponent (LayerMapTryGet, AddLayer, LayerMapSet, LayerSetSprite, LayerSetShader, LayerSetVisible, LayerSetColor) и уже используемый в файле SpriteSystem (_sprite). При TreatWarningsAsErrors устаревшие методы SpriteComponent могут сломать сборку, поэтому добавьте entity-вызовы через _sprite и удалите закомментированные строки 466 и 492-500.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Client/Humanoid/HumanoidAppearanceSystem.cs` around lines 449 - 479,
Переведите операции со слоями в этом блоке на API `_sprite`, включая
`LayerMapTryGet`, добавление слоя, установку спрайта, шейдера, видимости и
цвета, сохранив текущую логику. Удалите закомментированные строки вокруг
вычисления `targLayerAdj` и остальные указанные закомментированные фрагменты, не
оставляя вызовов устаревших методов `SpriteComponent`.


if (!visible || setting == null) // this is kinda implied
{
Expand All @@ -431,29 +489,18 @@ private void ApplyMarking(MarkingPrototype markingPrototype,
// Okay so if the marking prototype is modified but we load old marking data this may no longer be valid
// and we need to check the index is correct.
// So if that happens just default to white?
if (colors != null && j < colors.Count)
{
// Goob edit start
var color = colors[j];
if (hasInfo && info.Color != null)
color = Color.InterpolateBetween(color, info.Color.Value, 0.5f);
_sprite.LayerSetColor((entity.Owner, sprite), layerId, color);
// Goob edit end
}
else
{
// Goob edit start
var color = Color.White;
if (hasInfo && info.Color != null)
color = info.Color.Value;
_sprite.LayerSetColor((entity.Owner, sprite), layerId, color);
// Goob edit end
}

if (humanoid.MarkingsDisplacement.TryGetValue(markingPrototype.BodyPart, out var displacementData) && markingPrototype.CanBeDisplaced)
{
_displacement.TryAddDisplacement(displacementData, (entity.Owner, sprite), targetLayer + j + 1, layerId, out _);
}
// FLOOF ADD =3
sprite.LayerSetColor(layerId, colorDict.TryGetValue(rsi.RsiState, out var color) ? color : Color.White);

// FLOOF CHANGE
// if (colors != null && j < colors.Count)
// {
// sprite.LayerSetColor(layerId, colors[j]);
// }
// else
// {
// sprite.LayerSetColor(layerId, Color.White);
// }
}
}

Expand Down
27 changes: 26 additions & 1 deletion Content.Client/Humanoid/MarkingPicker.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,12 +409,37 @@ private void OnUsedMarkingSelected(ItemList.ItemListSelectedEventArgs item)
List<ColorSelectorSliders> colorSliders = new();
for (int i = 0; i < prototype.Sprites.Count; i++)
{
// first, check if the coloration is parented to another marking
// and if so, just kinda sorta dont display it
var skipdraw = false;
if (prototype.ColorLinks?.Count > 0)
{
var name = prototype.Sprites[i] switch
{
SpriteSpecifier.Rsi rsi => rsi.RsiState,
SpriteSpecifier.Texture texture => texture.TexturePath.Filename,
_ => null
};

if (name != null && prototype.ColorLinks.ContainsKey(name))
{
// dont show it, cus its parented to another marking
skipdraw = true;
}
}
Comment thread
UmbiMax marked this conversation as resolved.
var colorContainer = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
};

CMarkingColors.AddChild(colorContainer);
// so.
// the color selector sliders decide which destination color to modify
// based on its index in the list of color selectors.
// this is a problem if we, say, want to *not* show a certain slider
// cus then it'll modify the wrong color, unless the color happened to
// be in index 0.
if(!skipdraw)
CMarkingColors.AddChild(colorContainer);

ColorSelectorSliders colorSelector = new ColorSelectorSliders();
colorSelector.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV
Expand Down
2 changes: 1 addition & 1 deletion Content.Client/RoundEnd/RoundEndSummaryWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private BoxContainer MakeRoundEndSummaryTab(string gamemode, string roundEnd, Ti
if (!string.IsNullOrEmpty(roundEnd))
{
var roundEndLabel = new RichTextLabel();
roundEndLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(roundEnd), tagsAllowed: null); // Arcane-Edit
roundEndLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(roundEnd), tagsAllowed: null); // Goob
roundEndSummaryContainer.AddChild(roundEndLabel);
}

Expand Down
21 changes: 20 additions & 1 deletion Content.Client/Stealth/StealthSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ public override void Initialize()
SubscribeLocalEvent<StealthComponent, BeforePostShaderRenderEvent>(OnShaderRender);
}

public override void Update(float frameTime) // for stealth to return to harpy after flight ends
// used for any system that forces a different shader on something while also having stealth from cobra gland
{
base.Update(frameTime);

var query = EntityQueryEnumerator<StealthComponent, SpriteComponent>();
while (query.MoveNext(out var uid, out var stealth, out var sprite))
{
if (stealth.Enabled && (sprite.PostShader == null || !sprite.RaiseShaderEvent))
{
SetShader(uid, true, stealth, sprite); // force the stealth shader
}
else if (!stealth.Enabled && sprite.PostShader == _shader)
{
SetShader(uid, false, stealth, sprite); // clean up
}
}
}

Comment on lines +33 to +51

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Найти другие места, где ставится SpriteComponent.PostShader без RaiseShaderEvent.
rg -nP --type=cs -C3 '\.PostShader\s*=' -g '!Content.Client/Stealth/StealthSystem.cs' Content.Client

Repository: ArcaneSS14/arcane

Length of output: 11137


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== StealthSystem.cs =="
wc -l Content.Client/Stealth/StealthSystem.cs
cat -n Content.Client/Stealth/StealthSystem.cs

echo
echo "== Candidate files with SetShader() or StealthComponent =="
rg -n --type=cs 'SetShader\(|StealthComponent|_shader|PostShader|RaiseShaderEvent' Content.Client/Stealth Content.Client/Content.Shared Content.Shared Content.Client | head -n 200

echo
echo "== Files touching larva related stealth =="
rg -nP --type=yml,yaml -C4 'larva|Xenomorph|Stealth|enabled:' Content.Shared/Prototypes Content.Client/Prototypes Content.Goobstation.Shared/Prototypes Content.Goobstation.Client/Prototypes | head -n 240

Repository: ArcaneSS14/arcane

Length of output: 30062


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Relevant PostShader assignment contexts =="
cat -n Content.Client/_EinsteinEngines/Flight/FlyingVisualizerSystem.cs
printf '\n---\n'
cat -n Content.Client/HolographicVisualizerSystem.cs 2>/dev/null || true
cat -n Content.Client/Movement/Systems/FloorOcclusionSystem.cs
printf '\n---\n'
cat -n Content.Client/Interactable/Components/InteractionOutlineComponent.cs
cat -n Content.Client/Outline/TargetOutlineSystem.cs
cat -n Content.Client/Interaction/DragDropSystem.cs | sed -n '440,485p'

echo
echo "== Entities with Stealth prototype =="
rg -nP --type=yml,yaml -C5 '<type:"Stealth">|Stealth:\s*enabled|hasStealth|EnableStealth|StartWithStealth' Content.Shared Content.Client Content.Goobstation | head -n 300

echo
echo "== Entities likely combining Xenomorph/larva with stealth or visuals =="
rg -nP --type=yml,yaml -C5 'Xenomorph|Xeno|larva|stealth|Stealth' Content.Shared Content.Goobstation | head -n 400

Repository: ArcaneSS14/arcane

Length of output: 19707


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Entities with Stealth component =="
rg -n -C5 'components:\s*- type: Stealth|type: Stealth|Stealth:\s*enabled|StartWithStealth|EnableStealth|hasStealth|HideOnStealth' '*.yml' '*.yaml' Content.Shared Content.Client Content.Goobstation | head -n 300

echo
echo "== Potential overlapping visual effects on stealthed prototypes =="
python3 - <<'PY'
import pathlib, re
root = pathlib.Path(".")
candidates = list(root.glob('**/*.yml')) + list(root.glob('**/*.yaml'))
for p in candidates:
    if 'Content.Client' in str(p) or 'Content.Shared' in str(p) or 'Content.Goobstation' in str(p):
        try:
            text = p.read_text(errors='ignore')
        except Exception:
            continue
        if re.search(r'components:.*(\n\s*- type: Stealth|Stealth:\s*enabled)', text, flags=re.S):
            matches = re.findall(r'(\n\s*- type: [A-Za-z0-9_]+.*)', text)
            nearby = "\n".join(m[:220] for m in matches)
            print(f"\n--- {p}")
            for m in matches:
                if re.search(r'(Fire|Damage|Xeno|Xenomorph|Warp|Anomaly|Outline|Drone|Overlay|Flight|Interaction|Status Icon|Damage|Hover|Lighting|Holographic)', m):
                    print(m[:420])
            if 'Xeno' in text or 'Xenomorph' in text or 'larva' in text.lower():
                print(text[:2500])
PY

Repository: ArcaneSS14/arcane

Length of output: 2172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file names with stealth/stealth components =="
git ls-files | rg '(^|/)stealth|Stealth|stealth' | head -n 200

echo
echo "== prototype snippets mentioning Stealth component =="
while IFS= read -r f; do
  rg -n -C3 'type: Stealth|Stealth:\s*enabled|StartWithStealth|EnableStealth|hasStealth|IgnoreStealth|IsStealthed|HideOnStealth' "$f" || true
done < <(git ls-files '*.yml' '*.yaml') | head -n 400

echo
echo "== file names containing Xenomorph/larva/stealth prototypes in content yaml =="
git ls-files '*.yml' '*.yaml' | rg -i 'xeno|larva|stealth|xenomorph' | head -n 200

echo
echo "== exact matching Yaml files =="
git ls-files | grep -E '/(larva|Xenomorph|Xeno|Stealth)[^/]*(\.yml|\.yaml)$|Xenomorph.*Stealth|larva.*Stealth' || true

Repository: ArcaneSS14/arcane

Length of output: 5352


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== prototype snippets mentioning Stealth component =="
rg -n -C6 'type: Stealth|Stealth:\s*enabled|StartWithStealth|EnableStealth|hasStealth|IgnoreStealth|IsStealthed|HideOnStealth' $(git ls-files '*.yml' '*.yaml') | head -n 500

echo
echo "== xenomorph/stealth prototypes =="
rg -n -C6 'Xeno|Xenomorph|Xenomorph|larva|stealth|Stealth' $(git ls-files '*.yml' '*.yaml') | head -n 600

echo
echo "== exact prototypes =="
git ls-files '*.yml' '*.yaml' | rg -i 'xenomorph|larva|stealth|nukiestealth' | head -n 200

Repository: ArcaneSS14/arcane

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== FloorOcclusion candidate entities =="
rg -n -C8 'type: FloorOcclusion|FloorOcclusion:\s*\[true|Enabled:\s*true|LayerSetPostShader|PostShader' $(git ls-files '*.yml' '*.yaml') | rg -C8 'FloorOcclusion' | head -n 300

echo
echo "== Other client PostShader assignments and RaiseShaderEvent state =="
rg -n --type=cs -C3 '\.RaiseShaderEvent\s*=|\.PostShader\s*=' Content.Client | rg -C3 -E 'RaiseShaderEvent|PostShader' | head -n 360

echo
echo "== Hover/Visualizer/Flight/Xeno systems references =="
rg -n --type=cs -C3 'BeforePostShaderRenderEvent|FlightVisualsComponent|Holographic|FloorOcclusion|PostShader|StealthComponent' Content.Client/* Content.Client/_* | head -n 400

Repository: ArcaneSS14/arcane

Length of output: 1654


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== FloorOcclusion assignments in prototypes =="
python3 - <<'PY'
import pathlib, re
for p in pathlib.Path('.').glob('**/*.yml'):
    txt = p.read_text(errors='ignore')
    if 'FloorOcclusion' in txt:
        for m in re.finditer(r'^{4,16}-{0,2} type: FloorOcclusion(\n.*){0,8}', txt, flags=re.M):
            start=max(0,m.start()-600); end=min(len(txt),m.end()+600)
            print(f"\n--- {p}")
            for i,line in enumerate(txt.splitlines(),1):
                if i >= start//10 and i <= end//10:
                    print(f"{i}: {line}")
PY

echo
echo "== Other client PostShader assignments with RaiseShaderEvent state =="
python3 - <<'PY'
for p in pathlib.Path('Content.Client').glob('**/*.cs'):
    txt=p.read_text(errors='ignore')
    if '.PostShader =' in txt or '.RaiseShaderEvent' in txt:
        print(f"\n--- {p}")
        for i,line in enumerate(txt.splitlines(),1):
            if '.PostShader =' in line or '.RaiseShaderEvent' in line or 'RaiseShaderEvent' in line:
                a=max(1,i-2); b=min(len(txt.splitlines()),i+2)
                for j in range(a,b+1):
                    print(f"{j}: {txt.splitlines()[j-1]}")
                print()
import pathlib
PY

echo
echo "== Target/DragDrop contexts around assignments =="
for f in Content.Client/Outline/TargetOutlineSystem.cs Content.Client/Interaction/DragDropSystem.cs Content.Client/Movement/Systems/FloorOcclusionSystem.cs Content.Client/HolographicVisualizerSystem.cs Content.Client/_EinsteinEngines/Flight/FlyingVisualizerSystem.cs; do
  [ -f "$f" ] && { echo "--- $f"; sed -n '1,220p' "$f"; }
done

Repository: ArcaneSS14/arcane

Length of output: 994


🏁 Script executed:

#!/bin/bash
set -u

echo "== prototype snippets directly mentioning FloorOcclusion =="
rg -n -C10 'type: FloorOcclusion' Resources/*.yml Content.Shared/*.yml Resources/Prototypes/**/*.yml Content.Shared/**/*.yml 2>/dev/null | head -n 250 || true

echo
echo "== client C# files with shader event control =="
rg -n -C3 '\.RaiseShaderEvent|\.PostShader' Content.Client/*.cs Content.Client/Robust.Shared/Client/*.cs Content.Client/*.cs Content.Shared/Content.SS14/Client/Robust.Shared/Client/*.cs Content.Shared/Robust.Shared/Client/Robust.Shared/Client/Legacy/*.cs 2>/dev/null | head -n 300 || true

echo
echo "== target/dragdrop hover systems =="
for f in Content.Client/Outline/TargetOutlineSystem.cs Content.Client/Interaction/DragDropSystem.cs Content.Client/Movement/Systems/FloorOcclusionSystem.cs; do
  if [ -f "$f" ]; then
    echo "--- $f"
    wc -l "$f"
    sed -n '1,220p' "$f"
  fi
done

Repository: ArcaneSS14/arcane

Length of output: 17283


🏁 Script executed:

#!/bin/bash
set -u

echo "== Target outline exact assignment slices =="
sed -n '136,188p' Content.Client/Outline/TargetOutlineSystem.cs
echo
sed -n '436,473p' Content.Client/Interaction/DragDropSystem.cs

echo
echo "== Flight system exact shader assignment =="
sed -n '37,49p' Content.Client/_EinsteinEngines/Flight/FlyingVisualizerSystem.cs

echo
echo "== Prototypes with both Stealth and components overlapping likely sprites or visual shaders =="
python3 - <<'PY'
import pathlib, re
for p in sorted(pathlib.Path('.').glob('**/*.yml')):
    txt = p.read_text(errors='ignore')
    if 'type: Stealth' not in txt:
        continue
    blocks = re.split(r'(?=^ {2}- type: [A-Za-z0-9_]+)', txt, flags=re.M)
    for block in blocks:
        if 'type: Stealth' in block:
            overlap=False
            for comp in ['StealthOnWalk','StealthOnMove','FloorOcclusion','FlightVisuals','InteractionOutline','Xenomorph','Xeno','Hunter','MobXenomorphLarva','MobXenomorph','Flying','Hover','Damageable','StatusIcon']:
                if comp in block:
                    overlap=True
                    break
            if overlap:
                print(f"\n--- {p}, first 28 lines")
                for i,line in enumerate(block.splitlines(),1):
                    if i <= 28:
                        print(f"{i}: {line}")
PY

Repository: ArcaneSS14/arcane

Length of output: 10325


🏁 Script executed:

#!/bin/bash
set -u

echo "== Robust SpriteComponent definitions for PostShader/RaiseShaderEvent =="
python3 - <<'PY'
import pathlib, re
targets = ['Robust.Client/GameObjects/SpriteComponent.cs','Robust.Client/GameObjects/SpriteComponent.Sets.cs','Robust.Shared.ContentPack/GameObjects/SpriteComponent.cs','Robust.Shared/GameObjects/SpriteComponent.cs']
for name in targets:
    p = pathlib.Path(name)
    if p.exists():
        print(f"\n--- {name}")
        txt=p.read_text(errors='ignore')
        for i,line in enumerate(txt.splitlines(),1):
            if 'PostShader' in line or 'RaiseShaderEvent' in line:
                a=max(1,i-5); b=min(len(txt.splitlines()),i+5)
                for j in range(a,b+1):
                    print(f"{j}: {txt.splitlines()[j-1]}")
PY

echo
echo "== Hologram visualizer system =="
sed -n '1,80p' Content.Client/Nyanotrasen/Holograms/HologramVisualizerSystem.cs

Repository: ArcaneSS14/arcane

Length of output: 1552


Учитывайте флаги шейдерных эффектов в StealthSystem.Update().

Этот Update() каждый кадр перезаписывает PostShader на стелс, если sprite.PostShader == null || !sprite.RaiseShaderEvent. Сейчас PostShader без RaiseShaderEvent = true используют несколько клиентских систем: FloorOcclusionSystem, TargetOutlineSystem, DragDropSystem, InteractionOutlineComponent, HologramVisualizerSystem. Если RoundStartMobXenomorphLarva получит один из этих эффектов вместе с включённым стелсом, его шейдер будет стёрт каждый кадр. Зафиксируйте правило: Update() должен сохранять только эффекты с RaiseShaderEvent = true, а не восстанавливать стелс поверх любого PostShader.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Client/Stealth/StealthSystem.cs` around lines 33 - 51, Update
StealthSystem.Update() so the enabled-stealth path does not overwrite an
existing PostShader when it is managed without RaiseShaderEvent; only restore
the stealth shader when PostShader is absent or when the existing effect
explicitly has RaiseShaderEvent = true. Preserve the cleanup behavior for
disabled stealth in the existing SetShader call.

public override void SetEnabled(EntityUid uid, bool value, StealthComponent? component = null)
{
if (!Resolve(uid, ref component) || component.Enabled == value)
Expand Down Expand Up @@ -100,4 +119,4 @@ private void OnShaderRender(EntityUid uid, StealthComponent component, BeforePos
visibility = MathF.Max(0, visibility);
_sprite.SetColor((uid, args.Sprite), new Color(visibility, visibility, 1, 1));
}
}
}
5 changes: 5 additions & 0 deletions Content.Client/_Lavaland/Atmos/LavalandAtmosphereSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Content.Shared._Lavaland.Atmos;

namespace Content.Client._Lavaland.Atmos;

public sealed class LavalandAtmosphereSystem : SharedLavalandAtmosphereSystem;
Loading
Loading