From 77920896a8cd637221b7f8dc3437e5757dbb7bb4 Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Fri, 25 Oct 2024 02:37:34 -0400
Subject: [PATCH 01/16] Add new bloodfledge quirk
Adds the Sanguine Metabolism quirk. This is a lite version of Bloodsucker Fledgling with no special powers.
---
code/__SPLURTCODE/DEFINES/traits.dm | 1 +
.../subsystem/processing/quirks.dm | 4 ++
modular_splurt/code/datums/traits/negative.dm | 48 +++++++++++++++++++
.../structures/crates_lockers/crates.dm | 11 +++++
4 files changed, 64 insertions(+)
diff --git a/code/__SPLURTCODE/DEFINES/traits.dm b/code/__SPLURTCODE/DEFINES/traits.dm
index 3274bba85986..9ede9736c22f 100644
--- a/code/__SPLURTCODE/DEFINES/traits.dm
+++ b/code/__SPLURTCODE/DEFINES/traits.dm
@@ -25,6 +25,7 @@
#define TRAIT_PHARMA "hepatic_pharmacokinesis"
#define TRAIT_CHOKE_SLUT "choke_slut"
#define TRAIT_BLOODFLEDGE "bloodfledge"
+#define TRAIT_BLOODFLEDGE_LITE "bloodfledge_lite"
#define TRAIT_INCUBUS "incubus"
#define TRAIT_SUCCUBUS "succubus"
#define TRAIT_ARACHNID "arachnid"
diff --git a/modular_splurt/code/controllers/subsystem/processing/quirks.dm b/modular_splurt/code/controllers/subsystem/processing/quirks.dm
index 6f25dda079b6..9d2480606bf5 100644
--- a/modular_splurt/code/controllers/subsystem/processing/quirks.dm
+++ b/modular_splurt/code/controllers/subsystem/processing/quirks.dm
@@ -21,4 +21,8 @@
// BLOCKED: Mechanic
// Bloodsuckers have NO_THIRST trait.
list("Thirsty","Bloodsucker Fledgling"),
+
+ // BLOCKED: Duplicate, mechanic
+ // This is a lite version of the same quirk.
+ list("Sanguine Metabolism","Bloodsucker Fledgling")
))
diff --git a/modular_splurt/code/datums/traits/negative.dm b/modular_splurt/code/datums/traits/negative.dm
index ad7d617a3422..8dfec878feb3 100644
--- a/modular_splurt/code/datums/traits/negative.dm
+++ b/modular_splurt/code/datums/traits/negative.dm
@@ -239,3 +239,51 @@
if(H)
var/datum/physiology/P = H.physiology
P.thirst_mod /= 2
+
+// Lite version of Bloodflege
+// Removes all special powers
+/datum/quirk/bloodfledge_lite
+ name = "Sanguine Metabolism"
+ desc = "You are a non-magical Bloodsucker Fledgling, with no special powers. Only blood will sate your hungers, and holy energies will cause your flesh to char."
+ value = 0
+ medical_record_text = "Patient exhibits an unusually weak sanguine curse."
+ mob_trait = TRAIT_BLOODFLEDGE_LITE
+ gain_text = span_notice("You feel a sanguine thirst.")
+ lose_text = span_notice("You feel the sanguine thirst fade away.")
+
+/datum/quirk/bloodfledge_lite/add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Add quirk traits
+ ADD_TRAIT(quirk_mob,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ ADD_TRAIT(quirk_mob,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+ // Add full trait to preserve bite, holy weakness, and examine functionality
+ ADD_TRAIT(quirk_mob,TRAIT_BLOODFLEDGE,ROUNDSTART_TRAIT)
+
+ // Lite version does not set skin tone or grant the language
+ // Lite version also has no examine text
+
+/datum/quirk/bloodfledge_lite/post_add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Define and grant ability Bite
+ var/datum/action/cooldown/bloodfledge/bite/act_bite = new
+ act_bite.Grant(quirk_mob)
+
+ // Lite version does not grant the revive ability
+
+/datum/quirk/bloodfledge_lite/remove()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove quirk traits
+ REMOVE_TRAIT(quirk_mob, TRAIT_NO_PROCESS_FOOD, ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(quirk_mob, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(quirk_mob, TRAIT_BLOODFLEDGE, ROUNDSTART_TRAIT)
+
+ // Remove quirk ability action datums
+ var/datum/action/cooldown/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
+ act_bite.Remove(quirk_mob)
diff --git a/modular_splurt/code/game/objects/structures/crates_lockers/crates.dm b/modular_splurt/code/game/objects/structures/crates_lockers/crates.dm
index bf2c2ae7c346..0512ebb32cf2 100644
--- a/modular_splurt/code/game/objects/structures/crates_lockers/crates.dm
+++ b/modular_splurt/code/game/objects/structures/crates_lockers/crates.dm
@@ -19,6 +19,12 @@
for(var/mob/living/carbon/coffin_user in contents)
// Check for bloodfledge
if(isbloodfledge(coffin_user))
+ // Check for lite version
+ if (HAS_TRAIT(coffin_user, TRAIT_BLOODFLEDGE_LITE))
+ // Warn user and continue
+ to_chat(coffin_user, span_warning("Your body lacks a proper sanguine connection! [src] does not respond."))
+ continue
+
// Check for synthetic
if(coffin_user.mob_biotypes && (coffin_user.mob_biotypes & MOB_ROBOTIC))
// Warn user and continue
@@ -50,6 +56,11 @@
for(var/mob/living/carbon/coffin_user in coffin_turf.contents)
// Check for bloodfledge
if(isbloodfledge(coffin_user))
+ // Check for lite version
+ if (HAS_TRAIT(coffin_user, TRAIT_BLOODFLEDGE_LITE))
+ // Do nothing
+ continue
+
// Define quirk entry
var/datum/quirk/bloodfledge/quirk_target = locate() in coffin_user.roundstart_quirks
From 41adebb16dd53ba9a093bba8de06911b612f27c5 Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Fri, 25 Oct 2024 02:58:00 -0400
Subject: [PATCH 02/16] Add missing bloodfledge lite quirk conflicts
Adds matching quirk incompatibilities to Sanguine Metabolism.
---
.../code/controllers/subsystem/processing/quirks.dm | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/modular_splurt/code/controllers/subsystem/processing/quirks.dm b/modular_splurt/code/controllers/subsystem/processing/quirks.dm
index 9d2480606bf5..0dd5090c0d63 100644
--- a/modular_splurt/code/controllers/subsystem/processing/quirks.dm
+++ b/modular_splurt/code/controllers/subsystem/processing/quirks.dm
@@ -24,5 +24,13 @@
// BLOCKED: Duplicate, mechanic
// This is a lite version of the same quirk.
- list("Sanguine Metabolism","Bloodsucker Fledgling")
+ list("Sanguine Metabolism","Bloodsucker Fledgling"),
+
+ // BLOCKED: Mechanic
+ // Bloodsuckers have NO_THIRST trait.
+ list("Sanguine Metabolism","Thirsty"),
+
+ // BLOCKED: Thematic, mechanic, game lore.
+ // Bloodsuckers cannot interact with Hallowed users.
+ list("Sanguine Metabolism","Hallowed")
))
From 8c7816b36bd6694e59deb09c5cf607d7188b33bf Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Fri, 25 Oct 2024 04:00:49 -0400
Subject: [PATCH 03/16] Add examine text to Sanguine Metabolism
Restores the examine text present in the source quirk. Text can only be seen by other bloodsucker quirk holders.
---
modular_splurt/code/datums/traits/negative.dm | 51 ++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/modular_splurt/code/datums/traits/negative.dm b/modular_splurt/code/datums/traits/negative.dm
index 8dfec878feb3..f0d2bd1ebdd6 100644
--- a/modular_splurt/code/datums/traits/negative.dm
+++ b/modular_splurt/code/datums/traits/negative.dm
@@ -263,7 +263,9 @@
ADD_TRAIT(quirk_mob,TRAIT_BLOODFLEDGE,ROUNDSTART_TRAIT)
// Lite version does not set skin tone or grant the language
- // Lite version also has no examine text
+
+ // Register examine text
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(quirk_examine_bloodfledge_lite))
/datum/quirk/bloodfledge_lite/post_add()
// Define quirk mob
@@ -287,3 +289,50 @@
// Remove quirk ability action datums
var/datum/action/cooldown/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
act_bite.Remove(quirk_mob)
+
+ // Unregister examine text
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+/datum/quirk/bloodfledge_lite/proc/quirk_examine_bloodfledge_lite(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ SIGNAL_HANDLER
+
+ // Check if human examiner exists
+ if(!istype(examiner))
+ return
+
+ // Check if examiner is a non-Fledgling
+ if(!isbloodfledge(examiner))
+ // Return with no effects
+ return
+
+ // Check if examiner is dumb
+ if(HAS_TRAIT(examiner, TRAIT_DUMB))
+ // Return with no effects
+ return
+
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Define hunger texts
+ var/examine_hunger
+
+ // Check hunger levels
+ switch(quirk_mob.nutrition)
+ // Hungry
+ if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
+ examine_hunger = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] blood starved!"
+
+ // Starving
+ if(0 to NUTRITION_LEVEL_STARVING)
+ examine_hunger = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] in dire need of blood!"
+
+ // Invalid hunger
+ else
+ // Return with no message
+ return
+
+ // Add detection text
+ examine_list += span_info("[quirk_holder.p_their(TRUE)] hunger allows you to identify [quirk_holder.p_them()] as a lesser Bloodsucker Fledgling!")
+
+ // Add hunger text
+ examine_list += span_warning(examine_hunger)
From aa5160f627cec7f768cc2617b6178c12758e5064 Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Fri, 25 Oct 2024 04:06:15 -0400
Subject: [PATCH 04/16] Move Sanguine Metabolism to neutral
Moves the Sanguine Metabolism from negative to neutral.
---
modular_splurt/code/datums/traits/negative.dm | 96 ------------------
modular_splurt/code/datums/traits/neutral.dm | 97 +++++++++++++++++++
2 files changed, 97 insertions(+), 96 deletions(-)
diff --git a/modular_splurt/code/datums/traits/negative.dm b/modular_splurt/code/datums/traits/negative.dm
index f0d2bd1ebdd6..889f9c701596 100644
--- a/modular_splurt/code/datums/traits/negative.dm
+++ b/modular_splurt/code/datums/traits/negative.dm
@@ -240,99 +240,3 @@
var/datum/physiology/P = H.physiology
P.thirst_mod /= 2
-// Lite version of Bloodflege
-// Removes all special powers
-/datum/quirk/bloodfledge_lite
- name = "Sanguine Metabolism"
- desc = "You are a non-magical Bloodsucker Fledgling, with no special powers. Only blood will sate your hungers, and holy energies will cause your flesh to char."
- value = 0
- medical_record_text = "Patient exhibits an unusually weak sanguine curse."
- mob_trait = TRAIT_BLOODFLEDGE_LITE
- gain_text = span_notice("You feel a sanguine thirst.")
- lose_text = span_notice("You feel the sanguine thirst fade away.")
-
-/datum/quirk/bloodfledge_lite/add()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Add quirk traits
- ADD_TRAIT(quirk_mob,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
- ADD_TRAIT(quirk_mob,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
-
- // Add full trait to preserve bite, holy weakness, and examine functionality
- ADD_TRAIT(quirk_mob,TRAIT_BLOODFLEDGE,ROUNDSTART_TRAIT)
-
- // Lite version does not set skin tone or grant the language
-
- // Register examine text
- RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(quirk_examine_bloodfledge_lite))
-
-/datum/quirk/bloodfledge_lite/post_add()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Define and grant ability Bite
- var/datum/action/cooldown/bloodfledge/bite/act_bite = new
- act_bite.Grant(quirk_mob)
-
- // Lite version does not grant the revive ability
-
-/datum/quirk/bloodfledge_lite/remove()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Remove quirk traits
- REMOVE_TRAIT(quirk_mob, TRAIT_NO_PROCESS_FOOD, ROUNDSTART_TRAIT)
- REMOVE_TRAIT(quirk_mob, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
- REMOVE_TRAIT(quirk_mob, TRAIT_BLOODFLEDGE, ROUNDSTART_TRAIT)
-
- // Remove quirk ability action datums
- var/datum/action/cooldown/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
- act_bite.Remove(quirk_mob)
-
- // Unregister examine text
- UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
-
-/datum/quirk/bloodfledge_lite/proc/quirk_examine_bloodfledge_lite(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
- SIGNAL_HANDLER
-
- // Check if human examiner exists
- if(!istype(examiner))
- return
-
- // Check if examiner is a non-Fledgling
- if(!isbloodfledge(examiner))
- // Return with no effects
- return
-
- // Check if examiner is dumb
- if(HAS_TRAIT(examiner, TRAIT_DUMB))
- // Return with no effects
- return
-
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Define hunger texts
- var/examine_hunger
-
- // Check hunger levels
- switch(quirk_mob.nutrition)
- // Hungry
- if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
- examine_hunger = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] blood starved!"
-
- // Starving
- if(0 to NUTRITION_LEVEL_STARVING)
- examine_hunger = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] in dire need of blood!"
-
- // Invalid hunger
- else
- // Return with no message
- return
-
- // Add detection text
- examine_list += span_info("[quirk_holder.p_their(TRUE)] hunger allows you to identify [quirk_holder.p_them()] as a lesser Bloodsucker Fledgling!")
-
- // Add hunger text
- examine_list += span_warning(examine_hunger)
diff --git a/modular_splurt/code/datums/traits/neutral.dm b/modular_splurt/code/datums/traits/neutral.dm
index fdef54a3be29..8f19814284c0 100644
--- a/modular_splurt/code/datums/traits/neutral.dm
+++ b/modular_splurt/code/datums/traits/neutral.dm
@@ -731,3 +731,100 @@
gain_text = span_lewd("You feel like kissing someone...")
lose_text = span_notice("You don't feel like kissing someone anymore...")
medical_record_text = "Patient seems to demonstrate an extraordinary liking in kissing."
+
+// Lite version of Bloodflege
+// Removes all special powers
+/datum/quirk/bloodfledge_lite
+ name = "Sanguine Metabolism"
+ desc = "You are a non-magical Bloodsucker Fledgling, with no special powers. Only blood will sate your hungers, and holy energies will cause your flesh to char."
+ value = 0
+ medical_record_text = "Patient exhibits an unusually weak sanguine curse."
+ mob_trait = TRAIT_BLOODFLEDGE_LITE
+ gain_text = span_notice("You feel a sanguine thirst.")
+ lose_text = span_notice("You feel the sanguine thirst fade away.")
+
+/datum/quirk/bloodfledge_lite/add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Add quirk traits
+ ADD_TRAIT(quirk_mob,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ ADD_TRAIT(quirk_mob,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+ // Add full trait to preserve bite, holy weakness, and examine functionality
+ ADD_TRAIT(quirk_mob,TRAIT_BLOODFLEDGE,ROUNDSTART_TRAIT)
+
+ // Lite version does not set skin tone or grant the language
+
+ // Register examine text
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(quirk_examine_bloodfledge_lite))
+
+/datum/quirk/bloodfledge_lite/post_add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Define and grant ability Bite
+ var/datum/action/cooldown/bloodfledge/bite/act_bite = new
+ act_bite.Grant(quirk_mob)
+
+ // Lite version does not grant the revive ability
+
+/datum/quirk/bloodfledge_lite/remove()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove quirk traits
+ REMOVE_TRAIT(quirk_mob, TRAIT_NO_PROCESS_FOOD, ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(quirk_mob, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(quirk_mob, TRAIT_BLOODFLEDGE, ROUNDSTART_TRAIT)
+
+ // Remove quirk ability action datums
+ var/datum/action/cooldown/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
+ act_bite.Remove(quirk_mob)
+
+ // Unregister examine text
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+/datum/quirk/bloodfledge_lite/proc/quirk_examine_bloodfledge_lite(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ SIGNAL_HANDLER
+
+ // Check if human examiner exists
+ if(!istype(examiner))
+ return
+
+ // Check if examiner is a non-Fledgling
+ if(!isbloodfledge(examiner))
+ // Return with no effects
+ return
+
+ // Check if examiner is dumb
+ if(HAS_TRAIT(examiner, TRAIT_DUMB))
+ // Return with no effects
+ return
+
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Define hunger texts
+ var/examine_hunger
+
+ // Check hunger levels
+ switch(quirk_mob.nutrition)
+ // Hungry
+ if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
+ examine_hunger = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] blood starved!"
+
+ // Starving
+ if(0 to NUTRITION_LEVEL_STARVING)
+ examine_hunger = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] in dire need of blood!"
+
+ // Invalid hunger
+ else
+ // Return with no message
+ return
+
+ // Add detection text
+ examine_list += span_info("[quirk_holder.p_their(TRUE)] hunger allows you to identify [quirk_holder.p_them()] as a lesser Bloodsucker Fledgling!")
+
+ // Add hunger text
+ examine_list += span_warning(examine_hunger)
From 7b038c2486f1a6a89cfb27583bc09340a4642fd7 Mon Sep 17 00:00:00 2001
From: CSDSP <160316561+CSDSP@users.noreply.github.com>
Date: Sun, 27 Oct 2024 02:22:14 -0700
Subject: [PATCH 05/16] Fix shuttle_catastrophe canSpawnEvent
---
code/modules/events/shuttle_catastrophe.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/modules/events/shuttle_catastrophe.dm b/code/modules/events/shuttle_catastrophe.dm
index 18b9dc61cabf..28be4acfd240 100644
--- a/code/modules/events/shuttle_catastrophe.dm
+++ b/code/modules/events/shuttle_catastrophe.dm
@@ -7,7 +7,7 @@
description = "Replaces the emergency shuttle with a random one."
/datum/round_event_control/shuttle_catastrophe/canSpawnEvent(players, gamemode)
- if(SSshuttle.emergency.name == "Build your own shuttle kit")
+ if(istype(SSshuttle.emergency, /obj/docking_port/mobile/emergency/shuttle_build))
return FALSE //don't undo manual player engineering, it also would unload people and ghost them, there's just a lot of problems
if(SSshuttle.emergency.in_flight())
return FALSE //ditto, problems
From e70aeea39b3a270b391b5c6182cec8f3eac522ef Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Mon, 4 Nov 2024 13:49:08 -0500
Subject: [PATCH 06/16] Quirk file separations
This does the following:
- Separates all modular quirks into separate files, with no other changes.
- Modularizes some non-modular edits into a new file
- Fixes location of hydra trait definition
---
code/__SPLURTCODE/DEFINES/traits.dm | 1 +
code/datums/traits/good.dm | 26 +-
code/datums/traits/negative.dm | 1 -
code/datums/traits/neutral.dm | 42 -
.../code/datums/mood_events/needs_events.dm | 9 -
modular_splurt/code/datums/traits/defines.dm | 2 -
modular_splurt/code/datums/traits/good.dm | 536 ------
modular_splurt/code/datums/traits/negative.dm | 241 ---
.../traits/negative_quirks/dumb_for_cum.dm | 85 +
.../datums/traits/negative_quirks/flimsy.dm | 15 +
.../datums/traits/negative_quirks/hungry.dm | 20 +
.../traits/negative_quirks/hypersensitive.dm | 20 +
.../traits/negative_quirks/illiterate.dm | 7 +
.../datums/traits/negative_quirks/no_guns.dm | 7 +
.../traits/negative_quirks/social_anxiety.dm | 65 +
.../datums/traits/negative_quirks/thirsty.dm | 18 +
modular_splurt/code/datums/traits/neutral.dm | 733 -------
.../traits/neutral_quirks/body_morpher.dm | 25 +
.../traits/neutral_quirks/choke_slut.dm | 7 +
.../datums/traits/neutral_quirks/cosglow.dm | 108 ++
.../datums/traits/neutral_quirks/cum_plus.dm | 33 +
.../datums/traits/neutral_quirks/cum_sniff.dm | 8 +
.../traits/neutral_quirks/cursed_blood.dm | 8 +
.../traits/neutral_quirks/fluid_infuser.dm | 9 +
.../datums/traits/neutral_quirks/gargoyle.dm | 148 ++
.../traits/neutral_quirks/headpat_hater.dm | 39 +
.../traits/neutral_quirks/headpat_slut.dm | 16 +
.../datums/traits/neutral_quirks/hydra.dm | 50 +
.../traits/neutral_quirks/hypnotic_gaze.dm | 382 ++++
.../datums/traits/neutral_quirks/incubus.dm | 24 +
.../traits/neutral_quirks/jiggly_ass.dm | 19 +
.../datums/traits/neutral_quirks/kiss_slut.dm | 8 +
.../traits/neutral_quirks/masked_mook.dm | 24 +
.../datums/traits/neutral_quirks/messy.dm | 8 +
.../traits/neutral_quirks/modularlimbs.dm | 66 +
.../datums/traits/neutral_quirks/nudist.dm | 88 +
.../traits/neutral_quirks/overweight.dm | 12 +
.../traits/neutral_quirks/pharmacokinesis.dm | 9 +
.../datums/traits/neutral_quirks/steel_ass.dm | 7 +
.../neutral_quirks/storage_concealment.dm | 27 +
.../datums/traits/neutral_quirks/succubus.dm | 24 +
.../datums/traits/neutral_quirks/undead.dm | 31 +
.../traits/neutral_quirks/vegetarian.dm | 22 +
.../traits/neutral_quirks/well_trained.dm | 83 +
.../datums/traits/neutral_quirks/werewolf.dm | 247 +++
.../datums/traits/positive_quirks/arachnid.dm | 30 +
.../traits/positive_quirks/ash_resistance.dm | 18 +
.../traits/positive_quirks/bloodfledge.dm | 1029 ++++++++++
.../traits/positive_quirks/breathless.dm | 24 +
.../traits/positive_quirks/cloth_eater.dm | 6 +
.../traits/positive_quirks/dominant_aura.dm | 72 +
.../datums/traits/positive_quirks/flutter.dm | 5 +
.../datums/traits/positive_quirks/hallowed.dm | 44 +
.../traits/positive_quirks/hard_soles.dm | 4 +
.../datums/traits/positive_quirks/radfiend.dm | 90 +
.../positive_quirks/restorative_metabolism.dm | 26 +
.../traits/positive_quirks/ropebunny.dm | 47 +
.../datums/traits/positive_quirks/tough.dm | 15 +
.../positive_quirks/vacuum_resistance.dm | 19 +
.../code/datums/traits/trait_actions.dm | 1704 -----------------
.../code/datums/traits/trait_edits.dm | 20 +
tgstation.dme | 56 +-
62 files changed, 3291 insertions(+), 3278 deletions(-)
delete mode 100644 modular_splurt/code/datums/traits/defines.dm
delete mode 100644 modular_splurt/code/datums/traits/good.dm
delete mode 100644 modular_splurt/code/datums/traits/negative.dm
create mode 100644 modular_splurt/code/datums/traits/negative_quirks/dumb_for_cum.dm
create mode 100644 modular_splurt/code/datums/traits/negative_quirks/flimsy.dm
create mode 100644 modular_splurt/code/datums/traits/negative_quirks/hungry.dm
create mode 100644 modular_splurt/code/datums/traits/negative_quirks/hypersensitive.dm
create mode 100644 modular_splurt/code/datums/traits/negative_quirks/illiterate.dm
create mode 100644 modular_splurt/code/datums/traits/negative_quirks/no_guns.dm
create mode 100644 modular_splurt/code/datums/traits/negative_quirks/social_anxiety.dm
create mode 100644 modular_splurt/code/datums/traits/negative_quirks/thirsty.dm
delete mode 100644 modular_splurt/code/datums/traits/neutral.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/body_morpher.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/choke_slut.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/cosglow.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/cum_plus.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/cum_sniff.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/cursed_blood.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/fluid_infuser.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/gargoyle.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/headpat_hater.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/headpat_slut.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/hydra.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/hypnotic_gaze.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/incubus.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/jiggly_ass.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/kiss_slut.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/masked_mook.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/messy.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/modularlimbs.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/nudist.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/overweight.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/pharmacokinesis.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/steel_ass.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/storage_concealment.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/succubus.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/undead.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/vegetarian.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/well_trained.dm
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/werewolf.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/arachnid.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/ash_resistance.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/bloodfledge.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/breathless.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/cloth_eater.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/dominant_aura.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/flutter.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/hallowed.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/hard_soles.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/radfiend.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/restorative_metabolism.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/ropebunny.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/tough.dm
create mode 100644 modular_splurt/code/datums/traits/positive_quirks/vacuum_resistance.dm
delete mode 100644 modular_splurt/code/datums/traits/trait_actions.dm
create mode 100644 modular_splurt/code/datums/traits/trait_edits.dm
diff --git a/code/__SPLURTCODE/DEFINES/traits.dm b/code/__SPLURTCODE/DEFINES/traits.dm
index 3274bba85986..6db0b086093f 100644
--- a/code/__SPLURTCODE/DEFINES/traits.dm
+++ b/code/__SPLURTCODE/DEFINES/traits.dm
@@ -50,3 +50,4 @@
#define TRAIT_MESSY "messy"
#define TRAIT_RESTORATIVE_METABOLISM "restorative_metabolism"
#define TRAIT_KISS_SLUT "kiss_slut"
+#define TRAIT_HYDRA_HEADS "hydrahead" //Hydra head port.
diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm
index 404bfd95a101..e0793e952af0 100644
--- a/code/datums/traits/good.dm
+++ b/code/datums/traits/good.dm
@@ -40,7 +40,7 @@
/datum/quirk/empath
name = "Empath"
desc = "Whether it's a sixth sense or careful study of body language, it only takes you a quick glance at someone to understand how they feel."
- value = 1 // SPLURT EDIT
+ value = 2
mob_trait = TRAIT_EMPATH
gain_text = "You feel in tune with those around you."
lose_text = "You feel isolated from others."
@@ -49,7 +49,7 @@
/datum/quirk/freerunning
name = "Freerunning"
desc = "You're great at quick moves! You can climb tables more quickly."
- value = 1 // SPLURT EDIT
+ value = 2
mob_trait = TRAIT_FREERUNNING
gain_text = "You feel lithe on your feet!"
lose_text = "You feel clumsy again."
@@ -90,7 +90,7 @@
/datum/quirk/quick_step
name = "Quick Step"
desc = "You walk with determined strides, and out-pace most people when walking."
- value = 1 // SPLURT EDIT
+ value = 2
mob_trait = TRAIT_SPEEDY_STEP
gain_text = "You feel determined. No time to lose."
lose_text = "You feel less determined. What's the rush, man?"
@@ -115,6 +115,22 @@
H.equip_to_slot_if_possible(musicaltuner, ITEM_SLOT_BACKPACK)
H.regenerate_icons()
+/datum/quirk/photographer
+ name = "Photographer"
+ desc = "You know how to handle a camera, shortening the delay between each shot."
+ value = 1
+ mob_trait = TRAIT_PHOTOGRAPHER
+ gain_text = "You know everything about photography."
+ lose_text = "You forget how photo cameras work."
+ medical_record_text = "Patient mentions photography as a stress-relieving hobby."
+
+/datum/quirk/photographer/on_spawn()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/camera/camera = new(get_turf(H))
+ H.put_in_hands(camera)
+ H.equip_to_slot(camera, ITEM_SLOT_NECK)
+ H.regenerate_icons()
+
/datum/quirk/selfaware
name = "Self-Aware"
desc = "You know your body well, and can accurately assess the extent of your wounds."
@@ -125,7 +141,7 @@
/datum/quirk/skittish
name = "Skittish"
desc = "You can conceal yourself in danger. Ctrl-shift-click a closed locker to jump into it, as long as you have access."
- value = 1 // SPLURT EDIT
+ value = 2
mob_trait = TRAIT_SKITTISH
medical_record_text = "Patient demonstrates a high aversion to danger and has described hiding in containers out of fear."
@@ -156,7 +172,7 @@
/datum/quirk/voracious
name = "Voracious"
- desc = "Nothing gets between you and your food. You eat twice as fast as everyone else, and won't get sad by being fat!"
+ desc = "Nothing gets between you and your food. You eat twice as fast as everyone else!"
value = 1
mob_trait = TRAIT_VORACIOUS
gain_text = "You feel HONGRY."
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index 961ef8e0fa53..6a68334c853f 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -354,7 +354,6 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
gain_text = "You start worrying about what you're saying."
lose_text = "You feel easier about talking again." //if only it were that easy!
medical_record_text = "Patient is usually anxious in social encounters and prefers to avoid them."
- mob_trait = TRAIT_ANXIOUS
var/dumb_thing = TRUE
processing_quirk = TRUE
diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm
index 45bcce41d8f2..4db3d688cef6 100644
--- a/code/datums/traits/neutral.dm
+++ b/code/datums/traits/neutral.dm
@@ -182,45 +182,3 @@
/datum/quirk/dullahan/post_add()
quirk_holder.AddComponent(/datum/component/dullahan)
-
-/datum/quirk/photographer
- name = "Photographer"
- desc = "You carry your camera and personal photo album everywhere you go - and you're quicker at taking pictures."
- value = 0
- mob_trait = TRAIT_PHOTOGRAPHER
- gain_text = "You know everything about photography."
- lose_text = "You forget how photo cameras work."
- medical_record_text = "Patient mentions photography as a stress-relieving hobby."
-
-/datum/quirk/photographer/on_spawn()
- var/mob/living/carbon/human/H = quirk_holder
- var/obj/item/storage/photo_album/photo_album = new(get_turf(H))
- H.put_in_hands(photo_album)
- H.equip_to_slot(photo_album, ITEM_SLOT_BACKPACK)
- photo_album.persistence_id = "personal_[H.mind.key]" // this is a persistent album, the ID is tied to the account's key to avoid tampering
- photo_album.persistence_load()
- photo_album.name = "[H.real_name]'s photo album"
- var/obj/item/camera/camera = new(get_turf(H))
- H.put_in_hands(camera)
- H.equip_to_slot(camera, ITEM_SLOT_BACKPACK) //SPLURT Edit
- H.regenerate_icons()
-
-/datum/quirk/jiggly_ass
- name = "Buns of Thunder"
- desc = "That pants-stretching, seat-creaking, undie-devouring butt of yours is as satisfying as it is difficult to keep balanced when smacked!"
- value = 0
- mob_trait = TRAIT_JIGGLY_ASS
- gain_text = span_notice("Your butt feels extremely smackable.")
- lose_text = span_notice("Your butt feels normally smackable again.")
-
-/datum/quirk/jiggly_ass/add()
- // Add examine text
- RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
-
-/datum/quirk/jiggly_ass/remove()
- // Remove examine text
- UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
-
-// Quirk examine text
-/datum/quirk/jiggly_ass/proc/on_examine_holder(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
- examine_list += span_lewd("[quirk_holder.p_their(TRUE)] butt could use a firm smack.")
diff --git a/modular_splurt/code/datums/mood_events/needs_events.dm b/modular_splurt/code/datums/mood_events/needs_events.dm
index 7e966dced081..e3e2de127ec3 100644
--- a/modular_splurt/code/datums/mood_events/needs_events.dm
+++ b/modular_splurt/code/datums/mood_events/needs_events.dm
@@ -5,12 +5,3 @@
return
description = span_nicegreen("MORE FOOD!!! MORE FOOD!!! MORE FOOD!!!\n")
mood_change = 8
-
-/datum/mood_event/cum_craving
- description = span_warning("I... NEED... CUM...\n")
- mood_change = -20
-
-/datum/mood_event/cum_stuffed
- description = span_nicegreen("The cum feels so good inside me!\n")
- mood_change = 8
- timeout = 5 MINUTES
diff --git a/modular_splurt/code/datums/traits/defines.dm b/modular_splurt/code/datums/traits/defines.dm
deleted file mode 100644
index 147f84653c40..000000000000
--- a/modular_splurt/code/datums/traits/defines.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-#define TRAIT_HYDRA_HEADS "hydrahead" //Hydra head port.
-
diff --git a/modular_splurt/code/datums/traits/good.dm b/modular_splurt/code/datums/traits/good.dm
deleted file mode 100644
index a001ab354718..000000000000
--- a/modular_splurt/code/datums/traits/good.dm
+++ /dev/null
@@ -1,536 +0,0 @@
-/datum/quirk/tough
- name = "Tough"
- desc = "Your body is abnormally enduring and can take 10% more damage."
- value = 2
- medical_record_text = "Patient has an abnormally high capacity for injury."
- gain_text = span_notice("You feel very sturdy.")
- lose_text = span_notice("You feel less sturdy.")
-
-/datum/quirk/tough/add()
- quirk_holder.maxHealth *= 1.1
-
-/datum/quirk/tough/remove()
- if(!quirk_holder)
- return
- quirk_holder.maxHealth *= 0.909 //close enough
-
-/datum/quirk/ashresistance
- name = "Ashen Resistance"
- desc = "Your body is adapted to the burning sheets of ash that coat volcanic worlds, though the heavy downpours of silt will still tire you."
- value = 2 //Is not actually THAT good. Does not grant breathing and does stamina damage to the point you are unable to attack. Crippling on lavaland, but you'll survive. Is not a replacement for SEVA suits for this reason. Can be adjusted.
- mob_trait = TRAIT_ASHRESISTANCE
- medical_record_text = "Patient has an abnormally thick epidermis."
- gain_text = span_notice("You feel resistant to burning brimstone.")
- lose_text = span_notice("You feel less as if your flesh is more flamamble.")
-
-/* --FALLBACK SYSTEM INCASE THE TRAIT FAILS TO WORK. Do NOT enable this without editing ash_storm.dm to deal stamina damage with ash immunity.
-/datum/quirk/ashresistance/add()
- quirk_holder.weather_immunities |= "ash"
-
-/datum/quirk/ashresistance/remove()
- if(!quirk_holder)
- return
- quirk_holder.weather_immunities -= "ash"
-*/
-
-/datum/quirk/rad_fiend
- name = "Rad Fiend"
- desc = "You've been blessed by Cherenkov's warming light, causing you to emit a subtle glow at all times. Only -very- intense radiation is capable of penetrating your protective barrier."
- value = 2
- mob_trait = TRAIT_RAD_FIEND
- gain_text = span_notice("You feel empowered by Cherenkov's glow.")
- lose_text = span_notice("You realize that rads aren't so rad.")
-
-/datum/quirk/rad_fiend/add()
- // Define quirk holder mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
- // Add glow control action
- var/datum/action/rad_fiend/update_glow/quirk_action = new
- quirk_action.Grant(quirk_mob)
-
-/datum/quirk/rad_fiend/remove()
- // Define quirk holder mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Remove glow control action
- var/datum/action/rad_fiend/update_glow/quirk_action = locate() in quirk_mob.actions
- quirk_action.Remove(quirk_mob)
-
- // Remove glow effect
- quirk_mob.remove_filter("rad_fiend_glow")
-
-/datum/quirk/dominant_aura
- name = "Dominant Aura"
- desc = "Your mere presence is assertive enough to appear as powerful to other people, so much in fact that the weaker kind can't help but throw themselves at your feet at the snap of a finger."
- value = 1
- gain_text = span_notice("You feel like making someone your pet.")
- lose_text = span_notice("You feel less assertive.")
-
-/datum/quirk/dominant_aura/add()
- . = ..()
- RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
- RegisterSignal(quirk_holder, COMSIG_MOB_EMOTE, PROC_REF(handle_snap))
-
-/datum/quirk/dominant_aura/remove()
- . = ..()
- UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
- UnregisterSignal(quirk_holder, COMSIG_MOB_EMOTE)
-
-/datum/quirk/dominant_aura/proc/on_examine_holder(atom/source, mob/user, list/examine_list)
- SIGNAL_HANDLER
-
- if(!ishuman(user))
- return
- var/mob/living/carbon/human/sub = user
- if(!sub.has_quirk(/datum/quirk/well_trained) || (sub == quirk_holder))
- return
-
- examine_list += span_lewd("\nYou can't make eye contact with [quirk_holder.p_them()] before flustering away!")
- if(!TIMER_COOLDOWN_CHECK(user, COOLDOWN_DOMINANT_EXAMINE))
- to_chat(quirk_holder, span_notice("\The [user] tries to look at you but immediately turns away with a red face..."))
- TIMER_COOLDOWN_START(user, COOLDOWN_DOMINANT_EXAMINE, 10 SECONDS)
- sub.dir = turn(get_dir(sub, quirk_holder), pick(-90, 90))
- sub.emote("blush")
-
-/datum/quirk/dominant_aura/proc/handle_snap(datum/source, datum/emote/emote)
- SIGNAL_HANDLER
-
- . = FALSE
- if(TIMER_COOLDOWN_CHECK(quirk_holder, COOLDOWN_DOMINANT_SNAP) || !findtext(emote.key, "snap"))
- return
- for(var/mob/living/carbon/human/sub in hearers(DOMINANT_DETECT_RANGE, quirk_holder))
- if(!sub.has_quirk(/datum/quirk/well_trained) || (sub == quirk_holder))
- continue
- var/good_x = "pet"
- switch(sub.gender)
- if(MALE)
- good_x = "boy"
- if(FEMALE)
- good_x = "girl"
- switch(emote.key)
- if("snap")
- sub.dir = get_dir(sub, quirk_holder)
- sub.emote(pick("blush", "pant"))
- sub.visible_message(span_notice("\The [sub] turns shyly towards \the [quirk_holder]."),
- span_lewd("You stare into \the [quirk_holder] submissively."))
- if("snap2")
- sub.dir = get_dir(sub, quirk_holder)
- sub.KnockToFloor()
- sub.emote(pick("blush", "pant"))
- sub.visible_message(span_lewd("\The [sub] submissively throws [sub.p_them()]self on the floor."),
- span_lewd("You throw yourself on the floor like a pathetic beast on [quirk_holder]'s command."))
- if("snap3")
- sub.KnockToFloor()
- step(sub, get_dir(sub, quirk_holder))
- sub.emote(pick("blush", "pant"))
- sub.do_jitter_animation(30) //You're being moved anyways
- sub.visible_message(span_lewd("\The [sub] crawls closer to \the [quirk_holder] on all fours, following [quirk_holder.p_their()] command."),
- span_lewd("You get on your hands and knees and crawl towards \the [quirk_holder] like a good [good_x] would."))
- . = TRUE
-
- if(.)
- TIMER_COOLDOWN_START(quirk_holder, COOLDOWN_DOMINANT_SNAP, DOMINANT_SNAP_COOLDOWN)
-
-/datum/quirk/arachnid
- name = "Arachnid"
- desc = "Your bodily anatomy allows you to spin webs and cocoons, even if you aren't an arachnid! (Note that this quirk does nothing for members of the arachnid species)"
- value = 1
- medical_record_text = "Patient has attempted to cover the room in webs, claiming to be \"making a nest\"."
- mob_trait = TRAIT_ARACHNID
- gain_text = span_notice("You feel a strange sensation near your anus...")
- lose_text = span_notice("You feel like you can't spin webs anymore...")
- processing_quirk = TRUE
-
-/datum/quirk/arachnid/add()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- if(is_species(H,/datum/species/arachnid))
- to_chat(H, span_warning("As an arachnid, this quirk does nothing for you, as these abilities are innate to your species."))
- return
- var/datum/action/innate/spin_web/SW = new
- var/datum/action/innate/spin_cocoon/SC = new
- SC.Grant(H)
- SW.Grant(H)
-
-/datum/quirk/arachnid/remove()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- if(is_species(H,/datum/species/arachnid))
- return
- var/datum/action/innate/spin_web/SW = locate(/datum/action/innate/spin_web) in H.actions
- var/datum/action/innate/spin_cocoon/SC = locate(/datum/action/innate/spin_cocoon) in H.actions
- SC?.Remove(H)
- SW?.Remove(H)
-
-/datum/quirk/flutter
- name = "Flutter"
- desc = "You are able to move about freely in pressurized low-gravity environments be it through the use of wings, magic, or some other physiological nonsense."
- value = 1
- mob_trait = TRAIT_FLUTTER
-
-/datum/quirk/cloth_eater
- name = "Clothes Eater"
- desc = "You can eat most apparel to gain a boost in mood, and to gain some nutrients. (Insects already have this.)"
- value = 1
- var/mood_category ="cloth_eaten"
- mob_trait = TRAIT_CLOTH_EATER
-
-/datum/quirk/ropebunny
- name = "Rope Bunny"
- desc = "You have mastered all forms of bondage! You can create bondage rope out of cloth, and bondage bolas out of bondage rope!"
- value = 2
-
-/datum/quirk/ropebunny/add()
- .=..()
- var/mob/living/carbon/human/H = quirk_holder
- if (!H)
- return
- var/datum/action/ropebunny/conversion/C = new
- C.Grant(H)
-
-/datum/quirk/ropebunny/remove()
- var/mob/living/carbon/human/H = quirk_holder
- var/datum/action/ropebunny/conversion/C = locate() in H.actions
- C.Remove(H)
- . = ..()
-
-/datum/quirk/hallowed
- name = "Hallowed"
- desc = "You have been blessed by a higher power or are otherwise imbued with holy energy in some way. Your divine presence drives away magic and the unholy! Holy water will restore your health."
- value = 1 // Maybe up the cost if more is added later.
- mob_trait = TRAIT_HALLOWED
- medical_record_text = "Patient contains an unidentified hallowed material concentrated in their blood. Please consult a chaplain."
- gain_text = span_notice("You feel holy energy starting to flow through your body.")
- lose_text = span_notice("You feel your holy energy fading away...")
-
-/datum/quirk/hallowed/add()
- // Define quirk mob.
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Give the holy trait.
- ADD_TRAIT(quirk_mob, TRAIT_HOLY, "quirk_hallowed")
-
- // Give the antimagic trait.
- ADD_TRAIT(quirk_mob, TRAIT_ANTIMAGIC, "quirk_hallowed")
-
- // Makes the user holy.
- quirk_mob.mind.isholy = TRUE
-
- // Add examine text.
- RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
-
-/datum/quirk/hallowed/remove()
- // Define quirk mob.
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Remove the holy trait.
- REMOVE_TRAIT(quirk_mob, TRAIT_HOLY, "quirk_hallowed")
-
- // Remove the antimagic trait.
- REMOVE_TRAIT(quirk_mob, TRAIT_ANTIMAGIC, "quirk_hallowed")
-
- // Makes the user not holy.
- quirk_mob.mind.isholy = FALSE
-
- // Remove examine text
- UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
-
-// Quirk examine text.
-/datum/quirk/hallowed/proc/on_examine_holder(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
- examine_list += "[quirk_holder.p_they(TRUE)] radiates divine power..."
-
-/datum/quirk/vacuum_resistance
- name = "Vacuum Resistance"
- desc = "Your body, whether due to technology, magic, or genetic engineering - is specially adapted to withstand and operate in the vacuum of space. You may still need a source of breathable air, however."
- value = 3
- gain_text = span_notice("Your physique attunes to the silence of space, now able to operate in zero pressure.")
- lose_text = span_notice("Your physiology reverts as your spacefaring gifts lay dormant once more.")
- var/list/perks = list(TRAIT_RESISTCOLD, TRAIT_RESISTLOWPRESSURE, TRAIT_LOWPRESSURECOOLING)
-
-/datum/quirk/vacuum_resistance/add()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- for(var/perk in perks)
- ADD_TRAIT(H, perk, ROUNDSTART_TRAIT)
-
-/datum/quirk/vacuum_resistance/remove()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- for(var/perk in perks)
- REMOVE_TRAIT(H, perk, ROUNDSTART_TRAIT)
-
-/datum/quirk/restorative_metabolism
- name = "Restorative Metabolism"
- desc = "Your body possesses a differentiated reconstutitive ability, allowing you to slowly recover from injuries. Note, however, that critical injuries, wounds or genetic damage will still require medical attention."
- value = 3
- mob_trait = TRAIT_RESTORATIVE_METABOLISM
- gain_text = span_notice("You feel a surge of reconstutitive vitality coursing through your body...")
- lose_text = span_notice("You sense your enhanced reconstutitive ability fading away...")
- processing_quirk = TRUE
-
-/datum/quirk/restorative_metabolism/on_process()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
-
- // The only_organic flag allows robotic biotypes to not be excluded.
- H.adjustBruteLoss(-0.5, only_organic = FALSE) //The healing factor will only regenerate fully if not burnt beyond a specific threshold.
- H.adjustFireLoss(-0.25, only_organic = FALSE)
- if(H.getBruteLoss() > 0 && H.getFireLoss() <= 50 || H.getFireLoss() > 0 && H.getFireLoss() <= 50)
- H.adjustBruteLoss(-0.5, forced = TRUE, only_organic = FALSE)
- H.adjustFireLoss(-0.25, forced = TRUE, only_organic = FALSE)
- /* Doesn't heal robotic toxin damage (only_organic = FALSE not needed for tox),
- another adjustToxLoss check with toxins_type = TOX_SYSCORRUPT,
- (or just adding TOX_OMNI to the already existing one) could be added to heal robotic corruption,
- but would make robotic species unbalanced.
- */
- else if (H.getToxLoss() <= 90)
- H.adjustToxLoss(-0.3, forced = TRUE)
-
-/datum/quirk/bloodfledge
- name = "Bloodsucker Fledgling"
- desc = "You are a fledgling belonging to ancient Bloodsucker bloodline. While the blessing has yet to fully convert you, some things have changed. Only blood will sate your hungers, and holy energies will cause your flesh to char. This is NOT an antagonist role!"
- value = 2
- medical_record_text = "Patient exhibits onset symptoms of a sanguine curse."
- mob_trait = TRAIT_BLOODFLEDGE
- gain_text = span_notice("You feel a sanguine thirst.")
- lose_text = span_notice("You feel the sanguine thirst fade away.")
- processing_quirk = FALSE // Handled by crates.dm
-
-/datum/quirk/bloodfledge/add()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Add quirk traits
- ADD_TRAIT(quirk_mob,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
- ADD_TRAIT(quirk_mob,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
-
- // Set skin tone, if possible
- if(!quirk_mob.dna.skin_tone_override)
- quirk_mob.skin_tone = "albino"
-
- // Add quirk language
- quirk_mob.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
-
- // Register examine text
- RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(quirk_examine_bloodfledge))
-
-/datum/quirk/bloodfledge/post_add()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Define and grant ability Bite
- var/datum/action/cooldown/bloodfledge/bite/act_bite = new
- act_bite.Grant(quirk_mob)
-
- // Check for synthetic
- // Robotic mobs have technical issues with adjusting damage
- if(quirk_mob.mob_biotypes & MOB_ROBOTIC)
- // Warn user
- to_chat(quirk_mob, span_warning("As a synthetic lifeform, your components are only able to grant limited sanguine abilities! Regeneration and revival are not possible."))
-
- // User is not synthetic
- else
- // Define and grant ability Revive
- var/datum/action/cooldown/bloodfledge/revive/act_revive = new
- act_revive.Grant(quirk_mob)
-
-/datum/quirk/bloodfledge/on_process()
- // Processing is currently only used for coffin healing
- // This is started and stopped by a proc in crates.dm
-
- // Define potential coffin
- var/quirk_coffin = quirk_holder.loc
-
- // Check if the current area is a coffin
- if(istype(quirk_coffin, /obj/structure/closet/crate/coffin))
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Quirk mob must be injured
- if(quirk_mob.health >= quirk_mob.maxHealth)
- // Warn user
- to_chat(quirk_mob, span_notice("[quirk_coffin] does nothing more to help you, as your body is fully mended."))
-
- // Stop processing and return
- STOP_PROCESSING(SSquirks, src)
- return
-
- // Nutrition (blood) level must be above STARVING
- if(quirk_mob.nutrition <= NUTRITION_LEVEL_STARVING)
- // Warn user
- to_chat(quirk_mob, span_warning("[quirk_coffin] requires blood to operate, which you are currently lacking. Your connection to the other-world fades once again."))
-
- // Stop processing and return
- STOP_PROCESSING(SSquirks, src)
- return
-
- // Define initial health
- var/health_start = quirk_mob.health
-
- // Heal brute and burn
- // Accounts for robotic limbs
- quirk_mob.heal_overall_damage(2,2)
- // Heal oxygen
- quirk_mob.adjustOxyLoss(-2)
- // Heal clone
- quirk_mob.adjustCloneLoss(-2)
-
- // Check for slime race
- // NOT a slime
- if(!isslimeperson(quirk_mob))
- // Heal toxin
- quirk_mob.adjustToxLoss(-2)
- // IS a slime
- else
- // Grant toxin (heals slimes)
- quirk_mob.adjustToxLoss(2)
-
- // Update health
- quirk_mob.updatehealth()
-
- // Determine healed amount
- var/health_restored = quirk_mob.health - health_start
-
- // Remove nutrition (blood) as compensation for healing
- // Amount is equal to 50% of healing done
- quirk_mob.adjust_nutrition(health_restored*-1)
-
- // User is not in a coffin
- // This should not occur without teleportation
- else
- // Warn user
- to_chat(quirk_holder, span_warning("Your connection to the other-world is broken upon leaving the [quirk_coffin]!"))
-
- // Stop processing
- STOP_PROCESSING(SSquirks, src)
-
-/datum/quirk/bloodfledge/remove()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Remove quirk traits
- REMOVE_TRAIT(quirk_mob, TRAIT_NO_PROCESS_FOOD, ROUNDSTART_TRAIT)
- REMOVE_TRAIT(quirk_mob, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
-
- // Remove quirk ability action datums
- var/datum/action/cooldown/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
- var/datum/action/cooldown/bloodfledge/revive/act_revive = locate() in quirk_mob.actions
- act_bite.Remove(quirk_mob)
- act_revive.Remove(quirk_mob)
-
- // Remove quirk language
- quirk_mob.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
-
- // Unregister examine text
- UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
-
-/datum/quirk/bloodfledge/on_spawn()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Create vampire ID card
- var/obj/item/card/id/vampire/id_vampire = new /obj/item/card/id/vampire(get_turf(quirk_holder))
-
- // Update card information
- id_vampire.registered_name = quirk_mob.real_name
- id_vampire.update_label(addtext(id_vampire.registered_name, "'s Bloodfledge"))
-
- // Determine banking ID information
- for(var/bank_account in SSeconomy.bank_accounts)
- // Define current iteration's account
- var/datum/bank_account/account = bank_account
-
- // Check for match
- if(account.account_id == quirk_mob.account_id)
- // Add to cards list
- account.bank_cards += src
-
- // Assign account
- id_vampire.registered_account = account
-
- // Stop searching
- break
-
- // Try to add ID to backpack
- var/id_in_bag = quirk_mob.equip_to_slot_if_possible(id_vampire, ITEM_SLOT_BACKPACK) || FALSE
-
- // Text for where the item was sent
- var/id_location = (id_in_bag ? "in your backpack" : "at your feet" )
-
- // Alert user in chat
- // This should not post_add, because the ID is added by on_spawn
- to_chat(quirk_holder, span_boldnotice("There is a bloodfledge's ID card [id_location], linked to your station account. It functions as a spare ID, but lacks job access."))
-
-/datum/quirk/bloodfledge/proc/quirk_examine_bloodfledge(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
- SIGNAL_HANDLER
-
- // Check if human examiner exists
- if(!istype(examiner))
- return
-
- // Check if examiner is dumb
- if(HAS_TRAIT(examiner, TRAIT_DUMB))
- // Return with no effects
- return
-
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Define hunger texts
- var/examine_hunger_public
- var/examine_hunger_secret
-
- // Check hunger levels
- switch(quirk_mob.nutrition)
- // Hungry
- if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
- examine_hunger_secret = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] blood starved!"
- examine_hunger_public = "[quirk_holder.p_they(TRUE)] seem[quirk_holder.p_s()] on edge from something."
-
- // Starving
- if(0 to NUTRITION_LEVEL_STARVING)
- examine_hunger_secret = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] in dire need of blood!"
- examine_hunger_public = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] radiating an aura of frenzied hunger!"
-
- // Invalid hunger
- else
- // Return with no message
- return
-
- // Check if examiner shares the quirk
- if(isbloodfledge(examiner))
- // Add detection text
- examine_list += span_info("[quirk_holder.p_their(TRUE)] hunger makes it easy to identify [quirk_holder.p_them()] as a fellow Bloodsucker Fledgling!")
-
- // Add hunger text
- examine_list += span_warning(examine_hunger_secret)
-
- // Check if public hunger text exists
- else
- // Add hunger text
- examine_list += span_warning(examine_hunger_public)
-
-/datum/quirk/breathless
- name = "Breathless"
- desc = "Whether due to genetic engineering, technology, or bluespace magic, you no longer require air to function. This also means that administering life-saving manuevers such as CPR would be impossible."
- value = 3
- medical_record_text = "Patient's biology demonstrates no need for breathing."
- gain_text = span_notice("You no longer need to breathe.")
- lose_text = span_notice("You need to breathe again...")
- processing_quirk = TRUE
-
-/datum/quirk/breathless/add()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- ADD_TRAIT(H,TRAIT_NOBREATH,ROUNDSTART_TRAIT)
-
-/datum/quirk/breathless/remove()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- REMOVE_TRAIT(H,TRAIT_NOBREATH, ROUNDSTART_TRAIT)
-
-/datum/quirk/breathless/on_process()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- H.adjustOxyLoss(-3) /* Bandaid-fix for a defibrillator "bug",
- Which causes oxy damage to stack for mobs that don't breathe */
diff --git a/modular_splurt/code/datums/traits/negative.dm b/modular_splurt/code/datums/traits/negative.dm
deleted file mode 100644
index ad7d617a3422..000000000000
--- a/modular_splurt/code/datums/traits/negative.dm
+++ /dev/null
@@ -1,241 +0,0 @@
-//Main code edits
-/datum/quirk/social_anxiety
- value = -3
- //mob_trait = TRAIT_ANXIOUS //Not in the code yet, neither are its implementations
- processing_quirk = FALSE //small fixes for big mistakes
-
-/datum/quirk/social_anxiety/add()
- . = ..()
- RegisterSignal(quirk_holder, COMSIG_MOB_SAY, PROC_REF(handle_speech))
-
-/datum/quirk/social_anxiety/remove()
- . = ..()
- UnregisterSignal(quirk_holder, COMSIG_MOB_SAY)
-
-/datum/quirk/social_anxiety/proc/handle_speech(datum/source, list/speech_args)
- SIGNAL_HANDLER
-
- if(HAS_TRAIT(quirk_holder, TRAIT_FEARLESS))
- return
-
- var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
- var/moodmod
- if(mood)
- moodmod = (1+0.02*(50-(max(50, mood.mood_level*(7-mood.sanity_level))))) //low sanity levels are better, they max at 6
- else
- moodmod = (1+0.02*(50-(max(50, 0.1*quirk_holder.nutrition))))
- var/nearby_people = 0
- for(var/mob/living/carbon/human/H in oview(3, quirk_holder))
- if(H.client)
- nearby_people++
- var/message = speech_args[SPEECH_MESSAGE]
- if(message)
- var/list/message_split = splittext(message, " ")
- var/list/new_message = list()
- var/mob/living/carbon/human/quirker = quirk_holder
- for(var/word in message_split)
- if(prob(max(5,(nearby_people*12.5*moodmod))) && word != message_split[1]) //Minimum 1/20 chance of filler
- new_message += pick("uh,","erm,","um,")
- if(prob(min(5,(0.05*(nearby_people*12.5)*moodmod)))) //Max 1 in 20 chance of cutoff after a succesful filler roll, for 50% odds in a 15 word sentence
- quirker.silent = max(3, quirker.silent)
- to_chat(quirker, span_danger("You feel self-conscious and stop talking. You need a moment to recover!"))
- break
- if(prob(max(5,(nearby_people*12.5*moodmod)))) //Minimum 1/20 chance of stutter
- // Add a short stutter, THEN treat our word
- quirker.stuttering = max(0.25, quirker.stuttering)
- new_message += quirker.treat_message(word)
-
- else
- new_message += word
-
- message = jointext(new_message, " ")
- var/mob/living/carbon/human/quirker = quirk_holder
- if(prob(min(50,(0.50*(nearby_people*12.5)*moodmod)))) //Max 50% chance of not talking
- if(dumb_thing)
- to_chat(quirker, span_userdanger("You think of a dumb thing you said a long time ago and scream internally."))
- dumb_thing = FALSE //only once per life
- if(prob(1))
- new/obj/item/reagent_containers/food/snacks/pastatomato(get_turf(quirker)) //now that's what I call spaghetti code
- else
- to_chat(quirk_holder, span_warning("You think that wouldn't add much to the conversation and decide not to say it."))
- if(prob(min(25,(0.25*(nearby_people*12.75)*moodmod)))) //Max 25% chance of silence stacks after succesful not talking roll
- to_chat(quirker, span_danger("You retreat into yourself. You really don't feel up to talking."))
- quirker.silent = max(5, quirker.silent)
- speech_args[SPEECH_MESSAGE] = pick("Uh.","Erm.","Um.")
- else
- speech_args[SPEECH_MESSAGE] = message
-
-//Own stuff
-/datum/quirk/no_guns
- name = "Fat-Fingered"
- desc = "Due to the shape of your hands, width of your fingers or just not having fingers at all, you're unable to fire guns without accommodation."
- value = -2
- mob_trait = TRAIT_CHUNKYFINGERS
- gain_text = span_notice("Your fingers feel... thick.")
- lose_text = span_notice("Your fingers feel normal again.")
-
-/datum/quirk/illiterate
- name = "Illiterate"
- desc = "You can't read nor write, plain and simple."
- value = -1
- mob_trait = TRAIT_ILLITERATE
- gain_text = span_notice("The knowledge of how to read seems to escape from you.")
- lose_text = span_notice("Written words suddenly make sense again.")
-
-/datum/quirk/flimsy
- name = "Flimsy"
- desc = "Your body is a little more fragile then most, decreasing total health by 20%."
- value = -2
- medical_record_text = "Patient has abnormally low capacity for injury."
- gain_text = span_notice("You feel like you could break with a single hit.")
- lose_text = span_notice("You feel more durable.")
-
-/datum/quirk/flimsy/add()
- quirk_holder.maxHealth *= 0.8
-
-/datum/quirk/flimsy/remove() //how do admins even remove traits?
- if(!quirk_holder)
- return
- quirk_holder.maxHealth *= 1.25
-
-/datum/quirk/hypersensitive
- name = "Hypersensitive"
- desc = "For better or worse, everything seems to affect your mood more than it should."
- value = -1
- gain_text = span_danger("You seem to make a big deal out of everything.")
- lose_text = span_notice("You don't seem to make a big deal out of everything anymore.")
- mood_quirk = TRUE //yogs
- medical_record_text = "Patient demonstrates a high level of emotional volatility."
-
-/datum/quirk/hypersensitive/add()
- var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
- if(mood)
- mood.mood_modifier += 0.5
-
-/datum/quirk/hypersensitive/remove()
- if(!quirk_holder)
- return
- var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
- if(mood)
- mood.mood_modifier -= 0.5
-
-// masked_mook moved to neutral
-
-//well-trained moved to neutral
-
-/datum/quirk/dumb4cum
- name = "Dumb For Cum"
- desc = "For one reason or another, you're totally obsessed with seminal fluids. The heat of it, the smell... the taste... It's quite simply euphoric."
- value = 0
- mob_trait = TRAIT_DUMB_CUM
- gain_text = span_notice("You feel an insatiable craving for seminal fluids.")
- lose_text = span_notice("Cum didn't even taste that good, anyways.")
- medical_record_text = "Patient seems to have an unhealthy psychological obsession with seminal fluids."
- mood_quirk = TRUE
- var/timer
- var/timer_trigger = 15 MINUTES
-
-/datum/quirk/dumb4cum/add()
- // Set timer
- timer = addtimer(CALLBACK(src, PROC_REF(crave)), timer_trigger, TIMER_STOPPABLE)
-
-/datum/quirk/dumb4cum/remove()
- // Remove status trait
- REMOVE_TRAIT(quirk_holder, TRAIT_DUMB_CUM_CRAVE, DUMB_CUM_TRAIT)
-
- // Remove penalty traits
- REMOVE_TRAIT(quirk_holder, TRAIT_ILLITERATE, DUMB_CUM_TRAIT)
- REMOVE_TRAIT(quirk_holder, TRAIT_DUMB, DUMB_CUM_TRAIT)
- REMOVE_TRAIT(quirk_holder, TRAIT_PACIFISM, DUMB_CUM_TRAIT)
-
- // Remove mood event
- SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, QMOOD_DUMB_CUM)
-
- // Remove timer
- deltimer(timer)
-
-/datum/quirk/dumb4cum/proc/crave()
- // Check if conscious
- if(quirk_holder.stat == CONSCIOUS)
- // Display emote
- quirk_holder.emote("sigh")
-
- // Define list of phrases
- var/list/trigger_phrases = list(
- "Your stomach rumbles a bit and cum comes to your mind.",\
- "Urgh, you should really get some cum...",\
- "Some jizz wouldn't be so bad right now!",\
- "You're starting to long for some more cum..."
- )
- // Alert user in chat
- to_chat(quirk_holder, span_love("[pick(trigger_phrases)]"))
-
- // Add active status trait
- ADD_TRAIT(quirk_holder, TRAIT_DUMB_CUM_CRAVE, DUMB_CUM_TRAIT)
-
- // Add illiterate, dumb, and pacifist
- ADD_TRAIT(quirk_holder, TRAIT_ILLITERATE, DUMB_CUM_TRAIT)
- ADD_TRAIT(quirk_holder, TRAIT_DUMB, DUMB_CUM_TRAIT)
- ADD_TRAIT(quirk_holder, TRAIT_PACIFISM, DUMB_CUM_TRAIT)
-
- // Add negative mood effect
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_DUMB_CUM, /datum/mood_event/cum_craving)
-
-/datum/quirk/dumb4cum/proc/uncrave()
- // Remove active status trait
- REMOVE_TRAIT(quirk_holder, TRAIT_DUMB_CUM_CRAVE, DUMB_CUM_TRAIT)
-
- // Remove penalty traits
- REMOVE_TRAIT(quirk_holder, TRAIT_ILLITERATE, DUMB_CUM_TRAIT)
- REMOVE_TRAIT(quirk_holder, TRAIT_DUMB, DUMB_CUM_TRAIT)
- REMOVE_TRAIT(quirk_holder, TRAIT_PACIFISM, DUMB_CUM_TRAIT)
-
- // Add positive mood event
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_DUMB_CUM, /datum/mood_event/cum_stuffed)
-
- // Remove timer
- deltimer(timer)
- timer = null
-
- // Add new timer
- timer = addtimer(CALLBACK(src, PROC_REF(crave)), timer_trigger, TIMER_STOPPABLE)
-
-// Small issue with this. If the quirk holder has NO_HUNGER or NO_THIRST, this trait can still be taken and they will still get the benefits of it.
-// It's unlikely that someone will be both, especially at round start, but vampirism makes me wary of having these separate.
-/datum/quirk/hungry
- name = "Hungry"
- desc = "You find yourself unusually hungry. Gotta eat twice as much as normal."
- value = -1
- gain_text = span_danger("You're starting to feel hungrier a lot faster.")
- lose_text = span_notice("Your elevated craving for food begins dying down.")
- medical_record_text = "Patient reports eating twice as many meals per day than usual for their species."
-
-/datum/quirk/hungry/add()
- var/mob/living/carbon/human/H = quirk_holder
- var/datum/physiology/P = H.physiology
- P.hunger_mod *= 2
-
-/datum/quirk/hungry/remove()
- var/mob/living/carbon/human/H = quirk_holder
- if(H)
- var/datum/physiology/P = H.physiology
- P.hunger_mod /= 2
-
-/datum/quirk/thirsty
- name = "Thirsty"
- desc = "You find yourself unusually thirsty. Gotta drink twice as much as normal."
- value = -1
- gain_text = span_danger("You're starting to feel thirstier a lot faster.")
- lose_text = span_notice("Your elevated craving for water begins dying down.")
- medical_record_text = "Patient reports drinking twice as many liquids per day than usual for their species."
-
-/datum/quirk/thirsty/add()
- var/mob/living/carbon/human/H = quirk_holder
- var/datum/physiology/P = H.physiology
- P.thirst_mod *= 2
-
-/datum/quirk/thirsty/remove()
- var/mob/living/carbon/human/H = quirk_holder
- if(H)
- var/datum/physiology/P = H.physiology
- P.thirst_mod /= 2
diff --git a/modular_splurt/code/datums/traits/negative_quirks/dumb_for_cum.dm b/modular_splurt/code/datums/traits/negative_quirks/dumb_for_cum.dm
new file mode 100644
index 000000000000..eb741c3dab4e
--- /dev/null
+++ b/modular_splurt/code/datums/traits/negative_quirks/dumb_for_cum.dm
@@ -0,0 +1,85 @@
+/datum/quirk/dumb4cum
+ name = "Dumb For Cum"
+ desc = "For one reason or another, you're totally obsessed with seminal fluids. The heat of it, the smell... the taste... It's quite simply euphoric."
+ value = 0
+ mob_trait = TRAIT_DUMB_CUM
+ gain_text = span_notice("You feel an insatiable craving for seminal fluids.")
+ lose_text = span_notice("Cum didn't even taste that good, anyways.")
+ medical_record_text = "Patient seems to have an unhealthy psychological obsession with seminal fluids."
+ mood_quirk = TRUE
+ var/timer
+ var/timer_trigger = 15 MINUTES
+
+/datum/quirk/dumb4cum/add()
+ // Set timer
+ timer = addtimer(CALLBACK(src, PROC_REF(crave)), timer_trigger, TIMER_STOPPABLE)
+
+/datum/quirk/dumb4cum/remove()
+ // Remove status trait
+ REMOVE_TRAIT(quirk_holder, TRAIT_DUMB_CUM_CRAVE, DUMB_CUM_TRAIT)
+
+ // Remove penalty traits
+ REMOVE_TRAIT(quirk_holder, TRAIT_ILLITERATE, DUMB_CUM_TRAIT)
+ REMOVE_TRAIT(quirk_holder, TRAIT_DUMB, DUMB_CUM_TRAIT)
+ REMOVE_TRAIT(quirk_holder, TRAIT_PACIFISM, DUMB_CUM_TRAIT)
+
+ // Remove mood event
+ SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, QMOOD_DUMB_CUM)
+
+ // Remove timer
+ deltimer(timer)
+
+/datum/quirk/dumb4cum/proc/crave()
+ // Check if conscious
+ if(quirk_holder.stat == CONSCIOUS)
+ // Display emote
+ quirk_holder.emote("sigh")
+
+ // Define list of phrases
+ var/list/trigger_phrases = list(
+ "Your stomach rumbles a bit and cum comes to your mind.",\
+ "Urgh, you should really get some cum...",\
+ "Some jizz wouldn't be so bad right now!",\
+ "You're starting to long for some more cum..."
+ )
+ // Alert user in chat
+ to_chat(quirk_holder, span_love("[pick(trigger_phrases)]"))
+
+ // Add active status trait
+ ADD_TRAIT(quirk_holder, TRAIT_DUMB_CUM_CRAVE, DUMB_CUM_TRAIT)
+
+ // Add illiterate, dumb, and pacifist
+ ADD_TRAIT(quirk_holder, TRAIT_ILLITERATE, DUMB_CUM_TRAIT)
+ ADD_TRAIT(quirk_holder, TRAIT_DUMB, DUMB_CUM_TRAIT)
+ ADD_TRAIT(quirk_holder, TRAIT_PACIFISM, DUMB_CUM_TRAIT)
+
+ // Add negative mood effect
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_DUMB_CUM, /datum/mood_event/cum_craving)
+
+/datum/quirk/dumb4cum/proc/uncrave()
+ // Remove active status trait
+ REMOVE_TRAIT(quirk_holder, TRAIT_DUMB_CUM_CRAVE, DUMB_CUM_TRAIT)
+
+ // Remove penalty traits
+ REMOVE_TRAIT(quirk_holder, TRAIT_ILLITERATE, DUMB_CUM_TRAIT)
+ REMOVE_TRAIT(quirk_holder, TRAIT_DUMB, DUMB_CUM_TRAIT)
+ REMOVE_TRAIT(quirk_holder, TRAIT_PACIFISM, DUMB_CUM_TRAIT)
+
+ // Add positive mood event
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_DUMB_CUM, /datum/mood_event/cum_stuffed)
+
+ // Remove timer
+ deltimer(timer)
+ timer = null
+
+ // Add new timer
+ timer = addtimer(CALLBACK(src, PROC_REF(crave)), timer_trigger, TIMER_STOPPABLE)
+
+/datum/mood_event/cum_craving
+ description = span_warning("I... NEED... CUM...\n")
+ mood_change = -20
+
+/datum/mood_event/cum_stuffed
+ description = span_nicegreen("The cum feels so good inside me!\n")
+ mood_change = 8
+ timeout = 5 MINUTES
diff --git a/modular_splurt/code/datums/traits/negative_quirks/flimsy.dm b/modular_splurt/code/datums/traits/negative_quirks/flimsy.dm
new file mode 100644
index 000000000000..dea8cb0fd442
--- /dev/null
+++ b/modular_splurt/code/datums/traits/negative_quirks/flimsy.dm
@@ -0,0 +1,15 @@
+/datum/quirk/flimsy
+ name = "Flimsy"
+ desc = "Your body is a little more fragile then most, decreasing total health by 20%."
+ value = -2
+ medical_record_text = "Patient has abnormally low capacity for injury."
+ gain_text = span_notice("You feel like you could break with a single hit.")
+ lose_text = span_notice("You feel more durable.")
+
+/datum/quirk/flimsy/add()
+ quirk_holder.maxHealth *= 0.8
+
+/datum/quirk/flimsy/remove() //how do admins even remove traits?
+ if(!quirk_holder)
+ return
+ quirk_holder.maxHealth *= 1.25
diff --git a/modular_splurt/code/datums/traits/negative_quirks/hungry.dm b/modular_splurt/code/datums/traits/negative_quirks/hungry.dm
new file mode 100644
index 000000000000..bfcd861abe21
--- /dev/null
+++ b/modular_splurt/code/datums/traits/negative_quirks/hungry.dm
@@ -0,0 +1,20 @@
+// Small issue with this. If the quirk holder has NO_HUNGER or NO_THIRST, this trait can still be taken and they will still get the benefits of it.
+// It's unlikely that someone will be both, especially at round start, but vampirism makes me wary of having these separate.
+/datum/quirk/hungry
+ name = "Hungry"
+ desc = "You find yourself unusually hungry. Gotta eat twice as much as normal."
+ value = -1
+ gain_text = span_danger("You're starting to feel hungrier a lot faster.")
+ lose_text = span_notice("Your elevated craving for food begins dying down.")
+ medical_record_text = "Patient reports eating twice as many meals per day than usual for their species."
+
+/datum/quirk/hungry/add()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/datum/physiology/P = H.physiology
+ P.hunger_mod *= 2
+
+/datum/quirk/hungry/remove()
+ var/mob/living/carbon/human/H = quirk_holder
+ if(H)
+ var/datum/physiology/P = H.physiology
+ P.hunger_mod /= 2
diff --git a/modular_splurt/code/datums/traits/negative_quirks/hypersensitive.dm b/modular_splurt/code/datums/traits/negative_quirks/hypersensitive.dm
new file mode 100644
index 000000000000..e9a292daa2ac
--- /dev/null
+++ b/modular_splurt/code/datums/traits/negative_quirks/hypersensitive.dm
@@ -0,0 +1,20 @@
+/datum/quirk/hypersensitive
+ name = "Hypersensitive"
+ desc = "For better or worse, everything seems to affect your mood more than it should."
+ value = -1
+ gain_text = span_danger("You seem to make a big deal out of everything.")
+ lose_text = span_notice("You don't seem to make a big deal out of everything anymore.")
+ mood_quirk = TRUE //yogs
+ medical_record_text = "Patient demonstrates a high level of emotional volatility."
+
+/datum/quirk/hypersensitive/add()
+ var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
+ if(mood)
+ mood.mood_modifier += 0.5
+
+/datum/quirk/hypersensitive/remove()
+ if(!quirk_holder)
+ return
+ var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
+ if(mood)
+ mood.mood_modifier -= 0.5
diff --git a/modular_splurt/code/datums/traits/negative_quirks/illiterate.dm b/modular_splurt/code/datums/traits/negative_quirks/illiterate.dm
new file mode 100644
index 000000000000..c33a410596ef
--- /dev/null
+++ b/modular_splurt/code/datums/traits/negative_quirks/illiterate.dm
@@ -0,0 +1,7 @@
+/datum/quirk/illiterate
+ name = "Illiterate"
+ desc = "You can't read nor write, plain and simple."
+ value = -1
+ mob_trait = TRAIT_ILLITERATE
+ gain_text = span_notice("The knowledge of how to read seems to escape from you.")
+ lose_text = span_notice("Written words suddenly make sense again.")
diff --git a/modular_splurt/code/datums/traits/negative_quirks/no_guns.dm b/modular_splurt/code/datums/traits/negative_quirks/no_guns.dm
new file mode 100644
index 000000000000..ec38885a158e
--- /dev/null
+++ b/modular_splurt/code/datums/traits/negative_quirks/no_guns.dm
@@ -0,0 +1,7 @@
+/datum/quirk/no_guns
+ name = "Fat-Fingered"
+ desc = "Due to the shape of your hands, width of your fingers or just not having fingers at all, you're unable to fire guns without accommodation."
+ value = -2
+ mob_trait = TRAIT_CHUNKYFINGERS
+ gain_text = span_notice("Your fingers feel... thick.")
+ lose_text = span_notice("Your fingers feel normal again.")
diff --git a/modular_splurt/code/datums/traits/negative_quirks/social_anxiety.dm b/modular_splurt/code/datums/traits/negative_quirks/social_anxiety.dm
new file mode 100644
index 000000000000..594842696e6f
--- /dev/null
+++ b/modular_splurt/code/datums/traits/negative_quirks/social_anxiety.dm
@@ -0,0 +1,65 @@
+/datum/quirk/social_anxiety
+ value = -3
+ mob_trait = TRAIT_ANXIOUS
+ processing_quirk = FALSE //small fixes for big mistakes
+
+/datum/quirk/social_anxiety/add()
+ . = ..()
+ RegisterSignal(quirk_holder, COMSIG_MOB_SAY, PROC_REF(handle_speech))
+
+/datum/quirk/social_anxiety/remove()
+ . = ..()
+ UnregisterSignal(quirk_holder, COMSIG_MOB_SAY)
+
+/datum/quirk/social_anxiety/proc/handle_speech(datum/source, list/speech_args)
+ SIGNAL_HANDLER
+
+ if(HAS_TRAIT(quirk_holder, TRAIT_FEARLESS))
+ return
+
+ var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
+ var/moodmod
+ if(mood)
+ moodmod = (1+0.02*(50-(max(50, mood.mood_level*(7-mood.sanity_level))))) //low sanity levels are better, they max at 6
+ else
+ moodmod = (1+0.02*(50-(max(50, 0.1*quirk_holder.nutrition))))
+ var/nearby_people = 0
+ for(var/mob/living/carbon/human/H in oview(3, quirk_holder))
+ if(H.client)
+ nearby_people++
+ var/message = speech_args[SPEECH_MESSAGE]
+ if(message)
+ var/list/message_split = splittext(message, " ")
+ var/list/new_message = list()
+ var/mob/living/carbon/human/quirker = quirk_holder
+ for(var/word in message_split)
+ if(prob(max(5,(nearby_people*12.5*moodmod))) && word != message_split[1]) //Minimum 1/20 chance of filler
+ new_message += pick("uh,","erm,","um,")
+ if(prob(min(5,(0.05*(nearby_people*12.5)*moodmod)))) //Max 1 in 20 chance of cutoff after a succesful filler roll, for 50% odds in a 15 word sentence
+ quirker.silent = max(3, quirker.silent)
+ to_chat(quirker, span_danger("You feel self-conscious and stop talking. You need a moment to recover!"))
+ break
+ if(prob(max(5,(nearby_people*12.5*moodmod)))) //Minimum 1/20 chance of stutter
+ // Add a short stutter, THEN treat our word
+ quirker.stuttering = max(0.25, quirker.stuttering)
+ new_message += quirker.treat_message(word)
+
+ else
+ new_message += word
+
+ message = jointext(new_message, " ")
+ var/mob/living/carbon/human/quirker = quirk_holder
+ if(prob(min(50,(0.50*(nearby_people*12.5)*moodmod)))) //Max 50% chance of not talking
+ if(dumb_thing)
+ to_chat(quirker, span_userdanger("You think of a dumb thing you said a long time ago and scream internally."))
+ dumb_thing = FALSE //only once per life
+ if(prob(1))
+ new/obj/item/reagent_containers/food/snacks/pastatomato(get_turf(quirker)) //now that's what I call spaghetti code
+ else
+ to_chat(quirk_holder, span_warning("You think that wouldn't add much to the conversation and decide not to say it."))
+ if(prob(min(25,(0.25*(nearby_people*12.75)*moodmod)))) //Max 25% chance of silence stacks after succesful not talking roll
+ to_chat(quirker, span_danger("You retreat into yourself. You really don't feel up to talking."))
+ quirker.silent = max(5, quirker.silent)
+ speech_args[SPEECH_MESSAGE] = pick("Uh.","Erm.","Um.")
+ else
+ speech_args[SPEECH_MESSAGE] = message
diff --git a/modular_splurt/code/datums/traits/negative_quirks/thirsty.dm b/modular_splurt/code/datums/traits/negative_quirks/thirsty.dm
new file mode 100644
index 000000000000..8eea83833c1d
--- /dev/null
+++ b/modular_splurt/code/datums/traits/negative_quirks/thirsty.dm
@@ -0,0 +1,18 @@
+/datum/quirk/thirsty
+ name = "Thirsty"
+ desc = "You find yourself unusually thirsty. Gotta drink twice as much as normal."
+ value = -1
+ gain_text = span_danger("You're starting to feel thirstier a lot faster.")
+ lose_text = span_notice("Your elevated craving for water begins dying down.")
+ medical_record_text = "Patient reports drinking twice as many liquids per day than usual for their species."
+
+/datum/quirk/thirsty/add()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/datum/physiology/P = H.physiology
+ P.thirst_mod *= 2
+
+/datum/quirk/thirsty/remove()
+ var/mob/living/carbon/human/H = quirk_holder
+ if(H)
+ var/datum/physiology/P = H.physiology
+ P.thirst_mod /= 2
diff --git a/modular_splurt/code/datums/traits/neutral.dm b/modular_splurt/code/datums/traits/neutral.dm
deleted file mode 100644
index fdef54a3be29..000000000000
--- a/modular_splurt/code/datums/traits/neutral.dm
+++ /dev/null
@@ -1,733 +0,0 @@
-// HYPERSTATION TRAITS
-
-/datum/quirk/choke_slut
- name = "Choke Slut"
- desc = "You are aroused by suffocation."
- value = 0
- mob_trait = TRAIT_CHOKE_SLUT
- gain_text = span_notice("You feel like you want to feel fingers around your neck, choking you until you pass out or make a mess... Maybe both.")
- lose_text = span_notice("Seems you don't have a kink for suffocation anymore.")
-
-/datum/quirk/pharmacokinesis //Supposed to prevent unwanted organ additions. But i don't think it's really working rn
- name = "Acute Hepatic Pharmacokinesis" //copypasting dumbo
- desc = "You have a genetic disorder that causes Incubus Draft and Succubus Milk to be absorbed by your liver instead."
- value = 0
- mob_trait = TRAIT_PHARMA
- lose_text = span_notice("Your liver feels... different, somehow.")
- var/active = FALSE
- var/power = 0
- var/cachedmoveCalc = 1
-
-/datum/quirk/steel_ass
- name = "Buns of Steel"
- desc = "You've never skipped ass day. You are completely immune to all forms of ass slapping and anyone who tries to slap your rock hard ass usually gets a broken hand."
- value = 0
- mob_trait = TRAIT_STEEL_ASS
- gain_text = span_notice("Your ass rivals those of golems.")
- lose_text = span_notice("Your butt feels more squishy and slappable.")
-
-/datum/quirk/cursed_blood
- name = "Cursed Blood"
- desc = "Your lineage is cursed with the paleblood curse. Best to stay away from holy water... Hell water, on the other hand..."
- value = 0
- mob_trait = TRAIT_CURSED_BLOOD
- gain_text = span_notice("A curse from a land where men return as beasts runs deep in your blood.")
- lose_text = span_notice("You feel the weight of the curse in your blood finally gone.")
- medical_record_text = "Patient suffers from an unknown type of aversion to holy reagents. Keep them away from a chaplain."
-
-/datum/quirk/headpat_hater
- name = "Distant"
- desc = "You don't seem to show much care for being touched. Whether it's because you're reserved or due to self control, others touching your head won't make you wag your tail should you possess one, and the action may even attract your ire."
- mob_trait = TRAIT_DISTANT
- value = 0
- gain_text = span_notice("Others' touches begin to make your blood boil...")
- lose_text = span_notice("Having your head pet doesn't sound so bad right about now...")
- medical_record_text = "Patient cares little with or dislikes being touched."
-
-/datum/quirk/headpat_hater/add()
-
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- var/datum/action/cooldown/toggle_distant/act_toggle = new
- act_toggle.Grant(quirk_mob)
-
-/datum/quirk/headpat_hater/remove()
-
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- var/datum/action/cooldown/toggle_distant/act_toggle = locate() in quirk_mob.actions
- if(act_toggle)
- act_toggle.Remove(quirk_mob)
-
-/datum/quirk/headpat_slut
- name = "Headpat Slut"
- desc = "You love the feeling of others touching your head! Maybe a little too much, actually... Others patting your head will provide a bigger mood boost and cause aroused reactions."
- mob_trait = TRAIT_HEADPAT_SLUT
- value = 0
- gain_text = span_notice("You crave headpats immensely!")
- lose_text = span_notice("Your headpats addiction wanes.")
- medical_record_text = "Patient seems overly affectionate."
-
-/datum/quirk/headpat_slut/add()
- . = ..()
- quirk_holder.AddElement(/datum/element/wuv/headpat, null, null, /datum/mood_event/pet_animal)
-
-/datum/quirk/headpat_slut/remove()
- . = ..()
- quirk_holder.RemoveElement(/datum/element/wuv/headpat)
-
-/datum/quirk/Hypnotic_gaze
- name = "Hypnotic Gaze"
- desc = "Be it through mysterious patterns, flickering colors, or some genetic oddity, prolonged eye contact with you will place the viewer into a highly-suggestible hypnotic trance."
- value = 0
- mob_trait = TRAIT_HYPNOTIC_GAZE
- gain_text = span_notice("Your eyes glimmer hypnotically...")
- lose_text = span_notice("Your eyes return to normal.")
- medical_record_text = "Prolonged exposure to Patient's eyes exhibits soporific effects."
-
-/datum/quirk/Hypnotic_gaze/add()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Add quirk ability action datum
- var/datum/action/cooldown/hypnotize/act_hypno = new
- act_hypno.Grant(quirk_mob)
-
- // Add examine text
- RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
-
-/datum/quirk/Hypnotic_gaze/remove()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Remove quirk ability action datum
- var/datum/action/cooldown/hypnotize/act_hypno = locate() in quirk_mob.actions
- act_hypno.Remove(quirk_mob)
-
- // Remove examine text
- UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
-
-// Quirk examine text
-/datum/quirk/Hypnotic_gaze/proc/on_examine_holder(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
- examine_list += "[quirk_holder.p_their(TRUE)] eyes glimmer with an entrancing power..."
-
-/datum/quirk/overweight
- name = "Overweight"
- desc = "You're particularly fond of food, and join the shift being overweight."
- value = 0
- gain_text = span_notice("You feel a bit chubby!")
- //no lose_text cause why would there be?
-
-/datum/quirk/overweight/on_spawn()
- var/mob/living/M = quirk_holder
- M.nutrition = rand(NUTRITION_LEVEL_FAT + NUTRITION_LEVEL_START_MIN, NUTRITION_LEVEL_FAT + NUTRITION_LEVEL_START_MAX)
- M.overeatduration = 100
- ADD_TRAIT(M, TRAIT_FAT, OBESITY)
-
-/datum/quirk/vegetarian
- name = "Vegetarian"
- desc = "You find the idea of eating meat morally and physically repulsive."
- value = 0
- gain_text = span_notice("You feel repulsion at the idea of eating meat.")
- lose_text = span_notice("You feel like eating meat isn't that bad.")
- medical_record_text = "Patient reports a vegetarian diet."
-
-/datum/quirk/vegetarian/add()
- var/mob/living/carbon/human/H = quirk_holder
- var/datum/species/species = H.dna.species
- species.liked_food &= ~MEAT
- species.disliked_food |= MEAT
-
-/datum/quirk/vegetarian/remove()
- var/mob/living/carbon/human/H = quirk_holder
- if(H)
- var/datum/species/species = H.dna.species
- if(initial(species.liked_food) & MEAT)
- species.liked_food |= MEAT
- if(initial(species.disliked_food) & ~MEAT)
- species.disliked_food &= ~MEAT
-
-//Zombies + Cumplus Fix\\
-
-/datum/quirk/undead
- name = "Undeath"
- desc = "Your body, be it anomalous, or just outright refusing to die - has indeed become undead. Due to this you may be hungrier."
- value = 0
- mob_trait = TRAIT_UNDEAD
- processing_quirk = TRUE
- // Note: The Undead cannot take advantage of healing viruses and genetic mutations, since they have no DNA.
- var/list/zperks = list(TRAIT_STABLEHEART,TRAIT_EASYDISMEMBER,TRAIT_VIRUSIMMUNE,TRAIT_RADIMMUNE,TRAIT_FAKEDEATH,TRAIT_NOSOFTCRIT, TRAIT_NOPULSE)
-
-/datum/quirk/undead/add()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- if(H.mob_biotypes == MOB_ROBOTIC)
- return FALSE //Lol, lmao, even
- H.mob_biotypes += MOB_UNDEAD
- for(var/A = 1, A <= zperks.len, A++)
- ADD_TRAIT(H,zperks[A],ROUNDSTART_TRAIT)
-
-/datum/quirk/undead/remove()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- H.mob_biotypes -= MOB_UNDEAD
- for(var/A = 1, A <= zperks.len, A++)
- REMOVE_TRAIT(H,zperks[A], null)
-
-/datum/quirk/undead/on_process()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- H.adjust_nutrition(-0.025)//The Undead are Hungry.
- H.set_screwyhud(SCREWYHUD_HEALTHY)
- H.adjustOxyLoss(-3) //Stops a defibrilator bug. Note to future self: Fix defib bug.
-
-/datum/quirk/cum_plus
- name = "Extra-Productive Genitals"
- desc = "Your genitals produce and hold more than normal."
- value = 0
- gain_text = span_notice("You feel pressure in your groin.")
- lose_text = span_notice("You feel a weight lifted from your groin.")
- medical_record_text = "Patient exhibits increased production of sexual fluids."
- var/increasedcum
-
-/datum/quirk/cum_plus/add()
- var/mob/living/carbon/M = quirk_holder
- if(M.getorganslot("testicles"))
- var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
- T.fluid_mult += 0.5 //Base is 1
- T.fluid_max_volume *= 1.75 //Fixes this.
-
-/datum/quirk/cum_plus/remove()
- var/mob/living/carbon/M = quirk_holder
- if(!M)
- return
- if(quirk_holder.getorganslot("testicles"))
- var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
- T.fluid_mult -= 0.5 //Base is 1
- T.fluid_max_volume *= 0.25 //Base is 50
-
-/datum/quirk/cum_plus/on_process()
- var/mob/living/carbon/M = quirk_holder //If you get balls later, then this will still proc
- if(M.getorganslot("testicles"))
- var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
- if(!increasedcum)
- T.fluid_mult = 1.5 //Base is 0.133
- T.fluid_max_volume *= 1.75
- increasedcum = TRUE
-
-//You are a CIA agent.
-/datum/quirk/cosglow
- name = "Cosmetic Glow"
- desc = "You glow! Be it an obscure radiation emission, or simple Bioluminescent properties.."
- value = 0
- mob_trait = TRAIT_COSGLOW
- gain_text = span_notice("You feel empowered by a three-letter agency!")
- lose_text = span_notice("You realize that working for the space CIA sucks!")
-
-/datum/quirk/cosglow/add()
- // Define quirk holder mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
- // Add glow control action
- var/datum/action/cosglow/update_glow/quirk_action = new
- quirk_action.Grant(quirk_mob)
-
-/datum/quirk/cosglow/remove()
- // Define quirk holder mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Remove glow control action
- var/datum/action/cosglow/update_glow/quirk_action = locate() in quirk_mob.actions
- quirk_action.Remove(quirk_mob)
-
- // Remove glow effect
- quirk_mob.remove_filter("rad_fiend_glow")
-
-//well-trained moved to neutral to stop the awkward situation of a dom snapping and the 30 trait powergamers fall to the floor.
-/datum/quirk/well_trained
- name = "Well-Trained"
- desc = "You absolutely love being dominated. The thought of someone stronger than you is enough to make you act up."
- value = 0
- gain_text = span_notice("You feel like being someone's pet...")
- lose_text = span_notice("You no longer feel like being a pet...")
- processing_quirk = TRUE
- var/notice_delay = 0
- var/mob/living/carbon/human/last_dom
-
-/datum/quirk/well_trained/add()
- . = ..()
- RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
-
-/datum/quirk/well_trained/remove()
- . = ..()
- UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
-
-/datum/quirk/well_trained/proc/on_examine_holder(atom/source, mob/living/user, list/examine_list)
- SIGNAL_HANDLER
-
- if(!istype(user))
- return
- if(!user.has_quirk(/datum/quirk/dominant_aura))
- return
- examine_list += span_lewd("You sense a strong aura of submission from [quirk_holder.p_them()].")
-
-/datum/quirk/well_trained/on_process()
- . = ..()
- if(!quirk_holder)
- return
-
- var/good_x = "pet"
- switch(quirk_holder.gender)
- if(MALE)
- good_x = "boy"
- if(FEMALE)
- good_x = "girl"
-
- //Check for possible doms with the dominant_aura quirk, and for the closest one if there is
- . = FALSE
- var/list/mob/living/carbon/human/doms = range(DOMINANT_DETECT_RANGE, quirk_holder)
- var/closest_distance
- for(var/mob/living/carbon/human/dom in doms)
- if(dom != quirk_holder && dom.has_quirk(/datum/quirk/dominant_aura))
- if(!closest_distance || get_dist(quirk_holder, dom) <= closest_distance)
- . = dom
- closest_distance = get_dist(quirk_holder, dom)
-
- //Return if no dom is found
- if(!.)
- last_dom = null
- return
-
- //Handle the mood
- var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
- if(istype(mood.mood_events[QMOOD_WELL_TRAINED], /datum/mood_event/dominant/good_boy))
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_WELL_TRAINED, /datum/mood_event/dominant/good_boy)
- else
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_WELL_TRAINED, /datum/mood_event/dominant/need)
-
- //Don't do anything if a previous dom was found
- if(last_dom)
- notice_delay = world.time + 15 SECONDS
- return
-
- last_dom = .
-
- if(notice_delay > world.time)
- return
-
- //Let them know they're near
- var/list/notices = list(
- "You feel someone's presence making you more submissive...",
- "Thoughts of being commanded flood you with lust...",
- "You really want to be called a good [good_x]...",
- "Someone's presence is making you all flustered...",
- "You start getting sweaty and excited..."
- )
-
- to_chat(quirk_holder, span_lewd(pick(notices)))
- notice_delay = world.time + 15 SECONDS
-
-
-//hydra code below
-
-/datum/quirk/hydra
- name = "Hydra Heads"
- desc = "You are a tri-headed creature. To use, format your name like (Rucks-Sucks-Ducks)."
- value = 0
- mob_trait = TRAIT_HYDRA_HEADS
- gain_text = span_notice("You hear two other voices inside of your head(s).")
- lose_text = span_danger("All of your minds become singular.")
- medical_record_text = "Patient has multiple heads and personalities affixed to their body."
-
-/datum/quirk/hydra/on_spawn()
- var/mob/living/carbon/human/hydra = quirk_holder
- var/datum/action/innate/hydra/spell = new
- var/datum/action/innate/hydrareset/resetspell = new
- spell.Grant(hydra)
- spell.owner = hydra
- resetspell.Grant(hydra)
- resetspell.owner = hydra
- hydra.name_archive = hydra.real_name
-
-//Own traits
-/datum/quirk/cum_sniff
- name = "Genital Taster"
- desc = "You've built so much experience savoring other people's genitals through your life that you can easily tell what liquids they're full of, besides reagents in their blood that is."
- value = 0
- mob_trait = TRAIT_GFLUID_DETECT
- gain_text = span_notice("You begin sensing peculiar smells from people's bits...")
- lose_text = span_notice("People's genitals start smelling all the same to you...")
- medical_record_text = "Patient attempted to get their doctor to drag his balls accross their face."
-
-/datum/quirk/fluid_infuser
- name = "Fluid Infuser"
- desc = "You just couldn't wait to get one of NanoTrasen's new fluid inducers when they first came out, so now you can hop in the station with editable titty milk!"
- value = 0
-
-/datum/quirk/fluid_infuser/on_spawn()
- . = ..()
- var/obj/item/implant/genital_fluid/put_in = new
- put_in.implant(quirk_holder, null, TRUE, TRUE)
-
-/datum/quirk/storage_concealment
- name = "Dorsualiphobic Augmentation"
- desc = "You despise the idea of being seen wearing any type of back-mounted storage apparatus! A new technology shields you from the immense shame you may experience, by hiding your equipped backpack."
-
- // UNUSED: Enable by setting these values to TRUE
- // The shame is unbearable
- mood_quirk = FALSE
- processing_quirk = FALSE
-
-/datum/quirk/storage_concealment/on_spawn()
- . = ..()
-
- // Create a new augment item
- var/obj/item/implant/hide_backpack/put_in = new
-
- // Apply the augment to the quirk holder
- put_in.implant(quirk_holder, null, TRUE, TRUE)
-
-/datum/quirk/storage_concealment/on_process()
- // This trait should only be applied by the augment
- // Check the quirk holder for the trait
- if(HAS_TRAIT(quirk_holder, TRAIT_HIDE_BACKPACK))
- // When found: Mood bonus
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_HIDE_BAG, /datum/mood_event/dorsualiphobic_mood_positive)
- else
- // When not found: Mood penalty
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_HIDE_BAG, /datum/mood_event/dorsualiphobic_mood_negative)
-
-//succubus and incubus below
-/datum/quirk/incubus
- name = "Incubus"
- desc = "Your seductor-like metabolism can only be sated by milk. (And semen, if you're a Succubus as well.)"
- value = 0
- mob_trait = TRAIT_INCUBUS
- processing_quirk = TRUE
-
-/datum/quirk/incubus/add()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- ADD_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
- ADD_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
-
-/datum/quirk/incubus/remove()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- REMOVE_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
- REMOVE_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
-
-/datum/quirk/incubus/on_process()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
-
-/datum/quirk/succubus
- name = "Succubus"
- desc = "Your seductress-like metabolism can only be sated by semen. (And milk, if you're an Incubus as well.)"
- value = 0
- mob_trait = TRAIT_SUCCUBUS
- processing_quirk = TRUE
-
-/datum/quirk/succubus/add()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- ADD_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
- ADD_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
-
-/datum/quirk/succubus/remove()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- REMOVE_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
- REMOVE_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
-
-/datum/quirk/succubus/on_process()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
-
-/datum/quirk/werewolf //adds the werewolf quirk
- name = "Werewolf"
- desc = "A beastly affliction allows you to shape-shift into a large anthropomorphic canine at will."
- value = 0
- mob_trait = TRAIT_WEREWOLF
- gain_text = span_notice("You feel the full moon beckon.")
- lose_text = span_notice("The moon's call hushes into silence.")
- medical_record_text = "Patient has been reported howling at the night sky."
- var/list/old_features
-
-/datum/quirk/werewolf/add()
- // Define old features
- old_features = list("species" = SPECIES_HUMAN, "legs" = "Plantigrade", "size" = 1, "bark")
-
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Record features
- old_features = quirk_mob.dna.features.Copy()
- old_features["species"] = quirk_mob.dna.species.type
- old_features["custom_species"] = quirk_mob.custom_species
- old_features["size"] = get_size(quirk_mob)
- old_features["bark"] = quirk_mob.vocal_bark_id
- old_features["taur"] = quirk_mob.dna.features["taur"]
- old_features["eye_type"] = quirk_mob.dna.species.eye_type
-
-/datum/quirk/werewolf/post_add()
- // Define quirk action
- var/datum/action/cooldown/werewolf/transform/quirk_action = new
-
- // Grant quirk action
- quirk_action.Grant(quirk_holder)
-
-/datum/quirk/werewolf/remove()
- // Define quirk action
- var/datum/action/cooldown/werewolf/transform/quirk_action = locate() in quirk_holder.actions
-
- // Revoke quirk action
- quirk_action.Remove(quirk_holder)
-
-/datum/quirk/gargoyle //Mmmm yes stone time
- name = "Gargoyle"
- desc = "You are some form of gargoyle! You can only leave your stone form for so long, and will have to return to it to regain energy. On the bright side, you heal in statue form!"
- value = 0
- processing_quirk = TRUE
- var/energy = 0
- var/transformed = 0
- var/cooldown = 0
- var/paused = 0
- var/turf/position
- var/obj/structure/statue/gargoyle/current = null
-
-/datum/quirk/gargoyle/add()
- .=..()
- var/mob/living/carbon/human/H = quirk_holder
- if (!H)
- return
- var/datum/action/gargoyle/transform/T = new
- var/datum/action/gargoyle/check/C = new
- var/datum/action/gargoyle/pause/P = new
- energy = 100
- cooldown = 30
- T.Grant(H)
- C.Grant(H)
- P.Grant(H)
-
-/datum/quirk/gargoyle/on_process()
- .=..()
- var/mob/living/carbon/human/H = quirk_holder
-
- if (!H)
- return
-
- if(paused && H.loc != position)
- paused = 0
- energy -= 20
-
- if(cooldown > 0)
- cooldown--
-
- if(!transformed && !paused && energy > 0)
- energy -= 0.05
-
- if(transformed)
- energy = min(energy + 0.3, 100)
- if (H.getBruteLoss() > 0 || H.getFireLoss() > 0)
- H.adjustBruteLoss(-0.5, forced = TRUE)
- H.adjustFireLoss(-0.5, forced = TRUE)
- else if (H.getOxyLoss() > 0 || H.getToxLoss() > 0)
- H.adjustToxLoss(-0.3, forced = TRUE)
- H.adjustOxyLoss(-0.5, forced = TRUE) //oxyloss heals by itself, doesn't need a nerfed heal
- else if (H.getCloneLoss() > 0)
- H.adjustCloneLoss(-0.3, forced = TRUE)
- else if (current && current.obj_integrity < current.max_integrity) //health == maxHealth is true since we checked all damages above
- current.obj_integrity = min(current.obj_integrity + 0.1, current.max_integrity)
-
- if(!transformed && energy <= 0)
- var/datum/action/gargoyle/transform/T = locate() in H.actions
- if (!T)
- T = new
- T.Grant(H)
- cooldown = 0
- T?.Trigger()
-
-/datum/quirk/gargoyle/remove()
- var/mob/living/carbon/human/H = quirk_holder
- if (!H)
- return ..()
- var/datum/action/gargoyle/transform/T = locate() in H.actions
- var/datum/action/gargoyle/check/C = locate() in H.actions
- var/datum/action/gargoyle/pause/P = locate() in H.actions
- T?.Remove(H)
- C?.Remove(H)
- P?.Remove(H)
- . = ..()
-
-/datum/quirk/nudist
- name = "Nudist"
- desc = "Wearing most types of clothing unnerves you. Bring a gear harness!"
- gain_text = span_notice("You feel spiritually connected to your natural form.")
- lose_text = span_notice("It feels like clothing could fit you comfortably.")
- medical_record_text = "Patient expresses a psychological need to remain unclothed."
- value = 0
- mood_quirk = TRUE
- var/is_nude
-
-/datum/quirk/nudist/add()
- // Register signal handlers
- RegisterSignal(quirk_holder, COMSIG_MOB_UPDATE_GENITALS, PROC_REF(check_outfit))
- RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(quirk_examine_nudist))
-
-/datum/quirk/nudist/remove()
- // Remove mood event
- SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, QMOOD_NUDIST)
-
- // Unregister signals
- UnregisterSignal(quirk_holder, list(COMSIG_MOB_UPDATE_GENITALS, COMSIG_PARENT_EXAMINE))
-
-/datum/quirk/nudist/post_add()
- // Evaluate outfit
- check_outfit()
-
-/datum/quirk/nudist/on_spawn()
- // Spawn a Rapid Disrobe Implant
- var/obj/item/implant/disrobe/quirk_implant = new
-
- // Implant into quirk holder
- quirk_implant.implant(quirk_holder, null, TRUE, TRUE)
-
-/datum/quirk/nudist/proc/check_outfit()
- SIGNAL_HANDLER
-
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Check if torso is uncovered
- if(quirk_mob.is_chest_exposed() && quirk_mob.is_groin_exposed())
- // Send positive mood event
- SEND_SIGNAL(quirk_mob, COMSIG_ADD_MOOD_EVENT, QMOOD_NUDIST, /datum/mood_event/nudist_positive)
-
- // Check if already set
- if(is_nude)
- return
-
- // Alert user in chat
- to_chat(quirk_mob, span_nicegreen("You begin to feel better without the restraint of clothing!"))
-
- // Set nude status
- is_nude = TRUE
-
- // Torso is covered
- else
- // Send negative mood event
- SEND_SIGNAL(quirk_mob, COMSIG_ADD_MOOD_EVENT, QMOOD_NUDIST, /datum/mood_event/nudist_negative)
-
- // Check if already set
- if(!is_nude)
- return
-
- // Alert user in chat
- to_chat(quirk_mob, span_warning("The clothes feel wrong on your body..."))
-
- // Set nude status
- is_nude = FALSE
-
-/datum/quirk/nudist/proc/quirk_examine_nudist(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
- SIGNAL_HANDLER
-
- // Define default status term
- var/mood_term = "content with [quirk_holder.p_their()] lack of"
-
- // Define default span class
- var/span_class
-
- // Check if dressed
- if(!is_nude)
- // Set negative term
- mood_term = "disturbed by wearing"
-
- // Set negative span class
- span_class = "warning"
-
- // Add examine text
- examine_list += "[quirk_holder.p_they(TRUE)] appear[quirk_holder.p_s()] [mood_term] clothing."
-
-/datum/quirk/masked_mook
- name = "Bane Syndrome"
- desc = "For some reason you don't feel... right without wearing some kind of gas mask."
- gain_text = span_danger("You start feeling unwell without any gas mask on.")
- lose_text = span_notice("You no longer have a need to wear some gas mask.")
- value = 0
- mood_quirk = TRUE
- medical_record_text = "Patient feels more secure when wearing a gas mask."
- processing_quirk = TRUE
-
-/datum/quirk/masked_mook/on_process()
- var/mob/living/carbon/human/H = quirk_holder
- var/obj/item/clothing/mask/gas/gasmask = H.get_item_by_slot(ITEM_SLOT_MASK)
- if(istype(gasmask))
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_MASKED_MOOK, /datum/mood_event/masked_mook)
- else
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_MASKED_MOOK, /datum/mood_event/masked_mook_incomplete)
-
-/datum/quirk/masked_mook/on_spawn()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- var/obj/item/clothing/mask/gas/cosmetic/gasmask = new(get_turf(quirk_holder)) // Uses a custom gas mask
- H.equip_to_slot_if_possible(gasmask, ITEM_SLOT_MASK) // If character have a loadout mask, the custom one will not overwrite it but instead will be dropped on floor
- H.regenerate_icons()
-
-/datum/quirk/body_morpher
- name = "Body Morpher"
- desc = "Somehow you developed an ability allowing your body to morph and shift itself to modify bodyparts, much like a slimeperson can."
- value = 0
- mob_trait = TRAIT_BODY_MORPHER
- gain_text = span_notice("Your body feels more malleable...")
- lose_text = span_notice("Your body is more firm.")
- medical_record_text = "Patient's body seems unusually malleable."
- var/datum/action/innate/ability/humanoid_customization/alter_form_action
-
-/datum/quirk/body_morpher/add()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Add quirk ability action datum
- alter_form_action = new
- alter_form_action.Grant(quirk_mob)
-
-/datum/quirk/body_morpher/remove()
- // Define quirk mob
- var/mob/living/carbon/human/quirk_mob = quirk_holder
-
- // Remove quirk ability action datum
- alter_form_action.Remove(quirk_mob)
- QDEL_NULL(alter_form_action)
-
-/datum/quirk/modular
- name = "Modular Limbs"
- desc = "Your limbs are able to be attached and detached easily... Unfortunately, everyone around you can alter your limbs too! Right click yourself to use this quirk."
- value = 0
-
-/datum/quirk/modular/add()
- var/mob/living/carbon/human/C = quirk_holder
- add_verb(C,/mob/living/proc/alterlimbs)
-
-/datum/quirk/modular/remove()
- var/mob/living/carbon/human/C = quirk_holder
- remove_verb(C,/mob/living/proc/alterlimbs)
-
-/datum/quirk/messy
- name = "Messy"
- desc = "Due to your unique biology/construction/bluespace magic you always manage to make a mess when you cum, even if it's not possible in normal circumstances."
- value = 0
- mob_trait = TRAIT_MESSY
- gain_text = span_lewd("You feel like covering something in layer of your fluids.")
- lose_text = span_notice("You don't feel 'messy' anymore.")
- medical_record_text = "Had to be sedated after covering entire hospital wing with cum."
-
-/datum/quirk/kiss_slut
- name = "Kiss Slut"
- desc = "The sheer thought of kissing someone makes you blush and overheat, effectively increasing your arousal with each smooch."
- value = 0
- mob_trait = TRAIT_KISS_SLUT
- gain_text = span_lewd("You feel like kissing someone...")
- lose_text = span_notice("You don't feel like kissing someone anymore...")
- medical_record_text = "Patient seems to demonstrate an extraordinary liking in kissing."
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/body_morpher.dm b/modular_splurt/code/datums/traits/neutral_quirks/body_morpher.dm
new file mode 100644
index 000000000000..9ec17871d79c
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/body_morpher.dm
@@ -0,0 +1,25 @@
+/datum/quirk/body_morpher
+ name = "Body Morpher"
+ desc = "Somehow you developed an ability allowing your body to morph and shift itself to modify bodyparts, much like a slimeperson can."
+ value = 0
+ mob_trait = TRAIT_BODY_MORPHER
+ gain_text = span_notice("Your body feels more malleable...")
+ lose_text = span_notice("Your body is more firm.")
+ medical_record_text = "Patient's body seems unusually malleable."
+ var/datum/action/innate/ability/humanoid_customization/alter_form_action
+
+/datum/quirk/body_morpher/add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Add quirk ability action datum
+ alter_form_action = new
+ alter_form_action.Grant(quirk_mob)
+
+/datum/quirk/body_morpher/remove()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove quirk ability action datum
+ alter_form_action.Remove(quirk_mob)
+ QDEL_NULL(alter_form_action)
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/choke_slut.dm b/modular_splurt/code/datums/traits/neutral_quirks/choke_slut.dm
new file mode 100644
index 000000000000..cdd25f24f0ba
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/choke_slut.dm
@@ -0,0 +1,7 @@
+/datum/quirk/choke_slut
+ name = "Choke Slut"
+ desc = "You are aroused by suffocation."
+ value = 0
+ mob_trait = TRAIT_CHOKE_SLUT
+ gain_text = span_notice("You feel like you want to feel fingers around your neck, choking you until you pass out or make a mess... Maybe both.")
+ lose_text = span_notice("Seems you don't have a kink for suffocation anymore.")
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/cosglow.dm b/modular_splurt/code/datums/traits/neutral_quirks/cosglow.dm
new file mode 100644
index 000000000000..6921d41ddc27
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/cosglow.dm
@@ -0,0 +1,108 @@
+//You are a CIA agent.
+/datum/quirk/cosglow
+ name = "Cosmetic Glow"
+ desc = "You glow! Be it an obscure radiation emission, or simple Bioluminescent properties.."
+ value = 0
+ mob_trait = TRAIT_COSGLOW
+ gain_text = span_notice("You feel empowered by a three-letter agency!")
+ lose_text = span_notice("You realize that working for the space CIA sucks!")
+
+/datum/quirk/cosglow/add()
+ // Define quirk holder mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+ // Add glow control action
+ var/datum/action/cosglow/update_glow/quirk_action = new
+ quirk_action.Grant(quirk_mob)
+
+/datum/quirk/cosglow/remove()
+ // Define quirk holder mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove glow control action
+ var/datum/action/cosglow/update_glow/quirk_action = locate() in quirk_mob.actions
+ quirk_action.Remove(quirk_mob)
+
+ // Remove glow effect
+ quirk_mob.remove_filter("rad_fiend_glow")
+
+//Quirk: Cosmetic Glow
+//Copy and pasted. Cry about it.
+/datum/action/cosglow
+ name = "Broken Glow Action"
+ desc = "Report this to a coder."
+ icon_icon = 'icons/effects/effects.dmi'
+ button_icon_state = "static"
+
+/datum/action/cosglow/update_glow
+ name = "Modify Glow"
+ desc = "Change your glow color."
+ button_icon_state = "blank"
+
+ // Glow color to use
+ var/glow_color
+
+ // Thickness of glow outline
+ var/glow_range
+
+ // Alpha of the glow outline
+ var/glow_intensity
+
+/datum/action/cosglow/update_glow/Grant()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Default glow intensity to 48 (in decimal)
+ glow_intensity = "30"
+
+ // Add outline effect
+ if(glow_color && glow_range)
+ action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color + glow_intensity, "size" = glow_range))
+
+/datum/action/cosglow/update_glow/Remove()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Remove glow
+ action_mob.remove_filter("rad_fiend_glow")
+
+/datum/action/cosglow/update_glow/Trigger()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Ask user for color input
+ var/input_color = input(action_mob, "Select a color to use for your glow outline.", "Select Glow Color", glow_color) as color|null
+
+ // Check if color input was given
+ // Reset to stored color when not given input
+ glow_color = (input_color ? input_color : glow_color)
+
+ // Ask user for range input
+ var/input_range = input(action_mob, "How much do you glow? Value may range between 0 to 4. 0 disables glow.", "Select Glow Range", glow_range) as num|null
+
+ // Check if range input was given
+ // Disable glow if input is 0.
+ // Reset to stored range when input is null.
+ // Input is clamped in the 0-4 range
+ glow_range = isnull(input_range) ? glow_range : clamp(input_range, 0, 4) //More customisable, so you know when you're looking at someone with Radfiend (doom) or a normal player.
+
+ // Ask user for intensity input
+ var/input_intensity = input(action_mob, "How intense is your glow? Value may range between 0 to 255. 0 disables glow.", "Select Glow Intensity", hex2num(glow_intensity)) as num|null
+
+ // Check if intensity input was given and clamp it
+ // If no input is given, reset to stored intensity
+ var/intensity_clamped = isnull(input_intensity) ? hex2num(glow_intensity) : clamp(input_intensity, 0, 255)
+
+ // Update glow intensity
+ glow_intensity = num2hex(intensity_clamped)
+
+ // Update outline effect
+ if(glow_range && glow_color)
+ action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color + glow_intensity, "size" = glow_range))
+ else
+ action_mob.remove_filter("rad_fiend_glow")
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/cum_plus.dm b/modular_splurt/code/datums/traits/neutral_quirks/cum_plus.dm
new file mode 100644
index 000000000000..d6dd75556c71
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/cum_plus.dm
@@ -0,0 +1,33 @@
+/datum/quirk/cum_plus
+ name = "Extra-Productive Genitals"
+ desc = "Your genitals produce and hold more than normal."
+ value = 0
+ gain_text = span_notice("You feel pressure in your groin.")
+ lose_text = span_notice("You feel a weight lifted from your groin.")
+ medical_record_text = "Patient exhibits increased production of sexual fluids."
+ var/increasedcum
+
+/datum/quirk/cum_plus/add()
+ var/mob/living/carbon/M = quirk_holder
+ if(M.getorganslot("testicles"))
+ var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
+ T.fluid_mult += 0.5 //Base is 1
+ T.fluid_max_volume *= 1.75 //Fixes this.
+
+/datum/quirk/cum_plus/remove()
+ var/mob/living/carbon/M = quirk_holder
+ if(!M)
+ return
+ if(quirk_holder.getorganslot("testicles"))
+ var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
+ T.fluid_mult -= 0.5 //Base is 1
+ T.fluid_max_volume *= 0.25 //Base is 50
+
+/datum/quirk/cum_plus/on_process()
+ var/mob/living/carbon/M = quirk_holder //If you get balls later, then this will still proc
+ if(M.getorganslot("testicles"))
+ var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
+ if(!increasedcum)
+ T.fluid_mult = 1.5 //Base is 0.133
+ T.fluid_max_volume *= 1.75
+ increasedcum = TRUE
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/cum_sniff.dm b/modular_splurt/code/datums/traits/neutral_quirks/cum_sniff.dm
new file mode 100644
index 000000000000..de53aabc9a76
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/cum_sniff.dm
@@ -0,0 +1,8 @@
+/datum/quirk/cum_sniff
+ name = "Genital Taster"
+ desc = "You've built so much experience savoring other people's genitals through your life that you can easily tell what liquids they're full of, besides reagents in their blood that is."
+ value = 0
+ mob_trait = TRAIT_GFLUID_DETECT
+ gain_text = span_notice("You begin sensing peculiar smells from people's bits...")
+ lose_text = span_notice("People's genitals start smelling all the same to you...")
+ medical_record_text = "Patient attempted to get their doctor to drag his balls accross their face."
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/cursed_blood.dm b/modular_splurt/code/datums/traits/neutral_quirks/cursed_blood.dm
new file mode 100644
index 000000000000..b2a46ecc884c
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/cursed_blood.dm
@@ -0,0 +1,8 @@
+/datum/quirk/cursed_blood
+ name = "Cursed Blood"
+ desc = "Your lineage is cursed with the paleblood curse. Best to stay away from holy water... Hell water, on the other hand..."
+ value = 0
+ mob_trait = TRAIT_CURSED_BLOOD
+ gain_text = span_notice("A curse from a land where men return as beasts runs deep in your blood.")
+ lose_text = span_notice("You feel the weight of the curse in your blood finally gone.")
+ medical_record_text = "Patient suffers from an unknown type of aversion to holy reagents. Keep them away from a chaplain."
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/fluid_infuser.dm b/modular_splurt/code/datums/traits/neutral_quirks/fluid_infuser.dm
new file mode 100644
index 000000000000..f430fffb69d1
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/fluid_infuser.dm
@@ -0,0 +1,9 @@
+/datum/quirk/fluid_infuser
+ name = "Fluid Infuser"
+ desc = "You just couldn't wait to get one of NanoTrasen's new fluid inducers when they first came out, so now you can hop in the station with editable titty milk!"
+ value = 0
+
+/datum/quirk/fluid_infuser/on_spawn()
+ . = ..()
+ var/obj/item/implant/genital_fluid/put_in = new
+ put_in.implant(quirk_holder, null, TRUE, TRUE)
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/gargoyle.dm b/modular_splurt/code/datums/traits/neutral_quirks/gargoyle.dm
new file mode 100644
index 000000000000..d5fceda5b3b3
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/gargoyle.dm
@@ -0,0 +1,148 @@
+/datum/quirk/gargoyle //Mmmm yes stone time
+ name = "Gargoyle"
+ desc = "You are some form of gargoyle! You can only leave your stone form for so long, and will have to return to it to regain energy. On the bright side, you heal in statue form!"
+ value = 0
+ processing_quirk = TRUE
+ var/energy = 0
+ var/transformed = 0
+ var/cooldown = 0
+ var/paused = 0
+ var/turf/position
+ var/obj/structure/statue/gargoyle/current = null
+
+/datum/quirk/gargoyle/add()
+ .=..()
+ var/mob/living/carbon/human/H = quirk_holder
+ if (!H)
+ return
+ var/datum/action/gargoyle/transform/T = new
+ var/datum/action/gargoyle/check/C = new
+ var/datum/action/gargoyle/pause/P = new
+ energy = 100
+ cooldown = 30
+ T.Grant(H)
+ C.Grant(H)
+ P.Grant(H)
+
+/datum/quirk/gargoyle/on_process()
+ .=..()
+ var/mob/living/carbon/human/H = quirk_holder
+
+ if (!H)
+ return
+
+ if(paused && H.loc != position)
+ paused = 0
+ energy -= 20
+
+ if(cooldown > 0)
+ cooldown--
+
+ if(!transformed && !paused && energy > 0)
+ energy -= 0.05
+
+ if(transformed)
+ energy = min(energy + 0.3, 100)
+ if (H.getBruteLoss() > 0 || H.getFireLoss() > 0)
+ H.adjustBruteLoss(-0.5, forced = TRUE)
+ H.adjustFireLoss(-0.5, forced = TRUE)
+ else if (H.getOxyLoss() > 0 || H.getToxLoss() > 0)
+ H.adjustToxLoss(-0.3, forced = TRUE)
+ H.adjustOxyLoss(-0.5, forced = TRUE) //oxyloss heals by itself, doesn't need a nerfed heal
+ else if (H.getCloneLoss() > 0)
+ H.adjustCloneLoss(-0.3, forced = TRUE)
+ else if (current && current.obj_integrity < current.max_integrity) //health == maxHealth is true since we checked all damages above
+ current.obj_integrity = min(current.obj_integrity + 0.1, current.max_integrity)
+
+ if(!transformed && energy <= 0)
+ var/datum/action/gargoyle/transform/T = locate() in H.actions
+ if (!T)
+ T = new
+ T.Grant(H)
+ cooldown = 0
+ T?.Trigger()
+
+/datum/quirk/gargoyle/remove()
+ var/mob/living/carbon/human/H = quirk_holder
+ if (!H)
+ return ..()
+ var/datum/action/gargoyle/transform/T = locate() in H.actions
+ var/datum/action/gargoyle/check/C = locate() in H.actions
+ var/datum/action/gargoyle/pause/P = locate() in H.actions
+ T?.Remove(H)
+ C?.Remove(H)
+ P?.Remove(H)
+ . = ..()
+
+//
+// Quirk actions: Gargoyle
+//
+
+/datum/action/gargoyle/transform
+ name = "Transform"
+ desc = "Transform into a statue, regaining energy in the process. Additionally, you will slowly heal while in statue form."
+ icon_icon = 'icons/mob/actions/actions_changeling.dmi'
+ button_icon_state = "ling_camouflage"
+ var/obj/structure/statue/gargoyle/current = null
+
+
+/datum/action/gargoyle/transform/Trigger()
+ .=..()
+ var/mob/living/carbon/human/H = owner
+ var/datum/quirk/gargoyle/T = locate() in H.roundstart_quirks
+ if(!T.cooldown)
+ if(!T.transformed)
+ if(!isturf(H.loc))
+ return 0
+ var/obj/structure/statue/gargoyle/S = new(H.loc, H)
+ S.name = "statue of [H.name]"
+ H.bleedsuppress = 1
+ S.copy_overlays(H)
+ var/newcolor = list(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
+ S.add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY)
+ current = S
+ T.transformed = 1
+ T.cooldown = 30
+ T.paused = 0
+ S.dir = H.dir
+ return 1
+ else
+ qdel(current)
+ T.transformed = 0
+ T.cooldown = 30
+ T.paused = 0
+ H.visible_message(span_warning("[H]'s skin rapidly softens, returning them to normal!"), span_userdanger("Your skin softens, freeing your movement once more!"))
+ else
+ to_chat(H, span_warning("You have transformed too recently; you cannot yet transform again!"))
+ return 0
+
+/datum/action/gargoyle/check
+ name = "Check"
+ desc = "Check your current energy levels."
+ icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
+ button_icon_state = "Linked Vanguard"
+
+/datum/action/gargoyle/check/Trigger()
+ .=..()
+ var/mob/living/carbon/human/H = owner
+ var/datum/quirk/gargoyle/T = locate() in H.roundstart_quirks
+ to_chat(H, span_warning("You have [T.energy]/100 energy remaining!"))
+
+/datum/action/gargoyle/pause
+ name = "Preserve"
+ desc = "Become near-motionless, thusly conserving your energy until you move from your current tile. Note, you will lose a chunk of energy when you inevitably move from your current position, so you cannot abuse this!"
+ icon_icon = 'icons/mob/actions/actions_flightsuit.dmi'
+ button_icon_state = "flightsuit_lock"
+
+/datum/action/gargoyle/pause/Trigger()
+ .=..()
+ var/mob/living/carbon/human/H = owner
+ var/datum/quirk/gargoyle/T = locate() in H.roundstart_quirks
+
+ if(!T.paused)
+ T.paused = 1
+ T.position = H.loc
+ to_chat(H, span_warning("You are now conserving your energy; this effect will end the moment you move from your current position!"))
+ return
+ else
+ to_chat(H, span_warning("You are already conserving your energy!"))
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/headpat_hater.dm b/modular_splurt/code/datums/traits/neutral_quirks/headpat_hater.dm
new file mode 100644
index 000000000000..884f67f565b1
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/headpat_hater.dm
@@ -0,0 +1,39 @@
+/datum/quirk/headpat_hater
+ name = "Distant"
+ desc = "You don't seem to show much care for being touched. Whether it's because you're reserved or due to self control, others touching your head won't make you wag your tail should you possess one, and the action may even attract your ire."
+ mob_trait = TRAIT_DISTANT
+ value = 0
+ gain_text = span_notice("Others' touches begin to make your blood boil...")
+ lose_text = span_notice("Having your head pet doesn't sound so bad right about now...")
+ medical_record_text = "Patient cares little with or dislikes being touched."
+
+/datum/quirk/headpat_hater/add()
+
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ var/datum/action/cooldown/toggle_distant/act_toggle = new
+ act_toggle.Grant(quirk_mob)
+
+/datum/quirk/headpat_hater/remove()
+
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ var/datum/action/cooldown/toggle_distant/act_toggle = locate() in quirk_mob.actions
+ if(act_toggle)
+ act_toggle.Remove(quirk_mob)
+
+/datum/action/cooldown/toggle_distant
+ name = "Toggle Distant"
+ desc = "Allows you to let your headpat-guard down, or put it back up."
+ icon_icon = 'modular_splurt/icons/mob/actions/lewd_actions/lewd_icons.dmi'
+ button_icon_state = "pain_max"
+
+/datum/action/cooldown/toggle_distant/Activate()
+ var/mob/living/carbon/human/action_owner = owner
+
+ if(HAS_TRAIT(action_owner, TRAIT_DISTANT))
+ REMOVE_TRAIT(action_owner, TRAIT_DISTANT, ROUNDSTART_TRAIT)
+ to_chat(action_owner, span_notice("You let your headpat-guard down!"))
+ else
+ ADD_TRAIT(action_owner, TRAIT_DISTANT, ROUNDSTART_TRAIT)
+ to_chat(action_owner, span_warning("You let your headpat-guard up!"))
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/headpat_slut.dm b/modular_splurt/code/datums/traits/neutral_quirks/headpat_slut.dm
new file mode 100644
index 000000000000..eebddcb75268
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/headpat_slut.dm
@@ -0,0 +1,16 @@
+/datum/quirk/headpat_slut
+ name = "Headpat Slut"
+ desc = "You love the feeling of others touching your head! Maybe a little too much, actually... Others patting your head will provide a bigger mood boost and cause aroused reactions."
+ mob_trait = TRAIT_HEADPAT_SLUT
+ value = 0
+ gain_text = span_notice("You crave headpats immensely!")
+ lose_text = span_notice("Your headpats addiction wanes.")
+ medical_record_text = "Patient seems overly affectionate."
+
+/datum/quirk/headpat_slut/add()
+ . = ..()
+ quirk_holder.AddElement(/datum/element/wuv/headpat, null, null, /datum/mood_event/pet_animal)
+
+/datum/quirk/headpat_slut/remove()
+ . = ..()
+ quirk_holder.RemoveElement(/datum/element/wuv/headpat)
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/hydra.dm b/modular_splurt/code/datums/traits/neutral_quirks/hydra.dm
new file mode 100644
index 000000000000..0a885dc56edb
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/hydra.dm
@@ -0,0 +1,50 @@
+//hydra code below
+/datum/quirk/hydra
+ name = "Hydra Heads"
+ desc = "You are a tri-headed creature. To use, format your name like (Rucks-Sucks-Ducks)."
+ value = 0
+ mob_trait = TRAIT_HYDRA_HEADS
+ gain_text = span_notice("You hear two other voices inside of your head(s).")
+ lose_text = span_danger("All of your minds become singular.")
+ medical_record_text = "Patient has multiple heads and personalities affixed to their body."
+
+/datum/quirk/hydra/on_spawn()
+ var/mob/living/carbon/human/hydra = quirk_holder
+ var/datum/action/innate/hydra/spell = new
+ var/datum/action/innate/hydrareset/resetspell = new
+ spell.Grant(hydra)
+ spell.owner = hydra
+ resetspell.Grant(hydra)
+ resetspell.owner = hydra
+ hydra.name_archive = hydra.real_name
+
+//
+// Quirk actions: Hydra Heads
+//
+
+/datum/action/innate/hydra
+ name = "Switch head"
+ desc = "Switch between each of the heads on your body."
+ icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
+ button_icon_state = "art_summon"
+
+/datum/action/innate/hydrareset
+ name = "Reset speech"
+ desc = "Go back to speaking as a whole."
+ icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
+ button_icon_state = "art_summon"
+
+/datum/action/innate/hydrareset/Activate()
+ var/mob/living/carbon/human/hydra = owner
+ hydra.real_name = hydra.name_archive
+ hydra.visible_message(span_notice("[hydra.name] pushes all three heads forwards; they seem to be talking as a collective."), \
+ span_notice("You are now talking as [hydra.name_archive]!"), ignored_mobs=owner)
+
+/datum/action/innate/hydra/Activate() //I hate this but its needed
+ var/mob/living/carbon/human/hydra = owner
+ var/list/names = splittext(hydra.name_archive,"-")
+ var/selhead = input("Who would you like to speak as?","Heads:") in names
+ hydra.real_name = selhead
+ hydra.visible_message(span_notice("[hydra.name] pulls the rest of their heads back; and puts [selhead]'s forward."), \
+ span_notice("You are now talking as [selhead]!"), ignored_mobs=owner)
+
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/hypnotic_gaze.dm b/modular_splurt/code/datums/traits/neutral_quirks/hypnotic_gaze.dm
new file mode 100644
index 000000000000..dbdc580546b3
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/hypnotic_gaze.dm
@@ -0,0 +1,382 @@
+#define HYPNOEYES_COOLDOWN_NORMAL 3 SECONDS
+#define HYPNOEYES_COOLDOWN_BRAINWASH 30 SECONDS
+
+/datum/quirk/Hypnotic_gaze
+ name = "Hypnotic Gaze"
+ desc = "Be it through mysterious patterns, flickering colors, or some genetic oddity, prolonged eye contact with you will place the viewer into a highly-suggestible hypnotic trance."
+ value = 0
+ mob_trait = TRAIT_HYPNOTIC_GAZE
+ gain_text = span_notice("Your eyes glimmer hypnotically...")
+ lose_text = span_notice("Your eyes return to normal.")
+ medical_record_text = "Prolonged exposure to Patient's eyes exhibits soporific effects."
+
+/datum/quirk/Hypnotic_gaze/add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Add quirk ability action datum
+ var/datum/action/cooldown/hypnotize/act_hypno = new
+ act_hypno.Grant(quirk_mob)
+
+ // Add examine text
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
+
+/datum/quirk/Hypnotic_gaze/remove()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove quirk ability action datum
+ var/datum/action/cooldown/hypnotize/act_hypno = locate() in quirk_mob.actions
+ act_hypno.Remove(quirk_mob)
+
+ // Remove examine text
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+// Quirk examine text
+/datum/quirk/Hypnotic_gaze/proc/on_examine_holder(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ examine_list += "[quirk_holder.p_their(TRUE)] eyes glimmer with an entrancing power..."
+
+//
+// Quirk actions: Hypnotic Gaze
+//
+
+/datum/action/cooldown/hypnotize
+ name = "Hypnotize"
+ desc = "Stare deeply into someone's eyes, drawing them into a hypnotic slumber."
+ button_icon_state = "Hypno_eye"
+ icon_icon = 'modular_splurt/icons/mob/actions/lewd_actions/lewd_icons.dmi'
+ background_icon_state = "bg_alien"
+ transparent_when_unavailable = TRUE
+ cooldown_time = HYPNOEYES_COOLDOWN_NORMAL
+
+ // Should this create a brainwashed victim?
+ // Enabled by using Mesmer Eyes with the quirk
+ var/mode_brainwash = FALSE
+
+ // Terminology used
+ var/term_hypno = "hypnotize"
+ var/term_suggest = "suggestion"
+
+/datum/action/cooldown/hypnotize/IsAvailable(silent)
+ . = ..()
+
+ // Check parent return
+ if(!.)
+ return FALSE
+
+ // Check for carbon owner
+ if(!iscarbon(owner))
+ // Warn user and return
+ to_chat(owner, span_warning("You shouldn't have this ability!"))
+ return FALSE
+
+ // Define action owner
+ var/mob/living/carbon/human/action_owner = owner
+
+ // Check if owner has eye protection
+ if(action_owner.get_eye_protection())
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("Your eyes need to be visible for this ability to work."))
+ return FALSE
+
+ // Define owner's eyes
+ var/obj/item/organ/eyes/owner_eyes = owner.getorganslot(ORGAN_SLOT_EYES)
+
+ // Check if eyes exist
+ if(!istype(owner_eyes))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You need eyes to use this ability!"))
+ return FALSE
+
+ // Check if owner is blind
+ if(HAS_TRAIT(action_owner, TRAIT_BLIND))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("Your blind [owner_eyes] are of no use."))
+ return FALSE
+
+ // All checks passed
+ return TRUE
+
+/datum/action/cooldown/hypnotize/proc/set_brainwash(set_to = FALSE)
+ // Check if state will change
+ if(mode_brainwash == set_to)
+ // Do nothing
+ return
+
+ // Set new variable
+ mode_brainwash = set_to
+
+ // Define toggle message
+ var/toggle_message = "experiences an error?"
+
+ // Define log message type
+ var/log_message_type = "somehow screwed up"
+
+ // Check if brainwashing
+ if(mode_brainwash)
+ // Set ability text
+ name = "Brainwash"
+ desc = "Stare deeply into someone's eyes, and force them to become your loyal slave."
+
+ // Set cooldown time
+ cooldown_time = HYPNOEYES_COOLDOWN_BRAINWASH
+
+ // Set terminology
+ term_hypno = "brainwash"
+ term_suggest = "command"
+
+ // Set message suffix
+ toggle_message = "suddenly feels more intense!"
+
+ // Set log message type
+ log_message_type = "GAINED"
+
+ // Not brainwashing
+ else
+ // Set ability text
+ name = "Hypnotize"
+ desc = "Stare deeply into someone's eyes, drawing them into a hypnotic slumber."
+
+ // Set cooldown time
+ cooldown_time = HYPNOEYES_COOLDOWN_NORMAL
+
+ // Set terminology
+ term_hypno = "hypnotize"
+ term_suggest = "suggestion"
+
+ // Set message suffix
+ toggle_message = "fades back to normal levels..."
+
+ // Set log message type
+ log_message_type = "LOST"
+
+ // Update buttons
+ owner.update_action_buttons()
+
+ // Reset cooldown time
+ StartCooldown()
+
+ // Alert user
+ to_chat(owner, span_mind_control("Your hypnotic power [toggle_message] You'll need time to adjust before using it again."))
+
+ // Log interaction
+ log_admin("[key_name(owner)] [log_message_type] hypnotic brainwashing powers.")
+
+/datum/action/cooldown/hypnotize/Activate()
+ // Define action owner
+ var/mob/living/carbon/human/action_owner = owner
+
+ // Define target
+ var/grab_target = action_owner.pulling
+
+ // Check for target
+ if(!grab_target)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You need to grab someone first!"))
+ return
+
+ // Check for cyborg
+ if(iscyborg(grab_target))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't [term_hypno] a cyborg!"))
+ return
+
+ // Check for alien
+ // Taken from eyedropper check
+ /*
+ if(isalien(grab_target))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[grab_target] doesn\'t seem to have any eyes!"))
+ return
+ */
+
+ // Check for carbon human target
+ if(!ishuman(grab_target))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("That's not a valid creature!"))
+ return
+
+ // Check if target is alive
+ if(!isliving(grab_target))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't [term_hypno] the dead!"))
+ return
+
+ // Check for aggressive grab
+ if(action_owner.grab_state < GRAB_AGGRESSIVE)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You need a stronger grip before trying to [term_hypno] [grab_target]!"))
+ return
+
+ // Define target
+ var/mob/living/carbon/human/action_target = grab_target
+
+ // Check if target has a mind
+ if(!action_target.mind)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[grab_target] doesn't have a compatible mind!"))
+ return
+
+ /* Unused: Replaced by get_eye_protection
+ // Check if target's eyes are obscured
+ // ... by headwear
+ if((action_target.head && action_target.head.flags_cover & HEADCOVERSEYES))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.head]."))
+ return
+
+ // ... by a mask
+ else if((action_target.wear_mask && action_target.wear_mask.flags_cover & MASKCOVERSEYES))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.wear_mask]."))
+ return
+
+ // ... by glasses
+ else if((action_target.glasses && action_target.glasses.flags_cover & GLASSESCOVERSEYES))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.glasses]."))
+ return
+ */
+
+ // Check if target has eye protection
+ if(action_target.get_eye_protection())
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You have difficulty focusing on [action_target]'s eyes due to some form of protection, and are left unable to [term_hypno] them."))
+ to_chat(action_target, span_notice("[action_owner] stares intensely at you, but stops after a moment."))
+ return
+
+ // Check if target is blind
+ if(HAS_TRAIT(action_target, TRAIT_BLIND))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, but see nothing but emptiness."))
+ return
+
+ // Check for anti-magic
+ // This does not include TRAIT_HOLY
+ if(action_target.anti_magic_check())
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes. They stare back at you as if nothing had happened."))
+ to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes for a moment. You sense nothing out of the ordinary from them."))
+ return
+
+ // Check client pref for hypno
+ if(action_target.client?.prefs.cit_toggles & NEVER_HYPNO)
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("You sense that [action_target] would rather not be hypnotized, and decide to respect their wishes."))
+ to_chat(action_target, span_notice("[action_owner] stares into your eyes with a strange conviction, but turns away after a moment."))
+ return
+
+ // Check for mindshield implant
+ if(HAS_TRAIT(action_target, TRAIT_MINDSHIELD))
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, but hear a faint buzzing from [action_target.p_their()] head. It seems something is interfering."))
+ to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes for a moment, before a buzzing sound emits from your head."))
+ return
+
+ // Check for sleep immunity
+ // This is required for SetSleeping to trigger
+ if(HAS_TRAIT(action_target, TRAIT_SLEEPIMMUNE))
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, and see nothing but unrelenting energy. You won't be able to subdue [action_target.p_them()] in this state!"))
+ to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes, but sees something unusual about you..."))
+ return
+
+ // Check for sleep
+ if(action_target.IsSleeping())
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't [term_hypno] [action_target] whilst [action_target.p_theyre()] asleep!"))
+ return
+
+ // Check for combat mode
+ if(SEND_SIGNAL(action_target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("[action_target] is acting too defensively! You'll need [action_target.p_them()] to lower [action_target.p_their()] guard first!"))
+ to_chat(action_target, span_notice("[action_owner] tries to stare into your eyes, but can't get a read on you."))
+ return
+
+ // Display chat messages
+ to_chat(action_owner, span_notice("You stare deeply into [action_target]'s eyes..."))
+ to_chat(action_target, span_warning("[action_owner] stares intensely into your eyes..."))
+
+ // Try to perform action timer
+ if(!do_mob(action_owner, action_target, 5 SECONDS))
+ // Action timer was interrupted
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You lose concentration on [action_target], and fail to [term_hypno] [action_target.p_them()]!"))
+ to_chat(action_target, span_notice("[action_owner]'s gaze is broken prematurely, freeing you from any potential effects."))
+ return
+
+ // Define blank response
+ var/input_consent
+
+ // Check for non-consensual setting
+ if(action_target.client?.prefs.nonconpref != "Yes")
+ // Non-consensual is NOT enabled
+ // Define warning suffix
+ var/warning_target = (mode_brainwash ? "You will become a brainwashed victim, and be required to follow all orders given. [action_owner] accepts all responsibility for antagonistic orders." : "These are only suggestions, and you may disobey cases that strongly violate your character.")
+
+ // Prompt target for consent response
+ input_consent = alert(action_target, "Will you fall into a hypnotic stupor? This will allow [action_owner] to issue hypnotic [term_suggest]s. [warning_target]", "Hypnosis", "Yes", "No")
+
+ // When consent is denied
+ if(input_consent == "No")
+ // Warn the users, then return
+ to_chat(action_owner, span_warning("[action_target]'s attention breaks, despite the attempt to [term_hypno] [action_target.p_them()]! [action_target.p_they()] clearly don't want this!"))
+ to_chat(action_target, span_notice("Your concentration breaks as you realize you have no interest in following [action_owner]'s words!"))
+ return
+
+ // Display local message
+ action_target.visible_message(span_warning("[action_target] falls into a deep slumber!"), span_danger("Your eyelids gently shut as you fall into a deep slumber. All you can hear is [action_owner]'s voice as you commit to following all of their [term_suggest]s."))
+
+ // Set sleeping
+ action_target.SetSleeping(2 MINUTES)
+
+ // Set drowsiness
+ action_target.drowsyness = max(action_target.drowsyness, 40)
+
+ // Define warning suffix
+ var/warning_owner = (mode_brainwash ? "You are responsible for any antagonistic actions they take as a result of the brainwashing." : "This is only a suggestion, and [action_target.p_they()] may disobey if it violates [action_target.p_their()] character.")
+
+ // Prompt action owner for response
+ var/input_suggestion = input("What would you like to suggest [action_target] do? Leave blank to release [action_target.p_them()] instead. [warning_owner]", "Hypnotic [term_suggest]", null, null)
+
+ // Check if input text exists
+ if(!input_suggestion)
+ // Alert user of no input
+ to_chat(action_owner, "You decide not to give [action_target] a [term_suggest].")
+
+ // Remove sleep, then return
+ action_target.SetSleeping(0)
+ return
+
+ // Sanitize input text
+ input_suggestion = sanitize(input_suggestion)
+
+ // Check if brainwash mode
+ if(mode_brainwash)
+ // Create brainwash objective
+ brainwash(action_target, input_suggestion)
+
+ // Not in brainwash mode
+ else
+ // Display message to target
+ to_chat(action_target, span_mind_control("...[input_suggestion]..."))
+
+ // Start cooldown
+ StartCooldown()
+
+ // Display message to action owner
+ to_chat(action_owner, "You whisper your [term_suggest] in a smooth calming voice to [action_target]")
+
+ // Play a sound effect
+ playsound(action_target, 'sound/magic/domain.ogg', 20, 1)
+
+ // Display local message
+ action_target.visible_message(span_warning("[action_target] wakes up from their deep slumber!"), span_danger("Your eyelids gently open as you see [action_owner]'s face staring back at you."))
+
+ // Remove sleep, then return
+ action_target.SetSleeping(0)
+ return
+
+#undef HYPNOEYES_COOLDOWN_NORMAL
+#undef HYPNOEYES_COOLDOWN_BRAINWASH
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/incubus.dm b/modular_splurt/code/datums/traits/neutral_quirks/incubus.dm
new file mode 100644
index 000000000000..fd05277c79f6
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/incubus.dm
@@ -0,0 +1,24 @@
+//succubus and incubus below
+/datum/quirk/incubus
+ name = "Incubus"
+ desc = "Your seductor-like metabolism can only be sated by milk. (And semen, if you're a Succubus as well.)"
+ value = 0
+ mob_trait = TRAIT_INCUBUS
+ processing_quirk = TRUE
+
+/datum/quirk/incubus/add()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ ADD_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ ADD_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+/datum/quirk/incubus/remove()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ REMOVE_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+/datum/quirk/incubus/on_process()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
\ No newline at end of file
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/jiggly_ass.dm b/modular_splurt/code/datums/traits/neutral_quirks/jiggly_ass.dm
new file mode 100644
index 000000000000..8d8ba8aee4e4
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/jiggly_ass.dm
@@ -0,0 +1,19 @@
+/datum/quirk/jiggly_ass
+ name = "Buns of Thunder"
+ desc = "That pants-stretching, seat-creaking, undie-devouring butt of yours is as satisfying as it is difficult to keep balanced when smacked!"
+ value = 0
+ mob_trait = TRAIT_JIGGLY_ASS
+ gain_text = span_notice("Your butt feels extremely smackable.")
+ lose_text = span_notice("Your butt feels normally smackable again.")
+
+/datum/quirk/jiggly_ass/add()
+ // Add examine text
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
+
+/datum/quirk/jiggly_ass/remove()
+ // Remove examine text
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+// Quirk examine text
+/datum/quirk/jiggly_ass/proc/on_examine_holder(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ examine_list += span_lewd("[quirk_holder.p_their(TRUE)] butt could use a firm smack.")
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/kiss_slut.dm b/modular_splurt/code/datums/traits/neutral_quirks/kiss_slut.dm
new file mode 100644
index 000000000000..603262b4f6b3
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/kiss_slut.dm
@@ -0,0 +1,8 @@
+/datum/quirk/kiss_slut
+ name = "Kiss Slut"
+ desc = "The sheer thought of kissing someone makes you blush and overheat, effectively increasing your arousal with each smooch."
+ value = 0
+ mob_trait = TRAIT_KISS_SLUT
+ gain_text = span_lewd("You feel like kissing someone...")
+ lose_text = span_notice("You don't feel like kissing someone anymore...")
+ medical_record_text = "Patient seems to demonstrate an extraordinary liking in kissing."
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/masked_mook.dm b/modular_splurt/code/datums/traits/neutral_quirks/masked_mook.dm
new file mode 100644
index 000000000000..2c7e8a389ec9
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/masked_mook.dm
@@ -0,0 +1,24 @@
+/datum/quirk/masked_mook
+ name = "Bane Syndrome"
+ desc = "For some reason you don't feel... right without wearing some kind of gas mask."
+ gain_text = span_danger("You start feeling unwell without any gas mask on.")
+ lose_text = span_notice("You no longer have a need to wear some gas mask.")
+ value = 0
+ mood_quirk = TRUE
+ medical_record_text = "Patient feels more secure when wearing a gas mask."
+ processing_quirk = TRUE
+
+/datum/quirk/masked_mook/on_process()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/clothing/mask/gas/gasmask = H.get_item_by_slot(ITEM_SLOT_MASK)
+ if(istype(gasmask))
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_MASKED_MOOK, /datum/mood_event/masked_mook)
+ else
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_MASKED_MOOK, /datum/mood_event/masked_mook_incomplete)
+
+/datum/quirk/masked_mook/on_spawn()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/clothing/mask/gas/cosmetic/gasmask = new(get_turf(quirk_holder)) // Uses a custom gas mask
+ H.equip_to_slot_if_possible(gasmask, ITEM_SLOT_MASK) // If character have a loadout mask, the custom one will not overwrite it but instead will be dropped on floor
+ H.regenerate_icons()
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/messy.dm b/modular_splurt/code/datums/traits/neutral_quirks/messy.dm
new file mode 100644
index 000000000000..93ffa7160c30
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/messy.dm
@@ -0,0 +1,8 @@
+/datum/quirk/messy
+ name = "Messy"
+ desc = "Due to your unique biology/construction/bluespace magic you always manage to make a mess when you cum, even if it's not possible in normal circumstances."
+ value = 0
+ mob_trait = TRAIT_MESSY
+ gain_text = span_lewd("You feel like covering something in layer of your fluids.")
+ lose_text = span_notice("You don't feel 'messy' anymore.")
+ medical_record_text = "Had to be sedated after covering entire hospital wing with cum."
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/modularlimbs.dm b/modular_splurt/code/datums/traits/neutral_quirks/modularlimbs.dm
new file mode 100644
index 000000000000..77aad4f931aa
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/modularlimbs.dm
@@ -0,0 +1,66 @@
+/datum/quirk/modular
+ name = "Modular Limbs"
+ desc = "Your limbs are able to be attached and detached easily... Unfortunately, everyone around you can alter your limbs too! Right click yourself to use this quirk."
+ value = 0
+
+/datum/quirk/modular/add()
+ var/mob/living/carbon/human/C = quirk_holder
+ add_verb(C,/mob/living/proc/alterlimbs)
+
+/datum/quirk/modular/remove()
+ var/mob/living/carbon/human/C = quirk_holder
+ remove_verb(C,/mob/living/proc/alterlimbs)
+
+/mob/living/proc/alterlimbs()
+ set name = "Alter Limbs"
+ set desc = "Remove or attach a limb!"
+ set category = "IC"
+ set src in view(usr.client)
+
+ var/mob/living/carbon/human/U = usr
+ var/mob/living/carbon/human/C = src
+
+ var/obj/item/I = U.get_active_held_item()
+ if(istype(I,/obj/item/bodypart))
+ var/obj/item/bodypart/L = I
+ if(!C.Adjacent(U))
+ to_chat(U, span_warning("You must be adjacent to [C] to do this!"))
+ return
+ if(C.get_bodypart(L.body_zone))
+ to_chat(U, span_warning("[C] already has a limb attached there!"))
+ return
+ C.visible_message(span_warning("[U] is attempting to attach [L] onto [C]!"), span_userdanger("[U] is attempting to re-attach one of your limbs!"))
+ if(do_after(U, 40, target = C) && C.Adjacent(U))
+ L.attach_limb(C)
+ C.visible_message(span_warning("[U] successfully attaches [L] onto [C]"), span_userdanger("[U] has successfully attached a [L.name] onto you; you can use that limb again!"))
+ return
+ else
+ to_chat(U, span_warning("You and [C] must both stand still for you to remove one of their limbs!"))
+ return
+ else
+ if(!C.Adjacent(U))
+ to_chat(U, span_warning("You must be adjacent to [C] to do this!"))
+ return
+ if(U.zone_selected == BODY_ZONE_CHEST || U.zone_selected == BODY_ZONE_HEAD)
+ to_chat(U, span_warning("You must target either an arm or a leg!"))
+ return
+ if(U.zone_selected == BODY_ZONE_PRECISE_GROIN || U.zone_selected == BODY_ZONE_PRECISE_EYES || U.zone_selected == BODY_ZONE_PRECISE_MOUTH)
+ to_chat(U, span_warning("There is no limb here; select an arm or a leg!"))
+ return
+ if(!C.get_bodypart(U.zone_selected))
+ to_chat(U, span_warning("They are already missing that limb!"))
+ return
+ C.visible_message(span_warning("[U] is attempting to remove one of [C]'s limbs!"), span_userdanger("[U] is attempting to disconnect one of your limbs!"))
+ var/obj/item/bodypart/B = C.get_bodypart(U.zone_selected)
+ if(C.Adjacent(U) && do_after(U, 40, target = C))
+ var/obj/item/bodypart/D = C.get_bodypart(U.zone_selected)
+ if(B != D)
+ to_chat(U, span_warning("You cannot target a different limb while already removing another!"))
+ return
+ D.drop_limb()
+ C.update_equipment_speed_mods()
+ C.visible_message(span_warning("[U] smoothly disconnects [C]'s [D.name]!"), span_userdanger("[U] has forcefully disconnected your [D.name]!"))
+ return
+ else
+ to_chat(U, span_warning("You and [C] must both stand still for you to remove one of their limbs!"))
+ return
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/nudist.dm b/modular_splurt/code/datums/traits/neutral_quirks/nudist.dm
new file mode 100644
index 000000000000..563ee9724865
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/nudist.dm
@@ -0,0 +1,88 @@
+/datum/quirk/nudist
+ name = "Nudist"
+ desc = "Wearing most types of clothing unnerves you. Bring a gear harness!"
+ gain_text = span_notice("You feel spiritually connected to your natural form.")
+ lose_text = span_notice("It feels like clothing could fit you comfortably.")
+ medical_record_text = "Patient expresses a psychological need to remain unclothed."
+ value = 0
+ mood_quirk = TRUE
+ var/is_nude
+
+/datum/quirk/nudist/add()
+ // Register signal handlers
+ RegisterSignal(quirk_holder, COMSIG_MOB_UPDATE_GENITALS, PROC_REF(check_outfit))
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(quirk_examine_nudist))
+
+/datum/quirk/nudist/remove()
+ // Remove mood event
+ SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, QMOOD_NUDIST)
+
+ // Unregister signals
+ UnregisterSignal(quirk_holder, list(COMSIG_MOB_UPDATE_GENITALS, COMSIG_PARENT_EXAMINE))
+
+/datum/quirk/nudist/post_add()
+ // Evaluate outfit
+ check_outfit()
+
+/datum/quirk/nudist/on_spawn()
+ // Spawn a Rapid Disrobe Implant
+ var/obj/item/implant/disrobe/quirk_implant = new
+
+ // Implant into quirk holder
+ quirk_implant.implant(quirk_holder, null, TRUE, TRUE)
+
+/datum/quirk/nudist/proc/check_outfit()
+ SIGNAL_HANDLER
+
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Check if torso is uncovered
+ if(quirk_mob.is_chest_exposed() && quirk_mob.is_groin_exposed())
+ // Send positive mood event
+ SEND_SIGNAL(quirk_mob, COMSIG_ADD_MOOD_EVENT, QMOOD_NUDIST, /datum/mood_event/nudist_positive)
+
+ // Check if already set
+ if(is_nude)
+ return
+
+ // Alert user in chat
+ to_chat(quirk_mob, span_nicegreen("You begin to feel better without the restraint of clothing!"))
+
+ // Set nude status
+ is_nude = TRUE
+
+ // Torso is covered
+ else
+ // Send negative mood event
+ SEND_SIGNAL(quirk_mob, COMSIG_ADD_MOOD_EVENT, QMOOD_NUDIST, /datum/mood_event/nudist_negative)
+
+ // Check if already set
+ if(!is_nude)
+ return
+
+ // Alert user in chat
+ to_chat(quirk_mob, span_warning("The clothes feel wrong on your body..."))
+
+ // Set nude status
+ is_nude = FALSE
+
+/datum/quirk/nudist/proc/quirk_examine_nudist(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ SIGNAL_HANDLER
+
+ // Define default status term
+ var/mood_term = "content with [quirk_holder.p_their()] lack of"
+
+ // Define default span class
+ var/span_class
+
+ // Check if dressed
+ if(!is_nude)
+ // Set negative term
+ mood_term = "disturbed by wearing"
+
+ // Set negative span class
+ span_class = "warning"
+
+ // Add examine text
+ examine_list += "[quirk_holder.p_they(TRUE)] appear[quirk_holder.p_s()] [mood_term] clothing."
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/overweight.dm b/modular_splurt/code/datums/traits/neutral_quirks/overweight.dm
new file mode 100644
index 000000000000..4bac376db58d
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/overweight.dm
@@ -0,0 +1,12 @@
+/datum/quirk/overweight
+ name = "Overweight"
+ desc = "You're particularly fond of food, and join the shift being overweight."
+ value = 0
+ gain_text = span_notice("You feel a bit chubby!")
+ //no lose_text cause why would there be?
+
+/datum/quirk/overweight/on_spawn()
+ var/mob/living/M = quirk_holder
+ M.nutrition = rand(NUTRITION_LEVEL_FAT + NUTRITION_LEVEL_START_MIN, NUTRITION_LEVEL_FAT + NUTRITION_LEVEL_START_MAX)
+ M.overeatduration = 100
+ ADD_TRAIT(M, TRAIT_FAT, OBESITY)
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/pharmacokinesis.dm b/modular_splurt/code/datums/traits/neutral_quirks/pharmacokinesis.dm
new file mode 100644
index 000000000000..442e9137e17a
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/pharmacokinesis.dm
@@ -0,0 +1,9 @@
+/datum/quirk/pharmacokinesis //Supposed to prevent unwanted organ additions. But i don't think it's really working rn
+ name = "Acute Hepatic Pharmacokinesis" //copypasting dumbo
+ desc = "You have a genetic disorder that causes Incubus Draft and Succubus Milk to be absorbed by your liver instead."
+ value = 0
+ mob_trait = TRAIT_PHARMA
+ lose_text = span_notice("Your liver feels... different, somehow.")
+ var/active = FALSE
+ var/power = 0
+ var/cachedmoveCalc = 1
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/steel_ass.dm b/modular_splurt/code/datums/traits/neutral_quirks/steel_ass.dm
new file mode 100644
index 000000000000..f729cfb3b018
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/steel_ass.dm
@@ -0,0 +1,7 @@
+/datum/quirk/steel_ass
+ name = "Buns of Steel"
+ desc = "You've never skipped ass day. You are completely immune to all forms of ass slapping and anyone who tries to slap your rock hard ass usually gets a broken hand."
+ value = 0
+ mob_trait = TRAIT_STEEL_ASS
+ gain_text = span_notice("Your ass rivals those of golems.")
+ lose_text = span_notice("Your butt feels more squishy and slappable.")
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/storage_concealment.dm b/modular_splurt/code/datums/traits/neutral_quirks/storage_concealment.dm
new file mode 100644
index 000000000000..194c6fbddc02
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/storage_concealment.dm
@@ -0,0 +1,27 @@
+/datum/quirk/storage_concealment
+ name = "Dorsualiphobic Augmentation"
+ desc = "You despise the idea of being seen wearing any type of back-mounted storage apparatus! A new technology shields you from the immense shame you may experience, by hiding your equipped backpack."
+
+ // UNUSED: Enable by setting these values to TRUE
+ // The shame is unbearable
+ mood_quirk = FALSE
+ processing_quirk = FALSE
+
+/datum/quirk/storage_concealment/on_spawn()
+ . = ..()
+
+ // Create a new augment item
+ var/obj/item/implant/hide_backpack/put_in = new
+
+ // Apply the augment to the quirk holder
+ put_in.implant(quirk_holder, null, TRUE, TRUE)
+
+/datum/quirk/storage_concealment/on_process()
+ // This trait should only be applied by the augment
+ // Check the quirk holder for the trait
+ if(HAS_TRAIT(quirk_holder, TRAIT_HIDE_BACKPACK))
+ // When found: Mood bonus
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_HIDE_BAG, /datum/mood_event/dorsualiphobic_mood_positive)
+ else
+ // When not found: Mood penalty
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_HIDE_BAG, /datum/mood_event/dorsualiphobic_mood_negative)
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/succubus.dm b/modular_splurt/code/datums/traits/neutral_quirks/succubus.dm
new file mode 100644
index 000000000000..205ceee2577a
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/succubus.dm
@@ -0,0 +1,24 @@
+//succubus and incubus below
+/datum/quirk/succubus
+ name = "Succubus"
+ desc = "Your seductress-like metabolism can only be sated by semen. (And milk, if you're an Incubus as well.)"
+ value = 0
+ mob_trait = TRAIT_SUCCUBUS
+ processing_quirk = TRUE
+
+/datum/quirk/succubus/add()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ ADD_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ ADD_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+/datum/quirk/succubus/remove()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ REMOVE_TRAIT(H,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(H,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+/datum/quirk/succubus/on_process()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
\ No newline at end of file
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/undead.dm b/modular_splurt/code/datums/traits/neutral_quirks/undead.dm
new file mode 100644
index 000000000000..4a70a6475368
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/undead.dm
@@ -0,0 +1,31 @@
+/datum/quirk/undead
+ name = "Undeath"
+ desc = "Your body, be it anomalous, or just outright refusing to die - has indeed become undead. Due to this you may be hungrier."
+ value = 0
+ mob_trait = TRAIT_UNDEAD
+ processing_quirk = TRUE
+ // Note: The Undead cannot take advantage of healing viruses and genetic mutations, since they have no DNA.
+ var/list/zperks = list(TRAIT_STABLEHEART,TRAIT_EASYDISMEMBER,TRAIT_VIRUSIMMUNE,TRAIT_RADIMMUNE,TRAIT_FAKEDEATH,TRAIT_NOSOFTCRIT, TRAIT_NOPULSE)
+
+/datum/quirk/undead/add()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ if(H.mob_biotypes == MOB_ROBOTIC)
+ return FALSE //Lol, lmao, even
+ H.mob_biotypes += MOB_UNDEAD
+ for(var/A = 1, A <= zperks.len, A++)
+ ADD_TRAIT(H,zperks[A],ROUNDSTART_TRAIT)
+
+/datum/quirk/undead/remove()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ H.mob_biotypes -= MOB_UNDEAD
+ for(var/A = 1, A <= zperks.len, A++)
+ REMOVE_TRAIT(H,zperks[A], null)
+
+/datum/quirk/undead/on_process()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ H.adjust_nutrition(-0.025)//The Undead are Hungry.
+ H.set_screwyhud(SCREWYHUD_HEALTHY)
+ H.adjustOxyLoss(-3) //Stops a defibrilator bug. Note to future self: Fix defib bug.
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/vegetarian.dm b/modular_splurt/code/datums/traits/neutral_quirks/vegetarian.dm
new file mode 100644
index 000000000000..32c014653512
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/vegetarian.dm
@@ -0,0 +1,22 @@
+/datum/quirk/vegetarian
+ name = "Vegetarian"
+ desc = "You find the idea of eating meat morally and physically repulsive."
+ value = 0
+ gain_text = span_notice("You feel repulsion at the idea of eating meat.")
+ lose_text = span_notice("You feel like eating meat isn't that bad.")
+ medical_record_text = "Patient reports a vegetarian diet."
+
+/datum/quirk/vegetarian/add()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/datum/species/species = H.dna.species
+ species.liked_food &= ~MEAT
+ species.disliked_food |= MEAT
+
+/datum/quirk/vegetarian/remove()
+ var/mob/living/carbon/human/H = quirk_holder
+ if(H)
+ var/datum/species/species = H.dna.species
+ if(initial(species.liked_food) & MEAT)
+ species.liked_food |= MEAT
+ if(initial(species.disliked_food) & ~MEAT)
+ species.disliked_food &= ~MEAT
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/well_trained.dm b/modular_splurt/code/datums/traits/neutral_quirks/well_trained.dm
new file mode 100644
index 000000000000..b31406d33013
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/well_trained.dm
@@ -0,0 +1,83 @@
+//well-trained moved to neutral to stop the awkward situation of a dom snapping and the 30 trait powergamers fall to the floor.
+/datum/quirk/well_trained
+ name = "Well-Trained"
+ desc = "You absolutely love being dominated. The thought of someone stronger than you is enough to make you act up."
+ value = 0
+ gain_text = span_notice("You feel like being someone's pet...")
+ lose_text = span_notice("You no longer feel like being a pet...")
+ processing_quirk = TRUE
+ var/notice_delay = 0
+ var/mob/living/carbon/human/last_dom
+
+/datum/quirk/well_trained/add()
+ . = ..()
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
+
+/datum/quirk/well_trained/remove()
+ . = ..()
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+/datum/quirk/well_trained/proc/on_examine_holder(atom/source, mob/living/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ if(!istype(user))
+ return
+ if(!user.has_quirk(/datum/quirk/dominant_aura))
+ return
+ examine_list += span_lewd("You sense a strong aura of submission from [quirk_holder.p_them()].")
+
+/datum/quirk/well_trained/on_process()
+ . = ..()
+ if(!quirk_holder)
+ return
+
+ var/good_x = "pet"
+ switch(quirk_holder.gender)
+ if(MALE)
+ good_x = "boy"
+ if(FEMALE)
+ good_x = "girl"
+
+ //Check for possible doms with the dominant_aura quirk, and for the closest one if there is
+ . = FALSE
+ var/list/mob/living/carbon/human/doms = range(DOMINANT_DETECT_RANGE, quirk_holder)
+ var/closest_distance
+ for(var/mob/living/carbon/human/dom in doms)
+ if(dom != quirk_holder && dom.has_quirk(/datum/quirk/dominant_aura))
+ if(!closest_distance || get_dist(quirk_holder, dom) <= closest_distance)
+ . = dom
+ closest_distance = get_dist(quirk_holder, dom)
+
+ //Return if no dom is found
+ if(!.)
+ last_dom = null
+ return
+
+ //Handle the mood
+ var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
+ if(istype(mood.mood_events[QMOOD_WELL_TRAINED], /datum/mood_event/dominant/good_boy))
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_WELL_TRAINED, /datum/mood_event/dominant/good_boy)
+ else
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_WELL_TRAINED, /datum/mood_event/dominant/need)
+
+ //Don't do anything if a previous dom was found
+ if(last_dom)
+ notice_delay = world.time + 15 SECONDS
+ return
+
+ last_dom = .
+
+ if(notice_delay > world.time)
+ return
+
+ //Let them know they're near
+ var/list/notices = list(
+ "You feel someone's presence making you more submissive...",
+ "Thoughts of being commanded flood you with lust...",
+ "You really want to be called a good [good_x]...",
+ "Someone's presence is making you all flustered...",
+ "You start getting sweaty and excited..."
+ )
+
+ to_chat(quirk_holder, span_lewd(pick(notices)))
+ notice_delay = world.time + 15 SECONDS
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/werewolf.dm b/modular_splurt/code/datums/traits/neutral_quirks/werewolf.dm
new file mode 100644
index 000000000000..d841958ecce1
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/werewolf.dm
@@ -0,0 +1,247 @@
+/datum/quirk/werewolf //adds the werewolf quirk
+ name = "Werewolf"
+ desc = "A beastly affliction allows you to shape-shift into a large anthropomorphic canine at will."
+ value = 0
+ mob_trait = TRAIT_WEREWOLF
+ gain_text = span_notice("You feel the full moon beckon.")
+ lose_text = span_notice("The moon's call hushes into silence.")
+ medical_record_text = "Patient has been reported howling at the night sky."
+ var/list/old_features
+
+/datum/quirk/werewolf/add()
+ // Define old features
+ old_features = list("species" = SPECIES_HUMAN, "legs" = "Plantigrade", "size" = 1, "bark")
+
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Record features
+ old_features = quirk_mob.dna.features.Copy()
+ old_features["species"] = quirk_mob.dna.species.type
+ old_features["custom_species"] = quirk_mob.custom_species
+ old_features["size"] = get_size(quirk_mob)
+ old_features["bark"] = quirk_mob.vocal_bark_id
+ old_features["taur"] = quirk_mob.dna.features["taur"]
+ old_features["eye_type"] = quirk_mob.dna.species.eye_type
+
+/datum/quirk/werewolf/post_add()
+ // Define quirk action
+ var/datum/action/cooldown/werewolf/transform/quirk_action = new
+
+ // Grant quirk action
+ quirk_action.Grant(quirk_holder)
+
+/datum/quirk/werewolf/remove()
+ // Define quirk action
+ var/datum/action/cooldown/werewolf/transform/quirk_action = locate() in quirk_holder.actions
+
+ // Revoke quirk action
+ quirk_action.Remove(quirk_holder)
+
+//
+// Quirk actions: Werewolf
+//
+
+/datum/action/cooldown/werewolf
+ name = "Werewolf Ability"
+ desc = "Do something related to werewolves."
+ icon_icon = 'modular_splurt/icons/mob/actions/misc_actions.dmi'
+ button_icon_state = "Transform"
+ check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUN | AB_CHECK_CONSCIOUS | AB_CHECK_ALIVE
+ cooldown_time = 5 SECONDS
+ transparent_when_unavailable = TRUE
+
+/datum/action/cooldown/werewolf/transform
+ name = "Toggle Werewolf Form"
+ desc = "Transform in or out of your wolf form."
+ var/transformed = FALSE
+ var/species_changed = FALSE
+ var/werewolf_gender = "Lycan"
+ var/list/old_features
+
+/datum/action/cooldown/werewolf/transform/Grant()
+ . = ..()
+
+ // Define carbon owner
+ var/mob/living/carbon/action_owner_carbon = owner
+
+ // Define parent quirk
+ var/datum/quirk/werewolf/quirk_data = locate() in action_owner_carbon.roundstart_quirks
+
+ // Check if data was copied
+ if(!quirk_data)
+ // Log error and return
+ log_game("Failed to get species data for werewolf action!")
+ return
+
+ // Define stored features
+ old_features = quirk_data.old_features.Copy()
+
+ // Define action owner
+ var/mob/living/carbon/human/action_owner = owner
+
+ // Set species gendered name
+ switch(action_owner.gender)
+ if(MALE)
+ werewolf_gender = "Wer"
+ if(FEMALE)
+ werewolf_gender = "Wīf"
+ if(PLURAL)
+ werewolf_gender = "Hie"
+ if(NEUTER)
+ werewolf_gender = "Þing"
+
+/datum/action/cooldown/werewolf/transform/Activate()
+ // Define action owner
+ var/mob/living/carbon/human/action_owner = owner
+
+ // Check for restraints
+ if(!CHECK_MOBILITY(action_owner, MOBILITY_USE))
+ // Warn user, then return
+ action_owner.visible_message(span_warning("You cannot transform while restrained!"))
+ return
+
+ // Define citadel organs
+ var/obj/item/organ/genital/penis/organ_penis = action_owner.getorganslot(ORGAN_SLOT_PENIS)
+ var/obj/item/organ/genital/breasts/organ_breasts = action_owner.getorganslot(ORGAN_SLOT_BREASTS)
+ var/obj/item/organ/genital/vagina/organ_vagina = action_owner.getorganslot(ORGAN_SLOT_VAGINA)
+
+ // Play shake animation
+ action_owner.shake_animation(2)
+
+ // Transform into wolf form
+ if(!transformed)
+ // Define current species type
+ var/datum/species/owner_species = action_owner.dna.species.type
+
+ // Check if species has changed
+ if(old_features["species"] != owner_species)
+ // Set old species
+ old_features["species"] = owner_species
+
+ // Define species prefix
+ var/custom_species_prefix
+
+ // Check if species is mammal (anthro)
+ if(ismammal(action_owner))
+ // Do nothing!
+
+ // Check if species is already a mammal sub-type
+ else if(ispath(owner_species, /datum/species/mammal))
+ // Do nothing!
+
+ // Check if species is a jelly
+ else if(isjellyperson(action_owner))
+ // Set species prefix
+ custom_species_prefix = "Jelly "
+
+ // Check if species is a jelly subtype
+ else if(ispath(owner_species, /datum/species/jelly))
+ // Set species prefix
+ custom_species_prefix = "Slime "
+
+ // Species is not a mammal
+ else
+ // Change species
+ action_owner.set_species(/datum/species/mammal, 1)
+
+ // Set species changed
+ species_changed = TRUE
+
+ // Set species features
+ action_owner.dna.custom_species = "[custom_species_prefix][werewolf_gender]wulf"
+ action_owner.dna.species.mutant_bodyparts["mam_tail"] = "Otusian"
+ action_owner.dna.species.mutant_bodyparts["legs"] = "Digitigrade"
+ action_owner.Digitigrade_Leg_Swap(FALSE)
+ action_owner.dna.species.mutant_bodyparts["mam_snouts"] = "Sergal"
+ action_owner.dna.features["mam_ears"] = "Jackal"
+ action_owner.dna.features["mam_tail"] = "Otusian"
+ action_owner.dna.features["mam_snouts"] = "Sergal"
+ action_owner.dna.features["legs"] = "Digitigrade"
+ action_owner.dna.features["insect_fluff"] = "Hyena"
+ action_owner.update_size(clamp(get_size(action_owner) + 0.5, RESIZE_MICRO, RESIZE_MACRO))
+ action_owner.set_bark("bark")
+ if(old_features["taur"] != "None")
+ action_owner.dna.features["taur"] = "Canine"
+ if(!(action_owner.dna.species.species_traits.Find(DIGITIGRADE)))
+ action_owner.dna.species.species_traits += DIGITIGRADE
+ action_owner.update_body()
+ action_owner.update_body_parts()
+
+ // Update possible citadel organs
+ if(organ_breasts)
+ organ_breasts.color = "#[action_owner.dna.features["mcolor"]]"
+ organ_breasts.update()
+ if(organ_penis)
+ organ_penis.shape = "Knotted"
+ organ_penis.color = "#ff7c80"
+ organ_penis.update()
+ organ_penis.modify_size(6)
+ if(organ_vagina)
+ organ_vagina.shape = "Furred"
+ organ_vagina.color = "#[action_owner.dna.features["mcolor"]]"
+ organ_vagina.update()
+
+ // Un-transform from wolf form
+ else
+ // Check if species was already mammal (anthro)
+ if(!species_changed)
+ // Do nothing!
+
+ // Species was not a mammal
+ else
+ // Revert species
+ action_owner.set_species(old_features["species"], TRUE)
+
+ // Clear species changed flag
+ species_changed = FALSE
+
+ // Revert species trait
+ action_owner.set_bark(old_features["bark"])
+ action_owner.dna.custom_species = old_features["custom_species"]
+ action_owner.dna.features["mam_ears"] = old_features["mam_ears"]
+ action_owner.dna.features["mam_snouts"] = old_features["mam_snouts"]
+ action_owner.dna.features["mam_tail"] = old_features["mam_tail"]
+ action_owner.dna.features["legs"] = old_features["legs"]
+ action_owner.dna.features["insect_fluff"] = old_features["insect_fluff"]
+ action_owner.dna.species.eye_type = old_features["eye_type"]
+ if(old_features["taur"] != "None")
+ action_owner.dna.features["taur"] = old_features["taur"]
+ if(old_features["legs"] == "Plantigrade")
+ action_owner.dna.species.species_traits -= DIGITIGRADE
+ action_owner.Digitigrade_Leg_Swap(TRUE)
+ action_owner.dna.species.mutant_bodyparts["legs"] = old_features["legs"]
+ action_owner.update_body()
+ action_owner.update_body_parts()
+ action_owner.update_size(clamp(get_size(action_owner) - 0.5, RESIZE_MICRO, RESIZE_MACRO))
+
+ // Revert citadel organs
+ if(organ_breasts)
+ organ_breasts.color = "#[old_features["breasts_color"]]"
+ organ_breasts.update()
+ if(action_owner.has_penis())
+ organ_penis.shape = old_features["cock_shape"]
+ organ_penis.color = "#[old_features["cock_color"]]"
+ organ_penis.update()
+ organ_penis.modify_size(-6)
+ if(action_owner.has_vagina())
+ organ_vagina.shape = old_features["vag_shape"]
+ organ_vagina.color = "#[old_features["vag_color"]]"
+ organ_vagina.update()
+ organ_vagina.update_size()
+
+ // Set transformation message
+ var/owner_p_their = action_owner.p_their()
+ var/toggle_message = (!transformed ? "[action_owner] shivers, [owner_p_their] flesh bursting with a sudden growth of thick fur as [owner_p_their] features contort to that of a beast, fully transforming [action_owner.p_them()] into a werewolf!" : "[action_owner] shrinks, [owner_p_their] wolfish features quickly receding.")
+
+ // Alert in local chat
+ action_owner.visible_message(span_danger(toggle_message))
+
+ // Toggle transformation state
+ transformed = !transformed
+
+ // Start cooldown
+ StartCooldown()
+
+ // Return success
+ return TRUE
diff --git a/modular_splurt/code/datums/traits/positive_quirks/arachnid.dm b/modular_splurt/code/datums/traits/positive_quirks/arachnid.dm
new file mode 100644
index 000000000000..bfc6dafa00fe
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/arachnid.dm
@@ -0,0 +1,30 @@
+/datum/quirk/arachnid
+ name = "Arachnid"
+ desc = "Your bodily anatomy allows you to spin webs and cocoons, even if you aren't an arachnid! (Note that this quirk does nothing for members of the arachnid species)"
+ value = 1
+ medical_record_text = "Patient has attempted to cover the room in webs, claiming to be \"making a nest\"."
+ mob_trait = TRAIT_ARACHNID
+ gain_text = span_notice("You feel a strange sensation near your anus...")
+ lose_text = span_notice("You feel like you can't spin webs anymore...")
+ processing_quirk = TRUE
+
+/datum/quirk/arachnid/add()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ if(is_species(H,/datum/species/arachnid))
+ to_chat(H, span_warning("As an arachnid, this quirk does nothing for you, as these abilities are innate to your species."))
+ return
+ var/datum/action/innate/spin_web/SW = new
+ var/datum/action/innate/spin_cocoon/SC = new
+ SC.Grant(H)
+ SW.Grant(H)
+
+/datum/quirk/arachnid/remove()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ if(is_species(H,/datum/species/arachnid))
+ return
+ var/datum/action/innate/spin_web/SW = locate(/datum/action/innate/spin_web) in H.actions
+ var/datum/action/innate/spin_cocoon/SC = locate(/datum/action/innate/spin_cocoon) in H.actions
+ SC?.Remove(H)
+ SW?.Remove(H)
diff --git a/modular_splurt/code/datums/traits/positive_quirks/ash_resistance.dm b/modular_splurt/code/datums/traits/positive_quirks/ash_resistance.dm
new file mode 100644
index 000000000000..fc9fa287a956
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/ash_resistance.dm
@@ -0,0 +1,18 @@
+/datum/quirk/ashresistance
+ name = "Ashen Resistance"
+ desc = "Your body is adapted to the burning sheets of ash that coat volcanic worlds, though the heavy downpours of silt will still tire you."
+ value = 2 //Is not actually THAT good. Does not grant breathing and does stamina damage to the point you are unable to attack. Crippling on lavaland, but you'll survive. Is not a replacement for SEVA suits for this reason. Can be adjusted.
+ mob_trait = TRAIT_ASHRESISTANCE
+ medical_record_text = "Patient has an abnormally thick epidermis."
+ gain_text = span_notice("You feel resistant to burning brimstone.")
+ lose_text = span_notice("You feel less as if your flesh is more flamamble.")
+
+/* --FALLBACK SYSTEM INCASE THE TRAIT FAILS TO WORK. Do NOT enable this without editing ash_storm.dm to deal stamina damage with ash immunity.
+/datum/quirk/ashresistance/add()
+ quirk_holder.weather_immunities |= "ash"
+
+/datum/quirk/ashresistance/remove()
+ if(!quirk_holder)
+ return
+ quirk_holder.weather_immunities -= "ash"
+*/
diff --git a/modular_splurt/code/datums/traits/positive_quirks/bloodfledge.dm b/modular_splurt/code/datums/traits/positive_quirks/bloodfledge.dm
new file mode 100644
index 000000000000..f906879c6865
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/bloodfledge.dm
@@ -0,0 +1,1029 @@
+#define BLOODFLEDGE_DRAIN_NUM 50
+#define BLOODFLEDGE_COOLDOWN_BITE 60 // Six seconds
+#define BLOODFLEDGE_COOLDOWN_REVIVE 3000 // Five minutes
+#define BLOODFLEDGE_BANK_CAPACITY (BLOODFLEDGE_DRAIN_NUM * 2)
+
+/datum/quirk/bloodfledge
+ name = "Bloodsucker Fledgling"
+ desc = "You are a fledgling belonging to ancient Bloodsucker bloodline. While the blessing has yet to fully convert you, some things have changed. Only blood will sate your hungers, and holy energies will cause your flesh to char. This is NOT an antagonist role!"
+ value = 2
+ medical_record_text = "Patient exhibits onset symptoms of a sanguine curse."
+ mob_trait = TRAIT_BLOODFLEDGE
+ gain_text = span_notice("You feel a sanguine thirst.")
+ lose_text = span_notice("You feel the sanguine thirst fade away.")
+ processing_quirk = FALSE // Handled by crates.dm
+
+/datum/quirk/bloodfledge/add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Add quirk traits
+ ADD_TRAIT(quirk_mob,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ ADD_TRAIT(quirk_mob,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+ // Set skin tone, if possible
+ if(!quirk_mob.dna.skin_tone_override)
+ quirk_mob.skin_tone = "albino"
+
+ // Add quirk language
+ quirk_mob.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
+
+ // Register examine text
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(quirk_examine_bloodfledge))
+
+/datum/quirk/bloodfledge/post_add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Define and grant ability Bite
+ var/datum/action/cooldown/bloodfledge/bite/act_bite = new
+ act_bite.Grant(quirk_mob)
+
+ // Check for synthetic
+ // Robotic mobs have technical issues with adjusting damage
+ if(quirk_mob.mob_biotypes & MOB_ROBOTIC)
+ // Warn user
+ to_chat(quirk_mob, span_warning("As a synthetic lifeform, your components are only able to grant limited sanguine abilities! Regeneration and revival are not possible."))
+
+ // User is not synthetic
+ else
+ // Define and grant ability Revive
+ var/datum/action/cooldown/bloodfledge/revive/act_revive = new
+ act_revive.Grant(quirk_mob)
+
+/datum/quirk/bloodfledge/on_process()
+ // Processing is currently only used for coffin healing
+ // This is started and stopped by a proc in crates.dm
+
+ // Define potential coffin
+ var/quirk_coffin = quirk_holder.loc
+
+ // Check if the current area is a coffin
+ if(istype(quirk_coffin, /obj/structure/closet/crate/coffin))
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Quirk mob must be injured
+ if(quirk_mob.health >= quirk_mob.maxHealth)
+ // Warn user
+ to_chat(quirk_mob, span_notice("[quirk_coffin] does nothing more to help you, as your body is fully mended."))
+
+ // Stop processing and return
+ STOP_PROCESSING(SSquirks, src)
+ return
+
+ // Nutrition (blood) level must be above STARVING
+ if(quirk_mob.nutrition <= NUTRITION_LEVEL_STARVING)
+ // Warn user
+ to_chat(quirk_mob, span_warning("[quirk_coffin] requires blood to operate, which you are currently lacking. Your connection to the other-world fades once again."))
+
+ // Stop processing and return
+ STOP_PROCESSING(SSquirks, src)
+ return
+
+ // Define initial health
+ var/health_start = quirk_mob.health
+
+ // Heal brute and burn
+ // Accounts for robotic limbs
+ quirk_mob.heal_overall_damage(2,2)
+ // Heal oxygen
+ quirk_mob.adjustOxyLoss(-2)
+ // Heal clone
+ quirk_mob.adjustCloneLoss(-2)
+
+ // Check for slime race
+ // NOT a slime
+ if(!isslimeperson(quirk_mob))
+ // Heal toxin
+ quirk_mob.adjustToxLoss(-2)
+ // IS a slime
+ else
+ // Grant toxin (heals slimes)
+ quirk_mob.adjustToxLoss(2)
+
+ // Update health
+ quirk_mob.updatehealth()
+
+ // Determine healed amount
+ var/health_restored = quirk_mob.health - health_start
+
+ // Remove nutrition (blood) as compensation for healing
+ // Amount is equal to 50% of healing done
+ quirk_mob.adjust_nutrition(health_restored*-1)
+
+ // User is not in a coffin
+ // This should not occur without teleportation
+ else
+ // Warn user
+ to_chat(quirk_holder, span_warning("Your connection to the other-world is broken upon leaving the [quirk_coffin]!"))
+
+ // Stop processing
+ STOP_PROCESSING(SSquirks, src)
+
+/datum/quirk/bloodfledge/remove()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove quirk traits
+ REMOVE_TRAIT(quirk_mob, TRAIT_NO_PROCESS_FOOD, ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(quirk_mob, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
+
+ // Remove quirk ability action datums
+ var/datum/action/cooldown/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
+ var/datum/action/cooldown/bloodfledge/revive/act_revive = locate() in quirk_mob.actions
+ act_bite.Remove(quirk_mob)
+ act_revive.Remove(quirk_mob)
+
+ // Remove quirk language
+ quirk_mob.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
+
+ // Unregister examine text
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+/datum/quirk/bloodfledge/on_spawn()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Create vampire ID card
+ var/obj/item/card/id/vampire/id_vampire = new /obj/item/card/id/vampire(get_turf(quirk_holder))
+
+ // Update card information
+ id_vampire.registered_name = quirk_mob.real_name
+ id_vampire.update_label(addtext(id_vampire.registered_name, "'s Bloodfledge"))
+
+ // Determine banking ID information
+ for(var/bank_account in SSeconomy.bank_accounts)
+ // Define current iteration's account
+ var/datum/bank_account/account = bank_account
+
+ // Check for match
+ if(account.account_id == quirk_mob.account_id)
+ // Add to cards list
+ account.bank_cards += src
+
+ // Assign account
+ id_vampire.registered_account = account
+
+ // Stop searching
+ break
+
+ // Try to add ID to backpack
+ var/id_in_bag = quirk_mob.equip_to_slot_if_possible(id_vampire, ITEM_SLOT_BACKPACK) || FALSE
+
+ // Text for where the item was sent
+ var/id_location = (id_in_bag ? "in your backpack" : "at your feet" )
+
+ // Alert user in chat
+ // This should not post_add, because the ID is added by on_spawn
+ to_chat(quirk_holder, span_boldnotice("There is a bloodfledge's ID card [id_location], linked to your station account. It functions as a spare ID, but lacks job access."))
+
+/datum/quirk/bloodfledge/proc/quirk_examine_bloodfledge(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ SIGNAL_HANDLER
+
+ // Check if human examiner exists
+ if(!istype(examiner))
+ return
+
+ // Check if examiner is dumb
+ if(HAS_TRAIT(examiner, TRAIT_DUMB))
+ // Return with no effects
+ return
+
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Define hunger texts
+ var/examine_hunger_public
+ var/examine_hunger_secret
+
+ // Check hunger levels
+ switch(quirk_mob.nutrition)
+ // Hungry
+ if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
+ examine_hunger_secret = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] blood starved!"
+ examine_hunger_public = "[quirk_holder.p_they(TRUE)] seem[quirk_holder.p_s()] on edge from something."
+
+ // Starving
+ if(0 to NUTRITION_LEVEL_STARVING)
+ examine_hunger_secret = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] in dire need of blood!"
+ examine_hunger_public = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] radiating an aura of frenzied hunger!"
+
+ // Invalid hunger
+ else
+ // Return with no message
+ return
+
+ // Check if examiner shares the quirk
+ if(isbloodfledge(examiner))
+ // Add detection text
+ examine_list += span_info("[quirk_holder.p_their(TRUE)] hunger makes it easy to identify [quirk_holder.p_them()] as a fellow Bloodsucker Fledgling!")
+
+ // Add hunger text
+ examine_list += span_warning(examine_hunger_secret)
+
+ // Check if public hunger text exists
+ else
+ // Add hunger text
+ examine_list += span_warning(examine_hunger_public)
+
+//
+// Quirk actions: Bloodsucker Fledgling / Vampire
+//
+
+// Basic action preset
+/datum/action/cooldown/bloodfledge
+ name = "Broken Bloodfledge Ability"
+ desc = "You shouldn't be seeing this!"
+ button_icon_state = "power_torpor"
+ background_icon_state = "vamp_power_off"
+ buttontooltipstyle = "cult"
+ icon_icon = 'icons/mob/actions/bloodsucker.dmi'
+ button_icon = 'icons/mob/actions/bloodsucker.dmi'
+ transparent_when_unavailable = TRUE
+
+// Basic can-use check
+/datum/action/cooldown/bloodfledge/IsAvailable(silent = FALSE)
+ . = ..()
+
+ // Check parent return
+ if(!.)
+ return FALSE
+
+ // Check for carbon owner
+ if(!iscarbon(owner))
+ // Warn user and return
+ to_chat(owner, span_warning("You shouldn't have this ability!"))
+ return FALSE
+
+ // Check vampire ability mob proc
+ if(!owner.allow_vampiric_ability(silent = FALSE))
+ return FALSE
+
+ // Action can be used
+ return TRUE
+
+// Action: Bite
+/datum/action/cooldown/bloodfledge/bite
+ name = "Fledgling Bite"
+ desc = "Sink your vampiric fangs into the person you are grabbing, and attempt to drink their blood."
+ button_icon_state = "power_feed"
+ cooldown_time = BLOODFLEDGE_COOLDOWN_BITE
+ var/time_interact = 30
+
+ // Reagent holder, used to change reaction type
+ var/datum/reagents/blood_bank
+
+/datum/action/cooldown/bloodfledge/bite/Grant()
+ . = ..()
+
+ // Check for voracious
+ if(HAS_TRAIT(owner, TRAIT_VORACIOUS))
+ // Make times twice as fast
+ cooldown_time *= 0.5
+ time_interact*= 0.5
+
+ // Create reagent holder
+ blood_bank = new(BLOODFLEDGE_BANK_CAPACITY)
+
+/datum/action/cooldown/bloodfledge/bite/Activate()
+ // Define action owner
+ var/mob/living/carbon/action_owner = owner
+
+ // Check for any grabbed target
+ if(!action_owner.pulling)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You need a victim first!"))
+ return
+
+ // Limit maximum nutrition
+ if(action_owner.nutrition >= NUTRITION_LEVEL_FAT)
+ // Warn the user, then return
+ to_chat(action_owner, span_notice("You are too full to drain any more."))
+ return
+
+ // Limit maximum potential nutrition
+ if(action_owner.nutrition + BLOODFLEDGE_DRAIN_NUM >= NUTRITION_LEVEL_FAT)
+ // Warn the user, then return
+ to_chat(action_owner, span_notice("You would become too full by draining any more blood."))
+ return
+
+ // Check for muzzle
+ if(action_owner.is_muzzled())
+ // Warn the user, then return
+ to_chat(action_owner, span_notice("You can't bite things while muzzled!"))
+ return
+
+ // Check for covered mouth
+ if(action_owner.is_mouth_covered())
+ // Warn the user, then return
+ to_chat(action_owner, span_notice("You can't bite things with your mouth covered!"))
+ return
+
+ // Define pulled target
+ var/pull_target = action_owner.pulling
+
+ // Define bite target
+ var/mob/living/carbon/human/bite_target
+
+ // Define if action owner is dumb
+ var/action_owner_dumb = HAS_TRAIT(action_owner, TRAIT_DUMB)
+
+ // Check if the target is carbon
+ if(iscarbon(pull_target))
+ // Set the bite target
+ bite_target = pull_target
+
+ // Or cocooned carbon
+ else if(istype(pull_target,/obj/structure/arachnid/cocoon))
+ // Define if cocoon has a valid target
+ // This cannot use pull_target
+ var/possible_cocoon_target = locate(/mob/living/carbon/human) in action_owner.pulling.contents
+
+ // Check defined cocoon target
+ if(possible_cocoon_target)
+ // Set the bite target
+ bite_target = possible_cocoon_target
+
+ // Or a blood tomato
+ else if(istype(pull_target,/obj/item/reagent_containers/food/snacks/grown/tomato/blood))
+ // Set message based on dumbness
+ var/message_tomato_suffix = (action_owner_dumb ? ", and absorb it\'s delicious vegan-friendly blood!" : "! It's not very nutritious.")
+ // Warn the user, then return
+ to_chat(action_owner, span_danger("You plunge your fangs into [pull_target][message_tomato_suffix]"))
+ return
+
+ // This doesn't actually interact with the item
+
+ // Or none of the above
+ else
+ // Set message based on dumbness
+ var/message_invalid_target = (action_owner_dumb ? "You bite at [pull_target], but nothing seems to happen" : "You can't drain blood from [pull_target]!")
+ // Warn the user, then return
+ to_chat(action_owner, span_warning(message_invalid_target))
+ return
+
+ // Define selected zone
+ var/target_zone = action_owner.zone_selected
+
+ // Check if target can be penetrated
+ // Bypass pierce immunity so feedback can be provided later
+ if(!bite_target.can_inject(action_owner, FALSE, target_zone, FALSE, TRUE))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("There\'s no exposed flesh or thin material in that region of [bite_target]'s body. You're unable to bite them!"))
+ return
+
+ // Check targeted body part
+ var/obj/item/bodypart/bite_bodypart = bite_target.get_bodypart(target_zone)
+
+ // Define zone name
+ var/target_zone_name = "flesh"
+
+ // Define if target zone has special effects
+ var/target_zone_effects = FALSE
+
+ // Define if zone should be checked
+ // Uses dismember check to determine if it can be missing
+ // Missing limbs are assumed to be dismembered
+ var/target_zone_check = bite_bodypart?.can_dismember() || TRUE
+
+ // Set zone name based on region
+ // Also checks for some protections
+ switch(target_zone)
+ if(BODY_ZONE_HEAD)
+ target_zone_name = "neck"
+
+ if(BODY_ZONE_CHEST)
+ target_zone_name = "shoulder"
+
+ if(BODY_ZONE_L_ARM)
+ target_zone_name = "left arm"
+
+ if(BODY_ZONE_R_ARM)
+ target_zone_name = "right arm"
+
+ if(BODY_ZONE_L_LEG)
+ target_zone_name = "left thigh"
+
+ if(BODY_ZONE_R_LEG)
+ target_zone_name = "right thigh"
+
+ if(BODY_ZONE_PRECISE_EYES)
+ // Check if eyes exist and are exposed
+ if(!bite_target.has_eyes() == HAS_EXPOSED_GENITAL)
+ // Warn user and return
+ to_chat(action_owner, span_warning("You can't find [bite_target]'s eyes to bite them!"))
+ return
+
+ // Set region data normally
+ target_zone_name = "eyes"
+ target_zone_check = FALSE
+ target_zone_effects = TRUE
+
+ if(BODY_ZONE_PRECISE_MOUTH)
+ // Check if mouth exists and is exposed
+ if(!(bite_target.has_mouth() && bite_target.mouth_is_free()))
+ to_chat(action_owner, span_warning("You can't find [bite_target]'s lips to bite them!"))
+ return
+
+ // Set region data normally
+ target_zone_name = "lips"
+ target_zone_check = FALSE
+ target_zone_effects = TRUE
+
+ if(BODY_ZONE_PRECISE_GROIN)
+ target_zone_name = "groin"
+ target_zone_check = FALSE
+
+ if(BODY_ZONE_PRECISE_L_HAND)
+ target_zone_name = "left wrist"
+
+ if(BODY_ZONE_PRECISE_R_HAND)
+ target_zone_name = "right wrist"
+
+ if(BODY_ZONE_PRECISE_L_FOOT)
+ target_zone_name = "left ankle"
+
+ if(BODY_ZONE_PRECISE_R_FOOT)
+ target_zone_name = "right ankle"
+
+ // Check if target should be checked
+ if(target_zone_check)
+ // Check if bodypart exists
+ if(!bite_bodypart)
+ // Warn user and return
+ to_chat(action_owner, span_warning("[bite_target] doesn't have a [target_zone_name] for you to bite!"))
+ return
+
+ // Check if bodypart is organic
+ if(!bite_bodypart.is_organic_limb())
+ // Display local message
+ action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but is unable to penetrate the mechanical prosthetic!"), span_warning("You attempt to bite [bite_target]'s [target_zone_name], but can't penetrate the mechanical prosthetic!"))
+
+ // Warn user
+ to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but is unable to penetrate the mechanical prosthetic!"))
+
+ // Play metal hit sound
+ playsound(bite_target, "sound/effects/clang[pick(1,2)].ogg", 30, 1, -2)
+
+ // Start cooldown early to prevent spam
+ StartCooldown()
+
+ // Return without further effects
+ return
+
+ // Check for anti-magic
+ if(bite_target.anti_magic_check(FALSE, TRUE, FALSE, 0))
+ // Check for a dumb user
+ if(action_owner_dumb)
+ // Display local message
+ action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but bursts into flames just as [action_owner.p_they()] come[action_owner.p_s()] into contact with [bite_target.p_them()]!"), span_userdanger("Surges of pain course through your body as you attempt to bite [bite_target]! What were you thinking?"))
+
+ // Warn target
+ to_chat(bite_target, span_warning("[action_owner] tries to bite you, but bursts into flames just as [action_owner.p_they()] come[action_owner.p_s()] into contact with you!"))
+
+ // Stop grabbing
+ action_owner.stop_pulling()
+
+ // Ignite action owner
+ action_owner.adjust_fire_stacks(2)
+ action_owner.IgniteMob()
+
+ // Return with no further effects
+ return
+
+ // Warn the user and target, then return
+ to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but stops before touching you!"))
+ to_chat(action_owner, span_warning("[bite_target] is blessed! You stop just in time to avoid catching fire."))
+ return
+
+ // Check for garlic necklace or garlic in the bloodstream
+ if(!blood_sucking_checks(bite_target, TRUE, TRUE))
+ // Check for a dumb user
+ if(action_owner_dumb)
+ // Display local message
+ action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but immediately recoils in disgust upon touching [bite_target.p_them()]!"), span_userdanger("An intense wave of disgust washes over your body as you attempt to bite [bite_target]! What were you thinking?"))
+
+ // Warn target
+ to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but recoils in disgust just as [action_owner.p_they()] come[action_owner.p_s()] into contact with you!"))
+
+ // Stop grabbing
+ action_owner.stop_pulling()
+
+ // Add disgust
+ action_owner.adjust_disgust(10)
+
+ // Vomit
+ action_owner.vomit()
+
+ // Return with no further effects
+ return
+
+ // Warn the user and target, then return
+ to_chat(bite_target, span_warning("[action_owner] leans in to bite your [target_zone_name], but is warded off by your Allium Sativum!"))
+ to_chat(action_owner, span_warning("You sense that [bite_target] is protected by Allium Sativum, and refrain from biting [bite_target.p_them()]."))
+ return
+
+ // Define bite target's blood volume
+ var/target_blood_volume = bite_target.blood_volume
+
+ // Check for sufficient blood volume
+ if(target_blood_volume < BLOODFLEDGE_DRAIN_NUM)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("There's not enough blood in [bite_target]!"))
+ return
+
+ // Check if total blood would become too low
+ if((target_blood_volume - BLOODFLEDGE_DRAIN_NUM) <= BLOOD_VOLUME_OKAY)
+ // Check for a dumb user
+ if(action_owner_dumb)
+ // Warn the user, but allow
+ to_chat(action_owner, span_warning("You pay no attention to [bite_target]'s blood volume, and bite [bite_target.p_their()] [target_zone_name] without hesitation."))
+
+ // Check for aggressive grab
+ else if(action_owner.grab_state < GRAB_AGGRESSIVE)
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You sense that [bite_target] is running low on blood. You'll need a tighter grip on [bite_target.p_them()] to continue."))
+ return
+
+ // Check for pacifist
+ else if(HAS_TRAIT(action_owner, TRAIT_PACIFISM))
+ // Warn the user, then return
+ to_chat(action_owner, span_warning("You can't drain any more blood from [bite_target] without hurting [bite_target.p_them()]!"))
+ return
+
+ // Check for pierce immunity
+ if(HAS_TRAIT(bite_target, TRAIT_PIERCEIMMUNE))
+ // Display local chat message
+ action_owner.visible_message(span_danger("[action_owner] tries to bite down on [bite_target]'s [target_zone_name], but can't seem to pierce [bite_target.p_them()]!"), span_danger("You try to bite down on [bite_target]'s [target_zone_name], but are completely unable to pierce [bite_target.p_them()]!"))
+
+ // Warn bite target
+ to_chat(bite_target, span_userdanger("[action_owner] tries to bite your [target_zone_name], but is unable to piece you!"))
+
+ // Return without further effects
+ return
+
+ // Check for target zone special effects
+ if(target_zone_effects)
+ // Check if biting eyes or mouth
+ if((target_zone == BODY_ZONE_PRECISE_EYES) || (target_zone == BODY_ZONE_PRECISE_MOUTH))
+ // Check if biting target with proto-type face
+ // Snout type is a string that cannot use subtype search
+ if(findtext(bite_target.dna?.features["mam_snouts"], "Synthetic Lizard"))
+ // Display local chat message
+ action_owner.visible_message(span_notice("[action_owner]'s fangs clank harmlessly against [bite_target]'s face screen!"), span_notice("Your fangs clank harmlessly against [bite_target]'s face screen!"))
+
+ // Play glass tap sound
+ playsound(bite_target, 'sound/effects/Glasshit.ogg', 30, 1, -2)
+
+ // Start cooldown early to prevent spam
+ StartCooldown()
+
+ // Return without further effects
+ return
+
+ // Check for strange bite regions
+ switch(target_zone)
+ // Zone is eyes
+ if(BODY_ZONE_PRECISE_EYES)
+ // Define target's eyes
+ var/obj/item/organ/eyes/target_eyes = bite_target.getorganslot(ORGAN_SLOT_EYES)
+
+ // Check if eyes exist
+ if(target_eyes)
+ // Display warning
+ to_chat(bite_target, span_userdanger("Your [target_eyes] rupture in pain as [action_owner]'s fangs pierce their surface!"))
+
+ // Blur vision
+ bite_target.blur_eyes(10)
+
+ // Add organ damage
+ target_eyes.applyOrganDamage(20)
+
+ // Zone is mouth
+ if(BODY_ZONE_PRECISE_MOUTH)
+ // Cause temporary stuttering
+ bite_target.stuttering = 10
+
+ // Display local chat message
+ action_owner.visible_message(span_danger("[action_owner] bites down on [bite_target]'s [target_zone_name]!"), span_danger("You bite down on [bite_target]'s [target_zone_name]!"))
+
+ // Play a bite sound effect
+ playsound(action_owner, 'sound/weapons/bite.ogg', 30, 1, -2)
+
+ // Check if bite target species has blood
+ if(NOBLOOD in bite_target.dna?.species?.species_traits)
+ // Warn the user and target
+ to_chat(bite_target, span_warning("[action_owner] bit your [target_zone_name] in an attempt to drain your blood, but couldn't find any!"))
+ to_chat(action_owner, span_warning("[bite_target] doesn't have any blood to drink!"))
+
+ // Start cooldown early to prevent sound spam
+ StartCooldown()
+
+ // Return without effects
+ return
+
+ // Warn bite target
+ to_chat(bite_target, span_userdanger("[action_owner] has bitten your [target_zone_name], and is trying to drain your blood!"))
+
+ // Try to perform action timer
+ if(!do_after(action_owner, time_interact, target = bite_target))
+ // When failing
+ // Display a local chat message
+ action_owner.visible_message(span_danger("[action_owner]'s fangs are prematurely torn from [bite_target]'s [target_zone_name], spilling some of [bite_target.p_their()] blood!"), span_danger("Your fangs are prematurely torn from [bite_target]'s [target_zone_name], spilling some of [bite_target.p_their()] blood!"))
+
+ // Bite target "drops" 20% of the blood
+ // This creates large blood splatter
+ bite_target.bleed((BLOODFLEDGE_DRAIN_NUM*0.2), FALSE)
+
+ // Play splatter sound
+ playsound(get_turf(target), 'sound/effects/splat.ogg', 40, 1)
+
+ // Check for masochism
+ if(!HAS_TRAIT(bite_target, TRAIT_MASO))
+ // Force bite_target to play the scream emote
+ bite_target.emote("scream")
+
+ // Log the biting action failure
+ log_combat(action_owner,bite_target,"bloodfledge bitten (interrupted)")
+
+ // Add target's blood to quirk holder and themselves
+ bite_target.add_mob_blood(bite_target)
+ action_owner.add_mob_blood(bite_target)
+
+ // Check if body part is valid for bleeding
+ // This reuses the dismember-able check
+ if(target_zone_check)
+ // Cause minor bleeding
+ bite_bodypart.generic_bleedstacks += 2
+
+ // Apply minor damage
+ bite_bodypart.receive_damage(brute = rand(4,8), sharpness = SHARP_POINTY)
+
+ // Start cooldown early
+ // This is to prevent bite interrupt spam
+ StartCooldown()
+
+ // Return
+ return
+ else
+ // Variable for species with non-blood blood volumes
+ var/blood_valid = TRUE
+
+ // Variable for gaining blood volume
+ var/blood_transfer = FALSE
+
+ // Name of blood volume to be taken
+ // Action owner assumes blood until after drinking
+ var/blood_name = "blood"
+
+ // Check if target has exotic blood
+ if(bite_target.dna?.species?.exotic_bloodtype)
+ // Define blood types for owner and target
+ var/blood_type_owner = action_owner.dna?.species?.exotic_bloodtype
+ var/blood_type_target = bite_target.dna?.species?.exotic_bloodtype
+
+ // Define if blood types match
+ var/blood_type_match = (blood_type_owner == blood_type_target ? TRUE : FALSE)
+
+ // Check if types matched
+ if(blood_type_match)
+ // Add positive mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_exotic_match", /datum/mood_event/drank_exotic_matched)
+
+ // Switch for target's blood type
+ switch(blood_type_target)
+ // Synth blood
+ if("S")
+ // Mark blood as invalid
+ blood_valid = FALSE
+
+ // Set blood type name
+ blood_name = "coolant"
+
+ // Check if blood types match
+ if(blood_type_match)
+ // Allow transferring blood from this
+ blood_transfer = TRUE
+
+ // Blood types do not match
+ else
+ // Warn the user
+ to_chat(action_owner, span_warning("That didn't taste like blood at all..."))
+
+ // Add disgust
+ action_owner.adjust_disgust(2)
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_synth", /datum/mood_event/drankblood_synth)
+
+ // Slime blood
+ if("GEL")
+ // Mark blood as invalid
+ blood_valid = FALSE
+
+ // Allow transferring blood from this
+ blood_transfer = TRUE
+
+ // Set blood type name
+ blood_name = "slime"
+
+ // Check if blood types match
+ if(!blood_type_match)
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_slime", /datum/mood_event/drankblood_slime)
+
+ // Bug blood
+ if("BUG")
+ // Set blood type name
+ blood_name = "hemolymph"
+
+ // Check if blood types match
+ if(!blood_type_match)
+ // Mark blood as invalid
+ blood_valid = FALSE
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_insect", /datum/mood_event/drankblood_insect)
+
+ // Xenomorph blood
+ if("X*")
+ // Set blood type name
+ blood_name = "xeno blood"
+
+ // Check if blood types match
+ if(!blood_type_match)
+ // Mark blood as invalid
+ blood_valid = FALSE
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_xeno", /datum/mood_event/drankblood_xeno)
+
+ // Lizard blood
+ if("L")
+ // Set blood type name
+ blood_name = "reptilian blood"
+
+ // End of exotic blood checks
+
+ // Define user's remaining capacity to absorb blood
+ var/blood_volume_difference = BLOOD_VOLUME_MAXIMUM - action_owner.blood_volume
+ var/drained_blood = min(target_blood_volume, BLOODFLEDGE_DRAIN_NUM, blood_volume_difference)
+
+ // Transfer reagents from target to action owner
+ // Limited to a maximum 10% of bite amount (default 10u)
+ bite_target.reagents.trans_to(action_owner, (drained_blood*0.1))
+
+ // Alert the bite target and local user of success
+ // Yes, this is AFTER the message for non-valid blood
+ to_chat(bite_target, span_danger("[action_owner] has taken some of your [blood_name]!"))
+ to_chat(action_owner, span_notice("You've drained some of [bite_target]'s [blood_name]!"))
+
+ // Check if action owner received valid (nourishing) blood
+ if(blood_valid)
+ // Add blood reagent to reagent holder
+ blood_bank.add_reagent(/datum/reagent/blood/, drained_blood, bite_target.get_blood_data())
+
+ // Set reaction type to INGEST
+ blood_bank.reaction(action_owner, INGEST)
+
+ // Transfer reagent to action owner
+ blood_bank.trans_to(action_owner, drained_blood)
+
+ // Remove all reagents
+ blood_bank.remove_all()
+
+ // Check if blood transfer should occur
+ else if(blood_transfer)
+ // Check if action holder's blood volume limit was exceeded
+ if(action_owner.blood_volume >= BLOOD_VOLUME_MAXIMUM)
+ // Warn user
+ to_chat(action_owner, span_warning("You body cannot integrate any more [blood_name]. The remainder will be lost."))
+
+ // Blood volume limit was not exceeded
+ else
+ // Alert user
+ to_chat(action_owner, span_notice("You body integrates the [blood_name] directly, instead of processing it into nutrition."))
+
+ // Transfer blood directly
+ bite_target.transfer_blood_to(action_owner, drained_blood, TRUE)
+
+ // Set drain amount to none
+ // This prevents double removal
+ drained_blood = 0
+
+ // Valid blood was not received
+ // No direct blood transfer occurred
+ else
+ // Warn user of failure
+ to_chat(action_owner, span_warning("Your body cannot process the [blood_name] into nourishment!"))
+
+ // Remove blood from bite target
+ bite_target.blood_volume = clamp(target_blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
+
+ // Play a heartbeat sound effect
+ // This was changed to match bloodsucker
+ playsound(action_owner, 'sound/effects/singlebeat.ogg', 30, 1, -2)
+
+ // Log the biting action success
+ log_combat(action_owner,bite_target,"bloodfledge bitten (successfully), transferring [blood_name]")
+
+ // Mood events
+ // Check if bite target is dead or undead
+ if((bite_target.stat >= DEAD) || (bite_target.mob_biotypes & MOB_UNDEAD))
+ // Warn the user
+ to_chat(action_owner, span_warning("The rotten [blood_name] tasted foul."))
+
+ // Add disgust
+ action_owner.adjust_disgust(2)
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_dead", /datum/mood_event/drankblood_dead)
+
+ // Check if bite target's blood has been depleted
+ if(!bite_target.blood_volume)
+ // Warn the user
+ to_chat(action_owner, span_warning("You've depleted [bite_target]'s [blood_name] supply!"))
+
+ // Cause negative mood
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_killed", /datum/mood_event/drankkilled)
+
+ // Check if bite target has cursed blood
+ if(HAS_TRAIT(bite_target, TRAIT_CURSED_BLOOD))
+ // Check action owner for cursed blood
+ var/owner_cursed = HAS_TRAIT(action_owner, TRAIT_CURSED_BLOOD)
+
+ // Set chat message based on action owner's trait status
+ var/warn_message = (owner_cursed ? "You taste the unholy touch of a familiar curse in [bite_target]\'s blood." : "You experience a sensation of intense dread just after drinking from [bite_target]. Something about their blood feels... wrong.")
+
+ // Alert user in chat
+ to_chat(action_owner, span_notice(warn_message))
+
+ // Set mood type based on curse status
+ var/mood_type = (owner_cursed ? /datum/mood_event/drank_cursed_good : /datum/mood_event/drank_cursed_bad)
+
+ // Cause mood event
+ SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_cursed_blood", mood_type)
+
+ // Start cooldown
+ StartCooldown()
+
+// Action: Revive
+/datum/action/cooldown/bloodfledge/revive
+ name = "Fledgling Revive"
+ desc = "Expend all of your remaining energy to escape death."
+ button_icon_state = "power_strength"
+ cooldown_time = BLOODFLEDGE_COOLDOWN_REVIVE
+
+/datum/action/cooldown/bloodfledge/revive/Activate()
+ // Define mob
+ var/mob/living/carbon/human/action_owner = owner
+
+ // Early check for being dead
+ // Users are most likely to click this while alive
+ if(action_owner.stat != DEAD)
+ // Warn user in chat
+ to_chat(action_owner, "You can't use this ability while alive!")
+
+ // Return
+ return
+
+ // Define failure message
+ var/revive_failed
+
+ // Condition: Mob isn't in a closed coffin
+ // if(!istype(action_owner.loc, /obj/structure/closet/crate/coffin))
+ // revive_failed += "\n- You need to be in a closed coffin!"
+
+ // Condition: Insufficient nutrition (blood)
+ if(action_owner.nutrition <= NUTRITION_LEVEL_STARVING)
+ revive_failed += "\n- You don't have enough blood left!"
+
+ /*
+ * Removed to buff revivals
+ *
+ // Condition: Can be revived
+ // This is used by revive(), and must be checked here to prevent false feedback
+ if(!action_owner.can_be_revived())
+ revive_failed += "\n- Your body is too weak to sustain life!"
+
+ // Condition: Damage limit, brute
+ if(action_owner.getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE)
+ revive_failed += "\n- Your body is too battered!"
+
+ // Condition: Damage limit, burn
+ if(action_owner.getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE)
+ revive_failed += "\n- Your body is too badly burned!"
+ */
+
+ // Condition: Suicide
+ if(action_owner.suiciding)
+ revive_failed += "\n- You chose this path."
+
+ // Condition: No revivals
+ if(HAS_TRAIT(action_owner, TRAIT_NOCLONE))
+ revive_failed += "\n- You only had one chance."
+
+ // Condition: Demonic contract
+ if(action_owner.hellbound)
+ revive_failed += "\n- The soul pact must be honored."
+
+ // Check for failure
+ if(revive_failed)
+ // Set combined message
+ revive_failed = span_warning("You can't revive right now because: [revive_failed]")
+
+ // Alert user in chat of failure
+ to_chat(action_owner, revive_failed)
+
+ // Return
+ return
+
+ // Check if health is too low to use revive()
+ if(action_owner.health <= HEALTH_THRESHOLD_DEAD)
+ // Set health high enough to revive
+ // Based on defib.dm
+
+ // Define damage values
+ var/damage_brute = action_owner.getBruteLoss()
+ var/damage_burn = action_owner.getFireLoss()
+ var/damage_tox = action_owner.getToxLoss()
+ var/damage_oxy = action_owner.getOxyLoss()
+ var/damage_clone = action_owner.getCloneLoss()
+ var/damage_brain = action_owner.getOrganLoss(ORGAN_SLOT_BRAIN)
+
+ // Define total damage
+ var/damage_total = damage_brute + damage_burn + damage_tox + damage_oxy + damage_brain + damage_clone
+
+ // Define to prevent redundant math
+ var/health_half_crit = action_owner.health - HALFWAYCRITDEATH
+
+ // Adjust damage types
+ action_owner.adjustOxyLoss(health_half_crit * (damage_oxy / damage_total), 0)
+ action_owner.adjustToxLoss(health_half_crit * (damage_tox / damage_total), 0)
+ action_owner.adjustFireLoss(health_half_crit * (damage_burn / damage_total), 0)
+ action_owner.adjustBruteLoss(health_half_crit * (damage_brute / damage_total), 0)
+ action_owner.adjustCloneLoss(health_half_crit * (damage_clone / damage_total), 0)
+ action_owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, health_half_crit * (damage_brain / damage_total))
+
+ // Update health
+ action_owner.updatehealth()
+
+ // Check if revival is possible
+ // This is used by revive(), and must be checked here to prevent false feedback
+ if(!action_owner.can_be_revived())
+ // Warn user
+ to_chat(action_owner, span_warning("Despite your body's best attempts at mending, it remains too weak to revive! Something this terrible shouldn't be possible!"))
+
+ // Start cooldown anyway, since healing was performed
+ StartCooldown()
+
+ // Return without revival
+ return
+
+ // Define time dead
+ // Used for revive policy
+ var/time_dead = world.time - action_owner.timeofdeath
+
+ // Revive the action owner
+ action_owner.revive()
+
+ // Alert the user in chat of success
+ action_owner.visible_message(span_notice("An ominous energy radiates from the [action_owner.loc]..."), span_warning("You've expended all remaining blood to bring your body back to life!"))
+
+ // Play a haunted sound effect
+ playsound(action_owner, 'sound/hallucinations/growl1.ogg', 30, 1, -2)
+
+ // Remove all nutrition (blood)
+ action_owner.set_nutrition(0)
+
+ // Apply daze effect
+ action_owner.Daze(20)
+
+ // Define time limit for revival
+ // Determines memory loss, using defib time and policies
+ var/revive_time_limit = CONFIG_GET(number/defib_cmd_time_limit) * 10
+
+ // Define revive time threshold
+ // Late causes memory loss, according to policy
+ var/time_late = revive_time_limit && (time_dead > revive_time_limit)
+
+ // Define policy to use
+ var/list/policies = CONFIG_GET(keyed_list/policy)
+ var/time_policy = time_late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
+
+ // Check if policy exists
+ if(time_policy)
+ // Alert user in chat of policy
+ to_chat(action_owner, time_policy)
+
+ // Log the revival and effective policy
+ action_owner.log_message("revived using a vampire quirk ability after being dead for [time_dead] deciseconds. Considered [time_late? "late" : "memory-intact"] revival under configured policy limits.", LOG_GAME)
+
+ // Start cooldown
+ StartCooldown()
+
+#undef BLOODFLEDGE_DRAIN_NUM
+#undef BLOODFLEDGE_COOLDOWN_BITE
+#undef BLOODFLEDGE_COOLDOWN_REVIVE
+#undef BLOODFLEDGE_BANK_CAPACITY
diff --git a/modular_splurt/code/datums/traits/positive_quirks/breathless.dm b/modular_splurt/code/datums/traits/positive_quirks/breathless.dm
new file mode 100644
index 000000000000..e2b0ecdaaacc
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/breathless.dm
@@ -0,0 +1,24 @@
+/datum/quirk/breathless
+ name = "Breathless"
+ desc = "Whether due to genetic engineering, technology, or bluespace magic, you no longer require air to function. This also means that administering life-saving manuevers such as CPR would be impossible."
+ value = 3
+ medical_record_text = "Patient's biology demonstrates no need for breathing."
+ gain_text = span_notice("You no longer need to breathe.")
+ lose_text = span_notice("You need to breathe again...")
+ processing_quirk = TRUE
+
+/datum/quirk/breathless/add()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ ADD_TRAIT(H,TRAIT_NOBREATH,ROUNDSTART_TRAIT)
+
+/datum/quirk/breathless/remove()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ REMOVE_TRAIT(H,TRAIT_NOBREATH, ROUNDSTART_TRAIT)
+
+/datum/quirk/breathless/on_process()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ H.adjustOxyLoss(-3) /* Bandaid-fix for a defibrillator "bug",
+ Which causes oxy damage to stack for mobs that don't breathe */
diff --git a/modular_splurt/code/datums/traits/positive_quirks/cloth_eater.dm b/modular_splurt/code/datums/traits/positive_quirks/cloth_eater.dm
new file mode 100644
index 000000000000..db3110ea6b03
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/cloth_eater.dm
@@ -0,0 +1,6 @@
+/datum/quirk/cloth_eater
+ name = "Clothes Eater"
+ desc = "You can eat most apparel to gain a boost in mood, and to gain some nutrients. (Insects already have this.)"
+ value = 1
+ var/mood_category ="cloth_eaten"
+ mob_trait = TRAIT_CLOTH_EATER
diff --git a/modular_splurt/code/datums/traits/positive_quirks/dominant_aura.dm b/modular_splurt/code/datums/traits/positive_quirks/dominant_aura.dm
new file mode 100644
index 000000000000..d48257b2a4b2
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/dominant_aura.dm
@@ -0,0 +1,72 @@
+/datum/quirk/dominant_aura
+ name = "Dominant Aura"
+ desc = "Your mere presence is assertive enough to appear as powerful to other people, so much in fact that the weaker kind can't help but throw themselves at your feet at the snap of a finger."
+ value = 1
+ gain_text = span_notice("You feel like making someone your pet.")
+ lose_text = span_notice("You feel less assertive.")
+
+/datum/quirk/dominant_aura/add()
+ . = ..()
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
+ RegisterSignal(quirk_holder, COMSIG_MOB_EMOTE, PROC_REF(handle_snap))
+
+/datum/quirk/dominant_aura/remove()
+ . = ..()
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+ UnregisterSignal(quirk_holder, COMSIG_MOB_EMOTE)
+
+/datum/quirk/dominant_aura/proc/on_examine_holder(atom/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/sub = user
+ if(!sub.has_quirk(/datum/quirk/well_trained) || (sub == quirk_holder))
+ return
+
+ examine_list += span_lewd("\nYou can't make eye contact with [quirk_holder.p_them()] before flustering away!")
+ if(!TIMER_COOLDOWN_CHECK(user, COOLDOWN_DOMINANT_EXAMINE))
+ to_chat(quirk_holder, span_notice("\The [user] tries to look at you but immediately turns away with a red face..."))
+ TIMER_COOLDOWN_START(user, COOLDOWN_DOMINANT_EXAMINE, 10 SECONDS)
+ sub.dir = turn(get_dir(sub, quirk_holder), pick(-90, 90))
+ sub.emote("blush")
+
+/datum/quirk/dominant_aura/proc/handle_snap(datum/source, list/emote_args)
+ SIGNAL_HANDLER
+
+ . = FALSE
+ var/key = GLOB.emote_list[lowertext(emote_args[EMOTE_ACT])]
+ if(TIMER_COOLDOWN_CHECK(quirk_holder, COOLDOWN_DOMINANT_SNAP) || !findtext(key, "snap"))
+ return
+ for(var/mob/living/carbon/human/sub in hearers(DOMINANT_DETECT_RANGE, quirk_holder))
+ if(!sub.has_quirk(/datum/quirk/well_trained) || (sub == quirk_holder))
+ continue
+ var/good_x = "pet"
+ switch(sub.gender)
+ if(MALE)
+ good_x = "boy"
+ if(FEMALE)
+ good_x = "girl"
+ switch(key)
+ if("snap")
+ sub.dir = get_dir(sub, quirk_holder)
+ sub.emote(pick("blush", "pant"))
+ sub.visible_message(span_notice("\The [sub] turns shyly towards \the [quirk_holder]."),
+ span_lewd("You stare into \the [quirk_holder] submissively."))
+ if("snap2")
+ sub.dir = get_dir(sub, quirk_holder)
+ sub.KnockToFloor()
+ sub.emote(pick("blush", "pant"))
+ sub.visible_message(span_lewd("\The [sub] submissively throws [sub.p_them()]self on the floor."),
+ span_lewd("You throw yourself on the floor like a pathetic beast on [quirk_holder]'s command."))
+ if("snap3")
+ sub.KnockToFloor()
+ step(sub, get_dir(sub, quirk_holder))
+ sub.emote(pick("blush", "pant"))
+ sub.do_jitter_animation(30) //You're being moved anyways
+ sub.visible_message(span_lewd("\The [sub] crawls closer to \the [quirk_holder] on all fours, following [quirk_holder.p_their()] command."),
+ span_lewd("You get on your hands and knees and crawl towards \the [quirk_holder] like a good [good_x] would."))
+ . = TRUE
+
+ if(.)
+ TIMER_COOLDOWN_START(quirk_holder, COOLDOWN_DOMINANT_SNAP, DOMINANT_SNAP_COOLDOWN)
diff --git a/modular_splurt/code/datums/traits/positive_quirks/flutter.dm b/modular_splurt/code/datums/traits/positive_quirks/flutter.dm
new file mode 100644
index 000000000000..0ccdb68d65af
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/flutter.dm
@@ -0,0 +1,5 @@
+/datum/quirk/flutter
+ name = "Flutter"
+ desc = "You are able to move about freely in pressurized low-gravity environments be it through the use of wings, magic, or some other physiological nonsense."
+ value = 1
+ mob_trait = TRAIT_FLUTTER
diff --git a/modular_splurt/code/datums/traits/positive_quirks/hallowed.dm b/modular_splurt/code/datums/traits/positive_quirks/hallowed.dm
new file mode 100644
index 000000000000..6fb724fa2356
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/hallowed.dm
@@ -0,0 +1,44 @@
+/datum/quirk/hallowed
+ name = "Hallowed"
+ desc = "You have been blessed by a higher power or are otherwise imbued with holy energy in some way. Your divine presence drives away magic and the unholy! Holy water will restore your health."
+ value = 1 // Maybe up the cost if more is added later.
+ mob_trait = TRAIT_HALLOWED
+ medical_record_text = "Patient contains an unidentified hallowed material concentrated in their blood. Please consult a chaplain."
+ gain_text = span_notice("You feel holy energy starting to flow through your body.")
+ lose_text = span_notice("You feel your holy energy fading away...")
+
+/datum/quirk/hallowed/add()
+ // Define quirk mob.
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Give the holy trait.
+ ADD_TRAIT(quirk_mob, TRAIT_HOLY, "quirk_hallowed")
+
+ // Give the antimagic trait.
+ ADD_TRAIT(quirk_mob, TRAIT_ANTIMAGIC, "quirk_hallowed")
+
+ // Makes the user holy.
+ quirk_mob.mind.isholy = TRUE
+
+ // Add examine text.
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine_holder))
+
+/datum/quirk/hallowed/remove()
+ // Define quirk mob.
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove the holy trait.
+ REMOVE_TRAIT(quirk_mob, TRAIT_HOLY, "quirk_hallowed")
+
+ // Remove the antimagic trait.
+ REMOVE_TRAIT(quirk_mob, TRAIT_ANTIMAGIC, "quirk_hallowed")
+
+ // Makes the user not holy.
+ quirk_mob.mind.isholy = FALSE
+
+ // Remove examine text
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+// Quirk examine text.
+/datum/quirk/hallowed/proc/on_examine_holder(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ examine_list += "[quirk_holder.p_they(TRUE)] radiates divine power..."
diff --git a/modular_splurt/code/datums/traits/positive_quirks/hard_soles.dm b/modular_splurt/code/datums/traits/positive_quirks/hard_soles.dm
new file mode 100644
index 000000000000..ed2f236d6a62
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/hard_soles.dm
@@ -0,0 +1,4 @@
+// Quirk was made free to replace the Invisifiber Footwraps
+// This was the intention behind the item
+/datum/quirk/hard_soles
+ value = 0
diff --git a/modular_splurt/code/datums/traits/positive_quirks/radfiend.dm b/modular_splurt/code/datums/traits/positive_quirks/radfiend.dm
new file mode 100644
index 000000000000..b68955f9cb2c
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/radfiend.dm
@@ -0,0 +1,90 @@
+/datum/quirk/rad_fiend
+ name = "Rad Fiend"
+ desc = "You've been blessed by Cherenkov's warming light, causing you to emit a subtle glow at all times. Only -very- intense radiation is capable of penetrating your protective barrier."
+ value = 2
+ mob_trait = TRAIT_RAD_FIEND
+ gain_text = span_notice("You feel empowered by Cherenkov's glow.")
+ lose_text = span_notice("You realize that rads aren't so rad.")
+
+/datum/quirk/rad_fiend/add()
+ // Define quirk holder mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+ // Add glow control action
+ var/datum/action/rad_fiend/update_glow/quirk_action = new
+ quirk_action.Grant(quirk_mob)
+
+/datum/quirk/rad_fiend/remove()
+ // Define quirk holder mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove glow control action
+ var/datum/action/rad_fiend/update_glow/quirk_action = locate() in quirk_mob.actions
+ quirk_action.Remove(quirk_mob)
+
+ // Remove glow effect
+ quirk_mob.remove_filter("rad_fiend_glow")
+
+//
+// Quirk actions: Rad Fiend
+//
+
+/datum/action/rad_fiend
+ name = "Broken Rad Action"
+ desc = "Report this to a coder."
+ icon_icon = 'icons/effects/effects.dmi'
+ button_icon_state = "static"
+
+/datum/action/rad_fiend/update_glow
+ name = "Modify Glow"
+ desc = "Change your radioactive glow color."
+ button_icon_state = "blank"
+
+ // Glow color to use
+ var/glow_color = "#39ff14" // Neon green
+
+ // Thickness of glow outline
+ var/glow_range = 2
+
+
+/datum/action/rad_fiend/update_glow/Grant()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Add outline effect
+ action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
+
+/datum/action/rad_fiend/update_glow/Remove()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Remove glow
+ action_mob.remove_filter("rad_fiend_glow")
+
+/datum/action/rad_fiend/update_glow/Trigger()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Ask user for color input
+ var/input_color = input(action_mob, "Select a color to use for your glow outline.", "Select Glow Color", glow_color) as color|null
+
+ // Check if color input was given
+ // Reset to stored color when not given input
+ glow_color = (input_color ? input_color : glow_color)
+
+ // Ask user for range input
+ var/input_range = input(action_mob, "How much do you glow? Value may range between 1 to 2.", "Select Glow Range", glow_range) as num|null
+
+ // Check if range input was given
+ // Reset to stored color when not given input
+ // Input is clamped in the 1-4 range
+ glow_range = (input_range ? clamp(input_range, 1, 4) : glow_range)
+
+ // Update outline effect
+ action_mob.remove_filter("rad_fiend_glow")
+ action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
diff --git a/modular_splurt/code/datums/traits/positive_quirks/restorative_metabolism.dm b/modular_splurt/code/datums/traits/positive_quirks/restorative_metabolism.dm
new file mode 100644
index 000000000000..25ca16ada43a
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/restorative_metabolism.dm
@@ -0,0 +1,26 @@
+/datum/quirk/restorative_metabolism
+ name = "Restorative Metabolism"
+ desc = "Your body possesses a differentiated reconstutitive ability, allowing you to slowly recover from injuries. Note, however, that critical injuries, wounds or genetic damage will still require medical attention."
+ value = 3
+ mob_trait = TRAIT_RESTORATIVE_METABOLISM
+ gain_text = span_notice("You feel a surge of reconstutitive vitality coursing through your body...")
+ lose_text = span_notice("You sense your enhanced reconstutitive ability fading away...")
+ processing_quirk = TRUE
+
+/datum/quirk/restorative_metabolism/on_process()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+
+ // The only_organic flag allows robotic biotypes to not be excluded.
+ H.adjustBruteLoss(-0.5, only_organic = FALSE) //The healing factor will only regenerate fully if not burnt beyond a specific threshold.
+ H.adjustFireLoss(-0.25, only_organic = FALSE)
+ if(H.getBruteLoss() > 0 && H.getFireLoss() <= 50 || H.getFireLoss() > 0 && H.getFireLoss() <= 50)
+ H.adjustBruteLoss(-0.5, forced = TRUE, only_organic = FALSE)
+ H.adjustFireLoss(-0.25, forced = TRUE, only_organic = FALSE)
+ /* Doesn't heal robotic toxin damage (only_organic = FALSE not needed for tox),
+ another adjustToxLoss check with toxins_type = TOX_SYSCORRUPT,
+ (or just adding TOX_OMNI to the already existing one) could be added to heal robotic corruption,
+ but would make robotic species unbalanced.
+ */
+ else if (H.getToxLoss() <= 90)
+ H.adjustToxLoss(-0.3, forced = TRUE)
diff --git a/modular_splurt/code/datums/traits/positive_quirks/ropebunny.dm b/modular_splurt/code/datums/traits/positive_quirks/ropebunny.dm
new file mode 100644
index 000000000000..d8015bc36a04
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/ropebunny.dm
@@ -0,0 +1,47 @@
+/datum/quirk/ropebunny
+ name = "Rope Bunny"
+ desc = "You have mastered all forms of bondage! You can create bondage rope out of cloth, and bondage bolas out of bondage rope!"
+ value = 2
+
+/datum/quirk/ropebunny/add()
+ .=..()
+ var/mob/living/carbon/human/H = quirk_holder
+ if (!H)
+ return
+ var/datum/action/ropebunny/conversion/C = new
+ C.Grant(H)
+
+/datum/quirk/ropebunny/remove()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/datum/action/ropebunny/conversion/C = locate() in H.actions
+ C.Remove(H)
+ . = ..()
+
+/datum/action/ropebunny/conversion
+ name = "Convert Bondage"
+ desc = "Convert five cloth into bondage rope, or convert bondage ropes into bondage bolas."
+ icon_icon = 'modular_splurt/icons/obj/clothing/masks.dmi'
+ button_icon_state = "ballgag"
+
+/datum/action/ropebunny/conversion/Trigger()
+ .=..()
+ var/mob/living/carbon/human/H = owner
+ var/obj/item/I = H.get_active_held_item()
+
+ if(istype(I,/obj/item/stack/sheet/cloth))
+ var/obj/item/stack/sheet/cloth/C = I
+ if(C.amount < 5)
+ to_chat(H, span_warning("There is not enough cloth left to make more rope!"))
+ return
+ else
+ C.amount -= 5
+ new /obj/item/restraints/bondage_rope(H.loc)
+ to_chat(H, span_warning("You successfully create a set of bondage ropes."))
+ return
+ if(istype(I,/obj/item/restraints/bondage_rope))
+ new /obj/item/shibola(H.loc)
+ to_chat(H, span_warning("You successfully create a shibari bola."))
+ qdel(I)
+ return
+ else
+ to_chat(H, span_warning("You must either be holding cloth or a bondage rope to use this ability!"))
diff --git a/modular_splurt/code/datums/traits/positive_quirks/tough.dm b/modular_splurt/code/datums/traits/positive_quirks/tough.dm
new file mode 100644
index 000000000000..a54897182213
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/tough.dm
@@ -0,0 +1,15 @@
+/datum/quirk/tough
+ name = "Tough"
+ desc = "Your body is abnormally enduring and can take 10% more damage."
+ value = 2
+ medical_record_text = "Patient has an abnormally high capacity for injury."
+ gain_text = span_notice("You feel very sturdy.")
+ lose_text = span_notice("You feel less sturdy.")
+
+/datum/quirk/tough/add()
+ quirk_holder.maxHealth *= 1.1
+
+/datum/quirk/tough/remove()
+ if(!quirk_holder)
+ return
+ quirk_holder.maxHealth *= 0.909 //close enough
diff --git a/modular_splurt/code/datums/traits/positive_quirks/vacuum_resistance.dm b/modular_splurt/code/datums/traits/positive_quirks/vacuum_resistance.dm
new file mode 100644
index 000000000000..9d0e671c934c
--- /dev/null
+++ b/modular_splurt/code/datums/traits/positive_quirks/vacuum_resistance.dm
@@ -0,0 +1,19 @@
+/datum/quirk/vacuum_resistance
+ name = "Vacuum Resistance"
+ desc = "Your body, whether due to technology, magic, or genetic engineering - is specially adapted to withstand and operate in the vacuum of space. You may still need a source of breathable air, however."
+ value = 3
+ gain_text = span_notice("Your physique attunes to the silence of space, now able to operate in zero pressure.")
+ lose_text = span_notice("Your physiology reverts as your spacefaring gifts lay dormant once more.")
+ var/list/perks = list(TRAIT_RESISTCOLD, TRAIT_RESISTLOWPRESSURE, TRAIT_LOWPRESSURECOOLING)
+
+/datum/quirk/vacuum_resistance/add()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ for(var/perk in perks)
+ ADD_TRAIT(H, perk, ROUNDSTART_TRAIT)
+
+/datum/quirk/vacuum_resistance/remove()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ for(var/perk in perks)
+ REMOVE_TRAIT(H, perk, ROUNDSTART_TRAIT)
diff --git a/modular_splurt/code/datums/traits/trait_actions.dm b/modular_splurt/code/datums/traits/trait_actions.dm
deleted file mode 100644
index 3e42b4a883f3..000000000000
--- a/modular_splurt/code/datums/traits/trait_actions.dm
+++ /dev/null
@@ -1,1704 +0,0 @@
-#define BLOODFLEDGE_DRAIN_NUM 50
-#define BLOODFLEDGE_COOLDOWN_BITE 60 // Six seconds
-#define BLOODFLEDGE_COOLDOWN_REVIVE 3000 // Five minutes
-#define BLOODFLEDGE_BANK_CAPACITY (BLOODFLEDGE_DRAIN_NUM * 2)
-#define HYPNOEYES_COOLDOWN_NORMAL 3 SECONDS
-#define HYPNOEYES_COOLDOWN_BRAINWASH 30 SECONDS
-
-//
-// Quirk: Hypnotic Gaze
-//
-
-/datum/action/cooldown/hypnotize
- name = "Hypnotize"
- desc = "Stare deeply into someone's eyes, drawing them into a hypnotic slumber."
- button_icon_state = "Hypno_eye"
- icon_icon = 'modular_splurt/icons/mob/actions/lewd_actions/lewd_icons.dmi'
- background_icon_state = "bg_alien"
- transparent_when_unavailable = TRUE
- cooldown_time = HYPNOEYES_COOLDOWN_NORMAL
-
- // Should this create a brainwashed victim?
- // Enabled by using Mesmer Eyes with the quirk
- var/mode_brainwash = FALSE
-
- // Terminology used
- var/term_hypno = "hypnotize"
- var/term_suggest = "suggestion"
-
-/datum/action/cooldown/hypnotize/IsAvailable(silent)
- . = ..()
-
- // Check parent return
- if(!.)
- return FALSE
-
- // Check for carbon owner
- if(!iscarbon(owner))
- // Warn user and return
- to_chat(owner, span_warning("You shouldn't have this ability!"))
- return FALSE
-
- // Define action owner
- var/mob/living/carbon/human/action_owner = owner
-
- // Check if owner has eye protection
- if(action_owner.get_eye_protection())
- // Warn the user, then return
- to_chat(action_owner, span_warning("Your eyes need to be visible for this ability to work."))
- return FALSE
-
- // Define owner's eyes
- var/obj/item/organ/eyes/owner_eyes = owner.getorganslot(ORGAN_SLOT_EYES)
-
- // Check if eyes exist
- if(!istype(owner_eyes))
- // Warn the user, then return
- to_chat(action_owner, span_warning("You need eyes to use this ability!"))
- return FALSE
-
- // Check if owner is blind
- if(HAS_TRAIT(action_owner, TRAIT_BLIND))
- // Warn the user, then return
- to_chat(action_owner, span_warning("Your blind [owner_eyes] are of no use."))
- return FALSE
-
- // All checks passed
- return TRUE
-
-/datum/action/cooldown/hypnotize/proc/set_brainwash(set_to = FALSE)
- // Check if state will change
- if(mode_brainwash == set_to)
- // Do nothing
- return
-
- // Set new variable
- mode_brainwash = set_to
-
- // Define toggle message
- var/toggle_message = "experiences an error?"
-
- // Define log message type
- var/log_message_type = "somehow screwed up"
-
- // Check if brainwashing
- if(mode_brainwash)
- // Set ability text
- name = "Brainwash"
- desc = "Stare deeply into someone's eyes, and force them to become your loyal slave."
-
- // Set cooldown time
- cooldown_time = HYPNOEYES_COOLDOWN_BRAINWASH
-
- // Set terminology
- term_hypno = "brainwash"
- term_suggest = "command"
-
- // Set message suffix
- toggle_message = "suddenly feels more intense!"
-
- // Set log message type
- log_message_type = "GAINED"
-
- // Not brainwashing
- else
- // Set ability text
- name = "Hypnotize"
- desc = "Stare deeply into someone's eyes, drawing them into a hypnotic slumber."
-
- // Set cooldown time
- cooldown_time = HYPNOEYES_COOLDOWN_NORMAL
-
- // Set terminology
- term_hypno = "hypnotize"
- term_suggest = "suggestion"
-
- // Set message suffix
- toggle_message = "fades back to normal levels..."
-
- // Set log message type
- log_message_type = "LOST"
-
- // Update buttons
- owner.update_action_buttons()
-
- // Reset cooldown time
- StartCooldown()
-
- // Alert user
- to_chat(owner, span_mind_control("Your hypnotic power [toggle_message] You'll need time to adjust before using it again."))
-
- // Log interaction
- log_admin("[key_name(owner)] [log_message_type] hypnotic brainwashing powers.")
-
-/datum/action/cooldown/hypnotize/Activate()
- // Define action owner
- var/mob/living/carbon/human/action_owner = owner
-
- // Define target
- var/grab_target = action_owner.pulling
-
- // Check for target
- if(!grab_target)
- // Warn the user, then return
- to_chat(action_owner, span_warning("You need to grab someone first!"))
- return
-
- // Check for cyborg
- if(iscyborg(grab_target))
- // Warn the user, then return
- to_chat(action_owner, span_warning("You can't [term_hypno] a cyborg!"))
- return
-
- // Check for alien
- // Taken from eyedropper check
- /*
- if(isalien(grab_target))
- // Warn the user, then return
- to_chat(action_owner, span_warning("[grab_target] doesn\'t seem to have any eyes!"))
- return
- */
-
- // Check for carbon human target
- if(!ishuman(grab_target))
- // Warn the user, then return
- to_chat(action_owner, span_warning("That's not a valid creature!"))
- return
-
- // Check if target is alive
- if(!isliving(grab_target))
- // Warn the user, then return
- to_chat(action_owner, span_warning("You can't [term_hypno] the dead!"))
- return
-
- // Check for aggressive grab
- if(action_owner.grab_state < GRAB_AGGRESSIVE)
- // Warn the user, then return
- to_chat(action_owner, span_warning("You need a stronger grip before trying to [term_hypno] [grab_target]!"))
- return
-
- // Define target
- var/mob/living/carbon/human/action_target = grab_target
-
- // Check if target has a mind
- if(!action_target.mind)
- // Warn the user, then return
- to_chat(action_owner, span_warning("[grab_target] doesn't have a compatible mind!"))
- return
-
- /* Unused: Replaced by get_eye_protection
- // Check if target's eyes are obscured
- // ... by headwear
- if((action_target.head && action_target.head.flags_cover & HEADCOVERSEYES))
- // Warn the user, then return
- to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.head]."))
- return
-
- // ... by a mask
- else if((action_target.wear_mask && action_target.wear_mask.flags_cover & MASKCOVERSEYES))
- // Warn the user, then return
- to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.wear_mask]."))
- return
-
- // ... by glasses
- else if((action_target.glasses && action_target.glasses.flags_cover & GLASSESCOVERSEYES))
- // Warn the user, then return
- to_chat(action_owner, span_warning("[action_target]'s eyes are obscured by [action_target.glasses]."))
- return
- */
-
- // Check if target has eye protection
- if(action_target.get_eye_protection())
- // Warn the user, then return
- to_chat(action_owner, span_warning("You have difficulty focusing on [action_target]'s eyes due to some form of protection, and are left unable to [term_hypno] them."))
- to_chat(action_target, span_notice("[action_owner] stares intensely at you, but stops after a moment."))
- return
-
- // Check if target is blind
- if(HAS_TRAIT(action_target, TRAIT_BLIND))
- // Warn the user, then return
- to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, but see nothing but emptiness."))
- return
-
- // Check for anti-magic
- // This does not include TRAIT_HOLY
- if(action_target.anti_magic_check())
- // Warn the users, then return
- to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes. They stare back at you as if nothing had happened."))
- to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes for a moment. You sense nothing out of the ordinary from them."))
- return
-
- // Check client pref for hypno
- if(action_target.client?.prefs.cit_toggles & NEVER_HYPNO)
- // Warn the users, then return
- to_chat(action_owner, span_warning("You sense that [action_target] would rather not be hypnotized, and decide to respect their wishes."))
- to_chat(action_target, span_notice("[action_owner] stares into your eyes with a strange conviction, but turns away after a moment."))
- return
-
- // Check for mindshield implant
- if(HAS_TRAIT(action_target, TRAIT_MINDSHIELD))
- // Warn the users, then return
- to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, but hear a faint buzzing from [action_target.p_their()] head. It seems something is interfering."))
- to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes for a moment, before a buzzing sound emits from your head."))
- return
-
- // Check for sleep immunity
- // This is required for SetSleeping to trigger
- if(HAS_TRAIT(action_target, TRAIT_SLEEPIMMUNE))
- // Warn the users, then return
- to_chat(action_owner, span_warning("You stare deeply into [action_target]'s eyes, and see nothing but unrelenting energy. You won't be able to subdue [action_target.p_them()] in this state!"))
- to_chat(action_target, span_notice("[action_owner] stares intensely into your eyes, but sees something unusual about you..."))
- return
-
- // Check for sleep
- if(action_target.IsSleeping())
- // Warn the user, then return
- to_chat(action_owner, span_warning("You can't [term_hypno] [action_target] whilst [action_target.p_theyre()] asleep!"))
- return
-
- // Check for combat mode
- if(SEND_SIGNAL(action_target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
- // Warn the users, then return
- to_chat(action_owner, span_warning("[action_target] is acting too defensively! You'll need [action_target.p_them()] to lower [action_target.p_their()] guard first!"))
- to_chat(action_target, span_notice("[action_owner] tries to stare into your eyes, but can't get a read on you."))
- return
-
- // Display chat messages
- to_chat(action_owner, span_notice("You stare deeply into [action_target]'s eyes..."))
- to_chat(action_target, span_warning("[action_owner] stares intensely into your eyes..."))
-
- // Try to perform action timer
- if(!do_mob(action_owner, action_target, 5 SECONDS))
- // Action timer was interrupted
- // Warn the user, then return
- to_chat(action_owner, span_warning("You lose concentration on [action_target], and fail to [term_hypno] [action_target.p_them()]!"))
- to_chat(action_target, span_notice("[action_owner]'s gaze is broken prematurely, freeing you from any potential effects."))
- return
-
- // Define blank response
- var/input_consent
-
- // Check for non-consensual setting
- if(action_target.client?.prefs.nonconpref != "Yes")
- // Non-consensual is NOT enabled
- // Define warning suffix
- var/warning_target = (mode_brainwash ? "You will become a brainwashed victim, and be required to follow all orders given. [action_owner] accepts all responsibility for antagonistic orders." : "These are only suggestions, and you may disobey cases that strongly violate your character.")
-
- // Prompt target for consent response
- input_consent = alert(action_target, "Will you fall into a hypnotic stupor? This will allow [action_owner] to issue hypnotic [term_suggest]s. [warning_target]", "Hypnosis", "Yes", "No")
-
- // When consent is denied
- if(input_consent == "No")
- // Warn the users, then return
- to_chat(action_owner, span_warning("[action_target]'s attention breaks, despite the attempt to [term_hypno] [action_target.p_them()]! [action_target.p_they()] clearly don't want this!"))
- to_chat(action_target, span_notice("Your concentration breaks as you realize you have no interest in following [action_owner]'s words!"))
- return
-
- // Display local message
- action_target.visible_message(span_warning("[action_target] falls into a deep slumber!"), span_danger("Your eyelids gently shut as you fall into a deep slumber. All you can hear is [action_owner]'s voice as you commit to following all of their [term_suggest]s."))
-
- // Set sleeping
- action_target.SetSleeping(2 MINUTES)
-
- // Set drowsiness
- action_target.drowsyness = max(action_target.drowsyness, 40)
-
- // Define warning suffix
- var/warning_owner = (mode_brainwash ? "You are responsible for any antagonistic actions they take as a result of the brainwashing." : "This is only a suggestion, and [action_target.p_they()] may disobey if it violates [action_target.p_their()] character.")
-
- // Prompt action owner for response
- var/input_suggestion = input("What would you like to suggest [action_target] do? Leave blank to release [action_target.p_them()] instead. [warning_owner]", "Hypnotic [term_suggest]", null, null)
-
- // Check if input text exists
- if(!input_suggestion)
- // Alert user of no input
- to_chat(action_owner, "You decide not to give [action_target] a [term_suggest].")
-
- // Remove sleep, then return
- action_target.SetSleeping(0)
- return
-
- // Sanitize input text
- input_suggestion = sanitize(input_suggestion)
-
- // Check if brainwash mode
- if(mode_brainwash)
- // Create brainwash objective
- brainwash(action_target, input_suggestion)
-
- // Not in brainwash mode
- else
- // Display message to target
- to_chat(action_target, span_mind_control("...[input_suggestion]..."))
-
- // Start cooldown
- StartCooldown()
-
- // Display message to action owner
- to_chat(action_owner, "You whisper your [term_suggest] in a smooth calming voice to [action_target]")
-
- // Play a sound effect
- playsound(action_target, 'sound/magic/domain.ogg', 20, 1)
-
- // Display local message
- action_target.visible_message(span_warning("[action_target] wakes up from their deep slumber!"), span_danger("Your eyelids gently open as you see [action_owner]'s face staring back at you."))
-
- // Remove sleep, then return
- action_target.SetSleeping(0)
- return
-
-//
-// Quirk: Hydra Heads
-//
-
-/datum/action/innate/hydra
- name = "Switch head"
- desc = "Switch between each of the heads on your body."
- icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
- button_icon_state = "art_summon"
-
-/datum/action/innate/hydrareset
- name = "Reset speech"
- desc = "Go back to speaking as a whole."
- icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
- button_icon_state = "art_summon"
-
-/datum/action/innate/hydrareset/Activate()
- var/mob/living/carbon/human/hydra = owner
- hydra.real_name = hydra.name_archive
- hydra.visible_message(span_notice("[hydra.name] pushes all three heads forwards; they seem to be talking as a collective."), \
- span_notice("You are now talking as [hydra.name_archive]!"), ignored_mobs=owner)
-
-/datum/action/innate/hydra/Activate() //I hate this but its needed
- var/mob/living/carbon/human/hydra = owner
- var/list/names = splittext(hydra.name_archive,"-")
- var/selhead = input("Who would you like to speak as?","Heads:") in names
- hydra.real_name = selhead
- hydra.visible_message(span_notice("[hydra.name] pulls the rest of their heads back; and puts [selhead]'s forward."), \
- span_notice("You are now talking as [selhead]!"), ignored_mobs=owner)
-
-//
-// Quirk: Bloodsucker Fledgling / Vampire
-//
-
-// Basic action preset
-/datum/action/cooldown/bloodfledge
- name = "Broken Bloodfledge Ability"
- desc = "You shouldn't be seeing this!"
- button_icon_state = "power_torpor"
- background_icon_state = "vamp_power_off"
- buttontooltipstyle = "cult"
- icon_icon = 'icons/mob/actions/bloodsucker.dmi'
- button_icon = 'icons/mob/actions/bloodsucker.dmi'
- transparent_when_unavailable = TRUE
-
-// Basic can-use check
-/datum/action/cooldown/bloodfledge/IsAvailable(silent = FALSE)
- . = ..()
-
- // Check parent return
- if(!.)
- return FALSE
-
- // Check for carbon owner
- if(!iscarbon(owner))
- // Warn user and return
- to_chat(owner, span_warning("You shouldn't have this ability!"))
- return FALSE
-
- // Check vampire ability mob proc
- if(!owner.allow_vampiric_ability(silent = FALSE))
- return FALSE
-
- // Action can be used
- return TRUE
-
-// Action: Bite
-/datum/action/cooldown/bloodfledge/bite
- name = "Fledgling Bite"
- desc = "Sink your vampiric fangs into the person you are grabbing, and attempt to drink their blood."
- button_icon_state = "power_feed"
- cooldown_time = BLOODFLEDGE_COOLDOWN_BITE
- var/time_interact = 30
-
- // Reagent holder, used to change reaction type
- var/datum/reagents/blood_bank
-
-/datum/action/cooldown/bloodfledge/bite/Grant()
- . = ..()
-
- // Check for voracious
- if(HAS_TRAIT(owner, TRAIT_VORACIOUS))
- // Make times twice as fast
- cooldown_time *= 0.5
- time_interact*= 0.5
-
- // Create reagent holder
- blood_bank = new(BLOODFLEDGE_BANK_CAPACITY)
-
-/datum/action/cooldown/bloodfledge/bite/Activate()
- // Define action owner
- var/mob/living/carbon/action_owner = owner
-
- // Check for any grabbed target
- if(!action_owner.pulling)
- // Warn the user, then return
- to_chat(action_owner, span_warning("You need a victim first!"))
- return
-
- // Limit maximum nutrition
- if(action_owner.nutrition >= NUTRITION_LEVEL_FAT)
- // Warn the user, then return
- to_chat(action_owner, span_notice("You are too full to drain any more."))
- return
-
- // Limit maximum potential nutrition
- if(action_owner.nutrition + BLOODFLEDGE_DRAIN_NUM >= NUTRITION_LEVEL_FAT)
- // Warn the user, then return
- to_chat(action_owner, span_notice("You would become too full by draining any more blood."))
- return
-
- // Check for muzzle
- if(action_owner.is_muzzled())
- // Warn the user, then return
- to_chat(action_owner, span_notice("You can't bite things while muzzled!"))
- return
-
- // Check for covered mouth
- if(action_owner.is_mouth_covered())
- // Warn the user, then return
- to_chat(action_owner, span_notice("You can't bite things with your mouth covered!"))
- return
-
- // Define pulled target
- var/pull_target = action_owner.pulling
-
- // Define bite target
- var/mob/living/carbon/human/bite_target
-
- // Define if action owner is dumb
- var/action_owner_dumb = HAS_TRAIT(action_owner, TRAIT_DUMB)
-
- // Check if the target is carbon
- if(iscarbon(pull_target))
- // Set the bite target
- bite_target = pull_target
-
- // Or cocooned carbon
- else if(istype(pull_target,/obj/structure/arachnid/cocoon))
- // Define if cocoon has a valid target
- // This cannot use pull_target
- var/possible_cocoon_target = locate(/mob/living/carbon/human) in action_owner.pulling.contents
-
- // Check defined cocoon target
- if(possible_cocoon_target)
- // Set the bite target
- bite_target = possible_cocoon_target
-
- // Or a blood tomato
- else if(istype(pull_target,/obj/item/reagent_containers/food/snacks/grown/tomato/blood))
- // Set message based on dumbness
- var/message_tomato_suffix = (action_owner_dumb ? ", and absorb it\'s delicious vegan-friendly blood!" : "! It's not very nutritious.")
- // Warn the user, then return
- to_chat(action_owner, span_danger("You plunge your fangs into [pull_target][message_tomato_suffix]"))
- return
-
- // This doesn't actually interact with the item
-
- // Or none of the above
- else
- // Set message based on dumbness
- var/message_invalid_target = (action_owner_dumb ? "You bite at [pull_target], but nothing seems to happen" : "You can't drain blood from [pull_target]!")
- // Warn the user, then return
- to_chat(action_owner, span_warning(message_invalid_target))
- return
-
- // Define selected zone
- var/target_zone = action_owner.zone_selected
-
- // Check if target can be penetrated
- // Bypass pierce immunity so feedback can be provided later
- if(!bite_target.can_inject(action_owner, FALSE, target_zone, FALSE, TRUE))
- // Warn the user, then return
- to_chat(action_owner, span_warning("There\'s no exposed flesh or thin material in that region of [bite_target]'s body. You're unable to bite them!"))
- return
-
- // Check targeted body part
- var/obj/item/bodypart/bite_bodypart = bite_target.get_bodypart(target_zone)
-
- // Define zone name
- var/target_zone_name = "flesh"
-
- // Define if target zone has special effects
- var/target_zone_effects = FALSE
-
- // Define if zone should be checked
- // Uses dismember check to determine if it can be missing
- // Missing limbs are assumed to be dismembered
- var/target_zone_check = bite_bodypart?.can_dismember() || TRUE
-
- // Set zone name based on region
- // Also checks for some protections
- switch(target_zone)
- if(BODY_ZONE_HEAD)
- target_zone_name = "neck"
-
- if(BODY_ZONE_CHEST)
- target_zone_name = "shoulder"
-
- if(BODY_ZONE_L_ARM)
- target_zone_name = "left arm"
-
- if(BODY_ZONE_R_ARM)
- target_zone_name = "right arm"
-
- if(BODY_ZONE_L_LEG)
- target_zone_name = "left thigh"
-
- if(BODY_ZONE_R_LEG)
- target_zone_name = "right thigh"
-
- if(BODY_ZONE_PRECISE_EYES)
- // Check if eyes exist and are exposed
- if(!bite_target.has_eyes() == HAS_EXPOSED_GENITAL)
- // Warn user and return
- to_chat(action_owner, span_warning("You can't find [bite_target]'s eyes to bite them!"))
- return
-
- // Set region data normally
- target_zone_name = "eyes"
- target_zone_check = FALSE
- target_zone_effects = TRUE
-
- if(BODY_ZONE_PRECISE_MOUTH)
- // Check if mouth exists and is exposed
- if(!(bite_target.has_mouth() && bite_target.mouth_is_free()))
- to_chat(action_owner, span_warning("You can't find [bite_target]'s lips to bite them!"))
- return
-
- // Set region data normally
- target_zone_name = "lips"
- target_zone_check = FALSE
- target_zone_effects = TRUE
-
- if(BODY_ZONE_PRECISE_GROIN)
- target_zone_name = "groin"
- target_zone_check = FALSE
-
- if(BODY_ZONE_PRECISE_L_HAND)
- target_zone_name = "left wrist"
-
- if(BODY_ZONE_PRECISE_R_HAND)
- target_zone_name = "right wrist"
-
- if(BODY_ZONE_PRECISE_L_FOOT)
- target_zone_name = "left ankle"
-
- if(BODY_ZONE_PRECISE_R_FOOT)
- target_zone_name = "right ankle"
-
- // Check if target should be checked
- if(target_zone_check)
- // Check if bodypart exists
- if(!bite_bodypart)
- // Warn user and return
- to_chat(action_owner, span_warning("[bite_target] doesn't have a [target_zone_name] for you to bite!"))
- return
-
- // Check if bodypart is organic
- if(!bite_bodypart.is_organic_limb())
- // Display local message
- action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but is unable to penetrate the mechanical prosthetic!"), span_warning("You attempt to bite [bite_target]'s [target_zone_name], but can't penetrate the mechanical prosthetic!"))
-
- // Warn user
- to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but is unable to penetrate the mechanical prosthetic!"))
-
- // Play metal hit sound
- playsound(bite_target, "sound/effects/clang[pick(1,2)].ogg", 30, 1, -2)
-
- // Start cooldown early to prevent spam
- StartCooldown()
-
- // Return without further effects
- return
-
- // Check for anti-magic
- if(bite_target.anti_magic_check(FALSE, TRUE, FALSE, 0))
- // Check for a dumb user
- if(action_owner_dumb)
- // Display local message
- action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but bursts into flames just as [action_owner.p_they()] come[action_owner.p_s()] into contact with [bite_target.p_them()]!"), span_userdanger("Surges of pain course through your body as you attempt to bite [bite_target]! What were you thinking?"))
-
- // Warn target
- to_chat(bite_target, span_warning("[action_owner] tries to bite you, but bursts into flames just as [action_owner.p_they()] come[action_owner.p_s()] into contact with you!"))
-
- // Stop grabbing
- action_owner.stop_pulling()
-
- // Ignite action owner
- action_owner.adjust_fire_stacks(2)
- action_owner.IgniteMob()
-
- // Return with no further effects
- return
-
- // Warn the user and target, then return
- to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but stops before touching you!"))
- to_chat(action_owner, span_warning("[bite_target] is blessed! You stop just in time to avoid catching fire."))
- return
-
- // Check for garlic necklace or garlic in the bloodstream
- if(!blood_sucking_checks(bite_target, TRUE, TRUE))
- // Check for a dumb user
- if(action_owner_dumb)
- // Display local message
- action_owner.visible_message(span_danger("[action_owner] tries to bite [bite_target]'s [target_zone_name], but immediately recoils in disgust upon touching [bite_target.p_them()]!"), span_userdanger("An intense wave of disgust washes over your body as you attempt to bite [bite_target]! What were you thinking?"))
-
- // Warn target
- to_chat(bite_target, span_warning("[action_owner] tries to bite your [target_zone_name], but recoils in disgust just as [action_owner.p_they()] come[action_owner.p_s()] into contact with you!"))
-
- // Stop grabbing
- action_owner.stop_pulling()
-
- // Add disgust
- action_owner.adjust_disgust(10)
-
- // Vomit
- action_owner.vomit()
-
- // Return with no further effects
- return
-
- // Warn the user and target, then return
- to_chat(bite_target, span_warning("[action_owner] leans in to bite your [target_zone_name], but is warded off by your Allium Sativum!"))
- to_chat(action_owner, span_warning("You sense that [bite_target] is protected by Allium Sativum, and refrain from biting [bite_target.p_them()]."))
- return
-
- // Define bite target's blood volume
- var/target_blood_volume = bite_target.blood_volume
-
- // Check for sufficient blood volume
- if(target_blood_volume < BLOODFLEDGE_DRAIN_NUM)
- // Warn the user, then return
- to_chat(action_owner, span_warning("There's not enough blood in [bite_target]!"))
- return
-
- // Check if total blood would become too low
- if((target_blood_volume - BLOODFLEDGE_DRAIN_NUM) <= BLOOD_VOLUME_OKAY)
- // Check for a dumb user
- if(action_owner_dumb)
- // Warn the user, but allow
- to_chat(action_owner, span_warning("You pay no attention to [bite_target]'s blood volume, and bite [bite_target.p_their()] [target_zone_name] without hesitation."))
-
- // Check for aggressive grab
- else if(action_owner.grab_state < GRAB_AGGRESSIVE)
- // Warn the user, then return
- to_chat(action_owner, span_warning("You sense that [bite_target] is running low on blood. You'll need a tighter grip on [bite_target.p_them()] to continue."))
- return
-
- // Check for pacifist
- else if(HAS_TRAIT(action_owner, TRAIT_PACIFISM))
- // Warn the user, then return
- to_chat(action_owner, span_warning("You can't drain any more blood from [bite_target] without hurting [bite_target.p_them()]!"))
- return
-
- // Check for pierce immunity
- if(HAS_TRAIT(bite_target, TRAIT_PIERCEIMMUNE))
- // Display local chat message
- action_owner.visible_message(span_danger("[action_owner] tries to bite down on [bite_target]'s [target_zone_name], but can't seem to pierce [bite_target.p_them()]!"), span_danger("You try to bite down on [bite_target]'s [target_zone_name], but are completely unable to pierce [bite_target.p_them()]!"))
-
- // Warn bite target
- to_chat(bite_target, span_userdanger("[action_owner] tries to bite your [target_zone_name], but is unable to piece you!"))
-
- // Return without further effects
- return
-
- // Check for target zone special effects
- if(target_zone_effects)
- // Check if biting eyes or mouth
- if((target_zone == BODY_ZONE_PRECISE_EYES) || (target_zone == BODY_ZONE_PRECISE_MOUTH))
- // Check if biting target with proto-type face
- // Snout type is a string that cannot use subtype search
- if(findtext(bite_target.dna?.features["mam_snouts"], "Synthetic Lizard"))
- // Display local chat message
- action_owner.visible_message(span_notice("[action_owner]'s fangs clank harmlessly against [bite_target]'s face screen!"), span_notice("Your fangs clank harmlessly against [bite_target]'s face screen!"))
-
- // Play glass tap sound
- playsound(bite_target, 'sound/effects/Glasshit.ogg', 30, 1, -2)
-
- // Start cooldown early to prevent spam
- StartCooldown()
-
- // Return without further effects
- return
-
- // Check for strange bite regions
- switch(target_zone)
- // Zone is eyes
- if(BODY_ZONE_PRECISE_EYES)
- // Define target's eyes
- var/obj/item/organ/eyes/target_eyes = bite_target.getorganslot(ORGAN_SLOT_EYES)
-
- // Check if eyes exist
- if(target_eyes)
- // Display warning
- to_chat(bite_target, span_userdanger("Your [target_eyes] rupture in pain as [action_owner]'s fangs pierce their surface!"))
-
- // Blur vision
- bite_target.blur_eyes(10)
-
- // Add organ damage
- target_eyes.applyOrganDamage(20)
-
- // Zone is mouth
- if(BODY_ZONE_PRECISE_MOUTH)
- // Cause temporary stuttering
- bite_target.stuttering = 10
-
- // Display local chat message
- action_owner.visible_message(span_danger("[action_owner] bites down on [bite_target]'s [target_zone_name]!"), span_danger("You bite down on [bite_target]'s [target_zone_name]!"))
-
- // Play a bite sound effect
- playsound(action_owner, 'sound/weapons/bite.ogg', 30, 1, -2)
-
- // Check if bite target species has blood
- if(NOBLOOD in bite_target.dna?.species?.species_traits)
- // Warn the user and target
- to_chat(bite_target, span_warning("[action_owner] bit your [target_zone_name] in an attempt to drain your blood, but couldn't find any!"))
- to_chat(action_owner, span_warning("[bite_target] doesn't have any blood to drink!"))
-
- // Start cooldown early to prevent sound spam
- StartCooldown()
-
- // Return without effects
- return
-
- // Warn bite target
- to_chat(bite_target, span_userdanger("[action_owner] has bitten your [target_zone_name], and is trying to drain your blood!"))
-
- // Try to perform action timer
- if(!do_after(action_owner, time_interact, target = bite_target))
- // When failing
- // Display a local chat message
- action_owner.visible_message(span_danger("[action_owner]'s fangs are prematurely torn from [bite_target]'s [target_zone_name], spilling some of [bite_target.p_their()] blood!"), span_danger("Your fangs are prematurely torn from [bite_target]'s [target_zone_name], spilling some of [bite_target.p_their()] blood!"))
-
- // Bite target "drops" 20% of the blood
- // This creates large blood splatter
- bite_target.bleed((BLOODFLEDGE_DRAIN_NUM*0.2), FALSE)
-
- // Play splatter sound
- playsound(get_turf(target), 'sound/effects/splat.ogg', 40, 1)
-
- // Check for masochism
- if(!HAS_TRAIT(bite_target, TRAIT_MASO))
- // Force bite_target to play the scream emote
- bite_target.emote("scream")
-
- // Log the biting action failure
- log_combat(action_owner,bite_target,"bloodfledge bitten (interrupted)")
-
- // Add target's blood to quirk holder and themselves
- bite_target.add_mob_blood(bite_target)
- action_owner.add_mob_blood(bite_target)
-
- // Check if body part is valid for bleeding
- // This reuses the dismember-able check
- if(target_zone_check)
- // Cause minor bleeding
- bite_bodypart.generic_bleedstacks += 2
-
- // Apply minor damage
- bite_bodypart.receive_damage(brute = rand(4,8), sharpness = SHARP_POINTY)
-
- // Start cooldown early
- // This is to prevent bite interrupt spam
- StartCooldown()
-
- // Return
- return
- else
- // Variable for species with non-blood blood volumes
- var/blood_valid = TRUE
-
- // Variable for gaining blood volume
- var/blood_transfer = FALSE
-
- // Name of blood volume to be taken
- // Action owner assumes blood until after drinking
- var/blood_name = "blood"
-
- // Check if target has exotic blood
- if(bite_target.dna?.species?.exotic_bloodtype)
- // Define blood types for owner and target
- var/blood_type_owner = action_owner.dna?.species?.exotic_bloodtype
- var/blood_type_target = bite_target.dna?.species?.exotic_bloodtype
-
- // Define if blood types match
- var/blood_type_match = (blood_type_owner == blood_type_target ? TRUE : FALSE)
-
- // Check if types matched
- if(blood_type_match)
- // Add positive mood
- SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_exotic_match", /datum/mood_event/drank_exotic_matched)
-
- // Switch for target's blood type
- switch(blood_type_target)
- // Synth blood
- if("S")
- // Mark blood as invalid
- blood_valid = FALSE
-
- // Set blood type name
- blood_name = "coolant"
-
- // Check if blood types match
- if(blood_type_match)
- // Allow transferring blood from this
- blood_transfer = TRUE
-
- // Blood types do not match
- else
- // Warn the user
- to_chat(action_owner, span_warning("That didn't taste like blood at all..."))
-
- // Add disgust
- action_owner.adjust_disgust(2)
-
- // Cause negative mood
- SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_synth", /datum/mood_event/drankblood_synth)
-
- // Slime blood
- if("GEL")
- // Mark blood as invalid
- blood_valid = FALSE
-
- // Allow transferring blood from this
- blood_transfer = TRUE
-
- // Set blood type name
- blood_name = "slime"
-
- // Check if blood types match
- if(!blood_type_match)
- // Cause negative mood
- SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_slime", /datum/mood_event/drankblood_slime)
-
- // Bug blood
- if("BUG")
- // Set blood type name
- blood_name = "hemolymph"
-
- // Check if blood types match
- if(!blood_type_match)
- // Mark blood as invalid
- blood_valid = FALSE
-
- // Cause negative mood
- SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_insect", /datum/mood_event/drankblood_insect)
-
- // Xenomorph blood
- if("X*")
- // Set blood type name
- blood_name = "xeno blood"
-
- // Check if blood types match
- if(!blood_type_match)
- // Mark blood as invalid
- blood_valid = FALSE
-
- // Cause negative mood
- SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_xeno", /datum/mood_event/drankblood_xeno)
-
- // Lizard blood
- if("L")
- // Set blood type name
- blood_name = "reptilian blood"
-
- // End of exotic blood checks
-
- // Define user's remaining capacity to absorb blood
- var/blood_volume_difference = BLOOD_VOLUME_MAXIMUM - action_owner.blood_volume
- var/drained_blood = min(target_blood_volume, BLOODFLEDGE_DRAIN_NUM, blood_volume_difference)
-
- // Transfer reagents from target to action owner
- // Limited to a maximum 10% of bite amount (default 10u)
- bite_target.reagents.trans_to(action_owner, (drained_blood*0.1))
-
- // Alert the bite target and local user of success
- // Yes, this is AFTER the message for non-valid blood
- to_chat(bite_target, span_danger("[action_owner] has taken some of your [blood_name]!"))
- to_chat(action_owner, span_notice("You've drained some of [bite_target]'s [blood_name]!"))
-
- // Check if action owner received valid (nourishing) blood
- if(blood_valid)
- // Add blood reagent to reagent holder
- blood_bank.add_reagent(/datum/reagent/blood/, drained_blood, bite_target.get_blood_data())
-
- // Set reaction type to INGEST
- blood_bank.reaction(action_owner, INGEST)
-
- // Transfer reagent to action owner
- blood_bank.trans_to(action_owner, drained_blood)
-
- // Remove all reagents
- blood_bank.remove_all()
-
- // Check if blood transfer should occur
- else if(blood_transfer)
- // Check if action holder's blood volume limit was exceeded
- if(action_owner.blood_volume >= BLOOD_VOLUME_MAXIMUM)
- // Warn user
- to_chat(action_owner, span_warning("You body cannot integrate any more [blood_name]. The remainder will be lost."))
-
- // Blood volume limit was not exceeded
- else
- // Alert user
- to_chat(action_owner, span_notice("You body integrates the [blood_name] directly, instead of processing it into nutrition."))
-
- // Transfer blood directly
- bite_target.transfer_blood_to(action_owner, drained_blood, TRUE)
-
- // Set drain amount to none
- // This prevents double removal
- drained_blood = 0
-
- // Valid blood was not received
- // No direct blood transfer occurred
- else
- // Warn user of failure
- to_chat(action_owner, span_warning("Your body cannot process the [blood_name] into nourishment!"))
-
- // Remove blood from bite target
- bite_target.blood_volume = clamp(target_blood_volume - drained_blood, 0, BLOOD_VOLUME_MAXIMUM)
-
- // Play a heartbeat sound effect
- // This was changed to match bloodsucker
- playsound(action_owner, 'sound/effects/singlebeat.ogg', 30, 1, -2)
-
- // Log the biting action success
- log_combat(action_owner,bite_target,"bloodfledge bitten (successfully), transferring [blood_name]")
-
- // Mood events
- // Check if bite target is dead or undead
- if((bite_target.stat >= DEAD) || (bite_target.mob_biotypes & MOB_UNDEAD))
- // Warn the user
- to_chat(action_owner, span_warning("The rotten [blood_name] tasted foul."))
-
- // Add disgust
- action_owner.adjust_disgust(2)
-
- // Cause negative mood
- SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_dead", /datum/mood_event/drankblood_dead)
-
- // Check if bite target's blood has been depleted
- if(!bite_target.blood_volume)
- // Warn the user
- to_chat(action_owner, span_warning("You've depleted [bite_target]'s [blood_name] supply!"))
-
- // Cause negative mood
- SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_killed", /datum/mood_event/drankkilled)
-
- // Check if bite target has cursed blood
- if(HAS_TRAIT(bite_target, TRAIT_CURSED_BLOOD))
- // Check action owner for cursed blood
- var/owner_cursed = HAS_TRAIT(action_owner, TRAIT_CURSED_BLOOD)
-
- // Set chat message based on action owner's trait status
- var/warn_message = (owner_cursed ? "You taste the unholy touch of a familiar curse in [bite_target]\'s blood." : "You experience a sensation of intense dread just after drinking from [bite_target]. Something about their blood feels... wrong.")
-
- // Alert user in chat
- to_chat(action_owner, span_notice(warn_message))
-
- // Set mood type based on curse status
- var/mood_type = (owner_cursed ? /datum/mood_event/drank_cursed_good : /datum/mood_event/drank_cursed_bad)
-
- // Cause mood event
- SEND_SIGNAL(action_owner, COMSIG_ADD_MOOD_EVENT, "bloodfledge_drank_cursed_blood", mood_type)
-
- // Start cooldown
- StartCooldown()
-
-// Action: Revive
-/datum/action/cooldown/bloodfledge/revive
- name = "Fledgling Revive"
- desc = "Expend all of your remaining energy to escape death."
- button_icon_state = "power_strength"
- cooldown_time = BLOODFLEDGE_COOLDOWN_REVIVE
-
-/datum/action/cooldown/bloodfledge/revive/Activate()
- // Define mob
- var/mob/living/carbon/human/action_owner = owner
-
- // Early check for being dead
- // Users are most likely to click this while alive
- if(action_owner.stat != DEAD)
- // Warn user in chat
- to_chat(action_owner, "You can't use this ability while alive!")
-
- // Return
- return
-
- // Define failure message
- var/revive_failed
-
- // Condition: Mob isn't in a closed coffin
- // if(!istype(action_owner.loc, /obj/structure/closet/crate/coffin))
- // revive_failed += "\n- You need to be in a closed coffin!"
-
- // Condition: Insufficient nutrition (blood)
- if(action_owner.nutrition <= NUTRITION_LEVEL_STARVING)
- revive_failed += "\n- You don't have enough blood left!"
-
- /*
- * Removed to buff revivals
- *
- // Condition: Can be revived
- // This is used by revive(), and must be checked here to prevent false feedback
- if(!action_owner.can_be_revived())
- revive_failed += "\n- Your body is too weak to sustain life!"
-
- // Condition: Damage limit, brute
- if(action_owner.getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE)
- revive_failed += "\n- Your body is too battered!"
-
- // Condition: Damage limit, burn
- if(action_owner.getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE)
- revive_failed += "\n- Your body is too badly burned!"
- */
-
- // Condition: Suicide
- if(action_owner.suiciding)
- revive_failed += "\n- You chose this path."
-
- // Condition: No revivals
- if(HAS_TRAIT(action_owner, TRAIT_NOCLONE))
- revive_failed += "\n- You only had one chance."
-
- // Condition: Demonic contract
- if(action_owner.hellbound)
- revive_failed += "\n- The soul pact must be honored."
-
- // Check for failure
- if(revive_failed)
- // Set combined message
- revive_failed = span_warning("You can't revive right now because: [revive_failed]")
-
- // Alert user in chat of failure
- to_chat(action_owner, revive_failed)
-
- // Return
- return
-
- // Check if health is too low to use revive()
- if(action_owner.health <= HEALTH_THRESHOLD_DEAD)
- // Set health high enough to revive
- // Based on defib.dm
-
- // Define damage values
- var/damage_brute = action_owner.getBruteLoss()
- var/damage_burn = action_owner.getFireLoss()
- var/damage_tox = action_owner.getToxLoss()
- var/damage_oxy = action_owner.getOxyLoss()
- var/damage_clone = action_owner.getCloneLoss()
- var/damage_brain = action_owner.getOrganLoss(ORGAN_SLOT_BRAIN)
-
- // Define total damage
- var/damage_total = damage_brute + damage_burn + damage_tox + damage_oxy + damage_brain + damage_clone
-
- // Define to prevent redundant math
- var/health_half_crit = action_owner.health - HALFWAYCRITDEATH
-
- // Adjust damage types
- action_owner.adjustOxyLoss(health_half_crit * (damage_oxy / damage_total), 0)
- action_owner.adjustToxLoss(health_half_crit * (damage_tox / damage_total), 0)
- action_owner.adjustFireLoss(health_half_crit * (damage_burn / damage_total), 0)
- action_owner.adjustBruteLoss(health_half_crit * (damage_brute / damage_total), 0)
- action_owner.adjustCloneLoss(health_half_crit * (damage_clone / damage_total), 0)
- action_owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, health_half_crit * (damage_brain / damage_total))
-
- // Update health
- action_owner.updatehealth()
-
- // Check if revival is possible
- // This is used by revive(), and must be checked here to prevent false feedback
- if(!action_owner.can_be_revived())
- // Warn user
- to_chat(action_owner, span_warning("Despite your body's best attempts at mending, it remains too weak to revive! Something this terrible shouldn't be possible!"))
-
- // Start cooldown anyway, since healing was performed
- StartCooldown()
-
- // Return without revival
- return
-
- // Define time dead
- // Used for revive policy
- var/time_dead = world.time - action_owner.timeofdeath
-
- // Revive the action owner
- action_owner.revive()
-
- // Alert the user in chat of success
- action_owner.visible_message(span_notice("An ominous energy radiates from the [action_owner.loc]..."), span_warning("You've expended all remaining blood to bring your body back to life!"))
-
- // Play a haunted sound effect
- playsound(action_owner, 'sound/hallucinations/growl1.ogg', 30, 1, -2)
-
- // Remove all nutrition (blood)
- action_owner.set_nutrition(0)
-
- // Apply daze effect
- action_owner.Daze(20)
-
- // Define time limit for revival
- // Determines memory loss, using defib time and policies
- var/revive_time_limit = CONFIG_GET(number/defib_cmd_time_limit) * 10
-
- // Define revive time threshold
- // Late causes memory loss, according to policy
- var/time_late = revive_time_limit && (time_dead > revive_time_limit)
-
- // Define policy to use
- var/list/policies = CONFIG_GET(keyed_list/policy)
- var/time_policy = time_late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
-
- // Check if policy exists
- if(time_policy)
- // Alert user in chat of policy
- to_chat(action_owner, time_policy)
-
- // Log the revival and effective policy
- action_owner.log_message("revived using a vampire quirk ability after being dead for [time_dead] deciseconds. Considered [time_late? "late" : "memory-intact"] revival under configured policy limits.", LOG_GAME)
-
- // Start cooldown
- StartCooldown()
-
-//
-// Quirk: Werewolf
-//
-
-/datum/action/cooldown/werewolf
- name = "Werewolf Ability"
- desc = "Do something related to werewolves."
- icon_icon = 'modular_splurt/icons/mob/actions/misc_actions.dmi'
- button_icon_state = "Transform"
- check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUN | AB_CHECK_CONSCIOUS | AB_CHECK_ALIVE
- cooldown_time = 5 SECONDS
- transparent_when_unavailable = TRUE
-
-/datum/action/cooldown/werewolf/transform
- name = "Toggle Werewolf Form"
- desc = "Transform in or out of your wolf form."
- var/transformed = FALSE
- var/species_changed = FALSE
- var/werewolf_gender = "Lycan"
- var/list/old_features
-
-/datum/action/cooldown/werewolf/transform/Grant()
- . = ..()
-
- // Define carbon owner
- var/mob/living/carbon/action_owner_carbon = owner
-
- // Define parent quirk
- var/datum/quirk/werewolf/quirk_data = locate() in action_owner_carbon.roundstart_quirks
-
- // Check if data was copied
- if(!quirk_data)
- // Log error and return
- log_game("Failed to get species data for werewolf action!")
- return
-
- // Define stored features
- old_features = quirk_data.old_features.Copy()
-
- // Define action owner
- var/mob/living/carbon/human/action_owner = owner
-
- // Set species gendered name
- switch(action_owner.gender)
- if(MALE)
- werewolf_gender = "Wer"
- if(FEMALE)
- werewolf_gender = "Wīf"
- if(PLURAL)
- werewolf_gender = "Hie"
- if(NEUTER)
- werewolf_gender = "Þing"
-
-/datum/action/cooldown/werewolf/transform/Activate()
- // Define action owner
- var/mob/living/carbon/human/action_owner = owner
-
- // Check for restraints
- if(!CHECK_MOBILITY(action_owner, MOBILITY_USE))
- // Warn user, then return
- action_owner.visible_message(span_warning("You cannot transform while restrained!"))
- return
-
- // Define citadel organs
- var/obj/item/organ/genital/penis/organ_penis = action_owner.getorganslot(ORGAN_SLOT_PENIS)
- var/obj/item/organ/genital/breasts/organ_breasts = action_owner.getorganslot(ORGAN_SLOT_BREASTS)
- var/obj/item/organ/genital/vagina/organ_vagina = action_owner.getorganslot(ORGAN_SLOT_VAGINA)
-
- // Play shake animation
- action_owner.shake_animation(2)
-
- // Transform into wolf form
- if(!transformed)
- // Define current species type
- var/datum/species/owner_species = action_owner.dna.species.type
-
- // Check if species has changed
- if(old_features["species"] != owner_species)
- // Set old species
- old_features["species"] = owner_species
-
- // Define species prefix
- var/custom_species_prefix
-
- // Check if species is mammal (anthro)
- if(ismammal(action_owner))
- // Do nothing!
-
- // Check if species is already a mammal sub-type
- else if(ispath(owner_species, /datum/species/mammal))
- // Do nothing!
-
- // Check if species is a jelly
- else if(isjellyperson(action_owner))
- // Set species prefix
- custom_species_prefix = "Jelly "
-
- // Check if species is a jelly subtype
- else if(ispath(owner_species, /datum/species/jelly))
- // Set species prefix
- custom_species_prefix = "Slime "
-
- // Species is not a mammal
- else
- // Change species
- action_owner.set_species(/datum/species/mammal, 1)
-
- // Set species changed
- species_changed = TRUE
-
- // Set species features
- action_owner.dna.custom_species = "[custom_species_prefix][werewolf_gender]wulf"
- action_owner.dna.species.mutant_bodyparts["mam_tail"] = "Otusian"
- action_owner.dna.species.mutant_bodyparts["legs"] = "Digitigrade"
- action_owner.Digitigrade_Leg_Swap(FALSE)
- action_owner.dna.species.mutant_bodyparts["mam_snouts"] = "Sergal"
- action_owner.dna.features["mam_ears"] = "Jackal"
- action_owner.dna.features["mam_tail"] = "Otusian"
- action_owner.dna.features["mam_snouts"] = "Sergal"
- action_owner.dna.features["legs"] = "Digitigrade"
- action_owner.dna.features["insect_fluff"] = "Hyena"
- action_owner.update_size(clamp(get_size(action_owner) + 0.5, RESIZE_MICRO, RESIZE_MACRO))
- action_owner.set_bark("bark")
- if(old_features["taur"] != "None")
- action_owner.dna.features["taur"] = "Canine"
- if(!(action_owner.dna.species.species_traits.Find(DIGITIGRADE)))
- action_owner.dna.species.species_traits += DIGITIGRADE
- action_owner.update_body()
- action_owner.update_body_parts()
-
- // Update possible citadel organs
- if(organ_breasts)
- organ_breasts.color = "#[action_owner.dna.features["mcolor"]]"
- organ_breasts.update()
- if(organ_penis)
- organ_penis.shape = "Knotted"
- organ_penis.color = "#ff7c80"
- organ_penis.update()
- organ_penis.modify_size(6)
- if(organ_vagina)
- organ_vagina.shape = "Furred"
- organ_vagina.color = "#[action_owner.dna.features["mcolor"]]"
- organ_vagina.update()
-
- // Un-transform from wolf form
- else
- // Check if species was already mammal (anthro)
- if(!species_changed)
- // Do nothing!
-
- // Species was not a mammal
- else
- // Revert species
- action_owner.set_species(old_features["species"], TRUE)
-
- // Clear species changed flag
- species_changed = FALSE
-
- // Revert species trait
- action_owner.set_bark(old_features["bark"])
- action_owner.dna.custom_species = old_features["custom_species"]
- action_owner.dna.features["mam_ears"] = old_features["mam_ears"]
- action_owner.dna.features["mam_snouts"] = old_features["mam_snouts"]
- action_owner.dna.features["mam_tail"] = old_features["mam_tail"]
- action_owner.dna.features["legs"] = old_features["legs"]
- action_owner.dna.features["insect_fluff"] = old_features["insect_fluff"]
- action_owner.dna.species.eye_type = old_features["eye_type"]
- if(old_features["taur"] != "None")
- action_owner.dna.features["taur"] = old_features["taur"]
- if(old_features["legs"] == "Plantigrade")
- action_owner.dna.species.species_traits -= DIGITIGRADE
- action_owner.Digitigrade_Leg_Swap(TRUE)
- action_owner.dna.species.mutant_bodyparts["legs"] = old_features["legs"]
- action_owner.update_body()
- action_owner.update_body_parts()
- action_owner.update_size(clamp(get_size(action_owner) - 0.5, RESIZE_MICRO, RESIZE_MACRO))
-
- // Revert citadel organs
- if(organ_breasts)
- organ_breasts.color = "#[old_features["breasts_color"]]"
- organ_breasts.update()
- if(action_owner.has_penis())
- organ_penis.shape = old_features["cock_shape"]
- organ_penis.color = "#[old_features["cock_color"]]"
- organ_penis.update()
- organ_penis.modify_size(-6)
- if(action_owner.has_vagina())
- organ_vagina.shape = old_features["vag_shape"]
- organ_vagina.color = "#[old_features["vag_color"]]"
- organ_vagina.update()
- organ_vagina.update_size()
-
- // Set transformation message
- var/owner_p_their = action_owner.p_their()
- var/toggle_message = (!transformed ? "[action_owner] shivers, [owner_p_their] flesh bursting with a sudden growth of thick fur as [owner_p_their] features contort to that of a beast, fully transforming [action_owner.p_them()] into a werewolf!" : "[action_owner] shrinks, [owner_p_their] wolfish features quickly receding.")
-
- // Alert in local chat
- action_owner.visible_message(span_danger(toggle_message))
-
- // Toggle transformation state
- transformed = !transformed
-
- // Start cooldown
- StartCooldown()
-
- // Return success
- return TRUE
-
-//
-// Quirk: Gargoyle
-//
-
-/datum/action/gargoyle/transform
- name = "Transform"
- desc = "Transform into a statue, regaining energy in the process. Additionally, you will slowly heal while in statue form."
- icon_icon = 'icons/mob/actions/actions_changeling.dmi'
- button_icon_state = "ling_camouflage"
- var/obj/structure/statue/gargoyle/current = null
-
-
-/datum/action/gargoyle/transform/Trigger()
- .=..()
- var/mob/living/carbon/human/H = owner
- var/datum/quirk/gargoyle/T = locate() in H.roundstart_quirks
- if(!T.cooldown)
- if(!T.transformed)
- if(!isturf(H.loc))
- return 0
- var/obj/structure/statue/gargoyle/S = new(H.loc, H)
- S.name = "statue of [H.name]"
- H.bleedsuppress = 1
- S.copy_overlays(H)
- var/newcolor = list(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
- S.add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY)
- current = S
- T.transformed = 1
- T.cooldown = 30
- T.paused = 0
- S.dir = H.dir
- return 1
- else
- qdel(current)
- T.transformed = 0
- T.cooldown = 30
- T.paused = 0
- H.visible_message(span_warning("[H]'s skin rapidly softens, returning them to normal!"), span_userdanger("Your skin softens, freeing your movement once more!"))
- else
- to_chat(H, span_warning("You have transformed too recently; you cannot yet transform again!"))
- return 0
-
-/datum/action/gargoyle/check
- name = "Check"
- desc = "Check your current energy levels."
- icon_icon = 'icons/mob/actions/actions_clockcult.dmi'
- button_icon_state = "Linked Vanguard"
-
-/datum/action/gargoyle/check/Trigger()
- .=..()
- var/mob/living/carbon/human/H = owner
- var/datum/quirk/gargoyle/T = locate() in H.roundstart_quirks
- to_chat(H, span_warning("You have [T.energy]/100 energy remaining!"))
-
-/datum/action/gargoyle/pause
- name = "Preserve"
- desc = "Become near-motionless, thusly conserving your energy until you move from your current tile. Note, you will lose a chunk of energy when you inevitably move from your current position, so you cannot abuse this!"
- icon_icon = 'icons/mob/actions/actions_flightsuit.dmi'
- button_icon_state = "flightsuit_lock"
-
-/datum/action/gargoyle/pause/Trigger()
- .=..()
- var/mob/living/carbon/human/H = owner
- var/datum/quirk/gargoyle/T = locate() in H.roundstart_quirks
-
- if(!T.paused)
- T.paused = 1
- T.position = H.loc
- to_chat(H, span_warning("You are now conserving your energy; this effect will end the moment you move from your current position!"))
- return
- else
- to_chat(H, span_warning("You are already conserving your energy!"))
-
-//Quirk: Cosmetic Glow
-//Copy and pasted. Cry about it.
-/datum/action/cosglow
- name = "Broken Glow Action"
- desc = "Report this to a coder."
- icon_icon = 'icons/effects/effects.dmi'
- button_icon_state = "static"
-
-/datum/action/cosglow/update_glow
- name = "Modify Glow"
- desc = "Change your glow color."
- button_icon_state = "blank"
-
- // Glow color to use
- var/glow_color
-
- // Thickness of glow outline
- var/glow_range
-
- // Alpha of the glow outline
- var/glow_intensity
-
-/datum/action/cosglow/update_glow/Grant()
- . = ..()
-
- // Define user mob
- var/mob/living/carbon/human/action_mob = owner
-
- // Default glow intensity to 48 (in decimal)
- glow_intensity = "30"
-
- // Add outline effect
- if(glow_color && glow_range)
- action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color + glow_intensity, "size" = glow_range))
-
-/datum/action/cosglow/update_glow/Remove()
- . = ..()
-
- // Define user mob
- var/mob/living/carbon/human/action_mob = owner
-
- // Remove glow
- action_mob.remove_filter("rad_fiend_glow")
-
-/datum/action/cosglow/update_glow/Trigger()
- . = ..()
-
- // Define user mob
- var/mob/living/carbon/human/action_mob = owner
-
- // Ask user for color input
- var/input_color = input(action_mob, "Select a color to use for your glow outline.", "Select Glow Color", glow_color) as color|null
-
- // Check if color input was given
- // Reset to stored color when not given input
- glow_color = (input_color ? input_color : glow_color)
-
- // Ask user for range input
- var/input_range = input(action_mob, "How much do you glow? Value may range between 0 to 4. 0 disables glow.", "Select Glow Range", glow_range) as num|null
-
- // Check if range input was given
- // Disable glow if input is 0.
- // Reset to stored range when input is null.
- // Input is clamped in the 0-4 range
- glow_range = isnull(input_range) ? glow_range : clamp(input_range, 0, 4) //More customisable, so you know when you're looking at someone with Radfiend (doom) or a normal player.
-
- // Ask user for intensity input
- var/input_intensity = input(action_mob, "How intense is your glow? Value may range between 0 to 255. 0 disables glow.", "Select Glow Intensity", hex2num(glow_intensity)) as num|null
-
- // Check if intensity input was given and clamp it
- // If no input is given, reset to stored intensity
- var/intensity_clamped = isnull(input_intensity) ? hex2num(glow_intensity) : clamp(input_intensity, 0, 255)
-
- // Update glow intensity
- glow_intensity = num2hex(intensity_clamped)
-
- // Update outline effect
- if(glow_range && glow_color)
- action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color + glow_intensity, "size" = glow_range))
- else
- action_mob.remove_filter("rad_fiend_glow")
-
-//
-// Quirk: Rad Fiend
-//
-
-/datum/action/rad_fiend
- name = "Broken Rad Action"
- desc = "Report this to a coder."
- icon_icon = 'icons/effects/effects.dmi'
- button_icon_state = "static"
-
-/datum/action/rad_fiend/update_glow
- name = "Modify Glow"
- desc = "Change your radioactive glow color."
- button_icon_state = "blank"
-
- // Glow color to use
- var/glow_color = "#39ff14" // Neon green
-
- // Thickness of glow outline
- var/glow_range = 2
-
-
-/datum/action/rad_fiend/update_glow/Grant()
- . = ..()
-
- // Define user mob
- var/mob/living/carbon/human/action_mob = owner
-
- // Add outline effect
- action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
-
-/datum/action/rad_fiend/update_glow/Remove()
- . = ..()
-
- // Define user mob
- var/mob/living/carbon/human/action_mob = owner
-
- // Remove glow
- action_mob.remove_filter("rad_fiend_glow")
-
-/datum/action/rad_fiend/update_glow/Trigger()
- . = ..()
-
- // Define user mob
- var/mob/living/carbon/human/action_mob = owner
-
- // Ask user for color input
- var/input_color = input(action_mob, "Select a color to use for your glow outline.", "Select Glow Color", glow_color) as color|null
-
- // Check if color input was given
- // Reset to stored color when not given input
- glow_color = (input_color ? input_color : glow_color)
-
- // Ask user for range input
- var/input_range = input(action_mob, "How much do you glow? Value may range between 1 to 2.", "Select Glow Range", glow_range) as num|null
-
- // Check if range input was given
- // Reset to stored color when not given input
- // Input is clamped in the 1-4 range
- glow_range = (input_range ? clamp(input_range, 1, 4) : glow_range)
-
- // Update outline effect
- action_mob.remove_filter("rad_fiend_glow")
- action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
-
-/datum/action/ropebunny/conversion
- name = "Convert Bondage"
- desc = "Convert five cloth into bondage rope, or convert bondage ropes into bondage bolas."
- icon_icon = 'modular_splurt/icons/obj/clothing/masks.dmi'
- button_icon_state = "ballgag"
-
-/datum/action/ropebunny/conversion/Trigger()
- .=..()
- var/mob/living/carbon/human/H = owner
- var/obj/item/I = H.get_active_held_item()
-
- if(istype(I,/obj/item/stack/sheet/cloth))
- var/obj/item/stack/sheet/cloth/C = I
- if(C.amount < 5)
- to_chat(H, span_warning("There is not enough cloth left to make more rope!"))
- return
- else
- C.amount -= 5
- new /obj/item/restraints/bondage_rope(H.loc)
- to_chat(H, span_warning("You successfully create a set of bondage ropes."))
- return
- if(istype(I,/obj/item/restraints/bondage_rope))
- new /obj/item/shibola(H.loc)
- to_chat(H, span_warning("You successfully create a shibari bola."))
- qdel(I)
- return
- else
- to_chat(H, span_warning("You must either be holding cloth or a bondage rope to use this ability!"))
-
-/mob/living/proc/alterlimbs()
- set name = "Alter Limbs"
- set desc = "Remove or attach a limb!"
- set category = "IC"
- set src in view(usr.client)
-
- var/mob/living/carbon/human/U = usr
- var/mob/living/carbon/human/C = src
-
- var/obj/item/I = U.get_active_held_item()
- if(istype(I,/obj/item/bodypart))
- var/obj/item/bodypart/L = I
- if(!C.Adjacent(U))
- to_chat(U, span_warning("You must be adjacent to [C] to do this!"))
- return
- if(C.get_bodypart(L.body_zone))
- to_chat(U, span_warning("[C] already has a limb attached there!"))
- return
- C.visible_message(span_warning("[U] is attempting to attach [L] onto [C]!"), span_userdanger("[U] is attempting to re-attach one of your limbs!"))
- if(do_after(U, 40, target = C) && C.Adjacent(U))
- L.attach_limb(C)
- C.visible_message(span_warning("[U] successfully attaches [L] onto [C]"), span_userdanger("[U] has successfully attached a [L.name] onto you; you can use that limb again!"))
- return
- else
- to_chat(U, span_warning("You and [C] must both stand still for you to remove one of their limbs!"))
- return
- else
- if(!C.Adjacent(U))
- to_chat(U, span_warning("You must be adjacent to [C] to do this!"))
- return
- if(U.zone_selected == BODY_ZONE_CHEST || U.zone_selected == BODY_ZONE_HEAD)
- to_chat(U, span_warning("You must target either an arm or a leg!"))
- return
- if(U.zone_selected == BODY_ZONE_PRECISE_GROIN || U.zone_selected == BODY_ZONE_PRECISE_EYES || U.zone_selected == BODY_ZONE_PRECISE_MOUTH)
- to_chat(U, span_warning("There is no limb here; select an arm or a leg!"))
- return
- if(!C.get_bodypart(U.zone_selected))
- to_chat(U, span_warning("They are already missing that limb!"))
- return
- C.visible_message(span_warning("[U] is attempting to remove one of [C]'s limbs!"), span_userdanger("[U] is attempting to disconnect one of your limbs!"))
- var/obj/item/bodypart/B = C.get_bodypart(U.zone_selected)
- if(C.Adjacent(U) && do_after(U, 40, target = C))
- var/obj/item/bodypart/D = C.get_bodypart(U.zone_selected)
- if(B != D)
- to_chat(U, span_warning("You cannot target a different limb while already removing another!"))
- return
- D.drop_limb()
- C.update_equipment_speed_mods()
- C.visible_message(span_warning("[U] smoothly disconnects [C]'s [D.name]!"), span_userdanger("[U] has forcefully disconnected your [D.name]!"))
- return
- else
- to_chat(U, span_warning("You and [C] must both stand still for you to remove one of their limbs!"))
- return
-
-/datum/action/cooldown/toggle_distant
- name = "Toggle Distant"
- desc = "Allows you to let your headpat-guard down, or put it back up."
- icon_icon = 'modular_splurt/icons/mob/actions/lewd_actions/lewd_icons.dmi'
- button_icon_state = "pain_max"
-
-/datum/action/cooldown/toggle_distant/Activate()
- var/mob/living/carbon/human/action_owner = owner
-
- if(HAS_TRAIT(action_owner, TRAIT_DISTANT))
- REMOVE_TRAIT(action_owner, TRAIT_DISTANT, ROUNDSTART_TRAIT)
- to_chat(action_owner, span_notice("You let your headpat-guard down!"))
- else
- ADD_TRAIT(action_owner, TRAIT_DISTANT, ROUNDSTART_TRAIT)
- to_chat(action_owner, span_warning("You let your headpat-guard up!"))
-
-#undef HYPNOEYES_COOLDOWN_NORMAL
-#undef HYPNOEYES_COOLDOWN_BRAINWASH
diff --git a/modular_splurt/code/datums/traits/trait_edits.dm b/modular_splurt/code/datums/traits/trait_edits.dm
new file mode 100644
index 000000000000..5ae601b25082
--- /dev/null
+++ b/modular_splurt/code/datums/traits/trait_edits.dm
@@ -0,0 +1,20 @@
+// This file is maintained for minor edits of upstream quirks.
+// Place new quirks and larger edits into the sub-folders.
+
+/datum/quirk/empath
+ value = 1 // Was 2
+
+/datum/quirk/freerunning
+ value = 1 // Was 2
+
+/datum/quirk/quick_step
+ value = 1 // Was 2
+
+/datum/quirk/photographer
+ value = 0 // Was 1
+
+/datum/quirk/skittish
+ value = 1 // Was 2
+
+/datum/quirk/voracious
+ desc = "Nothing gets between you and your food. You eat twice as fast as everyone else, and won't get sad by being fat!"
diff --git a/tgstation.dme b/tgstation.dme
index 7c0826cab877..72ef4b60c82c 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -4542,11 +4542,57 @@
#include "modular_splurt\code\datums\ruins\space.dm"
#include "modular_splurt\code\datums\ruins\station.dm"
#include "modular_splurt\code\datums\status_effects\pregnancy.dm"
-#include "modular_splurt\code\datums\traits\defines.dm"
-#include "modular_splurt\code\datums\traits\good.dm"
-#include "modular_splurt\code\datums\traits\negative.dm"
-#include "modular_splurt\code\datums\traits\neutral.dm"
-#include "modular_splurt\code\datums\traits\trait_actions.dm"
+#include "modular_splurt\code\datums\traits\trait_edits.dm"
+#include "modular_splurt\code\datums\traits\negative_quirks\dumb_for_cum.dm"
+#include "modular_splurt\code\datums\traits\negative_quirks\flimsy.dm"
+#include "modular_splurt\code\datums\traits\negative_quirks\hungry.dm"
+#include "modular_splurt\code\datums\traits\negative_quirks\hypersensitive.dm"
+#include "modular_splurt\code\datums\traits\negative_quirks\illiterate.dm"
+#include "modular_splurt\code\datums\traits\negative_quirks\no_guns.dm"
+#include "modular_splurt\code\datums\traits\negative_quirks\social_anxiety.dm"
+#include "modular_splurt\code\datums\traits\negative_quirks\thirsty.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\body_morpher.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\choke_slut.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\cosglow.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\cum_plus.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\cum_sniff.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\cursed_blood.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\fluid_infuser.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\gargoyle.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\headpat_hater.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\headpat_slut.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\hydra.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\hypnotic_gaze.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\incubus.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\jiggly_ass.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\kiss_slut.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\masked_mook.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\messy.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\modularlimbs.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\nudist.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\overweight.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\pharmacokinesis.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\steel_ass.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\storage_concealment.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\succubus.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\undead.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\vegetarian.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\well_trained.dm"
+#include "modular_splurt\code\datums\traits\neutral_quirks\werewolf.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\arachnid.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\ash_resistance.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\bloodfledge.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\breathless.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\cloth_eater.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\dominant_aura.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\flutter.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\hallowed.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\hard_soles.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\radfiend.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\restorative_metabolism.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\ropebunny.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\tough.dm"
+#include "modular_splurt\code\datums\traits\positive_quirks\vacuum_resistance.dm"
#include "modular_splurt\code\game\atoms.dm"
#include "modular_splurt\code\game\atoms_movable.dm"
#include "modular_splurt\code\game\data_huds.dm"
From 28e7b99472a8689d2dca649821444b6e2f1a8d0c Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Mon, 4 Nov 2024 14:48:01 -0500
Subject: [PATCH 07/16] Modularize Sheltered edit
Modularizes the edits to Sandstorm's quirk Sheltered
---
modular_sand/code/datums/traits/negative.dm | 8 ++++----
modular_splurt/code/datums/traits/trait_edits.dm | 6 ++++++
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/modular_sand/code/datums/traits/negative.dm b/modular_sand/code/datums/traits/negative.dm
index 2d238f1589e1..c093f5bb8f13 100644
--- a/modular_sand/code/datums/traits/negative.dm
+++ b/modular_sand/code/datums/traits/negative.dm
@@ -1,11 +1,11 @@
/datum/quirk/sheltered
name = "Sheltered"
- desc = "For one reason or another, you either can't or haven't learned the common tongue."
+ desc = "You never learned galactic common."
value = 0
mob_trait = TRAIT_SHELTERED
- gain_text = span_danger("The words of others begin to blur together...")
- lose_text = span_notice("You start putting together what people are saying!")
- medical_record_text = "Patient has shown an inability to use common speaking languages."
+ gain_text = span_danger("You do not understand galactic common.")
+ lose_text = span_notice("You start to put together what people are saying in galactic common.")
+ medical_record_text = "Patient looks perplexed when questioned in galactic common."
/datum/quirk/sheltered/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
diff --git a/modular_splurt/code/datums/traits/trait_edits.dm b/modular_splurt/code/datums/traits/trait_edits.dm
index 5ae601b25082..27d691842d62 100644
--- a/modular_splurt/code/datums/traits/trait_edits.dm
+++ b/modular_splurt/code/datums/traits/trait_edits.dm
@@ -18,3 +18,9 @@
/datum/quirk/voracious
desc = "Nothing gets between you and your food. You eat twice as fast as everyone else, and won't get sad by being fat!"
+
+/datum/quirk/sheltered
+ desc = "For one reason or another, you either can't or haven't learned the common tongue."
+ gain_text = span_danger("The words of others begin to blur together...")
+ lose_text = span_notice("You start putting together what people are saying!")
+ medical_record_text = "Patient has shown an inability to use common speaking languages."
From 685221b8403f706f88cda9f19e47ea655489e1bc Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Mon, 4 Nov 2024 15:05:49 -0500
Subject: [PATCH 08/16] Remove invalid quirk
Removes a mistakenly added reference to a new-codebase quirk.
---
.../code/datums/traits/positive_quirks/hard_soles.dm | 4 ----
tgstation.dme | 1 -
2 files changed, 5 deletions(-)
delete mode 100644 modular_splurt/code/datums/traits/positive_quirks/hard_soles.dm
diff --git a/modular_splurt/code/datums/traits/positive_quirks/hard_soles.dm b/modular_splurt/code/datums/traits/positive_quirks/hard_soles.dm
deleted file mode 100644
index ed2f236d6a62..000000000000
--- a/modular_splurt/code/datums/traits/positive_quirks/hard_soles.dm
+++ /dev/null
@@ -1,4 +0,0 @@
-// Quirk was made free to replace the Invisifiber Footwraps
-// This was the intention behind the item
-/datum/quirk/hard_soles
- value = 0
diff --git a/tgstation.dme b/tgstation.dme
index 72ef4b60c82c..5c173c5b341e 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -4587,7 +4587,6 @@
#include "modular_splurt\code\datums\traits\positive_quirks\dominant_aura.dm"
#include "modular_splurt\code\datums\traits\positive_quirks\flutter.dm"
#include "modular_splurt\code\datums\traits\positive_quirks\hallowed.dm"
-#include "modular_splurt\code\datums\traits\positive_quirks\hard_soles.dm"
#include "modular_splurt\code\datums\traits\positive_quirks\radfiend.dm"
#include "modular_splurt\code\datums\traits\positive_quirks\restorative_metabolism.dm"
#include "modular_splurt\code\datums\traits\positive_quirks\ropebunny.dm"
From 073c86d3c8aeb44583195583b4d1c5984cdf4d82 Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Tue, 5 Nov 2024 08:30:45 -0500
Subject: [PATCH 09/16] Fix missing trailing newlines
Adds missing trailing newlines to two quirk files.
---
modular_splurt/code/datums/traits/neutral_quirks/incubus.dm | 2 +-
modular_splurt/code/datums/traits/neutral_quirks/succubus.dm | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/incubus.dm b/modular_splurt/code/datums/traits/neutral_quirks/incubus.dm
index fd05277c79f6..2cf38ba994da 100644
--- a/modular_splurt/code/datums/traits/neutral_quirks/incubus.dm
+++ b/modular_splurt/code/datums/traits/neutral_quirks/incubus.dm
@@ -21,4 +21,4 @@
/datum/quirk/incubus/on_process()
. = ..()
var/mob/living/carbon/human/H = quirk_holder
- H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
\ No newline at end of file
+ H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/succubus.dm b/modular_splurt/code/datums/traits/neutral_quirks/succubus.dm
index 205ceee2577a..8f93f2c24a3a 100644
--- a/modular_splurt/code/datums/traits/neutral_quirks/succubus.dm
+++ b/modular_splurt/code/datums/traits/neutral_quirks/succubus.dm
@@ -21,4 +21,4 @@
/datum/quirk/succubus/on_process()
. = ..()
var/mob/living/carbon/human/H = quirk_holder
- H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
\ No newline at end of file
+ H.adjust_nutrition(-0.09)//increases their nutrition loss rate to encourage them to gain a partner they can essentially leech off of
From 8ba097e2d4e2fbb58a7550639ba7db8c2fa4e0d0 Mon Sep 17 00:00:00 2001
From: SPLURT Bot <97993072+SPLURT-Station-Bot@users.noreply.github.com>
Date: Sat, 9 Nov 2024 22:49:28 +0000
Subject: [PATCH 10/16] Automatic changelog generation for PR #1210 [ci skip]
---
html/changelogs/AutoChangeLog-pr-1210.yml | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 html/changelogs/AutoChangeLog-pr-1210.yml
diff --git a/html/changelogs/AutoChangeLog-pr-1210.yml b/html/changelogs/AutoChangeLog-pr-1210.yml
new file mode 100644
index 000000000000..c3da3ad6da4e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-1210.yml
@@ -0,0 +1,4 @@
+author: LeDrascol
+delete-after: true
+changes:
+ - rscadd: 'Added a new quirk: Sanguine Metabolism (variant of Bloodsucker Fledgling)'
From 4da20799de41da9d5157d8cc97a1f680d9e33258 Mon Sep 17 00:00:00 2001
From: Changelogs
Date: Sat, 9 Nov 2024 22:51:00 +0000
Subject: [PATCH 11/16] Automatic changelog compile [ci skip]
---
html/changelogs/AutoChangeLog-pr-1210.yml | 4 ----
html/changelogs/archive/2024-11.yml | 3 +++
2 files changed, 3 insertions(+), 4 deletions(-)
delete mode 100644 html/changelogs/AutoChangeLog-pr-1210.yml
diff --git a/html/changelogs/AutoChangeLog-pr-1210.yml b/html/changelogs/AutoChangeLog-pr-1210.yml
deleted file mode 100644
index c3da3ad6da4e..000000000000
--- a/html/changelogs/AutoChangeLog-pr-1210.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: LeDrascol
-delete-after: true
-changes:
- - rscadd: 'Added a new quirk: Sanguine Metabolism (variant of Bloodsucker Fledgling)'
diff --git a/html/changelogs/archive/2024-11.yml b/html/changelogs/archive/2024-11.yml
index da1590628128..e0625065dc92 100644
--- a/html/changelogs/archive/2024-11.yml
+++ b/html/changelogs/archive/2024-11.yml
@@ -3,3 +3,6 @@
- server: Server can now be compiled on byond 515.1543
MosleyTheMalO:
- bugfix: Fixes the snap on dominant aura
+2024-11-09:
+ LeDrascol:
+ - rscadd: 'Added a new quirk: Sanguine Metabolism (variant of Bloodsucker Fledgling)'
From d8d3f5a4440ccbd08cbfd4933756daa7c9d846d8 Mon Sep 17 00:00:00 2001
From: SPLURT Bot <97993072+SPLURT-Station-Bot@users.noreply.github.com>
Date: Sat, 9 Nov 2024 22:52:45 +0000
Subject: [PATCH 12/16] Automatic changelog generation for PR #1212 [ci skip]
---
html/changelogs/AutoChangeLog-pr-1212.yml | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 html/changelogs/AutoChangeLog-pr-1212.yml
diff --git a/html/changelogs/AutoChangeLog-pr-1212.yml b/html/changelogs/AutoChangeLog-pr-1212.yml
new file mode 100644
index 000000000000..92cddaa096f9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-1212.yml
@@ -0,0 +1,4 @@
+author: CSDSP
+delete-after: true
+changes:
+ - bugfix: Code disabling the shuttle_catastrophe event while a BYOS is active.
From c85b64d230dc04e2c0c494353d1863e728083322 Mon Sep 17 00:00:00 2001
From: Changelogs
Date: Sat, 9 Nov 2024 22:54:08 +0000
Subject: [PATCH 13/16] Automatic changelog compile [ci skip]
---
html/changelogs/AutoChangeLog-pr-1212.yml | 4 ----
html/changelogs/archive/2024-11.yml | 2 ++
2 files changed, 2 insertions(+), 4 deletions(-)
delete mode 100644 html/changelogs/AutoChangeLog-pr-1212.yml
diff --git a/html/changelogs/AutoChangeLog-pr-1212.yml b/html/changelogs/AutoChangeLog-pr-1212.yml
deleted file mode 100644
index 92cddaa096f9..000000000000
--- a/html/changelogs/AutoChangeLog-pr-1212.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: CSDSP
-delete-after: true
-changes:
- - bugfix: Code disabling the shuttle_catastrophe event while a BYOS is active.
diff --git a/html/changelogs/archive/2024-11.yml b/html/changelogs/archive/2024-11.yml
index e0625065dc92..b4d9b5b485b0 100644
--- a/html/changelogs/archive/2024-11.yml
+++ b/html/changelogs/archive/2024-11.yml
@@ -4,5 +4,7 @@
MosleyTheMalO:
- bugfix: Fixes the snap on dominant aura
2024-11-09:
+ CSDSP:
+ - bugfix: Code disabling the shuttle_catastrophe event while a BYOS is active.
LeDrascol:
- rscadd: 'Added a new quirk: Sanguine Metabolism (variant of Bloodsucker Fledgling)'
From bf28d3bcffb502fe27c6b461f13add6730c4e9d6 Mon Sep 17 00:00:00 2001
From: Darius <5933805+LeDrascol@users.noreply.github.com>
Date: Sat, 9 Nov 2024 20:53:27 -0500
Subject: [PATCH 14/16] Move Sanguine Metabolism
Moves Sanguine Metabolism to a separate file to match new formatting.
---
.../neutral_quirks/sanguine_metabolism.dm | 96 +++++++++++++++++++
1 file changed, 96 insertions(+)
create mode 100644 modular_splurt/code/datums/traits/neutral_quirks/sanguine_metabolism.dm
diff --git a/modular_splurt/code/datums/traits/neutral_quirks/sanguine_metabolism.dm b/modular_splurt/code/datums/traits/neutral_quirks/sanguine_metabolism.dm
new file mode 100644
index 000000000000..d7bb12c24527
--- /dev/null
+++ b/modular_splurt/code/datums/traits/neutral_quirks/sanguine_metabolism.dm
@@ -0,0 +1,96 @@
+// Lite version of Bloodflege
+// Removes all special powers
+/datum/quirk/bloodfledge_lite
+ name = "Sanguine Metabolism"
+ desc = "You are a non-magical Bloodsucker Fledgling, with no special powers. Only blood will sate your hungers, and holy energies will cause your flesh to char."
+ value = 0
+ medical_record_text = "Patient exhibits an unusually weak sanguine curse."
+ mob_trait = TRAIT_BLOODFLEDGE_LITE
+ gain_text = span_notice("You feel a sanguine thirst.")
+ lose_text = span_notice("You feel the sanguine thirst fade away.")
+
+/datum/quirk/bloodfledge_lite/add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Add quirk traits
+ ADD_TRAIT(quirk_mob,TRAIT_NO_PROCESS_FOOD,ROUNDSTART_TRAIT)
+ ADD_TRAIT(quirk_mob,TRAIT_NOTHIRST,ROUNDSTART_TRAIT)
+
+ // Add full trait to preserve bite, holy weakness, and examine functionality
+ ADD_TRAIT(quirk_mob,TRAIT_BLOODFLEDGE,ROUNDSTART_TRAIT)
+
+ // Lite version does not set skin tone or grant the language
+
+ // Register examine text
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, PROC_REF(quirk_examine_bloodfledge_lite))
+
+/datum/quirk/bloodfledge_lite/post_add()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Define and grant ability Bite
+ var/datum/action/cooldown/bloodfledge/bite/act_bite = new
+ act_bite.Grant(quirk_mob)
+
+ // Lite version does not grant the revive ability
+
+/datum/quirk/bloodfledge_lite/remove()
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove quirk traits
+ REMOVE_TRAIT(quirk_mob, TRAIT_NO_PROCESS_FOOD, ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(quirk_mob, TRAIT_NOTHIRST, ROUNDSTART_TRAIT)
+ REMOVE_TRAIT(quirk_mob, TRAIT_BLOODFLEDGE, ROUNDSTART_TRAIT)
+
+ // Remove quirk ability action datums
+ var/datum/action/cooldown/bloodfledge/bite/act_bite = locate() in quirk_mob.actions
+ act_bite.Remove(quirk_mob)
+
+ // Unregister examine text
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+/datum/quirk/bloodfledge_lite/proc/quirk_examine_bloodfledge_lite(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
+ SIGNAL_HANDLER
+
+ // Check if human examiner exists
+ if(!istype(examiner))
+ return
+
+ // Check if examiner is a non-Fledgling
+ if(!isbloodfledge(examiner))
+ // Return with no effects
+ return
+
+ // Check if examiner is dumb
+ if(HAS_TRAIT(examiner, TRAIT_DUMB))
+ // Return with no effects
+ return
+
+ // Define quirk mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Define hunger texts
+ var/examine_hunger
+
+ // Check hunger levels
+ switch(quirk_mob.nutrition)
+ // Hungry
+ if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
+ examine_hunger = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] blood starved!"
+
+ // Starving
+ if(0 to NUTRITION_LEVEL_STARVING)
+ examine_hunger = "[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] in dire need of blood!"
+
+ // Invalid hunger
+ else
+ // Return with no message
+ return
+
+ // Add detection text
+ examine_list += span_info("[quirk_holder.p_their(TRUE)] hunger allows you to identify [quirk_holder.p_them()] as a lesser Bloodsucker Fledgling!")
+
+ // Add hunger text
+ examine_list += span_warning(examine_hunger)
From 1dcdcb4fe88ba81c3a0b85e9fd421da5716e5929 Mon Sep 17 00:00:00 2001
From: SPLURT Bot <97993072+SPLURT-Station-Bot@users.noreply.github.com>
Date: Thu, 14 Nov 2024 21:04:05 +0000
Subject: [PATCH 15/16] Automatic changelog generation for PR #1220 [ci skip]
---
html/changelogs/AutoChangeLog-pr-1220.yml | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 html/changelogs/AutoChangeLog-pr-1220.yml
diff --git a/html/changelogs/AutoChangeLog-pr-1220.yml b/html/changelogs/AutoChangeLog-pr-1220.yml
new file mode 100644
index 000000000000..a6849a0f3431
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-1220.yml
@@ -0,0 +1,5 @@
+author: LeDrascol
+delete-after: true
+changes:
+ - code_imp: Modular quirks are now in separate files
+ - code_imp: Modularized edits to upstream quirks
From 2279258bbbda86e99aca97a58802ed4fe7c21d02 Mon Sep 17 00:00:00 2001
From: Changelogs
Date: Thu, 14 Nov 2024 21:05:35 +0000
Subject: [PATCH 16/16] Automatic changelog compile [ci skip]
---
html/changelogs/AutoChangeLog-pr-1220.yml | 5 -----
html/changelogs/archive/2024-11.yml | 4 ++++
2 files changed, 4 insertions(+), 5 deletions(-)
delete mode 100644 html/changelogs/AutoChangeLog-pr-1220.yml
diff --git a/html/changelogs/AutoChangeLog-pr-1220.yml b/html/changelogs/AutoChangeLog-pr-1220.yml
deleted file mode 100644
index a6849a0f3431..000000000000
--- a/html/changelogs/AutoChangeLog-pr-1220.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: LeDrascol
-delete-after: true
-changes:
- - code_imp: Modular quirks are now in separate files
- - code_imp: Modularized edits to upstream quirks
diff --git a/html/changelogs/archive/2024-11.yml b/html/changelogs/archive/2024-11.yml
index b4d9b5b485b0..cfcb7124ceb4 100644
--- a/html/changelogs/archive/2024-11.yml
+++ b/html/changelogs/archive/2024-11.yml
@@ -8,3 +8,7 @@
- bugfix: Code disabling the shuttle_catastrophe event while a BYOS is active.
LeDrascol:
- rscadd: 'Added a new quirk: Sanguine Metabolism (variant of Bloodsucker Fledgling)'
+2024-11-14:
+ LeDrascol:
+ - code_imp: Modular quirks are now in separate files
+ - code_imp: Modularized edits to upstream quirks