diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index c3bb92898ab..5640245437e 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -1,4 +1,4 @@ - // Mob signals +// Mob signals #define COMSIG_MOB_ATTACK "mob_attack" #define COMSIG_MOB_SAY "mob_say" #define COMSIG_MOB_CLICKON "mob_clickon" @@ -14,3 +14,9 @@ // Collar signals #define COMSIG_CARBON_GAIN_COLLAR "carbon_gain_collar" #define COMSIG_CARBON_LOSE_COLLAR "carbon_lose_collar" + +// Living death signal +#define COMSIG_LIVING_DEATH "mob_death" + +// Living revive signal +#define COMSIG_LIVING_REVIVE "mob_revive" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 4398571d5cd..f06fcfa615d 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -518,3 +518,4 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define ADVENTURER_TRAIT "adventurer" #define TRAIT_SLAVEBOURNE "slavebourne" +#define TRAIT_SLAVEBOURNE_EXAMINE "trait_slavebourne_examine" diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm index 0ab55acedf9..a3f94c97696 100644 --- a/code/datums/traits/negative.dm +++ b/code/datums/traits/negative.dm @@ -629,20 +629,22 @@ /datum/quirk/slavebourne name = "Slavebourne" - desc = "You are magically bound to servitude. Without a master controlling you through a cursed collar, your abilities are severely diminished. A cursed collar appears near you." + desc = "You have an innate need to be collared and controlled. Without a master, your abilities are diminished." value = -6 mob_trait = TRAIT_SLAVEBOURNE - gain_text = span_danger("You feel purposeless without a master to serve...") - lose_text = span_notice("You feel free from the need for a master.") - medical_record_text = "They exhibit a magical dependency on being controlled." - var/debuff_active = FALSE // Start false, will be set after initial check + gain_text = span_notice("You want to rid yourself of the pain and harshness of choice..") + lose_text = span_notice("You feel more independent.") + medical_record_text = "Patient exhibits strong submissive tendencies and a psychological need for authority." + var/debuff_active = FALSE + var/master_dead = FALSE var/obj/item/clothing/neck/roguetown/cursed_collar/my_collar /datum/quirk/slavebourne/add() + . = ..() var/mob/living/carbon/human/H = quirk_holder if(!H) return - + ADD_TRAIT(H, TRAIT_SLAVEBOURNE_EXAMINE, TRAIT_GENERIC) ADD_TRAIT(H, TRAIT_SLAVEBOURNE, QUIRK_TRAIT) RegisterSignal(H, COMSIG_CARBON_GAIN_COLLAR, .proc/on_collared) RegisterSignal(H, COMSIG_CARBON_LOSE_COLLAR, .proc/on_uncollared) @@ -681,6 +683,7 @@ if(!H) return + REMOVE_TRAIT(H, TRAIT_SLAVEBOURNE_EXAMINE, TRAIT_GENERIC) REMOVE_TRAIT(H, TRAIT_SLAVEBOURNE, QUIRK_TRAIT) remove_debuff() UnregisterSignal(H, COMSIG_CARBON_GAIN_COLLAR) @@ -693,6 +696,7 @@ if(!H || !debuff_active) return + stack_trace("Applying slavebourne debuff to [H]") H.change_stat("strength", -4) H.change_stat("dexterity", -4) H.change_stat("intelligence", -4) @@ -703,9 +707,10 @@ /datum/quirk/slavebourne/proc/remove_debuff() var/mob/living/carbon/human/H = quirk_holder - if(!H) + if(!H || !debuff_active) return + stack_trace("Removing slavebourne debuff from [H]") H.change_stat("strength", 4) H.change_stat("dexterity", 4) H.change_stat("intelligence", 4) @@ -716,13 +721,69 @@ /datum/quirk/slavebourne/proc/on_collared(mob/living/carbon/human/source, obj/item/clothing/neck/roguetown/cursed_collar/collar) SIGNAL_HANDLER + if(master_dead) // If master died, new collars won't help + to_chat(source, span_warning("The death of your previous master has left you permanently weakened. No new master can restore your abilities...")) + return + if(istype(collar) && collar.collar_master && collar.collar_master != source) + // Unregister any existing death signals first + UnregisterSignal(collar.collar_master, COMSIG_LIVING_DEATH) + UnregisterSignal(collar.collar_master, COMSIG_LIVING_REVIVE) + + // Register new signals + RegisterSignal(collar.collar_master, COMSIG_LIVING_DEATH, .proc/on_master_death) + RegisterSignal(collar.collar_master, COMSIG_LIVING_REVIVE, .proc/on_master_revive) + debuff_active = FALSE remove_debuff() to_chat(source, span_notice("Your master's control strengthens you!")) /datum/quirk/slavebourne/proc/on_uncollared(mob/living/carbon/human/source) SIGNAL_HANDLER + if(master_dead) // If master died, keep debuff permanent + return + + // Clean up any existing signals + var/obj/item/clothing/neck/roguetown/cursed_collar/collar = source.get_item_by_slot(SLOT_NECK) + if(istype(collar) && collar.collar_master) + UnregisterSignal(collar.collar_master, list( + COMSIG_LIVING_DEATH, + COMSIG_LIVING_REVIVE + )) + debuff_active = TRUE apply_debuff() to_chat(source, span_warning("Without a master, you feel purposeless again...")) + +/datum/quirk/slavebourne/proc/on_master_death(mob/living/carbon/human/master) + SIGNAL_HANDLER + var/mob/living/carbon/human/H = quirk_holder + if(!H || master_dead) + return + + master_dead = TRUE + debuff_active = TRUE + apply_debuff() + to_chat(H, span_userdanger("You feel your master's death tear through your very being! Your abilities are permanently diminished...")) + playsound(H, 'sound/misc/astratascream.ogg', 50, TRUE) + + // Add debug message + stack_trace("Registering revival signal for [master]") + RegisterSignal(master, COMSIG_LIVING_REVIVE, .proc/on_master_revive) + +/datum/quirk/slavebourne/proc/on_master_revive(mob/living/carbon/human/master) + SIGNAL_HANDLER + var/mob/living/carbon/human/H = quirk_holder + if(!H || !master_dead) + return + + // Add debug message + stack_trace("Master [master] has been revived!") + master_dead = FALSE + debuff_active = FALSE + remove_debuff() + to_chat(H, span_notice("You feel your master's life force return! Your abilities are restored!")) + UnregisterSignal(master, COMSIG_LIVING_REVIVE) + +/datum/quirk/slavebourne/proc/examine(mob/living/carbon/human/H) + return span_notice("[H.p_they(TRUE)] carries [H.p_them()]self with a submissive demeanor as if seeking direction.") diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 3a4d9486b5e..6ef282f83ff 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -251,6 +251,7 @@ resistance_flags = INDESTRUCTIBLE armor = list("blunt" = 0, "slash" = 0, "stab" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) var/locked = FALSE + var/being_removed = FALSE /obj/item/clothing/neck/roguetown/cursed_collar/proc/handle_speech(datum/source, list/speech_args) SIGNAL_HANDLER @@ -286,38 +287,28 @@ /obj/item/clothing/neck/roguetown/cursed_collar/proc/prevent_removal(datum/source, mob/living/carbon/human/user) SIGNAL_HANDLER - // Get the user if not provided + if(being_removed) + return NONE + if(!user) user = usr - // CRITICAL: Master check first, before anything else if(user && (user == collar_master)) - // Explicitly remove everything that could block removal - locked = FALSE + being_removed = TRUE REMOVE_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT) - - // Clean up all signals + UnregisterSignal(src, COMSIG_ITEM_PRE_UNEQUIP) if(victim) UnregisterSignal(victim, list( COMSIG_MOB_CLICKON, COMSIG_MOB_ATTACK, COMSIG_MOB_SAY )) - UnregisterSignal(src, COMSIG_ITEM_PRE_UNEQUIP) - - // Clear victim reference - victim = null - - // Allow removal return NONE - // Everyone else gets blocked if(user && user == victim) to_chat(user, span_userdanger("The collar's magic holds it firmly in place! You can't remove it!")) playsound(user, 'sound/blank.ogg', 50, TRUE) - locked = TRUE - ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT) return COMPONENT_ITEM_BLOCK_UNEQUIP /obj/item/clothing/neck/roguetown/cursed_collar/attack(mob/living/carbon/human/M, mob/living/carbon/human/user) @@ -369,6 +360,7 @@ SEND_SIGNAL(user, COMSIG_CARBON_GAIN_COLLAR, src) /obj/item/clothing/neck/roguetown/cursed_collar/dropped(mob/user) + being_removed = FALSE . = ..() if(user == victim) UnregisterSignal(user, list( diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index b040472c81d..e2bb127f905 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -80,6 +80,10 @@ . = list("ø ------------ ø\nThis is [used_name], the wandering [custom_race_name ? "[custom_race_name] ([race_name])" : "[race_name]"].") else if(used_title) . = list("ø ------------ ø\nThis is [used_name], the [is_returning ? "returning " : ""][custom_race_name ? "[custom_race_name] ([race_name])" : "[race_name]"] [used_title].") + + // Add slavebourne text right after introduction + if(HAS_TRAIT(src, TRAIT_SLAVEBOURNE_EXAMINE)) + . += span_notice("[p_they(TRUE)] carries [p_them()]self with a submissive demeanor as if seeking direction.") else . = list("ø ------------ ø\nThis is the [used_name], the [custom_race_name ? "[custom_race_name] ([race_name])" : "[race_name]"].") if(dna.species.use_skintones) @@ -682,16 +686,3 @@ "\[View comment log\]", "\[Add comment\]"), "") . += "ø ------------ ø" - -/mob/living/proc/status_effect_examines(pronoun_replacement) //You can include this in any mob's examine() to show the examine texts of status effects! - var/list/dat = list() - if(!pronoun_replacement) - pronoun_replacement = p_they(TRUE) - for(var/V in status_effects) - var/datum/status_effect/E = V - if(E.examine_text) - var/new_text = replacetext(E.examine_text, "SUBJECTPRONOUN", pronoun_replacement) - new_text = replacetext(new_text, "[pronoun_replacement] is", "[pronoun_replacement] [p_are()]") //To make sure something become "They are" or "She is", not "They are" and "She are" - dat += "[new_text]\n" //dat.Join("\n") doesn't work here, for some reason - if(dat.len) - return dat.Join() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f8a349f54af..ee8f6429359 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2175,3 +2175,16 @@ reset_perspective() update_cone_show() // UnregisterSignal(src, COMSIG_MOVABLE_PRE_MOVE) + +/mob/living/proc/status_effect_examines(pronoun_replacement) //You can include this in any mob's examine() to show the examine texts of status effects! + var/list/dat = list() + if(!pronoun_replacement) + pronoun_replacement = p_they(TRUE) + for(var/V in status_effects) + var/datum/status_effect/E = V + if(E.examine_text) + var/new_text = replacetext(E.examine_text, "SUBJECTPRONOUN", pronoun_replacement) + new_text = replacetext(new_text, "[pronoun_replacement] is", "[pronoun_replacement] [p_are()]") //To make sure something become "They are" or "She is", not "They are" and "She are" + dat += "[new_text]\n" + if(dat.len) + return dat.Join()