Skip to content

Commit

Permalink
[MODULAR] Oh God The Zipper's Stuck: Entombed quirk granting unremova…
Browse files Browse the repository at this point in the history
…ble roundstart MODsuit (#656)

* Initial pass of Entombed quirk

* Fix incorrect preference path

* Fix improper preferences path

* Add entombed damage processing & toggle prefs for forced no-deploy

* Sort tgui imports and use the not broken text input

* Simplify toxin damage from entombed and remove fused MODsuit indestructible flag

* Handle custom stripping behavior on entombed MODsuits with storage modules

* Improve messaging on strip override

* Replace CMO modsuit w/ corpsman reskin & add infiltrator entombed skin

* Apply suggestions from code review

Co-authored-by: Bloop <[email protected]>

* Stack trace and qdel on null modsuit in add_unique(), code formatting improvements

* Extend equipping quirks to record what items they force-drop, and make Entombed transfer old bag contents into its MODsuit storage

* Apply suggestions from code review

Co-authored-by: Bloop <[email protected]>

* Add foley jitter to the life support failure process (makes it clearer what's killing the user)

* Handle dropped force-equipped items qdels gracefully
Co-authored-by: Bloop <[email protected]>

* Ensure customizations are applied to the entombed MODsuit

* Force deployed MODsuits give the no dismember trait, increase max chars on name customization to 48 & remove broken colonist skin

* Remove entombed MODsuit activation to post_add() to avoid loadout shenanigans

---------

Co-authored-by: Bloop <[email protected]>
  • Loading branch information
2 people authored and StealsThePRs committed Feb 26, 2024
1 parent 747f07b commit 047104c
Show file tree
Hide file tree
Showing 5 changed files with 418 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
var/list/items = list()
/// the items that will be forcefully equipped, formatted in the way of [item_path = list of slots it can be equipped to], will equip over nodrop items
var/list/forced_items = list()
/// did we force drop any items? if so, they're in this list. useful for transferring any applicable contents into new items on roundstart
var/list/force_dropped_items = list()

/datum/quirk/equipping/add_unique(client/client_source)
var/mob/living/carbon/carbon_holder = quirk_holder
Expand Down Expand Up @@ -45,7 +47,15 @@
if (check_nodrop && HAS_TRAIT(item_in_slot, TRAIT_NODROP))
return FALSE
target.dropItemToGround(item_in_slot, force = TRUE)
force_dropped_items += item_in_slot
RegisterSignal(item_in_slot, COMSIG_QDELETING, PROC_REF(dropped_items_cleanup))

return target.equip_to_slot_if_possible(item, slot, disable_warning = TRUE) // this should never not work tbh

/datum/quirk/equipping/proc/dropped_items_cleanup(obj/item/source)
SIGNAL_HANDLER

force_dropped_items -= source

/datum/quirk/equipping/proc/on_equip_item(obj/item/equipped, success)
return
273 changes: 273 additions & 0 deletions modular_nova/master_files/code/modules/entombed_quirk/code/entombed.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
/// How much damage should we be taking when the suit's been disabled a while?
#define ENTOMBED_TICK_DAMAGE 1.5

/datum/quirk/equipping/entombed
name = "Entombed"
desc = "You are permanently fused to (or otherwise reliant on) a single MOD unit that can never be removed from your person. If it runs out of charge or is turned off, you'll start to die!"
gain_text = span_warning("Your exosuit is both prison and home.")
lose_text = span_notice("At last, you're finally free from that horrible exosuit.")
medical_record_text = "Patient is physiologically reliant on a MOD unit for homeostasis. Do not attempt removal."
value = 0
icon = FA_ICON_ARROW_CIRCLE_DOWN
forced_items = list(/obj/item/mod/control/pre_equipped/entombed = list(ITEM_SLOT_BACK))
quirk_flags = QUIRK_HUMAN_ONLY | QUIRK_PROCESSES
/// The modsuit we're stuck in
var/obj/item/mod/control/pre_equipped/entombed/modsuit
/// Has the player chosen to deploy-lock?
var/deploy_locked = FALSE
/// How long before they start taking damage when the suit's not active?
var/life_support_failure_threshold = 1.5 MINUTES
/// TimerID for our timeframe tracker
var/life_support_timer
/// Are we taking damage?
var/life_support_failed = FALSE

/datum/quirk/equipping/entombed/process(seconds_per_tick)
var/mob/living/carbon/human/human_holder = quirk_holder
if (!modsuit || life_support_failed)
// we've got no modsuit or life support. take damage ow
human_holder.adjustToxLoss(ENTOMBED_TICK_DAMAGE * seconds_per_tick, updating_health = TRUE, forced = TRUE)
human_holder.set_jitter_if_lower(10 SECONDS)

if (!modsuit.active)
if (!life_support_timer)
//start the timer and let the player know
life_support_timer = addtimer(CALLBACK(src, PROC_REF(life_support_failure), human_holder), life_support_failure_threshold, TIMER_STOPPABLE | TIMER_DELETE_ME)

to_chat(human_holder, span_danger("Your physiology begins to erratically seize and twitch, bereft of your MODsuit's vital support. <b>Turn it back on as soon as you can!</b>"))
human_holder.balloon_alert(human_holder, "suit life support warning!")
human_holder.set_jitter_if_lower(life_support_failure_threshold) //give us some foley jitter
return
else
if (life_support_timer)
// clear our timer and let the player know everything's back to normal
deltimer(life_support_timer)
life_support_timer = null
life_support_failed = FALSE

to_chat(human_holder, span_notice("Relief floods your frame as your suit begins sustaining your life once more."))
human_holder.balloon_alert(human_holder, "suit life support restored!")
human_holder.adjust_jitter(-(life_support_failure_threshold / 2)) // clear half of it, wow, that was unpleasant

/datum/quirk/equipping/entombed/proc/life_support_failure()
// Warn the player and begin the gradual dying process.
var/mob/living/carbon/human/human_holder = quirk_holder

human_holder.visible_message(span_danger("[human_holder] suddenly staggers, a dire pallor overtaking [human_holder.p_their()] features as a feeble 'breep' emanates from their suit..."), span_userdanger("Terror descends as your suit's life support system breeps feebly, and then goes horrifyingly silent."))
human_holder.balloon_alert(human_holder, "SUIT LIFE SUPPORT FAILING!")
playsound(human_holder, 'sound/effects/alert.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) // OH GOD THE STRESS NOISE
life_support_failed = TRUE

/datum/quirk/equipping/entombed/add_unique(client/client_source)
. = ..()
var/mob/living/carbon/human/human_holder = quirk_holder
if (istype(human_holder.back, /obj/item/mod/control/pre_equipped/entombed))
modsuit = human_holder.back // link this up to the quirk for easy access

if (isnull(modsuit))
stack_trace("Entombed quirk couldn't create a fused MODsuit on [quirk_holder] and was force-removed.")
qdel(src)
return

var/lock_deploy = client_source?.prefs.read_preference(/datum/preference/toggle/entombed_deploy_lock)
if (!isnull(lock_deploy))
deploy_locked = lock_deploy

// set no dismember trait for deploy-locked dudes, i'm sorry, there's basically no better way to do this.
// it's a pretty ample buff but i dunno what else to do...
if (deploy_locked)
ADD_TRAIT(human_holder, TRAIT_NODISMEMBER, QUIRK_TRAIT)

// set all of our customization stuff from prefs, if we have it
var/modsuit_skin = client_source?.prefs.read_preference(/datum/preference/choiced/entombed_skin)

if (modsuit_skin == NONE)
modsuit_skin = "civilian"

modsuit.skin = lowertext(modsuit_skin)

var/modsuit_name = client_source?.prefs.read_preference(/datum/preference/text/entombed_mod_name)
if (modsuit_name)
modsuit.name = modsuit_name

var/modsuit_desc = client_source?.prefs.read_preference(/datum/preference/text/entombed_mod_desc)
if (modsuit_desc)
modsuit.desc = modsuit_desc

var/modsuit_skin_prefix = client_source?.prefs.read_preference(/datum/preference/text/entombed_mod_prefix)
if (modsuit_skin_prefix)
modsuit.theme.name = lowertext(modsuit_skin_prefix)

// ensure we're applying our config theme changes, just in case
for(var/obj/item/part as anything in modsuit.mod_parts)
part.name = "[modsuit.theme.name] [initial(part.name)]"
part.desc = "[initial(part.desc)] [modsuit.theme.desc]"

install_racial_features()

//transfer as many items across from our dropped backslot as we can. do this last incase something breaks
if (force_dropped_items)
var/obj/item/old_bag = locate() in force_dropped_items
if (old_bag.atom_storage)
old_bag.atom_storage.dump_content_at(modsuit, human_holder)

/datum/quirk/equipping/entombed/post_add()
. = ..()
// quickly deploy it on roundstart. we can't do this in add_unique because that gets called in the preview screen, which overwrites people's loadout stuff in suit/shoes/gloves slot. very unfun for them
modsuit.quick_activation()

/datum/quirk/equipping/entombed/remove()
var/mob/living/carbon/human/human_holder = quirk_holder
if (deploy_locked && HAS_TRAIT_FROM(human_holder, TRAIT_NODISMEMBER, QUIRK_TRAIT))
REMOVE_TRAIT(human_holder, TRAIT_NODISMEMBER, QUIRK_TRAIT)
QDEL_NULL(modsuit)

/datum/quirk/equipping/entombed/proc/install_racial_features()
// deploy specific racial features - ethereals get ethereal cores, plasmamen get free plasma stabilizer module
if (!modsuit) // really don't know how this could ever happen but it's better than runtimes
return
var/mob/living/carbon/human/human_holder = quirk_holder
if (isethereal(human_holder))
var/obj/item/mod/core/ethereal/eth_core = new
eth_core.install(modsuit)
else if (isplasmaman(human_holder))
var/obj/item/mod/module/plasma_stabilizer/entombed/plasma_stab = new
modsuit.install(plasma_stab, human_holder)

/datum/quirk_constant_data/entombed
associated_typepath = /datum/quirk/equipping/entombed
customization_options = list(
/datum/preference/choiced/entombed_skin,
/datum/preference/text/entombed_mod_desc,
/datum/preference/text/entombed_mod_name,
/datum/preference/text/entombed_mod_prefix,
/datum/preference/toggle/entombed_deploy_lock,
)

/datum/preference/choiced/entombed_skin
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "entombed_skin"
savefile_identifier = PREFERENCE_CHARACTER
can_randomize = FALSE

/datum/preference/choiced/entombed_skin/init_possible_values()
return list(
"Standard",
"Civilian",
"Advanced",
"Atmospheric",
"Corpsman",
"Cosmohonk",
"Engineering",
"Infiltrator",
"Interdyne",
"Loader",
"Medical",
"Mining",
"Prototype",
"Security",
)

/datum/preference/choiced/entombed_skin/create_default_value()
return "Civilian"

/datum/preference/choiced/entombed_skin/is_accessible(datum/preferences/preferences)
if (!..())
return FALSE

return "Entombed" in preferences.all_quirks

/datum/preference/choiced/entombed_skin/apply_to_human(mob/living/carbon/human/target, value)
return

/datum/preference/text/entombed_mod_name
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "entombed_mod_name"
savefile_identifier = PREFERENCE_CHARACTER
can_randomize = FALSE
maximum_value_length = 48

/datum/preference/text/entombed_mod_name/is_accessible(datum/preferences/preferences)
if (!..())
return FALSE

return "Entombed" in preferences.all_quirks

/datum/preference/text/entombed_mod_name/serialize(input)
return htmlrendertext(input)

/datum/preference/text/entombed_mod_name/deserialize(input, datum/preferences/preferences)
var/sanitized_input = htmlrendertext(input)
if(!isnull(sanitized_input))
return sanitized_input
else
return ""

/datum/preference/text/entombed_mod_name/apply_to_human(mob/living/carbon/human/target, value)
return

/datum/preference/text/entombed_mod_desc
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "entombed_mod_desc"
savefile_identifier = PREFERENCE_CHARACTER
can_randomize = FALSE

/datum/preference/text/entombed_mod_desc/is_accessible(datum/preferences/preferences)
if (!..())
return FALSE

return "Entombed" in preferences.all_quirks

/datum/preference/text/entombed_mod_desc/serialize(input)
return htmlrendertext(input)

/datum/preference/text/entombed_mod_desc/deserialize(input, datum/preferences/preferences)
var/sanitized_input = htmlrendertext(input)
if(!isnull(sanitized_input))
return sanitized_input
else
return ""

/datum/preference/text/entombed_mod_desc/apply_to_human(mob/living/carbon/human/target, value)
return

/datum/preference/text/entombed_mod_prefix
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "entombed_mod_prefix"
savefile_identifier = PREFERENCE_CHARACTER
can_randomize = FALSE
maximum_value_length = 16

/datum/preference/text/entombed_mod_prefix/is_accessible(datum/preferences/preferences)
if (!..())
return FALSE

return "Entombed" in preferences.all_quirks

/datum/preference/text/entombed_mod_prefix/serialize(input)
return htmlrendertext(input)

/datum/preference/text/entombed_mod_prefix/deserialize(input, datum/preferences/preferences)
return htmlrendertext(input)

/datum/preference/text/entombed_mod_prefix/create_default_value()
return "Fused"

/datum/preference/text/entombed_mod_prefix/apply_to_human(mob/living/carbon/human/target, value)
return

/datum/preference/toggle/entombed_deploy_lock
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
savefile_key = "entombed_deploy_lock"
savefile_identifier = PREFERENCE_CHARACTER

/datum/preference/toggle/entombed_deploy_lock/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE

return "Entombed" in preferences.all_quirks

/datum/preference/toggle/entombed_deploy_lock/apply_to_human(mob/living/carbon/human/target, value)
return

#undef ENTOMBED_TICK_DAMAGE
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/datum/mod_theme/entombed
name = "fused"
desc = "Circumstances have rendered this protective suit into someone's second skin. Literally."
extended_desc = "Some great aspect of someone's past has permanently bound them to this device, for better or worse."

default_skin = "civilian"
armor_type = /datum/armor/mod_entombed
resistance_flags = FIRE_PROOF | ACID_PROOF // It is better to die for the Emperor than live for yourself.
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
siemens_coefficient = 0
complexity_max = DEFAULT_MAX_COMPLEXITY - 5
charge_drain = DEFAULT_CHARGE_DRAIN
slowdown_inactive = 2.5 // very slow because the quirk infers you rely on this to move/exist
slowdown_active = 0.95
inbuilt_modules = list(
/obj/item/mod/module/joint_torsion/entombed,
/obj/item/mod/module/storage,
)
allowed_suit_storage = list(
/obj/item/tank/internals,
/obj/item/flashlight,
)

/datum/armor/mod_entombed
melee = ARMOR_LEVEL_WEAK
bullet = ARMOR_LEVEL_WEAK
laser = ARMOR_LEVEL_WEAK
energy = ARMOR_LEVEL_WEAK
bomb = ARMOR_LEVEL_WEAK
bio = ARMOR_LEVEL_WEAK
fire = ARMOR_LEVEL_WEAK
acid = ARMOR_LEVEL_WEAK
wound = WOUND_ARMOR_WEAK

/obj/item/mod/module/joint_torsion/entombed
name = "internal joint torsion adaptation"
desc = "Your adaptation to life in this MODsuit shell allows you to ambulate in such a way that your movements recharge the suit's internal batteries slightly, but only while under the effect of gravity."
removable = FALSE
complexity = 0
power_per_step = DEFAULT_CHARGE_DRAIN * 0.4

/obj/item/mod/module/plasma_stabilizer/entombed
name = "colony-stabilized interior seal"
desc = "Your colony has fully integrated the internal segments of your suit's plate into your skeleton, forming a hermetic seal between you and the outside world from which none of your atmosphere can escape. This is enough to allow your head to view the world with your helmet retracted."
complexity = 0
idle_power_cost = 0
removable = FALSE

/obj/item/mod/control/pre_equipped/entombed
theme = /datum/mod_theme/entombed
applied_cell = /obj/item/stock_parts/cell/high

/obj/item/mod/control/pre_equipped/entombed/canStrip(mob/who)
return TRUE //you can always try, and it'll hit doStrip below

/obj/item/mod/control/pre_equipped/entombed/doStrip(mob/who)
// attempt to handle custom stripping behavior - if we have a storage module of some kind
var/obj/item/mod/module/storage/inventory = locate() in src.modules
if (!isnull(inventory))
src.atom_storage.remove_all()
to_chat(who, span_notice("You empty out all the items from the MODsuit's storage module!"))
who.balloon_alert(who, "emptied out MOD storage items!")
return TRUE

to_chat(who, span_warning("The suit seems permanently fused to their frame - you can't remove it!"))
who.balloon_alert(who, "can't strip a fused MODsuit!")
return ..()

/obj/item/mod/control/pre_equipped/entombed/retract(mob/user, obj/item/part)
if (ishuman(user))
var/mob/living/carbon/human/human_user = user
var/datum/quirk/equipping/entombed/tomb_quirk = human_user.get_quirk(/datum/quirk/equipping/entombed)
//check to make sure we're not retracting something we shouldn't be able to
if (tomb_quirk && tomb_quirk.deploy_locked)
if (istype(part, /obj/item/clothing)) // make sure it's a modsuit piece and not a module, we retract those too
if (!istype(part, /obj/item/clothing/head/mod)) // they can only retract the helmet, them's the sticks
human_user.balloon_alert(human_user, "part is fused to you - can't retract!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
return ..()

/obj/item/mod/control/pre_equipped/entombed/quick_deploy(mob/user)
if (ishuman(user))
var/mob/living/carbon/human/human_user = user
var/datum/quirk/equipping/entombed/tomb_quirk = human_user.get_quirk(/datum/quirk/equipping/entombed)
//if we're deploy_locked, just disable this functionality entirely
if (tomb_quirk && tomb_quirk.deploy_locked)
human_user.balloon_alert(human_user, "you can only retract your helmet, and only manually!")
playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return
return ..()

/obj/item/mod/control/pre_equipped/entombed/Initialize(mapload, new_theme, new_skin, new_core)
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, QUIRK_TRAIT)
Loading

0 comments on commit 047104c

Please sign in to comment.