diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 8ddd264a95e..a99422e60db 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -74,7 +74,7 @@ #define STATUS_EFFECT_CRUSHERMARK /datum/status_effect/crusher_mark //if struck with a proto-kinetic crusher, takes a ton of damage -#define STATUS_EFFECT_SAWBLEED /datum/status_effect/saw_bleed //if the bleed builds up enough, takes a ton of damage +#define STATUS_EFFECT_SAWBLEED /datum/status_effect/stacking/saw_bleed //if the bleed builds up enough, takes a ton of damage #define STATUS_EFFECT_NECROPOLIS_CURSE /datum/status_effect/necropolis_curse #define STATUS_EFFECT_HIVEMIND_CURSE /datum/status_effect/necropolis_curse/hivemind diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index c3c801c2d96..a97c8775f02 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -390,78 +390,29 @@ owner.underlays -= marked_underlay //if this is being called, we should have an owner at this point. ..() -/datum/status_effect/saw_bleed +/datum/status_effect/stacking/saw_bleed id = "saw_bleed" - duration = -1 //removed under specific conditions tick_interval = 6 - alert_type = null - var/mutable_appearance/bleed_overlay - var/mutable_appearance/bleed_underlay - var/bleed_amount = 3 - var/bleed_buildup = 3 - var/delay_before_decay = 5 + delay_before_decay = 5 + stack_threshold = 10 + max_stacks = 10 + overlay_file = 'icons/effects/bleed.dmi' + underlay_file = 'icons/effects/bleed.dmi' + overlay_state = "bleed" + underlay_state = "bleed" var/bleed_damage = 200 - var/needs_to_bleed = FALSE -/datum/status_effect/saw_bleed/Destroy() - if(owner) - owner.cut_overlay(bleed_overlay) - owner.underlays -= bleed_underlay - QDEL_NULL(bleed_overlay) - return ..() - -/datum/status_effect/saw_bleed/on_apply() - if(owner.stat == DEAD) - return FALSE - bleed_overlay = mutable_appearance('icons/effects/bleed.dmi', "bleed[bleed_amount]") - bleed_underlay = mutable_appearance('icons/effects/bleed.dmi', "bleed[bleed_amount]") - var/icon/I = icon(owner.icon, owner.icon_state, owner.dir) - var/icon_height = I.Height() - bleed_overlay.pixel_x = -owner.pixel_x - bleed_overlay.pixel_y = FLOOR(icon_height * 0.25, 1) - bleed_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the bleed overlay's size based on the target's icon size - bleed_underlay.pixel_x = -owner.pixel_x - bleed_underlay.transform = matrix() * (icon_height/world.icon_size) * 3 - bleed_underlay.alpha = 40 - owner.add_overlay(bleed_overlay) - owner.underlays += bleed_underlay - return ..() - -/datum/status_effect/saw_bleed/tick() - if(owner.stat == DEAD) - qdel(src) - else - add_bleed(-1) - -/datum/status_effect/saw_bleed/proc/add_bleed(amount) - owner.cut_overlay(bleed_overlay) - owner.underlays -= bleed_underlay - bleed_amount += amount - if(bleed_amount) - if(bleed_amount >= 10) - needs_to_bleed = TRUE - qdel(src) - else - if(amount > 0) - tick_interval += delay_before_decay - bleed_overlay.icon_state = "bleed[bleed_amount]" - bleed_underlay.icon_state = "bleed[bleed_amount]" - owner.add_overlay(bleed_overlay) - owner.underlays += bleed_underlay - else - qdel(src) - -/datum/status_effect/saw_bleed/on_remove() - if(needs_to_bleed) - var/turf/T = get_turf(owner) - new /obj/effect/temp_visual/bleed/explode(T) - for(var/d in GLOB.alldirs) - new /obj/effect/temp_visual/dir_setting/bloodsplatter(T, d) - playsound(T, "desceration", 200, 1, -1) - owner.adjustBruteLoss(bleed_damage) - else - new /obj/effect/temp_visual/bleed(get_turf(owner)) +/datum/status_effect/stacking/saw_bleed/fadeout_effect() + new /obj/effect/temp_visual/bleed(get_turf(owner)) +/datum/status_effect/stacking/saw_bleed/threshold_cross_effect() + owner.adjustBruteLoss(bleed_damage) + var/turf/T = get_turf(owner) + new /obj/effect/temp_visual/bleed/explode(T) + for(var/d in GLOB.alldirs) + new /obj/effect/temp_visual/dir_setting/bloodsplatter(T, d) + playsound(T, "desceration", 200, 1, -1) + /mob/living/proc/apply_necropolis_curse(set_curse) var/datum/status_effect/necropolis_curse/C = has_status_effect(STATUS_EFFECT_NECROPOLIS_CURSE) if(!set_curse) diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index 6684065cc8d..c84bb27616f 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -131,3 +131,107 @@ for(var/datum/status_effect/S in status_effects) if(initial(S1.id) == S.id) . += 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) + ..() + src.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/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 71201ebad47..7b87594d9a0 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -661,6 +661,7 @@ nemesis_factions = list("mining", "boss") var/transform_cooldown var/swiping = FALSE + var/bleed_stacks_per_hit = 3 /obj/item/melee/transforming/cleaving_saw/examine(mob/user) . = ..() @@ -701,12 +702,11 @@ user.changeNext_move(CLICK_CD_MELEE * 0.5) //when closed, it attacks very rapidly /obj/item/melee/transforming/cleaving_saw/nemesis_effects(mob/living/user, mob/living/target) - var/datum/status_effect/saw_bleed/B = target.has_status_effect(STATUS_EFFECT_SAWBLEED) + var/datum/status_effect/stacking/saw_bleed/B = target.has_status_effect(STATUS_EFFECT_SAWBLEED) if(!B) - if(!active) //This isn't in the above if-check so that the else doesn't care about active - target.apply_status_effect(STATUS_EFFECT_SAWBLEED) + target.apply_status_effect(STATUS_EFFECT_SAWBLEED,bleed_stacks_per_hit) else - B.add_bleed(B.bleed_buildup) + B.add_stacks(bleed_stacks_per_hit) /obj/item/melee/transforming/cleaving_saw/attack(mob/living/target, mob/living/carbon/human/user) if(!active || swiping || !target.density || get_turf(target) == get_turf(user))