diff --git a/code/datums/status_effects/_status_effect.dm b/code/datums/status_effects/_status_effect.dm index 2b44e8bbaf0..4a569858655 100644 --- a/code/datums/status_effects/_status_effect.dm +++ b/code/datums/status_effects/_status_effect.dm @@ -3,19 +3,16 @@ /datum/status_effect /// The ID of the effect. ID is used in adding and removing effects to check for duplicates, among other things. var/id = "effect" - /// When set initially / in on_creation, this is how long the status effect lasts in deciseconds. - /// While processing, this becomes the world.time when the status effect will expire. - /// -1 = infinite duration. + /// This is how long the status effect lasts in deciseconds. + /// You can put STATUS_EFFECT_PERMANENT (or INFINITY) here for infinite duration. var/duration = STATUS_EFFECT_PERMANENT - /// When set initially / in on_creation, this is how long between [proc/tick] calls in deciseconds. - /// Note that this cannot be faster than the processing subsystem you choose to fire the effect on. (See: [var/processing_speed]) - /// While processing, this becomes the world.time when the next tick will occur. - /// -1 = will prevent ticks, and if duration is also unlimited (-1), stop processing wholesale. + /// This is how long between [proc/tick] calls in deciseconds. + /// This has to be a multiple of the [var/wait] of the subsystem this status effect is running on, which is based on [var/processing_speed]. + /// Putting STATUS_EFFECT_NO_TICK here will stop [proc/tick] calls, and if [var/duration] is STATUS_EFFECT_PERMANENT, it stops processing entirely. + /// Putting STATUS_EFFECT_AUTO_TICK here will make every subsystem tick call [proc/tick], making the tick interval depend entirely on [var/processing_speed] var/tick_interval = 1 SECONDS - ///If our tick intervals are set to be a dynamic value within a range, the lowerbound of said range - var/tick_interval_lowerbound - ///If our tick intervals are set to be a dynamic value within a range, the upperbound of said range - var/tick_interval_upperbound + /// The time until the next [proc/tick] call, gets set to [var/tick_interval] after every [proc/tick] call and decrements on every [proc/process] call. + var/time_until_next_tick /// The mob affected by the status effect. VAR_FINAL/mob/living/owner /// How many of the effect can be on one mob, and/or what happens when you try to add a duplicate. @@ -58,10 +55,9 @@ // we will optionally allow INFINITY, because i imagine it'll be convenient in some places, // but we'll still set it to -1 / STATUS_EFFECT_PERMANENT for proper unified handling duration = STATUS_EFFECT_PERMANENT - if(duration != STATUS_EFFECT_PERMANENT) - duration = world.time + duration + if(tick_interval != STATUS_EFFECT_NO_TICK) - tick_interval = world.time + tick_interval + time_until_next_tick = tick_interval if(alert_type) var/atom/movable/screen/alert/status_effect/new_alert = owner.throw_alert(id, alert_type) @@ -69,7 +65,7 @@ linked_alert = new_alert //so we can reference the alert, if we need to update_shown_duration() - if(duration > world.time || tick_interval > world.time) //don't process if we don't care + if(duration != STATUS_EFFECT_PERMANENT || tick_interval != STATUS_EFFECT_NO_TICK) //don't process if we don't care switch(processing_speed) if(STATUS_EFFECT_FAST_PROCESS) START_PROCESSING(SSfastprocess, src) @@ -108,7 +104,7 @@ if(!linked_alert || !show_duration) return - linked_alert.maptext = MAPTEXT_TINY_UNICODE("[round((duration - world.time)/10, 1)]s") + linked_alert.maptext = MAPTEXT_TINY_UNICODE("[round(duration / 10, 1)]s") // Status effect process. Handles adjusting its duration and ticks. // If you're adding processed effects, put them in [proc/tick] @@ -120,19 +116,23 @@ qdel(src) return + if (duration != STATUS_EFFECT_PERMANENT) + duration = max(0, duration - (seconds_per_tick SECONDS)) // doing it first means its more up to date for ticks to read + + if (tick_interval != STATUS_EFFECT_NO_TICK) + time_until_next_tick = max(0, time_until_next_tick - (seconds_per_tick SECONDS)) // same here + if(tick_interval == STATUS_EFFECT_AUTO_TICK) tick(seconds_per_tick) - else if(tick_interval != STATUS_EFFECT_NO_TICK && tick_interval < world.time) - var/tick_length = (tick_interval_upperbound && tick_interval_lowerbound) ? rand(tick_interval_lowerbound, tick_interval_upperbound) : initial(tick_interval) - tick(tick_length / (1 SECONDS)) - tick_interval = world.time + tick_length + else if(tick_interval != STATUS_EFFECT_NO_TICK && time_until_next_tick <= 0) + time_until_next_tick = tick_interval // same here as well + tick(tick_interval / 10) if(QDELING(src)) - // tick deleted us, no need to continue - return + return // tick deleted us, no need to continue if(duration != STATUS_EFFECT_PERMANENT) - if(duration < world.time) + if(duration <= 0) qdel(src) return update_shown_duration() @@ -185,10 +185,7 @@ /// Called when a status effect of status_type STATUS_EFFECT_REFRESH /// has its duration refreshed in apply_status_effect - is passed New() args /datum/status_effect/proc/refresh(effect, ...) - var/original_duration = initial(duration) - if(original_duration == STATUS_EFFECT_PERMANENT) - return - duration = world.time + original_duration + duration = initial(duration) /// Adds nextmove modifier multiplicatively to the owner while applied /datum/status_effect/proc/nextmove_modifier() @@ -208,13 +205,14 @@ if(!heal_flag_necessary || (heal_flags & heal_flag_necessary)) qdel(src) -/// Remove [seconds] of duration from the status effect, qdeling / ending if we eclipse the current world time. +/// Removes [seconds] of duration from the status effect. +/// Returns whether or not the status effect was qdeleted due to running out of duration. /datum/status_effect/proc/remove_duration(seconds) if(duration == STATUS_EFFECT_PERMANENT) // Infinite duration return FALSE - duration -= seconds - if(duration <= world.time) + duration -= (seconds SECONDS) + if(duration <= 0) qdel(src) return TRUE diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index d51ebe144ff..75edc87b4ec 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -236,7 +236,7 @@ var/skill_level_boost = (new_owner.mind?.get_skill_level(/datum/skill/athletics) - 1) * 2 SECONDS bonus_time = (bonus_time + food_boost + skill_level_boost) * modifier - var/exhaustion_limit = new_owner.mind?.get_skill_modifier(/datum/skill/athletics, SKILL_VALUE_MODIFIER) + world.time + var/exhaustion_limit = new_owner.mind?.get_skill_modifier(/datum/skill/athletics, SKILL_VALUE_MODIFIER) if(duration + bonus_time >= exhaustion_limit) duration = exhaustion_limit to_chat(new_owner, span_userdanger("Your muscles are exhausted! Might be a good idea to sleep...")) @@ -274,7 +274,7 @@ id = "Hippocratic Oath" status_type = STATUS_EFFECT_UNIQUE duration = STATUS_EFFECT_PERMANENT - tick_interval = 2.5 SECONDS + tick_interval = 2.6 SECONDS alert_type = null var/datum/component/aura_healing/aura_healing diff --git a/code/datums/status_effects/buffs/stop_drop_roll.dm b/code/datums/status_effects/buffs/stop_drop_roll.dm index e7ff393a1dd..8618aea5bc3 100644 --- a/code/datums/status_effects/buffs/stop_drop_roll.dm +++ b/code/datums/status_effects/buffs/stop_drop_roll.dm @@ -8,8 +8,7 @@ if(!iscarbon(owner)) return FALSE - var/actual_interval = initial(tick_interval) - if(!owner.Knockdown(actual_interval * 2, ignore_canstun = TRUE) || owner.body_position != LYING_DOWN) + if(!owner.Knockdown(tick_interval * 2, ignore_canstun = TRUE) || owner.body_position != LYING_DOWN) to_chat(owner, span_warning("You try to stop, drop, and roll - but you can't get on the ground!")) return FALSE @@ -40,12 +39,11 @@ qdel(src) return - var/actual_interval = initial(tick_interval) - if(!owner.Knockdown(actual_interval * 1.2, ignore_canstun = TRUE)) + if(!owner.Knockdown(tick_interval * 1.2, ignore_canstun = TRUE)) stop_rolling() return - owner.spin(spintime = actual_interval, speed = actual_interval / 4) + owner.spin(spintime = tick_interval, speed = tick_interval / 4) if(!reduce_firestacks(1)) return diff --git a/code/datums/status_effects/debuffs/choke.dm b/code/datums/status_effects/debuffs/choke.dm index e8af4b1b94d..05f41dcd178 100644 --- a/code/datums/status_effects/debuffs/choke.dm +++ b/code/datums/status_effects/debuffs/choke.dm @@ -66,7 +66,7 @@ if(flaming) ash = new(owner, /particles/smoke/ash, PARTICLE_ATTACH_MOB) var/clear_in = rand(15 SECONDS, 25 SECONDS) - if(duration != -1) + if(duration != STATUS_EFFECT_PERMANENT) clear_in = min(duration, clear_in) addtimer(CALLBACK(src, PROC_REF(clear_flame)), clear_in) return TRUE diff --git a/code/datums/status_effects/debuffs/dizziness.dm b/code/datums/status_effects/debuffs/dizziness.dm index b47083aa5e3..547ba44df50 100644 --- a/code/datums/status_effects/debuffs/dizziness.dm +++ b/code/datums/status_effects/debuffs/dizziness.dm @@ -26,7 +26,7 @@ /datum/status_effect/dizziness/tick(seconds_between_ticks) // How much time is left, in seconds - var/amount = (duration - world.time) / 10 + var/amount = duration / 10 if(amount <= 0) return diff --git a/code/datums/status_effects/debuffs/dna_transformation.dm b/code/datums/status_effects/debuffs/dna_transformation.dm index 46e125a391e..bd2a079d6f8 100644 --- a/code/datums/status_effects/debuffs/dna_transformation.dm +++ b/code/datums/status_effects/debuffs/dna_transformation.dm @@ -82,10 +82,10 @@ if(duration == STATUS_EFFECT_PERMANENT) return // Already paused - time_before_pause = duration - world.time + time_before_pause = duration duration = STATUS_EFFECT_PERMANENT // Resume if we're none of the above and also were paused else if(time_before_pause != -1) - duration = time_before_pause + world.time + duration = time_before_pause time_before_pause = -1 diff --git a/code/datums/status_effects/debuffs/hallucination.dm b/code/datums/status_effects/debuffs/hallucination.dm index e5528a66382..9c40203c1dd 100644 --- a/code/datums/status_effects/debuffs/hallucination.dm +++ b/code/datums/status_effects/debuffs/hallucination.dm @@ -94,7 +94,7 @@ var/lower_cd = lower_tick_interval var/upper_cd = upper_tick_interval if(variable_tier) - var/seconds_left = (duration - world.time) / 10 + var/seconds_left = duration / 10 switch(seconds_left) if(0 to 20) max_hallucination_tier = HALLUCINATION_TIER_COMMON diff --git a/code/datums/status_effects/debuffs/jitteriness.dm b/code/datums/status_effects/debuffs/jitteriness.dm index ad9a7625133..50bb8b5e09f 100644 --- a/code/datums/status_effects/debuffs/jitteriness.dm +++ b/code/datums/status_effects/debuffs/jitteriness.dm @@ -25,7 +25,7 @@ owner.update_offsets() /datum/status_effect/jitter/get_examine_text() - switch(duration - world.time) + switch(duration) if(5 MINUTES to INFINITY) return span_boldwarning("[owner.p_They()] [owner.p_are()] convulsing violently!") if(3 MINUTES to 5 MINUTES) @@ -47,7 +47,7 @@ if(owner.resting && remove_duration(4 * seconds_between_ticks)) return - var/time_left_in_seconds = (duration - world.time) / 10 + var/time_left_in_seconds = duration / 10 owner.do_jitter_animation(time_left_in_seconds) /// Helper proc that causes the mob to do a jittering animation by jitter_amount. diff --git a/code/datums/status_effects/debuffs/screen_blur.dm b/code/datums/status_effects/debuffs/screen_blur.dm index acdce13b5a0..ee893610c12 100644 --- a/code/datums/status_effects/debuffs/screen_blur.dm +++ b/code/datums/status_effects/debuffs/screen_blur.dm @@ -49,7 +49,7 @@ game_plane_master_controller.remove_filter("eye_blur") return - var/time_left_in_seconds = (duration - world.time) / (1 SECONDS) + var/time_left_in_seconds = duration / 10 var/amount_of_blur = clamp(time_left_in_seconds * BLUR_DURATION_TO_INTENSITY, 0.6, 3) game_plane_master_controller.add_filter("eye_blur", 1, gauss_blur_filter(amount_of_blur)) diff --git a/code/datums/status_effects/debuffs/staggered.dm b/code/datums/status_effects/debuffs/staggered.dm index 9919a156c31..4d640b64331 100644 --- a/code/datums/status_effects/debuffs/staggered.dm +++ b/code/datums/status_effects/debuffs/staggered.dm @@ -51,7 +51,7 @@ /datum/status_effect/dazed id = "dazed" status_type = STATUS_EFFECT_UNIQUE - tick_interval = 0.5 SECONDS + tick_interval = 0.6 SECONDS alert_type = null remove_on_fullheal = TRUE /// Our visual cue for the vulnerable state this status effect puts us in. @@ -121,9 +121,9 @@ /// Only applied by shoving someone to paralyze them /datum/status_effect/no_side_kick id = "no side kick" - duration = 3.5 SECONDS + duration = 3.6 SECONDS status_type = STATUS_EFFECT_UNIQUE - tick_interval = 0.5 SECONDS + tick_interval = 0.6 SECONDS alert_type = null remove_on_fullheal = TRUE diff --git a/code/datums/status_effects/stacking_effect.dm b/code/datums/status_effects/stacking_effect.dm index 287419028b0..327c614f8de 100644 --- a/code/datums/status_effects/stacking_effect.dm +++ b/code/datums/status_effects/stacking_effect.dm @@ -123,7 +123,7 @@ on_threshold_drop() if(stacks_added > 0) - tick_interval += delay_before_decay // refreshes time until decay + time_until_next_tick += delay_before_decay // refreshes time until decay if (status_overlay) status_overlay.icon_state = "[overlay_state][stacks]" diff --git a/code/modules/antagonists/changeling/powers/darkness_adaptation.dm b/code/modules/antagonists/changeling/powers/darkness_adaptation.dm index 47fe31c215b..4d8e8a7e474 100644 --- a/code/modules/antagonists/changeling/powers/darkness_adaptation.dm +++ b/code/modules/antagonists/changeling/powers/darkness_adaptation.dm @@ -51,7 +51,7 @@ /// Makes the user harder to see in the dark (and makes the user see in the dark easier) /datum/status_effect/darkness_adapted id = "darkness_adapted" - tick_interval = 0.5 SECONDS + tick_interval = 0.6 SECONDS alert_type = null /// Threshold before the dark color is applied var/dark_color_threshold = 70 diff --git a/code/modules/antagonists/heretic/magic/fire_blast.dm b/code/modules/antagonists/heretic/magic/fire_blast.dm index 873f131c28f..36ed3b4caab 100644 --- a/code/modules/antagonists/heretic/magic/fire_blast.dm +++ b/code/modules/antagonists/heretic/magic/fire_blast.dm @@ -157,7 +157,7 @@ id = "fire_blasted" alert_type = null duration = 5 SECONDS - tick_interval = 0.5 SECONDS + tick_interval = 0.6 SECONDS /// How much fire / stam to do per tick (stamina damage is doubled this) var/tick_damage = 1 /// How long does the animation of the appearance last? If 0 or negative, we make no overlay diff --git a/code/modules/mining/lavaland/cain_and_abel/dagger_status_effects.dm b/code/modules/mining/lavaland/cain_and_abel/dagger_status_effects.dm index 0f535f31951..a6cb6c04e67 100644 --- a/code/modules/mining/lavaland/cain_and_abel/dagger_status_effects.dm +++ b/code/modules/mining/lavaland/cain_and_abel/dagger_status_effects.dm @@ -1,8 +1,8 @@ ///status effect applied to us when we're wildly swinging /datum/status_effect/dagger_swinging id = "dagger swinging" - tick_interval = 0.25 SECONDS - duration = 1.75 SECONDS + tick_interval = 0.2 SECONDS + duration = 1.6 SECONDS alert_type = null ///base damage we apply to mobs near us var/base_damage = 5 diff --git a/code/modules/mob/living/basic/farm_animals/goose/goose_vomit.dm b/code/modules/mob/living/basic/farm_animals/goose/goose_vomit.dm index 2b347903e22..2d3f2b6df20 100644 --- a/code/modules/mob/living/basic/farm_animals/goose/goose_vomit.dm +++ b/code/modules/mob/living/basic/farm_animals/goose/goose_vomit.dm @@ -124,7 +124,7 @@ /// Stop fucking around and get the rest of it out /datum/status_effect/goose_vomit/proc/vomit_finale() - tick_interval = 0.1 SECONDS + tick_interval = 0.2 SECONDS owner.set_jitter_if_lower(1 SECONDS) hurl_item(vomit_strongly = TRUE) @@ -176,7 +176,7 @@ /datum/status_effect/goose_choking/on_remove() UnregisterSignal(owner, COMSIG_LIVING_DEATH) - if (duration >= world.time) + if (duration > 0) return // Saved by something, although probably by dying early owner.death_message = "lets out one final oxygen-deprived honk before [owner.p_they()] go[owner.p_es()] limp and lifeless.." owner.death() diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 58677d64bef..867c975ad21 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -36,7 +36,7 @@ /mob/living/proc/AmountStun() //How many deciseconds remain in our stun var/datum/status_effect/incapacitating/stun/S = IsStun() if(S) - return S.duration - world.time + return S.duration return 0 /mob/living/proc/Stun(amount, ignore_canstun = FALSE) //Can't go below remaining duration @@ -46,7 +46,7 @@ return var/datum/status_effect/incapacitating/stun/S = IsStun() if(S) - S.duration = max(world.time + amount, S.duration) + S.duration = max(amount, S.duration) else if(amount > 0) S = apply_status_effect(/datum/status_effect/incapacitating/stun, amount) return S @@ -62,7 +62,7 @@ qdel(S) else if(S) - S.duration = world.time + amount + S.duration = amount else S = apply_status_effect(/datum/status_effect/incapacitating/stun, amount) return S @@ -86,7 +86,7 @@ /mob/living/proc/AmountKnockdown() //How many deciseconds remain in our knockdown var/datum/status_effect/incapacitating/knockdown/K = IsKnockdown() if(K) - return K.duration - world.time + return K.duration return 0 /mob/living/proc/Knockdown(amount, daze_amount = 0, ignore_canstun = FALSE) //Can't go below remaining duration @@ -96,7 +96,7 @@ return var/datum/status_effect/incapacitating/knockdown/K = IsKnockdown() if(K) - K.duration = max(world.time + amount, K.duration) + K.duration = max(amount, K.duration) else if(amount > 0) K = apply_status_effect(/datum/status_effect/incapacitating/knockdown, amount) if(daze_amount > 0) @@ -114,7 +114,7 @@ qdel(K) else if(K) - K.duration = world.time + amount + K.duration = amount else K = apply_status_effect(/datum/status_effect/incapacitating/knockdown, amount) return K @@ -140,7 +140,7 @@ /mob/living/proc/AmountImmobilized() //How many deciseconds remain in our Immobilized status effect var/datum/status_effect/incapacitating/immobilized/I = IsImmobilized() if(I) - return I.duration - world.time + return I.duration return 0 /mob/living/proc/Immobilize(amount, ignore_canstun = FALSE) //Can't go below remaining duration @@ -150,7 +150,7 @@ return var/datum/status_effect/incapacitating/immobilized/I = IsImmobilized() if(I) - I.duration = max(world.time + amount, I.duration) + I.duration = max(amount, I.duration) else if(amount > 0) I = apply_status_effect(/datum/status_effect/incapacitating/immobilized, amount) return I @@ -166,7 +166,7 @@ qdel(I) else if(I) - I.duration = world.time + amount + I.duration = amount else I = apply_status_effect(/datum/status_effect/incapacitating/immobilized, amount) return I @@ -190,7 +190,7 @@ /mob/living/proc/AmountParalyzed() //How many deciseconds remain in our Paralyzed status effect var/datum/status_effect/incapacitating/paralyzed/P = IsParalyzed(FALSE) if(P) - return P.duration - world.time + return P.duration return 0 /mob/living/proc/Paralyze(amount, ignore_canstun = FALSE) //Can't go below remaining duration @@ -200,7 +200,7 @@ return var/datum/status_effect/incapacitating/paralyzed/P = IsParalyzed(FALSE) if(P) - P.duration = max(world.time + amount, P.duration) + P.duration = max(amount, P.duration) else if(amount > 0) P = apply_status_effect(/datum/status_effect/incapacitating/paralyzed, amount) return P @@ -216,7 +216,7 @@ qdel(P) else if(P) - P.duration = world.time + amount + P.duration = amount else P = apply_status_effect(/datum/status_effect/incapacitating/paralyzed, amount) return P @@ -240,7 +240,7 @@ /mob/living/proc/amount_incapacitated() var/datum/status_effect/incapacitating/incapacitated/incapacitated_status_effect = has_status_effect(/datum/status_effect/incapacitating/incapacitated) if (incapacitated_status_effect) - return incapacitated_status_effect.duration - world.time + return incapacitated_status_effect.duration else return 0 @@ -256,7 +256,7 @@ return var/datum/status_effect/incapacitating/incapacitated/incapacitated_status_effect = has_status_effect(/datum/status_effect/incapacitating/incapacitated) if(incapacitated_status_effect) - incapacitated_status_effect.duration = max(world.time + amount, incapacitated_status_effect.duration) + incapacitated_status_effect.duration = max(amount, incapacitated_status_effect.duration) else if(amount > 0) incapacitated_status_effect = apply_status_effect(/datum/status_effect/incapacitating/incapacitated, amount) return incapacitated_status_effect @@ -278,7 +278,7 @@ qdel(incapacitated_status_effect) else if(incapacitated_status_effect) - incapacitated_status_effect.duration = world.time + amount + incapacitated_status_effect.duration = amount else incapacitated_status_effect = apply_status_effect(/datum/status_effect/incapacitating/incapacitated, amount) return incapacitated_status_effect @@ -333,7 +333,7 @@ /mob/living/proc/AmountUnconscious() //How many deciseconds remain in our unconsciousness var/datum/status_effect/incapacitating/unconscious/U = IsUnconscious() if(U) - return U.duration - world.time + return U.duration return 0 /mob/living/proc/Unconscious(amount, ignore_canstun = FALSE) //Can't go below remaining duration @@ -343,7 +343,7 @@ return var/datum/status_effect/incapacitating/unconscious/U = IsUnconscious() if(U) - U.duration = max(world.time + amount, U.duration) + U.duration = max(amount, U.duration) else if(amount > 0) U = apply_status_effect(/datum/status_effect/incapacitating/unconscious, amount) return U @@ -358,7 +358,7 @@ if(U) qdel(U) else if(U) - U.duration = world.time + amount + U.duration = amount else U = apply_status_effect(/datum/status_effect/incapacitating/unconscious, amount) return U @@ -383,7 +383,7 @@ /mob/living/proc/AmountSleeping() //How many deciseconds remain in our sleep var/datum/status_effect/incapacitating/sleeping/S = IsSleeping() if(S) - return S.duration - world.time + return S.duration return 0 /mob/living/proc/Sleeping(amount) //Can't go below remaining duration @@ -393,7 +393,7 @@ return var/datum/status_effect/incapacitating/sleeping/S = IsSleeping() if(S) - S.duration = max(world.time + amount, S.duration) + S.duration = max(amount, S.duration) else if(amount > 0) S = apply_status_effect(/datum/status_effect/incapacitating/sleeping, amount) return S @@ -408,7 +408,7 @@ if(S) qdel(S) else if(S) - S.duration = world.time + amount + S.duration = amount else S = apply_status_effect(/datum/status_effect/incapacitating/sleeping, amount) return S @@ -622,13 +622,12 @@ if(isnum(max_duration) && duration > 0) // Check the duration remaining on the existing status effect // If it's greater than / equal to our passed max duration, we don't need to do anything - var/remaining_duration = existing.duration - world.time - if(remaining_duration >= max_duration) + if(existing.duration >= max_duration) return // Otherwise, add duration up to the max (max_duration - remaining_duration), // or just add duration if it doesn't exceed our max at all - existing.duration += min(max_duration - remaining_duration, duration) + existing.duration += min(max_duration - existing.duration, duration) else existing.duration += duration @@ -636,7 +635,7 @@ // If the duration was decreased and is now less 0 seconds, // qdel it / clean up the status effect immediately // (rather than waiting for the process tick to handle it) - if(existing.duration <= world.time) + if(existing.duration <= 0) qdel(existing) else if(duration > 0) @@ -669,12 +668,11 @@ if(only_if_higher) // If the existing status effect has a higher remaining duration // than what we aim to set it to, don't downgrade it - do nothing (return) - var/remaining_duration = existing.duration - world.time - if(remaining_duration >= duration) + if(existing.duration >= duration) return // Set the duration accordingly - existing.duration = world.time + duration + existing.duration = duration else if(duration > 0) apply_status_effect(effect, duration) @@ -697,7 +695,7 @@ if(existing.duration == STATUS_EFFECT_PERMANENT) return INFINITY - return existing.duration - world.time + return existing.duration /** * Adjust the "drunk value" the mob is currently experiencing, diff --git a/code/modules/projectiles/ammunition/ballistic/rifle.dm b/code/modules/projectiles/ammunition/ballistic/rifle.dm index 2ec850950b0..ab4e38dbb95 100644 --- a/code/modules/projectiles/ammunition/ballistic/rifle.dm +++ b/code/modules/projectiles/ammunition/ballistic/rifle.dm @@ -154,7 +154,7 @@ if (can_sleep && (owner.mob_biotypes & MOB_ORGANIC)) owner.adjust_drowsiness(drowsy_per_second * seconds_per_tick) var/datum/status_effect/drowsiness/drowsiness = owner.has_status_effect(/datum/status_effect/drowsiness) - if (drowsiness?.duration - world.time >= drowsy_knockout) + if (drowsiness?.duration >= drowsy_knockout) owner.Sleeping(3 SECONDS) if (casing.heals_left <= 0) fall_out() diff --git a/code/modules/spells/spell_types/cone/cone_of_cold.dm b/code/modules/spells/spell_types/cone/cone_of_cold.dm index 9327e2c2fad..3b8f880a842 100644 --- a/code/modules/spells/spell_types/cone/cone_of_cold.dm +++ b/code/modules/spells/spell_types/cone/cone_of_cold.dm @@ -50,7 +50,7 @@ if(ispath(frozen_status_effect_path) && unfreeze_mob_duration > 0 SECONDS) // 0 duration = don't apply the status effect var/datum/status_effect/freeze = target_mob.apply_status_effect(frozen_status_effect_path) if(unfreeze_mob_duration != INFINITY) - freeze.duration = world.time + unfreeze_mob_duration + freeze.duration = unfreeze_mob_duration if(on_freeze_brute_damage || on_freeze_burn_damage) target_mob.take_overall_damage(on_freeze_brute_damage, on_freeze_burn_damage) diff --git a/code/modules/unit_tests/burning.dm b/code/modules/unit_tests/burning.dm index daf99875f28..a23d5423c27 100644 --- a/code/modules/unit_tests/burning.dm +++ b/code/modules/unit_tests/burning.dm @@ -10,8 +10,8 @@ TEST_ASSERT(dummy.on_fire, "Dummy is not on fire despite having 20 fire stacks and being ignited.") // Manually tick it a few times var/datum/status_effect/fire_handler/fire_stacks/handler = locate() in dummy.status_effects + handler.tick_interval = STATUS_EFFECT_AUTO_TICK for(var/i in 1 to 5) - handler.tick_interval = world.time - 1 - handler.process() + handler.process(1) TEST_ASSERT(dummy.fire_stacks < 20, "Dummy should have decayed firestacks, but did not. (Dummy stacks: [dummy.fire_stacks]).") TEST_ASSERT(dummy.bodytemperature > initial_temp, "Dummy did not heat up despite being on fire. (Dummy temp: [dummy.bodytemperature], initial temp: [initial_temp])") diff --git a/code/modules/unit_tests/mob_damage.dm b/code/modules/unit_tests/mob_damage.dm index 1cc63466bc9..5567e3aa454 100644 --- a/code/modules/unit_tests/mob_damage.dm +++ b/code/modules/unit_tests/mob_damage.dm @@ -608,7 +608,7 @@ TEST_ASSERT_NOTNULL(tox_effect, "Dummy didn't get tox_vomit status effect despite at [dummy.getToxLoss()] toxin damage (Method: SET)!") // Clear the toxin damage away, and force a status effect tick: It should delete itself dummy.setToxLoss(0) - tox_effect.tick(initial(tox_effect.tick_interval)) + tox_effect.tick(tox_effect.tick_interval) TEST_ASSERT(QDELETED(tox_effect), "Dummy still has tox_vomit status effect despite at [dummy.getToxLoss()] toxin damage (Method: SET)!") // Test another method of gaining tox damage, use an entirely clean slate just to be sure var/mob/living/carbon/human/dummy_two = allocate(/mob/living/carbon/human/consistent) diff --git a/code/modules/unit_tests/status_effect_validity.dm b/code/modules/unit_tests/status_effect_validity.dm index 44c7b391610..e9c7cd70c90 100644 --- a/code/modules/unit_tests/status_effect_validity.dm +++ b/code/modules/unit_tests/status_effect_validity.dm @@ -8,23 +8,95 @@ var/tick_speed = initial(checking.tick_interval) if(tick_speed == STATUS_EFFECT_NO_TICK) continue + if(tick_speed < 0) + TEST_FAIL("Status effect [checking] has tick_interval set to a negative value other than STATUS_EFFECT_NO_TICK, this is not how you prevent ticks - use tick_interval = STATUS_EFFECT_NO_TICK instead.") + continue if(tick_speed == INFINITY) TEST_FAIL("Status effect [checking] has tick_interval set to INFINITY, this is not how you prevent ticks - use tick_interval = STATUS_EFFECT_NO_TICK instead.") continue switch(initial(checking.processing_speed)) if(STATUS_EFFECT_FAST_PROCESS) - if(tick_speed < SSfastprocess.wait && tick_speed != STATUS_EFFECT_AUTO_TICK) - TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is faster than SSfastprocess can tick ([SSfastprocess.wait]).") + if(tick_speed % SSfastprocess.wait != 0 && tick_speed != STATUS_EFFECT_AUTO_TICK) + TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is not a multiple of SSfastprocess wait time ([SSfastprocess.wait]).") if(STATUS_EFFECT_NORMAL_PROCESS) - if(tick_speed < SSprocessing.wait && tick_speed != STATUS_EFFECT_AUTO_TICK) - TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is faster than SSprocessing can tick ([SSprocessing.wait]).") + if(tick_speed % SSprocessing.wait != 0 && tick_speed != STATUS_EFFECT_AUTO_TICK) + TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is not a multiple of SSprocessing wait time ([SSprocessing.wait]).") if(STATUS_EFFECT_PRIORITY) var/priority_wait = world.tick_lag * SSpriority_effects.wait // SSpriority_effects has the SS_TICKER flag, so its wait is in ticks, so we have to convert it to deciseconds. - if(tick_speed < priority_wait && tick_speed != STATUS_EFFECT_AUTO_TICK) - TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is faster than SSpriority_effects can tick ([priority_wait]).") + if(tick_speed % priority_wait != 0 && tick_speed != STATUS_EFFECT_AUTO_TICK) + TEST_FAIL("Status effect [checking] has tick_interval set to [tick_speed], which is not a multiple of SSpriority_effects wait time ([priority_wait]).") else TEST_FAIL("Invalid processing speed for status effect [checking] : [initial(checking.processing_speed)]") +/// Validates status effect duration setup +/datum/unit_test/status_effect_duration + +/datum/unit_test/status_effect_duration/Run() + for(var/datum/status_effect/checking as anything in subtypesof(/datum/status_effect)) + if(initial(checking.id) == STATUS_EFFECT_ID_ABSTRACT) + continue + var/duration = initial(checking.duration) + if(duration == STATUS_EFFECT_PERMANENT) + continue + if(duration == INFINITY) // for some god forsaken reason, this is allowed + continue + if(duration < 0) + TEST_FAIL("Status effect [checking] has duration set to a negative value other than STATUS_EFFECT_PERMANENT, this is not how you make effects last forever - use duration = STATUS_EFFECT_PERMANENT instead.") + continue + switch(initial(checking.processing_speed)) + if(STATUS_EFFECT_FAST_PROCESS) + if(duration % SSfastprocess.wait != 0) + TEST_FAIL("Status effect [checking] has duration set to [duration], which is not a multiple of SSfastprocess wait time ([SSfastprocess.wait]).") + if(STATUS_EFFECT_NORMAL_PROCESS) + if(duration % SSprocessing.wait != 0) + TEST_FAIL("Status effect [checking] has duration set to [duration], which is not a multiple of SSprocessing wait time ([SSprocessing.wait]).") + if(STATUS_EFFECT_PRIORITY) + var/priority_wait = world.tick_lag * SSpriority_effects.wait // SSpriority_effects has the SS_TICKER flag, so its wait is in ticks, so we have to convert it to deciseconds. + if(duration % priority_wait != 0) + TEST_FAIL("Status effect [checking] has duration set to [duration], which is not a multiple of SSpriority_effects wait time ([priority_wait]).") + else + TEST_FAIL("Invalid processing speed for status effect [checking] : [initial(checking.processing_speed)]") + +/// Validates that status effect tick counts are directly proportional to duration, and that seconds_between_ticks added up is equal to duration. +/datum/unit_test/status_effect_tick_counts + +/datum/unit_test/status_effect_tick_counts/Run() + var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human/consistent, run_loc_floor_bottom_left) + + var/datum/status_effect/unit_test_tick_counter/counter = user.apply_status_effect(/datum/status_effect/unit_test_tick_counter) + + // The 0.2 here and in the for loop is arbitrary and can be any value that divides evenly into the duration and tick interval of the unit test status effect. + // I chose 0.2 specifically because it just happens to be the SSfastprocess.wait in seconds, which basically simulates this status effect running on SSfastprocess. + var/ticks_required = counter.duration / 10 / 0.2 + 1 + + for (var/i in 1 to ticks_required) + if (!QDELETED(counter)) + counter.process(0.2) + + var/expected_tick_count = initial(counter.duration) / counter.tick_interval + if (abs(counter.total_tick_count - expected_tick_count) > 0.01) + TEST_FAIL("Status effect tick count is not directly proportional to duration. Expected [expected_tick_count] ticks, got [counter.total_tick_count] ticks.") + + var/expected_seconds = initial(counter.duration) / 10 + if (abs(counter.total_seconds - expected_seconds) > 0.01) + TEST_FAIL("Status effect seconds_between_ticks accumulated together does not equal duration. Expected [expected_seconds] seconds, got [counter.total_seconds] seconds.") + + QDEL_NULL(counter) + +/datum/status_effect/unit_test_tick_counter + duration = 10 SECONDS + tick_interval = 0.4 SECONDS + + id = "unit_test_tick_counter" + alert_type = null + + var/total_tick_count = 0 + var/total_seconds = 0 + +/datum/status_effect/unit_test_tick_counter/tick(seconds_between_ticks) + total_tick_count++ + total_seconds += seconds_between_ticks + /// Validates status effect alert type setup /datum/unit_test/status_effect_alert