From be4c58986c8b79a2d57ae555e4beeb9992121847 Mon Sep 17 00:00:00 2001
From: nikothedude <59709059+nikothedude@users.noreply.github.com>
Date: Sun, 17 May 2026 14:40:01 -0400
Subject: [PATCH] Allows cursekin to load a preference slot instead of simply
changing species. (#5568)
## About The Pull Request
Title.
Adds a new preference option that allows you to designate a character
slot as your lycan transformation. Does not transfer quirks and does not
change the name.
Additionally, allows peopel to use the lycan species as a prefs-only
species for customization.
### Note
This will likely transfer languages and a few other things, but nothing
gamechanging. If the SAD does it (excluding quirks), this probably also
does it.
## Why It's Good For The Game
Adds for customization of the lycan form. Hair, Augments (NOT IMPLANTS).
Even taur mode.
## Proof Of Testing
Screenshots/Videos
## Changelog
:cl:
add: Cursekin can select a character slot to transform into rather than
just siwtching species.
/:cl:
---
...eenshot_humanoids__datum_species_lycan.png | Bin 1073 -> 1073 bytes
.../species/lycans/_lycan_species.dm | 17 ++++-
.../species/lycans/cursekin_prefs.dm | 20 ++++++
.../species/lycans/organs/lycan_brain.dm | 60 ++++++++++++++----
tgstation.dme | 1 +
.../bubbers/cursekin.tsx | 12 ++++
6 files changed, 96 insertions(+), 14 deletions(-)
create mode 100644 modular_zubbers/code/modules/customization/species/lycans/cursekin_prefs.dm
create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/bubbers/cursekin.tsx
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lycan.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lycan.png
index 6c6219f0e07e3d13ce9cfd93d04294ff35ee4685..3ac91e03901733b62715ca4b472d39291b3e8b76 100644
GIT binary patch
delta 72
zcmdnUv5{kfzU@pQwq0WEbA;IR)Z|imSkx6HTLoBmim_`cORFhJGB7asa55Wc$eZdY
cu`)6+F)-+=$?g_opDx74-s5PxG3O{V03k{aXaE2J
delta 72
zcmdnUv5{kfzHNht>+JBrCNCE~HMtN2BXtGId^@XI;elGp(rOBl3=9koDjEhF@}@dU
cEX=G-jLf=fvU4JW>fD{ycA6P%%sI*o0Hx^?9RL6T
diff --git a/modular_zubbers/code/modules/customization/species/lycans/_lycan_species.dm b/modular_zubbers/code/modules/customization/species/lycans/_lycan_species.dm
index 0eb445ea597..2a8f513f137 100644
--- a/modular_zubbers/code/modules/customization/species/lycans/_lycan_species.dm
+++ b/modular_zubbers/code/modules/customization/species/lycans/_lycan_species.dm
@@ -50,10 +50,12 @@
)
no_equip_flags = ITEM_SLOT_ICLOTHING | ITEM_SLOT_OCLOTHING | ITEM_SLOT_GLOVES | ITEM_SLOT_FEET | ITEM_SLOT_SUITSTORE | ITEM_SLOT_BACK | ITEM_SLOT_BELT | ITEM_SLOT_EARS | ITEM_SLOT_HEAD | ITEM_SLOT_MASK | ITEM_SLOT_EYES | ITEM_SLOT_BACK | ITEM_SLOT_NECK
+ always_customizable = TRUE
+ sort_bottom = TRUE
/datum/species/lycan/prepare_human_for_preview(mob/living/carbon/human/lycan)
var/main_color = "#362d23"
- var/secondary_color = "#9c5852"
+ var/secondary_color = "#bb1607"
var/tertiary_color = "#CCF6E2"
lycan.dna.features["mcolor"] = main_color
lycan.dna.features["mcolor2"] = secondary_color
@@ -68,15 +70,24 @@
race = /datum/species/lycan
/datum/species/lycan/get_species_description()
- return list(placeholder_description)
+ return list("The transformed version of a Cursekin. Only available for customization of a Cursekin's transformed form.")
/datum/species/lycan/get_species_lore()
- return list(placeholder_lore)
+ return list("See Cursekin lore.")
/datum/species/lycan/on_species_gain(mob/living/carbon/human/gainer, datum/species/old_species, pref_load, regenerate_icons = TRUE)
. = ..()
gainer.AddElement(/datum/element/inorganic_rejection)
+ gainer.dna.features["body_size"] = 2
+ gainer.maptext_height = 32 * gainer.dna.features["body_size"] //Adjust runechat height
+ gainer.mob_size = MOB_SIZE_LARGE
+ gainer.dna.update_body_size()
/datum/species/lycan/on_species_loss(mob/living/carbon/human/loser, datum/species/new_species, pref_load)
. = ..()
loser.RemoveElement(/datum/element/inorganic_rejection)
+ if(!loser.has_quirk(/datum/quirk/oversized))
+ loser.dna.features["body_size"] = loser?.client?.prefs?.read_preference(/datum/preference/numeric/body_size) || 1
+ loser.maptext_height = 32 * loser.dna.features["body_size"]
+ loser.mob_size = MOB_SIZE_HUMAN
+ loser.dna.update_body_size()
diff --git a/modular_zubbers/code/modules/customization/species/lycans/cursekin_prefs.dm b/modular_zubbers/code/modules/customization/species/lycans/cursekin_prefs.dm
new file mode 100644
index 00000000000..7859271a858
--- /dev/null
+++ b/modular_zubbers/code/modules/customization/species/lycans/cursekin_prefs.dm
@@ -0,0 +1,20 @@
+/datum/preference/numeric/cursekin_char_slot
+ category = PREFERENCE_CATEGORY_CHARACTER_BASICS
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "cursekin_char_slot"
+ can_randomize = FALSE
+ minimum = 0
+ maximum = /datum/preferences::max_save_slots
+
+/datum/preference/numeric/cursekin_char_slot/has_relevant_feature(datum/preferences/preferences)
+ . = ..()
+ if (!.)
+ return FALSE
+
+ return ispath(preferences.read_preference(/datum/preference/choiced/species), /datum/species/human/cursekin)
+
+/datum/preference/numeric/cursekin_char_slot/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
+ return
+
+/datum/preference/numeric/cursekin_char_slot/create_default_value()
+ return 0
diff --git a/modular_zubbers/code/modules/customization/species/lycans/organs/lycan_brain.dm b/modular_zubbers/code/modules/customization/species/lycans/organs/lycan_brain.dm
index 96432d0b572..107cab3d7e6 100644
--- a/modular_zubbers/code/modules/customization/species/lycans/organs/lycan_brain.dm
+++ b/modular_zubbers/code/modules/customization/species/lycans/organs/lycan_brain.dm
@@ -3,6 +3,23 @@
desc = "A larger than average, albeit slightly smoother brain. The hypothalamus seems larger than normal." // I read in a random medical artical that the hypothalamus controls aggression.
actions_types = list(/datum/action/cooldown/spell/beast_form)
var/list/removed_quirks = list()
+ var/last_slot
+
+/obj/item/organ/brain/lycan/on_mob_insert(mob/living/carbon/brain_owner, special, movement_flags)
+ . = ..()
+
+ addtimer(CALLBACK(src, PROC_REF(try_getting_slot)), 0.1 SECONDS)
+
+// no remove. we want it to REMEMBER.
+
+/// A delayed getter for the current slot of the mob. Used because theres no easy way to access the client when a mob is spawend...
+/obj/item/organ/brain/lycan/proc/try_getting_slot()
+ if (last_slot)
+ return
+
+ var/client/target_client = owner?.client
+ if (!isnull(target_client))
+ last_slot = target_client.prefs.savefile.get_entry("default_slot")
/obj/item/organ/brain/lycan/proc/enter_beast_form()
var/datum/species/human/cursekin/current_wolf = owner.dna.species
@@ -15,12 +32,30 @@
removed_quirks += bad_quirk.type
owner.remove_quirk(bad_quirk.type)
owner.visible_message(span_warning("[owner] grows massive, their body quickly getting covered in fur!"))
- owner.set_species(current_wolf.lycanthropy_species, TRUE, TRUE, FALSE)
+ var/client/target_client = owner.client
+ if (!isnull(target_client))
+ var/name = owner.real_name
+ var/slot = target_client.prefs.read_preference(/datum/preference/numeric/cursekin_char_slot)
+ var/transfer = TRUE
+ if (isnull(last_slot))
+ last_slot = target_client.prefs.savefile.get_entry("default_slot")
+ target_client.prefs.load_character(slot)
+ if (!ispath(target_client.prefs.read_preference(/datum.preference/choiced/species), current_wolf.lycanthropy_species))
+ to_chat(owner, span_warning("Your selected slot is not a lycan! Defaulting to simply changing your species..."))
+ target_client.prefs.load_character(last_slot)
+ owner.set_species(current_wolf.lycanthropy_species, TRUE, TRUE, FALSE)
+ last_slot = null // allows for easier switching in later procs
+ transfer = FALSE
+
+ if (transfer)
+ target_client.prefs.safe_transfer_prefs_to_with_damage(owner)
+ owner.real_name = name
+ owner.name = name
+ owner.dna.update_dna_identity()
+
+ target_client.prefs.load_character(last_slot)
+
ADD_TRAIT(owner, TRAIT_BEAST_FORM, SPECIES_TRAIT)
- owner.dna.features["body_size"] = 2
- owner.maptext_height = 32 * owner.dna.features["body_size"] //Adjust runechat height
- owner.dna.update_body_size()
- owner.mob_size = MOB_SIZE_LARGE
playsound(owner, 'modular_zubbers/code/modules/customization/species/lycans/transform.ogg', 50)
/obj/item/organ/brain/lycan/proc/leave_beast_form()
@@ -28,13 +63,16 @@
if(isnull(current_wolf) && !HAS_TRAIT_FROM(owner, TRAIT_BEAST_FORM, SPECIES_TRAIT))
return
owner.visible_message(span_warning("[owner] shrinks down, their fur receding!"))
- owner.set_species(/datum/species/human/cursekin, TRUE, TRUE, FALSE)
+
+ if (last_slot)
+ var/client/target_client = owner.client
+ if (!isnull(target_client))
+ target_client.prefs.load_character(last_slot)
+ target_client.prefs.safe_transfer_prefs_to_with_damage(owner)
+ owner.dna.update_dna_identity()
+ else
+ owner.set_species(/datum/species/human/cursekin, TRUE, TRUE, FALSE)
REMOVE_TRAIT(owner, TRAIT_BEAST_FORM, SPECIES_TRAIT)
- if(!owner.has_quirk(/datum/quirk/oversized))
- owner.dna.features["body_size"] = owner?.client?.prefs ?owner?.client?.prefs?.read_preference(/datum/preference/numeric/body_size) : 1
- owner.maptext_height = 32 * owner.dna.features["body_size"]
- owner.dna.update_body_size()
- owner.mob_size = MOB_SIZE_HUMAN
playsound(owner, 'modular_zubbers/code/modules/customization/species/lycans/transform.ogg', 50)
if(removed_quirks)
for(var/typepath in removed_quirks)
diff --git a/tgstation.dme b/tgstation.dme
index a61cca12cac..0a26d389a49 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -9502,6 +9502,7 @@
#include "modular_zubbers\code\modules\customization\species\lycans\_cursekin_species.dm"
#include "modular_zubbers\code\modules\customization\species\lycans\_lycan_species.dm"
#include "modular_zubbers\code\modules\customization\species\lycans\cursekin_blacklisted_quirks.dm"
+#include "modular_zubbers\code\modules\customization\species\lycans\cursekin_prefs.dm"
#include "modular_zubbers\code\modules\customization\species\lycans\lycan_verbs.dm"
#include "modular_zubbers\code\modules\customization\species\lycans\bodyparts\lycan_bodyparts.dm"
#include "modular_zubbers\code\modules\customization\species\lycans\organs\lycan_brain.dm"
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/bubbers/cursekin.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/bubbers/cursekin.tsx
new file mode 100644
index 00000000000..d7fdc3d587a
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/bubbers/cursekin.tsx
@@ -0,0 +1,12 @@
+import {
+ type Feature,
+ FeatureNumberInput,
+} from '../../base';
+
+export const cursekin_char_slot: Feature = {
+ name: 'Lycan Character Slot',
+ description:
+ "The index of the character slot, from top to bottom, of the lycan character slot that will be used when you transform. \
+ Does not use quirks, implants, or the set name. Set to a non-lycan slot to simply switch species instead of loading a slot.",
+ component: FeatureNumberInput,
+};