Skip to content

Commit

Permalink
Merge pull request #986 from Absolucy/ts-nerf
Browse files Browse the repository at this point in the history
[PORT] Changeling transformation string nerf/rework
  • Loading branch information
dwasint authored Feb 7, 2024
2 parents 573bfd5 + 71d1df3 commit 9c97bc2
Show file tree
Hide file tree
Showing 27 changed files with 183 additions and 56 deletions.
5 changes: 3 additions & 2 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_GUN_NATURAL "gunnatural"
/// Causes death-like unconsciousness
#define TRAIT_DEATHCOMA "deathcoma"
/// The mob has the stasis effect.
/// Does nothing on its own, applied via status effect.
#define TRAIT_STASIS "in_stasis"
/// Makes the owner appear as dead to most forms of medical examination
#define TRAIT_FAKEDEATH "fakedeath"
#define TRAIT_DISFIGURED "disfigured"
Expand Down Expand Up @@ -256,8 +259,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_LIVERLESS_METABOLISM "liverless_metabolism"
/// Humans with this trait cannot be turned into zombies
#define TRAIT_NO_ZOMBIFY "no_zombify"
/// Humans with this trait cannot be affected by changeling transformation stings
#define TRAIT_NO_TRANSFORMATION_STING "no_transformation_sting"
/// Carbons with this trait can't have their DNA copied by diseases nor changelings
#define TRAIT_NO_DNA_COPY "no_dna_copy"
/// Carbons with this trait can eat blood to regenerate their own blood volume, instead of injecting it
Expand Down
2 changes: 0 additions & 2 deletions code/__HELPERS/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -612,8 +612,6 @@ GLOBAL_LIST_EMPTY(species_list)

#define ISADVANCEDTOOLUSER(mob) (HAS_TRAIT(mob, TRAIT_ADVANCEDTOOLUSER) && !HAS_TRAIT(mob, TRAIT_DISCOORDINATED_TOOL_USER))

#define IS_IN_STASIS(mob) (mob.has_status_effect(/datum/status_effect/grouped/stasis) || mob.has_status_effect(/datum/status_effect/embryonic))

/// Gets the client of the mob, allowing for mocking of the client.
/// You only need to use this if you know you're going to be mocking clients somewhere else.
#define GET_CLIENT(mob) (##mob.client || ##mob.mock_client)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/components/irradiated.dm
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
process_tox_damage(human_parent, seconds_per_tick)

/datum/component/irradiated/proc/should_halt_effects(mob/living/carbon/human/target)
if (IS_IN_STASIS(target))
if (HAS_TRAIT(target, TRAIT_STASIS))
return TRUE

if (HAS_TRAIT(target, TRAIT_HALT_RADIATION_EFFECTS))
Expand Down
2 changes: 1 addition & 1 deletion code/datums/mutations/void_magnet.dm
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
/datum/action/cooldown/spell/void/cursed/proc/on_life(mob/living/source, seconds_per_tick, times_fired)
SIGNAL_HANDLER

if(!isliving(source) || IS_IN_STASIS(source) || source.stat == DEAD || HAS_TRAIT(source, TRAIT_NO_TRANSFORM))
if(!isliving(source) || HAS_TRAIT(source, TRAIT_STASIS) || source.stat == DEAD || HAS_TRAIT(source, TRAIT_NO_TRANSFORM))
return

if(!is_valid_target(source))
Expand Down
2 changes: 1 addition & 1 deletion code/datums/quirks/negative_quirks.dm
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@
if(!iscarbon(quirk_holder))
return

if(IS_IN_STASIS(quirk_holder))
if(HAS_TRAIT(quirk_holder, TRAIT_STASIS))
return

if(quirk_holder.stat == DEAD)
Expand Down
4 changes: 2 additions & 2 deletions code/datums/status_effects/debuffs/debuffs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@
. = ..()
if(!.)
return
owner.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), TRAIT_STATUS_EFFECT(id))
owner.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, TRAIT_STASIS), TRAIT_STATUS_EFFECT(id))
owner.add_filter("stasis_status_ripple", 2, list("type" = "ripple", "flags" = WAVE_BOUNDED, "radius" = 0, "size" = 2))
var/filter = owner.get_filter("stasis_status_ripple")
animate(filter, radius = 0, time = 0.2 SECONDS, size = 2, easing = JUMP_EASING, loop = -1, flags = ANIMATION_PARALLEL)
Expand All @@ -267,7 +267,7 @@
update_time_of_death()

/datum/status_effect/grouped/stasis/on_remove()
owner.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), TRAIT_STATUS_EFFECT(id))
owner.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED, TRAIT_STASIS), TRAIT_STATUS_EFFECT(id))
owner.remove_filter("stasis_status_ripple")
update_time_of_death()
if(iscarbon(owner))
Expand Down
92 changes: 92 additions & 0 deletions code/datums/status_effects/debuffs/dna_transformation.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/// Transforms a carbon mob into a new DNA for a set amount of time,
/// then turns them back to how they were before transformation.
/datum/status_effect/temporary_transformation
id = "temp_dna_transformation"
tick_interval = -1
duration = 1 MINUTES // set in on creation, this just needs to be any value to process
alert_type = null
remove_on_fullheal = TRUE
/// A reference to a COPY of the DNA that the mob will be transformed into.
var/datum/dna/new_dna
/// A reference to a COPY of the DNA of the mob prior to transformation.
var/datum/dna/old_dna

/datum/status_effect/temporary_transformation/Destroy()
. = ..() // parent must be called first, so we clear DNA refs AFTER transforming back... yeah i know
QDEL_NULL(new_dna)
QDEL_NULL(old_dna)

/datum/status_effect/temporary_transformation/on_creation(mob/living/new_owner, new_duration = 1 MINUTES, datum/dna/dna_to_copy)
src.duration = (new_duration == INFINITY) ? -1 : new_duration
src.new_dna = new()
src.old_dna = new()
dna_to_copy.copy_dna(new_dna)
return ..()

/datum/status_effect/temporary_transformation/on_apply()
if(!iscarbon(owner))
return FALSE

var/mob/living/carbon/transforming = owner
if(!transforming.has_dna())
return FALSE

// Save the old DNA
transforming.dna.copy_dna(old_dna)
// Makes them into the new DNA
new_dna.transfer_identity(transforming)
transforming.real_name = new_dna.real_name
transforming.name = transforming.get_visible_name()
transforming.updateappearance(mutcolor_update = TRUE)
transforming.domutcheck()
return TRUE

/datum/status_effect/temporary_transformation/on_remove()
var/mob/living/carbon/transforming = owner

if(!QDELING(owner)) // Don't really need to do appearance stuff if we're being deleted
old_dna.transfer_identity(transforming)
transforming.updateappearance(mutcolor_update = TRUE)
transforming.domutcheck()

transforming.real_name = old_dna.real_name // Name is fine though
transforming.name = transforming.get_visible_name()

/datum/status_effect/temporary_transformation/trans_sting
/// Tracks the time left on the effect when the owner last died. Used to pause the effect.
var/time_before_pause = -1
/// Signals which we react to to determine if we should pause the effect.
var/static/list/update_on_signals = list(
COMSIG_MOB_STATCHANGE,
SIGNAL_ADDTRAIT(TRAIT_STASIS),
SIGNAL_REMOVETRAIT(TRAIT_STASIS),
SIGNAL_ADDTRAIT(TRAIT_DEATHCOMA),
SIGNAL_REMOVETRAIT(TRAIT_DEATHCOMA),
)

/datum/status_effect/temporary_transformation/trans_sting/on_apply()
. = ..()
if(!.)
return
RegisterSignals(owner, update_on_signals, PROC_REF(pause_effect))
pause_effect(owner) // for if we sting a dead guy

/datum/status_effect/temporary_transformation/trans_sting/on_remove()
. = ..()
UnregisterSignal(owner, update_on_signals)

/datum/status_effect/temporary_transformation/trans_sting/proc/pause_effect(mob/living/source)
SIGNAL_HANDLER

// Pause if we're dead, appear dead, or in stasis
if(source.stat == DEAD || HAS_TRAIT(source, TRAIT_DEATHCOMA) || HAS_TRAIT(source, TRAIT_STASIS))
if(duration == -1)
return // Already paused

time_before_pause = duration - world.time
duration = -1

// Resume if we're none of the above and also were paused
else if(time_before_pause != -1)
duration = time_before_pause + world.time
time_before_pause = -1
2 changes: 1 addition & 1 deletion code/datums/status_effects/debuffs/drowsiness.dm
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

/datum/status_effect/drowsiness/tick(seconds_per_tick)
// You do not feel drowsy while unconscious or in stasis
if(owner.stat >= UNCONSCIOUS || IS_IN_STASIS(owner))
if(owner.stat >= UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_STASIS))
return

// Resting helps against drowsiness
Expand Down
2 changes: 1 addition & 1 deletion code/datums/status_effects/debuffs/drunk.dm
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@

/datum/status_effect/inebriated/tick()
// Drunk value does not decrease while dead or in stasis
if(owner.stat == DEAD || IS_IN_STASIS(owner))
if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_STASIS))
return

// Every tick, the drunk value decrases by
Expand Down
2 changes: 1 addition & 1 deletion code/datums/wounds/bones.dm
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
/datum/wound/blunt/bone/handle_process(seconds_per_tick, times_fired)
. = ..()

if (!victim || IS_IN_STASIS(victim))
if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return

if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group && world.time > next_trauma_cycle)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/wounds/burns.dm
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

/datum/wound/burn/flesh/handle_process(seconds_per_tick, times_fired)

if (!victim || IS_IN_STASIS(victim))
if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return

. = ..()
Expand Down
2 changes: 1 addition & 1 deletion code/datums/wounds/pierce.dm
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
return BLOOD_FLOW_STEADY

/datum/wound/pierce/bleed/handle_process(seconds_per_tick, times_fired)
if (!victim || IS_IN_STASIS(victim))
if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return

set_blood_flow(min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW))
Expand Down
2 changes: 1 addition & 1 deletion code/datums/wounds/slash.dm
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@

/datum/wound/slash/flesh/handle_process(seconds_per_tick, times_fired)

if (!victim || IS_IN_STASIS(victim))
if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return

// in case the victim has the NOBLOOD trait, the wound will simply not clot on it's own
Expand Down
6 changes: 3 additions & 3 deletions code/game/machinery/stasis.dm
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
/obj/machinery/stasis/Exited(atom/movable/gone, direction)
if(gone == occupant)
var/mob/living/L = gone
if(IS_IN_STASIS(L))
if(HAS_TRAIT(L, TRAIT_STASIS))
thaw_them(L)
return ..()

Expand Down Expand Up @@ -157,9 +157,9 @@
return
var/mob/living/L_occupant = occupant
if(stasis_running())
if(!IS_IN_STASIS(L_occupant))
if(!HAS_TRAIT(L_occupant, TRAIT_STASIS))
chill_out(L_occupant)
else if(IS_IN_STASIS(L_occupant))
else if(HAS_TRAIT(L_occupant, TRAIT_STASIS))
thaw_them(L_occupant)

/obj/machinery/stasis/screwdriver_act(mob/living/user, obj/item/I)
Expand Down
80 changes: 54 additions & 26 deletions code/modules/antagonists/changeling/powers/tiny_prick.dm
Original file line number Diff line number Diff line change
Expand Up @@ -65,46 +65,74 @@

/datum/action/changeling/sting/transformation
name = "Transformation Sting"
desc = "We silently sting a human, injecting a retrovirus that forces them to transform. Costs 50 chemicals."
helptext = "The victim will transform much like a changeling would. Does not provide a warning to others. Mutations will not be transferred, and monkeys will become human."
desc = "We silently sting an organism, injecting a retrovirus that forces them to transform."
helptext = "The victim will transform much like a changeling would. \
For complex humanoids, the transformation is temporarily, but the duration is paused while the victim is dead or in stasis. \
For more simple humanoids, such as monkeys, the transformation is permanent. \
Does not provide a warning to others. Mutations will not be transferred."
button_icon_state = "sting_transform"
chemical_cost = 50
dna_cost = 3
var/datum/changeling_profile/selected_dna = null
chemical_cost = 33 // Low enough that you can sting only two people in quick succession
dna_cost = 2
/// A reference to our active profile, which we grab DNA from
VAR_FINAL/datum/changeling_profile/selected_dna
/// Duration of the sting
var/sting_duration = 8 MINUTES

/datum/action/changeling/sting/transformation/Grant(mob/grant_to)
. = ..()
build_all_button_icons(UPDATE_BUTTON_NAME)

/datum/action/changeling/sting/transformation/Trigger(trigger_flags)
var/mob/user = usr
/datum/action/changeling/sting/transformation/update_button_name(atom/movable/screen/movable/action_button/button, force)
. = ..()
button.desc += " Lasts [DisplayTimeText(sting_duration)] for humans, but duration is paused while dead or in stasis."
button.desc += " Costs [chemical_cost] chemicals."

/datum/action/changeling/sting/transformation/Destroy()
selected_dna = null
return ..()

/datum/action/changeling/sting/transformation/set_sting(mob/user)
selected_dna = null
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling.chosen_sting)
unset_sting(user)
var/datum/changeling_profile/new_selected_dna = changeling.select_dna()
if(QDELETED(src) || QDELETED(changeling) || QDELETED(user))
return
selected_dna = changeling.select_dna()
if(!selected_dna)
if(!new_selected_dna || changeling.chosen_sting || selected_dna) // selected other sting or other DNA while sleeping
return
if(NOTRANSSTING in selected_dna.dna.species.species_traits)
user.balloon_alert(user, "incompatible DNA!")
return
..()
selected_dna = new_selected_dna
return ..()

/datum/action/changeling/sting/transformation/can_sting(mob/user, mob/living/carbon/target)
. = ..()
if(!.)
return
if((HAS_TRAIT(target, TRAIT_HUSK)) || !iscarbon(target) || (NOTRANSSTING in target.dna.species.species_traits))
// Similar checks here are ran to that of changeling can_absorb_dna -
// Logic being that if their DNA is incompatible with us, it's also bad for transforming
if(!iscarbon(target) \
|| !target.has_dna() \
|| HAS_TRAIT(target, TRAIT_HUSK) \
|| HAS_TRAIT(target, TRAIT_BADDNA) \
|| (HAS_TRAIT(target, TRAIT_NO_DNA_COPY) && !ismonkey(target))) // sure, go ahead, make a monk-clone
user.balloon_alert(user, "incompatible DNA!")
return FALSE
if(target.has_status_effect(/datum/status_effect/temporary_transformation/trans_sting))
user.balloon_alert(user, "already transformed!")
return FALSE
return TRUE

/datum/action/changeling/sting/transformation/sting_action(mob/user, mob/target)
log_combat(user, target, "stung", "transformation sting", " new identity is '[selected_dna.dna.real_name]'")
var/datum/dna/NewDNA = selected_dna.dna

var/mob/living/carbon/C = target
. = TRUE
if(istype(C))
C.real_name = NewDNA.real_name
NewDNA.transfer_identity(C)
C.updateappearance(mutcolor_update=1)
/datum/action/changeling/sting/transformation/sting_action(mob/living/user, mob/living/target)
var/final_duration = sting_duration
var/final_message = span_notice("We transform [target] into [selected_dna.dna.real_name].")
if(ismonkey(target))
final_duration = INFINITY
final_message = span_warning("Our genes cry out as we transform the lesser form of [target] into [selected_dna.dna.real_name] permanently!")

if(target.apply_status_effect(/datum/status_effect/temporary_transformation/trans_sting, final_duration, selected_dna.dna))
..()
log_combat(user, target, "stung", "transformation sting", " new identity is '[selected_dna.dna.real_name]'")
to_chat(user, final_message)
return TRUE
return FALSE


/datum/action/changeling/sting/false_armblade
Expand Down
1 change: 0 additions & 1 deletion code/modules/bitrunning/antagonists/cyber_police.dm
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
player.add_traits(list(
//TRAIT_NO_AUGMENTS,
TRAIT_NO_DNA_COPY,
TRAIT_NO_TRANSFORMATION_STING,
TRAIT_NOBLOOD,
TRAIT_NOBREATH,
TRAIT_NOHUNGER,
Expand Down
9 changes: 9 additions & 0 deletions code/modules/bitrunning/components/netpod_healing.dm
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@
id = "embryonic"
alert_type = /atom/movable/screen/alert/status_effect/embryonic

/datum/status_effect/embryonic/on_apply()
. = ..()
if(.)
ADD_TRAIT(owner, TRAIT_STASIS, TRAIT_STATUS_EFFECT(id))

/datum/status_effect/embryonic/on_remove()
REMOVE_TRAIT(owner, TRAIT_STASIS, TRAIT_STATUS_EFFECT(id))
return ..()

/atom/movable/screen/alert/status_effect/embryonic
name = "Embryonic Stasis"
icon_state = "netpod_stasis"
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/carbon/alien/larva/life.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/mob/living/carbon/alien/larva/Life(seconds_per_tick = SSMOBS_DT, times_fired)
if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
return
if(!..() || IS_IN_STASIS(src) || (amount_grown >= max_grown))
if(!..() || HAS_TRAIT(src, TRAIT_STASIS) || (amount_grown >= max_grown))
return // We're dead, in stasis, or already grown.
// GROW!
amount_grown = min(amount_grown + (0.5 * seconds_per_tick), max_grown)
Expand Down
4 changes: 2 additions & 2 deletions code/modules/mob/living/carbon/human/_species.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1548,15 +1548,15 @@ GLOBAL_LIST_EMPTY(features_by_species)
return

//Only stabilise core temp when alive and not in statis
if(humi.stat < DEAD && !IS_IN_STASIS(humi))
if(humi.stat < DEAD && !HAS_TRAIT(humi, TRAIT_STASIS))
body_temperature_core(humi, seconds_per_tick, times_fired)

//These do run in statis
body_temperature_skin(humi, seconds_per_tick, times_fired)
body_temperature_alerts(humi, seconds_per_tick, times_fired)

//Do not cause more damage in statis
if(!IS_IN_STASIS(humi))
if(!HAS_TRAIT(humi, TRAIT_STASIS))
body_temperature_damage(humi, seconds_per_tick, times_fired)

/**
Expand Down
Loading

0 comments on commit 9c97bc2

Please sign in to comment.