Ивентовый контент NDA/Terror spider#571
Ивентовый контент NDA/Terror spider#571Fredik21-git wants to merge 14 commits intoimperial-space:developfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughPR добавляет большой набор новых компонентов, систем и событий в модуль Imperial: механики SCP (SCP‑008, SCP‑096, SCP‑173, SCP‑Blink, SCP‑Fireman), систему Sanity, обширный пакет Terror Spider (множество компонентов, действий, яиц и систем), shared/networked компоненты для пауков, многочисленные action/event типы и реализацию EntitySystem для многих механик (таймеры, обработчики событий, эффекты). Также добавлены/обновлены ресурсы: прототипы, локализации, аудио‑атрибуции, alerts и текстуры. Sequence Diagram(s)sequenceDiagram
participant Player as Игрок
participant Actions as ActionsSystem
participant Fireman as SCPFiremanSystem
participant Spawn as PrototypeSystem
participant Spread as SCPFireSpreadSystem
participant Damage as DamageableSystem
Player->>Actions: выполнить ActionSCPFiremanIgnite
Actions->>Fireman: поднять SCPFiremanIgniteActionEvent
Fireman->>Fireman: проверить FirePoints и лимиты
alt валидно
Fireman->>Spawn: создать SCPFiremanFlame
Spawn-->>Fireman: сущность создана
Fireman->>Player: списать очки / обновить состояние
Fireman->>Spread: инициализировать распространение
loop цикл распространения
Spread->>Spread: искать горючие объекты в радиусе
Spread->>Damage: применять урон/зажечь цели
Damage-->>Spread: цели поражены/запалены
end
else невалидно
Fireman-->>Player: показать ошибку/фидбек
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~180 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 88
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 336-346: IsCoffeeLikeReagent currently does brittle substring
checks on reagentId; instead resolve the reagent prototype for the given
reagentId (using the reagent/prototype manager used in this system) and check a
dedicated tag or meta-group (e.g., "Coffee" or "coffee-like") on the
ReagentPrototype; update IsCoffeeLikeReagent to accept reagentId, fetch the
prototype, and return prototype.Tags.Contains("Coffee") (or prototype.MetaGroup
== "coffee-like") with a null/prototype-missing fallback of false, and adjust
any callers that pass raw ids to rely on the prototype-based check.
- Around line 497-515: PrototypeInherits can recurse indefinitely on cyclic
prototype inheritance; modify PrototypeInherits to accept (or create and
forward) a HashSet<string> visited set, immediately return false if prototypeId
is already in visited, add prototypeId to visited before recursing, and use that
same set when calling PrototypeInherits(parent, parentId, visited) so cycles are
detected and a StackOverflowException is avoided; keep existing logic otherwise
and ensure the public/entry call initializes the HashSet.
- Around line 527-531: Replace the hardcoded Russian popup strings in the
SanitySystem where _popup.PopupEntity is called (the branches checking
ent.Comp.Value vs ent.Comp.LowThreshold/HighThreshold) with calls to
Loc.GetString(...) using new localization keys (for example "sanity-popup-low"
and "sanity-popup-high"), and add the corresponding entries to the .ftl
localization files; ensure the PopupEntity calls pass the localized string
returned by Loc.GetString instead of the literal text so the messages are
localizable.
- Around line 35-40: The hardcoded HashSet FriendlyScpIds in SanitySystem makes
adding new friendly SCP prototypes require code changes; replace this with a tag
or marker component check (e.g., add a "FriendlySCP" prototype tag or an
IFriendlySCP component) and update any checks that currently reference
FriendlyScpIds to use entity prototype tags or ResolveComponent/HasTag logic
instead; modify SanitySystem methods that reference FriendlyScpIds (and mirror
the approach used for IsCoffeeLikeReagent) so they query prototype
tags/components at runtime rather than relying on the static HashSet.
- Around line 228-249: ProcessProximity currently calls IsNearLivingHumanoid,
ProcessScpProximity, IsCorpseNearVisible, IsScpNearVisible and IsNdaObjectNear
which each call GetEntitiesInRange causing 5 range queries per tick; replace
those multiple range queries with a single GetEntitiesInRange using the maximum
of ent.Comp.NearHumanRadius, CorpseRadius, ScpProximityRadius and NdaRadius,
then iterate the returned entities once to classify them (check for
humanoid/living, corpse/visible, scp/visible, NDA object, and any SCP-specific
handling currently in ProcessScpProximity), set flags/accumulate effects, and
finally call ModifySanity and TryPlayShockSound based on the aggregated results;
keep the original helper names (IsNearLivingHumanoid, ProcessScpProximity,
IsCorpseNearVisible, IsScpNearVisible, IsNdaObjectNear) only for logic reference
but move their internal checks to the single-pass classification to avoid extra
GetEntitiesInRange calls.
In
`@Content.Server/Imperial/SCP/SCP008/Components/SCP008InfectionAuraComponent.cs`:
- Around line 26-27: Replace the hardcoded Russian string in the WarningPopup
field of SCP008InfectionAuraComponent with a localization key (e.g.,
"scp-008-infection-warning") and update any UI/popup code that reads
WarningPopup to call the localization system with that key (so the .ftl entry
"scp-008-infection-warning = ..." contains the message); ensure the DataField
remains named "warningPopup" but its default value is the localization key and
not the full message so translations come from the .ftl file.
In `@Content.Server/Imperial/SCP/SCP008/Systems/SCP008InfectionAuraSystem.cs`:
- Around line 57-58: Вместо выделения нового HashSet и нового списка каждый тик,
добавьте поле в класс SCP008InfectionAuraSystem (например private readonly
HashSet<EntityUid> _reusableInRange = new(); и/или reusable list) и перед
использованием вызывайте .Clear(); затем вызовите перегрузку
_lookup.GetEntitiesInRange(uid, comp.Radius, comp.LookupFlags, _reusableInRange)
чтобы заполнить переиспользуемую коллекцию и работайте с ней (замените текущие
переменные currentlyInRange и локальные результаты GetEntitiesInRange на эти
поля).
In `@Content.Server/Imperial/SCP/SCP096/Components/SCP096RageOnLookComponent.cs`:
- Around line 1-4: Usings are placed after the file-scoped namespace which
breaks project style; move the using directives (e.g.,
System.Collections.Generic and Robust.Shared.Audio) above the file-scoped
namespace declaration `namespace Content.Server.Imperial.SCP.SCP096.Components;`
so that all `using` statements are declared before the namespace and reformat
the file to match project ordering conventions.
- Around line 36-43: Захардкоженные русские строки нужно заменить на
локализуемые значения: в компоненте SCP096RageOnLookComponent замените значения
полей RageWindupPopup, RagePopup и RageCalmPopup на ключи локализации (например
"scp096-rage-windup", "scp096-rage-start", "scp096-rage-calm" или аналогичные
LocId-ключи) и убедитесь, что в коде, где эти поля используются для показа
всплывающих окон, вы вызываете Loc.GetString(ключ) или используете LocId, а не
напрямую хардкоденные сообщения; обновите прототипы/локали соответствующими
переводами.
In `@Content.Server/Imperial/SCP/SCP096/Systems/SCP096RageOnLookSystem.cs`:
- Around line 130-138: When curTime >= comp.RageEndTime the ResetToCalm(uid,
comp) call clears comp.IsEnraged and sets Calm, but the subsequent calls to
UpdateStageAmbient(uid, comp, true) and SetVisualState(uid,
SCP096VisualState.Chasing) immediately overwrite that; modify the control flow
in the IsEnraged branch in SCP096RageOnLookSystem (check comp.IsEnraged /
curTime >= comp.RageEndTime) so that after calling ResetToCalm(uid, comp) you
skip the rest of the enraged-path logic (either return/continue or guard the
UpdateStageAmbient/SetVisualState calls with comp.IsEnraged) to ensure
ResetToCalm takes effect.
In
`@Content.Server/Imperial/SCP/SCP173/Components/SCP173LightFlickerComponent.cs`:
- Around line 24-25: Поле SCP173LightFlickerComponent.ActionEntity помечено как
[DataField], но это runtime-состояние (устанавливается через _actions.AddAction)
и должно быть помечено как [ViewVariables] в соответствии с проектным
соглашением; замените атрибут [DataField] на [ViewVariables] (или удалите
[DataField] и добавьте [ViewVariables]) чтобы убрать сериализацию и привести в
соответствие с реализацией TerrorSpiderCocoonComponent.ActionEntity.
In `@Content.Server/Imperial/SCP/SCP173/Systems/SCP173ContainmentCellSystem.cs`:
- Around line 14-31: The Update method in SCP173ContainmentCellSystem currently
runs every frame and re-checks IsInsideContainmentCell for every entity with
SCP173WatchLockComponent; reduce wasted work by adding a periodic update
interval (like SCP008InfectionAuraSystem.UpdateInterval) or switch to an
event-driven approach by handling
EntInsertedIntoContainerMessage/EntRemovedFromContainerMessage to trigger the
containment check and call _movement.RefreshMovementSpeedModifiers(uid) only
when containment state actually changes; modify the
SCP173ContainmentCellSystem.Update method (or add a timer field and loop logic)
to use the interval or implement message handlers so IsInsideContainmentCell is
not evaluated each frame.
In `@Content.Server/Imperial/SCP/SCP173/Systems/SCP173LightFlickerSystem.cs`:
- Around line 92-106: In ApplyLightState, move the capture into
CapturedLightStates so it runs only after the map and distance checks: first
ensure TryComp<PointLightComponent>(lightUid, out var pointLight) and
TryComp<TransformComponent>(lightUid, out var lightXform) with lightXform.MapID
== map, compute distance with _transform.GetWorldPosition(lightUid) and ensure
distance <= ent.Comp.Radius, and only then set
ent.Comp.CapturedLightStates[lightUid] = pointLight.Enabled (so RestoreLights
and future toggles only store states for relevant lights); update references to
CapturedLightStates, ApplyLightState, TransformComponent, PointLightComponent,
_transform, and _pointLight.SetEnabled accordingly.
- Line 16: Replace the fully qualified type on the dependency field with the
short type by adding a using for Robust.Shared.Timing and updating the field
declaration; specifically add "using Robust.Shared.Timing;" at the top of the
file and change the field declaration that references
Robust.Shared.Timing.IGameTiming to use IGameTiming (the _timing dependency
field).
In `@Content.Server/Imperial/SCP/SCP173/Systems/SCP173WatchLockSystem.cs`:
- Around line 95-135: The code does two separate Transform lookups for the same
entity in IsBeingWatched (and similarly in IsTargetLit) which is inefficient and
error-prone; fetch and cache the target's TransformComponent once (e.g., via
TryComp<TransformComponent>(target, out var targetTransform)) then use that
cached transform to get the world position and read MapID instead of calling
_transform.GetWorldPosition(target) and Transform(target).MapID twice; update
IsBeingWatched and IsTargetLit to use the single cached TransformComponent for
all position/rotation/MapID accesses and remove duplicate Transform(...) calls.
- Around line 45-65: Update currently runs every frame and calls IsBeingWatched
for each entity; add a float field _accumulator to SCP173WatchLockSystem,
subtract frameTime each Update, and only run the
EntityQueryEnumerator/IsBeingWatched loop when _accumulator <= 0, then reset
_accumulator to the desired throttle interval (e.g., 0.1f–0.25f). Keep calling
base.Update(frameTime) as before, ensure SetLocked, IsBeingWatched and the query
logic are executed only when the accumulator expires, and initialize the
_accumulator (and the chosen interval constant) on the system so the behavior
matches other throttled systems.
In `@Content.Server/Imperial/SCP/SCPBlink/Components/SCPBlinkableComponent.cs`:
- Around line 24-31: SCPBlinkableComponent currently hardcodes Russian strings
in the DataField defaults (ManualBlinkPopup, BlinkStartPopup, BlinkEndPopup);
change these fields to hold localization keys (e.g. "scpb.blink.manual",
"scpb.blink.start", "scpb.blink.end") and update any code that displays these
fields to resolve them through the localization system (call
Loc.GetString(ManualBlinkPopup) / Loc.GetString(BlinkStartPopup) /
Loc.GetString(BlinkEndPopup) or use LocId attributes) so the actual text is
provided by .ftl files for ru-RU and en-US rather than embedded Russian text.
In `@Content.Server/Imperial/SCP/SCPBlink/Systems/SCPBlinkSystem.cs`:
- Around line 57-72: OnShutdown currently clears the blink alert and then
duplicates the HasComp→RemComp→_blindable.UpdateIsBlind pattern for both
SCPBlinkBlindnessComponent and TemporaryBlindnessComponent; change it to remove
each component if present (using HasComp and RemComp for
SCPBlinkBlindnessComponent and TemporaryBlindnessComponent) but only call
_blindable.UpdateIsBlind once after both removals (or call it conditionally if
either removal happened) to avoid duplicate updates while keeping
_alerts.ClearAlert(ent.Owner, BlinkAlert) in place.
In `@Content.Server/Imperial/SCP/SCPFireman/Components/SCPFireSpreadComponent.cs`:
- Around line 6-7: UpdateSpreadFires uses the FireOwner field without validating
it; add a guard at the start of UpdateSpreadFires to bail out if
ent.Comp.FireOwner == EntityUid.Invalid (or otherwise ensure FireOwner is set on
component construction), so subsequent calls to AddPoints, HealOwner,
IgniteEntitiesInRadius and SpawnFireAtMap receive a valid owner; alternatively
enforce non-null owner assignment when the SCPFireSpreadComponent is created so
FireOwner can never be invalid.
In `@Content.Server/Imperial/SCP/SCPFireman/Components/SCPFireWhirlComponent.cs`:
- Around line 8-9: The FireOwner field in SCPFireWhirlComponent is runtime state
and must not be a DataField; replace the [DataField("owner")] attribute on the
public EntityUid FireOwner with [ViewVariables] so it is treated as view-only
runtime state (the owner is set by SCPFiremanSystem at spawn); leave the field
public and do not make it configurable via YAML prototypes.
In `@Content.Server/Imperial/SCP/SCPFireman/Systems/SCPFiremanSystem.cs`:
- Around line 285-292: OnWhirlMapInit and OnWhirlAction both initialize
SCPFireWhirlComponent (setting EndTime, NextEffectTime) and create/assign
TimedDespawnComponent causing redundant overwrite; remove the duplicate
initialization by keeping the initialization in one place (choose either
OnWhirlMapInit or OnWhirlAction) and delete the corresponding
EndTime/NextEffectTime/TimedDespawn setup from the other, or alternatively add
an idempotent guard in OnWhirlAction/OnWhirlMapInit that checks if
EndTime/NextEffectTime are already set (or TimedDespawnComponent already exists)
before assigning to avoid double initialization.
- Around line 65-66: The _pendingStrikes state is never cleared between
rounds/cards causing stale entries (and _ownedFireCounts also retains outdated
data); add logic to clear these collections when the game round/map resets or
during system initialization: clear _pendingStrikes, _pendingSpreads (even if
already cleared elsewhere), and _ownedFireCounts in your system's Initialize or
in a round-reset handler (subscribe to the round restart/round end event and
empty the lists/dictionaries there) so no stale state persists across rounds;
reference _pendingStrikes, _pendingSpreads, UpdateSpreadFires, and
_ownedFireCounts to locate where to add the clears.
- Around line 306-332: The two while loops that catch up missed ticks (using
comp.NextPassiveTick and comp.NextSecondModeDrainTick with AddPoints) can loop
unbounded if now is far ahead; change them to limit iterations per update (e.g.,
maxTicks = 10 or similar) or compute ticksToCatch = (int)Math.Min(maxTicks, (now
- comp.NextPassiveTick).TotalSeconds) and apply a single cumulative AddPoints
call or advance NextPassiveTick/NextSecondModeDrainTick by ticksToCatch seconds
in one step; also ensure the SecondModeEnabled checks and early exits still
occur when applying a batched drain via comp.SecondModeEnabled, AddPoints,
comp.NextSecondModeDrainTick and handle FirePoints hitting <= 0 appropriately.
- Around line 443-453: In TryConsumePoints (SCPFiremanSystem.TryConsumePoints)
the popup message is hardcoded in Russian; add a new localization entry (e.g.,
scpfireman-not-enough-points = "Недостаточно огненных очков.") to the module
.ftl file and replace the literal in the _popup.PopupEntity call with a
localized lookup (use the project's localization helper such as
Loc.GetString("scpfireman-not-enough-points") or the equivalent) so the popup
text is loaded from localization instead of being hardcoded.
- Around line 237-246: OnBeforeDamageChanged is dead code because it’s never
subscribed to BeforeDamageChangedEvent; either remove the method or wire it up
in Initialize to provide the extra protective check. If you choose to subscribe,
add a subscription for BeforeDamageChangedEvent that points to
OnBeforeDamageChanged in Initialize (so the handler runs and can cancel damage
when ent.Comp.TrueFlameActive and _timing.CurTime < ent.Comp.TrueFlameEnd); if
you prefer removal, delete OnBeforeDamageChanged to avoid unused code given
invulnerability is already handled via _godmode.EnableGodmode. Ensure references
to TrueFlameActive, TrueFlameEnd and _timing remain consistent with the intended
behavior.
In
`@Content.Server/Imperial/Seriozha/Administration/Commands/NedraNukeProtocolCommand.cs`:
- Around line 202-228: TriggerExplosionSpawners currently iterates all entities
via EntityQueryEnumerator<TransformComponent, MetaDataComponent>, which is
costly; add a marker component NedraNukeExplosionSpawnerComponent
(RegisterComponent) and attach it to the NedraNukeExplosionSpawner prototype,
then change the query inside TriggerExplosionSpawners to enumerate the marker
along with TransformComponent (e.g.,
EntityQueryEnumerator<NedraNukeExplosionSpawnerComponent, TransformComponent>),
remove the protoId string-check, and use the transform to queue the explosion as
before so only spawner entities are visited.
- Around line 102-112: The delayed Timer.Spawn callback uses
mapUid/targetMap.Value without ensuring the map/entity still exists and can
throw after ~125s; add a guard at the top of the callback (before calling
CloseCentcommHermeticsAndEnableGodmode, TriggerExplosionSpawners,
SetMapAmbientColor, SetAllPointLightsColor, UnboltAirlocks, DisableGodmode) that
verifies the map is still valid (e.g., targetMap.HasValue && the map/entity
exists via your MapManager/EntityManager API or
MapManager.IsValidMap(targetMap.Value)) and return early if the check fails.
- Around line 257-284: The substring checks in IsHermeticOrAirlock and
IsCentcommHermetic (reading MetaDataComponent.EntityPrototype.ID and calling
Contains("BlastDoor"/"Shutter"/"CentralCommand")) are fragile; replace them with
component-based checks (e.g., HasComponent<BlastDoorComponent>(uid),
HasComponent<ShutterComponent>(uid), HasComponent<CentcommMarkerComponent>(uid))
so prototype renames won't break logic, or if you cannot add marker components,
centralize those literal strings into well-named constants and use
exact/controlled matching against protoId; update the methods
IsHermeticOrAirlock and IsCentcommHermetic to use the new components or
constants and keep the existing AirlockComponent and FirelockComponent checks.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderDestroyerComponent.cs`:
- Around line 34-38: The two fields EmpUseSound and EmpDeathSound in
TerrorSpiderDestroyerComponent both default to "/Audio/Effects/sparks4.ogg",
likely a copy-paste error; update EmpDeathSound to a more appropriate death
sound (or null) so use and death sounds differ, e.g. replace the EmpDeathSound
default with the intended death audio asset or remove the default to fall back
to no sound; ensure you only change the EmpDeathSound initializer (leave
EmpUseSound as-is) and keep the DataField attribute intact.
- Line 2: Удалите неиспользуемую директиву using
Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; из
файла TerrorSpiderDestroyerComponent.cs: код использует EntProtoId (в
полях/атрибутах на строках, напр. объявления свойств), но явного использования
типов из указанного пространства имён нет, поэтому просто удалите эту строку
импорта чтобы убрать предупреждение об неиспользуемом using.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderHealerComponent.cs`:
- Around line 12-37: Поля PulseActionEntity, EggRusarActionEntity,
EggDronActionEntity, EggLurkerActionEntity и EggHealerActionEntity в компоненте
TerrorSpiderHealerComponent помечены как [DataField], тогда как они являются
рантайм-состоянием, устанавливаемым через _actions.AddAction; замените атрибут
[DataField] на [ViewVariables] для каждого из этих пяти полей (сохранив типы
EntityUid? и существующие именования EntProtoId полей), чтобы они
соответствовали соглашению о том, что только конфигурируемые через прототипы
поля используют [DataField].
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderPrincessBroodComponent.cs`:
- Around line 6-7: Change the attributes on the runtime-only fields in
TerrorSpiderPrincessBroodComponent: replace the [DataField] attribute on the
Princess and Elite fields with [ViewVariables] because they are set
programmatically by TerrorSpiderPrincessSystem (during brood hatch) and not
configured via YAML prototypes; locate the Princess and Elite fields in the
TerrorSpiderPrincessBroodComponent class and update their annotations
accordingly so they remain visible for debugging/inspection but are not treated
as serializable data fields.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderPrincessEggComponent.cs`:
- Around line 9-10: The fields Princess and HatchAt in
TerrorSpiderPrincessEggComponent are runtime state but are currently annotated
with [DataField]; change their attributes to [ViewVariables] instead so they
aren’t treated as prototype-configured data—update the attribute on the Princess
property and the HatchAt field in the TerrorSpiderPrincessEggComponent class to
[ViewVariables] to reflect they’re assigned/computed at runtime.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderPrincessOrphanDamageComponent.cs`:
- Around line 12-13: Поле NextTick помечено как [DataField], но это
runtime-состояние, а не конфигурация; замените атрибут [DataField] на
[ViewVariables] в компоненте TerrorSpiderPrincessOrphanDamageComponent
(переменная NextTick) чтобы пометить его как внутреннее изменяемое состояние, а
не настраиваемое через YAML-прототипы.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderQueenBroodComponent.cs`:
- Around line 6-7: The field Queen in the TerrorSpiderQueenBroodComponent is
runtime state but is annotated with [DataField]; change its attribute to
[ViewVariables] so it isn't treated as a YAML-configurable prototype
field—locate the TerrorSpiderQueenBroodComponent class and replace the
[DataField] on the public EntityUid? Queen property with [ViewVariables]
(keeping nullable type and accessibility intact).
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderQueenComponent.cs`:
- Around line 112-113: The field HiveCreated on TerrorSpiderQueenComponent is
runtime state but is incorrectly marked [DataField]; change its attribute to
[ViewVariables] (remove [DataField] and annotate HiveCreated with
[ViewVariables]) so it is treated as a runtime/view variable rather than a
YAML-configurable data field.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderQueenEggComponent.cs`:
- Around line 9-16: Поле(я) Queen и HatchAt в компоненте
TerrorSpiderQueenEggComponent являются runtime-состоянием и должны быть помечены
атрибутом [ViewVariables] вместо [DataField]; оставьте HatchDelay как
[DataField] (конфигурация прототипа). Найдите класс
TerrorSpiderQueenEggComponent и замените/добавьте атрибуты так, чтобы
свойство/поле Queen и свойство/поле HatchAt имели [ViewVariables], а HatchDelay
остался с [DataField], не меняя логики присвоения/инициализации этих полей.
- Around line 18-19: Change the SpawnPrototype field on
TerrorSpiderQueenEggComponent from a string with PrototypeIdSerializer to use
EntProtoId for consistency: replace the field declaration to use type EntProtoId
(keep DataField(required: true) but remove customTypeSerializer) and update any
code that constructs/assigns SpawnPrototype accordingly; also remove the
now-unneeded using for PrototypeIdSerializer and ensure any serialization/usage
sites accept EntProtoId instead of string.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderQueenOrphanDamageComponent.cs`:
- Around line 12-13: The NextTick field on
TerrorSpiderQueenOrphanDamageComponent is runtime state but currently annotated
with [DataField]; replace the [DataField] attribute with [ViewVariables] on the
NextTick TimeSpan field so it is treated as runtime/view-only instead of a
YAML-configured prototype value (update the attribute above the public TimeSpan
NextTick declaration in the TerrorSpiderQueenOrphanDamageComponent class).
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderRoyalComponent.cs`:
- Around line 16-17: The field StompActionEntity on TerrorSpiderRoyalComponent
is runtime state, not prototype-configured, so replace the [DataField] attribute
with [ViewVariables] (and remove any serialization intent) so it's not treated
as YAML-configurable; locate the StompActionEntity declaration in
TerrorSpiderRoyalComponent (referenced where _actions.AddAction(uid, ref
comp.StompActionEntity, comp.StompAction) is called) and change the attribute
accordingly to mark it as a view variable/runtime-only field.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderWidowMeleeDebuffComponent.cs`:
- Around line 12-13: The VenomReagent field on
TerrorSpiderWidowMeleeDebuffComponent (and its duplicate on
TerrorSpiderWidowWebAreaComponent) should be changed from a raw string to a
strongly-typed ProtoId<ReagentPrototype> to enable prototype validation at load;
update the field declaration (keep the [DataField] attribute) to use
ProtoId<ReagentPrototype> with the same default ("BlackTerrorVenom") expressed
as the proto id, and adjust any usages that treat it as a string to use the
ProtoId type instead so deserialization/validation will fail fast on invalid
reagent ids.
In
`@Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderWidowWebAreaComponent.cs`:
- Around line 9-10: Поле VenomReagent в компоненте
TerrorSpiderWidowWebAreaComponent должно быть ProtoId<ReagentPrototype> вместо
string: поменяйте объявление public string VenomReagent = "BlackTerrorVenom"; на
public ProtoId<ReagentPrototype> VenomReagent = "BlackTerrorVenom"; (и добавьте
нужный using для ProtoId/ReagentPrototype), и затем обновите все использования
VenomReagent в коде, чтобы они принимали ProtoId<ReagentPrototype> (если где-то
ожидается string, явно преобразуйте к строке через .ToString() или используйте
методы API, принимающие ProtoId).
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorDronDebuffSystem.cs`:
- Around line 29-33: The call in TerrorDronDebuffSystem currently reuses
MovementModStatusSystem.TaserSlowdown causing cross-source conflicts; define a
new, semantically named constant in MovementModStatusSystem (e.g.,
TerrorDroneSlowdown) and replace the use of TaserSlowdown in
TerrorDronDebuffSystem's TryUpdateMovementSpeedModDuration call to the new
constant; also update any other systems that should have distinct slowdown
identifiers (TerrorSpiderQueenSystem, TerrorSpiderPrincessSystem,
ExplosiveProjectileSystem, StunOnCollideSystem) to use their own new constants
so each source has an independent movement-mod key.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderDestroyerSystem.cs`:
- Around line 49-85: The EMP ranges are swapped: in OnEmpScreamAction you
currently pass ent.Comp.DeathEmpRange but should use ent.Comp.EmpScreamRange
(the smaller active-scream radius), and in OnMobStateChanged you currently pass
ent.Comp.EmpScreamRange but should use ent.Comp.DeathEmpRange (the larger death
EMP radius). Update the calls to _emp.EmpPulse in OnEmpScreamAction and
OnMobStateChanged to use ent.Comp.EmpScreamRange and ent.Comp.DeathEmpRange
respectively while leaving the other parameters and audio calls unchanged.
- Around line 102-126: The IgniteArea method performs side effects
(AdjustFireStacks and Ignite) before checking/adding to the ignited set, making
the ignited guard dead code; either move the ignited check to the top of the
loop so you skip processing any uid already in ignited (check
ignited.Contains(uid) or try Add(uid) and continue on false) before calling
_flammable.AdjustFireStacks/_flammable.Ignite/_damageable.TryChangeDamage, or
remove the ignited parameter and related set entirely if duplicates cannot occur
(simplify by deleting the ignited logic and its callers). Ensure changes
reference IgniteArea, the ignited parameter/variable, _nearFlammables,
AdjustFireStacks, Ignite, and TryChangeDamage so duplicate-processing prevention
is applied before side effects.
In
`@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderGuardianLeashSystem.cs`:
- Around line 59-63: Hardcoded Russian message passed to _popup.PopupEntity in
TerrorSpiderGuardianLeashSystem should use localization: replace the string
literal "Вы слишком далеко! Вернитесь к принцессе или королеве." with
Loc.GetString("terror-spider-guardian-leash-too-far") (or another descriptive
key) in the PopupEntity call, and add that key and its Russian value to the
appropriate localization resource file; ensure you import/resolve Loc where
needed and keep the PopupType.SmallCaution argument unchanged.
- Around line 67-98: The two identical search loops in IsNearAliveRoyal that
iterate EntityQueryEnumerator for TerrorSpiderPrincessComponent and
TerrorSpiderQueenComponent should be consolidated into a generic helper (e.g., a
private bool IsAnyNearbyAlive<TComponent>(MapCoordinates guardPos, float
maxRangeSquared, EntityUid guard) where TComponent : IComponent) that performs
the query, checks uid != guard, MobStateComponent.CurrentState ==
MobState.Alive, same MapId, and distance squared <= maxRangeSquared; then
replace the two explicit loops with calls to
IsAnyNearbyAlive<TerrorSpiderPrincessComponent>(...) and
IsAnyNearbyAlive<TerrorSpiderQueenComponent>(...) from IsNearAliveRoyal (keep
the existing IsNearAliveRoyal signature and behavior).
- Around line 100-103: Remove the dead IsTerrorSpider method and its only-use
dependencies: delete the IsTerrorSpider method and remove the _lookup and _tag
fields from the class since they are only referenced by that dead method;
replace the hard-coded Russian string "Вы слишком далеко! Вернитесь к принцессе
или королеве." with a call to Loc.GetString using a new localization key (e.g.
"terror-spider-too-far"), and update the call site to use
Loc.GetString("terror-spider-too-far"); finally, refactor IsNearAliveRoyal by
extracting the duplicated princess and queen entity-query/validation logic into
a single helper (e.g. TryGetNearbyAliveRoyal or IsNearbyAliveRoyalOfType) and
call that helper for both princess and queen to remove the duplicate blocks.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderHealerSystem.cs`:
- Around line 140-151: OnBeforeSpiderDamageChanged currently bypasses the healer
cooldown by calling ApplyHealAllDamageTypes directly; change it to call
TryApplyTouchHeal (the existing helper that checks NextTouchHealTime) and only
apply the heal there. Specifically, after computing amount with
GetTouchHealAmount(healer), replace the direct
ApplyHealAllDamageTypes(ent.Owner, amount) invocation with a call to
TryApplyTouchHeal(healer, ent.Owner, amount) (or the existing TryApplyTouchHeal
signature in this class), so the NextTouchHealTime check is respected before
healing.
- Around line 153-170: OnMeleeHit currently calls TryApplyTouchHeal for each
target but the cooldown (NextTouchHealTime) is enforced inside
TryApplyTouchHeal, so the first successful heal blocks subsequent targets in the
same swing; move the cooldown check out of the per-target loop: after obtaining
the TerrorSpiderHealerComponent and amount (GetTouchHealAmount), check
healer.NextTouchHealTime vs the current time and return early if still on
cooldown, otherwise iterate HitEntities and call a version of TryApplyTouchHeal
that does not set NextTouchHealTime (or remove the cooldown write from
TryApplyTouchHeal), and after processing all eligible targets set
healer.NextTouchHealTime once to now + cooldown; update TryApplyTouchHeal (or
its call sites) accordingly so the cooldown is only applied once per melee hit.
- Around line 185-205: In ApplyHealAllDamageTypes remove the first foreach that
iterates damageable.DamagePerGroup.Keys (the loop starting at the line that
references DamagePerGroup) because it doesn't apply healing; instead only
iterate over damageable.Damage.DamageDict.Keys to populate heal.DamageDict with
-amount for each damageType, leaving the existing heal.Empty check and the
_damageable.TryChangeDamage call intact.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderLurkerSystem.cs`:
- Around line 82-106: The OnWebStartCollide handler can process the same entity
pair multiple times when there are multiple fixtures; add a fixture-ID check at
the start of OnWebStartCollide to skip events for non-matching fixtures (compare
args.OurFixtureId to ent.Comp.FixtureId and return when they differ) so
TakeStaminaDamage and TryAddStatusEffect<MutedComponent> are only applied once
per relevant fixture; ensure you reference TerrorSpiderLurkerWebAreaComponent
via ent.Comp.FixtureId and keep the rest of the existing logic unchanged.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs`:
- Around line 285-287: OnJellyTerminating is an empty event handler for
EntityTerminatingEvent on TerrorSpiderMotherJellyComponent and should be removed
or implemented; either delete the OnJellyTerminating method and remove its
subscription (the SubscribeComponent/ event registration that references
OnJellyTerminating) to eliminate dead code, or implement the required
termination behavior (e.g., cleanup spawned entities, release held resources,
decrement any mother/jelly counters, and notify parent/system) inside
OnJellyTerminating so it performs those teardown steps for
TerrorSpiderMotherJellyComponent before the entity is removed.
- Around line 289-310: In OnAnyMeleeHit, multiple
TerrorSpiderMotherJellyComponent hits iterate args.HitEntities and repeatedly
overwrite the TerrorSpiderMotherRegenBuffComponent on args.User with the last
jelly's values; to fix, after creating/updating the buff
(EnsureComp<TerrorSpiderMotherRegenBuffComponent> and assigning HealPerTick,
TickInterval, NextTick, ExpiresAt) and deleting the target (Del(target)), stop
further processing by adding a break so only the first jelly applies;
alternatively, if intended to combine effects, implement accumulation logic that
adjusts buff values instead of replacing them.
- Around line 68-93: This iterates all DamageableComponent entities
(EntityQueryEnumerator<DamageableComponent, TransformComponent> in
TerrorSpiderMotherSystem) causing severe perf issues; change the logic to
perform a spatial query using EntityLookupSystem.GetEntitiesInRange centered on
motherPos with radius comp.AuraHalfRange, then for each returned entity use
TryComp<DamageableComponent> and TryComp<TransformComponent> (or Resolve) to run
the existing checks (same-map, distance bounds, IsTerrorSpider,
MobStateComponent check) and apply healing or poison via
ApplyHealAllDamageTypes/ _damageable.TryChangeDamage; apply the same replacement
in the OnPulseAction path that currently repeats the global enumeration so both
code paths use GetEntitiesInRange and per-entity component lookups instead of
EntityQueryEnumerator.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderPrincessSystem.cs`:
- Around line 255-263: The EMP pulse is applied to silicon entities without
checking if the target is a terror spider—mirror TerrorSpiderQueenSystem's
behavior by guarding the EMP block with a check for !IsTerrorSpider(uid). Wrap
the HasComp<SiliconLawBoundComponent>(uid) / _emp.EmpPulse(...) call inside an
additional condition that verifies the entity is not a terror spider (using
IsTerrorSpider(uid)), so the _emp.EmpPulse(xform.Coordinates, 0.2f,
ent.Comp.ScreamEmpEnergyConsumption,
TimeSpan.FromSeconds(ent.Comp.ScreamRobotDisableSeconds), ent.Owner) only runs
for non-terror-sprider silicons.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`:
- Around line 484-503: The EMP pulse is currently applied to any entity with
SiliconLawBoundComponent even when the entity is a Terror Spider; wrap the EMP
block so it only runs for non-TerrorSpider targets (same condition guarding the
slow/stamina logic) — e.g., check !IsTerrorSpider(uid) before calling
_emp.EmpPulse (or add it to the existing if-block that calls
_movementModStatus.TryUpdateMovementSpeedModDuration and
_stamina.TakeStaminaDamage) so only non-terror spiders with
SiliconLawBoundComponent get the EMP effect.
- Around line 61-70: The Update method currently runs four full EntityQuery
passes each frame (via UpdateRemoteViewMovementLock, UpdateHiveSpeedDebuff,
UpdateEggs, UpdateOrphanDamage), which is expensive; change to an event-driven
approach by removing per-frame queries for stable state changes and only
updating MovementSpeedMod and related properties when the underlying events
occur (e.g., when Eye.Target changes, when a Hive is created/modified, or on
explicit relevant component events). Concretely: stop calling
UpdateRemoteViewMovementLock and UpdateHiveSpeedDebuff every tick from Update(),
add listeners/handlers for target-change and hive-created/updated events to
recompute MovementSpeedMod and hive speed debuff for affected entities, and keep
UpdateEggs/UpdateOrphanDamage (or convert them similarly if they also can be
event-driven); ensure existing EntityQuery logic is migrated into those event
handlers so queries run only for affected entities rather than globally each
frame.
- Around line 242-310: Extract the duplicated logic from TerrorSpiderQueenSystem
and TerrorSpiderPrincessSystem into a shared helper (e.g.,
TerrorSpiderRoyalHelpers) and replace the per-system copies with calls into that
helper: move BuildHiveSenseLines and FindNearestBeaconName (and similarly
GetRemoteViewCandidates, IsTerrorSpider, UpdateRemoteViewMovementLock,
UpdateOrphanDamage and the scream-handling logic) into generic or parameterized
methods that accept the differing component types and any variable-specific
behavior as type parameters or delegates (for example
BuildHiveSenseLines<TBroodComp>(EntityUid queen, Func<EntityUid,TBroodComp?>
compResolver, ... ) where TBroodComp is TerrorSpiderQueenBroodComponent or
TerrorSpiderPrincessBroodComponent); update TerrorSpiderQueenSystem and
TerrorSpiderPrincessSystem to call
TerrorSpiderRoyalHelpers.BuildHiveSenseLines<TBroodComp>(...) and use the same
shared FindNearestBeaconName implementation, ensuring you reference the original
symbols (TerrorSpiderQueenBroodComponent, TerrorSpiderPrincessBroodComponent,
BuildHiveSenseLines, FindNearestBeaconName) so the logic is identical but
centralized.
In
`@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderReaperLifestealSystem.cs`:
- Around line 27-30: Переменная attacker всегда перезаписывается — текущее
присвоение attacker = args.User бесполезно, потому что сразу выполняется if
(attacker != ent.Owner) attacker = ent.Owner; Исправь логику в методе
(TerrorSpiderReaperLifestealSystem): либо удаляй начальное attacker = args.User,
либо (лучше) менять условие на проверку на null/пустое значение (например if
(attacker == null) attacker = ent.Owner) чтобы присваивать ent.Owner только
когда args.User отсутствует; проверь использование args.User, ent.Owner и
переменной attacker и обнови условие соответственно.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderRoyalSystem.cs`:
- Around line 96-98: Создайте объект DamageSpecifier один раз до цикла и
переиспользуйте его для всех целей вместо создания внутри foreach: вынесите
переменную damage = new DamageSpecifier(); и заполнение
damage.DamageDict[ent.Comp.StompDamageType] =
FixedPoint2.New(ent.Comp.StompDamage) перед циклом, затем просто вызывайте
_damageable.TryChangeDamage(uid, damage, ...) для каждой цели; убедитесь, что
никакой код в цикле не модифицирует damage, чтобы избежать побочных эффектов.
- Around line 59-80: Замените полный перебор через
EntityQueryEnumerator<TransformComponent> на пространственный поиск
EntityLookupSystem.GetEntitiesInRange(origin.MapId, origin.Position, radius) и
собирайте цели только из возвращённого перечисления; в проверках по-прежнему
фильтруйте SpiderComponent и TerrorSpiderWebBuffReceiverComponent, проверяйте
TryComp<MobStateComponent>(uid, out var mobState) && mobState.CurrentState ==
MobState.Alive, и добавляйте uid в targets как сейчас. Кешируйте MapPosition в
локальную переменную вместо повторного обращения к xform.MapPosition в одной
итерации, а также создайте единый экземпляр DamageSpecifier (те же параметры,
что раньше) до цикла и переиспользуйте его для всех целей вместо создания нового
для каждой цели. Убедитесь, что используете origin, radiusSquared/radius и
названия компонентов/переменных из текущего файла (TransformComponent,
SpiderComponent, TerrorSpiderWebBuffReceiverComponent, MobStateComponent,
MobState.Alive, targets).
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderWidowSystem.cs`:
- Around line 46-60: Current code scans all BloodstreamComponent each tick
causing perf issues; change the system to track only poisoned entities by
introducing/using a marker component TerrorVenomComponent and iterating over
that instead. In AddVenomToTarget ensure you Add the TerrorVenomComponent to the
target when venom is applied (if absent), and in the ticking logic in
TerrorSpiderWidowSystem replace EntityQueryEnumerator<BloodstreamComponent>()
with EntityQueryEnumerator<TerrorVenomComponent>() and read venom amount from
the target's bloodstream via _solution.GetTotalPrototypeQuantity(uid,
"BlackTerrorVenom"), removing the TerrorVenomComponent when venomAmount <= 0 so
the entity is no longer iterated. Ensure _damageable.TryChangeDamage stays the
same and any cleanup (removal of marker) happens reliably.
- Around line 57-58: Replace the hard-coded "Toxin" damage type literal in
TerrorSpiderWidowSystem (where DamageSpecifier.DamageDict["Toxin"] is assigned)
with a single reusable constant; add a private const string ToxinDamage =
"Toxin" (or a private static readonly ProtoId<DamageTypePrototype> ToxinDamage =
new("Toxin") if following ProtoId usage in the project) at the top of the class
and use that constant in the DamageDict assignment instead of the literal to
match project style and improve consistency.
- Line 49: The code uses the hardcoded reagent id "BlackTerrorVenom" when
calling _solution.GetTotalPrototypeQuantity(uid, "BlackTerrorVenom"), which
breaks configurability; instead read the actual VenomReagent string from the
relevant component (e.g. TerrorSpiderWidowMeleeDebuffComponent or
TerrorSpiderWidowWebAreaComponent attached to the spider) or from the target's
BloodstreamComponent and pass that variable into
_solution.GetTotalPrototypeQuantity (and into AddVenomToTarget which already
accepts reagentId); if the reagent id must be cached, save the component's
VenomReagent during spider initialization and use that stored value rather than
the literal.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderCocoonComponent.cs`:
- Line 19: Поле ActionEntity в компоненте TerrorSpiderCocoonComponent является
рантайм-состоянием (устанавливается через вызов _actions.AddAction(uid, ref
comp.ActionEntity, ...)) и должно быть помечено атрибутом [ViewVariables] вместо
отсутствующего атрибута; откройте класс TerrorSpiderCocoonComponent и добавьте
атрибут [ViewVariables] над объявлением public EntityUid? ActionEntity, чтобы
отражать, что это не конфигурируемое через YAML поле.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderGuardianShieldBarrierComponent.cs`:
- Line 1: Удалите неиспользуемый директиву using
Robust.Shared.Serialization.TypeSerializers.Implementations.Custom из файла
TerrorSpiderGuardianShieldBarrierComponent.cs — поле PassTag в компоненте
(свойство/поле PassTag) является обычной string и не использует кастомные
сериализаторы, так что просто уберите этот using из верха файла.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightGuardComponent.cs`:
- Line 16: The field ActionEntity on TerrorSpiderKnightGuardComponent is runtime
state and missing the [ViewVariables] attribute; add [ViewVariables] above the
public EntityUid? ActionEntity declaration (preserving its nullable type) so it
is exposed as a view variable per project conventions.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightRageComponent.cs`:
- Line 16: Поле ActionEntity в компоненте TerrorSpiderKnightRageComponent
является runtime-состоянием и должно быть помечено атрибутом [ViewVariables];
добавьте [ViewVariables] перед объявлением поля ActionEntity в классе
TerrorSpiderKnightRageComponent (аналогично TerrorSpiderKnightGuardComponent)
чтобы обеспечить видимость состояния в отладочных/инспекторных инструментах.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderWebBuffReceiverComponent.cs`:
- Around line 9-11: Поле WebContacts в компоненте
TerrorSpiderWebBuffReceiverComponent помечено как [DataField], но оно является
runtime-счётчиком коллизий и не должно быть конфигурируемым; замените атрибут
[DataField] на [ViewVariables], оставив [AutoNetworkedField] для сетевой
синхронизации, чтобы поле отображалось как runtime view-only/inspectable
состояние и не считалось YAML-конфигурируемым; обновите только атрибуты над
полем WebContacts, не меняя имени поля или логики обнуления в MapInit и
управления через StartCollide/EndCollide.
In
`@Content.Shared/Imperial/TerrorSpider/Events/TerrorSpiderCocoonWrappedEvent.cs`:
- Around line 3-14: Make the three event fields immutable by marking User,
Target and Cocoon as readonly on the TerrorSpiderCocoonWrappedEvent class so
they can only be set in the constructor; update the declarations of the fields
(User, Target, Cocoon) to readonly while leaving the constructor assignments in
the TerrorSpiderCocoonWrappedEvent(EntityUid user, EntityUid target, EntityUid
cocoon) intact.
In `@Content.Shared/Imperial/TerrorSpider/Events/TerrorSpiderHealerEvents.cs`:
- Line 3: Remove the unused using for PrototypeIdSerializer: delete the import
of Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype
from TerrorSpiderHealerEvents.cs because the code uses EntProtoId with a plain
[DataField(required: true)] (no customTypeSerializer). Ensure no other
references to PrototypeIdSerializer remain before committing.
In `@Content.Shared/Imperial/TerrorSpider/Events/TerrorSpiderKnightRageEvents.cs`:
- Line 5: Класс TerrorSpiderKnightRageActionEvent объявлен с точкой с запятой
вместо тела, из‑за чего синтаксическая ошибка; откройте тело класса и замените
окончание `;` на `{ }`, унаследовавшееся от InstantActionEvent, чтобы объявление
стало: public sealed partial class TerrorSpiderKnightRageActionEvent :
InstantActionEvent { } (или добавьте необходимую реализацию/члены внутрь тела
при необходимости).
In `@Content.Shared/Imperial/TerrorSpider/Events/TerrorSpiderQueenEvents.cs`:
- Around line 21-22: The SharedCooldownSeconds field currently defaults to 0f
which allows a zero cooldown when SharedCooldownKey is set; update the
TerrorSpiderQueenEvents class to ensure a nonzero default by initializing
SharedCooldownSeconds to a sensible value (e.g., 1f) or make the field
nullable/required so YAML must supply it; specifically adjust the
DataField-backed float SharedCooldownSeconds (and validate usage with
SharedCooldownKey) so configs cannot silently get a zero-second cooldown.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderArmorSystem.cs`:
- Around line 10-14: Поля приватных статических readonly названы BruteTypes и
BurnTypes, нарушая соглашение об именовании; переименуйте их в _bruteTypes и
_burnTypes и обновите все использования этих полей в файле (включая любые ссылки
в методах или условиях), чтобы ссылаться на новые имена; убедитесь, что
сигнатуры и модификаторы (private static readonly) остаются без изменений.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightGuardSystem.cs`:
- Around line 55-101: The OnGuardAction handler should validate GuardDuration
before enabling guard mode: check ent.Comp.GuardDuration is > 0 (or a minimum
threshold) and return early if not, so you don't set ent.Comp.IsGuarding or
ent.Comp.GuardEndTime when the duration is zero/negative; update the code paths
around OnGuardAction, GuardDuration, GuardEndTime (and where IsGuarding is set)
to perform this check and avoid caching/modifying components when the duration
is invalid.
- Around line 89-97: The code caches armor modifiers only when a
TerrorSpiderArmorComponent exists but doesn't record whether caching actually
occurred, so EndGuard may zero real modifiers if the armor component was added
mid-guard; add a boolean flag on the guard state (e.g., ArmorModifiersCached or
HasCachedArmorModifiers on the same struct/class as
CachedBruteModifier/CachedBurnModifier) and set it to true only inside the
TryComp<TerrorSpiderArmorComponent> branch when you save armor.BruteModifier and
armor.BurnModifier, then in EndGuard check that flag before restoring
armor.BruteModifier/armor.BurnModifier and clear the flag after restoring; also
ensure the flag is cleared if the armor component was never present or is
removed during guard.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`:
- Around line 133-136: Строковый литерал "Piercing" в вызовах SetDamageValue и в
самом методе используется хардкодом и может сломаться при переименовании типов
урона; замените этот литерал на типобезопасную переменную: добавьте либо public
const string PiercingDamageType в соответствующем компоненте/классе либо
DataField<string> PiercingDamageType в компоненте прототипа и используйте эту
константу/поле вместо прямой строки в SetDamageValue (и во всех местах, где
вызывается SetDamageValue), гарантируя что ключ берётся из единого места и
обновляется вместе с прототипами урона.
- Around line 66-71: The MeleeWeaponComponent damage is being modified to add a
"Piercing" entry without clearing existing types, causing Blunt/Slash to remain;
in the SharedTerrorSpiderKnightRageSystem where
TryComp<MeleeWeaponComponent>(ent.Owner, out var melee) is used, clear
melee.Damage (e.g., replace with an empty DamageSpecifier) before calling
SetDamageValue for "Piercing", mirror the approach used for
PassiveDamageComponent, ensure ent.Comp.CachedMeleeDamage still stores the
original DamageSpecifier and call Dirty(ent.Owner, melee) after the replacement
so EndRage can restore the full original damage correctly.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderWebBuffSystem.cs`:
- Around line 48-54: The while loop updating comp.NextRegenTick can spin forever
if comp.RegenInterval <= 0; update the loop in the
SharedTerrorSpiderWebBuffSystem so it first guards against non-positive
intervals (check comp.RegenInterval <= 0) and handle it by either breaking out
of the loop (or setting a sane default interval) before applying regen, ensuring
comp.NextRegenTick is advanced to avoid infinite iteration; reference the
variables comp.NextRegenTick and comp.RegenInterval in your change.
In `@Resources/Audio/Imperial/Seriozha/SCP/096/attributions.yml`:
- Line 1: Файл начинается с BOM (невидимый символ «\uFEFF») перед строкой
starting with files: ["nda096rage.ogg"]; откройте
Resources/Audio/Imperial/Seriozha/SCP/096/attributions.yml (строка начинающаяся
с files: ["nda096rage.ogg"]) и пересохраните его в кодировке UTF-8 без BOM
(уберите ведущий BOM-символ), затем проверьте, что первый символ файла теперь —
буква 'f' из "files" и что YAML парсится корректно.
- Around line 2-4: В файле атрибуции для SCP-096 уточните, какие конкретно
аудиотреки используются и их реальные лицензии: проверьте метаданные .ogg,
оригинальные файлы игры/редакции и источники (например, авторские страницы Kevin
MacLeod и т.п.), затем обновите поля "license", "copyright" и "source" для
SCP-096 так, чтобы каждый трек указывал фактического автора/правообладателя и
лицензию (если отличаются от CC-BY-SA-3.0), либо добавьте отдельную запись/поле
с перечислением треков и их индивидуальных лицензий; убедитесь, что атрибуция
"Trill Team" сохраняется только если они действительно являются правообладателем
конкретного файла.
In `@Resources/Audio/Imperial/Seriozha/SCP/173/attributions.yml`:
- Around line 1-24: Update the copyright attribution entries for the listed
files (e.g., "NeckSnap1.ogg", "NeckSnap2.ogg", "NeckSnap3.ogg", "Rattle1.ogg",
"Rattle2.ogg", "Rattle3.ogg") so the copyright holder reflects the original
creator Joonas "Regalis" Rikkonen / Undertow Games instead of "Trill Team"; keep
license "CC-BY-SA-3.0" unchanged and preserve the scpcbgame.ru source URL, and
if you want to acknowledge Trill Team's role, add a separate note (not as
copyright) indicating they performed the Russian voice conversion/localization.
In `@Resources/Prototypes/Alerts/alerts.yml`:
- Around line 42-70: The Sanity alert (type: alert, id: Sanity) currently reuses
the battery sprite (sprite: /Textures/Interface/Alerts/battery.rsi with states
battery0–battery10) which conflicts visually with SCPFiremanPoints; update the
Sanity alert to use a distinct, thematic sprite (for example a head/brain icon)
by replacing the sprite path and state names with the new .rsi asset and its
unique states (or add a new .rsi file with brain0–brain10 states), ensuring
minSeverity/maxSeverity mapping remains 0–10 and the icon state names in the
Sanity block match the new asset so the HUD can visually distinguish Sanity from
SCPFiremanPoints.
In `@Resources/Prototypes/Imperial/Seriozha/SCP/Fredik21/scp.yml`:
- Around line 4102-4104: Слой визуалов "enum.TriggerVisualLayers.Base" добавлен
к сущности с sprite Imperial/Seriozha/Fredik21/nda/nda4975.rsi и state: alive,
но нет контроллера, который бы переключал этот слой, из‑за чего происходит
двойная отрисовка; исправьте каждый такой блок (например тот с sprite
nda4975.rsi и аналогичные на 5098–5099, 7273–7275, 8908–8910, 8984–8986) либо
добавив соответствующий визуализатор (например добавить компонент
TimerTriggerVisuals или GenericVisualizer в описание сущности и настроить его на
переключение enum.TriggerVisualLayers.Base), либо убрать/сделать невидимым слой
по умолчанию (удалить enum.TriggerVisualLayers.Base из map или заменить на
скрытый флаг), чтобы предотвратить дублирование спрайта.
- Around line 7485-7503: Remove the redundant DamageStateVisuals entry so two
systems don't write the same visual layer; specifically delete the component
instance named DamageStateVisuals from the entity definition (the entry
referencing enum.DamageStateVisualLayers.Base), leaving the existing
GenericVisualizer (which maps enum.SCP096Visuals.State →
enum.DamageStateVisualLayers.Base) to control the Calm/Screaming/Chasing/Dead
states produced by SCP096RageOnLookSystem; ensure no other references to
MobStateVisuals.State / MobStateComponent remain that would reintroduce the
duplicate writer.
In `@Resources/Textures/Imperial/Seriozha/Fredik21/nda/nda173.rsi/meta.json`:
- Around line 3-4: В файле meta.json заполните поля "license" и "copyright"
(вместо текущих null) в соответствии с остальными файлами этого автора — либо
укажите конкретную лицензию и имя/организацию правообладателя, как в соседних
nda020.rsi/nda096.rsi, либо используйте ICLA, если этот автор в каталоге помечен
через ICLA; убедитесь, что значения строковые и формат совпадает с примерами в
соседних meta.json.
In `@Resources/Textures/Imperial/TerrorSpider/act.rsi/meta.json`:
- Around line 3-4: Замените в meta.json текущее значение поля "copyright"
(ссылка на репозиторий) на подробное указание источника в формате, принятом в
проекте: укажите, что ассет взят из Paradise (указать либо полный путь к файлу в
репозитории, напр. blob/master/... или конкретный хеш коммита), опишите
выполненные модификации (если были) и укажите директорию источника (особенно
важно для ассетов из icons/goonstation или sound/goonstation), при этом оставьте
поле "license": "CC-BY-SA-3.0" без изменений если лицензия подтверждена;
используйте пример формата в комментарии для ориентира (например: "Taken from
Paradise station https://github.com/ParadiseSS13/Paradise/blob/master/<path> and
modified by <your name>, <description>").
---
Duplicate comments:
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderPrincessSystem.cs`:
- Around line 441-541: These four methods are duplicated from
TerrorSpiderQueenSystem; extract shared logic into a single reusable component
or system (e.g., TerrorSpiderSharedSystem or TerrorSpiderUtilities) containing
BuildHiveSenseLines, FindNearestBeaconName, GetRemoteViewCandidates, and
IsTerrorSpider, move their implementations there, and have both
TerrorSpiderPrincessSystem and TerrorSpiderQueenSystem call into or inject that
shared system instead of duplicating code; ensure the shared system has access
to the same helpers/dependencies used in the implementations
(MetaData/Transform/TryComp/_tagSystem/EntityQueryEnumerator) and update both
systems to use the new shared API.
| [DataField] | ||
| public EntityUid? ActionEntity; |
There was a problem hiding this comment.
ActionEntity помечен [DataField] вместо [ViewVariables].
Поле ActionEntity устанавливается в рантайме системой (_actions.AddAction), а не конфигурируется через YAML-прототипы. По принятому в проекте соглашению такие поля должны иметь [ViewVariables]. Кроме того, это создаёт несоответствие с TerrorSpiderCocoonComponent.ActionEntity, у которого вообще нет атрибута.
🛠️ Предлагаемое исправление
- [DataField]
- public EntityUid? ActionEntity;
+ [ViewVariables]
+ public EntityUid? ActionEntity;Based on learnings: в проекте imperial-space/SS14-public поля компонентов, являющиеся рантайм-состоянием, должны иметь [ViewVariables].
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| [DataField] | |
| public EntityUid? ActionEntity; | |
| [ViewVariables] | |
| public EntityUid? ActionEntity; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/SCP/SCP173/Components/SCP173LightFlickerComponent.cs`
around lines 24 - 25, Поле SCP173LightFlickerComponent.ActionEntity помечено как
[DataField], но это runtime-состояние (устанавливается через _actions.AddAction)
и должно быть помечено как [ViewVariables] в соответствии с проектным
соглашением; замените атрибут [DataField] на [ViewVariables] (или удалите
[DataField] и добавьте [ViewVariables]) чтобы убрать сериализацию и привести в
соответствие с реализацией TerrorSpiderCocoonComponent.ActionEntity.
| [Dependency] private readonly EntityLookupSystem _lookup = default!; | ||
| [Dependency] private readonly SharedPointLightSystem _pointLight = default!; | ||
| [Dependency] private readonly SharedTransformSystem _transform = default!; | ||
| [Dependency] private readonly Robust.Shared.Timing.IGameTiming _timing = default!; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Использовать using-директиву вместо полного имени типа IGameTiming.
Robust.Shared.Timing.IGameTiming задан как полностью квалифицированное имя прямо в поле зависимости, тогда как все остальные зависимости в файле используют краткие имена через using. Нужно добавить using Robust.Shared.Timing; в начало файла.
🛠️ Предлагаемое исправление
using Content.Server.Actions;
using Content.Server.Imperial.SCP.SCP173.Components;
using Content.Shared.Imperial.SCP.SCP173;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
+using Robust.Shared.Timing;
namespace Content.Server.Imperial.SCP.SCP173.Systems;
...
- [Dependency] private readonly Robust.Shared.Timing.IGameTiming _timing = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| [Dependency] private readonly Robust.Shared.Timing.IGameTiming _timing = default!; | |
| using Content.Server.Actions; | |
| using Content.Server.Imperial.SCP.SCP173.Components; | |
| using Content.Shared.Imperial.SCP.SCP173; | |
| using Robust.Server.GameObjects; | |
| using Robust.Shared.Audio.Systems; | |
| using Robust.Shared.Timing; | |
| namespace Content.Server.Imperial.SCP.SCP173.Systems; | |
| [Dependency] private readonly IGameTiming _timing = default!; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/SCP/SCP173/Systems/SCP173LightFlickerSystem.cs` at
line 16, Replace the fully qualified type on the dependency field with the short
type by adding a using for Robust.Shared.Timing and updating the field
declaration; specifically add "using Robust.Shared.Timing;" at the top of the
file and change the field declaration that references
Robust.Shared.Timing.IGameTiming to use IGameTiming (the _timing dependency
field).
| if (!TryComp<PointLightComponent>(lightUid, out var pointLight)) | ||
| continue; | ||
|
|
||
| if (!TryComp<TransformComponent>(lightUid, out var lightXform) || lightXform.MapID != map) | ||
| continue; | ||
|
|
||
| if (!ent.Comp.CapturedLightStates.ContainsKey(lightUid)) | ||
| ent.Comp.CapturedLightStates[lightUid] = pointLight.Enabled; | ||
|
|
||
| var distance = (_transform.GetWorldPosition(lightUid) - origin).Length(); | ||
| if (distance > ent.Comp.Radius) | ||
| continue; | ||
|
|
||
| var targetEnabled = turnOn ? ent.Comp.CapturedLightStates[lightUid] : false; | ||
| _pointLight.SetEnabled(lightUid, targetEnabled, pointLight); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Состояние огней захватывается до проверок карты и дистанции — лишние записи в CapturedLightStates.
В ApplyLightState строки 98–99 добавляют сущность в CapturedLightStates до того, как выполняются проверки lightXform.MapID != map (строка 95) и distance > ent.Comp.Radius (строка 102). В результате огни, которые отсеялись бы этими фильтрами, всё равно попадают в словарь, но никогда не переключаются. RestoreLights восстановит им то же значение, что было — поведенчески безопасно, но загрязняет словарь и добавляет лишние итерации.
Рекомендуется перенести захват состояния после обеих проверок.
🛠️ Предлагаемое исправление
foreach (var lightUid in _lookup.GetEntitiesInRange(ent, ent.Comp.Radius, LookupFlags.Dynamic | LookupFlags.Static))
{
if (!TryComp<PointLightComponent>(lightUid, out var pointLight))
continue;
if (!TryComp<TransformComponent>(lightUid, out var lightXform) || lightXform.MapID != map)
continue;
- if (!ent.Comp.CapturedLightStates.ContainsKey(lightUid))
- ent.Comp.CapturedLightStates[lightUid] = pointLight.Enabled;
-
var distance = (_transform.GetWorldPosition(lightUid) - origin).Length();
if (distance > ent.Comp.Radius)
continue;
+ if (!ent.Comp.CapturedLightStates.ContainsKey(lightUid))
+ ent.Comp.CapturedLightStates[lightUid] = pointLight.Enabled;
+
var targetEnabled = turnOn ? ent.Comp.CapturedLightStates[lightUid] : false;
_pointLight.SetEnabled(lightUid, targetEnabled, pointLight);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/SCP/SCP173/Systems/SCP173LightFlickerSystem.cs`
around lines 92 - 106, In ApplyLightState, move the capture into
CapturedLightStates so it runs only after the map and distance checks: first
ensure TryComp<PointLightComponent>(lightUid, out var pointLight) and
TryComp<TransformComponent>(lightUid, out var lightXform) with lightXform.MapID
== map, compute distance with _transform.GetWorldPosition(lightUid) and ensure
distance <= ent.Comp.Radius, and only then set
ent.Comp.CapturedLightStates[lightUid] = pointLight.Enabled (so RestoreLights
and future toggles only store states for relevant lights); update references to
CapturedLightStates, ApplyLightState, TransformComponent, PointLightComponent,
_transform, and _pointLight.SetEnabled accordingly.
| [DataField("owner")] | ||
| public EntityUid FireOwner; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n "SCPFireSpreadComponent|FireOwner" --type=cs -C3Repository: imperial-space/SS14-public
Length of output: 15243
FireOwner должен быть установлен до первого использования в UpdateSpreadFires().
В методе UpdateSpreadFires() (lines 355-415) поле FireOwner используется без проверки на EntityUid.Invalid в нескольких местах:
- Line 356:
AddPoints(comp.FireOwner, 1f, fireman) - Line 363:
HealOwner(comp.FireOwner, comp.HealPerType) - Line 369, 414:
IgniteEntitiesInRadius(..., comp.FireOwner) - Line 415:
SpawnFireAtMap(..., comp.FireOwner)
Хотя основной путь спавна (line 550-556) корректно устанавливает это поле сразу после создания компонента, наличие [DataField("owner")] позволяет инициализировать компонент из YAML с невалидным значением. Добавьте проверку if (ent.Comp.FireOwner == EntityUid.Invalid) return; в начале UpdateSpreadFires() или убедитесь, что компонент никогда не создаётся без установки владельца.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/SCP/SCPFireman/Components/SCPFireSpreadComponent.cs`
around lines 6 - 7, UpdateSpreadFires uses the FireOwner field without
validating it; add a guard at the start of UpdateSpreadFires to bail out if
ent.Comp.FireOwner == EntityUid.Invalid (or otherwise ensure FireOwner is set on
component construction), so subsequent calls to AddPoints, HealOwner,
IgniteEntitiesInRadius and SpawnFireAtMap receive a valid owner; alternatively
enforce non-null owner assignment when the SCPFireSpreadComponent is created so
FireOwner can never be invalid.
| Timer.Spawn(phaseSeconds * 1000, () => | ||
| { | ||
| var centcommHermetics = CloseCentcommHermeticsAndEnableGodmode(targetMap.Value); | ||
|
|
||
| TriggerExplosionSpawners(targetMap.Value); | ||
|
|
||
| SetMapAmbientColor(mapUid, Color.FromHex("#000000")); | ||
| SetAllPointLightsColor(targetMap.Value, Color.White); | ||
| UnboltAirlocks(protocolAirlocks); | ||
| DisableGodmode(centcommHermetics); | ||
| }); |
There was a problem hiding this comment.
Отсутствует проверка существования карты в отложенном callback'е.
Через 125 секунд карта (mapUid) может быть удалена. Вызов SetMapAmbientColor → EnsureComponent<MapLightComponent>(mapUid) на удалённой сущности приведёт к исключению. Остальные методы (CloseCentcommHermeticsAndEnableGodmode, TriggerExplosionSpawners) тоже будут работать с невалидным состоянием.
Необходимо добавить guard-проверку в начале callback'а.
Предлагаемое исправление
Timer.Spawn(phaseSeconds * 1000, () =>
{
+ if (!_entManager.EntityExists(mapUid))
+ return;
+
var centcommHermetics = CloseCentcommHermeticsAndEnableGodmode(targetMap.Value);
TriggerExplosionSpawners(targetMap.Value);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@Content.Server/Imperial/Seriozha/Administration/Commands/NedraNukeProtocolCommand.cs`
around lines 102 - 112, The delayed Timer.Spawn callback uses
mapUid/targetMap.Value without ensuring the map/entity still exists and can
throw after ~125s; add a guard at the top of the callback (before calling
CloseCentcommHermeticsAndEnableGodmode, TriggerExplosionSpawners,
SetMapAmbientColor, SetAllPointLightsColor, UnboltAirlocks, DisableGodmode) that
verifies the map is still valid (e.g., targetMap.HasValue && the map/entity
exists via your MapManager/EntityManager API or
MapManager.IsValidMap(targetMap.Value)) and return early if the check fails.
| license: "CC-BY-SA-3.0" | ||
| copyright: "Trill Team, Converted from mp3 to .ogg" | ||
| source: "https://scpcbgame.ru/" |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
scpcbgame.ru license audio content CC-BY-SA
💡 Result:
- scpcbgame.ru (сам сайт): в футере указано, что контент сайта распространяется по лицензии Creative Commons Attribution‑ShareAlike 3.0 (CC BY‑SA 3.0). [1]
- Оригинальная SCP – Containment Breach (игра): официальный сайт игры указывает, что игра под той же лицензией Creative Commons, что и SCP Wiki (CC BY‑SA 3.0), и её можно использовать при соблюдении условий лицензии (атрибуция + share‑alike). [2]
- Аудио/музыка (важное уточнение): часть музыкальных треков в SCP:CB — это работы сторонних авторов (например, Kevin MacLeod, FireFox и др.), то есть у конкретных треков могут быть свои исходные лицензии/условия, и их лучше лицензировать/атрибутировать по треку, а не “как всё CC BY‑SA”. [3]
Источники: [1] [2] [3]
🌐 Web query:
scpcbgame.ru attribution copyright licensing terms
💡 Result:
scpcbgame.ru (русскоязычный сайт SCP – Containment Breach) указывает в футере страниц, что контент сайта распространяется по лицензии Creative Commons Attribution‑ShareAlike 3.0 (CC BY‑SA 3.0). [1][2]
Что это обычно означает для использования/перепубликации материалов с сайта (тексты, страницы, и т.п.) по CC BY‑SA 3.0:
- Attribution (BY): нужно указать авторство/источник (разумным способом), сохранить/добавить уведомление о лицензии и дать ссылку на лицензию. [1][3]
- ShareAlike (SA): если вы публикуете изменённую/производную версию, её нужно распространять под той же (или совместимой) лицензией. [3]
- Нельзя накладывать дополнительные юридические/технические ограничения поверх прав, которые даёт лицензия. [3]
Про саму игру SCP – Containment Breach: официальный сайт игры также указывает, что страница (и в целом проект) привязаны к CC BY‑SA 3.0, и что игра “под той же Creative Commons лицензией, что и SCP Wiki”. [4] Сам SCP‑контент на SCP Wiki лицензируется под CC BY‑SA 3.0. [3]
Важно: у отдельных элементов могут быть исключения/отдельные правообладатели (часто упоминают исключения по SCP‑173), поэтому для “железобетонной” чистоты прав при коммерческом использовании стоит отдельно проверять состав конкретной сборки/ассетов. [5]
Источники:
[1] scpcbgame.ru — “Ultimate Edition Reborn” (футер: CC BY‑SA 3.0)
[2] scpcbgame.ru — “Об игре” (футер: CC BY‑SA 3.0)
[3] SCP Wiki — Image Use Policy (лицензия SCP Wiki: CC BY‑SA 3.0 и базовые условия BY/SA)
[4] scpcbgame.com — Info (CC BY‑SA 3.0; “same license as the SCP Wiki”)
[5] Wikipedia — SCP – Containment Breach (лицензия CC BY‑SA 3.0, с отмеченными исключениями)
🌐 Web query:
SCP Containment Breach Russian fan site scpcbgame.ru license
💡 Result:
The Russian fan site scpcbgame.ru states in its footer (both on the homepage and the “Об игре” page) that content on the site is distributed under the Creative Commons Attribution‑ShareAlike 3.0 license (CC BY‑SA 3.0). [1], [2]
For context, the original SCP – Containment Breach game is also released under the same CC BY‑SA 3.0 license, per the official game site. [3]
Implication: if you reuse/adapt material from scpcbgame.ru (that they have rights to license), you must provide attribution and release derivatives under CC BY‑SA 3.0. [1], [2], [4]
Sources:
[1] scpcbgame.ru homepage footer (CC BY‑SA 3.0)
[2] scpcbgame.ru/info footer (CC BY‑SA 3.0)
[3] scpcbgame.com info page (game under CC license)
[4] Creative Commons BY‑SA 3.0 deed (terms: attribution + share‑alike)
Необходимо уточнить лицензию отдельных аудиотреков, которые могут отличаться от общей CC-BY-SA-3.0 сайта.
Хотя scpcbgame.ru действительно указывает CC-BY-SA 3.0 в общей лицензии сайта, веб-источники подтверждают, что в SCP: Containment Breach отдельные аудиотреки часто являются работами третьих авторов (например, Kevin MacLeod, Firefox и т.д.) с собственными лицензиями. Для этих файлов важно:
- Уточнить, являются ли аудиофайлы SCP-096 оригинальными композициями с CC-BY-SA-3.0 или это третьестороннего происхождения с отдельными условиями лицензирования.
- Подтвердить, что атрибуция именно на «Trill Team» соответствует действительному автору/правообладателю этих конкретных файлов.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Audio/Imperial/Seriozha/SCP/096/attributions.yml` around lines 2 -
4, В файле атрибуции для SCP-096 уточните, какие конкретно аудиотреки
используются и их реальные лицензии: проверьте метаданные .ogg, оригинальные
файлы игры/редакции и источники (например, авторские страницы Kevin MacLeod и
т.п.), затем обновите поля "license", "copyright" и "source" для SCP-096 так,
чтобы каждый трек указывал фактического автора/правообладателя и лицензию (если
отличаются от CC-BY-SA-3.0), либо добавьте отдельную запись/поле с перечислением
треков и их индивидуальных лицензий; убедитесь, что атрибуция "Trill Team"
сохраняется только если они действительно являются правообладателем конкретного
файла.
| - files: ["NeckSnap1.ogg"] | ||
| license: "CC-BY-SA-3.0" | ||
| copyright: "Trill Team, Converted from mp3 to .ogg" | ||
| source: "https://scpcbgame.ru/" | ||
| - files: ["NeckSnap2.ogg"] | ||
| license: "CC-BY-SA-3.0" | ||
| copyright: "Trill Team, Converted from mp3 to .ogg" | ||
| source: "https://scpcbgame.ru/" | ||
| - files: ["NeckSnap3.ogg"] | ||
| license: "CC-BY-SA-3.0" | ||
| copyright: "Trill Team, Converted from mp3 to .ogg" | ||
| source: "https://scpcbgame.ru/" | ||
| - files: ["Rattle1.ogg"] | ||
| license: "CC-BY-SA-3.0" | ||
| copyright: "Trill Team, Converted from mp3 to .ogg" | ||
| source: "https://scpcbgame.ru/" | ||
| - files: ["Rattle2.ogg"] | ||
| license: "CC-BY-SA-3.0" | ||
| copyright: "Trill Team, Converted from mp3 to .ogg" | ||
| source: "https://scpcbgame.ru/" | ||
| - files: ["Rattle3.ogg"] | ||
| license: "CC-BY-SA-3.0" | ||
| copyright: "Trill Team, Converted from mp3 to .ogg" | ||
| source: "https://scpcbgame.ru/" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for other SCP:CB related content and attributions
find Resources -name "*attributions.yml" | head -20Repository: imperial-space/SS14-public
Length of output: 1094
🏁 Script executed:
# Look for other SCP-related audio files that might reference SCP:CB or similar sources
rg "scpcbgame" --type yamlRepository: imperial-space/SS14-public
Length of output: 3032
🏁 Script executed:
# Search for references to "Trill Team" in the repository
rg "Trill Team" --type yamlRepository: imperial-space/SS14-public
Length of output: 3765
🏁 Script executed:
# Check if there are other SCP:CB audio entries
rg "SCP.*Containment.*Breach|scpcb" --type yaml -iRepository: imperial-space/SS14-public
Length of output: 7358
🏁 Script executed:
# Look for any documentation or references about audio source locations
rg "scpcbgame\.com|scpcbgame\.ru"Repository: imperial-space/SS14-public
Length of output: 3032
🏁 Script executed:
# Let's look at the specific file mentioned in the review
cat -n "Resources/Audio/Imperial/Seriozha/SCP/173/attributions.yml"Repository: imperial-space/SS14-public
Length of output: 1078
🏁 Script executed:
# Let's also check the parent SCP attributions file to see if there's more context
cat -n "Resources/Audio/Imperial/Seriozha/SCP/attributions.yml" | head -50Repository: imperial-space/SS14-public
Length of output: 3239
🌐 Web query:
SCP Containment Breach original game Joonas Regalis official domain license
💡 Result:
- Official domain (original SCP – Containment Breach by Joonas “Regalis” Rikkonen / Undertow Games):
scpcbgame.com(served aswww.scpcbgame.com). [1] - Game/source license (official): The game and its source code are licensed under Creative Commons Attribution–ShareAlike 3.0 (CC BY-SA 3.0) (per Regalis’ official source repository). [2]
- Upstream SCP setting license (relevant context): The SCP Wiki/Foundation content is also under CC BY‑SA 3.0, which is why derivative works (like the game) commonly use the same license. [3]
Sources: [1] [2] [3]
🌐 Web query:
scpcbgame.ru Russian port SCP Containment Breach Trill Team
💡 Result:
scpcbgame.ru is a Russian-language fan site that hosts localized (“Russian versions”) builds of SCP: Containment Breach, listing downloads for versions like v1.3.11, v1.3.10, v1.3.9, etc., and crediting who did textures/text translation, build/testing, and voice work. In its credits for multiple releases, it specifically lists “Trill Team — Russian voice acting (Русская озвучка)”. [1]
This is separate from the game’s commonly referenced official site for the original release/download, scpcbgame.com. [2]
Separately, Trill Team describes themselves (in a 2022 post) as a team that periodically makes SCP-related releases focused on SCP: Containment Breach for Android devices, indicating they’ve been active around SCP:CB ports/adaptations beyond just PC voice work. [3]
Sources: [1] scpcbgame.ru (Russian versions page), [2] scpcbgame.com (official download page), [3] DTF post by Trill Team (2022).
Исправить атрибуцию авторства — неправильный держатель прав
Аудиофайлы являются производными от оригинальной SCP: Containment Breach (автор Joonas "Regalis" Rikkonen / Undertow Games), но в атрибуции указан "Trill Team" как правообладатель. На самом деле Trill Team отвечали за русскую озвучку на портале scpcbgame.ru — они не являются авторами исходных звуков.
Лицензия CC-BY-SA-3.0 корректна и соответствует оригиналу, однако copyright должен отражать первоначального создателя:
Рекомендуемое исправление
- copyright: "Trill Team, Converted from mp3 to .ogg"
- source: "https://scpcbgame.ru/"
+ copyright: "Joonas 'Regalis' Rikkonen (Undertow Games), Converted from mp3 to .ogg / Russian distribution by scpcbgame.ru"
+ source: "https://scpcbgame.com/"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - files: ["NeckSnap1.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Trill Team, Converted from mp3 to .ogg" | |
| source: "https://scpcbgame.ru/" | |
| - files: ["NeckSnap2.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Trill Team, Converted from mp3 to .ogg" | |
| source: "https://scpcbgame.ru/" | |
| - files: ["NeckSnap3.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Trill Team, Converted from mp3 to .ogg" | |
| source: "https://scpcbgame.ru/" | |
| - files: ["Rattle1.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Trill Team, Converted from mp3 to .ogg" | |
| source: "https://scpcbgame.ru/" | |
| - files: ["Rattle2.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Trill Team, Converted from mp3 to .ogg" | |
| source: "https://scpcbgame.ru/" | |
| - files: ["Rattle3.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Trill Team, Converted from mp3 to .ogg" | |
| source: "https://scpcbgame.ru/" | |
| - files: ["NeckSnap1.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Joonas 'Regalis' Rikkonen (Undertow Games), Converted from mp3 to .ogg / Russian distribution by scpcbgame.ru" | |
| source: "https://scpcbgame.com/" | |
| - files: ["NeckSnap2.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Joonas 'Regalis' Rikkonen (Undertow Games), Converted from mp3 to .ogg / Russian distribution by scpcbgame.ru" | |
| source: "https://scpcbgame.com/" | |
| - files: ["NeckSnap3.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Joonas 'Regalis' Rikkonen (Undertow Games), Converted from mp3 to .ogg / Russian distribution by scpcbgame.ru" | |
| source: "https://scpcbgame.com/" | |
| - files: ["Rattle1.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Joonas 'Regalis' Rikkonen (Undertow Games), Converted from mp3 to .ogg / Russian distribution by scpcbgame.ru" | |
| source: "https://scpcbgame.com/" | |
| - files: ["Rattle2.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Joonas 'Regalis' Rikkonen (Undertow Games), Converted from mp3 to .ogg / Russian distribution by scpcbgame.ru" | |
| source: "https://scpcbgame.com/" | |
| - files: ["Rattle3.ogg"] | |
| license: "CC-BY-SA-3.0" | |
| copyright: "Joonas 'Regalis' Rikkonen (Undertow Games), Converted from mp3 to .ogg / Russian distribution by scpcbgame.ru" | |
| source: "https://scpcbgame.com/" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Audio/Imperial/Seriozha/SCP/173/attributions.yml` around lines 1 -
24, Update the copyright attribution entries for the listed files (e.g.,
"NeckSnap1.ogg", "NeckSnap2.ogg", "NeckSnap3.ogg", "Rattle1.ogg", "Rattle2.ogg",
"Rattle3.ogg") so the copyright holder reflects the original creator Joonas
"Regalis" Rikkonen / Undertow Games instead of "Trill Team"; keep license
"CC-BY-SA-3.0" unchanged and preserve the scpcbgame.ru source URL, and if you
want to acknowledge Trill Team's role, add a separate note (not as copyright)
indicating they performed the Russian voice conversion/localization.
| - type: alert | ||
| id: Sanity | ||
| icons: | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery0 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery1 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery2 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery3 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery4 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery5 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery6 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery7 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery8 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery9 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery10 | ||
| minSeverity: 0 | ||
| maxSeverity: 10 | ||
| name: Рассудок | ||
| description: Текущее состояние психики. |
There was a problem hiding this comment.
Иконки Sanity и SCPFiremanPoints визуально неразличимы.
Оба алерта используют одинаковый спрайт battery.rsi (состояния battery0–battery10). Если на одной сущности активны оба алерта одновременно, игрок не сможет понять, какой из двух отображает рассудок, а какой — огненные очки. Это сломает читаемость HUD для SCPFireman-персонажа.
Для Sanity стоит взять тематичную иконку (например, связанную с головой/мозгом), либо добавить отдельный .rsi-файл с уникальными состояниями.
🐛 Пример исправления (заглушка — замените путь на реальный)
- type: alert
id: Sanity
icons:
- - sprite: /Textures/Interface/Alerts/battery.rsi
- state: battery0
- # ... battery1–battery10
+ - sprite: /Textures/Interface/Alerts/sanity.rsi
+ state: sanity0
+ # ... sanity1–sanity10Also applies to: 81-109
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Prototypes/Alerts/alerts.yml` around lines 42 - 70, The Sanity
alert (type: alert, id: Sanity) currently reuses the battery sprite (sprite:
/Textures/Interface/Alerts/battery.rsi with states battery0–battery10) which
conflicts visually with SCPFiremanPoints; update the Sanity alert to use a
distinct, thematic sprite (for example a head/brain icon) by replacing the
sprite path and state names with the new .rsi asset and its unique states (or
add a new .rsi file with brain0–brain10 states), ensuring
minSeverity/maxSeverity mapping remains 0–10 and the icon state names in the
Sanity block match the new asset so the HUD can visually distinguish Sanity from
SCPFiremanPoints.
| "license": null, | ||
| "copyright": null, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all meta.json files with null license to understand the project pattern
echo "=== Checking for null license values in meta.json files ==="
rg '"license":\s*null' --type json Resources/ | head -20
echo -e "\n=== Checking for null copyright values in meta.json files ==="
rg '"copyright":\s*null' --type json Resources/ | head -20
echo -e "\n=== Checking the specific file ==="
cat -n Resources/Textures/Imperial/Seriozha/Fredik21/nda/nda173.rsi/meta.json
echo -e "\n=== Checking other files in the same directory ==="
find Resources/Textures/Imperial/Seriozha/Fredik21/ -name "meta.json" -exec echo "File: {}" \; -exec head -10 {} \;Repository: imperial-space/SS14-public
Length of output: 46521
Укажите информацию о лицензии и авторских правах для этого файла.
Поля license и copyright равны null. Все остальные файлы в этом директории имеют правильно заполненные данные лицензирования (см. соседние файлы nda020.rsi, nda096.rsi и т.д.). Необходимо добавить соответствующие значения:
- "license": null,
- "copyright": null,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "по Fredik21, imperial space",или используйте ICLA в соответствии с другими файлами этого автора, если это требуется.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "license": null, | |
| "copyright": null, | |
| "license": "CC-BY-SA-3.0", | |
| "copyright": "по Fredik21, imperial space", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Textures/Imperial/Seriozha/Fredik21/nda/nda173.rsi/meta.json`
around lines 3 - 4, В файле meta.json заполните поля "license" и "copyright"
(вместо текущих null) в соответствии с остальными файлами этого автора — либо
укажите конкретную лицензию и имя/организацию правообладателя, как в соседних
nda020.rsi/nda096.rsi, либо используйте ICLA, если этот автор в каталоге помечен
через ICLA; убедитесь, что значения строковые и формат совпадает с примерами в
соседних meta.json.
| "license": "CC-BY-SA-3.0", | ||
| "copyright": "https://github.com/ParadiseSS13/Paradise", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Paradise SS13 GitHub repository licensing CC-BY-NC-SA goonstation assets
💡 Result:
-
ParadiseSS13/Paradise licensing (GitHub):
- Code: GPLv3 before commit
1af3ddef…(2015-01-05 22:04 GMT), and AGPLv3 from and after that commit, unless a file/folder says otherwise. - Assets (non-code): default CC BY-SA 3.0, except specific subtrees with different licenses.
- Goon-derived assets inside Paradise: anything under
icons/goonstationorsound/goonstationis CC BY-NC-SA 3.0 (non-commercial, share-alike, attribution). [1]
- Code: GPLv3 before commit
-
Goonstation (upstream) licensing:
- Goonstation’s repo states it is licensed under CC BY-NC-SA 3.0 United States. [2]
-
Practical implication re “goonstation assets”:
- If you use/redistribute/modify Goonstation assets (whether from the Goon repo or the
*/goonstation/*directories inside Paradise), you must follow CC BY-NC-SA terms—most importantly non-commercial use only and ShareAlike for adaptations of those assets, plus attribution. That restriction is why Paradise keeps them in clearly marked directories under a different license than most Paradise assets. [1][2]
- If you use/redistribute/modify Goonstation assets (whether from the Goon repo or the
Sources:
[1] ParadiseSS13/Paradise README (Licenses section)
[2] goonstation/goonstation LICENSE file
🏁 Script executed:
# Search for existing copyright field patterns in meta.json files across the codebase
fd -e meta.json -x grep -l "copyright" {} \; | head -10 | xargs -I {} sh -c 'echo "=== {} ===" && head -5 {}'Repository: imperial-space/SS14-public
Length of output: 52
🏁 Script executed:
# Search for any references to TerrorSpider in the codebase
rg -i "terrorspider|terror.spider" --type-list | head -20Repository: imperial-space/SS14-public
Length of output: 459
🏁 Script executed:
# Check existing meta.json files to understand copyright field conventions in this project
fd -e meta.json | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && cat {}'Repository: imperial-space/SS14-public
Length of output: 52
🏁 Script executed:
# List meta.json files in the repository
fd "meta.json" | head -20Repository: imperial-space/SS14-public
Length of output: 1290
🏁 Script executed:
# Search for TerrorSpider references in the codebase
rg -i "terrorspider" | head -20Repository: imperial-space/SS14-public
Length of output: 2057
🏁 Script executed:
# Look at a few example meta.json files to see copyright field conventions
fd "meta.json" | head -3 | while read file; do echo "=== $file ==="; cat "$file"; echo ""; doneRepository: imperial-space/SS14-public
Length of output: 1261
🏁 Script executed:
# Check if the file in question exists and show its full content
cat "Resources/Textures/Imperial/TerrorSpider/act.rsi/meta.json"Repository: imperial-space/SS14-public
Length of output: 841
🏁 Script executed:
# Search for any documentation or guidelines about copyright attribution in this project
fd -e md -e txt | xargs grep -l "copyright\|attribution\|license" 2>/dev/null | head -5Repository: imperial-space/SS14-public
Length of output: 270
🏁 Script executed:
# Check if there are any ICLA or project-specific license files
fd -e md -e txt -e yml | xargs grep -l "ICLA\|Imperial" 2>/dev/null | grep -i license | head -5Repository: imperial-space/SS14-public
Length of output: 283
🏁 Script executed:
# Check README for licensing guidelines
grep -A 10 -B 5 -i "copyright\|attribution\|license" README.md | head -40Repository: imperial-space/SS14-public
Length of output: 3259
🏁 Script executed:
# Examine one of the license.txt files to understand project conventions
cat "Resources/Prototypes/Imperial/MiningWeapons/license.txt"Repository: imperial-space/SS14-public
Length of output: 230
🏁 Script executed:
# Check if there are other Paradise-sourced assets and how they're attributed
fd "meta.json" | xargs grep -l "ParadiseSS13\|Paradise" | head -10Repository: imperial-space/SS14-public
Length of output: 714
🏁 Script executed:
# Look at how Paradise-sourced assets are credited in this project
fd "meta.json" | xargs grep -B2 -A2 "ParadiseSS13" | head -30Repository: imperial-space/SS14-public
Length of output: 3201
Дополните информацию об авторстве в поле copyright
В проекте принято указывать для ассетов из Paradise детальную информацию о происхождении: конкретный коммит или путь файла, а также были ли произведены модификации. В текущем файле указана только голая ссылка на репозиторий без контекста.
Согласно примерам проекта, поле должно содержать:
- Конкретный путь файла или хеш коммита в Paradise
- Информацию о выполненных модификациях (если применимо)
- Уточнение, из какой директории взят ассет (особенно важно: ассеты из
icons/goonstationилиsound/goonstationв Paradise лицензированы как CC-BY-NC-SA-3.0, а не CC-BY-SA-3.0)
Пример правильного формата из проекта:
"copyright": "Taken from Paradise station https://github.com/ParadiseSS13/Paradise/blob/master/icons/obj/smooth_structures/windows/pod_window.dmi and modified by NULL882, transparency tweaked by Ubaser."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Textures/Imperial/TerrorSpider/act.rsi/meta.json` around lines 3 -
4, Замените в meta.json текущее значение поля "copyright" (ссылка на
репозиторий) на подробное указание источника в формате, принятом в проекте:
укажите, что ассет взят из Paradise (указать либо полный путь к файлу в
репозитории, напр. blob/master/... или конкретный хеш коммита), опишите
выполненные модификации (если были) и укажите директорию источника (особенно
важно для ассетов из icons/goonstation или sound/goonstation), при этом оставьте
поле "license": "CC-BY-SA-3.0" без изменений если лицензия подтверждена;
используйте пример формата в комментарии для ориентира (например: "Taken from
Paradise station https://github.com/ParadiseSS13/Paradise/blob/master/<path> and
modified by <your name>, <description>").
| private static bool IsCoffeeLikeReagent(string reagentId) | ||
| { | ||
| return reagentId.Contains("Coffee", StringComparison.OrdinalIgnoreCase) | ||
| || reagentId.Contains("Caffeine", StringComparison.OrdinalIgnoreCase) | ||
| || reagentId.Contains("Espresso", StringComparison.OrdinalIgnoreCase) | ||
| || reagentId.Contains("Latte", StringComparison.OrdinalIgnoreCase) | ||
| || reagentId.Contains("Cappuccino", StringComparison.OrdinalIgnoreCase) | ||
| || reagentId.Contains("Americano", StringComparison.OrdinalIgnoreCase) | ||
| || reagentId.Contains("Mocha", StringComparison.OrdinalIgnoreCase) | ||
| || reagentId.Contains("Macchiato", StringComparison.OrdinalIgnoreCase); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Хрупкая логика определения кофейных реагентов через подстроки.
IsCoffeeLikeReagent использует Contains для определения кофейных реагентов. Это может дать ложные срабатывания (например, реагент с "Latte" в названии, не являющийся кофе). Более надёжный подход — использовать HashSet<string> с конкретными ID реагентов или пометить нужные реагенты тегом/компонентом.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs` around lines 332 -
342, The IsCoffeeLikeReagent method currently matches reagentId by substring
which is fragile; replace this with a deterministic lookup (e.g., a readonly
HashSet<string> of allowed reagent IDs) or check for a Coffee tag/component on
the reagent entity instead of using Contains; update calls to
IsCoffeeLikeReagent to use the new lookup (keep method name or provide a new
IsKnownCoffeeReagent) and populate the HashSet with the canonical reagent IDs
(or query the reagent's component/tag) so only exact, intended reagents are
treated as coffee-like.
| ent-TerrorEgg1Tir = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEgg2Tir = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
| .suffix = пауки ужаса | ||
|
|
||
| ent-TerrorEgg3Tir = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
| .suffix = пауки ужаса | ||
|
|
||
| ent-SpiderWebTerrorGuardian = паутина паука ужаса | ||
| .desc = УЖАСНО! | ||
| .suffix = пауки ужаса | ||
| ent-TerrorEggPrincessTier1 = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-SpiderWebPrincess = паутина паука ужаса | ||
| .desc = УЖАСНО! | ||
| .suffix = пауки ужаса | ||
| ent-TerrorEggPrincessTier2 = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggHealerRusar = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggHealerDron = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggHealerLurker = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggHealerHealer = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenRusar = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenDron = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenLurker = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenHealer = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenReaper = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenWidow = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenGuardian = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenDestroyer = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenPrince = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenPrincess = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggQueenMother = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggPrincessRusar = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggPrincessDron = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggPrincessLurker = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggPrincessHealer = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggPrincessReaper = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggPrincessWidow = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggPrincessGuardian = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
||
| ent-TerrorEggPrincessDestroyer = яйцо паука ужаса | ||
| .desc = На вид мерзкое. | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Все яйца неотличимы по названию и описанию.
22 различных сущности яиц (TerrorEggPrincessTier1, TerrorEggQueenRusar, TerrorEggPrincessDestroyer и т.д.) имеют одинаковое имя яйцо паука ужаса и описание На вид мерзкое. Если это намеренно (экипаж не должен отличать яйца), рекомендуется оставить комментарий в файле. Если же пауки-игроки должны различать свои яйца при осмотре, стоит добавить суффиксы или уточнения к именам/описаниям (например, «яйцо паука ужаса (рыцарь)»).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Locale/ru-RU/Imperial/Terrorspider/spider.ftl` around lines 144 -
227, Several egg entities (ent-TerrorEgg1Tir, ent-TerrorEggPrincessTier1,
ent-TerrorEggQueenRusar, ent-TerrorEggPrincessDestroyer, etc.) all share the
identical name and description; decide whether this is intentional and either
add a clarifying comment or make them distinguishable. If intentional, add a
short comment near these entries stating "identical names/descriptions
intentional — crew cannot distinguish eggs"; otherwise, update each identifier's
.value and/or .desc to include a distinguishing suffix or role label (e.g.,
"(рыцарь)", "(королева)", "(принцесса)") for the entries ent-TerrorEgg1Tir,
ent-TerrorEgg2Tir, ent-TerrorEgg3Tir, ent-TerrorEggPrincessTier1,
ent-TerrorEggQueenRusar, ent-TerrorEggPrincessDestroyer and the other listed
ent-TerrorEgg* keys so players can tell eggs apart when inspecting them.
| { | ||
| "version": 1, | ||
| "license": "CC-BY-SA-3.0", | ||
| "copyright": "https://github.com/Foundation-19", |
There was a problem hiding this comment.
Поле copyright не содержит корректной строки атрибуции.
Текущее значение "https://github.com/Foundation-19" — это ссылка на GitHub-организацию, а не стандартная строка авторства. Лицензия CC-BY-SA-3.0 требует явного указания имени автора/правообладателя (и желательно года). URL без имени автора не удовлетворяет требованиям атрибуции BY.
Рекомендуемый формат:
- "copyright": "https://github.com/Foundation-19",
+ "copyright": "drawn by <Имя автора>, https://github.com/Foundation-19",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Textures/Imperial/Seriozha/Fredik21/nda/nda096.rsi/meta.json` at
line 4, The "copyright" field in meta.json currently contains a GitHub URL
instead of a proper attribution string required by CC-BY-SA-3.0; update the
"copyright" value to a standard attribution (e.g., "Author Name, 2024;
https://github.com/Foundation-19" or "© Author Name 2024 —
https://github.com/Foundation-19") so it explicitly names the
author/rightsholder and optionally the year while keeping the existing URL for
reference.
Resources/Textures/Imperial/Seriozha/Fredik21/nda/nda096.rsi/meta.json
Outdated
Show resolved
Hide resolved
| "version": 1, | ||
| "license": "CC-BY-SA-3.0", | ||
| "copyright": "By Fredik21 https://github.com/imperial-space", | ||
| "copyright": "https://github.com/Foundation-19", |
There was a problem hiding this comment.
Поле copyright содержит URL вместо текстовой атрибуции.
Лицензия CC-BY-SA-3.0 требует указания имени автора или правообладателя. Значение "https://github.com/Foundation-19" — ссылка на профиль, а не текстовая атрибуция, которая используется в соседних файлах этого каталога. Рекомендуется привести к единому формату:
🛠️ Предлагаемое исправление
- "copyright": "https://github.com/Foundation-19",
+ "copyright": "Foundation-19",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "copyright": "https://github.com/Foundation-19", | |
| { | |
| "version": 1, | |
| "license": "CC-BY-SA-3.0", | |
| "copyright": "Foundation-19", | |
| "size": { | |
| "x": 32, | |
| "y": 64 | |
| }, | |
| "states": [ | |
| { | |
| "name": "173", | |
| "directions": 4, | |
| "delays": [ | |
| [0.1, 0.1, 0.1, 0.1] | |
| ] | |
| } | |
| ] | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Textures/Imperial/Seriozha/Fredik21/nda/nda173.rsi/meta.json` at
line 4, Поле copyright в meta.json содержит только URL вместо текстовой
атрибуции; замените значение на текстовую подпись в том же формате, что и в
соседних файлах (например имя автора/правообладателя — "Foundation-19" или
"Foundation-19 (GitHub: https://github.com/Foundation-19)" ), обновив ключ
"copyright" в этом файле чтобы соответствовать требованиям CC-BY-SA-3.0 и
остальной структуре каталога.
| "states": [ | ||
| { | ||
| "name": "cocoon_large3" | ||
| }, | ||
| { | ||
| "name": "terror_shield" | ||
| } | ||
| ] |
There was a problem hiding this comment.
Отсутствуют поля directions и delays в описании состояний.
В RSI-формате при отсутствии directions по умолчанию принимается значение 1 (один угол обзора, без анимации). Если спрайты cocoon_large3 или terror_shield многонаправленные или анимированные, метаданные будут некорректны и движок отобразит их неверно.
Убедитесь, что спрайт-листы в .png соответствуют дефолтным значениям (1 направление, 1 кадр). При необходимости добавьте явные поля, например:
📝 Пример с явными полями направления и анимации
{
- "name": "cocoon_large3"
+ "name": "cocoon_large3",
+ "directions": 1,
+ "delays": [[1.0]]
},
{
- "name": "terror_shield"
+ "name": "terror_shield",
+ "directions": 1,
+ "delays": [[1.0]]
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Textures/Imperial/TerrorSpider/effects.rsi/meta.json` around lines
9 - 16, В метаданных состояний "cocoon_large3" и "terror_shield" отсутствуют
поля directions и delays, из‑за чего движок будет считать их однонаправленными и
неанимированными; откройте объект states и для каждого состояния с именами
"cocoon_large3" и "terror_shield" добавьте явные записи directions (значение 1
если один угол обзора, или нужное число направлений) и delays (значение 1 или
массив из длительностей кадров), например "directions": 1 и "delays": 1 или
соответствующие массивы, чтобы метаданные отражали структуру .png спрайт-листов
и избежать неправильного отображения.
| [DataField("warningPopup")] | ||
| public string WarningPopup = "SCP-008: покиньте зону немедленно, иначе вы превратитесь в зомби!"; |
There was a problem hiding this comment.
Захардкоженная строка вместо локализации.
WarningPopup содержит русский текст напрямую. В SS14 принято использовать систему локализации (Loc.GetString) с ключом вместо встроенных строк, чтобы поддерживать возможность перевода и централизованное управление текстами. Аналогичная проблема имеется и в SCP096RageOnLookComponent (строки 37, 40, 43).
🛠️ Предлагаемое исправление
- [DataField("warningPopup")]
- public string WarningPopup = "SCP-008: покиньте зону немедленно, иначе вы превратитесь в зомби!";
+ [DataField("warningPopup")]
+ public LocId WarningPopup = "scp008-infection-aura-warning";Соответственно, в системе использовать Loc.GetString(comp.WarningPopup) при вызове _popup.PopupEntity(...).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@Content.Server/Imperial/SCP/SCP008/Components/SCP008InfectionAuraComponent.cs`
around lines 26 - 27, Replace hardcoded Russian text in WarningPopup on
SCP008InfectionAuraComponent with a localization key string (e.g.,
"scp008-warning-popup") and change call sites that pass comp.WarningPopup into
popup text to wrap it with Loc.GetString(comp.WarningPopup) when calling
_popup.PopupEntity(...); do the same for the analogous hardcoded strings in
SCP096RageOnLookComponent (the fields around the string constants and their
_popup.PopupEntity usages) so all popup messages use localization keys and
Loc.GetString at display time.
Content.Server/Imperial/SCP/SCP008/Systems/SCP008InfectionAuraSystem.cs
Outdated
Show resolved
Hide resolved
| private void OnAttackAttempt(Entity<SCP096RageOnLookComponent> ent, ref AttackAttemptEvent args) | ||
| { | ||
| if (ent.Comp.IsEnraged) | ||
| { | ||
| if (args.Target is not { } target) | ||
| return; | ||
|
|
||
| if (HasComp<DoorComponent>(target)) | ||
| return; | ||
|
|
||
| if (ent.Comp.RageTargets.Contains(target)) | ||
| return; | ||
| } | ||
|
|
||
| args.Cancel(); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Неочевидная логика OnAttackAttempt — Cancel() вызывается для всех необработанных случаев.
Когда IsEnraged == true, ранние return-ы пропускают отмену для дверей и целей ярости, а остальные случаи «проваливаются» к args.Cancel() на строке 59. Аналогично, не-enraged SCP-096 всегда попадает на Cancel(). Логика корректна, но без else блока или комментария её легко неправильно интерпретировать при будущих правках.
♻️ Более явная структура
private void OnAttackAttempt(Entity<SCP096RageOnLookComponent> ent, ref AttackAttemptEvent args)
{
if (ent.Comp.IsEnraged)
{
if (args.Target is not { } target)
return;
if (HasComp<DoorComponent>(target))
return;
if (ent.Comp.RageTargets.Contains(target))
return;
+
+ // Enraged but target is not a rage target or door — block attack
}
+ // Not enraged or target not valid — block attack
args.Cancel();
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/SCP/SCP096/Systems/SCP096RageOnLookSystem.cs` around
lines 45 - 60, The OnAttackAttempt logic is unclear because args.Cancel() is
executed for all fall-through cases; make the intent explicit by restructuring
the method: if ent.Comp.IsEnraged then validate target (check target null,
HasComp<DoorComponent>(target), and ent.Comp.RageTargets.Contains(target)) and
call args.Cancel() only when none of those early-return conditions match;
otherwise (else) call args.Cancel() for the non-enraged path — use the
OnAttackAttempt method, ent.Comp.IsEnraged, ent.Comp.RageTargets,
HasComp<DoorComponent>(target), and args.Cancel() to guide the changes or
alternatively add a clarifying comment above the current flow if you prefer
minimal edits.
Content.Server/Imperial/SCP/SCP096/Systems/SCP096RageOnLookSystem.cs
Outdated
Show resolved
Hide resolved
| if (TryComp<TerrorSpiderArmorComponent>(ent.Owner, out var armor)) | ||
| { | ||
| ent.Comp.CachedBruteModifier = armor.BruteModifier; | ||
| ent.Comp.CachedBurnModifier = armor.BurnModifier; | ||
| armor.BruteModifier = ent.Comp.BruteIncomingMultiplier; | ||
| armor.BurnModifier = ent.Comp.BurnIncomingMultiplier; | ||
| Dirty(ent.Owner, armor); | ||
| } |
There was a problem hiding this comment.
Та же проблема с кешированием брони, что и в SharedTerrorSpiderKnightGuardSystem.
CachedBruteModifier / CachedBurnModifier по умолчанию равны 0f. Если TerrorSpiderArmorComponent отсутствует при активации ярости, EndRage (строки 114–119) обнулит модификаторы, если компонент будет добавлен во время ярости. Добавьте булевый флаг HasCachedArmor по аналогии с рекомендацией для Guard-системы.
Предлагаемое исправление
if (TryComp<TerrorSpiderArmorComponent>(ent.Owner, out var armor))
{
+ ent.Comp.HasCachedArmor = true;
ent.Comp.CachedBruteModifier = armor.BruteModifier;
ent.Comp.CachedBurnModifier = armor.BurnModifier;
armor.BruteModifier = ent.Comp.BruteIncomingMultiplier;
armor.BurnModifier = ent.Comp.BurnIncomingMultiplier;
Dirty(ent.Owner, armor);
}- if (TryComp<TerrorSpiderArmorComponent>(uid, out var armor))
+ if (comp.HasCachedArmor && TryComp<TerrorSpiderArmorComponent>(uid, out var armor))
{
armor.BruteModifier = comp.CachedBruteModifier;
armor.BurnModifier = comp.CachedBurnModifier;
Dirty(uid, armor);
}
+ comp.HasCachedArmor = false;Also applies to: 114-119
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`
around lines 80 - 87, SharedTerrorSpiderKnightRageSystem currently caches armor
modifiers into ent.Comp.CachedBruteModifier/CachedBurnModifier which default to
0f, causing EndRage to zero out real armor if TerrorSpiderArmorComponent is
added during rage; add a boolean flag (e.g., HasCachedArmor) to the component
and set it true when you successfully cache values in the TryComp block, then in
EndRage only restore and clear modifiers when HasCachedArmor is true (and reset
HasCachedArmor to false afterward) so you don't overwrite newly-added armor
modifiers inadvertently.
| - type: alert | ||
| id: Sanity | ||
| icons: | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery0 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery1 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery2 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery3 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery4 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery5 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery6 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery7 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery8 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery9 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery10 | ||
| minSeverity: 0 | ||
| maxSeverity: 10 | ||
| name: Рассудок | ||
| description: Текущее состояние психики. | ||
|
|
||
| - type: alert | ||
| id: SCPBlink | ||
| icons: | ||
| - sprite: /Textures/Clothing/Eyes/Glasses/glasses.rsi | ||
| state: icon | ||
| clickEvent: !type:SCPBlinkAlertEvent | ||
| name: Моргание | ||
| description: До следующего моргания. | ||
|
|
||
| - type: alert | ||
| id: SCPFiremanPoints | ||
| icons: | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery0 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery1 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery2 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery3 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery4 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery5 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery6 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery7 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery8 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery9 | ||
| - sprite: /Textures/Interface/Alerts/battery.rsi | ||
| state: battery10 | ||
| minSeverity: 0 | ||
| maxSeverity: 10 | ||
| name: Огненные очки | ||
| description: Текущий запас огненной энергии. |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Жёстко заданные русские строки name/description вместо ключей локализации.
Все три новых алерта (Sanity, SCPBlink, SCPFiremanPoints) содержат прямой текст на русском, тогда как все соседние алерты в этом же файле используют ключи FTL (alerts-low-oxygen-name, alerts-low-nitrogen-name и т. д.). Это нарушает единообразие и затруднит будущие правки текстов.
♻️ Предлагаемое исправление — вынести в FTL-ключи
- type: alert
id: Sanity
...
- name: Рассудок
- description: Текущее состояние психики.
+ name: alerts-sanity-name
+ description: alerts-sanity-desc
- type: alert
id: SCPBlink
...
- name: Моргание
- description: До следующего моргания.
+ name: alerts-scpblink-name
+ description: alerts-scpblink-desc
- type: alert
id: SCPFiremanPoints
...
- name: Огненные очки
- description: Текущий запас огненной энергии.
+ name: alerts-scpfiremanpoints-name
+ description: alerts-scpfiremanpoints-descДобавьте соответствующие записи в файл локализации .ftl.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Prototypes/Alerts/alerts.yml` around lines 42 - 109, The three
alert entries (ids Sanity, SCPBlink, SCPFiremanPoints) use hard-coded Russian
text in their name and description fields; replace these with FTL localization
keys (e.g., alerts-sanity-name, alerts-sanity-desc, alerts-scpblink-name,
alerts-scpblink-desc, alerts-scpfiremanpoints-name,
alerts-scpfiremanpoints-desc) in the YAML and add the corresponding entries to
the .ftl localization file(s) with the Russian strings currently present; ensure
you update both name and description for each alert and follow the existing key
naming pattern used by neighboring alerts.
| - type: entity | ||
| name: SCP-173 | ||
| suffix: Fredik21, Недра, Объект | ||
| parent: ImperialSCPBase | ||
| id: ImperialSCP173 | ||
| description: Не разрывайте зрительный контакт. | ||
| components: | ||
| - type: Sprite | ||
| drawdepth: Mobs | ||
| layers: | ||
| - map: ["enum.DamageStateVisualLayers.Base"] | ||
| state: 173 | ||
| sprite: Imperial/Seriozha/Fredik21/nda/nda173.rsi | ||
| - map: ["enum.TriggerVisualLayers.Base"] | ||
| state: 173 | ||
| sprite: Imperial/Seriozha/Fredik21/nda/nda173.rsi | ||
| visible: false | ||
| - type: TimerTriggerVisuals | ||
| unprimedSprite: 173 | ||
| primingSprite: 173 | ||
| - type: DamageStateVisuals | ||
| states: | ||
| Alive: | ||
| Base: 173 | ||
| Dead: | ||
| Base: 173 | ||
| - type: MobThresholds | ||
| thresholds: | ||
| 0: Alive | ||
| 8000: Dead | ||
| - type: MovementSpeedModifier | ||
| baseWalkSpeed: 4.2 | ||
| baseSprintSpeed: 6.0 | ||
| - type: MeleeWeapon | ||
| hidden: true | ||
| altDisarm: false | ||
| soundHit: | ||
| collection: SCP173NeckSnap | ||
| angle: 0 | ||
| animation: WeaponArcBite | ||
| attackRate: 1.2 | ||
| damage: | ||
| types: | ||
| Blunt: 200 | ||
| - type: FootstepModifier | ||
| footstepSoundCollection: | ||
| collection: SCP173Rattle | ||
| - type: SCP173WatchLock | ||
| observeRadius: 8 | ||
| minLookDot: 0.35 | ||
| requireLightToObserve: true | ||
| lightLookupRadius: 12 | ||
| - type: SCPBlinkManualTrigger | ||
| - type: SCP173LightFlicker | ||
| action: ActionSCP173LightFlicker | ||
| radius: 9 | ||
| duration: 8 | ||
| toggleInterval: 0.25 | ||
| - type: HTN | ||
| rootTask: | ||
| task: SimpleHostileCompound | ||
| - type: Puller | ||
| - type: Pullable | ||
| - type: Fixtures | ||
| fixtures: | ||
| fix1: | ||
| shape: | ||
| !type:PhysShapeCircle | ||
| radius: 0.4 | ||
| density: 350 | ||
| mask: | ||
| - MobMask | ||
| layer: | ||
| - MobLayer | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the ImperialSCPBase definition
rg -n "id:\s*ImperialSCPBase" -g '*.yml' -A 30Repository: imperial-space/SS14-public
Length of output: 5059
🏁 Script executed:
#!/bin/bash
# Get the full ImperialSCPBase definition
sed -n '4,150p' Resources/Prototypes/Imperial/Seriozha/SCP/SCP.ymlRepository: imperial-space/SS14-public
Length of output: 3497
🏁 Script executed:
#!/bin/bash
# Find all entities with TimerTriggerVisuals
rg -n "type:\s*TimerTriggerVisuals" -g '*.yml' -B 20 -A 5 | head -100Repository: imperial-space/SS14-public
Length of output: 8459
🏁 Script executed:
#!/bin/bash
# Find SimpleSpaceMobBase definition
rg -n "id:\s*SimpleSpaceMobBase" -g '*.yml' -A 30Repository: imperial-space/SS14-public
Length of output: 10085
Добавьте компонент Appearance в ImperialSCPBase для работы TimerTriggerVisuals.
Базовый прототип ImperialSCPBase содержит TimerTriggerVisuals, но не имеет компонента Appearance, необходимого для обновления визуального состояния. Это влияет на все наследующие его SCP-объекты, включая SCP-173. Добавьте Appearance в базовый прототип:
🛠️ Исправление
- type: TimerTriggerVisuals
primingSound:
path: /Audio/Effects/Smoke-grenade.ogg
+ - type: Appearance🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Prototypes/Imperial/Seriozha/SCP/Fredik21/scp.yml` around lines
7389 - 7463, В базовом прототипе ImperialSCPBase отсутствует компонент
Appearance, поэтому TimerTriggerVisuals не может обновлять визуальное состояние;
откройте прототип ImperialSCPBase и добавьте компонент Appearance (с нужными
слотами/слоями, которые ожидает
TimerTriggerVisuals/TimerTriggerVisuals.unprimedSprite/primingSprite и
DamageStateVisuals), чтобы наследники вроде ImperialSCP173 (SCP-173) могли
корректно менять спрайты через TimerTriggerVisuals и DamageStateVisuals.
| - type: entity | ||
| id: ActionSCPFiremanIgnite | ||
| name: Поджёг | ||
| description: Создаёт огонь под собой. | ||
| components: | ||
| - type: Action | ||
| checkCanAccess: false | ||
| useDelay: 1 | ||
| - type: InstantAction | ||
| event: !type:SCPFiremanIgniteActionEvent | ||
|
|
||
| - type: entity | ||
| id: ActionSCPFiremanFireball | ||
| name: Огненный шар | ||
| description: Создаёт взрыв огня в выбранной точке. | ||
| components: | ||
| - type: Action | ||
| checkCanAccess: false | ||
| useDelay: 2 | ||
| - type: TargetAction | ||
| checkCanAccess: false | ||
| range: 60 | ||
| - type: WorldTargetAction | ||
| event: !type:SCPFiremanFireballActionEvent | ||
|
|
||
| - type: entity | ||
| id: ActionSCPFiremanWhirl | ||
| name: Огненный вихрь | ||
| description: Выпускает огненный вихрь в направлении курсора. | ||
| components: | ||
| - type: Action | ||
| checkCanAccess: false | ||
| useDelay: 2 | ||
| - type: TargetAction | ||
| checkCanAccess: false | ||
| range: 60 | ||
| - type: WorldTargetAction | ||
| event: !type:SCPFiremanWhirlActionEvent | ||
|
|
||
| - type: entity | ||
| id: ActionSCPFiremanMelt | ||
| name: Плавление | ||
| description: Наносит 100 урона выбранной структуре и поджигает её. | ||
| components: | ||
| - type: Action | ||
| checkCanAccess: false | ||
| useDelay: 2 | ||
| - type: TargetAction | ||
| checkCanAccess: false | ||
| range: 20 | ||
| - type: EntityTargetAction | ||
| event: !type:SCPFiremanMeltActionEvent | ||
|
|
||
| - type: entity | ||
| id: ActionSCPFiremanTrueFlame | ||
| name: Истинное пламя | ||
| description: Делает вас неуязвимым на 25 секунд. | ||
| components: | ||
| - type: Action | ||
| checkCanAccess: false | ||
| useDelay: 2 | ||
| - type: InstantAction | ||
| event: !type:SCPFiremanTrueFlameActionEvent | ||
|
|
||
| - type: entity | ||
| id: ActionSCPFiremanStrike | ||
| name: Огненный удар | ||
| description: Через 1.5 секунды поджигает существ в радиусе 20 клеток. | ||
| components: | ||
| - type: Action | ||
| checkCanAccess: false | ||
| useDelay: 2 | ||
| - type: InstantAction | ||
| event: !type:SCPFiremanStrikeActionEvent | ||
|
|
||
| - type: entity | ||
| id: ActionSCPFiremanSecondMode | ||
| name: Второй режим | ||
| description: Переключает режим ЛКМ-излучения огня. | ||
| components: | ||
| - type: Action | ||
| checkCanAccess: false | ||
| useDelay: 0.5 | ||
| - type: InstantAction | ||
| event: !type:SCPFiremanSecondModeActionEvent |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's find the actual SCPFireman-related C# files to see if icons are assigned programmatically
find . -type f -name "*.cs" | xargs grep -l "SCPFireman" | head -20Repository: imperial-space/SS14-public
Length of output: 442
🏁 Script executed:
#!/bin/bash
# Check the actual content of the SCP.yml file at the mentioned lines
sed -n '698,782p' Resources/Prototypes/Imperial/Seriozha/SCP/SCP.yml | head -100Repository: imperial-space/SS14-public
Length of output: 2225
🏁 Script executed:
#!/bin/bash
# Look for other action entities in the prototype files to see how they define parent and Icon
rg "type: entity" Resources/Prototypes/ -A 5 | grep -E "id: Action|parent:|Icon:" | head -50Repository: imperial-space/SS14-public
Length of output: 3988
🏁 Script executed:
#!/bin/bash
# Search specifically for BaseAction and how action entities typically inherit
rg "BaseAction|parent:.*Action" Resources/Prototypes/Imperial/ -B 2 -A 8 | head -100Repository: imperial-space/SS14-public
Length of output: 7816
🏁 Script executed:
#!/bin/bash
# Check the SCPFiremanSystem.cs for icon assignment
cat -n ./Content.Server/Imperial/SCP/SCPFireman/Systems/SCPFiremanSystem.cs | head -200Repository: imperial-space/SS14-public
Length of output: 10524
🏁 Script executed:
#!/bin/bash
# Search for icon-related code in SCPFireman files
rg -n "icon\|Icon" ./Content.Server/Imperial/SCP/SCPFireman/ -iRepository: imperial-space/SS14-public
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Check the SCPFiremanComponent.cs for icon definitions
cat -n ./Content.Server/Imperial/SCP/SCPFireman/Components/SCPFiremanComponent.csRepository: imperial-space/SS14-public
Length of output: 2714
🏁 Script executed:
#!/bin/bash
# Look at BaseAction prototype to understand what icons should be present
rg -n "BaseAction" Resources/Prototypes/ -A 10 | head -100Repository: imperial-space/SS14-public
Length of output: 7375
У action-сущностей отсутствуют родительский прототип и иконки
Все семь action-сущностей (ActionSCPFiremanIgnite, ActionSCPFiremanFireball, ActionSCPFiremanWhirl, ActionSCPFiremanMelt, ActionSCPFiremanTrueFlame, ActionSCPFiremanStrike, ActionSCPFiremanSecondMode) не имеют parent и определений иконок. В SS14 действия без иконок отображаются пустыми кнопками в интерфейсе.
Добавьте parent: BaseAction и укажите icon в компоненте Action для каждого действия, аналогично другим action-сущностям в проекте (например, TerrorSpider).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Prototypes/Imperial/Seriozha/SCP/SCP.yml` around lines 698 - 782,
The seven action entities (ActionSCPFiremanIgnite, ActionSCPFiremanFireball,
ActionSCPFiremanWhirl, ActionSCPFiremanMelt, ActionSCPFiremanTrueFlame,
ActionSCPFiremanStrike, ActionSCPFiremanSecondMode) are missing a parent and
icon definitions; add parent: BaseAction to each entity and add an icon entry
inside their Action component (e.g., icon: "path/to/icon.png" or reuse an
existing action icon like TerrorSpider) so the UI shows proper buttons; update
each entity block to mirror other actions' structure (parent +
Action.component.icon).
Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderMotherComponent.cs
Outdated
Show resolved
Hide resolved
Content.Server/Imperial/TerrorSpider/Components/TerrorSpiderMotherComponent.cs
Outdated
Show resolved
Hide resolved
Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs
Show resolved
Hide resolved
| private void OnPulseAction(Entity<TerrorSpiderMotherComponent> ent, ref TerrorSpiderMotherPulseActionEvent args) | ||
| { | ||
| if (args.Handled) | ||
| return; | ||
|
|
||
| var origin = Transform(ent.Owner).MapPosition; | ||
| var rangeSquared = ent.Comp.PulseRange * ent.Comp.PulseRange; | ||
| var query = EntityQueryEnumerator<DamageableComponent, TransformComponent>(); | ||
|
|
||
| while (query.MoveNext(out var target, out var damageable, out var xform)) | ||
| { | ||
| if (!IsTerrorSpider(target)) | ||
| continue; | ||
|
|
||
| if (xform.MapPosition.MapId != origin.MapId) | ||
| continue; | ||
|
|
||
| if ((xform.MapPosition.Position - origin.Position).LengthSquared() > rangeSquared) | ||
| continue; | ||
|
|
||
| ApplyHealAllDamageTypes(target, damageable, ent.Comp.PulseHealAmount); | ||
| } | ||
|
|
||
| args.Handled = true; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Значительное дублирование кода: Remote View, IsTerrorSpider, GetRemoteViewCandidates идентичны в Mother, Princess и Queen системах.
Логика OnRemoteViewNext/Previous/ExitAction, GetRemoteViewCandidates, IsTerrorSpider, и UpdateRemoteViewMovementLock практически побайтово одинакова в трёх системах. Это нарушение DRY создаёт значительную нагрузку на поддержку: исправление бага в одном месте потребует идентичных правок ещё в двух. Стоит извлечь общую логику в базовый класс или вспомогательный утилитный класс.
Also applies to: 140-194, 196-208, 220-245, 358-361
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs`
around lines 114 - 138, Multiple systems (TerrorSpiderMotherSystem, Princess,
Queen) duplicate remote-view logic (OnRemoteViewNext/Previous/ExitAction,
GetRemoteViewCandidates, IsTerrorSpider, UpdateRemoteViewMovementLock). Refactor
by extracting the shared logic into a common base class or helper (e.g.,
TerrorSpiderRemoteViewBase or TerrorSpiderRemoteViewHelper) that implements
IsTerrorSpider, GetRemoteViewCandidates, UpdateRemoteViewMovementLock and the
generic handlers for OnRemoteViewNext/Previous/ExitAction; have
TerrorSpiderMotherSystem (and the Princess/Queen systems) either inherit from
that base or delegate to the helper, remove the duplicated methods from each
system, and update event registrations to call the base/helper implementations
while preserving use of system-specific component types
(TerrorSpiderMotherComponent, etc.) via protected/parameterized accessors so
existing behavior and ent.Comp.PulseRange / PulseHealAmount usage remain
unchanged.
Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs
Outdated
Show resolved
Hide resolved
Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderPrincessSystem.cs
Show resolved
Hide resolved
| private void UpdateRemoteViewMovementLock() | ||
| { | ||
| var query = EntityQueryEnumerator<TerrorSpiderPrincessComponent>(); | ||
| while (query.MoveNext(out var uid, out var comp)) | ||
| { | ||
| if (!TryComp<EyeComponent>(uid, out var eye)) | ||
| continue; | ||
|
|
||
| var speedMultiplier = eye.Target == null ? 1f : 0f; | ||
| _movementModStatus.TryUpdateMovementSpeedModDuration( | ||
| uid, | ||
| comp.RemoteViewImmobileStatusEffect, | ||
| TimeSpan.FromSeconds(comp.RemoteViewImmobileRefresh), | ||
| speedMultiplier); | ||
| } | ||
| } |
There was a problem hiding this comment.
UpdateRemoteViewMovementLock() вызывается каждый кадр для всех Princess сущностей.
Этот метод заново применяет статус-эффект скорости каждый тик для каждой Princess, даже если состояние не изменилось. Аналогичная проблема в Queen (L371-386). Рассмотрите event-driven подход (обновлять только при изменении eye.Target), или хотя бы пропускать обновление, если текущее состояние не требует блокировки движения.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderPrincessSystem.cs`
around lines 317 - 332, UpdateRemoteViewMovementLock currently reapplies the
RemoteViewImmobileStatusEffect each tick for every TerrorSpiderPrincess (using
EyeComponent.Target,
TerrorSpiderPrincessComponent.RemoteViewImmobileStatusEffect and
RemoteViewImmobileRefresh) which is wasteful; change it to only update when the
desired lock state changes by either (a) subscribing to EyeComponent.Target
change events and calling a handler that sets/clears the movement mod, or (b)
add a guard in UpdateRemoteViewMovementLock that checks the current movement-mod
state/duration for uid (via _movementModStatus or an IsMovementLocked check) and
only call _movementModStatus.TryUpdateMovementSpeedModDuration when the computed
speedMultiplier (eye.Target == null ? 1f : 0f) differs from the currently
applied multiplier or the remaining duration is below a threshold; apply the
same pattern to the Queen code paths referenced.
| public override void Update(float frameTime) | ||
| { | ||
| base.Update(frameTime); | ||
|
|
||
| var now = _timing.CurTime; | ||
| UpdateRemoteViewMovementLock(); | ||
| UpdateHiveSpeedDebuff(); | ||
| UpdateEggs(now); | ||
| UpdateOrphanDamage(now); | ||
| } |
There was a problem hiding this comment.
Четыре полных перебора сущностей каждый кадр в Update().
UpdateRemoteViewMovementLock(), UpdateHiveSpeedDebuff(), UpdateEggs(), UpdateOrphanDamage() — каждый выполняет EntityQueryEnumerator на каждом тике. В сумме с аналогичной нагрузкой из Princess и Mother систем это может создать ощутимую нагрузку. Рассмотрите event-driven подход или guard-переменные для пропуска no-op итераций (например, пропускать UpdateOrphanDamage если нет сущностей с TerrorSpiderQueenOrphanDamageComponent).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`
around lines 61 - 70, The Update() currently runs four full entity queries every
frame (UpdateRemoteViewMovementLock(), UpdateHiveSpeedDebuff(), UpdateEggs(),
UpdateOrphanDamage()), causing excess iteration; change to an event-driven or
guarded approach by (1) adding lightweight guard/dirty flags or subscription
hooks that flip when relevant components are added/removed/changed (e.g.,
subscribe to component add/remove for TerrorSpiderQueenOrphanDamageComponent and
only schedule UpdateOrphanDamage when the flag is set), (2) cache
EntityQueryEnumerator results or store matching-entity lists and keep them
updated via component events instead of re-querying each tick, and (3)
early-return from the per-frame methods when their guard says “no work” so
Update() no longer iterates on empty sets.
Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightGuardComponent.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Content.Server/Imperial/Sanity/Components/SanityComponent.cs`:
- Around line 24-25: Поле Value в SanityComponent сейчас не инициализировано и
полагается на OnMapInit; инициализируйте Value при объявлении в классе
SanityComponent (например присвоением StartSanity) чтобы избежать попадания в
Low сразу при динамическом добавлении компонента; обновите объявление поля
Value, используя существующее поле/константу StartSanity и сохраните поведение
OnMapInit (не удаляйте вызовы), лишь уберите зависимость от обязательного вызова
OnMapInit.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 354-365: ProcessHighSanityRegen currently adds entries for every
key in damageable.Damage.DamageDict regardless of whether the current damage
value is zero; update the loop that builds the DamageSpecifier (variable
healing) to only add entries for damage types whose current damage amount is
non-zero (check damageable.Damage.DamageDict[type] != FixedPoint2.Zero or
equivalent), so you only create healing entries when there is actually damage to
heal; keep the existing flow using TryComp<DamageableComponent>,
ent.Comp.HighRegenPerType, DamageSpecifier healing and
_damageable.TryChangeDamage but skip adding keys with zero damage and avoid
calling TryChangeDamage if healing remains empty.
- Around line 367-382: ProcessLowSanitySound currently calls
_audio.PlayPvs(sound, ent) which broadcasts to everyone; change it to play the
sound only for the owning player by using _audio.PlayEntity (or the audio method
that accepts a filter) and pass a filter targeting the player entity/session
that owns this sanity entity (e.g. resolve the player actor/session via the
entity's owner/mind/ActorComponent) so heartbeat/breathing sounds from
ent.Comp.LowSanitySounds are audible only to the entity's owner rather than all
nearby entities.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs`:
- Around line 255-265: The handler OnBeforeInteractHand currently sets
args.Handled = true even when no healing occurs; only absorb the event when
healing was actually applied. Fix by checking that the target has a
DamageableComponent (e.g., HasComponent<TargetEntity, DamageableComponent> or
TryGetComponent) or by making ApplyHealAllDamageTypes return a bool indicating
success, then call ApplyHealAllDamageTypes(args.Target,
ent.Comp.TouchHealAmount) and set args.Handled = true only when healing was
performed; keep the existing IsTerrorSpider check and use the existing symbols
OnBeforeInteractHand, IsTerrorSpider, ApplyHealAllDamageTypes,
ent.Comp.TouchHealAmount, and args.Handled to locate and update the code.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightGuardComponent.cs`:
- Around line 41-42: Поле GuardEndTime должно быть сетевым и корректно
сериализоваться/корректироваться при паузах, иначе клиент получает TimeSpan.Zero
и вызывает EndGuard на каждом тике; пометьте поле GuardEndTime атрибутами
[AutoNetworkedField, TimeOffsetSerializer] и (при необходимости)
[AutoPausedField] на уровне поля/класса (см. атрибуты
[AutoGenerateComponentPause] на компоненте) чтобы значение синхронизировалось с
сервера и корректно смещалось по игровому времени; проверьте в
SharedTerrorSpiderKnightGuardSystem.Update() логику вокруг
comp.IsGuarding/comp.GuardEndTime чтобы убедиться, что клиент больше не вызывает
EndGuard преждевременно.
---
Duplicate comments:
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 332-342: Replace the fragile substring checks in
IsCoffeeLikeReagent with a HashSet of canonical reagent IDs and test for
membership; specifically, create a static readonly HashSet<string> (e.g.,
CoffeeReagentIds) containing the exact reagent IDs you consider coffee-like and
change IsCoffeeLikeReagent to return CoffeeReagentIds.Contains(reagentId)
(optionally using case-insensitive comparer if IDs vary), removing all
.Contains(...) substring checks to avoid false positives.
- Around line 510-514: Replace the hardcoded Russian popup messages in the
SanitySystem sanity-check where _popup.PopupEntity is called (the two locations
using ent.Comp.Value vs ent.Comp.LowThreshold/HighThreshold) with localized
strings via Loc.GetString; call Loc.GetString("sanity-low-warning") and
Loc.GetString("sanity-high-boost") (or similar descriptive keys) instead of the
literal texts, and add matching keys/values to the .ftl localization file so the
popup uses the localized messages.
- Around line 480-498: PrototypeInherits can recurse indefinitely; add a
recursion depth guard by introducing an optional depth parameter (e.g., int
depth = 0) and a MAX_DEPTH constant; at the start of PrototypeInherits return
false if depth >= MAX_DEPTH, and when recursing call PrototypeInherits(parent,
parentId, depth + 1). Keep existing prototype resolution via
_prototypes.TryIndex<EntityPrototype> and prototype.Parents unchanged; this will
prevent stack overflows from cycles or very deep inheritance chains (optionally
also consider adding a visited HashSet<string> if you want cycle detection in
addition to the depth limit).
- Around line 227-245: ProcessProximity is currently double-applying penalties
for hostile SCPs because ProcessScpProximity applies -ScpHostileLoss and later
IsScpNearVisible causes ModifySanity with -SeenScpLoss; update the logic so a
hostile SCP only triggers one penalty: either (A) have ProcessScpProximity
set/return a flag (e.g. sawHostileScp) and if true skip the
IsScpNearVisible/SeenScpLoss branch in ProcessProximity, or (B) move the hostile
penalty out of ProcessScpProximity and consolidate all SCP visual/hostile
penalties in the IsScpNearVisible branch; reference ProcessProximity,
ProcessScpProximity, IsScpNearVisible and the fields ent.Comp.ScpHostileLoss /
ent.Comp.SeenScpLoss and ensure only one ModifySanity call runs per hostile SCP.
- Around line 422-445: In IsNdaObjectNear, avoid substring matching for NDA
detection: replace the faction check (NpcFactionMemberComponent ->
faction.Factions / id.Id) from Contains("NDA") to an exact equality check (id.Id
== "NDA"), and replace the MetaDataComponent.EntityPrototype?.ID.Contains("NDA")
logic with a reliable test such as checking for a dedicated marker component on
the entity (e.g., NdaComponent) or comparing against an explicit whitelist of
prototype IDs (exact equality), so NDA detection uses exact/explicit identifiers
or component presence instead of substring matching.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs`:
- Around line 113-137: Duplicate remote-view logic across
TerrorSpiderMotherSystem, Princess and Queen should be consolidated: extract the
identical methods OnRemoteViewNext, OnRemoteViewPrevious,
OnRemoteViewExitAction, GetRemoteViewCandidates, IsTerrorSpider and
UpdateRemoteViewMovementLock into a shared helper class or base system (e.g.,
TerrorSpiderRemoteViewHelper or TerrorSpiderBaseSystem) and have each specific
system call into that helper; update references in TerrorSpiderMotherSystem
(methods named above) to delegate to the new shared implementation, keep method
signatures and behavior unchanged, and ensure any component/service dependencies
are passed into the helper so no system-specific state is duplicated.
- Around line 338-351: In ApplyHealAllDamageTypes, the code assigns a raw float
into DamageSpecifier.DamageDict (heal.DamageDict[damageType] = -amount), which
must use the FixedPoint2 wrapper; change assignments to use
FixedPoint2.New(-amount) (or FixedPoint2.New(amount) with appropriate sign) for
each damageType when populating the DamageSpecifier so DamageDict holds
FixedPoint2 values before calling _damageable.TryChangeDamage.
- Around line 46-93: Update's inner scan that uses
EntityQueryEnumerator<DamageableComponent, TransformComponent>() must be
replaced with a spatial query to avoid O(mothers×all). Use the entity lookup
system (EntityLookupSystem.GetEntitiesInRange or equivalent on your
_entityLookup/_lookup field) to fetch nearby entities around motherPos with
radius comp.AuraHalfRange (or a small envelope that matches the existing X/Y
half-range check), then for each returned entity call TryComp to get
DamageableComponent and TransformComponent and retain the existing checks
(mapId, X/Y half-range rectangle test), IsTerrorSpider/ApplyHealAllDamageTypes
path and _damageable.TryChangeDamage call; ensure you add/use the existing
lookup system dependency if not already present.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightGuardComponent.cs`:
- Line 15: Поле ActionEntity в компоненте TerrorSpiderKnightGuardComponent не
помечено атрибутом ViewVariables; добавьте атрибут [ViewVariables] к полю
ActionEntity (EntityUid? ActionEntity) в классе
TerrorSpiderKnightGuardComponent, чтобы оно стало доступно через систему
просмотра переменных (и при необходимости используйте соответствующие параметры
атрибута, например для чтения/записи).
In `@Resources/Prototypes/Imperial/Seriozha/SCP/SCP.yml`:
- Around line 698-782: The seven action entities (ActionSCPFiremanIgnite,
ActionSCPFiremanFireball, ActionSCPFiremanWhirl, ActionSCPFiremanMelt,
ActionSCPFiremanTrueFlame, ActionSCPFiremanStrike, ActionSCPFiremanSecondMode)
are missing parent: BaseAction and have no icon set in their Action component;
add parent: BaseAction to each entity definition and add an icon property inside
the Action component (e.g. icon: "icon-name" or an appropriate existing sprite
key) so the UI shows buttons, ensuring you update the Action component block for
each of those entity ids.
In `@Resources/Textures/Imperial/Seriozha/Fredik21/nda/nda096.rsi/meta.json`:
- Line 4: Поле copyright в meta.json должно содержать явное имя правообладателя,
а не только URL; замените значение "https://github.com/Foundation-19" на
корректную строку атрибуции, например указав имя автора (и при желании ссылку) в
поле "copyright" — например "Foundation-19 (https://github.com/Foundation-19)"
или реальное имя/организацию правообладателя, чтобы соответствовать требованиям
CC-BY-SA-3.0; отредактируйте запись в meta.json, сохранив формат JSON и кавычки.
Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 19
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 68-78: В методе OnMapInit замените захардкоженные
TimeSpan.FromSeconds(8) и TimeSpan.FromSeconds(1) на поля компонента: добавьте в
SanityComponent два [DataField] свойства (например HighRegenInterval и
LowSoundInterval/LowSoundDelay) и используйте их при инициализации NextLowSound
и NextHighRegenTick; обновите сериализацию/дефолтные значения в SanityComponent
и все места создания компонента, чтобы значения не были магическими числами.
- Around line 422-445: The IsNdaObjectNear method incorrectly reports the owner
as a nearby NDA object and doesn't check line-of-sight; update IsNdaObjectNear
to skip the owner (add a check like if (ent == uid) continue) and use the same
visibility/range check as the other methods by calling
_interaction.InRangeUnobstructed(uid, ent, radius) (or equivalent) before
treating an entity as NDA; keep the existing checks for
MetaDataComponent.EntityPrototype.ID and NpcFactionMemberComponent.Factions but
only return true when the entity is not the owner and is unobstructed/visible.
In
`@Content.Server/Imperial/SCP/SCP008/Components/SCP008InfectionAuraComponent.cs`:
- Around line 17-18: The DataField attribute on the LookupFlags field is
inconsistent with the component's other fields; update the LookupFlags
declaration in SCP008InfectionAuraComponent to use an explicit string key (match
the naming style used for fields like "radius" and "zombifyDelay") by changing
the attribute to something like [DataField("lookupFlags")] while keeping the
default value LookupFlags.Dynamic.
- Line 1: В файле с компонентом SCP008InfectionAuraComponent удалите
неиспользуемую директиву using Content.Shared.Mobs; — компонент не использует
типы из Content.Shared.Mobs (например MobState или MobStateVisuals), поэтому
просто удалите строку с using и сохраните файл; убедитесь, что класс
SCP008InfectionAuraComponent и его ссылки (LookupFlags, DataField,
ViewVariables, EntityUid, LocId) корректно собираются после удаления.
In `@Content.Server/Imperial/SCP/SCP096/Components/SCP096RageOnLookComponent.cs`:
- Around line 1-4: The using directives (System.Collections.Generic and
Robust.Shared.Audio) are placed after the namespace declaration in
SCP096RageOnLookComponent (namespace
Content.Server.Imperial.SCP.SCP096.Components); move those using lines so they
appear above the namespace declaration to follow C# conventions and match the
repository style.
- Around line 51-52: The default RageLoopSound in SCP096RageOnLookComponent is
set to an unrelated anomaly generator ambient file; update the SoundSpecifier
for the RageLoopSound field (public SoundSpecifier RageLoopSound) to point to
the correct SCP-096 loop audio asset, or if this is intentionally a placeholder,
replace the hardcoded SoundPathSpecifier with a neutral null/default value and
add an inline comment on the RageLoopSound field indicating it is a temporary
stub awaiting the proper SCP-096 audio resource so the intent is clear to future
reviewers.
In `@Content.Server/Imperial/SCP/SCP096/Systems/SCP096RageOnLookSystem.cs`:
- Around line 134-142: The code calls ResetToCalm(uid, comp) which clears
comp.IsEnraged and sets visual to Calm, but immediately afterwards
unconditionally calls UpdateStageAmbient(...) and SetVisualState(..., Chasing),
reapplying Chasing; fix by ensuring UpdateStageAmbient and
SetVisualState(Chasing) only run when the entity is still enraged — e.g., after
the curTime check either (A) return/continue immediately after ResetToCalm(uid,
comp) so the Chasing branch is skipped, or (B) re-evaluate comp.IsEnraged and
only call UpdateStageAmbient(uid, comp, true) and SetVisualState(uid,
SCP096VisualState.Chasing) when comp.IsEnraged remains true.
- Around line 198-226: The method UpdateRageTargets has an unnecessary Transform
lookup for observers and a confusing parameter name: replace the call to
_transform.GetWorldPosition(observer) with the overload that accepts the
already-obtained observerTransform (use observerTransform when computing
observerPosition), and rename the parameter target (and all its uses inside
UpdateRageTargets) to a clearer name like scp096Uid or uid to reflect that it is
the observed SCP-096 entity; update references to
targetXform/targetPosition/targetMap accordingly so the code uses the distinct
names for the SCP-096 entity vs observer variables.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs`:
- Around line 291-292: In TerrorSpiderMotherSystem, replace the immediate
deletion call pattern that checks Deleted(target) then calls Del(target) with
the idiomatic delayed deletion QueueDel(target): locate the code using the
Deleted(...) and Del(...) calls (the target local/variable passed into Del) and
change it to call QueueDel(target) so the entity is removed at end-of-frame
rather than immediately, preserving event ordering and avoiding potential ECS
processing issues.
- Line 20: Rename the private static field TerrorSpiderTag to follow project
convention (_terrorSpiderTag) and update its two usages inside the
IsTerrorSpider method; specifically change the field declaration
ProtoId<TagPrototype> TerrorSpiderTag to _terrorSpiderTag and replace both
references to TerrorSpiderTag within IsTerrorSpider so they reference
_terrorSpiderTag.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`:
- Line 30: The private static readonly field TerrorSpiderTag violates the
project's private-field naming convention; rename the field to _terrorSpiderTag
and update all references to it (e.g., usages inside TerrorSpiderQueenSystem
such as where TerrorSpiderTag is read/compared) so the symbol name is consistent
across the class; ensure the new name preserves static readonly modifiers and
that any XML docs or comments referencing the old name are updated too.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightRageComponent.cs`:
- Line 15: Add the missing ViewVariables attribute to the ActionEntity field in
the TerrorSpiderKnightRageComponent so it matches the other state fields (e.g.,
IsEnraged, RageEndTime, CachedMeleeDamage); locate the ActionEntity declaration
in the TerrorSpiderKnightRageComponent class and annotate it with
[ViewVariables] so it becomes visible in the Variable Viewer for debugging.
- Around line 41-42: RageEndTime is not being networked or paused, causing the
client to see TimeSpan.Zero and repeatedly call EndRage in
SharedTerrorSpiderKnightRageSystem.Update; mark the component class with
[AutoGenerateComponentPause] and annotate the RageEndTime field with both
[AutoNetworkedField] and [AutoPausedField] so its absolute end time replicates
to the client and respects entity pause state, then verify
EndRage/RefreshMovementSpeedModifiers/Dirty are no longer repeatedly invoked on
the client.
- Around line 56-57: In EndRage(EntityUid uid, TerrorSpiderKnightRageComponent
comp) replace the invalid reference to ent (e.g. ent.Comp.HasCachedArmor) with
the component parameter comp (use comp.HasCachedArmor) so the method uses the
passed-in TerrorSpiderKnightRageComponent; update all occurrences in EndRage
that reference ent.Comp to comp to fix the undefined variable compile error.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`:
- Around line 50-53: В методе OnShutdown нужно перед удалением действия
восстанавливать состояние ярости: если у компонента
TerrorSpiderKnightRageComponent флаг IsEnraged установлен, вызвать EndRage для
этой сущности перед удалением действия, затем только вызвать
_actions.RemoveAction(uid, comp.ActionEntity); при этом учитывать возможные
null/пустые ActionEntity и защититься от повторного завершения (если EndRage
проверяет/снимает IsEnraged). Это гарантирует откат модификаторов брони/урона до
RemoveAction.
- Around line 95-126: The compilation error is caused by an undefined variable
ent in EndRage; replace the incorrect reference ent.Comp.HasCachedArmor with the
component's property comp.HasCachedArmor so the method uses the passed-in
TerrorSpiderKnightRageComponent; keep the existing
TryComp<TerrorSpiderArmorComponent>(uid, out var armor) check and restore
armor.BruteModifier and armor.BurnModifier from comp.CachedBruteModifier and
comp.CachedBurnModifier, then Dirty(uid, armor) as currently written.
In `@Resources/Locale/ru-RU/Imperial/Seriozha/SCP/locale.ftl`:
- Around line 216-219: Remove the unused localization key
terror-spider-hive-sense-entry from locale.ftl; locate the key
"terror-spider-hive-sense-entry" in
Resources/Locale/ru-RU/Imperial/Seriozha/SCP/locale.ftl and delete it, since
TerrorSpiderQueenSystem.cs and TerrorSpiderPrincessSystem.cs currently construct
the same string via direct interpolation (e.g. $"{name}: {hp:0}/{hpMax:0} HP,
маяк: {beacon}") and the project only uses terror-spider-hive-sense-empty and
terror-spider-hive-sense-unknown.
In `@Resources/Prototypes/Imperial/Seriozha/SCP/SCP.yml`:
- Around line 756-766: В компоненте ActionSCPFiremanTrueFlame useDelay выставлен
в 2 секунды, но эффект длится 25 секунд и метод OnTrueFlameAction не ставит
кулдаун, поэтому игроки могут релоадить эффект каждые 2s; в OnTrueFlameAction
вызовите _actions.SetCooldown(ent.Comp.TrueFlameActionEntity,
ent.Comp.TrueFlameDuration) (или явно 25s) после применения эффекта, чтобы
синхронизировать блокировку действия с длительностью эффекта и предотвратить
стекание; проверьте также, что ActionSCPFiremanTrueFlame и InstantAction
используют ту же сущность TrueFlameActionEntity и что TrueFlameDuration содержит
значение в секундах.
- Around line 840-862: Remove the redundant lifetime entry from the TimedDespawn
component in the SCPFiremanWhirl entity because SCPFireWhirlComponent is the
authoritative source; OnWhirlMapInit explicitly copies
SCPFireWhirlComponent.Lifetime into TimedDespawnComponent.Lifetime, so keep
lifetime only under the SCPFireWhirl component and delete the
TimedDespawn.lifetime key to avoid dead/ignored config.
---
Duplicate comments:
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 367-382: In ProcessLowSanitySound, replace the global broadcast
call _audio.PlayPvs(sound, ent) with a per-player playback using
_audio.PlayEntity (or Play) combined with a Filter that targets the sanity
component's owner/player so the heartbeat/breathing is subjective; locate the
SanityComponent owner via ent (e.g., ent.Owner or ent.Comp.Owner) and apply a
Filter.Local or Filter.SinglePlayer for that player's session before calling
PlayEntity; also add the required using (e.g., Robust.Server.Player) for Filter
types.
In `@Content.Server/Imperial/SCP/SCP008/Systems/SCP008InfectionAuraSystem.cs`:
- Around line 58-59: Currently the system allocates a new HashSet
(currentlyInRange) and relies on _lookup.GetEntitiesInRange to return a fresh
collection each tick; reuse and avoid per-tick allocations by reusing a
pooled/field HashSet or List inside SCP008InfectionAuraSystem and by calling a
non-allocating lookup overload (or changing GetEntitiesInRange to accept a
target collection) so you fill the existing collection instead of creating a new
one; update logic that consumes currentlyInRange (and any callers using
comp.Radius/comp.LookupFlags) to clear and reuse the collection each tick rather
than allocating.
- Around line 105-115: Вместо создания новой локальной List<EntityUid> toRemove
каждый тик, используйте переиспользуемое поле (например _toRemove) или список из
пула и просто Clear() его перед использованием; затем итеративно собирайте в
него отсутсвующие EntityUid из comp.ExposureTime (через foreach var (tracked, _)
in comp.ExposureTime) и после цикла удаляйте с помощью
comp.ExposureTime.Remove(tracked) как сейчас; это уберёт per-tick аллокацию
переменной toRemove и сохранит текущую логику удаления.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs`:
- Around line 327-334: In ApplyHealAllDamageTypes, the code assigns a float into
DamageSpecifier.DamageDict instead of a FixedPoint2; change the assignment
inside the loop in ApplyHealAllDamageTypes so each entry uses
FixedPoint2.New(-amount) (i.e., set heal.DamageDict[damageType] =
FixedPoint2.New(-amount)) to match the expected FixedPoint2 type for DamageDict
values when building the DamageSpecifier.
- Around line 109-126: Multiple methods (IsTerrorSpider,
GetRemoteViewCandidates, UpdateRemoteViewMovementLock) are duplicated across
TerrorSpiderMotherSystem, TerrorSpiderQueenSystem and
TerrorSpiderPrincessSystem; extract the common logic into a shared location
(either a new abstract base class TerrorSpiderBaseSystem that these three
inherit from or a static helper class TerrorSpiderHelpers) and replace the
per-system copies with calls to the centralized implementations, keeping
original signatures used by each system (e.g., calls from OnPulseAction should
now call the extracted IsTerrorSpider and the lookup/movement lock code should
call GetRemoteViewCandidates/UpdateRemoteViewMovementLock in the helper/base
class) so fixes are applied in one place.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`:
- Around line 61-70: Update() currently triggers four full entity iterations
every frame via UpdateRemoteViewMovementLock(), UpdateHiveSpeedDebuff(),
UpdateEggs(now) and UpdateOrphanDamage(now); change these to be event-driven or
short-circuit when there is nothing to do: add guard/dirty flags or timers to
each system so the EntityQueryEnumerator only runs when relevant state changes
(or at a lower frequency), or consolidate the four queries into a single
EntityQueryEnumerator pass and dispatch per-entity work inside that loop;
specifically update the implementations of UpdateRemoteViewMovementLock,
UpdateHiveSpeedDebuff, UpdateEggs, and UpdateOrphanDamage to subscribe to
component change events (or set/clear boolean dirty flags when
components/components counts change) and bail out early if no entities need
processing, or refactor into one combined query to avoid four separate full
scans each tick.
- Line 275: Замените захардкоженный текст в вызове lines.Add($"{name}:
{hp:0}/{hpMax:0} HP, маяк: {beacon}"); на использование локализации через
Loc.GetString — вызвать Loc.GetString("terror-spider-hive-sense-brood-entry",
("name", name), ("hp", hp.ToString("0")), ("hpMax", hpMax.ToString("0")),
("beacon", beacon)) (используйте точные имена переменных name, hp, hpMax,
beacon) и добавьте в .ftl-файл запись terror-spider-hive-sense-brood-entry =
{$name}: {$hp}/{$hpMax} HP, маяк: {$beacon}.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`:
- Around line 80-88: The HasCachedArmor flag is correctly set after caching
armor modifiers in the block within SharedTerrorSpiderKnightRageSystem (check
TryComp<TerrorSpiderArmorComponent>, ent.Comp.CachedBruteModifier,
ent.Comp.CachedBurnModifier, and ent.Comp.HasCachedArmor); no code change
required—leave the assignment of HasCachedArmor = true and the subsequent armor
modifier swaps (armor.BruteModifier/armor.BurnModifier and Dirty(ent.Owner,
armor)) as is.
In `@Resources/Prototypes/Alerts/alerts.yml`:
- Around line 42-70: The Sanity alert currently re-uses the battery.rsi sprites
(states battery0–battery10) which makes it indistinguishable from
SCPFiremanPoints and BorgBattery; update the alert entries for id: Sanity and
id: SCPFiremanPoints to point to unique .rsi assets (e.g., a brain/head themed
.rsi for Sanity and a flame/fire .rsi for SCPFiremanPoints) and ensure the state
names used (battery0..battery10) match the new asset's state names (or rename
them appropriately), and do the same fix for the duplicate instances referenced
around the other block (lines 81–109) so each alert (Sanity, SCPFiremanPoints,
BorgBattery) has distinct sprite file paths and matching states.
In `@Resources/Prototypes/Imperial/Seriozha/SCP/SCP.yml`:
- Around line 699-790: The Action components for the seven action entities
(ActionSCPFiremanIgnite, ActionSCPFiremanFireball, ActionSCPFiremanWhirl,
ActionSCPFiremanMelt, ActionSCPFiremanTrueFlame, ActionSCPFiremanStrike,
ActionSCPFiremanSecondMode) are missing an icon, which makes their UI buttons
appear empty; add an icon property to each Action component (e.g., point each
Action's Action component to an appropriate sprite/resource id consistent with
other actions) so the abilities render correctly in the SS14 interface.
| private void OnMapInit(Entity<SanityComponent> ent, ref MapInitEvent args) | ||
| { | ||
| var now = _timing.CurTime; | ||
| ent.Comp.Value = Math.Clamp(ent.Comp.StartSanity, ent.Comp.MinSanity, ent.Comp.MaxSanity); | ||
| ent.Comp.NextUpdate = now + ent.Comp.UpdateInterval; | ||
| ent.Comp.NextProximityCheck = now + ent.Comp.ProximityInterval; | ||
| ent.Comp.LastDialogueTime = now; | ||
| ent.Comp.NextSilencePenalty = now + ent.Comp.SilencePenaltyInterval; | ||
| ent.Comp.NextCoffeeGain = now; | ||
| ent.Comp.NextLowSound = now + TimeSpan.FromSeconds(8); | ||
| ent.Comp.NextHighRegenTick = now + TimeSpan.FromSeconds(1); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Захардкоженные интервалы таймеров в OnMapInit.
Строки 77 и 78 используют магические числа (TimeSpan.FromSeconds(8) и TimeSpan.FromSeconds(1)). В остальных случаях интервалы берутся из полей компонента (UpdateInterval, ProximityInterval и т.д.). Для консистентности эти значения тоже стоит вынести в SanityComponent как DataField.
♻️ Пример рефакторинга
- ent.Comp.NextLowSound = now + TimeSpan.FromSeconds(8);
- ent.Comp.NextHighRegenTick = now + TimeSpan.FromSeconds(1);
+ ent.Comp.NextLowSound = now + ent.Comp.LowSoundMinInterval;
+ ent.Comp.NextHighRegenTick = now + ent.Comp.HighRegenInterval;С добавлением соответствующего [DataField] поля HighRegenInterval в SanityComponent.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs` around lines 68 - 78,
В методе OnMapInit замените захардкоженные TimeSpan.FromSeconds(8) и
TimeSpan.FromSeconds(1) на поля компонента: добавьте в SanityComponent два
[DataField] свойства (например HighRegenInterval и
LowSoundInterval/LowSoundDelay) и используйте их при инициализации NextLowSound
и NextHighRegenTick; обновите сериализацию/дефолтные значения в SanityComponent
и все места создания компонента, чтобы значения не были магическими числами.
Content.Server/Imperial/SCP/SCP008/Components/SCP008InfectionAuraComponent.cs
Outdated
Show resolved
Hide resolved
Content.Server/Imperial/SCP/SCP008/Components/SCP008InfectionAuraComponent.cs
Outdated
Show resolved
Hide resolved
| namespace Content.Server.Imperial.SCP.SCP096.Components; | ||
|
|
||
| using System.Collections.Generic; | ||
| using Robust.Shared.Audio; |
There was a problem hiding this comment.
Директивы using расположены после объявления namespace — нарушение стандартного соглашения C#.
В стандартном стиле C# (и в большинстве файлов репозитория) директивы using располагаются до файлового пространства имён.
♻️ Предлагаемое исправление
+using System.Collections.Generic;
+using Robust.Shared.Audio;
+
namespace Content.Server.Imperial.SCP.SCP096.Components;
-
-using System.Collections.Generic;
-using Robust.Shared.Audio;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| namespace Content.Server.Imperial.SCP.SCP096.Components; | |
| using System.Collections.Generic; | |
| using Robust.Shared.Audio; | |
| using System.Collections.Generic; | |
| using Robust.Shared.Audio; | |
| namespace Content.Server.Imperial.SCP.SCP096.Components; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/SCP/SCP096/Components/SCP096RageOnLookComponent.cs`
around lines 1 - 4, The using directives (System.Collections.Generic and
Robust.Shared.Audio) are placed after the namespace declaration in
SCP096RageOnLookComponent (namespace
Content.Server.Imperial.SCP.SCP096.Components); move those using lines so they
appear above the namespace declaration to follow C# conventions and match the
repository style.
Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs
Show resolved
Hide resolved
Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Content.Server/Imperial/Sanity/Components/SanityComponent.cs`:
- Around line 96-100: SanityComponent defines unused fields NearJellyGain and
NearJellyRadius; remove these dead fields from the SanityComponent class to
avoid unused configuration and confusion. Locate NearJellyGain and
NearJellyRadius in SanityComponent.cs and delete their declarations, keeping
NearHumanGain and any other used fields intact; verify SanitySystem relies on
FriendlyScpIds and ScpFriendlyGain (see SanitySystem) so no other code changes
are required. Run a build and search for references to ensure no remaining
usages before committing.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 529-533: UpdateAlert currently computes severity assuming
MaxSanity == 100 by dividing by 10 and clamping to 10; change it to compute
severity as a proportion of comp.Value over the component's MaxSanity so custom
MaxSanity works: compute a float ratio = comp.Value / comp.MaxSanity (handle
zero MaxSanity defensively), multiply by the alert severity scale (use 10 or the
alert system's max severity constant if available), floor and cast to short,
then clamp between 0 and that max severity; update the method UpdateAlert and
reference SanityComponent.MaxSanity and SanityAlert (or the alert system's max)
when implementing.
In `@Content.Server/Imperial/SCP/SCP096/Components/SCP096RageOnLookComponent.cs`:
- Line 1: Удалите избыточную директиву "using System.Collections.Generic;" в
файле SCP096RageOnLookComponent.cs — проект уже объявляет global using
System.Collections.Generic в GlobalUsings.cs, поэтому просто убрать эту строку
рядом с объявлением класса SCP096RageOnLookComponent; убедитесь, что файл
компилируется после удаления (все ссылки на HashSet/коллекции остаются
доступными через глобальный using).
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderPrincessSystem.cs`:
- Around line 344-372: In UpdateEggs, replace the immediate deletions Del(uid)
inside the EntityQueryEnumerator loop with deferred deletions QueueDel(uid) so
that entity removal is queued until end-of-frame; update both occurrences (the
Del after failing to find hatchProto and the Del after spawning/assigning brood)
to call QueueDel(uid) and keep the rest of the logic (SelectHatchPrototype,
Spawn, EnsureComp<TerrorSpiderPrincessBroodComponent>, brood assignments)
unchanged.
- Line 31: Private static readonly field name `TerrorSpiderTag` violates project
naming (should be camelCase with leading underscore); rename the field to
`_terrorSpiderTag` and update all usages accordingly (notably in the
`IsTerrorSpider` method) to reference `_terrorSpiderTag` instead of
`TerrorSpiderTag`, ensuring the declaration `private static readonly
ProtoId<TagPrototype> _terrorSpiderTag = "TerrorSpider";` and any
comparisons/uses are updated to the new identifier.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`:
- Around line 416-443: In UpdateEggs (TerrorSpiderQueenSystem) replace the two
immediate Del(uid) calls with deferred deletes using QueueDel(uid) so we don't
remove entities synchronously while iterating the EntityQueryEnumerator;
specifically change the Del(uid) after the empty/invalid SpawnPrototype branch
and the Del(uid) at the end of the spawn-handling branch to QueueDel(uid) to
avoid mutating the enumeration during iteration.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightGuardComponent.cs`:
- Around line 41-44: Duplicate [ViewVariables] attributes are applied to the
GuardEndTime field in TerrorSpiderKnightGuardComponent (the public TimeSpan
GuardEndTime field); remove the redundant [ViewVariables] so only a single
instance remains directly above GuardEndTime to eliminate the duplicate
attribute. Ensure the remaining attributes ([ViewVariables] and
[AutoNetworkedField]) keep their original order and spacing.
- Around line 41-44: Поле GuardEndTime в компоненте
TerrorSpiderKnightGuardComponent не смещается при паузе — добавьте атрибут
[AutoPausedField] к полю GuardEndTime и пометьте сам класс атрибутом
[AutoGenerateComponentPause], чтобы система автоматически корректировала
значение таймера при постановке/снятии паузы; убедитесь, что существующий
[AutoNetworkedField] остаётся и что имя поля GuardEndTime и флаг IsGuarding
используются для логики продолжения/окончания охраны.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`:
- Around line 69-74: The literal "Piercing" used in SetDamageValue should be a
shared constant to avoid magic strings and typos; define a descriptive constant
(e.g., PiercingDamageType) near the related class/enum and replace the string
literal in the TryComp<MeleeWeaponComponent> block where you call
SetDamageValue(melee.Damage, "Piercing", ent.Comp.EnragedMeleeDamage) with that
constant, ensuring EndRage/restore logic still compares/uses the same constant
and that DamageSpecifier/SetDamageValue signatures remain unchanged.
---
Duplicate comments:
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 332-342: IsCoffeeLikeReagent currently uses substring matching on
reagentId via String.Contains which yields false positives; replace it with a
definitive lookup (e.g., a static readonly HashSet<string> CoffeeReagentIds
using StringComparer.OrdinalIgnoreCase) and change
IsCoffeeLikeReagent(reagentId) to check CoffeeReagentIds.Contains(reagentId);
alternatively, if your entity system supports it, check a specific tag/component
(e.g., ICoffeeComponent or "Coffee" tag) on the reagent entity instead of
matching IDs to avoid fragile substring logic.
- Around line 227-245: ProcessProximity currently triggers GetEntitiesInRange
five times per tick; replace those repeated range queries with a single
GetEntitiesInRange call whose radius is the max of NearHumanRadius,
CorpseRadius, ScpProximityRadius, NdaRadius, then reuse that result for all
checks (pass the resulting entity enumerable to IsNearLivingHumanoid,
IsCorpseNearVisible, IsScpNearVisible, IsNdaObjectNear and ProcessScpProximity
or add overloads to those helpers to accept a precomputed entity list); this
reduces duplicate work while keeping existing logic in ProcessProximity and
helper methods like
IsNearLivingHumanoid/IsCorpseNearVisible/IsScpNearVisible/IsNdaObjectNear/ProcessScpProximity
intact.
- Around line 344-368: ProcessHighSanityRegen uses a hardcoded
TimeSpan.FromSeconds(1) for the tick interval; make this configurable by adding
a new [DataField] (e.g., HighRegenTickInterval of type TimeSpan) to
SanityComponent and use that field instead of the magic constant when setting
NextHighRegenTick in ProcessHighSanityRegen; also ensure
initialization/OnMapInit logic that uses InitialHighRegenDelay remains
consistent or is unified with the new HighRegenTickInterval so the component’s
timing (NextHighRegenTick and HighRegenPerType) is driven by the new data field
rather than a literal.
In `@Content.Server/Imperial/SCP/SCP008/Systems/SCP008InfectionAuraSystem.cs`:
- Around line 106-116: The code currently allocates a new List<EntityUid> named
toRemove on every Update call; instead, add a private field List<EntityUid>
_toRemove in SCP008InfectionAuraSystem (similar to _reusableInRange), then in
the Update method replace the local new List with _toRemove.Clear(); populate
_toRemove, iterate it to remove entries from comp.ExposureTime, and finally call
_toRemove.Clear() if needed—this reuses the list allocation and eliminates
per-tick allocations while preserving the existing logic that collects tracked
IDs then removes them.
In `@Content.Server/Imperial/SCP/SCP096/Components/SCP096RageOnLookComponent.cs`:
- Around line 51-52: The default RageLoopSound in SCP096RageOnLookComponent
(RageLoopSound: SoundSpecifier) is pointing to a generic anomaly generator
ambience file and must be replaced or documented; update the DataField default
for RageLoopSound to reference an appropriate SCP-096-specific audio resource
(or set it to a clearly named placeholder/null) and add a brief in-code comment
or DataField description indicating it is intentionally temporary if you cannot
supply the final asset yet so reviewers know it’s not a valid final sound.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderMotherSystem.cs`:
- Line 20: Private field naming violates convention: rename the private static
readonly field TerrorSpiderTag to _terrorSpiderTag and update all references;
locate the declaration of TerrorSpiderTag in TerrorSpiderMotherSystem (private
static readonly ProtoId<TagPrototype> TerrorSpiderTag = "TerrorSpider") and
change its identifier to _terrorSpiderTag, then refactor every usage in that
class (and any static helper methods) to use _terrorSpiderTag to maintain
compilation.
- Around line 219-229: The handler always marks the interaction handled even
when no healing occurred; update OnBeforeInteractHand to first verify the target
can be healed (e.g., check for DamageableComponent via TryComp/HasComp on
args.Target or modify ApplyHealAllDamageTypes to return a bool indicating
success) and only call args.Handled = true when healing was actually applied;
reference the OnBeforeInteractHand method, the ApplyHealAllDamageTypes wrapper,
TerrorSpiderMotherComponent.TouchHealAmount, and the DamageableComponent/TryComp
check so the change is localized to that interaction flow.
- Around line 266-267: Inside TerrorSpiderMotherSystem (the HitEntities
handling), don't call Del(target) immediately — replace the immediate deletion
with a deferred deletion by calling QueueDel(target) instead; keep the
Deleted(target) guard (or remove if redundant) but ensure you use QueueDel in
place of Del to avoid removing entities during HitEntities iteration and follow
SS14 idioms.
- Around line 302-315: In ApplyHealAllDamageTypes, DamageSpecifier.DamageDict
expects FixedPoint2 values but the code assigns float (-amount); change
assignments inside the loop to use FixedPoint2.New(-amount) (e.g.,
heal.DamageDict[damageType] = FixedPoint2.New(-amount)) so the dictionary stores
the correct fixed-point type before calling _damageable.TryChangeDamage; ensure
you update every assignment to DamageDict in this method to use FixedPoint2.New.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderPrincessSystem.cs`:
- Around line 374-393: Multiple methods (UpdateOrphanDamage,
BuildHiveSenseLines, FindNearestBeaconName, GetRemoteViewCandidates,
IsTerrorSpider and Remote View handlers) in TerrorSpiderPrincessSystem are
near-duplicate of implementations in TerrorSpiderQueenSystem/Mother; extract the
shared logic into a single reusable location (e.g., a common base class
TerrorSpiderBaseSystem or a static helper class TerrorSpiderHelpers) and have
TerrorSpiderPrincessSystem, TerrorSpiderQueenSystem and Mother inherit/consume
that common code; specifically move shared routines (damage tick logic from
UpdateOrphanDamage, hive sense line construction from BuildHiveSenseLines,
beacon lookup from FindNearestBeaconName, remote view candidate selection from
GetRemoteViewCandidates, type check IsTerrorSpider and the remote view handlers)
into protected/shared methods in the new base/helper and update each system to
call those methods to remove duplication while preserving existing behavior and
signatures.
- Line 481: The lines in TerrorSpiderPrincessSystem that call lines.Add
currently contain hardcoded Russian text ("маяк: {beacon}" and "неизвестно");
replace these with localization calls: use Loc.GetString for the unknown value
(Loc.GetString("terror-spider-hive-sense-unknown")) and replace the beacon
fragment with a localized string key (e.g.
Loc.GetString("terror-spider-hive-sense-beacon", ("beacon", beacon))) so the
lines.Add call in TerrorSpiderPrincessSystem uses Loc.GetString(...) instead of
hardcoded Russian; ensure the same keys/formats are consistent with
QueenSystem's usage.
- Around line 317-332: UpdateRemoteViewMovementLock currently reapplies the
movement-speed status every tick because it doesn't check whether the
remote-view target actually changed; modify the logic to only call
_movementModStatus.TryUpdateMovementSpeedModDuration when eye.Target has changed
since the last check. Add a small state field on TerrorSpiderPrincessComponent
(e.g., LastRemoteViewTarget or RemoteViewTargetId) to store the previous
eye.Target (or null), compare that stored value to eye.Target inside
UpdateRemoteViewMovementLock, and only update the stored value and call
TryUpdateMovementSpeedModDuration (using comp.RemoteViewImmobileStatusEffect and
comp.RemoteViewImmobileRefresh) when they differ; keep the comparison and update
atomic per-entity to avoid reapplying the effect every frame.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`:
- Line 30: The private readonly field TerrorSpiderTag in TerrorSpiderQueenSystem
violates the private-field naming convention; rename the field to
_terrorSpiderTag (type ProtoId<TagPrototype>) and update all references/usages
in the TerrorSpiderQueenSystem (and related members) to use _terrorSpiderTag;
ensure the declaration remains private static readonly and that any
comparisons/assignments that used TerrorSpiderTag are updated to the new
identifier to avoid compile errors.
- Line 275: The HP line in BuildHiveSenseLines is hardcoded with the Russian
label "маяк: {beacon}" — change it to use localization like the unknown-case
fix: replace the literal in TerrorSpiderQueenSystem.BuildHiveSenseLines (the
lines.Add call that constructs $"{name}: {hp:0}/{hpMax:0} HP, маяк: {beacon}")
so the beacon label is produced via Loc.GetString (e.g. a
"terror-spider-hive-sense-beacon" key with a parameter for beacon) rather than a
hardcoded string; apply the same change in the analogous code path in
TerrorSpiderPrincessSystem so both systems use the localized beacon label.
- Around line 61-70: The Update method in TerrorSpiderQueenSystem currently does
four full entity enumerations every frame (via UpdateRemoteViewMovementLock,
UpdateHiveSpeedDebuff, UpdateEggs, UpdateOrphanDamage), causing unnecessary
per-frame overhead; fix it by consolidating entity queries: perform a single
EntityQueryEnumerator (or cached list) inside Update() over the relevant
components once, collect the entities/components you need, then pass that
collection or enumerator to the four helper methods (or inline their logic to
operate on the shared collection) so those methods no longer run their own
queries; update references in UpdateRemoteViewMovementLock,
UpdateHiveSpeedDebuff, UpdateEggs and UpdateOrphanDamage to accept the shared
data (or remove their internal queries) and ensure the single enumeration covers
the component sets used by those methods.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightGuardComponent.cs`:
- Line 15: The field ActionEntity on the TerrorSpiderKnightGuardComponent is
runtime state set in OnMapInit via _actions.AddAction() and must be exposed with
[ViewVariables] (not [DataField]); update the component by adding the
[ViewVariables] attribute to the ActionEntity declaration (and remove any
[DataField] if present) so the runtime-only EntityUid is correctly viewable in
debug/VR tools.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`:
- Around line 83-91: The previous review remark about caching armor is already
implemented: TryComp<TerrorSpiderArmorComponent> sets
ent.Comp.CachedBruteModifier/ CachedBurnModifier and ent.Comp.HasCachedArmor,
and EndRage / restoration clears it; remove the duplicate review comment or mark
it resolved, and while here quickly verify the HasCachedArmor flag is reset in
EndRage and restoration code paths (references:
TryComp<TerrorSpiderArmorComponent>, ent.Comp.HasCachedArmor, EndRage) to avoid
stale comments.
| private void UpdateAlert(EntityUid uid, SanityComponent comp) | ||
| { | ||
| var severity = (short) Math.Clamp((int) MathF.Floor(comp.Value / 10f), 0, 10); | ||
| _alerts.ShowAlert(uid, SanityAlert, severity); | ||
| } |
There was a problem hiding this comment.
Расчёт серьёзности алерта жёстко привязан к MaxSanity = 100f.
Делитель 10f и максимум 10 в UpdateAlert предполагают, что MaxSanity всегда равен 100. Если прототип переопределит MaxSanity на другое значение, серьёзность будет некорректной.
♻️ Предлагаемое исправление
- var severity = (short) Math.Clamp((int) MathF.Floor(comp.Value / 10f), 0, 10);
+ var severity = (short) Math.Clamp((int) MathF.Floor(comp.Value / comp.MaxSanity * 10f), 0, 10);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs` around lines 529 -
533, UpdateAlert currently computes severity assuming MaxSanity == 100 by
dividing by 10 and clamping to 10; change it to compute severity as a proportion
of comp.Value over the component's MaxSanity so custom MaxSanity works: compute
a float ratio = comp.Value / comp.MaxSanity (handle zero MaxSanity defensively),
multiply by the alert severity scale (use 10 or the alert system's max severity
constant if available), floor and cast to short, then clamp between 0 and that
max severity; update the method UpdateAlert and reference
SanityComponent.MaxSanity and SanityAlert (or the alert system's max) when
implementing.
Content.Server/Imperial/SCP/SCP096/Components/SCP096RageOnLookComponent.cs
Outdated
Show resolved
Hide resolved
Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderPrincessSystem.cs
Outdated
Show resolved
Hide resolved
Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderPrincessSystem.cs
Show resolved
Hide resolved
Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightGuardComponent.cs
Show resolved
Hide resolved
Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 409-423: The IsCorpseNearVisible method can mistakenly consider
the owner entity as a nearby corpse; add the same self-exclusion check used in
IsNearLivingHumanoid and IsScpNearVisible by skipping the loop iteration when
ent == uid (i.e., place an if (ent == uid) continue; near the top of
IsCorpseNearVisible before checking MobStateComponent). This ensures callers
outside Update won't detect the entity itself as a corpse while leaving the rest
of the range, MobStateComponent, and unobstructed checks intact.
- Around line 31-40: Private static readonly fields violate naming conventions:
rename SanityAlert to _sanityAlert and FriendlyScpIds to _friendlyScpIds and
update all usages in this file accordingly; specifically change the declaration
of SanityAlert (ProtoId<AlertPrototype>) to _sanityAlert and FriendlyScpIds
(HashSet<string>) to _friendlyScpIds, then replace every reference to
SanityAlert (e.g., currently referenced in methods around the SanitySystem
logic) and FriendlyScpIds (e.g., checks that test SCP IDs) with the new names so
compilation and behavior remain unchanged.
- Around line 344-368: ProcessHighSanityRegen currently hardcodes
TimeSpan.FromSeconds(1); add a configurable DataField on SanityComponent (e.g.
HighRegenInterval or RepeatingHighRegenDelay) with a sensible default (matching
current 1s) and use that field instead of TimeSpan.FromSeconds(1) when setting
ent.Comp.NextHighRegenTick in ProcessHighSanityRegen; update any
documentation/comments and ensure OnMapInit still uses InitialHighRegenDelay
while the repeating tick uses the new HighRegenInterval so both the initial
delay and the recurring interval are configurable.
- Around line 171-191: Replace the hardcoded threshold 2f in
ProcessNutritionChanges with configurable fields on SanityComponent: add
DataField-backed floats (e.g., HungerThreshold and ThirstThreshold) to
SanityComponent (alongside existing EatGain/DrinkGain) and use those in the
checks instead of 2f; update ProcessNutritionChanges (method
ProcessNutritionChanges, references LastHunger, LastThirst, ModifySanity,
EatGain, DrinkGain) to compare delta >= ent.Comp.HungerThreshold and delta >=
ent.Comp.ThirstThreshold so the thresholds follow the DataField configuration
used elsewhere.
In `@Content.Server/Imperial/SCP/SCPFireman/Systems/SCPFiremanSystem.cs`:
- Around line 318-334: Когда режим SecondMode выключается из-за того, что
comp.FirePoints <= 0 (внутри цикла в SCPFiremanSystem.Update/where this loop
lives), сбросьте comp.NextSecondModeDrainTick в текущее время плюс один тик
(например now + TimeSpan.FromSeconds(1)) перед установкой comp.SecondModeEnabled
= false, чтобы предотвратить мгновенный catch-up при повторном включении;
сделайте это для обоих мест в цикле (первый break и второй break после
AddPoints) и используйте те же сущности из diff: comp.NextSecondModeDrainTick,
comp.SecondModeEnabled, AddPoints(uid, -comp.SecondModeDrainPerSecond, comp), и
now.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`:
- Around line 466-512: In ApplyScream the code currently calls _emp.EmpPulse for
every silicon mob found, causing N separate EMP pulses; change this to emit a
single EMP pulse outside the loop: inside ApplyScream keep the per-entity checks
but replace the per-entity _emp.EmpPulse call with a boolean flag (e.g.,
sawSilicon) set when HasComp<SiliconLawBoundComponent>(uid) is true; after the
entity loop, if the flag is true call _emp.EmpPulse once using the queen/origin
coordinates (Transform(queen).Coordinates or origin) with the intended radius
and the same energy/duration arguments (use comp values or introduce
comp.ScreamEmpRadius if needed), then remove the per-entity _emp.EmpPulse
invocation so only one shared pulse is produced.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`:
- Around line 85-97: The code mixes Dirty(ent) and Dirty(uid, comp) styles
causing inconsistency between the caching block and EndRage; pick and use one
style consistently (prefer the explicit Dirty(uid, comp) overload used in
EndRage for clarity). Update the block that sets HasCachedArmor and modifies
armor (the TryComp<TerrorSpiderArmorComponent> branch in
SharedTerrorSpiderKnightRageSystem) to call Dirty(ent.Owner, armor) and call
Dirty(ent.Owner, ent.Comp) (or the matching uid/comp overload) instead of
Dirty(ent) so both cache and clear paths use the same Dirty(uid, comp) form.
In `@Resources/Locale/ru-RU/Imperial/Terrorspider/spider.ftl`:
- Around line 455-463: The section header "# Knight actions" is duplicated;
rename the second header (the one preceding ent-ActionTerrorSpiderKnightGuard
and ent-ActionTerrorSpiderKnightRage) to a distinct name (e.g., "# Knight entity
actions" or "# TerrorSpider knight actions") to avoid collision with the earlier
"spider-terror-knight-*" section, then update only the header text in spider.ftl
so the keys ent-ActionTerrorSpiderKnightGuard and
ent-ActionTerrorSpiderKnightRage remain unchanged and are under the new, unique
section title.
- Around line 113-139: Several spider web localization entries
(ent-SpiderWebTerrorRusar, ent-SpiderWebTerrorDron, ent-SpiderWebTerrorLurker,
ent-SpiderWebTerrorWidow, ent-SpiderWebTerrorGuardian,
ent-SpiderWebTerrorGuardianBarrier, ent-SpiderWebTerrorHealer,
ent-SpiderWebPrincess, ent-SpiderWebTerrorUwU) are identical and
non-descriptive; update each key to a distinct human-readable name and a
matching short .desc that reflects the web's in-game effect (e.g.,
"Паутина-замедлитель" / "Замедляет движение", "Ядовитая паутина" / "Наносит яд"
, "Защитный барьер стража" keep the unique name but change .desc from «Ужасно!»
to a descriptive phrase like "Создаёт барьер, поглощающий урон"), ensure
ent-SpiderWebTerrorGuardianBarrier's description matches its unique role, and
apply similar effect-specific wording for the other keys so players can
distinguish types in-game.
---
Duplicate comments:
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs`:
- Around line 227-245: ProcessProximity currently performs five separate spatial
queries (via IsNearLivingHumanoid, ProcessScpProximity, IsCorpseNearVisible,
IsScpNearVisible, IsNdaObjectNear), causing repeated GetEntitiesInRange calls
and allocations; fix it by computing the maximum radius from the SanityComponent
fields (NearHumanRadius, ScpProximityRadius, CorpseRadius, ScpProximityRadius,
NdaRadius), call GetEntitiesInRange once for that max radius, then iterate the
returned collection once and classify each entity to decide whether it counts as
a living humanoid, corpse, scp, or NDA object (reusing the existing checks or
inlining their logic), calling ModifySanity or delegating to ProcessScpProximity
logic as appropriate; ensure you replace the multiple Is* calls in
ProcessProximity with the single-pass classification and avoid allocating
per-check collections (use the returned collection/pool directly).
- Around line 332-342: IsCoffeeLikeReagent currently does fragile substring
matching on reagentId; replace it with a robust check: either maintain a
readonly HashSet<string> (e.g., CoffeeReagentIds) containing the canonical
reagent IDs and test membership (CoffeeReagentIds.Contains(reagentId)), or
resolve the reagent prototype and check for an explicit "Coffee" tag/component
via the prototype system (use IPrototypeManager to get the ReagentPrototype and
test its Tags/HasComponent). Update IsCoffeeLikeReagent to use one of these
approaches, populate the HashSet with the real reagent IDs or check prototype
tags instead, and remove all string Contains checks.
In `@Content.Server/Imperial/SCP/SCP096/Components/SCP096RageOnLookComponent.cs`:
- Around line 50-51: SCP096RageOnLookComponent's RageLoopSound default points to
an unrelated anomaly generator audio (SoundPathSpecifier value
"/Audio/Ambience/Objects/anomaly_generator_ambi.ogg"); update the DataField
default in SCP096RageOnLookComponent by replacing the path with the correct
SCP-096 audio resource (or clearly mark it as a deliberate placeholder comment
and remove the hardcoded default so the proper sound is provided via the YAML
prototype), ensuring the component's RageLoopSound value is either a valid
SCP-096 sound file or left unset for prototype override.
In `@Content.Server/Imperial/SCP/SCPFireman/Systems/SCPFiremanSystem.cs`:
- Around line 239-248: The method OnBeforeDamageChanged in SCPFiremanSystem is
never called because SubscribeLocalEvent<SCPFiremanComponent,
BeforeDamageChangedEvent>(OnBeforeDamageChanged) is missing; open
SCPFiremanSystem.Initialize() and add that subscription so events for
BeforeDamageChanged are routed to OnBeforeDamageChanged (and remove or adjust
any duplicate/incorrect subscriptions if present), ensuring the
TrueFlameActive/TrueFlameEnd damage cancellation logic runs when the component
is active.
- Around line 373-377: The loop calls IgniteEntitiesInRadius(mapCoords,
comp.IgniteRadius, comp.FireOwner) without verifying comp.FireOwner is valid;
mirror the earlier guard (the hasOwner check used around lines 357–370) and only
call IgniteEntitiesInRadius when comp.FireOwner is valid (or otherwise pass a
safe/null source), so you don't forward EntityUid.Invalid into
_flammable.Ignite; update the while loop to check hasOwner (or validate
comp.FireOwner) before calling IgniteEntitiesInRadius.
- Around line 449-459: In TryConsumePoints replace the hardcoded Russian popup
string with a localized lookup: call
Loc.GetString("scp-fireman-not-enough-points") instead of the literal
"Недостаточно огненных очков." when invoking _popup.PopupEntity (and keep the
same PopupType/arguments); also add the key scp-fireman-not-enough-points to the
appropriate .ftl localization file with the Russian translation so the lookup
resolves. Ensure AddPoints and other logic remain unchanged.
- Around line 162-170: The code in OnWhirlAction duplicates initialization
already done in OnWhirlMapInit: avoid resetting SCPFireWhirlComponent.EndTime
and TimedDespawnComponent.Lifetime there. Update OnWhirlAction to skip setting
whirlComp.EndTime and avoid calling EnsureComp<TimedDespawnComponent> when the
values were already initialized by OnWhirlMapInit (e.g., check if
whirlComp.EndTime is non-default or if a TimedDespawnComponent already exists
via TryComp before assigning); keep assigning only FireOwner and Direction to
prevent double-initialization and lifetime drift.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`:
- Line 30: Rename the private static field TerrorSpiderTag to follow the
repository C# naming convention (use _terrorSpiderTag) and update all references
to it (e.g., usages inside TerrorSpiderQueenSystem methods such as where the tag
is queried or compared) to the new name; ensure the ProtoId<TagPrototype>
declaration remains static readonly and only the identifier is changed to
_terrorSpiderTag to match the naming used in TerrorSpiderPrincessSystem.
In
`@Content.Shared/Imperial/TerrorSpider/Components/TerrorSpiderKnightGuardComponent.cs`:
- Line 15: The field ActionEntity on TerrorSpiderKnightGuardComponent is runtime
state (initialized via _actions.AddAction() in OnMapInit) and must be annotated
with the [ViewVariables] attribute; update the declaration of ActionEntity in
class TerrorSpiderKnightGuardComponent to add [ViewVariables] so the inspector
can display it (this follows the project's convention for non-YAML-configured
component state).
- Line 8: Регрессия: отсутствуют атрибуты паузы — добавьте
[AutoGenerateComponentPause] на класс TerrorSpiderKnightGuardComponent и
пометьте поле GuardEndTime атрибутом [AutoPausedField]; это гарантирует, что
система SharedTerrorSpiderKnightGuardSystem корректно сдвинет таймер при паузе
игры. Найдите класс TerrorSpiderKnightGuardComponent и поле GuardEndTime в нём и
вставьте соответствующие атрибуты (и убедитесь, что те же правки применены к
аналогичным определениям в других участках на 41–43 строках), затем пересоберите
чтобы проверить, что пауза больше не засчитывается как прошедшее время.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`:
- Around line 52-58: OnShutdown currently ends rage with EndRage(uid, comp) when
comp.IsEnraged and then calls _actions.RemoveAction(uid, comp.ActionEntity);
ensure you guard RemoveAction against a missing/invalid action entity and only
call it when comp.ActionEntity is set (e.g., check comp.ActionEntity !=
null/invalid), and keep the existing comp.IsEnraged check before calling EndRage
to avoid null/duplicate teardown; locate OnShutdown, EndRage, comp.IsEnraged and
comp.ActionEntity to apply the guard.
- Around line 100-131: EndRage correctly restores cached state; no code changes
required—leave the EndRage method as-is (it resets comp.IsEnraged/ RageEndTime,
restores MeleeWeaponComponent and PassiveDamageComponent using
comp.CachedMeleeDamage/comp.CachedPassiveDamage, restores
TerrorSpiderArmorComponent modifiers when comp.HasCachedArmor, clears cached
fields, refreshes movement via _movement.RefreshMovementSpeedModifiers(uid), and
Dirties the relevant components).
In `@Resources/Locale/ru-RU/Imperial/Terrorspider/spider.ftl`:
- Around line 144-227: The locale file defines 22+ egg entities (e.g.,
ent-TerrorEgg1Tir, ent-TerrorEgg2Tir, ent-TerrorEggPrincessTier1,
ent-TerrorEggHealerRusar, ent-TerrorEggQueenReaper,
ent-TerrorEggPrincessDestroyer, etc.) but they all share the identical name and
description; update each ent-TerrorEgg* key to have distinct,
context-appropriate Russian names and descriptions (reflecting tier, role,
queen/princess variants and any special traits) by copying the correct unique
strings from the English source or design doc and translating them consistently
so each entity key has a unique .desc and label matching its variant.
- Around line 33-67: The Angry-variant mob descriptions are exact duplicates of
the base mob descriptions (e.g., ent-MobRusarSpider, ent-MobDronSpider,
ent-MobsogladatelSpider, etc.), so remove the duplicated .desc blocks for the
*Angry* variants and make those Angry keys reuse the base key's description
instead; specifically, for each pair like ent-MobX and ent-MobXAngry, delete the
duplicated .desc text under the Angry key and point ent-MobXAngry.desc to reuse
ent-MobX.desc (or otherwise alias the Angry key to the base key) so there is a
single source of truth for each mob description.
- Around line 285-295: The four implant entries ent-LightImplantSpiderRusar,
ent-LightImplantSpiderDron, ent-LightImplantSpiderGuardian and
ent-LightImplantSpiderPrincess (and their action counterparts
ent-ActionSpawnSpiderRusar, ent-ActionSpawnSpiderDron,
ent-ActionSpawnSpiderGuardian, ent-ActionSpawnSpiderPrincess) currently share
identical name and description; update each name (. = display label) and .desc
to include the spider type or a distinguishing trait (e.g., "создание паутины
(Rusar) — прочная паутина", "создание паутины (Princess) — обширная паутина") or
specific effect/size so players can tell them apart; keep the Russian phrasing
consistent and concise and reflect any in-game mechanical differences (strength,
size, effect) in the corresponding entries for both ent-LightImplantSpider* and
ent-ActionSpawnSpider*.
In `@Resources/Prototypes/Imperial/Seriozha/SCP/SCP.yml`:
- Around line 699-790: All seven action entities (ActionSCPFiremanIgnite,
ActionSCPFiremanFireball, ActionSCPFiremanWhirl, ActionSCPFiremanMelt,
ActionSCPFiremanTrueFlame, ActionSCPFiremanStrike, ActionSCPFiremanSecondMode)
are missing an icon in their Action component; open each entity and add an
"icon: <resource_id>" (or appropriate icon reference used elsewhere) inside the
components -> type: Action block so the UI buttons render correctly, ensuring
you use the correct icon resource names consistent with project conventions.
| private static readonly ProtoId<AlertPrototype> SanityAlert = "Sanity"; | ||
| private const string ScpBaseParent = "ImperialSCPBase"; | ||
| private const string ScpPresetParent = "ImperialSCPBasePreset"; | ||
|
|
||
| private static readonly HashSet<string> FriendlyScpIds = | ||
| [ | ||
| "ImperialSCPNDA131", | ||
| "ImperialSCPNDA131A", | ||
| "ImperialSCPJelly", | ||
| ]; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Нарушение соглашения именования для private static readonly полей.
Поля SanityAlert (строка 31) и FriendlyScpIds (строки 35–40) являются приватными static readonly полями и должны начинаться с нижнего подчёркивания и использовать camelCase согласно требованиям проекта. Соответственно необходимо обновить все места использования этих идентификаторов в файле.
♻️ Предлагаемое исправление
- private static readonly ProtoId<AlertPrototype> SanityAlert = "Sanity";
+ private static readonly ProtoId<AlertPrototype> _sanityAlert = "Sanity";
private const string ScpBaseParent = "ImperialSCPBase";
private const string ScpPresetParent = "ImperialSCPBasePreset";
- private static readonly HashSet<string> FriendlyScpIds =
+ private static readonly HashSet<string> _friendlyScpIds =
[
"ImperialSCPNDA131",
"ImperialSCPNDA131A",
"ImperialSCPJelly",
];Также заменить SanityAlert → _sanityAlert (строки 92, 532) и FriendlyScpIds → _friendlyScpIds (строки 267, 474).
Based on learnings: "Enforce C# private field naming: all private fields must start with an underscore and use camelCase (e.g., _transformSystem). This rule applies across the repository (imperial-space/SS14-public) to maintain consistency in all C# files."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs` around lines 31 - 40,
Private static readonly fields violate naming conventions: rename SanityAlert to
_sanityAlert and FriendlyScpIds to _friendlyScpIds and update all usages in this
file accordingly; specifically change the declaration of SanityAlert
(ProtoId<AlertPrototype>) to _sanityAlert and FriendlyScpIds (HashSet<string>)
to _friendlyScpIds, then replace every reference to SanityAlert (e.g., currently
referenced in methods around the SanitySystem logic) and FriendlyScpIds (e.g.,
checks that test SCP IDs) with the new names so compilation and behavior remain
unchanged.
| private void ProcessNutritionChanges(Entity<SanityComponent> ent) | ||
| { | ||
| if (TryComp<HungerComponent>(ent, out var hungerComp)) | ||
| { | ||
| var current = _hunger.GetHunger(hungerComp); | ||
| var delta = current - ent.Comp.LastHunger; | ||
| if (delta >= 2f) | ||
| ModifySanity(ent, ent.Comp.EatGain); | ||
|
|
||
| ent.Comp.LastHunger = current; | ||
| } | ||
|
|
||
| if (TryComp<ThirstComponent>(ent, out var thirstComp)) | ||
| { | ||
| var delta = thirstComp.CurrentThirst - ent.Comp.LastThirst; | ||
| if (delta >= 2f) | ||
| ModifySanity(ent, ent.Comp.DrinkGain); | ||
|
|
||
| ent.Comp.LastThirst = thirstComp.CurrentThirst; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Магическое число 2f в пороге срабатывания прироста санити при еде/питье.
Пороговые значения 2f (строки 177 и 186) определяют минимальный прирост голода/жажды, при котором начисляется санити. Эти числа захардкожены, тогда как аналогичные игровые параметры вынесены в DataField. Если системы питания изменят единицы измерения, порог не обновится.
♻️ Предлагаемое исправление
Добавить в SanityComponent.cs:
+ [DataField("nutritionGainDelta")]
+ public float NutritionGainDelta = 2f;В SanitySystem.cs:
- if (delta >= 2f)
+ if (delta >= ent.Comp.NutritionGainDelta)
ModifySanity(ent, ent.Comp.EatGain);и аналогично для строки 186 (DrinkGain).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs` around lines 171 -
191, Replace the hardcoded threshold 2f in ProcessNutritionChanges with
configurable fields on SanityComponent: add DataField-backed floats (e.g.,
HungerThreshold and ThirstThreshold) to SanityComponent (alongside existing
EatGain/DrinkGain) and use those in the checks instead of 2f; update
ProcessNutritionChanges (method ProcessNutritionChanges, references LastHunger,
LastThirst, ModifySanity, EatGain, DrinkGain) to compare delta >=
ent.Comp.HungerThreshold and delta >= ent.Comp.ThirstThreshold so the thresholds
follow the DataField configuration used elsewhere.
| private void ProcessHighSanityRegen(Entity<SanityComponent> ent, TimeSpan now) | ||
| { | ||
| if (ent.Comp.State != SanityState.High) | ||
| return; | ||
|
|
||
| if (now < ent.Comp.NextHighRegenTick) | ||
| return; | ||
|
|
||
| ent.Comp.NextHighRegenTick = now + TimeSpan.FromSeconds(1); | ||
|
|
||
| if (!TryComp<DamageableComponent>(ent, out var damageable)) | ||
| return; | ||
|
|
||
| var healing = new DamageSpecifier(); | ||
| foreach (var kv in damageable.Damage.DamageDict) | ||
| { | ||
| if (kv.Value == FixedPoint2.Zero) | ||
| continue; | ||
|
|
||
| healing.DamageDict[kv.Key] = FixedPoint2.New(-ent.Comp.HighRegenPerType); | ||
| } | ||
|
|
||
| if (!healing.Empty) | ||
| _damageable.TryChangeDamage(ent.Owner, healing, ignoreResistances: true, interruptsDoAfters: false); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Захардкоженный интервал 1 секунда в ProcessHighSanityRegen.
Строка 352 использует TimeSpan.FromSeconds(1) напрямую для планирования следующего тика регенерации, тогда как начальная задержка (OnMapInit, строка 78) вынесена в DataField InitialHighRegenDelay. Интервал повторяющегося тика также должен быть настраиваемым через DataField.
♻️ Предлагаемое исправление
Добавить в SanityComponent.cs:
+ [DataField("highRegenInterval")]
+ public TimeSpan HighRegenInterval = TimeSpan.FromSeconds(1);В SanitySystem.cs:
- ent.Comp.NextHighRegenTick = now + TimeSpan.FromSeconds(1);
+ ent.Comp.NextHighRegenTick = now + ent.Comp.HighRegenInterval;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs` around lines 344 -
368, ProcessHighSanityRegen currently hardcodes TimeSpan.FromSeconds(1); add a
configurable DataField on SanityComponent (e.g. HighRegenInterval or
RepeatingHighRegenDelay) with a sensible default (matching current 1s) and use
that field instead of TimeSpan.FromSeconds(1) when setting
ent.Comp.NextHighRegenTick in ProcessHighSanityRegen; update any
documentation/comments and ensure OnMapInit still uses InitialHighRegenDelay
while the repeating tick uses the new HighRegenInterval so both the initial
delay and the recurring interval are configurable.
| private bool IsCorpseNearVisible(EntityUid uid, float radius) | ||
| { | ||
| foreach (var ent in _lookup.GetEntitiesInRange(uid, radius, LookupFlags.Dynamic)) | ||
| { | ||
| if (!TryComp<MobStateComponent>(ent, out var state) || state.CurrentState != MobState.Dead) | ||
| continue; | ||
|
|
||
| if (!_interaction.InRangeUnobstructed(uid, ent, radius + 0.1f)) | ||
| continue; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } |
There was a problem hiding this comment.
IsCorpseNearVisible не исключает саму сущность-владельца.
В отличие от IsNearLivingHumanoid (строка 391) и IsScpNearVisible (строка 460), метод IsCorpseNearVisible не содержит проверки if (ent == uid) continue;. В текущем потоке это безопасно, поскольку Update обрабатывает только живые сущности, — но при вызове метода из другого контекста сущность обнаружит себя как труп, если окажется мёртвой.
🐛 Предлагаемое исправление
private bool IsCorpseNearVisible(EntityUid uid, float radius)
{
foreach (var ent in _lookup.GetEntitiesInRange(uid, radius, LookupFlags.Dynamic))
{
+ if (ent == uid)
+ continue;
+
if (!TryComp<MobStateComponent>(ent, out var state) || state.CurrentState != MobState.Dead)
continue;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/Sanity/Systems/SanitySystem.cs` around lines 409 -
423, The IsCorpseNearVisible method can mistakenly consider the owner entity as
a nearby corpse; add the same self-exclusion check used in IsNearLivingHumanoid
and IsScpNearVisible by skipping the loop iteration when ent == uid (i.e., place
an if (ent == uid) continue; near the top of IsCorpseNearVisible before checking
MobStateComponent). This ensures callers outside Update won't detect the entity
itself as a corpse while leaving the rest of the range, MobStateComponent, and
unobstructed checks intact.
| while (now >= comp.NextSecondModeDrainTick) | ||
| { | ||
| if (comp.FirePoints <= 0f) | ||
| { | ||
| comp.SecondModeEnabled = false; | ||
| break; | ||
| } | ||
|
|
||
| AddPoints(uid, -comp.SecondModeDrainPerSecond, comp); | ||
| comp.NextSecondModeDrainTick += TimeSpan.FromSeconds(1); | ||
|
|
||
| if (comp.FirePoints <= 0f) | ||
| { | ||
| comp.SecondModeEnabled = false; | ||
| break; | ||
| } | ||
| } |
There was a problem hiding this comment.
NextSecondModeDrainTick не сбрасывается при авто-отключении — немедленный catch-up при повторном включении.
Когда SecondModeEnabled устанавливается в false из-за нулевых очков (первый break, строка 322), NextSecondModeDrainTick остаётся в прошлом. При повторном включении режима цикл while выполняет catch-up за все пропущенные тики подряд, мгновенно обнуляя все накопленные очки.
🐛 Предлагаемое исправление
if (comp.FirePoints <= 0f)
{
comp.SecondModeEnabled = false;
+ comp.NextSecondModeDrainTick = now + TimeSpan.FromSeconds(1);
break;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/SCP/SCPFireman/Systems/SCPFiremanSystem.cs` around
lines 318 - 334, Когда режим SecondMode выключается из-за того, что
comp.FirePoints <= 0 (внутри цикла в SCPFiremanSystem.Update/where this loop
lives), сбросьте comp.NextSecondModeDrainTick в текущее время плюс один тик
(например now + TimeSpan.FromSeconds(1)) перед установкой comp.SecondModeEnabled
= false, чтобы предотвратить мгновенный catch-up при повторном включении;
сделайте это для обоих мест в цикле (первый break и второй break после
AddPoints) и используйте те же сущности из diff: comp.NextSecondModeDrainTick,
comp.SecondModeEnabled, AddPoints(uid, -comp.SecondModeDrainPerSecond, comp), и
now.
| private void ApplyScream(EntityUid queen, TerrorSpiderQueenComponent comp) | ||
| { | ||
| var origin = Transform(queen).MapPosition; | ||
| var rangeSquared = comp.ScreamRange * comp.ScreamRange; | ||
| var query = EntityQueryEnumerator<MobStateComponent, TransformComponent>(); | ||
|
|
||
| while (query.MoveNext(out var uid, out var mobState, out var xform)) | ||
| { | ||
| if (uid == queen) | ||
| continue; | ||
|
|
||
| if (mobState.CurrentState != MobState.Alive) | ||
| continue; | ||
|
|
||
| if (xform.MapPosition.MapId != origin.MapId) | ||
| continue; | ||
|
|
||
| if ((xform.MapPosition.Position - origin.Position).LengthSquared() > rangeSquared) | ||
| continue; | ||
|
|
||
| if (!IsTerrorSpider(uid)) | ||
| { | ||
| _movementModStatus.TryUpdateMovementSpeedModDuration( | ||
| uid, | ||
| MovementModStatusSystem.TaserSlowdown, | ||
| TimeSpan.FromSeconds(comp.ScreamSlowDuration), | ||
| comp.ScreamSlowMultiplier); | ||
|
|
||
| _stamina.TakeStaminaDamage(uid, comp.ScreamStaminaDamage, source: queen); | ||
| } | ||
|
|
||
| if (HasComp<SiliconLawBoundComponent>(uid)) | ||
| { | ||
| _emp.EmpPulse( | ||
| xform.Coordinates, | ||
| 0.2f, | ||
| comp.ScreamEmpEnergyConsumption, | ||
| TimeSpan.FromSeconds(comp.ScreamRobotDisableSeconds), | ||
| queen); | ||
| } | ||
| } | ||
|
|
||
| BreakLightsInRange(queen, comp.LightBreakHalfRange); | ||
|
|
||
| if (comp.ScreamSound != null) | ||
| _audio.PlayPvs(comp.ScreamSound, queen); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
ApplyScream: EMP-пульс создаётся на каждого silicon-моба отдельно.
Для каждого SiliconLawBoundComponent в радиусе вызывается _emp.EmpPulse с радиусом 0.2f. При большом скоплении silicon-мобов в зоне крика это порождает N отдельных EMP-пульсов, каждый из которых внутренне итерирует сущности. Если это намеренный дизайн «точечного EMP по каждому боргу» — ОК, но если нужен один общий импульс, лучше вынести EmpPulse за цикл с бо́льшим радиусом.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Content.Server/Imperial/TerrorSpider/Systems/TerrorSpiderQueenSystem.cs`
around lines 466 - 512, In ApplyScream the code currently calls _emp.EmpPulse
for every silicon mob found, causing N separate EMP pulses; change this to emit
a single EMP pulse outside the loop: inside ApplyScream keep the per-entity
checks but replace the per-entity _emp.EmpPulse call with a boolean flag (e.g.,
sawSilicon) set when HasComp<SiliconLawBoundComponent>(uid) is true; after the
entity loop, if the flag is true call _emp.EmpPulse once using the queen/origin
coordinates (Transform(queen).Coordinates or origin) with the intended radius
and the same energy/duration arguments (use comp values or introduce
comp.ScreamEmpRadius if needed), then remove the per-entity _emp.EmpPulse
invocation so only one shared pulse is produced.
| if (TryComp<TerrorSpiderArmorComponent>(ent.Owner, out var armor)) | ||
| { | ||
| ent.Comp.CachedBruteModifier = armor.BruteModifier; | ||
| ent.Comp.CachedBurnModifier = armor.BurnModifier; | ||
| ent.Comp.HasCachedArmor = true; | ||
| armor.BruteModifier = ent.Comp.BruteIncomingMultiplier; | ||
| armor.BurnModifier = ent.Comp.BurnIncomingMultiplier; | ||
| Dirty(ent.Owner, armor); | ||
| } | ||
|
|
||
| _movement.RefreshMovementSpeedModifiers(ent.Owner); | ||
| Dirty(ent); | ||
| args.Handled = true; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
HasCachedArmor и Dirty(ent) / Dirty(uid, comp) — несогласованность стиля.
Флаг HasCachedArmor правильно устанавливается на строке 89 — замечание из предыдущего ревью учтено. Однако на строке 96 используется Dirty(ent) (перегрузка Entity<T>), тогда как в EndRage (строка 130) используется Dirty(uid, comp). Оба варианта эквивалентны, но для единообразия стоит придерживаться одного стиля.
♻️ Предлагаемое исправление
- Dirty(ent);
+ Dirty(ent.Owner, ent.Comp);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@Content.Shared/Imperial/TerrorSpider/Systems/SharedTerrorSpiderKnightRageSystem.cs`
around lines 85 - 97, The code mixes Dirty(ent) and Dirty(uid, comp) styles
causing inconsistency between the caching block and EndRage; pick and use one
style consistently (prefer the explicit Dirty(uid, comp) overload used in
EndRage for clarity). Update the block that sets HasCachedArmor and modifies
armor (the TryComp<TerrorSpiderArmorComponent> branch in
SharedTerrorSpiderKnightRageSystem) to call Dirty(ent.Owner, armor) and call
Dirty(ent.Owner, ent.Comp) (or the matching uid/comp overload) instead of
Dirty(ent) so both cache and clear paths use the same Dirty(uid, comp) form.
| ent-SpiderWebTerrorRusar = паутина паука ужаса | ||
| .desc = УЖАСНО! | ||
| .suffix = пауки ужаса | ||
| .desc = Ужасно! | ||
|
|
||
| ent-SpiderWebTerrorDron = паутина паука ужаса | ||
| .desc = УЖАСНО! | ||
| .suffix = пауки ужаса | ||
| .desc = Ужасно! | ||
|
|
||
| ent-WeaponTerrorSpit = плевок ужаса | ||
| .desc = ПлЭвок | ||
| .suffix = пауки ужаса | ||
| ent-SpiderWebTerrorLurker = паутина паука ужаса | ||
| .desc = Ужасно! | ||
|
|
||
| ent-TerrorSpitBullet = плевок ужаса | ||
| .desc = ПлЭвок | ||
| .suffix = пауки ужаса | ||
| ent-SpiderWebTerrorWidow = паутина паука ужаса | ||
| .desc = Ужасно! | ||
|
|
||
| ent-SpiderWebTerrorGuardian = паутина паука ужаса | ||
| .desc = Ужасно! | ||
|
|
||
| ent-SpiderWebTerrorGuardianBarrier = защитный барьер стража | ||
| .desc = Ужасно! | ||
|
|
||
| ent-SpiderWebTerrorHealer = паутина паука ужаса | ||
| .desc = Ужасно! | ||
|
|
||
| ent-SpiderWebPrincess = паутина паука ужаса | ||
| .desc = Ужасно! | ||
|
|
||
| ent-SpiderWebTerrorUwU = паутина паука ужаса | ||
| .desc = Ужасно! | ||
|
|
There was a problem hiding this comment.
Все паутины неотличимы по названию и описанию.
Восемь из девяти сущностей паутины имеют одинаковое название паутина паука ужаса и описание Ужасно!. Если у разных паутин (барьер стража, паутина вдовы, соглядатая и т.д.) различаются игровые эффекты (замедление, яд, барьер), игрок при осмотре не сможет понять, с чем имеет дело.
Дополнительно: ent-SpiderWebTerrorGuardianBarrier (строка 128) имеет уникальное название защитный барьер стража, однако его описание всё равно Ужасно! — это несоответствие по сравнению с другими элементами, у которых название совпадает с описанием по степени «информативности».
💡 Пример дифференциации
-ent-SpiderWebTerrorGuardianBarrier = защитный барьер стража
- .desc = Ужасно!
+ent-SpiderWebTerrorGuardianBarrier = защитный барьер стража
+ .desc = Непробиваемый барьер, возведённый стражем ужаса.Аналогично для остальных типов паутины — добавить краткое указание на эффект (замедление, яд, кислота и т.д.).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Locale/ru-RU/Imperial/Terrorspider/spider.ftl` around lines 113 -
139, Several spider web localization entries (ent-SpiderWebTerrorRusar,
ent-SpiderWebTerrorDron, ent-SpiderWebTerrorLurker, ent-SpiderWebTerrorWidow,
ent-SpiderWebTerrorGuardian, ent-SpiderWebTerrorGuardianBarrier,
ent-SpiderWebTerrorHealer, ent-SpiderWebPrincess, ent-SpiderWebTerrorUwU) are
identical and non-descriptive; update each key to a distinct human-readable name
and a matching short .desc that reflects the web's in-game effect (e.g.,
"Паутина-замедлитель" / "Замедляет движение", "Ядовитая паутина" / "Наносит яд"
, "Защитный барьер стража" keep the unique name but change .desc from «Ужасно!»
to a descriptive phrase like "Создаёт барьер, поглощающий урон"), ensure
ent-SpiderWebTerrorGuardianBarrier's description matches its unique role, and
apply similar effect-specific wording for the other keys so players can
distinguish types in-game.
| ent-WeaponTerrorSpit = плевок ужаса | ||
| .desc = Плевок | ||
|
|
||
| ent-TerrorSpitBullet = плевок ужаса | ||
| .desc = Плевок | ||
|
|
||
| ent-TerrorDrontAcid = кислотный плевок ужаса | ||
| .desc = Кислота | ||
|
|
||
| ent-TerrorRavAcid = ЭМИ-кислотный плевок ужаса | ||
| .desc = Кислота | ||
|
|
||
| ent-BulletGrenadeEMPToxin = ЭМИ-плевок ужаса | ||
| .desc = ЭМИ | ||
|
|
||
| ent-TerrorWidowSpit = плевок вдовы | ||
| .desc = Яд | ||
|
|
||
| ent-TerrorWidowSmokeSpitBullet = дымный плевок вдовы | ||
| .desc = Дым | ||
|
|
||
| ent-TerrorWidowPoisonSpitBullet = ядовитый плевок вдовы | ||
| .desc = Яд | ||
|
|
||
| ent-TerrorPrincessSpit = плевок принцессы | ||
| .desc = Плевок | ||
|
|
||
| ent-TerrorQueentAcid = кислотный плевок королевы | ||
| .desc = Кислота | ||
|
|
||
| ent-ProjectileHealingBoltTerror = лечащий сгусток | ||
| .desc = Я ПРИКАЗЫВАЮ ТЕБЕ ЖИТЬ! | ||
|
|
||
| ent-WeaponXenoSpit = плевок ужаса | ||
| .desc = Концентрированная токсичность | ||
| .suffix = пауки ужаса | ||
|
|
||
| ent-GasXenoSpitBullet = газовый плевок ксено | ||
| .desc = Концентрированная токсичность | ||
| .suffix = пауки ужаса | ||
|
|
There was a problem hiding this comment.
Описания снарядов состоят из одного слова и не отражают функцию.
Несколько снарядов имеют описание в виде единственного слова (Плевок, Кислота, ЭМИ, Яд, Дым). Отдельно: ent-TerrorRavAcid (строка 251) называется ЭМИ-кислотный плевок ужаса, но его описание — Кислота, что противоречит ЭМИ-составляющей названия. Если снаряды можно осмотреть в игре, рекомендуется уточнить описания.
💡 Пример исправления
-ent-TerrorRavAcid = ЭМИ-кислотный плевок ужаса
- .desc = Кислота
+ent-TerrorRavAcid = ЭМИ-кислотный плевок ужаса
+ .desc = Кислотный плевок с электромагнитным импульсом.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ent-WeaponTerrorSpit = плевок ужаса | |
| .desc = Плевок | |
| ent-TerrorSpitBullet = плевок ужаса | |
| .desc = Плевок | |
| ent-TerrorDrontAcid = кислотный плевок ужаса | |
| .desc = Кислота | |
| ent-TerrorRavAcid = ЭМИ-кислотный плевок ужаса | |
| .desc = Кислота | |
| ent-BulletGrenadeEMPToxin = ЭМИ-плевок ужаса | |
| .desc = ЭМИ | |
| ent-TerrorWidowSpit = плевок вдовы | |
| .desc = Яд | |
| ent-TerrorWidowSmokeSpitBullet = дымный плевок вдовы | |
| .desc = Дым | |
| ent-TerrorWidowPoisonSpitBullet = ядовитый плевок вдовы | |
| .desc = Яд | |
| ent-TerrorPrincessSpit = плевок принцессы | |
| .desc = Плевок | |
| ent-TerrorQueentAcid = кислотный плевок королевы | |
| .desc = Кислота | |
| ent-ProjectileHealingBoltTerror = лечащий сгусток | |
| .desc = Я ПРИКАЗЫВАЮ ТЕБЕ ЖИТЬ! | |
| ent-WeaponXenoSpit = плевок ужаса | |
| .desc = Концентрированная токсичность | |
| .suffix = пауки ужаса | |
| ent-GasXenoSpitBullet = газовый плевок ксено | |
| .desc = Концентрированная токсичность | |
| .suffix = пауки ужаса | |
| ent-WeaponTerrorSpit = плевок ужаса | |
| .desc = Плевок | |
| ent-TerrorSpitBullet = плевок ужаса | |
| .desc = Плевок | |
| ent-TerrorDrontAcid = кислотный плевок ужаса | |
| .desc = Кислота | |
| ent-TerrorRavAcid = ЭМИ-кислотный плевок ужаса | |
| .desc = Кислотный плевок с электромагнитным импульсом. | |
| ent-BulletGrenadeEMPToxin = ЭМИ-плевок ужаса | |
| .desc = ЭМИ | |
| ent-TerrorWidowSpit = плевок вдовы | |
| .desc = Яд | |
| ent-TerrorWidowSmokeSpitBullet = дымный плевок вдовы | |
| .desc = Дым | |
| ent-TerrorWidowPoisonSpitBullet = ядовитый плевок вдовы | |
| .desc = Яд | |
| ent-TerrorPrincessSpit = плевок принцессы | |
| .desc = Плевок | |
| ent-TerrorQueentAcid = кислотный плевок королевы | |
| .desc = Кислота | |
| ent-ProjectileHealingBoltTerror = лечащий сгусток | |
| .desc = Я ПРИКАЗЫВАЮ ТЕБЕ ЖИТЬ! | |
| ent-WeaponXenoSpit = плевок ужаса | |
| .desc = Концентрированная токсичность | |
| ent-GasXenoSpitBullet = газовый плевок ксено | |
| .desc = Концентрированная токсичность |
| # ========================================== | ||
| # Knight actions | ||
| # ========================================== | ||
|
|
||
| ent-ActionTerrorSpiderKnightGuard = защита | ||
| .desc = Оборонительная стойка: замедление, снижение урона, усиление регенерации. | ||
|
|
||
| ent-ActionTerrorSpiderKnightRage = ярость | ||
| .desc = Яростная стойка: ускорение, увеличение урона, отключение регенерации. |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Дублирующийся заголовок секции # Knight actions.
Заголовок # Knight actions встречается дважды: на строке 18 (для строковых ключей spider-terror-knight-*) и на строке 455 (для ключей сущностей ent-ActionTerrorSpiderKnight*). Рекомендуется переименовать вторую секцию, чтобы различать их.
💡 Предлагаемое исправление
-# Knight actions
+# Knight entity actions (ent-*)
ent-ActionTerrorSpiderKnightGuard = защита🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Resources/Locale/ru-RU/Imperial/Terrorspider/spider.ftl` around lines 455 -
463, The section header "# Knight actions" is duplicated; rename the second
header (the one preceding ent-ActionTerrorSpiderKnightGuard and
ent-ActionTerrorSpiderKnightRage) to a distinct name (e.g., "# Knight entity
actions" or "# TerrorSpider knight actions") to avoid collision with the earlier
"spider-terror-knight-*" section, then update only the header text in spider.ftl
so the keys ent-ActionTerrorSpiderKnightGuard and
ent-ActionTerrorSpiderKnightRage remain unchanged and are under the new, unique
section title.
… action from dron
…mageModifyEvent (changes were silently discarded)
…ay, RadiationSource, Toxin puddle damage
… DrawableSolution
О ПР`е
Тип: feat
Изменения: Ивентовый контент
Технические детали
Нет
Изменения кода официальных разработчиков
Нет
Summary by CodeRabbit
Новые возможности
Terror Spider
Локализация и ресурсы
Алерты