diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index f451a34a8b7..9e55ae1ff00 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -1,3 +1,4 @@ + //These are all the different status effects. Use the paths for each effect in the defines. #define STATUS_EFFECT_MULTIPLE 0 //if it allows multiple instances of the effect @@ -6,7 +7,7 @@ #define STATUS_EFFECT_REPLACE 2 //if it allows only one, but new instances replace -#define BASIC_STATUS_EFFECT /datum/status_effect //Has no effect. +#define STATUS_EFFECT_REFRESH 3 // if it only allows one, and new instances just instead refresh the timer /////////// // BUFFS // @@ -16,6 +17,8 @@ #define STATUS_EFFECT_VOID_PRICE /datum/status_effect/void_price //The price of healing yourself with void energy. Deals 3 brute damage every 3 seconds for 30 seconds. #define STATUS_EFFECT_EXERCISED /datum/status_effect/exercised //Prevents heart disease +#define STATUS_EFFECT_REGENERATIVE_CORE /datum/status_effect/regenerative_core + //#define STATUS_EFFECT_VANGUARD /datum/status_effect/vanguard_shield //Grants temporary stun absorption, but will stun the user based on how many stuns they absorbed. //#define STATUS_EFFECT_INATHNEQS_ENDOWMENT /datum/status_effect/inathneqs_endowment //A 15-second invulnerability and stun absorption, granted by Inath-neq. //#define STATUS_EFFECT_WRAITHSPECS /datum/status_effect/wraith_spectacles diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 25673377bee..378ee0eb676 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -55,6 +55,7 @@ . = ..() if(.) if(ishuman(owner)) + owner.status_flags |= IGNORESLOWDOWN var/mob/living/carbon/human/H = owner for(var/X in H.bodyparts) var/obj/item/organ/external/BP = X @@ -65,8 +66,7 @@ H.dna.species.clone_mod *= 0.1 H.dna.species.stamina_mod *= 0.1 add_attack_logs(owner, owner, "gained blood-drunk stun immunity", ATKLOG_ALL) - var/status = CANSTUN | CANWEAKEN | CANPARALYSE | IGNORESLOWDOWN - owner.status_flags &= ~status + owner.add_stun_absorption("blooddrunk", INFINITY, 4) owner.playsound_local(get_turf(owner), 'sound/effects/singlebeat.ogg', 40, 1) /datum/status_effect/blooddrunk/on_remove() @@ -81,7 +81,9 @@ H.dna.species.clone_mod *= 10 H.dna.species.stamina_mod *= 10 add_attack_logs(owner, owner, "lost blood-drunk stun immunity", ATKLOG_ALL) - owner.status_flags |= CANSTUN | CANWEAKEN | CANPARALYSE | IGNORESLOWDOWN + owner.status_flags &= ~IGNORESLOWDOWN + if(islist(owner.stun_absorption) && owner.stun_absorption["blooddrunk"]) + owner.stun_absorption -= "blooddrunk" /datum/status_effect/exercised id = "Exercised" @@ -96,3 +98,23 @@ /datum/status_effect/exercised/Destroy() . = ..() STOP_PROCESSING(SSprocessing, src) + +/obj/screen/alert/status_effect/regenerative_core + name = "Reinforcing Tendrils" + desc = "You can move faster than your broken body could normally handle!" + icon_state = "regenerative_core" + name = "Regenerative Core Tendrils" + +/datum/status_effect/regenerative_core + id = "Regenerative Core" + duration = 1 MINUTES + status_type = STATUS_EFFECT_REPLACE + alert_type = /obj/screen/alert/status_effect/regenerative_core + +/datum/status_effect/regenerative_core/on_apply() + owner.status_flags |= IGNORESLOWDOWN + owner.revive() + return TRUE + +/datum/status_effect/regenerative_core/on_remove() + owner.status_flags &= ~IGNORESLOWDOWN \ No newline at end of file diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index 8f5023e4a74..b2365ad4e1c 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -43,5 +43,5 @@ duration = 40 alert_type = null -/datum/status_effect/high_five/on_timeout() - owner.visible_message("[owner] was left hanging....") +/datum/status_effect/high_five/on_remove() + owner.visible_message("[owner] was left hanging....") \ No newline at end of file diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index dd478a8f64c..8ed41c559b5 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -1,4 +1,3 @@ - //Status effects are used to apply temporary or permanent effects to mobs. Mobs are aware of their status effects at all times. //This file contains their code, plus code for applying and removing them. //When making a new status effect, add a define to status_effects.dm in __DEFINES for ease of use! @@ -52,13 +51,11 @@ tick() tick_interval = world.time + initial(tick_interval) if(duration != -1 && duration < world.time) - on_timeout() qdel(src) /datum/status_effect/proc/on_apply() //Called whenever the buff is applied; returning FALSE will cause it to autoremove itself. return TRUE /datum/status_effect/proc/tick() //Called every tick. -/datum/status_effect/proc/on_timeout()//called when a buff times out /datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed; do note that at the point this is called, it is out of the owner's status_effects but owner is not yet null /datum/status_effect/proc/be_replaced() //Called instead of on_remove when a status effect is replaced by itself or when a status effect with on_remove_on_mob_delete = FALSE has its mob deleted owner.clear_alert(id) @@ -66,6 +63,19 @@ owner = null qdel(src) +/datum/status_effect/proc/refresh() + var/original_duration = initial(duration) + if(original_duration == -1) + return + duration = world.time + original_duration + +//clickdelay/nextmove modifiers! +/datum/status_effect/proc/nextmove_modifier() + return 1 + +/datum/status_effect/proc/nextmove_adjust() + return 0 + //////////////// // ALERT HOOK // //////////////// @@ -87,6 +97,9 @@ if(S.id == initial(S1.id) && S.status_type) if(S.status_type == STATUS_EFFECT_REPLACE) S.be_replaced() + else if(S.status_type == STATUS_EFFECT_REFRESH) + S.refresh() + return else return var/list/arguments = args.Copy() @@ -117,4 +130,108 @@ var/datum/status_effect/S1 = effect for(var/datum/status_effect/S in status_effects) if(initial(S1.id) == S.id) - . += S \ No newline at end of file + . += S + +////////////////////// +// STACKING EFFECTS // +////////////////////// + +/datum/status_effect/stacking + id = "stacking_base" + duration = -1 //removed under specific conditions + alert_type = null + var/stacks = 0 //how many stacks are accumulated, also is # of stacks that target will have when first applied + var/delay_before_decay //deciseconds until ticks start occuring, which removes stacks (first stack will be removed at this time plus tick_interval) + tick_interval = 10 //deciseconds between decays once decay starts + var/stack_decay = 1 //how many stacks are lost per tick (decay trigger) + var/stack_threshold //special effects trigger when stacks reach this amount + var/max_stacks //stacks cannot exceed this amount + var/consumed_on_threshold = TRUE //if status should be removed once threshold is crossed + var/threshold_crossed = FALSE //set to true once the threshold is crossed, false once it falls back below + var/overlay_file + var/underlay_file + var/overlay_state // states in .dmi must be given a name followed by a number which corresponds to a number of stacks. put the state name without the number in these state vars + var/underlay_state // the number is concatonated onto the string based on the number of stacks to get the correct state name + var/mutable_appearance/status_overlay + var/mutable_appearance/status_underlay + +/datum/status_effect/stacking/proc/threshold_cross_effect() //what happens when threshold is crossed + +/datum/status_effect/stacking/proc/stacks_consumed_effect() //runs if status is deleted due to threshold being crossed + +/datum/status_effect/stacking/proc/fadeout_effect() //runs if status is deleted due to being under one stack + +/datum/status_effect/stacking/proc/stack_decay_effect() //runs every time tick() causes stacks to decay + +/datum/status_effect/stacking/proc/on_threshold_cross() + threshold_cross_effect() + if(consumed_on_threshold) + stacks_consumed_effect() + qdel(src) + +/datum/status_effect/stacking/proc/on_threshold_drop() + +/datum/status_effect/stacking/proc/can_have_status() + return owner.stat != DEAD + +/datum/status_effect/stacking/proc/can_gain_stacks() + return owner.stat != DEAD + +/datum/status_effect/stacking/tick() + if(!can_have_status()) + qdel(src) + else + add_stacks(-stack_decay) + stack_decay_effect() + +/datum/status_effect/stacking/proc/add_stacks(stacks_added) + if(stacks_added > 0 && !can_gain_stacks()) + return FALSE + owner.cut_overlay(status_overlay) + owner.underlays -= status_underlay + stacks += stacks_added + if(stacks > 0) + if(stacks >= stack_threshold && !threshold_crossed) //threshold_crossed check prevents threshold effect from occuring if changing from above threshold to still above threshold + threshold_crossed = TRUE + on_threshold_cross() + else if(stacks < stack_threshold && threshold_crossed) + threshold_crossed = FALSE //resets threshold effect if we fall below threshold so threshold effect can trigger again + on_threshold_drop() + if(stacks_added > 0) + tick_interval += delay_before_decay //refreshes time until decay + stacks = min(stacks, max_stacks) + status_overlay.icon_state = "[overlay_state][stacks]" + status_underlay.icon_state = "[underlay_state][stacks]" + owner.add_overlay(status_overlay) + owner.underlays += status_underlay + else + fadeout_effect() + qdel(src) //deletes status if stacks fall under one + +/datum/status_effect/stacking/on_creation(mob/living/new_owner, stacks_to_apply) + ..() + add_stacks(stacks_to_apply) + +/datum/status_effect/stacking/on_apply() + if(!can_have_status()) + return FALSE + status_overlay = mutable_appearance(overlay_file, "[overlay_state][stacks]") + status_underlay = mutable_appearance(underlay_file, "[underlay_state][stacks]") + var/icon/I = icon(owner.icon, owner.icon_state, owner.dir) + var/icon_height = I.Height() + status_overlay.pixel_x = -owner.pixel_x + status_overlay.pixel_y = FLOOR(icon_height * 0.25, 1) + status_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the status's overlay size based on the target's icon size + status_underlay.pixel_x = -owner.pixel_x + status_underlay.transform = matrix() * (icon_height/world.icon_size) * 3 + status_underlay.alpha = 40 + owner.add_overlay(status_overlay) + owner.underlays += status_underlay + return ..() + +/datum/status_effect/stacking/Destroy() + if(owner) + owner.cut_overlay(status_overlay) + owner.underlays -= status_underlay + QDEL_NULL(status_overlay) + return ..() \ No newline at end of file diff --git a/code/modules/mining/equipment/regenerative_core.dm b/code/modules/mining/equipment/regenerative_core.dm index 9d56d8ccd6e..4b5e5c911da 100644 --- a/code/modules/mining/equipment/regenerative_core.dm +++ b/code/modules/mining/equipment/regenerative_core.dm @@ -84,7 +84,7 @@ else to_chat(user, "You start to smear [src] on yourself. Disgusting tendrils hold you together and allow you to keep moving, but for how long?") feedback_add_details("hivelord_core","[src.type]|used|self") - H.revive() + H.apply_status_effect(STATUS_EFFECT_REGENERATIVE_CORE) user.drop_item() qdel(src) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index ad6d00d88aa..0e1a19da662 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -576,8 +576,10 @@ "visible_message" = message, "self_message" = self_message, "examine_message" = examine_message) /mob/living/proc/absorb_stun(amount, ignoring_flag_presence) - if(!amount || amount <= 0 || stat || ignoring_flag_presence || !islist(stun_absorption)) + if(amount < 0 || stat || ignoring_flag_presence || !islist(stun_absorption)) return FALSE + if(!amount) + amount = 0 var/priority_absorb_key var/highest_priority for(var/i in stun_absorption) @@ -585,12 +587,13 @@ priority_absorb_key = stun_absorption[i] highest_priority = priority_absorb_key["priority"] if(priority_absorb_key) - if(priority_absorb_key["visible_message"] || priority_absorb_key["self_message"]) - if(priority_absorb_key["visible_message"] && priority_absorb_key["self_message"]) - visible_message("[src][priority_absorb_key["visible_message"]]", "[priority_absorb_key["self_message"]]") - else if(priority_absorb_key["visible_message"]) - visible_message("[src][priority_absorb_key["visible_message"]]") - else if(priority_absorb_key["self_message"]) - to_chat(src, "[priority_absorb_key["self_message"]]") - priority_absorb_key["stuns_absorbed"] += amount + if(amount) //don't spam up the chat for continuous stuns + if(priority_absorb_key["visible_message"] || priority_absorb_key["self_message"]) + if(priority_absorb_key["visible_message"] && priority_absorb_key["self_message"]) + visible_message("[src][priority_absorb_key["visible_message"]]", "[priority_absorb_key["self_message"]]") + else if(priority_absorb_key["visible_message"]) + visible_message("[src][priority_absorb_key["visible_message"]]") + else if(priority_absorb_key["self_message"]) + to_chat(src, "[priority_absorb_key["self_message"]]") + priority_absorb_key["stuns_absorbed"] += amount return TRUE \ No newline at end of file diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi index 2b29cad5b0c..2fe2c7cd21c 100644 Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ