[PORT] Neuroaversão#752
Conversation
Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> Co-authored-by: funkystationbot <funky@funkystation.org>
Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> Co-authored-by: funkystationbot <funky@funkystation.org>
Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> Co-authored-by: funkystationbot <funky@funkystation.org>
* refactor neuroaversion and chronic migraines refactor neuroaversion and chronic migraines wait improve nightmare code fix fuck * remove comment * chore: Automatically update REUSE headers * Delete triggerseizurecomponent * chore: Automatically update REUSE headers * "are you stupid" me: * mmm --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> Co-authored-by: funkystationbot <funky@funkystation.org>
WalkthroughAdição completa de dois traços neurológicos jogáveis (Enxaquecas Crônicas e Neuroaversão) com componentes sincronizados, sistemas de orquestração, renderização visual com shader, áudio preditivo e mecânicas de dano/movimento incluindo gatilho probabilístico de convulsão via acúmulo de "seizure build". ChangesSistema de Enxaqueca e Convulsão com Neuroaversão
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
Kyoth25f
left a comment
There was a problem hiding this comment.
altere os nomes das funções, nome das variáveis, comentários e documentação de acordo com a mudança de todo implante afetar.
There was a problem hiding this comment.
Actionable comments posted: 12
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Content.Client/Traits/Assorted/MigraineSystem.cs`:
- Around line 54-59: Content.Client/Traits/Assorted/MigraineSystem.cs (54-59):
change the guard that currently only sets _playedStartSound = false when
LocalSession?.AttachedEntity is null to instead detect when AttachedEntity !=
_lastLocalEntity and reset _playedStartSound (and update _lastLocalEntity) so
flags are cleared on body swap, not only on detach;
Content.Client/Traits/Assorted/MigraineSystem.cs (77-84): on the detach branch
also clear the tracked _lastLocalEntity (or set to EntityUid.Invalid) in
addition to existing per-component resets so there is no carry‑over after swap;
Content.Client/Traits/Assorted/SeizureSystem.cs (42-48): detect change of
LocalSession?.AttachedEntity compared to a stored _lastLocalEntity and reset
_playedProdromeSound and _playedSeizureSound when the local entity changes
(update _lastLocalEntity as well);
Content.Client/Traits/Assorted/SeizureSystem.cs (76-95): keep the existing
per-component cycle resets but augment with the local-entity-swap reset (no
direct change needed here if the root fix updates _lastLocalEntity and resets
flags globally, otherwise add an explicit check to reset these flags when
LocalSession?.AttachedEntity differs from _lastLocalEntity).
In `@Content.Server/Traits/Assorted/NeuroAversionSystem.cs`:
- Around line 124-127: The conditional that sets traitInteractionMult in
NeuroAversionSystem.cs uses ChronicMigraineInteractionMultiplier for the true
branch but incorrectly uses 1.3f for the false branch as well, neutralizing the
effect; change the false branch to the neutral multiplier (1.0f) so it reads:
var traitInteractionMult = hasChronicMigraines ?
ChronicMigraineInteractionMultiplier : 1.0f, updating the expression that uses
hasChronicMigraines, traitInteractionMult and
ChronicMigraineInteractionMultiplier.
- Around line 95-101: The migraine timer progression is inverted: in
NeuroAversionSystem.cs compute seconds as frameTime * migraineFactor *
severityMultiplier (i.e., multiply by migraineFactor) instead of frameTime * (1f
/ migraineFactor) * severityMultiplier so that more-damaged entities advance the
timer faster; update the seconds calculation that feeds comp.NextMigraineTime
accordingly (referencing migraineFactor, severityMultiplier, frameTime, and
comp.NextMigraineTime).
In `@Content.Server/Traits/Assorted/SeizureSystem.cs`:
- Around line 112-128: StartSeizure currently ignores the seizureDuration
parameter so callers can't override seizure length; set
seizureComp.SeizureDuration to the provided seizureDuration (or leave default
when null) and, if appropriate, update seizureComp.RemainingTime when you
directly start the Seizure state. Modify the StartSeizure method to copy the
seizureDuration into SeizureComponent.SeizureDuration (and adjust RemainingTime
when entering SeizureState), and apply the same change to any other StartSeizure
overloads (the other StartSeizure implementation mentioned in the review) so all
callers' custom durations are honored.
- Around line 238-245: StopSeizure currently only removes SeizureComponent and
stutter, leaving SeizureOverlayComponent and active effects (paralyze/jitter)
running; update StopSeizure to immediately terminate all seizure state: remove
or trigger fade-out on SeizureOverlayComponent, cancel any
prodrome/active-seizure timers, and clear or remove any paralysis/jitter
status/effect components applied by the seizure logic so there is no lingering
disable/jitter. Refer to StopSeizure, SeizureComponent, SeizureOverlayComponent
and _stuttering when making changes and ensure any helper/cleanup methods used
by seizure start (e.g., effect-apply or timer registration) are reversed here so
the public contract "stops immediately" is satisfied.
In `@Content.Shared/EntityEffects/Effects/ReduceSeizureBuild.cs`:
- Around line 45-52: Remove the unprofessional inline comment "this is probably
a fucked way to do this" in ReduceSeizureBuild.cs; replace it with a
professional note if needed (e.g., "TODO: review approach for accessing
bloodstream Solution via SharedSolutionContainerSystem and consider alternative
APIs or performance implications" or open a ticket) near the block that obtains
solutionSystem, Solution? bloodstream, and the TryGetComponent/TryGetSolution
calls so the intent and any concerns are documented clearly.
- Around line 39-40: Remova o comentário "Clamp that EXPLICITIVE" e ajuste a
lógica de clamp em ReduceSeizureBuild: em vez de forçar um mínimo arbitrário
após a escala, clame o valor base (ReductionAmount) para o intervalo válido (por
exemplo 0..1) antes de aplicar Scale e então compute actualReduction =
clampedReductionAmount * Scale; se for necessário garantir limites finais,
aplique um clamp final em actualReduction para [0,1] (não um mínimo arbitrário
como 0.001). Referencie os símbolos ReductionAmount, Scale e actualReduction ao
fazer a mudança.
In `@Content.Shared/Traits/Assorted/MigraineSystem.cs`:
- Around line 57-63: O trecho em MigraineSystem que calcula fadeSpeed/fadeAmount
pode dividir por zero quando migraine.FadeOutDuration == 0; corrija verificando
FadeOutDuration antes da divisão (por exemplo, se FadeOutDuration <= 0 então
tratar fadeAmount como 1.0f ou calcular fadeAmount = frameTime / FadeOutDuration
somente quando > 0) e em seguida clamp (0..1) o fadeAmount antes de usá-lo nas
chamadas MathHelper.Lerp para migraine.BlurryMagnitude e
migraine.PulseAmplitude; ajuste as variáveis fadeSpeed/fadeAmount (referenciadas
no bloco onde migraine.IsFading é true) para evitar Infinity/NaN e garantir
valores válidos passados ao Lerp.
In `@Content.Shared/Traits/Assorted/SharedNeuroAversionSystem.cs`:
- Around line 61-75: GetConditionMultiplier currently multiplies base health
multiplier by the mindshield severity multiplier (uses comp.StartedMindShielded
? comp.StartedMindShieldedMultiplier : comp.MidRoundMindShieldedMultiplier), but
the server applies that severity multiplier again later, causing duplication;
remove the severity multiplication here so
GetConditionMultiplier(NeuroAversionComponent comp, bool isCritical, float
missingHpFrac) only computes and returns the base health multiplier
(ConditionCritical/Bad/Okay/Good) and let the hazard code continue to apply the
mindshield severity multiplier once.
In `@Content.Shared/Traits/Assorted/SharedSeizureSystem.cs`:
- Around line 49-116: SharedSeizureSystem: the overlay is being advanced twice
per tick—remove duplicate mutation by ensuring only one path updates
pulse/fade/blur: in UpdateSeizureComponents (which calls UpdateOverlayVisuals)
stop mutating SeizureOverlayComponent there (e.g., remove
overlayComp.PulseAccumulator+=frameTime and the fade/ramp logic) and leave
UpdateOverlayComponents responsible for PulseAccumulator, UpdateOverlayFade and
Dirty, or vice versa—pick one canonical updater and make the other only perform
non-visual responsibilities (e.g., movement recovery). Server SeizureSystem:
apply the same separation in the override in
Content.Server/Traits/Assorted/SeizureSystem.cs (lines 66-105) so the server
does not also advance the overlay twice—replicate the same change (make the
override call the single chosen visual updater or remove duplicate pulse/fade
mutations) so behavior is consistent between shared and server implementations.
- Around line 211-223: When transitioning to Fading, avoid double-applying
slowdown by clearing or ignoring the sealed component multiplier: in
TransitionToFading set seizure.MovementSpeedMultiplier = 1.0 (or 0 if
multipliers are applied additively) immediately after setting
seizure.CurrentState = SeizureState.Fading so only the overlay's
MovementSpeedMultiplier is used; alternatively, modify OnRefreshMovementSpeed to
early-return when seizure.CurrentState == SeizureState.Fading so it does not
apply SeizureComponent.MovementSpeedMultiplier while the overlay is fading.
Update whichever approach you choose (functions: TransitionToFading,
OnRefreshMovementSpeed; field: SeizureComponent.MovementSpeedMultiplier; enum:
SeizureState.Fading) and add a unit comment explaining the behavior.
- Around line 198-223: TransitionToSeizure and TransitionToRecovery update
SeizureComponent fields but never mark the component dirty, so add a Dirty(uid,
seizure) call after setting seizure.CurrentState and seizure.RemainingTime in
both TransitionToSeizure and TransitionToRecovery to ensure state and
RemainingTime replicate to clients (use the same Dirty helper already used in
TransitionToFading); no other logic changes required.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 0ab33d97-b688-4748-a532-d215a8741017
⛔ Files ignored due to path filters (3)
Resources/Audio/_Funkystation/Effects/Migraine/migraine.oggis excluded by!**/*.oggResources/Audio/_Funkystation/Effects/Migraine/neuroseize.oggis excluded by!**/*.oggResources/Audio/_Funkystation/Effects/Migraine/prodrome.oggis excluded by!**/*.ogg
📒 Files selected for processing (25)
Content.Client/Traits/Assorted/MigraineOverlay.csContent.Client/Traits/Assorted/MigraineSystem.csContent.Client/Traits/Assorted/SeizureSystem.csContent.Server/Traits/Assorted/ChronicMigrainesSystem.csContent.Server/Traits/Assorted/NeuroAversionSystem.csContent.Server/Traits/Assorted/SeizureSystem.csContent.Shared/EntityEffects/Effects/ReduceSeizureBuild.csContent.Shared/Traits/Assorted/ChronicMigrainesComponent.csContent.Shared/Traits/Assorted/MigraineComponent.csContent.Shared/Traits/Assorted/MigraineSystem.csContent.Shared/Traits/Assorted/NeuroAversionComponent.csContent.Shared/Traits/Assorted/SeizureComponent.csContent.Shared/Traits/Assorted/SeizureOverlayComponent.csContent.Shared/Traits/Assorted/SharedNeuroAversionSystem.csContent.Shared/Traits/Assorted/SharedSeizureSystem.csResources/Audio/_Funkystation/Effects/Migraine/attributions.ymlResources/Locale/en-US/_Funkystation/chemistry/effects.ftlResources/Locale/en-US/_Funkystation/traits/traits.ftlResources/Locale/en-US/traits/traits.ftlResources/Locale/pt-BR/_Funkystation/chemistry/effects.ftlResources/Locale/pt-BR/_Funkystation/traits/traits.ftlResources/Prototypes/Reagents/medicine.ymlResources/Prototypes/Traits/quirks.ymlResources/Prototypes/_Funkystation/Traits/quirks.ymlResources/Prototypes/status_effects.yml
| var localEntity = _playerManager.LocalSession?.AttachedEntity; | ||
| if (localEntity == null) | ||
| { | ||
| _playedStartSound = false; | ||
| return; | ||
| } |
There was a problem hiding this comment.
As flags de áudio one-shot não estão acopladas à troca de entidade local, causando supressão de som após swap de corpo.
Content.Client/Traits/Assorted/MigraineSystem.cs#L54-L59: resetar_playedStartSoundquandoAttachedEntitymudar (não apenas quando virarnull).Content.Client/Traits/Assorted/MigraineSystem.cs#L77-L84: no detach, limpar também o estado da última entidade rastreada para evitar carry-over.Content.Client/Traits/Assorted/SeizureSystem.cs#L42-L48: resetar_playedProdromeSound/_playedSeizureSoundquandoAttachedEntitymudar.Content.Client/Traits/Assorted/SeizureSystem.cs#L76-L95: manter reset de ciclo por componente, mas complementar com reset por troca de entidade local.
📍 Affects 2 files
Content.Client/Traits/Assorted/MigraineSystem.cs#L54-L59(this comment)Content.Client/Traits/Assorted/MigraineSystem.cs#L77-L84Content.Client/Traits/Assorted/SeizureSystem.cs#L42-L48Content.Client/Traits/Assorted/SeizureSystem.cs#L76-L95
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Client/Traits/Assorted/MigraineSystem.cs` around lines 54 - 59,
Content.Client/Traits/Assorted/MigraineSystem.cs (54-59): change the guard that
currently only sets _playedStartSound = false when LocalSession?.AttachedEntity
is null to instead detect when AttachedEntity != _lastLocalEntity and reset
_playedStartSound (and update _lastLocalEntity) so flags are cleared on body
swap, not only on detach; Content.Client/Traits/Assorted/MigraineSystem.cs
(77-84): on the detach branch also clear the tracked _lastLocalEntity (or set to
EntityUid.Invalid) in addition to existing per-component resets so there is no
carry‑over after swap; Content.Client/Traits/Assorted/SeizureSystem.cs (42-48):
detect change of LocalSession?.AttachedEntity compared to a stored
_lastLocalEntity and reset _playedProdromeSound and _playedSeizureSound when the
local entity changes (update _lastLocalEntity as well);
Content.Client/Traits/Assorted/SeizureSystem.cs (76-95): keep the existing
per-component cycle resets but augment with the local-entity-swap reset (no
direct change needed here if the root fix updates _lastLocalEntity and resets
flags globally, otherwise add an explicit check to reset these flags when
LocalSession?.AttachedEntity differs from _lastLocalEntity).
| var migraineFactor = 1f - (0.5f * (1f - missingHpFrac)); | ||
| var severityMultiplier = comp.StartedMindShielded | ||
| ? comp.StartedMindShieldedMultiplier | ||
| : comp.MidRoundMindShieldedMultiplier; | ||
|
|
||
| var seconds = frameTime * (1f / migraineFactor) * severityMultiplier; | ||
| comp.NextMigraineTime -= TimeSpan.FromSeconds(seconds); |
There was a problem hiding this comment.
A progressão do timer de enxaqueca está invertida em relação ao dano.
Na Line 100, dividir por migraineFactor faz entidades mais saudáveis avançarem o timer mais rápido que entidades mais feridas, contrariando a regra descrita no próprio método.
🔧 Correção sugerida
- var seconds = frameTime * (1f / migraineFactor) * severityMultiplier;
+ var seconds = frameTime * migraineFactor * severityMultiplier;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/Traits/Assorted/NeuroAversionSystem.cs` around lines 95 - 101,
The migraine timer progression is inverted: in NeuroAversionSystem.cs compute
seconds as frameTime * migraineFactor * severityMultiplier (i.e., multiply by
migraineFactor) instead of frameTime * (1f / migraineFactor) *
severityMultiplier so that more-damaged entities advance the timer faster;
update the seconds calculation that feeds comp.NextMigraineTime accordingly
(referencing migraineFactor, severityMultiplier, frameTime, and
comp.NextMigraineTime).
| var traitInteractionMult = hasChronicMigraines | ||
| ? ChronicMigraineInteractionMultiplier | ||
| : 1.3f; | ||
|
|
There was a problem hiding this comment.
A interação com ChronicMigraines está neutralizada.
Nas Lines 124-127, traitInteractionMult recebe 1.3f nos dois ramos; na prática, ter ou não ChronicMigraines não altera o cálculo.
🔧 Correção sugerida
- var traitInteractionMult = hasChronicMigraines
- ? ChronicMigraineInteractionMultiplier
- : 1.3f;
+ var traitInteractionMult = hasChronicMigraines
+ ? ChronicMigraineInteractionMultiplier
+ : 1f;📝 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.
| var traitInteractionMult = hasChronicMigraines | |
| ? ChronicMigraineInteractionMultiplier | |
| : 1.3f; | |
| var traitInteractionMult = hasChronicMigraines | |
| ? ChronicMigraineInteractionMultiplier | |
| : 1f; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/Traits/Assorted/NeuroAversionSystem.cs` around lines 124 -
127, The conditional that sets traitInteractionMult in NeuroAversionSystem.cs
uses ChronicMigraineInteractionMultiplier for the true branch but incorrectly
uses 1.3f for the false branch as well, neutralizing the effect; change the
false branch to the neutral multiplier (1.0f) so it reads: var
traitInteractionMult = hasChronicMigraines ?
ChronicMigraineInteractionMultiplier : 1.0f, updating the expression that uses
hasChronicMigraines, traitInteractionMult and
ChronicMigraineInteractionMultiplier.
| public void StartSeizure(EntityUid uid, float? seizureDuration = null, float prodromeDuration = 10f, | ||
| string? prodromePopupKey = null, string? seizurePopupKey = null, string? recoveryPopupKey = null) | ||
| { | ||
| if (HasComp<SeizureComponent>(uid)) | ||
| return; // Already having a seizure | ||
|
|
||
| var seizureComp = AddComp<SeizureComponent>(uid); | ||
| seizureComp.CurrentState = SeizureState.Prodrome; | ||
| seizureComp.ProdromeDuration = prodromeDuration; | ||
| seizureComp.RemainingTime = prodromeDuration; | ||
|
|
||
| seizureComp.ProdromePopupKey = prodromePopupKey; | ||
| seizureComp.SeizurePopupKey = seizurePopupKey; | ||
| seizureComp.RecoveryPopupKey = recoveryPopupKey; | ||
|
|
||
| StartProdromePhase(uid, seizureComp); | ||
| } |
There was a problem hiding this comment.
seizureDuration é aceito e descartado.
TriggerSeizureEffect calcula uma duração customizada e a passa para StartSeizure, mas esse valor nunca é copiado para SeizureComponent.SeizureDuration. Na prática, reagentes/callers não conseguem alterar a duração da crise.
Also applies to: 269-272
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/Traits/Assorted/SeizureSystem.cs` around lines 112 - 128,
StartSeizure currently ignores the seizureDuration parameter so callers can't
override seizure length; set seizureComp.SeizureDuration to the provided
seizureDuration (or leave default when null) and, if appropriate, update
seizureComp.RemainingTime when you directly start the Seizure state. Modify the
StartSeizure method to copy the seizureDuration into
SeizureComponent.SeizureDuration (and adjust RemainingTime when entering
SeizureState), and apply the same change to any other StartSeizure overloads
(the other StartSeizure implementation mentioned in the review) so all callers'
custom durations are honored.
| public void StopSeizure(EntityUid uid) | ||
| { | ||
| if (!HasComp<SeizureComponent>(uid)) | ||
| return; | ||
|
|
||
| _stuttering.DoRemoveStutter(uid, 0); | ||
| RemComp<SeizureComponent>(uid); | ||
| } |
There was a problem hiding this comment.
StopSeizure() não encerra a crise de fato.
O método remove só SeizureComponent e stutter. Se for chamado durante prodrome/crise, o SeizureOverlayComponent fica ativo sem entrar em fade, e efeitos já aplicados como paralyze/jitter continuam até expirarem. Isso contradiz a API pública de "stops immediately".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/Traits/Assorted/SeizureSystem.cs` around lines 238 - 245,
StopSeizure currently only removes SeizureComponent and stutter, leaving
SeizureOverlayComponent and active effects (paralyze/jitter) running; update
StopSeizure to immediately terminate all seizure state: remove or trigger
fade-out on SeizureOverlayComponent, cancel any prodrome/active-seizure timers,
and clear or remove any paralysis/jitter status/effect components applied by the
seizure logic so there is no lingering disable/jitter. Refer to StopSeizure,
SeizureComponent, SeizureOverlayComponent and _stuttering when making changes
and ensure any helper/cleanup methods used by seizure start (e.g., effect-apply
or timer registration) are reversed here so the public contract "stops
immediately" is satisfied.
| if (migraine.IsFading) | ||
| { | ||
| var fadeSpeed = 1.0f / migraine.FadeOutDuration; | ||
| var fadeAmount = fadeSpeed * frameTime; | ||
|
|
||
| migraine.BlurryMagnitude = MathHelper.Lerp(migraine.BlurryMagnitude, 0f, fadeAmount); | ||
| migraine.PulseAmplitude = MathHelper.Lerp(migraine.PulseAmplitude, 0f, fadeAmount); |
There was a problem hiding this comment.
Evite divisão por zero no fade da enxaqueca.
Na Line 59, FadeOutDuration pode vir como 0 (config/protótipo), gerando Infinity em fadeAmount e comportamento de interpolação inválido.
🔧 Correção sugerida
- var fadeSpeed = 1.0f / migraine.FadeOutDuration;
- var fadeAmount = fadeSpeed * frameTime;
+ var fadeDuration = MathF.Max(0.001f, migraine.FadeOutDuration);
+ var fadeAmount = MathF.Min(1f, frameTime / fadeDuration);📝 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.
| if (migraine.IsFading) | |
| { | |
| var fadeSpeed = 1.0f / migraine.FadeOutDuration; | |
| var fadeAmount = fadeSpeed * frameTime; | |
| migraine.BlurryMagnitude = MathHelper.Lerp(migraine.BlurryMagnitude, 0f, fadeAmount); | |
| migraine.PulseAmplitude = MathHelper.Lerp(migraine.PulseAmplitude, 0f, fadeAmount); | |
| if (migraine.IsFading) | |
| { | |
| var fadeDuration = MathF.Max(0.001f, migraine.FadeOutDuration); | |
| var fadeAmount = MathF.Min(1f, frameTime / fadeDuration); | |
| migraine.BlurryMagnitude = MathHelper.Lerp(migraine.BlurryMagnitude, 0f, fadeAmount); | |
| migraine.PulseAmplitude = MathHelper.Lerp(migraine.PulseAmplitude, 0f, fadeAmount); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Shared/Traits/Assorted/MigraineSystem.cs` around lines 57 - 63, O
trecho em MigraineSystem que calcula fadeSpeed/fadeAmount pode dividir por zero
quando migraine.FadeOutDuration == 0; corrija verificando FadeOutDuration antes
da divisão (por exemplo, se FadeOutDuration <= 0 então tratar fadeAmount como
1.0f ou calcular fadeAmount = frameTime / FadeOutDuration somente quando > 0) e
em seguida clamp (0..1) o fadeAmount antes de usá-lo nas chamadas
MathHelper.Lerp para migraine.BlurryMagnitude e migraine.PulseAmplitude; ajuste
as variáveis fadeSpeed/fadeAmount (referenciadas no bloco onde migraine.IsFading
é true) para evitar Infinity/NaN e garantir valores válidos passados ao Lerp.
| protected static float GetConditionMultiplier(NeuroAversionComponent comp, bool isCritical, float missingHpFrac) | ||
| { | ||
| // Determine base multiplier from health state | ||
| float baseMultiplier = isCritical ? comp.ConditionCriticalMultiplier | ||
| : missingHpFrac >= BadHealthThreshold ? comp.ConditionBadMultiplier | ||
| : missingHpFrac >= OkayHealthThreshold ? comp.ConditionOkayMultiplier | ||
| : comp.ConditionGoodMultiplier; | ||
|
|
||
| // Apply severity multiplier based on when mindshield was applied | ||
| var severityMultiplier = comp.StartedMindShielded | ||
| ? comp.StartedMindShieldedMultiplier | ||
| : comp.MidRoundMindShieldedMultiplier; | ||
|
|
||
| return baseMultiplier * severityMultiplier; | ||
| } |
There was a problem hiding this comment.
Não duplique o multiplicador de mindshield no cálculo de convulsão.
As Lines 70-74 já aplicam severidade de mindshield dentro de GetConditionMultiplier, e o servidor multiplica novamente no hazard. Isso infla/achata a probabilidade final de forma não intencional.
🔧 Correção sugerida (na causa raiz)
- // Apply severity multiplier based on when mindshield was applied
- var severityMultiplier = comp.StartedMindShielded
- ? comp.StartedMindShieldedMultiplier
- : comp.MidRoundMindShieldedMultiplier;
-
- return baseMultiplier * severityMultiplier;
+ return baseMultiplier;📝 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.
| protected static float GetConditionMultiplier(NeuroAversionComponent comp, bool isCritical, float missingHpFrac) | |
| { | |
| // Determine base multiplier from health state | |
| float baseMultiplier = isCritical ? comp.ConditionCriticalMultiplier | |
| : missingHpFrac >= BadHealthThreshold ? comp.ConditionBadMultiplier | |
| : missingHpFrac >= OkayHealthThreshold ? comp.ConditionOkayMultiplier | |
| : comp.ConditionGoodMultiplier; | |
| // Apply severity multiplier based on when mindshield was applied | |
| var severityMultiplier = comp.StartedMindShielded | |
| ? comp.StartedMindShieldedMultiplier | |
| : comp.MidRoundMindShieldedMultiplier; | |
| return baseMultiplier * severityMultiplier; | |
| } | |
| protected static float GetConditionMultiplier(NeuroAversionComponent comp, bool isCritical, float missingHpFrac) | |
| { | |
| // Determine base multiplier from health state | |
| float baseMultiplier = isCritical ? comp.ConditionCriticalMultiplier | |
| : missingHpFrac >= BadHealthThreshold ? comp.ConditionBadMultiplier | |
| : missingHpFrac >= OkayHealthThreshold ? comp.ConditionOkayMultiplier | |
| : comp.ConditionGoodMultiplier; | |
| return baseMultiplier; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Shared/Traits/Assorted/SharedNeuroAversionSystem.cs` around lines 61
- 75, GetConditionMultiplier currently multiplies base health multiplier by the
mindshield severity multiplier (uses comp.StartedMindShielded ?
comp.StartedMindShieldedMultiplier : comp.MidRoundMindShieldedMultiplier), but
the server applies that severity multiplier again later, causing duplication;
remove the severity multiplication here so
GetConditionMultiplier(NeuroAversionComponent comp, bool isCritical, float
missingHpFrac) only computes and returns the base health multiplier
(ConditionCritical/Bad/Okay/Good) and let the hazard code continue to apply the
mindshield severity multiplier once.
| protected virtual void UpdateSeizureComponents(float frameTime) | ||
| { | ||
| var query = EntityQueryEnumerator<SeizureComponent>(); | ||
| while (query.MoveNext(out var uid, out var seizure)) | ||
| { | ||
| seizure.RemainingTime -= frameTime; | ||
|
|
||
| UpdateMovementSpeed(uid, seizure, frameTime); | ||
| UpdateOverlayVisuals(uid, seizure, frameTime); | ||
|
|
||
| if (seizure.RemainingTime <= 0f) | ||
| { | ||
| HandlePhaseTransition(uid, seizure); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| protected virtual void UpdateOverlayComponents(float frameTime) | ||
| { | ||
| var overlayQuery = EntityQueryEnumerator<SeizureOverlayComponent>(); | ||
| while (overlayQuery.MoveNext(out var overlayUid, out var overlay)) | ||
| { | ||
| overlay.PulseAccumulator += frameTime; | ||
|
|
||
| if (overlay.HandleMovementRecovery) | ||
| { | ||
| UpdateMovementRecovery(overlayUid, overlay, frameTime); | ||
| } | ||
|
|
||
| UpdateOverlayFade(overlayUid, overlay, frameTime); | ||
| Dirty(overlayUid, overlay); | ||
| } | ||
| } | ||
|
|
||
| protected virtual void UpdateOverlayVisuals(EntityUid uid, SeizureComponent seizure, float frameTime) | ||
| { | ||
| if (!TryComp<SeizureOverlayComponent>(uid, out var overlayComp)) | ||
| return; | ||
|
|
||
| overlayComp.PulseAccumulator += frameTime; | ||
|
|
||
| if (overlayComp.IsFading) | ||
| { | ||
| var fadeSpeed = 1.0f / overlayComp.FadeOutDuration; | ||
| var fadeAmount = fadeSpeed * frameTime; | ||
|
|
||
| overlayComp.BlurryMagnitude = MathHelper.Lerp(overlayComp.BlurryMagnitude, 0f, fadeAmount); | ||
| overlayComp.PulseAmplitude = MathHelper.Lerp(overlayComp.PulseAmplitude, 0f, fadeAmount); | ||
| overlayComp.CurrentBlur = MathHelper.Lerp(overlayComp.CurrentBlur, 0f, fadeAmount); | ||
|
|
||
| if (overlayComp.BlurryMagnitude <= 0.01f && overlayComp.PulseAmplitude <= 0.01f && overlayComp.CurrentBlur <= 0.01f) | ||
| { | ||
| RemComp<SeizureOverlayComponent>(uid); | ||
| return; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| var targetBlur = overlayComp.BlurryMagnitude; | ||
| var rampSpeed = targetBlur > overlayComp.CurrentBlur | ||
| ? overlayComp.RampUpSpeed | ||
| : overlayComp.RampDownSpeed; | ||
|
|
||
| overlayComp.CurrentBlur = MathHelper.Lerp(overlayComp.CurrentBlur, targetBlur, frameTime * rampSpeed); | ||
| } | ||
|
|
||
| Dirty(uid, overlayComp); | ||
| } |
There was a problem hiding this comment.
O SeizureOverlayComponent está sendo avançado duas vezes por frame.
UpdateSeizureComponents() já chama UpdateOverlayVisuals(...), e depois UpdateOverlayComponents() percorre o mesmo overlay no mesmo tick para incrementar PulseAccumulator, interpolar blur e aplicar fade outra vez. Isso acelera pulso/fade enquanto a crise está ativa.
Content.Shared/Traits/Assorted/SharedSeizureSystem.cs#L49-L116: deixe apenas um dos caminhos mutarPulseAccumulator, blur e fade; o outro deve ficar só com a responsabilidade que ainda faltar.Content.Server/Traits/Assorted/SeizureSystem.cs#L66-L105: replique a mesma separação no override, senão o servidor continuará dobrando a atualização do overlay mesmo após corrigir a base.
📍 Affects 2 files
Content.Shared/Traits/Assorted/SharedSeizureSystem.cs#L49-L116(this comment)Content.Server/Traits/Assorted/SeizureSystem.cs#L66-L105
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Shared/Traits/Assorted/SharedSeizureSystem.cs` around lines 49 - 116,
SharedSeizureSystem: the overlay is being advanced twice per tick—remove
duplicate mutation by ensuring only one path updates pulse/fade/blur: in
UpdateSeizureComponents (which calls UpdateOverlayVisuals) stop mutating
SeizureOverlayComponent there (e.g., remove
overlayComp.PulseAccumulator+=frameTime and the fade/ramp logic) and leave
UpdateOverlayComponents responsible for PulseAccumulator, UpdateOverlayFade and
Dirty, or vice versa—pick one canonical updater and make the other only perform
non-visual responsibilities (e.g., movement recovery). Server SeizureSystem:
apply the same separation in the override in
Content.Server/Traits/Assorted/SeizureSystem.cs (lines 66-105) so the server
does not also advance the overlay twice—replicate the same change (make the
override call the single chosen visual updater or remove duplicate pulse/fade
mutations) so behavior is consistent between shared and server implementations.
| protected virtual void TransitionToSeizure(EntityUid uid, SeizureComponent seizure) | ||
| { | ||
| seizure.CurrentState = SeizureState.Seizure; | ||
| var actualDuration = Random.NextFloat(seizure.SeizureDuration.X, seizure.SeizureDuration.Y); | ||
| seizure.RemainingTime = actualDuration; | ||
| } | ||
|
|
||
| protected virtual void TransitionToRecovery(EntityUid uid, SeizureComponent seizure) | ||
| { | ||
| seizure.CurrentState = SeizureState.Recovery; | ||
| seizure.RemainingTime = seizure.RecoveryDuration; | ||
| } | ||
|
|
||
| protected virtual void TransitionToFading(EntityUid uid, SeizureComponent seizure) | ||
| { | ||
| if (TryComp<SeizureOverlayComponent>(uid, out var overlay)) | ||
| { | ||
| overlay.IsFading = true; | ||
| overlay.FadeOutDuration = DefaultFadeOutDuration; | ||
| overlay.MovementSpeedMultiplier = seizure.MovementSpeedMultiplier; | ||
| overlay.HandleMovementRecovery = true; | ||
|
|
||
| seizure.CurrentState = SeizureState.Fading; | ||
| seizure.RemainingTime = overlay.FadeOutDuration; | ||
|
|
||
| Dirty(uid, overlay); |
There was a problem hiding this comment.
Marcar SeizureComponent como dirty nas transições para sincronizar CurrentState/RemainingTime
Em SharedSeizureSystem.cs (linhas 198-223), TransitionToSeizure/TransitionToRecovery alteram seizure.CurrentState e seizure.RemainingTime (incluindo Random.NextFloat(...)), mas não chamam Dirty(uid, seizure)—diferente de TransitionToFading, que faz Dirty(uid, overlay). Isso pode impedir (ou atrasar) a replicação correta para clientes. [major fixes required] [data integrity]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Shared/Traits/Assorted/SharedSeizureSystem.cs` around lines 198 -
223, TransitionToSeizure and TransitionToRecovery update SeizureComponent fields
but never mark the component dirty, so add a Dirty(uid, seizure) call after
setting seizure.CurrentState and seizure.RemainingTime in both
TransitionToSeizure and TransitionToRecovery to ensure state and RemainingTime
replicate to clients (use the same Dirty helper already used in
TransitionToFading); no other logic changes required.
| protected virtual void TransitionToFading(EntityUid uid, SeizureComponent seizure) | ||
| { | ||
| if (TryComp<SeizureOverlayComponent>(uid, out var overlay)) | ||
| { | ||
| overlay.IsFading = true; | ||
| overlay.FadeOutDuration = DefaultFadeOutDuration; | ||
| overlay.MovementSpeedMultiplier = seizure.MovementSpeedMultiplier; | ||
| overlay.HandleMovementRecovery = true; | ||
|
|
||
| seizure.CurrentState = SeizureState.Fading; | ||
| seizure.RemainingTime = overlay.FadeOutDuration; | ||
|
|
||
| Dirty(uid, overlay); |
There was a problem hiding this comment.
Fading mantém dois multiplicadores de movimento ativos ao mesmo tempo.
Ao entrar em Fading, o overlay começa a recuperar MovementSpeedMultiplier, mas OnRefreshMovementSpeed continua aplicando o último multiplicador do SeizureComponent sem checar o estado atual. Até o componente ser removido, o slowdown é aplicado pelos dois handlers. Zere o multiplicador do SeizureComponent na transição ou ignore SeizureState.Fading no refresh.
Also applies to: 277-287
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Shared/Traits/Assorted/SharedSeizureSystem.cs` around lines 211 -
223, When transitioning to Fading, avoid double-applying slowdown by clearing or
ignoring the sealed component multiplier: in TransitionToFading set
seizure.MovementSpeedMultiplier = 1.0 (or 0 if multipliers are applied
additively) immediately after setting seizure.CurrentState = SeizureState.Fading
so only the overlay's MovementSpeedMultiplier is used; alternatively, modify
OnRefreshMovementSpeed to early-return when seizure.CurrentState ==
SeizureState.Fading so it does not apply
SeizureComponent.MovementSpeedMultiplier while the overlay is fading. Update
whichever approach you choose (functions: TransitionToFading,
OnRefreshMovementSpeed; field: SeizureComponent.MovementSpeedMultiplier; enum:
SeizureState.Fading) and add a unit comment explaining the behavior.
Sobre a PR
Porta as PRs do Funky que adiciona a trait enxaquecas crônicas, que aplica um efeito em um tempo aleatório. E a trait neuroaversão, que adiciona o mesmo efeito das enxaquecas crônicas, só que com algumas coisas a mais, mas isso apenas acontece quando se coloca qualquer implante.
Prs: funky-station/funky-station#2365, funky-station/funky-station#2495, funky-station/funky-station#2597 & funky-station/funky-station#2765.
Por quê? / Balanceamento
Mais traits.
Anexos
2026-06-05.10-18-59.mp4
Requerimentos
Changelog
🆑