diff --git a/code/__DEFINES/mob.dm b/code/__DEFINES/mob.dm index 3121f35b13c..5312cba6e14 100644 --- a/code/__DEFINES/mob.dm +++ b/code/__DEFINES/mob.dm @@ -171,6 +171,3 @@ #define hasorgans(A) (ishuman(A)) #define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0) - -//effects -#define EFFECT_HIGHFIVE "highfiving" diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm new file mode 100644 index 00000000000..b38b2666863 --- /dev/null +++ b/code/__DEFINES/status_effects.dm @@ -0,0 +1,78 @@ +//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 + +#define STATUS_EFFECT_UNIQUE 1 //if it allows only one, preventing new instances + +#define STATUS_EFFECT_REPLACE 2 //if it allows only one, but new instances replace + +#define BASIC_STATUS_EFFECT /datum/status_effect //Has no effect. + +/////////// +// BUFFS // +/////////// + +#define STATUS_EFFECT_SHADOW_MEND /datum/status_effect/shadow_mend //Quick, powerful heal that deals damage afterwards. Heals 15 brute/burn every second for 3 seconds. +#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_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 + +//#define STATUS_EFFECT_POWERREGEN /datum/status_effect/cyborg_power_regen //Regenerates power on a given cyborg over time + +//#define STATUS_EFFECT_HISGRACE /datum/status_effect/his_grace //His Grace. + +//#define STATUS_EFFECT_WISH_GRANTERS_GIFT /datum/status_effect/wish_granters_gift //If you're currently resurrecting with the Wish Granter + +//#define STATUS_EFFECT_BLOODDRUNK /datum/status_effect/blooddrunk //Stun immunity and greatly reduced damage taken + +///////////// +// DEBUFFS // +///////////// + +//#define STATUS_EFFECT_STUN /datum/status_effect/incapacitating/stun //the affected is stunned + +//#define STATUS_EFFECT_KNOCKDOWN /datum/status_effect/incapacitating/knockdown //the affected is knocked down + +//#define STATUS_EFFECT_UNCONSCIOUS /datum/status_effect/incapacitating/unconscious //the affected is unconscious + +//#define STATUS_EFFECT_SLEEPING /datum/status_effect/incapacitating/sleeping //the affected is asleep + +//#define STATUS_EFFECT_BELLIGERENT /datum/status_effect/belligerent //forces the affected to walk, doing damage if they try to run + +//#define STATUS_EFFECT_GEISTRACKER /datum/status_effect/geis_tracker //if you're using geis, this tracks that and keeps you from using scripture + +//#define STATUS_EFFECT_MANIAMOTOR /datum/status_effect/maniamotor //disrupts, damages, and confuses the affected as long as they're in range of the motor +//#define MAX_MANIA_SEVERITY 100 //how high the mania severity can go +//#define MANIA_DAMAGE_TO_CONVERT 90 //how much damage is required before it'll convert affected targets + +//#define STATUS_EFFECT_HISWRATH /datum/status_effect/his_wrath //His Wrath. + +//#define STATUS_EFFECT_SUMMONEDGHOST /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes + +//#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_NECROPOLIS_CURSE /datum/status_effect/necropolis_curse +//#define CURSE_BLINDING 1 //makes the edges of the target's screen obscured +//#define CURSE_SPAWNING 2 //spawns creatures that attack the target only +//#define CURSE_WASTING 4 //causes gradual damage +//#define CURSE_GRASPING 8 //hands reach out from the sides of the screen, doing damage and stunning if they hit the target + +//#define STATUS_EFFECT_KINDLE /datum/status_effect/kindle //A knockdown reduced by 1 second for every 3 points of damage the target takes. + +//#define STATUS_EFFECT_ICHORIAL_STAIN /datum/status_effect/ichorial_stain //Prevents a servant from being revived by vitality matrices for one minute. + +///////////// +// NEUTRAL // +///////////// + +#define STATUS_EFFECT_HIGHFIVE /datum/status_effect/high_five + +//#define STATUS_EFFECT_SIGILMARK /datum/status_effect/sigil_mark + +//#define STATUS_EFFECT_CRUSHERDAMAGETRACKING /datum/status_effect/crusher_damage //tracks total kinetic crusher damage on a target + +//#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module \ No newline at end of file diff --git a/code/controllers/Processes/effect_process.dm b/code/controllers/Processes/effect_process.dm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm new file mode 100644 index 00000000000..26d8ec67ef0 --- /dev/null +++ b/code/datums/status_effects/buffs.dm @@ -0,0 +1,41 @@ +//Largely beneficial effects go here, even if they have drawbacks. An example is provided in Shadow Mend. + +/datum/status_effect/shadow_mend + id = "shadow_mend" + duration = 30 + alert_type = /obj/screen/alert/status_effect/shadow_mend + +/obj/screen/alert/status_effect/shadow_mend + name = "Shadow Mend" + desc = "Shadowy energies wrap around your wounds, sealing them at a price. After healing, you will slowly lose health every three seconds for thirty seconds." + icon_state = "shadow_mend" + +/datum/status_effect/shadow_mend/on_apply() + owner.visible_message("Violet light wraps around [owner]'s body!", "Violet light wraps around your body!") + playsound(owner, 'sound/magic/teleport_app.ogg', 50, 1) + return ..() + +/datum/status_effect/shadow_mend/tick() + owner.adjustBruteLoss(-15) + owner.adjustFireLoss(-15) + +/datum/status_effect/shadow_mend/on_remove() + owner.visible_message("The violet light around [owner] glows black!", "The tendrils around you cinch tightly and reap their toll...") + playsound(owner, 'sound/magic/teleport_diss.ogg', 50, 1) + owner.apply_status_effect(STATUS_EFFECT_VOID_PRICE) + + +/datum/status_effect/void_price + id = "void_price" + duration = 300 + tick_interval = 30 + alert_type = /obj/screen/alert/status_effect/void_price + +/obj/screen/alert/status_effect/void_price + name = "Void Price" + desc = "Black tendrils cinch tightly against you, digging wicked barbs into your flesh." + icon_state = "shadow_mend" + +/datum/status_effect/void_price/tick() + playsound(owner, 'sound/weapons/bite.ogg', 50, 1) + owner.adjustBruteLoss(3) \ No newline at end of file diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm new file mode 100644 index 00000000000..cfafa912b64 --- /dev/null +++ b/code/datums/status_effects/debuffs.dm @@ -0,0 +1,10 @@ +//OTHER DEBUFFS + +/datum/status_effect/cultghost //is a cult ghost and can't use manifest runes + id = "cult_ghost" + duration = -1 + alert_type = null + +/datum/status_effect/cultghost/tick() + if(owner.reagents) + owner.reagents.del_reagent("holywater") //can't be deconverted \ No newline at end of file diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm new file mode 100644 index 00000000000..270dbc71cdc --- /dev/null +++ b/code/datums/status_effects/neutral.dm @@ -0,0 +1,6 @@ +//entirely neutral or internal status effects go here + +/datum/status_effect/high_five + id = "high_five" + duration = 25 + alert_type = null \ No newline at end of file diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm new file mode 100644 index 00000000000..8bb5a0429d6 --- /dev/null +++ b/code/datums/status_effects/status_effect.dm @@ -0,0 +1,120 @@ + +//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! + +/datum/status_effect + var/id = "effect" //Used for screen alerts. + var/duration = -1 //How long the status effect lasts in DECISECONDS. Enter -1 for an effect that never ends unless removed through some means. + var/tick_interval = 10 //How many deciseconds between ticks, approximately. Leave at 10 for every second. + var/mob/living/owner //The mob affected by the status effect. + var/status_type = STATUS_EFFECT_UNIQUE //How many of the effect can be on one mob, and what happens when you try to add another + var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted + var/examine_text //If defined, this text will appear when the mob is examined - to use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves + var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description + var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists + +/datum/status_effect/New(list/arguments) + on_creation(arglist(arguments)) + +/datum/status_effect/proc/on_creation(mob/living/new_owner, ...) + if(new_owner) + owner = new_owner + if(owner) + LAZYADD(owner.status_effects, src) + if(!owner || !on_apply()) + qdel(src) + return + if(duration != -1) + duration = world.time + duration + tick_interval = world.time + tick_interval + if(alert_type) + var/obj/screen/alert/status_effect/A = owner.throw_alert(id, alert_type) + A.attached_effect = src //so the alert can reference us, if it needs to + linked_alert = A //so we can reference the alert, if we need to + processing_objects.Add(src) + fast_processing.Add(src) + return TRUE + +/datum/status_effect/Destroy() + processing_objects.Remove(src) + fast_processing.Remove(src) + if(owner) + owner.clear_alert(id) + LAZYREMOVE(owner.status_effects, src) + on_remove() + owner = null + return ..() + +/datum/status_effect/proc/process() + if(!owner) + qdel(src) + return + if(tick_interval < world.time) + tick() + tick_interval = world.time + initial(tick_interval) + if(duration != -1 && duration < world.time) + 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_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) + LAZYREMOVE(owner.status_effects, src) + owner = null + qdel(src) + +//////////////// +// ALERT HOOK // +//////////////// + +/obj/screen/alert/status_effect + name = "Curse of Mundanity" + desc = "You don't feel any different..." + var/datum/status_effect/attached_effect + +////////////////// +// HELPER PROCS // +////////////////// + +/mob/living/proc/apply_status_effect(effect, ...) //applies a given status effect to this mob, returning the effect if it was successful + . = FALSE + var/datum/status_effect/S1 = effect + LAZYINITLIST(status_effects) + for(var/datum/status_effect/S in status_effects) + if(S.id == initial(S1.id) && S.status_type) + if(S.status_type == STATUS_EFFECT_REPLACE) + S.be_replaced() + else + return + var/list/arguments = args.Copy() + arguments[1] = src + S1 = new effect(arguments) + . = S1 + +/mob/living/proc/remove_status_effect(effect) //removes all of a given status effect from this mob, returning TRUE if at least one was removed + . = FALSE + if(status_effects) + var/datum/status_effect/S1 = effect + for(var/datum/status_effect/S in status_effects) + if(initial(S1.id) == S.id) + qdel(S) + . = TRUE + +/mob/living/proc/has_status_effect(effect) //returns the effect if the mob calling the proc owns the given status effect + . = FALSE + if(status_effects) + var/datum/status_effect/S1 = effect + for(var/datum/status_effect/S in status_effects) + if(initial(S1.id) == S.id) + return S + +/mob/living/proc/has_status_effect_list(effect) //returns a list of effects with matching IDs that the mod owns; use for effects there can be multiple of + . = list() + if(status_effects) + 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 diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 7ed39d78382..728733420b9 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -24,7 +24,6 @@ var/wetlevel = 0 //how wet the mob is var/failed_last_breath = FALSE //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. - var/list/active_effect = list() var/co2overloadtime = null blood_volume = BLOOD_VOLUME_NORMAL diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index a9fde120c44..dac421e444f 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -840,14 +840,13 @@ if("highfive") if(restrained()) return - if(EFFECT_HIGHFIVE in active_effect) - to_chat(src, "You give up on the high-five.") - active_effect -= EFFECT_HIGHFIVE - return - active_effect |= EFFECT_HIGHFIVE - for(var/mob/living/carbon/C in orange(1)) - if(EFFECT_HIGHFIVE in C.active_effect) - if((C.mind.special_role == SPECIAL_ROLE_WIZARD) && (mind.special_role == SPECIAL_ROLE_WIZARD)) + visible_message("[name] requests a highfive.", "You request a highfive.") + if(!has_status_effect(STATUS_EFFECT_HIGHFIVE)) + apply_status_effect(STATUS_EFFECT_HIGHFIVE) + + if(has_status_effect(STATUS_EFFECT_HIGHFIVE)) + for(var/mob/living/carbon/C in orange(1)) + if((mind.special_role == SPECIAL_ROLE_WIZARD) && (C.mind && (C.mind.special_role == SPECIAL_ROLE_WIZARD))) visible_message("[name] and [C.name] high-five EPICALLY!") status_flags |= GODMODE C.status_flags |= GODMODE @@ -855,16 +854,9 @@ status_flags &= ~GODMODE C.status_flags &= ~GODMODE break - visible_message("[name] and [C.name] high-five!") - C.active_effect -= EFFECT_HIGHFIVE - active_effect -= EFFECT_HIGHFIVE - playsound('sound/effects/snap.ogg', 50) - break - if(EFFECT_HIGHFIVE in active_effect) - visible_message("[name] requests a highfive.", "You request a highfive.") - if(do_after(src, 25, target = src)) - visible_message("[name] was left hanging. Embarrassing.", "You are left hanging. How embarrassing!") - active_effect -= EFFECT_HIGHFIVE + visible_message("[name] and [C.name] high-five!") + playsound('sound/effects/snap.ogg', 50) + break if("help") var/emotelist = "aflap(s), airguitar, blink(s), blink(s)_r, blush(es), bow(s)-(none)/mob, burp(s), choke(s), chuckle(s), clap(s), collapse(s), cough(s),cry, cries, custom, dance, dap(s)(none)/mob," \ diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 42e461b6e03..b2cfeb66825 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2,6 +2,13 @@ /mob/living/Destroy() if(ranged_ability) ranged_ability.remove_ranged_ability(src) + if(LAZYLEN(status_effects)) + for(var/s in status_effects) + var/datum/status_effect/S = s + if(S.on_remove_on_mob_delete) //the status effect calls on_remove when its mob is deleted + qdel(S) + else + S.be_replaced() return ..() /mob/living/ghostize(can_reenter_corpse = 1) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 5aaf02688d3..bb95742ac62 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -64,3 +64,6 @@ var/list/recent_tastes = list() var/blood_volume = 0 //how much blood the mob has + + var/list/status_effects //a list of all status effects the mob has + diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi index aa3916b07d6..56c1730fd3a 100644 Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ diff --git a/paradise.dme b/paradise.dme index 4cf60716903..1dc15bf5891 100644 --- a/paradise.dme +++ b/paradise.dme @@ -51,6 +51,7 @@ #include "code\__DEFINES\snpc.dm" #include "code\__DEFINES\sound.dm" #include "code\__DEFINES\stat.dm" +#include "code\__DEFINES\status_effects.dm" #include "code\__DEFINES\tick.dm" #include "code\__DEFINES\typeids.dm" #include "code\__DEFINES\vv.dm" @@ -176,6 +177,7 @@ #include "code\controllers\voting.dm" #include "code\controllers\Processes\air.dm" #include "code\controllers\Processes\alarm.dm" +#include "code\controllers\Processes\effect_process.dm" #include "code\controllers\Processes\event.dm" #include "code\controllers\Processes\fast_process.dm" #include "code\controllers\Processes\fires.dm" @@ -325,6 +327,10 @@ #include "code\datums\spells\trigger.dm" #include "code\datums\spells\turf_teleport.dm" #include "code\datums\spells\wizard.dm" +#include "code\datums\status_effects\buffs.dm" +#include "code\datums\status_effects\debuffs.dm" +#include "code\datums\status_effects\neutral.dm" +#include "code\datums\status_effects\status_effect.dm" #include "code\datums\vr\level.dm" #include "code\datums\weather\weather.dm" #include "code\datums\weather\weather_types.dm"