From 82edf376e583cc6ce03c5ed922be0c4b67e498ec Mon Sep 17 00:00:00 2001
From: nikothedude <59709059+nikothedude@users.noreply.github.com>
Date: Sun, 24 May 2026 17:40:06 -0400
Subject: [PATCH] Refactors lycan form into a status effect + adds a examine
blurb when you are a lycan (#5653)
## About The Pull Request
Title.
## Why It's Good For The Game
The refactor makes it a bit more sane and enables the blurb.
The blurb is crucial for security, who woulkd have no idea what a
boosted lycan can do otherwise, and how to counter it.
Also fixes a bug where you could get stuck in lycan form on death.
## Proof Of Testing
Screenshots/Videos
## Changelog
:cl:
add: A examine blurb explaining what lycans can do
fix: A bug where you could get stuck in lycan form if you die
/:cl:
---
.../species/lycans/lycan_status.dm | 134 ++++++++++++++++++
.../species/lycans/organs/lycan_brain.dm | 61 +-------
tgstation.dme | 1 +
3 files changed, 141 insertions(+), 55 deletions(-)
create mode 100644 modular_zubbers/code/modules/customization/species/lycans/lycan_status.dm
diff --git a/modular_zubbers/code/modules/customization/species/lycans/lycan_status.dm b/modular_zubbers/code/modules/customization/species/lycans/lycan_status.dm
new file mode 100644
index 00000000000..8674359b705
--- /dev/null
+++ b/modular_zubbers/code/modules/customization/species/lycans/lycan_status.dm
@@ -0,0 +1,134 @@
+#define BEAST_FORM_EXPOSITION_LINK "beast_form_exposition_link"
+
+/datum/status_effect/beast_form
+ id = "lycan_beast_form"
+ alert_type = null // TODO - maybe add an alert??
+ var/datum/species/initial_species
+
+/datum/status_effect/beast_form/on_apply()
+ . = ..()
+ if (!.)
+ return FALSE
+
+ var/mob/living/carbon/human_owner = owner
+ if (!istype(human_owner))
+ return FALSE
+
+ var/obj/item/organ/brain/lycan/lycan_brain = human_owner.get_organ_by_type(/obj/item/organ/brain/lycan)
+ if (!istype(lycan_brain))
+ return FALSE
+
+ initial_species = human_owner.dna.species
+
+ var/datum/species/human/cursekin/current_wolf = human_owner.dna.species
+ if(!istype(current_wolf))
+ return FALSE
+
+ human_owner.visible_message(span_warning("[human_owner] turns [human_owner.p_their()] head to the sky and howls, rapidly growing and transforming into a horrible beast!"))
+
+ var/client/target_client = human_owner.client
+ if (!isnull(target_client))
+ var/name = human_owner.real_name
+ var/slot = target_client.prefs.read_preference(/datum/preference/numeric/cursekin_char_slot)
+ var/transfer = TRUE
+ if (isnull(lycan_brain.last_slot))
+ lycan_brain.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(human_owner, span_warning("Your selected slot is not a lycan! Defaulting to simply changing your species..."))
+ target_client.prefs.load_character(lycan_brain.last_slot)
+ human_owner.set_species(current_wolf.lycanthropy_species, TRUE, TRUE, FALSE)
+ lycan_brain.last_slot = null // allows for easier switching in later procs
+ transfer = FALSE
+
+ if (transfer)
+ target_client.prefs.safe_transfer_prefs_to_with_damage(human_owner)
+ human_owner.real_name = name
+ human_owner.name = name
+ SSquirks.OverrideQuirks(human_owner, target_client)
+ human_owner.dna.update_dna_identity()
+
+ target_client.prefs.load_character(lycan_brain.last_slot)
+
+ ADD_TRAIT(human_owner, TRAIT_BEAST_FORM, SPECIES_TRAIT)
+ playsound(human_owner, 'modular_zubbers/code/modules/customization/species/lycans/transform.ogg', 50)
+
+/datum/status_effect/beast_form/on_remove()
+ . = ..()
+
+ var/mob/living/carbon/human_owner = owner
+ if (!istype(human_owner))
+ return
+
+ var/obj/item/organ/brain/lycan/lycan_brain = human_owner.get_organ_by_type(/obj/item/organ/brain/lycan)
+ if (!istype(lycan_brain))
+ return
+
+ human_owner.visible_message(span_warning("[human_owner] shrinks down, their fur receding!"))
+
+ if (lycan_brain.last_slot)
+ var/client/target_client = human_owner.client
+ if (isnull(target_client))
+ human_owner.set_species(initial_species, TRUE, TRUE, FALSE)
+ else
+ target_client.prefs.load_character(lycan_brain.last_slot)
+ target_client.prefs.safe_transfer_prefs_to_with_damage(human_owner)
+ SSquirks.OverrideQuirks(human_owner, target_client)
+ human_owner.dna.update_dna_identity()
+ else
+ human_owner.set_species(initial_species, TRUE, TRUE, FALSE)
+
+ REMOVE_TRAIT(human_owner, TRAIT_BEAST_FORM, SPECIES_TRAIT)
+ playsound(human_owner, 'modular_zubbers/code/modules/customization/species/lycans/transform.ogg', 50)
+
+/datum/status_effect/beast_form/tick(seconds_between_ticks)
+ . = ..()
+
+ var/mob/living/carbon/human_owner = owner
+ if (!istype(human_owner))
+ qdel(src)
+ return
+
+ if (human_owner.stat >= HARD_CRIT || human_owner.dna.species.id != SPECIES_LYCAN)
+ qdel(src)
+ return
+
+/datum/status_effect/beast_form/get_examine_text()
+ return span_notice("The NT employee manual has a entry on this species... Recall what you read?")
+
+/datum/status_effect/beast_form/Topic(href, list/href_list)
+ . = ..()
+
+ if (href_list[BEAST_FORM_EXPOSITION_LINK])
+ print_exposition(usr)
+
+/datum/status_effect/beast_form/proc/print_exposition(mob/user)
+ var/list/render_list = list()
+
+ render_list += "Legends of lycanthropy and animalistic shapeshifters have always existed, but were not confirmed until a century ago when the Cursekin species was \
+ officially recognized by most polities across the orion arm. The origin for their curse may vary by Cursekin - some from a god, some from genetics - but each \
+ has the ability to transform into a terrifying beast with claws like razors."
+ render_list += "
"
+ render_list += span_notice("Lycans and related may come in various forms, but each have similarities in how they function.")
+ render_list += span_notice("\nLycans possess [EXAMINE_HINT("exceptionally strong claws")], about as strong as a [EXAMINE_HINT("circular saw")] without the armor penetration.")
+ render_list += span_notice("\nLycans also have [EXAMINE_HINT("significant damage resistance")] to brute, and to a lesser extent, burn.")
+ render_list += span_notice("\nHowever, Lycans [EXAMINE_HINT("cannot wear any clothes")] and cannot have synthetic organs.")
+ render_list += span_notice("\nAdditionally, each Lycan possesses a [EXAMINE_HINT("marked weakness to silver")], taking [EXAMINE_HINT("increased damage")] from weapons comprised of it.")
+ render_list += span_notice("\nFinally, Lycans [EXAMINE_HINT("cannot use batons")] nor [EXAMINE_HINT("most firearms")] due to the size of their fingers.")
+ render_list += span_notice("\nThe Lycan form can be [EXAMINE_HINT("toggled at will")], or [EXAMINE_HINT("forcefully removed")] by [EXAMINE_HINT("knocking them into hard-crit")].")
+ render_list += "
"
+ render_list += span_warning("While mostly docile, tamed, and relatively weak, it is rumored that some Lycans harbor a [EXAMINE_HINT("exceptionally capable")] form of the curse. Keep an eye out for any of the following:")
+ render_list += span_warning("\n * Extremely powerful claws")
+ render_list += span_warning("\n * Aggressive health regeneration")
+ render_list += span_warning("\n * Immunity to shoves and resistance to pain/stunning")
+ render_list += span_warning("\n * The ability to shrug off any amount of pain and keep sprinting")
+ render_list += span_boldwarning("\nIf you find yourself facing a Lycan with these traits, take these precautions.")
+ render_list += span_warning("\n * Avoid physical weaponry and stamina weapons - their regeneration rapidly heals brute and stamina drain")
+ render_list += span_warning("\n * Use silver weaponry - they deal extra burn damage")
+ render_list += span_warning("\n * Use aerosolized silver - they cannot use internals and will burn on contact with it")
+
+ var/output = jointext(render_list, "")
+
+ to_chat(user, custom_boxed_message("blue_box", output), trailing_newline = FALSE, type = MESSAGE_TYPE_INFO)
+
+#undef BEAST_FORM_EXPOSITION_LINK
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 2827c232a31..709bb14154d 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
@@ -21,68 +21,19 @@
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
- if(!istype(current_wolf) && HAS_TRAIT_FROM(owner, TRAIT_BEAST_FORM, SPECIES_TRAIT))
- return
- 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
- SSquirks.OverrideQuirks(owner, target_client)
- owner.dna.update_dna_identity()
-
- target_client.prefs.load_character(last_slot)
-
- ADD_TRAIT(owner, TRAIT_BEAST_FORM, SPECIES_TRAIT)
- playsound(owner, 'modular_zubbers/code/modules/customization/species/lycans/transform.ogg', 50)
+ owner.apply_status_effect(/datum/status_effect/beast_form)
/obj/item/organ/brain/lycan/proc/leave_beast_form()
- var/datum/species/current_wolf = owner.dna.species
- 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!"))
-
- 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)
- SSquirks.OverrideQuirks(owner, target_client)
- owner.dna.update_dna_identity()
- else
- owner.set_species(/datum/species/human/cursekin, TRUE, TRUE, FALSE)
- REMOVE_TRAIT(owner, TRAIT_BEAST_FORM, SPECIES_TRAIT)
- playsound(owner, 'modular_zubbers/code/modules/customization/species/lycans/transform.ogg', 50)
+ owner.remove_status_effect(/datum/status_effect/beast_form)
/obj/item/organ/brain/lycan/proc/toggle_beast_form(mob/user)
set name = "Enter/Leave Lycan Form"
set desc = "Succumb to the rage and turn into a lycan."
set category = "Lycan"
+
if(!user)
return
- if(!HAS_TRAIT(user, TRAIT_BEAST_FORM))
+ if(user.has_status_effect(/datum/status_effect/beast_form))
+ leave_beast_form()
+ else
enter_beast_form()
- else if(HAS_TRAIT(user, TRAIT_BEAST_FORM))
- leave_beast_form()
-
-/obj/item/organ/brain/lycan/on_death(seconds_per_tick, times_fired)
- . = ..()
- if(!owner)
- return
- if(HAS_TRAIT(owner, TRAIT_BEAST_FORM))
- leave_beast_form()
diff --git a/tgstation.dme b/tgstation.dme
index f874d399173..96fa82084a1 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -9503,6 +9503,7 @@
#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_status.dm"
#include "modular_zubbers\code\modules\customization\species\lycans\lycan_verbs.dm"
#include "modular_zubbers\code\modules\customization\species\lycans\martial_arts.dm"
#include "modular_zubbers\code\modules\customization\species\lycans\bodyparts\lycan_bodyparts.dm"