From 064c49a0f9e9de41ae0565a5881e88f183036061 Mon Sep 17 00:00:00 2001
From: Arturlang <24881678+Arturlang@users.noreply.github.com>
Date: Thu, 21 Mar 2024 19:57:12 +0200
Subject: [PATCH] [DONE] Adds the weakness to sol quirk (#1255)
## About The Pull Request
You know how bloodsuckers are weak to Sol? Now you can be too! Only as a
hemophage.
Features included: The very same warnings and UI that bloodsuckers get
to know when they are in danger!
This quirk also makes healing in a coffin cost only half blood, neat
huh?
## Why It's Good For The Game
People wanted to suffer, and why am I to deny them pain?
## Proof Of Testing
HUD/ burning / healing works fine
## Changelog
:cl:
add: Adds the hemophage-only quirk, Sol weakness
code: Refactors the sol subsystem so that itself decides if it should
run or not based on if anyone is using it.
code: Refactors bloodsucker UI elements so that they can function fine
outside a bloodsucker antag datum
code: bloodsucker_blood_volume, humanity_lost, bloodsucker_level and
bloodsucker_level_unspent are now private variables due to them being
used for UI updates, if you need to change them, use the procs that are
written in the autodoc
code: Quirk species whitelists no longer use bitflags, but use the
species ID's. Easily allowing you to add new whitelists without having
to mess around with making a new bitfield
/:cl:
---------
Co-authored-by: projectkepler-RU <99981766+projectkepler-ru@users.noreply.github.com>
Co-authored-by: Waterpig <49160555+Majkl-J@users.noreply.github.com>
---
.../~~bubber_defines/quirk_whitelist.dm | 4 -
.../subsystem/processing/quirks.dm | 7 +-
code/datums/quirks/_quirk.dm | 6 --
.../client/preferences/middleware/quirks.dm | 8 +-
.../hemophage/hemophage_status_effects.dm | 3 +-
.../code/__DEFINES/bloodsucker_defines.dm | 20 ++++
.../code/_globalvars/lists/quirks.dm | 17 ++++
.../{ => processing}/sol_subsystem.dm | 99 ++++++++++++++-----
.../code/datums/bubber_quirks/hydrophilic.dm | 6 +-
.../datums/mood_events/bloodsucker_events.dm | 7 +-
.../dynamic/dynamic_rulesets_latejoin.dm | 2 +-
.../dynamic/dynamic_rulesets_midround.dm | 2 +-
.../bloodsucker/bloodsuckers/bloodsucker.dm | 42 ++++----
.../bloodsucker/bloodsuckers/hud.dm | 68 +++++++------
.../bloodsucker/bloodsuckers/integration.dm | 2 +-
.../bloodsucker/bloodsuckers/life.dm | 9 +-
.../bloodsucker/bloodsuckers/procs.dm | 24 +++--
.../bloodsucker/bloodsuckers/sol.dm | 35 +------
.../antagonists/bloodsucker/clans/clan.dm | 16 +--
.../bloodsucker/clans/clan_malkavian.dm | 2 +-
.../bloodsucker/clans/clan_tremere.dm | 4 +-
.../bloodsucker/clans/clan_ventrue.dm | 6 +-
.../antagonists/bloodsucker/powers/_powers.dm | 4 +-
.../antagonists/bloodsucker/powers/feed.dm | 2 +-
.../bloodsucker/structures/coffin.dm | 10 +-
.../client/preferences/middleware/species.dm | 16 +++
.../modules/status_effects/buffs/frenzy.dm | 2 +-
modular_zubbers/modules/quirks/code/_quirk.dm | 20 +++-
.../code/negative_quirks/sol_weakness.dm | 96 ++++++++++++++++++
tgstation.dme | 6 +-
.../interfaces/PreferencesMenu/QuirksPage.tsx | 35 +++----
.../tgui/interfaces/PreferencesMenu/data.ts | 2 +-
32 files changed, 388 insertions(+), 194 deletions(-)
delete mode 100644 code/__DEFINES/~~bubber_defines/quirk_whitelist.dm
create mode 100644 modular_zubbers/code/_globalvars/lists/quirks.dm
rename modular_zubbers/code/controllers/subsystem/{ => processing}/sol_subsystem.dm (52%)
create mode 100644 modular_zubbers/code/modules/client/preferences/middleware/species.dm
create mode 100644 modular_zubbers/modules/quirks/code/negative_quirks/sol_weakness.dm
diff --git a/code/__DEFINES/~~bubber_defines/quirk_whitelist.dm b/code/__DEFINES/~~bubber_defines/quirk_whitelist.dm
deleted file mode 100644
index c699b9d07cc..00000000000
--- a/code/__DEFINES/~~bubber_defines/quirk_whitelist.dm
+++ /dev/null
@@ -1,4 +0,0 @@
-// Modular file for whitelist bitflag defines
-
-/// Quirk only applicable to a roundstartslime
-#define QUIRK_SLIMEPERSON_ONLY (1<<0)
diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm
index d5c24c7a50c..09af55a748a 100644
--- a/code/controllers/subsystem/processing/quirks.dm
+++ b/code/controllers/subsystem/processing/quirks.dm
@@ -67,12 +67,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
var/list/quirk_points = list() //Assoc. list of quirk names and their "point cost"; positive numbers are good traits, and negative ones are bad
///An assoc list of quirks that can be obtained as a hardcore character, and their hardcore value.
var/list/hardcore_quirks = list()
- //BUBBER EDIT ADDITION START - Species quirks
- /// A list of quirks that can only be used by a certain species. Format: list(quirk, species define)
- var/static/list/quirk_species_whitelist = list(
- list("Hydrophilic", "[SPECIES_SLIMESTART]")
- )
- //BUBBER EDIT ADDITION END
+
/datum/controller/subsystem/processing/quirks/Initialize()
get_quirks()
return SS_INIT_SUCCESS
diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm
index 947c1f4a8d3..873ffd95fbd 100644
--- a/code/datums/quirks/_quirk.dm
+++ b/code/datums/quirks/_quirk.dm
@@ -67,12 +67,6 @@
if(quirk_holder)
CRASH("Attempted to add quirk to a holder when it already has a holder.")
- //BUBBER EDIT ADDITION START - Species quirks
- //We silently fail if a whitelisted quirk *somehow* got into someone's preferences.
- if(quirk_whitelist_flags & QUIRK_SLIMEPERSON_ONLY && !isroundstartslime(new_holder))
- CRASH("Attempted to add quirk [name] to [quirk_holder], but the species is ineligible for it!")
- //BUBBER EDIT ADDITION END
-
quirk_holder = new_holder
quirk_holder.quirks += src
// If we weren't passed a client source try to use a present one
diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm
index ed5ba9ed37d..67eaacbe499 100644
--- a/code/modules/client/preferences/middleware/quirks.dm
+++ b/code/modules/client/preferences/middleware/quirks.dm
@@ -41,6 +41,12 @@
var/datum/quirk_constant_data/constant_data = GLOB.all_quirk_constant_data[quirk]
var/list/datum/preference/customization_options = constant_data?.get_customization_data()
+ // BUBBER EDIT START - Species only quirks
+ var/list/species_whitelist = list()
+ for(var/species_id in GLOB.quirk_species_whitelist[quirk])
+ var/datum/species/species_type = GLOB.species_list[species_id]
+ var/species_name = initial(species_type.name)
+ species_whitelist[species_id] += species_name // BUBBER EDIT END - Species only quirks
quirk_info[sanitize_css_class_name(quirk_name)] = list(
"description" = initial(quirk.desc),
"icon" = initial(quirk.icon),
@@ -49,6 +55,7 @@
"customizable" = constant_data?.is_customizable(),
"customization_options" = customization_options,
"veteran_only" = initial(quirk.veteran_only), // SKYRAT EDIT - Veteran quirks
+ "species_whitelist" = species_whitelist, //BUBBER EDIT - Species quirks
)
return list(
@@ -56,7 +63,6 @@
"quirk_info" = quirk_info,
"quirk_blacklist" = GLOB.quirk_string_blacklist,
"points_enabled" = !CONFIG_GET(flag/disable_quirk_points),
- "quirk_species_whitelist" = SSquirks.quirk_species_whitelist, //BUBBER EDIT - Species quirks
)
/datum/preference_middleware/quirks/on_new_character(mob/user)
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_status_effects.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_status_effects.dm
index b8406d77511..86516e1ec55 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_status_effects.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/hemophage/hemophage_status_effects.dm
@@ -51,6 +51,7 @@
alert_type = /atom/movable/screen/alert/status_effect/blood_regen_active
/// Current multiplier for how much blood they spend healing themselves for every point of damage healed.
var/blood_to_health_multiplier = 1
+ var/cost_blood = 1 /// BUBBER CHANGE, allows scaling of hemophage healing blood cost.
/datum/status_effect/blood_regen_active/on_apply()
@@ -135,7 +136,7 @@
regenerator.remove_status_effect(/datum/status_effect/blood_regen_active)
return
- regenerator.blood_volume = max(regenerator.blood_volume - blood_used, MINIMUM_VOLUME_FOR_REGEN)
+ regenerator.blood_volume = max(regenerator.blood_volume - blood_used * cost_blood, MINIMUM_VOLUME_FOR_REGEN) // BUBBER CHANGE, allows scaling of hemophage healing blood cost.
/datum/movespeed_modifier/hemophage_dormant_state
diff --git a/modular_zubbers/code/__DEFINES/bloodsucker_defines.dm b/modular_zubbers/code/__DEFINES/bloodsucker_defines.dm
index 822c903c8ca..772b23c4951 100644
--- a/modular_zubbers/code/__DEFINES/bloodsucker_defines.dm
+++ b/modular_zubbers/code/__DEFINES/bloodsucker_defines.dm
@@ -31,6 +31,26 @@
///The level when at a bloodsucker becomes snobby about who they drink from and gain their non-fledling reputation
#define BLOODSUCKER_HIGH_LEVEL 4
+/**
+ * Sol defines
+ */
+///How long Sol will last until it's night again.
+#define TIME_BLOODSUCKER_DAY 60
+///Base time nighttime should be in for, until Sol rises.
+// Can't put defines in defines, so we have to use deciseconds.
+#define TIME_BLOODSUCKER_NIGHT_MAX 1320 // 22 minutes
+#define TIME_BLOODSUCKER_NIGHT_MIN 1020 // 17 minutes
+
+///Time left to send an alert to Bloodsuckers about an incoming Sol.
+#define TIME_BLOODSUCKER_DAY_WARN 90
+///Time left to send an urgent alert to Bloodsuckers about an incoming Sol.
+#define TIME_BLOODSUCKER_DAY_FINAL_WARN 30
+///Time left to alert that Sol is rising.
+#define TIME_BLOODSUCKER_BURN_INTERVAL 5
+
+///How much time Sol can be 'off' by, keeping the time inconsistent.
+#define TIME_BLOODSUCKER_SOL_DELAY 90
+
/**
* Vassal defines
*/
diff --git a/modular_zubbers/code/_globalvars/lists/quirks.dm b/modular_zubbers/code/_globalvars/lists/quirks.dm
new file mode 100644
index 00000000000..a5303180fa0
--- /dev/null
+++ b/modular_zubbers/code/_globalvars/lists/quirks.dm
@@ -0,0 +1,17 @@
+/// Neccesary for accessing non-instanced quirk whitelists, as initial() cannot access lists
+GLOBAL_LIST_INIT(quirk_species_whitelist, generate_quirk_species_whitelist())
+
+// unfortunately we have to instance the quirks here or otherwise we cannot access a list,
+// initial cannot access lists as they're not actually compile-time constants, only initialized at runtime
+/proc/generate_quirk_species_whitelist()
+ var/list/all_quirks = subtypesof(/datum/quirk)
+ var/list/quirk_species_whitelist = list()
+ for(var/quirk_type in all_quirks)
+ var/datum/quirk/quirk = new quirk_type()
+ if(length(quirk.species_whitelist))
+ quirk_species_whitelist[quirk.type] = list()
+ for(var/species_id in quirk.species_whitelist)
+ quirk_species_whitelist[quirk.type] += species_id
+ qdel(quirk)
+ return quirk_species_whitelist
+
diff --git a/modular_zubbers/code/controllers/subsystem/sol_subsystem.dm b/modular_zubbers/code/controllers/subsystem/processing/sol_subsystem.dm
similarity index 52%
rename from modular_zubbers/code/controllers/subsystem/sol_subsystem.dm
rename to modular_zubbers/code/controllers/subsystem/processing/sol_subsystem.dm
index edab6b7c558..b22a712d0df 100644
--- a/modular_zubbers/code/controllers/subsystem/sol_subsystem.dm
+++ b/modular_zubbers/code/controllers/subsystem/processing/sol_subsystem.dm
@@ -1,25 +1,11 @@
-///How long Sol will last until it's night again.
-#define TIME_BLOODSUCKER_DAY 60
-///Base time nighttime should be in for, until Sol rises.
-// Can't put defines in defines, so we have to use deciseconds.
-#define TIME_BLOODSUCKER_NIGHT_MAX 1320 // 22 minutes
-#define TIME_BLOODSUCKER_NIGHT_MIN 1020 // 17 minutes
-///Time left to send an alert to Bloodsuckers about an incoming Sol.
-#define TIME_BLOODSUCKER_DAY_WARN 90
-///Time left to send an urgent alert to Bloodsuckers about an incoming Sol.
-#define TIME_BLOODSUCKER_DAY_FINAL_WARN 30
-///Time left to alert that Sol is rising.
-#define TIME_BLOODSUCKER_BURN_INTERVAL 5
-///How much time Sol can be 'off' by, keeping the time inconsistent.
-#define TIME_BLOODSUCKER_SOL_DELAY 90
-
-SUBSYSTEM_DEF(sunlight)
+PROCESSING_SUBSYSTEM_DEF(sunlight)
name = "Sol"
can_fire = FALSE
+ runlevels = RUNLEVEL_GAME
wait = 2 SECONDS
- flags = SS_NO_INIT | SS_BACKGROUND | SS_TICKER
+ flags = SS_NO_INIT | SS_KEEP_TIMING | SS_TICKER
///If the Sun is currently out our not.
var/sunlight_active = FALSE
@@ -27,8 +13,10 @@ SUBSYSTEM_DEF(sunlight)
var/time_til_cycle = TIME_BLOODSUCKER_NIGHT_MAX
///If Bloodsucker levels for the night has been given out yet.
var/issued_XP = FALSE
+ /// Mobs that make use of the sunlight system.
+ var/list/sun_sufferers = list()
-/datum/controller/subsystem/sunlight/fire(resumed = FALSE)
+/datum/controller/subsystem/processing/sunlight/fire(resumed = FALSE)
time_til_cycle--
if(sunlight_active)
if(time_til_cycle > 0)
@@ -48,7 +36,7 @@ SUBSYSTEM_DEF(sunlight)
vampire_warning_message = span_announce("The solar flare has ended, and the daylight danger has passed... for now."),
vassal_warning_message = span_announce("The solar flare has ended, and the daylight danger has passed... for now."),
)
- return
+ return ..()
switch(time_til_cycle)
if(TIME_BLOODSUCKER_DAY_WARN)
@@ -79,15 +67,72 @@ SUBSYSTEM_DEF(sunlight)
vampire_warning_message = span_userdanger("Solar flares bombard the station with deadly UV light! Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!"),
vassal_warning_message = span_userdanger("Solar flares bombard the station with UV light!"),
)
+ ..()
-/datum/controller/subsystem/sunlight/proc/warn_daylight(danger_level, vampire_warning_message, vassal_warning_message)
+/datum/controller/subsystem/processing/sunlight/proc/warn_daylight(danger_level, vampire_warning_message, vassal_warning_message)
SEND_SIGNAL(src, COMSIG_SOL_WARNING_GIVEN, danger_level, vampire_warning_message, vassal_warning_message)
-#undef TIME_BLOODSUCKER_SOL_DELAY
-#undef TIME_BLOODSUCKER_DAY
-#undef TIME_BLOODSUCKER_NIGHT_MAX
-#undef TIME_BLOODSUCKER_NIGHT_MIN
-#undef TIME_BLOODSUCKER_DAY_WARN
-#undef TIME_BLOODSUCKER_DAY_FINAL_WARN
-#undef TIME_BLOODSUCKER_BURN_INTERVAL
+/datum/controller/subsystem/processing/sunlight/proc/add_sun_sufferer(mob/victim)
+ var/sufferer_list = is_sufferer(victim)
+ if(sufferer_list)
+ return FALSE
+ var/atom/movable/screen/bloodsucker/sunlight_counter/sun_hud = new(null, victim.hud_used)
+ victim.hud_used.infodisplay += sun_hud
+ victim.hud_used.show_hud(victim.hud_used.hud_version)
+ sun_hud.update_sol_hud()
+ sun_sufferers += list(list("victim" = WEAKREF(victim), "sun_hud" = sun_hud))
+ if(length(sun_sufferers))
+ can_fire = TRUE
+
+ return TRUE
+
+/datum/controller/subsystem/processing/sunlight/proc/remove_sun_sufferer(mob/victim)
+ var/sufferer_list = is_sufferer(victim)
+ if(!sufferer_list)
+ return FALSE
+ var/atom/movable/screen/bloodsucker/sunlight_counter/sun_hud = sufferer_list["sun_hud"]
+ if(sun_hud)
+ victim?.hud_used.infodisplay -= sun_hud
+ qdel(sun_hud)
+ // We have to use the index here as -= won't work with two lists
+ sun_sufferers.Cut(sufferer_list["index"])
+ if(!length(sun_sufferers))
+ can_fire = FALSE
+ sunlight_active = initial(sunlight_active)
+ time_til_cycle = initial(time_til_cycle)
+ issued_XP = initial(issued_XP)
+ return TRUE
+
+/datum/controller/subsystem/processing/sunlight/proc/warn_notify(mob/target, danger_level, message)
+ if(!target)
+ return
+ to_chat(target, message)
+
+ switch(danger_level)
+ if(DANGER_LEVEL_FIRST_WARNING)
+ target.playsound_local(null, 'modular_zubbers/sound/bloodsucker/griffin_3.ogg', 50, TRUE)
+ if(DANGER_LEVEL_SECOND_WARNING)
+ target.playsound_local(null, 'modular_zubbers/sound/bloodsucker/griffin_5.ogg', 50, TRUE)
+ if(DANGER_LEVEL_THIRD_WARNING)
+ target.playsound_local(null, 'sound/effects/alert.ogg', 75, TRUE)
+ if(DANGER_LEVEL_SOL_ROSE)
+ target.playsound_local(null, 'sound/ambience/ambimystery.ogg', 75, TRUE)
+ if(DANGER_LEVEL_SOL_ENDED)
+ target.playsound_local(null, 'sound/misc/ghosty_wind.ogg', 90, TRUE)
+
+
+/datum/controller/subsystem/processing/sunlight/proc/is_sufferer(mob/victim)
+ for(var/i in 1 to length(sun_sufferers))
+ var/list/sufferers_original = sun_sufferers[i]
+ var/list/sufferer_list = sufferers_original?.Copy()
+ if(!sufferer_list)
+ continue
+ sufferer_list["index"] = i
+ var/datum/weakref/sufferer_ref = sufferer_list["victim"]
+ if(!sufferer_ref)
+ continue
+ var/sufferer = sufferer_ref.resolve()
+ if(sufferer == victim)
+ return sufferer_list
+ return null
diff --git a/modular_zubbers/code/datums/bubber_quirks/hydrophilic.dm b/modular_zubbers/code/datums/bubber_quirks/hydrophilic.dm
index 5e990ba269b..27e704c777f 100644
--- a/modular_zubbers/code/datums/bubber_quirks/hydrophilic.dm
+++ b/modular_zubbers/code/datums/bubber_quirks/hydrophilic.dm
@@ -4,9 +4,9 @@
icon = FA_ICON_BUG // I can't be asked to make an icon for this, my spritework sucks.
value = 0
quirk_flags = QUIRK_HUMAN_ONLY
- quirk_whitelist_flags = QUIRK_SLIMEPERSON_ONLY
- gain_text = "You get the feeling you should stay away from water..."
- lose_text = "Somehow, you feel as if water is no longer dangerous."
+ species_whitelist = list(SPECIES_SLIMEPERSON)
+ gain_text = span_danger("You get the feeling you should stay away from water...")
+ lose_text = span_notice("Somehow, you feel as if water is no longer dangerous.")
medical_record_text = "Patient scans indicate extreme hydrophilicity."
hardcore_value = 0
mob_trait = TRAIT_HYDROPHILIC
diff --git a/modular_zubbers/code/datums/mood_events/bloodsucker_events.dm b/modular_zubbers/code/datums/mood_events/bloodsucker_events.dm
index 13c39870065..c3a8b897965 100644
--- a/modular_zubbers/code/datums/mood_events/bloodsucker_events.dm
+++ b/modular_zubbers/code/datums/mood_events/bloodsucker_events.dm
@@ -33,12 +33,15 @@
mood_change = 10
timeout = 6 MINUTES
-/datum/mood_event/daylight_1
+/datum/mood_event/coffinsleep/quirk
+ mood_change = 4
+
+/datum/mood_event/daylight_bad_sleep
description = "I slept poorly in a makeshift coffin during the day.\n"
mood_change = -3
timeout = 6 MINUTES
-/datum/mood_event/daylight_2
+/datum/mood_event/daylight_sun_scorched
description = "I have been scorched by the unforgiving rays of the sun.\n"
mood_change = -6
timeout = 6 MINUTES
diff --git a/modular_zubbers/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/modular_zubbers/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
index d9d98897419..76e6a174fb4 100644
--- a/modular_zubbers/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/modular_zubbers/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
@@ -31,7 +31,7 @@
assigned -= candidate_mind
message_admins("[ADMIN_LOOKUPFLW(candidate_mind)] was selected by the [name] ruleset, but couldn't be made into a Bloodsucker.")
continue
- bloodsuckerdatum.bloodsucker_level_unspent = rand(2,3)
+ bloodsuckerdatum.AdjustUnspentRank(rand(2, 3))
message_admins("[ADMIN_LOOKUPFLW(candidate_mind)] was selected by the [name] ruleset and has been made into a midround Bloodsucker.")
log_game("DYNAMIC: [key_name(candidate_mind)] was selected by the [name] ruleset and has been made into a midround Bloodsucker.")
return TRUE
diff --git a/modular_zubbers/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/modular_zubbers/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index f7f0ccffe67..713c1572018 100644
--- a/modular_zubbers/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/modular_zubbers/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -41,7 +41,7 @@
assigned -= selected_mobs.mind
message_admins("[ADMIN_LOOKUPFLW(selected_mobs)] was selected by the [name] ruleset, but couldn't be made into a Bloodsucker.")
return FALSE
- bloodsuckerdatum.bloodsucker_level_unspent = rand(2,3)
+ bloodsuckerdatum.AdjustUnspentRank(rand(2, 3))
message_admins("[ADMIN_LOOKUPFLW(selected_mobs)] was selected by the [name] ruleset and has been made into a midround Bloodsucker.")
log_game("DYNAMIC: [key_name(selected_mobs)] was selected by the [name] ruleset and has been made into a midround Bloodsucker.")
return TRUE
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/bloodsucker.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/bloodsucker.dm
index 0eed71fcf56..6f238903aee 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/bloodsucker.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/bloodsucker.dm
@@ -12,8 +12,8 @@
ui_name = "AntagInfoBloodsucker"
preview_outfit = /datum/outfit/bloodsucker_outfit
- /// How much blood we have, starting off at default blood levels.
- var/bloodsucker_blood_volume = BLOOD_VOLUME_NORMAL
+ /// How much blood we have, starting off at default blood levels. Do not adjust this directly, use adjustBloodVolume(), and use getBloodVolume() to get the current value.
+ VAR_PRIVATE/bloodsucker_blood_volume = BLOOD_VOLUME_NORMAL
/// How much blood we can have at once, increases per level.
var/max_blood_volume = 600
@@ -34,8 +34,8 @@
///Used for assigning your reputation
var/bloodsucker_reputation
- ///Amount of Humanity lost
- var/humanity_lost = 0
+ ///Amount of Humanity lost, don't modify this directly, use AddHumanityLost(), and use GetHumanityLost() to get the current value.
+ VAR_PRIVATE/humanity_lost = 0
///Have we been broken the Masquerade?
var/broke_masquerade = FALSE
///How many Masquerade Infractions do we have?
@@ -53,8 +53,10 @@
///Special vassals I own, to not have double of the same type.
var/list/datum/antagonist/vassal/special_vassals = list()
- var/bloodsucker_level = 0
- var/bloodsucker_level_unspent = 1
+ ///How many ranks we have, don't modify this directly, use AdjustRank() and use GetRank() to get the current value.
+ VAR_PRIVATE/bloodsucker_level = 0
+ /// Unspent ranks, don't modify this directly, use AdjustUnspentRanks() and use GetUnspentRanks() to get the current value.
+ VAR_PRIVATE/bloodsucker_level_unspent = 1
var/additional_regen
var/bloodsucker_regen_rate = 0.3
@@ -67,8 +69,6 @@
var/atom/movable/screen/bloodsucker/blood_counter/blood_display
///Vampire level display HUD
var/atom/movable/screen/bloodsucker/rank_counter/vamprank_display
- ///Sunlight timer HUD
- var/atom/movable/screen/bloodsucker/sunlight_counter/sunlight_display
/// Static typecache of all bloodsucker powers.
var/static/list/all_bloodsucker_powers = typecacheof(/datum/action/cooldown/bloodsucker, ignore_root_path = TRUE)
@@ -123,6 +123,7 @@
talking_head()
handle_clown_mutation(current_mob, mob_override ? null : "As a vampiric clown, you are no longer a danger to yourself. Your clownish nature has been subdued by your thirst for blood.")
add_team_hud(current_mob)
+ remove_invalid_quirks()
if(current_mob.hud_used)
on_hud_created()
@@ -150,34 +151,35 @@
remove_signals_from_heart(current_mob)
UnregisterSignal(current_mob, list(COMSIG_LIVING_LIFE, COMSIG_ATOM_EXAMINE, COMSIG_LIVING_DEATH, COMSIG_SPECIES_GAIN, COMSIG_QDELETING))
handle_clown_mutation(current_mob, removing = FALSE)
-
if(current_mob.hud_used)
var/datum/hud/hud_used = current_mob.hud_used
hud_used.infodisplay -= blood_display
hud_used.infodisplay -= vamprank_display
- hud_used.infodisplay -= sunlight_display
QDEL_NULL(blood_display)
QDEL_NULL(vamprank_display)
- QDEL_NULL(sunlight_display)
+
+ SSsunlight.remove_sun_sufferer(owner.current) //check if sunlight should end
current_mob.dna?.species?.on_bloodsucker_loss(current_mob)
+ if(current_mob.client)
+ // We need to let the bloodsucker antag datum get removed before we can re-add quirks
+ addtimer(CALLBACK(SSquirks, TYPE_PROC_REF(/datum/controller/subsystem/processing/quirks, AssignQuirks), current_mob, current_mob.client), 1 SECONDS)
+
/datum/antagonist/bloodsucker/proc/on_hud_created(datum/source)
SIGNAL_HANDLER
var/datum/hud/bloodsucker_hud = owner.current.hud_used
- blood_display = new /atom/movable/screen/bloodsucker/blood_counter(null, bloodsucker_hud)
+ blood_display = new(null, bloodsucker_hud)
bloodsucker_hud.infodisplay += blood_display
- vamprank_display = new /atom/movable/screen/bloodsucker/rank_counter(null, bloodsucker_hud)
+ vamprank_display = new(null, bloodsucker_hud)
bloodsucker_hud.infodisplay += vamprank_display
- sunlight_display = new /atom/movable/screen/bloodsucker/sunlight_counter(null, bloodsucker_hud)
- bloodsucker_hud.infodisplay += sunlight_display
-
bloodsucker_hud.show_hud(bloodsucker_hud.hud_version)
+ SSsunlight.add_sun_sufferer(owner.current)
UnregisterSignal(owner.current, COMSIG_MOB_HUD_CREATED)
- update_hud()
update_blood_hud()
+ update_rank_hud()
/// Override some properties of incompatible species
/datum/antagonist/bloodsucker/proc/on_species_gain(mob/living/carbon/human/target, datum/species/current_species, datum/species/old_species)
@@ -219,7 +221,6 @@
show_in_roundend = FALSE
else
// Start Sunlight if first Bloodsucker
- check_start_sunlight()
// Name and Titles
SelectFirstName()
SelectTitle(am_fledgling = TRUE)
@@ -234,9 +235,7 @@
/// Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
/datum/antagonist/bloodsucker/on_removal()
- UnregisterSignal(SSsunlight, list(COMSIG_SOL_RANKUP_BLOODSUCKERS, COMSIG_SOL_NEAR_START, COMSIG_SOL_END, COMSIG_SOL_RISE_TICK, COMSIG_SOL_WARNING_GIVEN, COMSIG_QDELETING))
free_all_vassals()
- check_cancel_sunlight() //check if sunlight should end
if(!owner?.current)
return
if(ishuman(owner.current))
@@ -488,6 +487,9 @@
user_eyes.sight_flags = initial(user_eyes.sight_flags)
user.update_sight()
+/datum/antagonist/bloodsucker/proc/remove_invalid_quirks()
+ owner.current.remove_quirk(/datum/quirk/sol_weakness)
+
/// Name shown on antag list
/datum/antagonist/bloodsucker/antag_listing_name()
return ..() + "([return_full_name()])"
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/hud.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/hud.dm
index 48be42016ee..c32103ec3b4 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/hud.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/hud.dm
@@ -18,68 +18,74 @@
icon_state = "blood_display"
screen_loc = UI_BLOOD_DISPLAY
+/atom/movable/screen/bloodsucker/blood_counter/proc/update_blood_hud(blood_volume)
+ maptext = FORMAT_BLOODSUCKER_HUD_TEXT(hud_text_color(), blood_volume)
+
/atom/movable/screen/bloodsucker/rank_counter
name = "Bloodsucker Rank"
icon_state = "rank"
screen_loc = UI_VAMPRANK_DISPLAY
+/atom/movable/screen/bloodsucker/rank_counter/proc/update_rank_hud(level = 0, unspent_level = 0, blood_volume = 0)
+ if(unspent_level > 0)
+ icon_state = "[initial(icon_state)]_up"
+ else
+ icon_state = initial(icon_state)
+ maptext = FORMAT_BLOODSUCKER_HUD_TEXT(hud_text_color(), level)
+
/atom/movable/screen/bloodsucker/sunlight_counter
name = "Solar Flare Timer"
icon_state = "sunlight"
screen_loc = UI_SUNLIGHT_DISPLAY
-#ifdef BLOODSUCKER_TESTING
- var/datum/controller/subsystem/sunlight/sunlight_subsystem
-/atom/movable/screen/bloodsucker/sunlight_counter/New(loc, ...)
+/atom/movable/screen/bloodsucker/sunlight_counter/Initialize(mapload, datum/hud/hud_owner)
. = ..()
- sunlight_subsystem = SSsunlight
-#endif
-
-/// Update Blood Counter + Rank Counter
-/datum/antagonist/bloodsucker/proc/update_hud()
- if(vamprank_display)
- if(bloodsucker_level_unspent > 0)
- vamprank_display.icon_state = "[initial(vamprank_display.icon_state)]_up"
- else
- vamprank_display.icon_state = initial(vamprank_display.icon_state)
- vamprank_display.maptext = FORMAT_BLOODSUCKER_HUD_TEXT(hud_text_color(), bloodsucker_level)
-
update_sol_hud()
+ START_PROCESSING(SSsunlight, src)
-/datum/antagonist/bloodsucker/proc/hud_text_color()
- return bloodsucker_blood_volume > BLOOD_VOLUME_SAFE ? "#FFDDDD" : "#FFAAAA"
+/atom/movable/screen/bloodsucker/sunlight_counter/Destroy()
+ STOP_PROCESSING(SSsunlight, src)
+ . = ..()
-/// Updated every time blood is changed by either
-/datum/antagonist/bloodsucker/proc/update_blood_hud()
- if(!blood_display)
- return
- blood_display.maptext = FORMAT_BLOODSUCKER_HUD_TEXT(hud_text_color(), bloodsucker_blood_volume)
-
-/datum/antagonist/bloodsucker/proc/update_sol_hud()
+/atom/movable/screen/bloodsucker/sunlight_counter/proc/update_sol_hud()
var/valuecolor = hud_text_color()
- if(!sunlight_display)
+ if(!SSsunlight)
return
if(SSsunlight.sunlight_active)
valuecolor = "#FF5555"
- sunlight_display.icon_state = "[initial(sunlight_display.icon_state)]_day"
+ icon_state = "[initial(icon_state)]_day"
else
switch(round(SSsunlight.time_til_cycle, 1))
if(0 to 30)
- sunlight_display.icon_state = "[initial(sunlight_display.icon_state)]_30"
+ icon_state = "[initial(icon_state)]_30"
valuecolor = "#FFCCCC"
if(31 to 60)
- sunlight_display.icon_state = "[initial(sunlight_display.icon_state)]_60"
+ icon_state = "[initial(icon_state)]_60"
valuecolor = "#FFE6CC"
if(61 to 90)
- sunlight_display.icon_state = "[initial(sunlight_display.icon_state)]_90"
+ icon_state = "[initial(icon_state)]_90"
valuecolor = "#FFFFCC"
else
- sunlight_display.icon_state = "[initial(sunlight_display.icon_state)]_night"
+ icon_state = "[initial(icon_state)]_night"
valuecolor = "#FFFFFF"
- sunlight_display.maptext = FORMAT_BLOODSUCKER_SUNLIGHT_TEXT( \
+ maptext = FORMAT_BLOODSUCKER_SUNLIGHT_TEXT( \
valuecolor, \
(SSsunlight.time_til_cycle >= 60) ? "[round(SSsunlight.time_til_cycle / 60, 1)] m" : "[round(SSsunlight.time_til_cycle, 1)] s" \
)
+
+/atom/movable/screen/bloodsucker/sunlight_counter/process(seconds_per_tick)
+ update_sol_hud()
+
+/atom/movable/screen/bloodsucker/proc/hud_text_color(blood_volume)
+ return blood_volume > BLOOD_VOLUME_SAFE ? "#FFDDDD" : "#FFAAAA"
+
+/// Updated every time blood is changed by either
+/datum/antagonist/bloodsucker/proc/update_blood_hud()
+ blood_display?.update_blood_hud(bloodsucker_blood_volume)
+
+/datum/antagonist/bloodsucker/proc/update_rank_hud()
+ vamprank_display?.update_rank_hud(bloodsucker_level, bloodsucker_level_unspent, bloodsucker_blood_volume)
+
/// 1 tile down
#undef UI_BLOOD_DISPLAY
/// 2 tiles down
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/integration.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/integration.dm
index ef7857b4e1e..fe08d5c1afe 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/integration.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/integration.dm
@@ -8,7 +8,7 @@
var/datum/antagonist/bloodsucker/bloodsuckerdatum = IS_BLOODSUCKER(exposed_mob)
if(!bloodsuckerdatum)
return ..()
- if(bloodsuckerdatum.bloodsucker_blood_volume > BLOOD_VOLUME_NORMAL)
+ if(bloodsuckerdatum.GetBloodVolume() > BLOOD_VOLUME_NORMAL)
return
bloodsuckerdatum.AdjustBloodVolume(round(reac_volume, 0.1))
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/life.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/life.dm
index 44fcc51f6fd..0a36ccba231 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/life.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/life.dm
@@ -8,7 +8,6 @@
INVOKE_ASYNC(src, PROC_REF(HandleDeath))
return
if(isbrain(owner.current))
- INVOKE_ASYNC(src, PROC_REF(update_hud))
return
if(HAS_TRAIT_FROM_ONLY(owner.current, TRAIT_NODEATH, BLOODSUCKER_TRAIT))
check_end_torpor()
@@ -23,7 +22,6 @@
INVOKE_ASYNC(src, PROC_REF(HandleStarving))
INVOKE_ASYNC(src, PROC_REF(update_blood))
- INVOKE_ASYNC(src, PROC_REF(update_hud))
SEND_SIGNAL(src, COMSIG_BLOODSUCKER_ON_LIFETICK)
/datum/antagonist/bloodsucker/proc/on_death(mob/living/source, gibbed)
@@ -39,6 +37,10 @@
/**
* ## BLOOD STUFF
*/
+
+/datum/antagonist/bloodsucker/proc/GetBloodVolume()
+ return bloodsucker_blood_volume
+
/datum/antagonist/bloodsucker/proc/AdjustBloodVolume(value)
bloodsucker_blood_volume = clamp(bloodsucker_blood_volume + value, 0, max_blood_volume)
update_blood_hud()
@@ -49,6 +51,9 @@
#define MASQUERADE /datum/action/cooldown/bloodsucker/masquerade
+/datum/antagonist/bloodsucker/proc/GetHumanityLost()
+ return humanity_lost
+
/datum/antagonist/bloodsucker/proc/AddHumanityLost(value)
if(value == 0)
return
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/procs.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/procs.dm
index ced72487b24..907e216e75a 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/procs.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/procs.dm
@@ -67,7 +67,7 @@
/datum/antagonist/bloodsucker/proc/RankUp(force = FALSE)
if(!owner || !owner.current)
return
- bloodsucker_level_unspent++
+ AdjustUnspentRank(1)
if(!my_clan)
to_chat(owner.current, span_notice("You have gained a rank. Join a Clan to spend it."))
return
@@ -80,7 +80,7 @@
SpendRank()
/datum/antagonist/bloodsucker/proc/RankDown()
- bloodsucker_level_unspent--
+ AdjustUnspentRank(-1)
/datum/antagonist/bloodsucker/proc/remove_nondefault_powers(return_levels = FALSE)
for(var/datum/action/cooldown/bloodsucker/power as anything in powers)
@@ -88,7 +88,7 @@
continue
RemovePower(power)
if(return_levels)
- bloodsucker_level_unspent++
+ AdjustUnspentRank(1)
/datum/antagonist/bloodsucker/proc/LevelUpPowers()
for(var/datum/action/cooldown/bloodsucker/power as anything in powers)
@@ -108,6 +108,19 @@
return
SEND_SIGNAL(src, BLOODSUCKER_RANK_UP, target, cost_rank, blood_cost)
+/datum/antagonist/bloodsucker/proc/GetRank()
+ return bloodsucker_level
+
+/datum/antagonist/bloodsucker/proc/AdjustRank(amount)
+ bloodsucker_level = max(bloodsucker_level + amount, 0)
+ update_rank_hud()
+
+/datum/antagonist/bloodsucker/proc/GetUnspentRank()
+ return bloodsucker_level_unspent
+
+/datum/antagonist/bloodsucker/proc/AdjustUnspentRank(amount)
+ bloodsucker_level_unspent = max(bloodsucker_level_unspent + amount, 0)
+ update_rank_hud()
/**
* Called when a Bloodsucker reaches Final Death
* Releases all Vassals and gives them the ex_vassal datum.
@@ -179,7 +192,7 @@
return
UnregisterSignal(organ, COMSIG_ORGAN_REMOVED)
UnregisterSignal(organ, COMSIG_ORGAN_BEING_REPLACED)
- organ = null
+ heart = null
/datum/antagonist/bloodsucker/proc/on_organ_removal(obj/item/organ/organ, mob/living/carbon/old_owner)
SIGNAL_HANDLER
@@ -207,7 +220,7 @@
RegisterSignal(owner.current, COMSIG_LIVING_LIFE, PROC_REF(LifeTick), TRUE)
CRASH("What the fuck, somehow called on_organ_gain signal on [src] without current_mob being the antag datum's owner?")
UnregisterSignal(current_mob, COMSIG_ENTER_COFFIN)
- RegisterSignal(current_mob, COMSIG_LIVING_LIFE, PROC_REF(LifeTick))
+ RegisterSignal(current_mob, COMSIG_LIVING_LIFE, PROC_REF(LifeTick), TRUE) // overriding here due to the fact this can without removing the signal due to before_organ_replace()
add_signals_to_heart(current_mob)
/// This handles regen_organs replacing organs, without this the bloodsucker would die for a moment due to their heart being removed for a moment
@@ -241,7 +254,6 @@
if(blood == null)
return
SetBloodVolume(blood)
- update_hud()
/datum/antagonist/bloodsucker/proc/regain_heart(mob/coffin_dweller, obj/structure/closet/crate/coffin/coffin, mob/user)
SIGNAL_HANDLER
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/sol.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/sol.dm
index d883645dd75..d4cf5ed2ff7 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/sol.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/bloodsuckers/sol.dm
@@ -4,20 +4,6 @@
* Sol is the sunlight, during this period, all Bloodsuckers must be in their coffin, else they burn.
*/
-/// Start Sol, called when someone is assigned Bloodsucker
-/datum/antagonist/bloodsucker/proc/check_start_sunlight()
- var/list/existing_suckers = get_antag_minds(/datum/antagonist/bloodsucker) - owner
- if(!length(existing_suckers))
- message_admins("New Sol has been created due to Bloodsucker assignment.")
- SSsunlight.can_fire = TRUE
-
-/// End Sol, if you're the last Bloodsucker
-/datum/antagonist/bloodsucker/proc/check_cancel_sunlight()
- var/list/existing_suckers = get_antag_minds(/datum/antagonist/bloodsucker) - owner
- if(!length(existing_suckers))
- message_admins("Sol has been deleted due to the lack of Bloodsuckers")
- SSsunlight.can_fire = FALSE
-
///Ranks the Bloodsucker up, called by Sol.
/datum/antagonist/bloodsucker/proc/sol_rank_up(atom/source)
SIGNAL_HANDLER
@@ -38,7 +24,6 @@
if(istype(power, /datum/action/cooldown/bloodsucker/gohome))
RemovePower(power)
-/// Cycle through all vamp antags and check if they're inside a closet.
/datum/antagonist/bloodsucker/proc/handle_sol()
SIGNAL_HANDLER
if(!owner || !owner.current || isbrain(owner.current))
@@ -59,7 +44,7 @@
owner.current.ignite_mob()
owner.current.adjustFireLoss(2 + (bloodsucker_level / 2))
owner.current.updatehealth()
- owner.current.add_mood_event("vampsleep", /datum/mood_event/daylight_2)
+ owner.current.add_mood_event("vampsleep", /datum/mood_event/daylight_sun_scorched)
return
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin)) // Coffins offer the BEST protection
@@ -76,25 +61,11 @@
COOLDOWN_START(src, bloodsucker_spam_sol_burn, BLOODSUCKER_SPAM_SOL) //This should happen twice per Sol
owner.current.adjustFireLoss(0.5 + (bloodsucker_level / 4))
owner.current.updatehealth()
- owner.current.add_mood_event("vampsleep", /datum/mood_event/daylight_1)
+ owner.current.add_mood_event("vampsleep", /datum/mood_event/daylight_bad_sleep)
/datum/antagonist/bloodsucker/proc/give_warning(atom/source, danger_level, vampire_warning_message, vassal_warning_message)
SIGNAL_HANDLER
- if(!owner)
- return
- to_chat(owner, vampire_warning_message)
-
- switch(danger_level)
- if(DANGER_LEVEL_FIRST_WARNING)
- owner.current.playsound_local(null, 'modular_zubbers/sound/bloodsucker/griffin_3.ogg', 50, 1)
- if(DANGER_LEVEL_SECOND_WARNING)
- owner.current.playsound_local(null, 'modular_zubbers/sound/bloodsucker/griffin_5.ogg', 50, 1)
- if(DANGER_LEVEL_THIRD_WARNING)
- owner.current.playsound_local(null, 'sound/effects/alert.ogg', 75, 1)
- if(DANGER_LEVEL_SOL_ROSE)
- owner.current.playsound_local(null, 'sound/ambience/ambimystery.ogg', 75, 1)
- if(DANGER_LEVEL_SOL_ENDED)
- owner.current.playsound_local(null, 'sound/misc/ghosty_wind.ogg', 90, 1)
+ SSsunlight.warn_notify(owner.current, danger_level, vampire_warning_message)
/**
* # Torpor
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan.dm
index c09f75b1f7e..dd313dae2a8 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan.dm
@@ -159,9 +159,9 @@
// Give them the UI to purchase a power.
var/choice = tgui_input_list(human_user, "You have the opportunity to grow more ancient. Spend [round(blood_cost, 1)] blood to advance your rank", "Your Blood Thickens...", options)
// Prevent Bloodsuckers from closing/reopning their coffin to spam Levels.
- if(cost_rank && bloodsuckerdatum.bloodsucker_level_unspent <= 0)
+ if(cost_rank && bloodsuckerdatum.GetUnspentRank() <= 0)
return
- if(blood_cost && bloodsuckerdatum.bloodsucker_blood_volume < blood_cost)
+ if(blood_cost && bloodsuckerdatum.GetBloodVolume() < blood_cost)
human_user.balloon_alert(human_user, "not enough blood!")
to_chat(human_user, span_notice("You need at the very least [blood_cost] blood to thicken your blood."))
return
@@ -202,23 +202,23 @@
user_right_hand.unarmed_damage_high += 0.5
// We're almost done - Spend your Rank now.
- bloodsuckerdatum.bloodsucker_level++
+ bloodsuckerdatum.AdjustRank(1)
if(cost_rank)
- bloodsuckerdatum.bloodsucker_level_unspent--
+ bloodsuckerdatum.AdjustUnspentRank(-1)
+
if(blood_cost)
bloodsuckerdatum.AdjustBloodVolume(-blood_cost)
// Ranked up enough to get your true Reputation?
- if(bloodsuckerdatum.bloodsucker_level == BLOODSUCKER_HIGH_LEVEL)
+ if(bloodsuckerdatum.GetRank() == BLOODSUCKER_HIGH_LEVEL)
to_chat(bloodsuckerdatum.owner.current, span_warning("Drinking from mindless humans is now much more less effective."))
bloodsuckerdatum.SelectReputation(am_fledgling = FALSE, forced = TRUE)
- to_chat(bloodsuckerdatum.owner.current, span_notice("You are now a rank [bloodsuckerdatum.bloodsucker_level] Bloodsucker. \
+ to_chat(bloodsuckerdatum.owner.current, span_notice("You are now a rank [bloodsuckerdatum.GetRank()] Bloodsucker. \
Your strength, feed rate, regen rate, and maximum blood capacity have all increased! \n\
* Your existing powers have all ranked up as well!"))
bloodsuckerdatum.owner.current.playsound_local(null, 'sound/effects/pope_entry.ogg', 25, TRUE, pressure_affected = FALSE)
- bloodsuckerdatum.update_hud()
bloodsuckerdatum.update_static_data_for_all_viewers()
// unlock vassalizing if we have a vassal slot
@@ -247,7 +247,7 @@
if(!vassaldatum.owner.can_make_special(creator = bloodsuckerdatum.owner))
to_chat(bloodsuckerdatum.owner.current, span_notice("This Vassal is unable to gain a Special rank due to innate features."))
return FALSE
- if(bloodsuckerdatum.bloodsucker_blood_volume < 150)
+ if(bloodsuckerdatum.GetBloodVolume() < 150)
to_chat(bloodsuckerdatum.owner.current, span_notice("You need at least 150 blood to make a Vassal a Favorite Vassal."))
return FALSE
var/list/options = list()
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_malkavian.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_malkavian.dm
index 81938f9b9fa..a1d980dd56c 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_malkavian.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_malkavian.dm
@@ -45,7 +45,7 @@
// This is the reversed version since we want to increase the prob as the number decreases.
// Equation: interpolated value = end + normalized factor * (start - end)
// normalized factor(between 0 and 1, in decimals)
- var/interpolated_chance = max_madness_chance + (source.humanity_lost / 50) * (min_madness_chance - max_madness_chance)
+ var/interpolated_chance = max_madness_chance + (source.GetHumanityLost() / 50) * (min_madness_chance - max_madness_chance)
var/madness_chance = clamp(interpolated_chance, min_madness_chance, max_madness_chance)
if(prob(madness_chance) || bloodsuckerdatum.owner.current.stat != CONSCIOUS || HAS_TRAIT(bloodsuckerdatum.owner.current, TRAIT_MASQUERADE))
return
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_tremere.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_tremere.dm
index 8f5fd1ece7a..f5f9f83e261 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_tremere.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_tremere.dm
@@ -47,7 +47,7 @@
// Give them the UI to purchase a power.
var/choice = tgui_input_list(bloodsuckerdatum.owner.current, "You have the opportunity to grow more ancient. Spend [round(blood_cost, 1)] blood to upgrade a power.", "Your Blood Thickens...", options)
// Prevent Bloodsuckers from closing/reopning their coffin to spam Levels.
- if(cost_rank && bloodsuckerdatum.bloodsucker_level_unspent <= 0)
+ if(cost_rank && bloodsuckerdatum.GetUnspentRank() <= 0)
return
// Did you choose a power?
if(!choice || !options[choice])
@@ -83,4 +83,4 @@
/datum/bloodsucker_clan/tremere/on_vassal_made(datum/antagonist/bloodsucker/source, mob/living/user, mob/living/target)
. = ..()
to_chat(bloodsuckerdatum.owner.current, span_danger("You have now gained an additional Rank to spend!"))
- bloodsuckerdatum.bloodsucker_level_unspent++
+ bloodsuckerdatum.AdjustUnspentRank(1)
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_ventrue.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_ventrue.dm
index 905527e9c93..60fe9d76965 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_ventrue.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/clans/clan_ventrue.dm
@@ -54,7 +54,7 @@
// Give them the UI to purchase a power.
var/choice = tgui_input_list(bloodsuckerdatum.owner.current, "You have the opportunity to level up your Favorite Vassal. Select a power you wish them to recieve.", "Your Blood Thickens...", options)
// Prevent Bloodsuckers from closing/reopning their coffin to spam Levels.
- if(cost_rank && bloodsuckerdatum.bloodsucker_level_unspent <= 0)
+ if(cost_rank && bloodsuckerdatum.GetUnspentRank() <= 0)
return
// Did you choose a power?
if(!choice || !options[choice])
@@ -125,10 +125,10 @@
return TRUE
if(!istype(vassaldatum))
return FALSE
- if(!bloodsuckerdatum.bloodsucker_level_unspent <= 0)
+ if(!bloodsuckerdatum.GetUnspentRank() <= 0)
bloodsuckerdatum.SpendRank(vassaldatum.owner.current)
return TRUE
- if(bloodsuckerdatum.bloodsucker_blood_volume >= BLOODSUCKER_BLOOD_RANKUP_COST)
+ if(bloodsuckerdatum.GetBloodVolume() >= BLOODSUCKER_BLOOD_RANKUP_COST)
// We don't have any ranks to spare? Let them upgrade... with enough Blood.
to_chat(bloodsuckerdatum.owner.current, span_warning("Do you wish to spend [BLOODSUCKER_BLOOD_RANKUP_COST] Blood to Rank [vassaldatum.owner.current] up?"))
var/static/list/rank_options = list(
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/powers/_powers.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/powers/_powers.dm
index b13f7775d6a..0ccbea886af 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/powers/_powers.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/powers/_powers.dm
@@ -93,7 +93,7 @@
// Have enough blood? Bloodsuckers in a Frenzy don't need to pay them
if(bloodsuckerdatum_power.frenzied)
return TRUE
- if(bloodsuckerdatum_power.bloodsucker_blood_volume < bloodcost)
+ if(bloodsuckerdatum_power.GetBloodVolume() < bloodcost)
to_chat(owner, span_warning("You need at least [bloodcost] blood to activate [name]"))
return FALSE
return TRUE
@@ -164,7 +164,7 @@
return ..()
/datum/action/cooldown/bloodsucker/proc/can_pay_blood(mob/living/carbon/user)
- return bloodsuckerdatum_power ? bloodsuckerdatum_power?.bloodsucker_blood_volume >= 0 : user.blood_volume >= 0
+ return bloodsuckerdatum_power ? bloodsuckerdatum_power?.GetBloodVolume() >= 0 : user.blood_volume >= 0
/datum/action/cooldown/bloodsucker/proc/can_deactivate()
return TRUE
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/powers/feed.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/powers/feed.dm
index 6d83cfe5e80..2f36ed11d67 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/powers/feed.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/powers/feed.dm
@@ -190,7 +190,7 @@
owner.balloon_alert(owner, "your victim's blood is at an unsafe level.")
warning_target_bloodvol = feed_target.blood_volume
- if(bloodsuckerdatum_power.bloodsucker_blood_volume >= bloodsuckerdatum_power.max_blood_volume)
+ if(bloodsuckerdatum_power.GetBloodVolume() >= bloodsuckerdatum_power.max_blood_volume)
user.balloon_alert(owner, "full on blood!")
DeactivatePower()
return
diff --git a/modular_zubbers/code/modules/antagonists/bloodsucker/structures/coffin.dm b/modular_zubbers/code/modules/antagonists/bloodsucker/structures/coffin.dm
index d4565ca6700..982abd3875a 100644
--- a/modular_zubbers/code/modules/antagonists/bloodsucker/structures/coffin.dm
+++ b/modular_zubbers/code/modules/antagonists/bloodsucker/structures/coffin.dm
@@ -202,7 +202,7 @@
/obj/structure/closet/crate/coffin/close(mob/living/user)
- var/datum/antagonist/bloodsucker/bloodsuckerdatum = user?.mind?.has_antag_datum(/datum/antagonist/bloodsucker)
+ var/datum/antagonist/bloodsucker/bloodsuckerdatum = IS_BLOODSUCKER(user)
if(bloodsuckerdatum && user.mob_size > max_mob_size)
if(!HAS_TRAIT_FROM_ONLY(src, TRAIT_COFFIN_ENLARGED, "bloodsucker_coffin"))
if(prompt_coffin_claim(bloodsuckerdatum))
@@ -215,10 +215,9 @@
for(var/atom/thing as anything in contents)
SEND_SIGNAL(thing, COMSIG_ENTER_COFFIN, src, user)
// Only the User can put themself into Torpor. If already in it, you'll start to heal.
- if(user in src)
- if(!resident && !prompt_coffin_claim(bloodsuckerdatum))
- return FALSE
- LockMe(user)
+ if(bloodsuckerdatum && (user in src))
+ if(prompt_coffin_claim(bloodsuckerdatum))
+ LockMe(user)
//Level up if possible.
if(!bloodsuckerdatum.my_clan)
user.balloon_alert("enter a clan!")
@@ -227,7 +226,6 @@
// Level ups cost 30% of your max blood volume, which scales with your rank.
if(!bloodsuckerdatum.frenzied)
bloodsuckerdatum.SpendRank(blood_cost = bloodsuckerdatum.max_blood_volume * BLOODSUCKER_LEVELUP_PERCENTAGE)
- // You're in a Coffin, everything else is done, you're likely here to heal. Let's offer them the opportunity to do so.
bloodsuckerdatum.check_begin_torpor(TORPOR_SKIP_CHECK_DAMAGE)
return TRUE
diff --git a/modular_zubbers/code/modules/client/preferences/middleware/species.dm b/modular_zubbers/code/modules/client/preferences/middleware/species.dm
new file mode 100644
index 00000000000..44ae5305366
--- /dev/null
+++ b/modular_zubbers/code/modules/client/preferences/middleware/species.dm
@@ -0,0 +1,16 @@
+
+// /datum/preference/choiced/species/update_preference(datum/preference/preference, preference_value)
+// // check that the current preference does not have invalid quirks
+// var/quirks = all_quirks
+// Remove any invalid quirks by the species whitelist
+/datum/preference_middleware/species/post_set_preference(mob/user, preference, value)
+ . = ..()
+ var/species_id = value
+ if(!length(GLOB.quirk_species_whitelist) || !species_id)
+ return
+ for(var/quirk_name as anything in preferences.all_quirks)
+ var/datum/quirk/quirk_type = SSquirks.quirks[quirk_name]
+ var/list/species_whitelist = GLOB.quirk_species_whitelist[quirk_type]
+ if(!length(species_whitelist) || (species_id in species_whitelist))
+ continue
+ preferences.all_quirks -= quirk_name
diff --git a/modular_zubbers/code/modules/status_effects/buffs/frenzy.dm b/modular_zubbers/code/modules/status_effects/buffs/frenzy.dm
index 43583baf48a..12ab78ab0d8 100644
--- a/modular_zubbers/code/modules/status_effects/buffs/frenzy.dm
+++ b/modular_zubbers/code/modules/status_effects/buffs/frenzy.dm
@@ -75,4 +75,4 @@
// If duration is not -1, that means we're about to loose frenzy, let's give them some safe time.
if(!bloodsuckerdatum.frenzied || duration > 0 || user.stat != CONSCIOUS )
return
- user.adjustFireLoss(1 + (bloodsuckerdatum.humanity_lost / 10))
+ user.adjustFireLoss(1 + (bloodsuckerdatum.GetHumanityLost() / 10))
diff --git a/modular_zubbers/modules/quirks/code/_quirk.dm b/modular_zubbers/modules/quirks/code/_quirk.dm
index 96b96d22c23..dd29c1e234e 100644
--- a/modular_zubbers/modules/quirks/code/_quirk.dm
+++ b/modular_zubbers/modules/quirks/code/_quirk.dm
@@ -1,3 +1,19 @@
/datum/quirk
- /// Flags related to species whitelists.
- var/quirk_whitelist_flags // Whitelist bitflags in code/__DEFINES/~~bubber_defines/quirk_whitelist.dm
+ /// List of species that this quirk is valid for, or empty if it's valid for all species. Only use species ids here.
+ var/list/species_whitelist = list()
+
+/datum/quirk/add_to_holder(mob/living/new_holder, quirk_transfer, client/client_source)
+ if(!can_add(new_holder))
+ CRASH("Attempted to add quirk to holder that can't have it.")
+ . = ..()
+
+/// Returns true if the quirk is valid for the target, call parent so qurk_species_whitelist can be checked.
+/datum/quirk/proc/can_add(mob/target)
+ SHOULD_CALL_PARENT(TRUE)
+ if(length(species_whitelist))
+ if(!ishuman(target))
+ return FALSE
+ var/mob/living/carbon/human = target
+ if(!(human?.dna.species.id in species_whitelist))
+ return FALSE
+ return TRUE
diff --git a/modular_zubbers/modules/quirks/code/negative_quirks/sol_weakness.dm b/modular_zubbers/modules/quirks/code/negative_quirks/sol_weakness.dm
new file mode 100644
index 00000000000..342ebd1f557
--- /dev/null
+++ b/modular_zubbers/modules/quirks/code/negative_quirks/sol_weakness.dm
@@ -0,0 +1,96 @@
+
+#define COFFIN_HEALING_COST 0.5
+
+/datum/quirk/sol_weakness
+ name = "Sol Weakness"
+ icon = FA_ICON_SUN
+ desc = "Your sub-species of the Hemophage virus renders you weak to Solar radiation, \
+ you will have to hide in a coffin or a closet during the day, or risk burning to a crisp. \
+ Thankfully, you will also heal your wounds at half cost in a coffin."
+ gain_text = span_warning("You feel a sudden weakness in your body, and a burning sensation on your skin. \
+ You should find a coffin to hide in during the day.")
+ lose_text = span_notice("You feel safe in Sol's embrace once more.")
+ medical_record_text = "Patient's strain of the hemophage virus is weak to sunlight. \
+ They will have to hide in a coffin or a closet during the day, or risk burning to a crisp."
+ value = -4
+ hardcore_value = 6
+ species_whitelist = list(SPECIES_HEMOPHAGE)
+ quirk_flags = QUIRK_HIDE_FROM_SCAN | QUIRK_HUMAN_ONLY
+ COOLDOWN_DECLARE(sun_burn)
+
+/datum/quirk/sol_weakness/add()
+ if(!quirk_holder.hud_used)
+ RegisterSignal(quirk_holder, COMSIG_MOB_HUD_CREATED, PROC_REF(add_sun_timer_hud))
+ return
+ add_sun_timer_hud()
+
+/datum/quirk/sol_weakness/remove()
+ SSsunlight.remove_sun_sufferer(quirk_holder)
+ UnregisterSignal(SSsunlight, list(COMSIG_SOL_RISE_TICK, COMSIG_SOL_WARNING_GIVEN))
+
+/datum/quirk/sol_weakness/can_add(mob/target)
+ . = ..()
+ if(!.)
+ return
+ return !IS_BLOODSUCKER(target)
+
+/datum/quirk/sol_weakness/process(seconds_per_tick)
+ var/datum/status_effect/blood_regen_active/regen = quirk_holder?.has_status_effect(/datum/status_effect/blood_regen_active)
+ if(in_coffin() && ishemophage(quirk_holder))
+ if(!regen)
+ return
+ // cheaper healing as long as you're in a coffin
+ regen.cost_blood = COFFIN_HEALING_COST
+ else if(regen?.blood_to_health_multiplier == COFFIN_HEALING_COST)
+ regen.cost_blood = initial(regen.blood_to_health_multiplier)
+
+/datum/quirk/sol_weakness/proc/add_sun_timer_hud()
+ if(!quirk_holder.hud_used)
+ CRASH("Sol Weakness quirk holder has no HUD")
+ SSsunlight.add_sun_sufferer(quirk_holder)
+ UnregisterSignal(quirk_holder, COMSIG_MOB_HUD_CREATED)
+ RegisterSignal(SSsunlight, COMSIG_SOL_RISE_TICK, PROC_REF(sun_risen))
+ RegisterSignal(SSsunlight, COMSIG_SOL_WARNING_GIVEN, PROC_REF(sun_warning))
+
+/datum/quirk/sol_weakness/proc/sun_risen()
+ SIGNAL_HANDLER
+ if(!istype(quirk_holder.loc, /obj/structure))
+ sun_burn()
+ else
+ if(in_coffin())
+ quirk_holder.add_mood_event("vampsleep", /datum/mood_event/coffinsleep/quirk)
+ sun_burn_message(span_warning("The sun is up, but you safely rest in your [quirk_holder.loc]."))
+ else
+ quirk_holder.add_mood_event("vampsleep", /datum/mood_event/daylight_bad_sleep)
+ quirk_holder.adjustFireLoss(1)
+ sun_burn_message(span_warning("[quirk_holder.loc] is not a coffin, but it keeps you safe enough."))
+
+/datum/quirk/sol_weakness/proc/sun_burn()
+ quirk_holder.add_mood_event("vampsleep", /datum/mood_event/daylight_sun_scorched)
+ if(quirk_holder.blood_volume > BLOOD_VOLUME_MAXIMUM * 0.4)
+ quirk_holder.blood_volume -= 5
+ sun_burn_message(span_warning("The sun burns your skin, but your blood protects you from the worst of it..."))
+ quirk_holder.adjustFireLoss(1)
+ return
+ sun_burn_message(span_userdanger("THE SUN, IT BURNS!"))
+ quirk_holder.adjustFireLoss(2)
+ quirk_holder.adjust_fire_stacks(1)
+ quirk_holder.ignite_mob()
+
+/datum/quirk/sol_weakness/proc/sun_burn_message(text)
+ SIGNAL_HANDLER
+ if(!COOLDOWN_FINISHED(src, sun_burn))
+ return
+ to_chat(quirk_holder, text)
+ COOLDOWN_START(src, sun_burn, 30 SECONDS)
+
+/datum/quirk/sol_weakness/proc/sun_warning(atom/source, danger_level, vampire_warning_message, vassal_warning_message)
+ SIGNAL_HANDLER
+ if(danger_level == DANGER_LEVEL_SOL_ROSE)
+ vampire_warning_message = span_userdanger("Solar flares bombard the station with deadly UV light! Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk death!")
+ SSsunlight.warn_notify(quirk_holder, danger_level, vampire_warning_message)
+
+/datum/quirk/sol_weakness/proc/in_coffin()
+ return istype(quirk_holder.loc, /obj/structure/closet/crate/coffin)
+
+#undef COFFIN_HEALING_COST
diff --git a/tgstation.dme b/tgstation.dme
index 5031f7a120f..755e68ea0e9 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -480,7 +480,6 @@
#include "code\__DEFINES\~~bubber_defines\footsteps.dm"
#include "code\__DEFINES\~~bubber_defines\jobs.dm"
#include "code\__DEFINES\~~bubber_defines\misc.dm"
-#include "code\__DEFINES\~~bubber_defines\quirk_whitelist.dm"
#include "code\__DEFINES\~~bubber_defines\robot_defines.dm"
#include "code\__DEFINES\~~bubber_defines\say.dm"
#include "code\__DEFINES\~~bubber_defines\signals.dm"
@@ -8190,10 +8189,11 @@
#include "modular_zubbers\code\_globalvars\lists\maintenance_loot_rare.dm"
#include "modular_zubbers\code\_globalvars\lists\maintenance_loot_trash.dm"
#include "modular_zubbers\code\_globalvars\lists\maintenance_loot_uncommon.dm"
+#include "modular_zubbers\code\_globalvars\lists\quirks.dm"
#include "modular_zubbers\code\_globalvars\lists\~maintenance_loot.dm"
#include "modular_zubbers\code\controllers\subsystem\air.dm"
#include "modular_zubbers\code\controllers\subsystem\mapping.dm"
-#include "modular_zubbers\code\controllers\subsystem\sol_subsystem.dm"
+#include "modular_zubbers\code\controllers\subsystem\processing\sol_subsystem.dm"
#include "modular_zubbers\code\datums\action.dm"
#include "modular_zubbers\code\datums\armor_overrides.dm"
#include "modular_zubbers\code\datums\department_security.dm"
@@ -8377,6 +8377,7 @@
#include "modular_zubbers\code\modules\cargo\packs\security.dm"
#include "modular_zubbers\code\modules\cargo\packs\service.dm"
#include "modular_zubbers\code\modules\client\autopunctuation\preferences.dm"
+#include "modular_zubbers\code\modules\client\preferences\middleware\species.dm"
#include "modular_zubbers\code\modules\client\verbs\character_directory.dm"
#include "modular_zubbers\code\modules\clothing\undershirt.dm"
#include "modular_zubbers\code\modules\clothing\gloves\syndicate.dm"
@@ -8646,6 +8647,7 @@
#include "modular_zubbers\modules\pollution\code\pollutants_generic.dm"
#include "modular_zubbers\modules\quirks\code\_quirk.dm"
#include "modular_zubbers\modules\quirks\code\negative_quirks\sensitive_snout.dm"
+#include "modular_zubbers\modules\quirks\code\negative_quirks\sol_weakness.dm"
#include "modular_zubbers\modules\quirks\code\neutral_quirks\hungry.dm"
#include "modular_zubbers\modules\quirks\code\neutral_quirks\waddle.dm"
#include "modular_zubbers\modules\security\secmed\automapper.dm"
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx
index b7922c09b44..15533a1cb35 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx
@@ -317,8 +317,6 @@ export function QuirksPage(props) {
const {
max_positive_quirks: maxPositiveQuirks,
quirk_blacklist: quirkBlacklist,
- // BUBBER EDIT ADDITION - Species quirks
- quirk_species_whitelist: quirkSpeciesWhitelist,
quirk_info: quirkInfo,
points_enabled: pointsEnabled,
} = server_data.quirks; // SKYRAT EDIT - Quirks balance refactor
@@ -356,7 +354,6 @@ export function QuirksPage(props) {
return 'You need to be a veteran to select this quirk, apply today!';
}
// SKYRAT EDIT END
-
const selectedQuirkNames = selectedQuirks.map((quirkKey) => {
return quirkInfo[quirkKey].name;
});
@@ -377,26 +374,22 @@ export function QuirksPage(props) {
}
// BUBBER EDIT ADDITION START - Species quirks
-
- // Iterate through all whitelists
-
- for (const whitelist of quirkSpeciesWhitelist) {
- const currentSpeciesName = data.character_preferences.misc.species;
-
- // Format: Quirk ([0]), Species define ([1])
- const whitelistSpeciesName = whitelist[1];
- // Quirk is not in the whitelisted ones, continue to next list
- if (whitelist.indexOf(quirk.name) === -1) {
- continue;
- }
-
- // If the selected species is NOT in the list, do not give the quirk
- // Otherwise, give the quirk
- if (whitelist.indexOf(currentSpeciesName) === -1) {
- return `This quirk can only be taken by ${whitelistSpeciesName}!`;
+ const currentSpeciesID = data.character_preferences.misc.species;
+ // keys are the species_ids, values are the species names
+ const speciesWhitelistKeys = Object.keys(quirk.species_whitelist);
+ if (
+ speciesWhitelistKeys?.length &&
+ !speciesWhitelistKeys.includes(currentSpeciesID)
+ ) {
+ const speciesWhitelistNames = Object.values(
+ quirk.species_whitelist,
+ );
+ if (speciesWhitelistNames.length === 1) {
+ return `This quirk can only be taken by the ${speciesWhitelistNames[0]} species.`;
}
+ const speciesList = speciesWhitelistNames.join(', ');
+ return `This quirk can only be taken by the following species: ${speciesList}.`;
}
-
// BUBBER EDIT ADDITION END
return undefined;
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
index db5f069ac06..87518a15109 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
@@ -93,6 +93,7 @@ export type Quirk = {
customizable: boolean;
customization_options?: string[];
veteran_only: boolean; // SKYRAT EDIT - Veteran quirks
+ species_whitelist: Record[]; // BUBBER EDIT ADDITION - Species quirks
};
// SKYRAT EDIT START
@@ -137,7 +138,6 @@ export type QuirkInfo = {
max_positive_quirks: number;
quirk_info: Record;
quirk_blacklist: string[][];
- quirk_species_whitelist: string[][]; // BUBBER EDIT ADDITION - Species quirks
points_enabled: boolean;
};