From 389cdd67160b93470ed82b1339baa1309344ae24 Mon Sep 17 00:00:00 2001 From: Jacquerel Date: Tue, 10 Oct 2023 03:09:21 +0100 Subject: [PATCH] Replaces the changeling spacesuit with a passive ability (#78763) ## About The Pull Request Fixes #74168 I was going to make changes to the changeling spacesuit so that it works on Icebox but then I thought, why not _not_ do that. This isn't a commonly picked adaption so why don't we make it a little better. What's more spooky, hearing a knock on the window and seeing a fat suit outside? Or this? ![image](https://github.com/tgstation/tgstation/assets/7483112/b87dec0c-5e98-45a5-8f83-f7a2967c743f) Picking Void Adaption will now make you immune to low temperature and pressure (but not high temperature and pressure) and you will stop breathing. If you enter an area with low temperature or pressure then your chemical regeneration rate will decrease until you leave that area. Because it doesn't put a suit on you, it now also works during Lesser Form. While testing this I noticed that we weren't calling `Grant` on passive changeling abilities for some reason, I replaced that with the already-written interface for making "an action which doesn't give you a button". If people really _really_ miss the fat suit I guess I'll rework that instead, but I think I like this more. ## Why It's Good For The Game Makes a niche-pick ability more useful and easier to use. Meteor Changelings who land on Icebox now don't roll a dice to see if they get instantly knocked out by the atmosphere there. ## Changelog :cl: balance: The Changeling Space Suit has been replaced by a new ability which makes you passively spaceproof without replacing your clothing. admin: Editing the atmos sensitivity variables on a basic mob during the game will now actually do something. /:cl: --- .../antagonists/changeling/changeling.dm | 8 +- .../changeling/changeling_power.dm | 5 +- .../changeling/powers/defib_grasp.dm | 2 +- .../changeling/powers/mutations.dm | 72 ------------------ .../changeling/powers/void_adaption.dm | 68 +++++++++++++++++ code/modules/meteors/meteor_spawning.dm | 2 +- code/modules/mob/living/basic/basic.dm | 48 +++++++++--- ...eenshot_antag_icons_changelingmidround.png | Bin 690 -> 628 bytes tgstation.dme | 1 + 9 files changed, 113 insertions(+), 93 deletions(-) create mode 100644 code/modules/antagonists/changeling/powers/void_adaption.dm diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 171468e3d7e..de47610564c 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -350,12 +350,11 @@ /datum/antagonist/changeling/proc/regain_powers() emporium_action.Grant(owner.current) for(var/datum/action/changeling/power as anything in innate_powers) - if(power.needs_button) - power.Grant(owner.current) + power.Grant(owner.current) for(var/power_path in purchased_powers) var/datum/action/changeling/power = purchased_powers[power_path] - if(istype(power) && power.needs_button) + if(istype(power)) power.Grant(owner.current) /* @@ -1048,9 +1047,6 @@ /datum/outfit/changeling_space name = "Changeling (Space)" - - head = /obj/item/clothing/head/helmet/space/changeling - suit = /obj/item/clothing/suit/space/changeling l_hand = /obj/item/melee/arm_blade #undef FORMAT_CHEM_CHARGES_TEXT diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm index f4949306ded..1e2d54eb456 100644 --- a/code/modules/antagonists/changeling/changeling_power.dm +++ b/code/modules/antagonists/changeling/changeling_power.dm @@ -7,8 +7,6 @@ background_icon_state = "bg_changeling" overlay_icon_state = "bg_changeling_border" button_icon = 'icons/mob/actions/actions_changeling.dmi' - /// For passive abilities like hivemind that dont need an action button - var/needs_button = TRUE /// Details displayed in fine print within the changling emporium var/helptext = "" /// How many changeling chems it costs to use @@ -44,8 +42,7 @@ the same goes for Remove(). if you override Remove(), call parent or else your p /datum/action/changeling/proc/on_purchase(mob/user, is_respec) if(!is_respec) SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, name) - if(needs_button) - Grant(user)//how powers are added rather than the checks in mob.dm + Grant(user)//how powers are added rather than the checks in mob.dm /datum/action/changeling/Trigger(trigger_flags) var/mob/user = owner diff --git a/code/modules/antagonists/changeling/powers/defib_grasp.dm b/code/modules/antagonists/changeling/powers/defib_grasp.dm index 20ff3049c8f..135b9b243f7 100644 --- a/code/modules/antagonists/changeling/powers/defib_grasp.dm +++ b/code/modules/antagonists/changeling/powers/defib_grasp.dm @@ -4,7 +4,7 @@ we will snatch their arms off and instantly finalize our stasis." helptext = "This ability is passive, and will trigger when a defibrillator paddle is applied to our chest \ while we are dead or in stasis. Will also stun cyborgs momentarily." - needs_button = FALSE + owner_has_control = FALSE dna_cost = 0 /// Flags to pass to fully heal when we get zapped diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index bf4f8c2b3da..54027b7d8a2 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -480,78 +480,6 @@ remaining_uses-- return ..() - -/***************************************\ -|*********SPACE SUIT + HELMET***********| -\***************************************/ -/datum/action/changeling/suit/organic_space_suit - name = "Organic Space Suit" - desc = "We grow an organic suit to protect ourselves from space exposure, including regulation of temperature and oxygen needs. Costs 20 chemicals." - helptext = "We must constantly repair our form to make it space-proof, reducing chemical production while we are protected. Cannot be used in lesser form." - button_icon_state = "organic_suit" - chemical_cost = 20 - dna_cost = 2 - req_human = TRUE - - suit_type = /obj/item/clothing/suit/space/changeling - helmet_type = /obj/item/clothing/head/helmet/space/changeling - suit_name_simple = "flesh shell" - helmet_name_simple = "space helmet" - recharge_slowdown = 0.25 - blood_on_castoff = 1 - -/obj/item/clothing/suit/space/changeling - name = "flesh mass" - icon_state = "lingspacesuit_t" - icon = 'icons/obj/clothing/suits/costume.dmi' - worn_icon = 'icons/mob/clothing/suits/costume.dmi' - desc = "A huge, bulky mass of pressure and temperature-resistant organic tissue, evolved to facilitate space travel." - item_flags = DROPDEL - clothing_flags = STOPSPRESSUREDAMAGE //Not THICKMATERIAL because it's organic tissue, so if somebody tries to inject something into it, it still ends up in your blood. (also balance but muh fluff) - allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/oxygen) - armor_type = /datum/armor/space_changeling - actions_types = list() - cell = null - show_hud = FALSE - -/datum/armor/space_changeling - bio = 100 - fire = 90 - acid = 90 - -/obj/item/clothing/suit/space/changeling/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) - if(ismob(loc)) - loc.visible_message(span_warning("[loc.name]\'s flesh rapidly inflates, forming a bloated mass around [loc.p_their()] body!"), span_warning("We inflate our flesh, creating a spaceproof suit!"), span_hear("You hear organic matter ripping and tearing!")) - START_PROCESSING(SSobj, src) - -// seal the cell door -/obj/item/clothing/suit/space/changeling/toggle_spacesuit_cell(mob/user) - return - -/obj/item/clothing/suit/space/changeling/process(seconds_per_tick) - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - H.reagents.add_reagent(/datum/reagent/medicine/salbutamol, REAGENTS_METABOLISM * (seconds_per_tick / SSMOBS_DT)) - H.adjust_bodytemperature(temperature_setting - H.bodytemperature) // force changelings to normal temp step mode played badly - -/obj/item/clothing/head/helmet/space/changeling - name = "flesh mass" - icon = 'icons/obj/clothing/head/costume.dmi' - worn_icon = 'icons/mob/clothing/head/costume.dmi' - icon_state = "lingspacehelmet" - inhand_icon_state = null - desc = "A covering of pressure and temperature-resistant organic tissue with a glass-like chitin front." - item_flags = DROPDEL - clothing_flags = STOPSPRESSUREDAMAGE | HEADINTERNALS - armor_type = /datum/armor/space_changeling - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - -/obj/item/clothing/head/helmet/space/changeling/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) - /***************************************\ |*****************ARMOR*****************| \***************************************/ diff --git a/code/modules/antagonists/changeling/powers/void_adaption.dm b/code/modules/antagonists/changeling/powers/void_adaption.dm new file mode 100644 index 00000000000..76c0eeffc97 --- /dev/null +++ b/code/modules/antagonists/changeling/powers/void_adaption.dm @@ -0,0 +1,68 @@ +/datum/action/changeling/void_adaption + name = "Void Adaption" + desc = "We prepare our cells to resist the hostile environment outside of the station. We may freely travel wherever we wish." + helptext = "This ability is passive, and will automatically protect you in situations of extreme cold or vacuum, \ + as well as removing your need to breathe. While it is actively protecting you from temperature or pressure \ + it reduces your chemical regeneration rate." + owner_has_control = FALSE + dna_cost = 2 + + /// Traits we apply to become immune to the environment + var/static/list/gain_traits = list(TRAIT_NOBREATH, TRAIT_RESISTCOLD, TRAIT_RESISTLOWPRESSURE, TRAIT_SNOWSTORM_IMMUNE) + /// How much we slow chemical regeneration while active, in chems per second + var/recharge_slowdown = 0.25 + /// Are we currently protecting our user? + var/currently_active = FALSE + +/datum/action/changeling/void_adaption/on_purchase(mob/user, is_respec) + . = ..() + user.add_traits(gain_traits, REF(src)) + RegisterSignal(user, COMSIG_LIVING_LIFE, PROC_REF(check_environment)) + +/datum/action/changeling/void_adaption/Remove(mob/remove_from) + remove_from.remove_traits(gain_traits, REF(src)) + UnregisterSignal(remove_from, COMSIG_LIVING_LIFE) + if (currently_active) + on_removed_adaption(remove_from, "Our cells relax, despite the danger!") + return ..() + +/// Checks if we would be providing any useful benefit at present +/datum/action/changeling/void_adaption/proc/check_environment(mob/living/void_adapted) + SIGNAL_HANDLER + + var/list/active_reasons = list() + + var/datum/gas_mixture/environment = void_adapted.loc.return_air() + if (!isnull(environment)) + var/vulnerable_temperature = void_adapted.get_body_temp_cold_damage_limit() + var/affected_temperature = environment.return_temperature() + if (ishuman(void_adapted)) + var/mob/living/carbon/human/special_boy = void_adapted + var/cold_protection = special_boy.get_cold_protection(affected_temperature) + vulnerable_temperature *= (1 - cold_protection) + + var/affected_pressure = special_boy.calculate_affecting_pressure(environment.return_pressure()) + if (affected_pressure < HAZARD_LOW_PRESSURE) + active_reasons += "vacuum" + + if (affected_temperature < vulnerable_temperature) + active_reasons += "cold" + + var/should_be_active = !!length(active_reasons) + if (currently_active == should_be_active) + return + + if (!should_be_active) + on_removed_adaption(void_adapted, "Our cells relax in safer air.") + return + var/datum/antagonist/changeling/changeling_data = void_adapted.mind?.has_antag_datum(/datum/antagonist/changeling) + to_chat(void_adapted, span_changeling("Our cells harden themselves against the [pick(active_reasons)].")) + changeling_data?.chem_recharge_slowdown -= recharge_slowdown + currently_active = TRUE + +/// Called when we stop being adapted +/datum/action/changeling/void_adaption/proc/on_removed_adaption(mob/living/former, message) + var/datum/antagonist/changeling/changeling_data = former.mind?.has_antag_datum(/datum/antagonist/changeling) + to_chat(former, span_changeling(message)) + changeling_data?.chem_recharge_slowdown += recharge_slowdown + currently_active = FALSE diff --git a/code/modules/meteors/meteor_spawning.dm b/code/modules/meteors/meteor_spawning.dm index eac365bc2a8..97c359d03bf 100644 --- a/code/modules/meteors/meteor_spawning.dm +++ b/code/modules/meteors/meteor_spawning.dm @@ -109,7 +109,7 @@ new_changeling.log_message("was spawned as a midround space changeling by an event.", LOG_GAME) var/datum/antagonist/changeling/changeling_datum = locate() in player_mind.antag_datums - changeling_datum.give_power(/datum/action/changeling/suit/organic_space_suit) + changeling_datum.give_power(/datum/action/changeling/void_adaption) changeling_datum.give_power(/datum/action/changeling/weapon/arm_blade) new_changeling.equipOutfit(/datum/outfit/changeling_space) diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm index aebb54770d7..ab8daf47635 100644 --- a/code/modules/mob/living/basic/basic.dm +++ b/code/modules/mob/living/basic/basic.dm @@ -117,13 +117,23 @@ if(speak_emote) speak_emote = string_list(speak_emote) - if(unsuitable_atmos_damage != 0) - //String assoc list returns a cached list, so this is like a static list to pass into the element below. - habitable_atmos = string_assoc_list(habitable_atmos) - AddElement(/datum/element/atmos_requirements, habitable_atmos, unsuitable_atmos_damage) + apply_atmos_requirements() + apply_temperature_requirements() + +/// Ensures this mob can take atmospheric damage if it's supposed to +/mob/living/basic/proc/apply_atmos_requirements() + if(unsuitable_atmos_damage == 0) + return + //String assoc list returns a cached list, so this is like a static list to pass into the element below. + habitable_atmos = string_assoc_list(habitable_atmos) + AddElement(/datum/element/atmos_requirements, habitable_atmos, unsuitable_atmos_damage) + +/// Ensures this mob can take temperature damage if it's supposed to +/mob/living/basic/proc/apply_temperature_requirements() + if(unsuitable_cold_damage == 0 && unsuitable_heat_damage == 0) + return + AddElement(/datum/element/basic_body_temp_sensitive, minimum_survivable_temperature, maximum_survivable_temperature, unsuitable_cold_damage, unsuitable_heat_damage) - if(unsuitable_cold_damage != 0 && unsuitable_heat_damage != 0) - AddElement(/datum/element/basic_body_temp_sensitive, minimum_survivable_temperature, maximum_survivable_temperature, unsuitable_cold_damage, unsuitable_heat_damage) /mob/living/basic/Life(seconds_per_tick = SSMOBS_DT, times_fired) . = ..() @@ -207,10 +217,24 @@ melee_attack(attack_target, modifiers) /mob/living/basic/vv_edit_var(vname, vval) + switch(vname) + if(NAMEOF(src, habitable_atmos), NAMEOF(src, unsuitable_atmos_damage)) + RemoveElement(/datum/element/atmos_requirements, habitable_atmos, unsuitable_atmos_damage) + . = TRUE + if(NAMEOF(src, minimum_survivable_temperature), NAMEOF(src, maximum_survivable_temperature), NAMEOF(src, unsuitable_cold_damage), NAMEOF(src, unsuitable_heat_damage)) + RemoveElement(/datum/element/basic_body_temp_sensitive, minimum_survivable_temperature, maximum_survivable_temperature, unsuitable_cold_damage, unsuitable_heat_damage) + . = TRUE + . = ..() - if(vname == NAMEOF(src, speed)) - datum_flags |= DF_VAR_EDITED - set_varspeed(vval) + + switch(vname) + if(NAMEOF(src, habitable_atmos), NAMEOF(src, unsuitable_atmos_damage)) + apply_atmos_requirements() + if(NAMEOF(src, minimum_survivable_temperature), NAMEOF(src, maximum_survivable_temperature), NAMEOF(src, unsuitable_cold_damage), NAMEOF(src, unsuitable_heat_damage)) + apply_temperature_requirements() + if(NAMEOF(src, speed)) + datum_flags |= DF_VAR_EDITED + set_varspeed(vval) /mob/living/basic/proc/set_varspeed(var_value) speed = var_value @@ -260,3 +284,9 @@ else if(on_fire && !isnull(last_icon_state)) return last_icon_state return null + +/mob/living/basic/get_body_temp_heat_damage_limit() + return maximum_survivable_temperature + +/mob/living/basic/get_body_temp_cold_damage_limit() + return minimum_survivable_temperature diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_changelingmidround.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_changelingmidround.png index ac412207c236df0ca79fe8f738b6edfa1671beca..d8e9c548db997af8bb170a04a5159be69e639e49 100644 GIT binary patch delta 490 zcmVcgPLl}<{{R3gDk@uBTY7qWi{M*uk$8ZAg-Jv~R9J=Wma$62KoExaDqH6~ z#Mbe=!%`On%N&EE{XKvdYpWJ+Yq^DmrQ2D``Up|*0qy}TyxztVQ|1fev(0N<>(V1%Mun(2J64NOS=A+Qsd$l;bgO@?{eN z0Ck3cTha>$>a^TY^#elaYetkpROkAlLVb*YSkYI(OBKs&2RJQybf~=8-T=ll4FEut zd)Oy0+_Bb{1YmSP2V{YqGdKSP^lTU+b7qk4LJ?YpJ) z!mO@~!xfQQfPbY)L_t(&f$f;la)K}vM1_c@CDm42%Kv|9cPE<(YC9w~K49;@ge0(s zapih(voV=WCX>ljQO%VMaN*uU&bcjw!C_VtVt^&{rt3Nv+;`9ugc%mQ8jQyP^nL#v z^aAM+i@gEv4rufM48uT6%n0BrW>_3k#z!dSrklbGo}sy+LJJuN=^vy>9Su`;(Lb z58oj$a-%T7l6fOXbtbGlDm%L*tzeG<6M!rBLH`55#uo1vv42RZ93gdgI+Xxsfh#yH z#bAIE0DsK6!goE)TyW=xAi4><@(k~5Qn}`UnE|Q*^naX6zMruqAqC*KA6KY*$$1vm51>aEM98lhV2=Qn3w-_`P-X@O z$YId+6H?G^s8F#MrGAFzFxGJ#3CIcA0J#8od_6&8#STVN*4X1YV-TGSLS8o@Le$bt q(?p-t&Jm5pNh@Or;8fsV5AY3Kv2ZQHf7-bK0000