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"