Files
Bubberstation/code/modules/lost_crew/lost_crew_manager.dm
MrMelbertandGitHub da64374423 Reverts "Add prosthetic limb" surgery to involve targeting limbs, rather than targeting chest. (Adds stumps) (#95252)
## About The Pull Request

- The `prosthetic replacement` surgical operation has been reverted to
be closer to how it used to work: The operation is done targeting the
limb that's missing

The change was made out of necessity, as surgical state was tied to
limbs - you had to operate on the chest to re-attach limbs because there
was no limb to operate on.

To circumvent that, I have done the unthinkable of adding stumps when
you are dismembered.

- Missing limbs are now represented as an invisible, un-removable,
un-interactable limb.

Making this change was not as difficult as originally anticipated, and
(at least surface level) seems to have broken very little.

Surprisingly little had to change to make this work. 

Direct accesses to `mob.bodyparts` was changed to `mob.get_bodyparts()`
with an optional `include_stumps` argument.
Similarly, `get_bodypart()` had an optional `include_stumps` added. 

This means we ultimately barely needed to change anything, and in fact,
some loops/checks were able to be streamlined.

## Why It's Good For The Game

- As mentioned, this change was out of necessity and was easily the
least intuitive part of the broader changes. Reverting it back to how it
used to work should make it far easier for people to pick up on, and
means we can cut out a bunch of bespoke instruction sets that I had to
include.

- The addition of stumps also adds a ton of future potential - code wise
it allows for stuff like better damage tracking (we can transfer damage
between limb <-> stump rather than limb <-> chest), and feature we can
do "fun" stuff like have stumps bleed on dismemberment that you can
bandage.

## Changelog

🆑 Melbert
del: "Add prosthetic limb" surgical operation has been reverted to be a
bit closer to how it used to work - you operate on the missing limb /
limb stump, rather than on the chest.
refactor: Missing limbs are now represented as limb stumps. In practice
this should change nothing (for now), as no features were rewritten to
make use of these besides surgery. Please report any oddities with
missing limbs, however.
/🆑
2026-03-20 14:32:41 +13:00

174 lines
7.8 KiB
Plaintext

/// Manager for the lost crew bodies, for spawning and granting rewards
GLOBAL_DATUM_INIT(lost_crew_manager, /datum/lost_crew_manager, new)
/// Handles procs and timers for the lost crew bodies
/datum/lost_crew_manager
/// How many credits we reward the medical budget on a successful revive
var/credits_on_succes = /datum/supply_pack/medical/lost_crew::cost + CARGO_CRATE_VALUE * 2
/// How long after successful revival we check to see if theyre still alive, and give rewards
var/succes_check_time = 3 MINUTES
/// How much the revived crew start with on their cards
var/starting_funds = 200
/**
* Creates a body with random background and injuries
*
* Arguments:
* * revivable - Whether or not we can be revived to grand a ghost controle
* * container - Humans really dont like not having a loc, so please either give the container where you want to spawn it or a turf
* * forced_class - To force a specific damage class for some specific lore reason
* * recovered_items - Items recovered, such as some organs, dropped directly with the body
* * protected_items - Items that can only be recovered by the revived player
* * body_data - Debug data we can use to get a readout of what has been done
*/
/datum/lost_crew_manager/proc/create_lost_crew(revivable = TRUE, datum/corpse_damage_class/forced_class, list/recovered_items, list/protected_items, list/body_data = list())
var/mob/living/carbon/human/new_body = new()
new_body.death()
var/static/list/scenarios = list()
if(!scenarios.len)
var/list/types = subtypesof(/datum/corpse_damage_class)
for(var/datum/corpse_damage_class/scenario as anything in types)
scenarios[scenario] = initial(scenario.weight)
var/list/datum/callback/on_revive_and_player_occupancy = list()
var/datum/corpse_damage_class/scenario = forced_class || pick_weight(scenarios)
scenario = new scenario ()
new_body.living_flags |= STOP_OVERLAY_UPDATE_BODY_PARTS
scenario.apply_character(new_body, protected_items, recovered_items, on_revive_and_player_occupancy, body_data)
scenario.apply_injuries(new_body, recovered_items, on_revive_and_player_occupancy, body_data)
scenario.death_lore += "I should get a formalized assignment!"
new_body.living_flags &= ~STOP_OVERLAY_UPDATE_BODY_PARTS
new_body.update_body_parts()
. = new_body
// so bodies can also be used for runes, morgue, etc
if(!revivable)
return
//it's not necessary since we dont spawn the body until we open the bodybag, but why not be nice for once
new_body.reagents.add_reagent(/datum/reagent/toxin/formaldehyde, 5)
if(!recovered_items)
return
var/obj/item/paper/paper = new()
recovered_items += paper
if(!HAS_TRAIT(new_body, TRAIT_HUSK))
paper.name = "DO NOT REMOVE BRAIN"
paper.add_raw_text("Body swapping is not covered by medical insurance for unhusked bodies. Chemical brain explosives have been administered to enforce stipend.")
var/obj/item/organ/brain/boombrain = new_body.get_organ_by_type(/obj/item/organ/brain)
//I swear to fuck I will explode you. you're not clever
//everyone thought of this, but I am the fool for having any faith
//in people actually wanting to play the job in an interesting manner
//instead of just taking the easiest way out and learning nothing
//(no one abused it yet but I am already getting pinged by people who think they've broken the system when really I just expected better of them)
boombrain.AddElement(/datum/element/dangerous_organ_removal)
else
paper.name = "BODYSWAPPING PERMITTED"
paper.add_raw_text("Body swapping is covered by medical insurance in case of husking and a lack of skill in the practictioner.")
var/obj/item/organ/brain/hersens = new_body.get_organ_by_type(/obj/item/organ/brain)
hersens.AddComponent(
/datum/component/ghostrole_on_revive, \
/* refuse_revival_if_failed = */ TRUE, \
/*on_revival = */ CALLBACK(src, PROC_REF(on_successful_revive), hersens, scenario.death_lore, on_revive_and_player_occupancy) \
)
/// Set a timer for awarding succes and drop some awesome deathlore
/datum/lost_crew_manager/proc/on_successful_revive(obj/item/organ/brain/brain, list/death_lore, list/datum/callback/on_revive_and_player_occupancy)
var/mob/living/carbon/human/owner = brain.owner
owner.mind.add_antag_datum(/datum/antagonist/recovered_crew) //for tracking mostly
var/datum/bank_account/bank_account = new(owner.real_name, owner.mind.assigned_role, owner.dna.species.payday_modifier)
bank_account.adjust_money(starting_funds, "[starting_funds][MONEY_SYMBOL] given to [owner.name] as starting fund.")
owner.account_id = bank_account.account_id
bank_account.replaceable = FALSE
owner.add_mob_memory(/datum/memory/key/account, remembered_id = owner.account_id)
death_lore += "My account number was [owner.account_id]."
brain.RemoveElement(/datum/element/dangerous_organ_removal)
// Drop the sick ass death lore and give them an indicator of who they were and what they can do
for(var/i in 1 to death_lore.len)
addtimer(CALLBACK(src, GLOBAL_PROC_REF(to_chat), owner, span_boldnotice(death_lore[i])), 10 SECONDS + 2 SECONDS * i)
addtimer(CALLBACK(src, PROC_REF(award_succes), owner.mind, death_lore), succes_check_time)
// Run any callbacks our characters or damages may have placed for some effects for when the player is revived
for(var/datum/callback/callback as anything in on_revive_and_player_occupancy)
callback.Invoke()
/// Give medbay a happy announcement and put some money into their budget
/datum/lost_crew_manager/proc/award_succes(datum/mind/revived_mind, list/death_lore)
// I am incredibly disappointed in you
if(revived_mind.current.stat == DEAD)
aas_config_announce(/datum/aas_config_entry/medical_lost_crew_reward, list("PERSON" = revived_mind.name, "AWARD" = 0), null, list(RADIO_CHANNEL_MEDICAL), "Deceased")
return
// You are a credit to society
aas_config_announce(/datum/aas_config_entry/medical_lost_crew_reward, list("PERSON" = revived_mind.name, "AWARD" = credits_on_succes), null, list(RADIO_CHANNEL_MEDICAL), "Revived")
var/datum/bank_account/medical_budget = SSeconomy.get_dep_account(ACCOUNT_MED)
medical_budget.adjust_money(credits_on_succes)
/// A box for recovered items that can only be opened by the new crewmember
/obj/item/storage/lockbox/mind
name = "mind lockbox"
desc = "A locked box, openable only by one mind."
/// The mind needed to unlock the box
var/datum/mind/mind
/obj/item/storage/lockbox/mind/attack_hand(mob/user, list/modifiers)
if (!(src in user.held_items))
return ..()
if(atom_storage.locked && can_unlock(user, silent = TRUE))
toggle_locked(user)
return
return ..()
/obj/item/storage/lockbox/mind/attack_self(mob/user, modifiers)
if (atom_storage.locked && can_unlock(user))
toggle_locked(user)
return
return ..()
/obj/item/storage/lockbox/mind/can_unlock(mob/living/user, obj/item/card/id/id_card, silent = FALSE)
if (user.mind == mind)
return TRUE
if (!silent)
balloon_alert(user, "access denied!")
return FALSE
/obj/item/storage/lockbox/mind/toggle_locked(mob/living/user)
if(!atom_storage.locked)
return
atom_storage.set_locked(STORAGE_NOT_LOCKED)
balloon_alert(user, "unlocked")
/obj/item/storage/lockbox/mind/add_context(atom/source, list/context, obj/item/held_item, mob/user)
if(broken || user.mind != mind)
return NONE
context[SCREENTIP_CONTEXT_LMB] = "Use in-hand to unlock"
return CONTEXTUAL_SCREENTIP_SET
/datum/aas_config_entry/medical_lost_crew_reward
name = "Medical Alert: Lost Crew Revival Program"
announcement_lines_map = list(
"Deceased" = "Sensors indicate lifesigns of %PERSON have seized. Please inform their family of your failure.",
"Revived" = "Sensors indicate continued survival of %PERSON. Well done, %AWARDcr has been transferred to the medical budget."
)
vars_and_tooltips_map = list(
"PERSON" = "will be replaced with body's name",
"AWARD" = "with money that medical department receive, if any"
)
modifiable = FALSE