Recovered Crew | Medical+Cargo Respawns (#87072)

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
This commit is contained in:
Time-Green
2024-10-26 09:36:57 +02:00
committed by GitHub
parent 95cc258845
commit d170a410d4
45 changed files with 950 additions and 72 deletions

View File

@@ -123,6 +123,14 @@
#define JOB_CENTCOM_SPECIAL_OFFICER "Special Ops Officer"
#define JOB_CENTCOM_PRIVATE_SECURITY "Private Security Force"
//Lost crew
#define JOB_LOSTCREW_ENGINEER "Visiting Engineer"
#define JOB_LOSTCREW_MEDICAL "Visiting Doctor"
#define JOB_LOSTCREW_SECURITY "Visiting Officer"
#define JOB_LOSTCREW_SCIENCE "Visiting Scientist"
#define JOB_LOSTCREW_CARGO "Visiting Technician"
#define JOB_LOSTCREW_CIVILLIAN "Visiting Civillian"
#define JOB_GROUP_ENGINEERS list( \
JOB_STATION_ENGINEER, \
JOB_ATMOSPHERIC_TECHNICIAN, \

View File

@@ -84,6 +84,8 @@
#define ROLE_SYNDICATE_MEDBORG "Syndicate Medical Cyborg"
#define ROLE_SYNDICATE_ASSAULTBORG "Syndicate Assault Cyborg"
#define ROLE_RECOVERED_CREW "Recovered Crew"
//Spawner roles
#define ROLE_ANCIENT_CREW "Ancient Crew"
#define ROLE_ASHWALKER "Ash Walker"
@@ -167,6 +169,7 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_GLITCH = 0,
ROLE_PAI = 0,
ROLE_SENTIENCE = 0,
ROLE_RECOVERED_CREW = 0,
))
//Job defines for what happens when you fail to qualify for any job during job selection

View File

@@ -69,6 +69,9 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using
#define HOURS MINUTES*60
#define DAYS HOURS*24
#define YEARS DAYS*365 //fuck leap days, they were removed in 2069
#define TICKS *world.tick_lag
#define DS2TICKS(DS) ((DS)/world.tick_lag)

View File

@@ -1352,5 +1352,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
///A "fake" effect that should not be subject to normal effect removal methods (like the effect remover component)
#define TRAIT_ILLUSORY_EFFECT "illusory_effect"
/// Gives a little examine to their body that they can be revived with a soul
#define TRAIT_GHOSTROLE_ON_REVIVE "ghostrole_on_revive"
// END TRAIT DEFINES

View File

@@ -36,6 +36,7 @@
#define POLL_IGNORE_STALKER "stalker"
#define POLL_IGNORE_SYNDICATE "syndicate"
#define POLL_IGNORE_VENUSHUMANTRAP "venus_human_trap"
#define POLL_IGNORE_RECOVERED_CREW "recovered_crew"
GLOBAL_LIST_INIT(poll_ignore_desc, list(
POLL_IGNORE_ACADEMY_WIZARD = "Academy Wizard Defender",
@@ -74,6 +75,7 @@ GLOBAL_LIST_INIT(poll_ignore_desc, list(
POLL_IGNORE_STALKER = "Stalker",
POLL_IGNORE_SYNDICATE = "Syndicate",
POLL_IGNORE_VENUSHUMANTRAP = "Venus Human Traps",
POLL_IGNORE_RECOVERED_CREW = "recovered_crew",
))
GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore())

View File

@@ -564,6 +564,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SPEECH_BOOSTER" = TRAIT_SPEECH_BOOSTER,
"TRAIT_MINING_PARRYING" = TRAIT_MINING_PARRYING,
"TRAIT_ILLUSORY_EFFECT" = TRAIT_ILLUSORY_EFFECT,
"TRAIT_GHOSTROLE_ON_REVIVE" = TRAIT_GHOSTROLE_ON_REVIVE,
"TRAIT_IGNORE_FIRE_PROTECTION" = TRAIT_IGNORE_FIRE_PROTECTION,
"TRAIT_LEFT_EYE_SCAR" = TRAIT_LEFT_EYE_SCAR,
"TRAIT_RIGHT_EYE_SCAR" = TRAIT_RIGHT_EYE_SCAR,

View File

@@ -261,7 +261,7 @@
candidates -= player
else if(is_centcom_level(player.z))
candidates -= player // We don't autotator people in CentCom
else if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0))
else if(player.mind && (player.mind.special_role || player.mind.can_roll_midround()))
candidates -= player // We don't autotator people with roles already
/datum/dynamic_ruleset/midround/from_living/autotraitor/execute()
@@ -310,7 +310,7 @@
continue
if(isnull(player.mind))
continue
if(player.mind.special_role || length(player.mind.antag_datums))
if(player.mind.special_role || player.mind.can_roll_midround())
continue
candidates += player
@@ -479,7 +479,7 @@
candidates -= player
continue
if(player.mind && (player.mind.special_role || length(player.mind.antag_datums) > 0))
if(player.mind && (player.mind.special_role || player.mind.can_roll_midround()))
candidates -= player
/datum/dynamic_ruleset/midround/from_living/blob_infection/execute()

View File

@@ -30,3 +30,8 @@
icon_state = "bags"
draw_color = COLOR_WEBSAFE_DARK_GRAY
layers = EXTERNAL_ADJACENT
///PENDING eyes drawn on the face
/datum/bodypart_overlay/simple/soul_pending_eyes
icon_state = "soul_pending_eyes"
layers = EXTERNAL_FRONT

View File

@@ -0,0 +1,119 @@
/// Proc ghosts to enter the body when it get's revived
/datum/component/ghostrole_on_revive
/// If revived and no ghosts, just die again?
var/refuse_revival_if_failed
/// Callback for when the mob is revived and has their body occupied by a ghost
var/datum/callback/on_succesful_revive
/datum/component/ghostrole_on_revive/Initialize(refuse_revival_if_failed, on_succesful_revive)
. = ..()
src.refuse_revival_if_failed = refuse_revival_if_failed
src.on_succesful_revive = on_succesful_revive
ADD_TRAIT(parent, TRAIT_GHOSTROLE_ON_REVIVE, REF(src)) //for adding an alternate examination
if(ismob(parent))
prepare_mob(parent)
return
if(!istype(parent, /obj/item/organ/internal/brain))
return COMPONENT_INCOMPATIBLE
var/obj/item/organ/internal/brain/brein = parent
if(brein.owner)
prepare_mob(brein.owner)
else
prepare_brain(brein)
/// Give the appropriate signals, and watch for organ removal
/datum/component/ghostrole_on_revive/proc/prepare_mob(mob/living/liver)
RegisterSignal(liver, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
ADD_TRAIT(liver, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
liver.med_hud_set_status()
if(iscarbon(liver))
var/mob/living/carbon/carbon = liver
var/obj/item/organ/brain = carbon.get_organ_by_type(/obj/item/organ/internal/brain)
if(brain)
RegisterSignal(brain, COMSIG_ORGAN_REMOVED, PROC_REF(on_remove))
/datum/component/ghostrole_on_revive/proc/on_remove(obj/item/organ/brain, mob/living/old_owner)
SIGNAL_HANDLER
REMOVE_TRAIT(old_owner, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
// we might have some lingering blinking eyes
var/obj/item/bodypart/head/head = old_owner?.get_bodypart(BODY_ZONE_HEAD)
if(head)
var/soul_eyes = locate(/datum/bodypart_overlay/simple/soul_pending_eyes) in head.bodypart_overlays
if(soul_eyes)
head.remove_bodypart_overlay(soul_eyes)
prepare_brain(brain)
/datum/component/ghostrole_on_revive/proc/prepare_brain(obj/item/organ/brein)
SIGNAL_HANDLER
RegisterSignal(brein, COMSIG_ORGAN_IMPLANTED, PROC_REF(prepare_mob_from_brain))
UnregisterSignal(brein, COMSIG_ORGAN_REMOVED)
/datum/component/ghostrole_on_revive/proc/prepare_mob_from_brain(obj/item/organ/internal/brain/brein, mob/living/owner)
SIGNAL_HANDLER
UnregisterSignal(brein, COMSIG_ORGAN_IMPLANTED)
prepare_mob(owner)
/datum/component/ghostrole_on_revive/proc/on_revive(mob/living/aliver)
SIGNAL_HANDLER
INVOKE_ASYNC(src, PROC_REF(poll_ghosts), aliver)
/datum/component/ghostrole_on_revive/proc/poll_ghosts(mob/living/aliver)
var/soul_eyes
var/obj/item/bodypart/head
// adds soulful SOUL PENDING eyes to indicate what's happening to observers
var/mob/living/carbon/human/hewmon
if(ishuman(aliver))
hewmon = aliver
head = hewmon.get_bodypart(BODY_ZONE_HEAD)
if(head)
soul_eyes = new /datum/bodypart_overlay/simple/soul_pending_eyes ()
head.add_bodypart_overlay(soul_eyes)
hewmon.update_body_parts()
var/mob/dead/observer/chosen_one = SSpolling.poll_ghosts_for_target(
question = "Would you like to play as a recovered crewmember?",
role = null,
check_jobban = ROLE_RECOVERED_CREW,
poll_time = 15 SECONDS,
checked_target = aliver,
ignore_category = POLL_IGNORE_RECOVERED_CREW,
alert_pic = aliver,
role_name_text = "recovered crew",
)
if(head)
head.remove_bodypart_overlay(soul_eyes)
hewmon?.update_body_parts()
if(!isobserver(chosen_one))
if(refuse_revival_if_failed)
aliver.death()
aliver.visible_message(span_deadsay("[aliver.name]'s soul is struggling to return!"))
else
aliver.key = chosen_one.key
on_succesful_revive?.Invoke(aliver)
qdel(src)
/datum/component/ghostrole_on_revive/Destroy(force)
REMOVE_TRAIT(parent, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
var/mob/living/living
if(isliving(parent))
living = parent
else if(istype(parent, /obj/item/organ/internal/brain))
var/obj/item/organ/internal/brain/brain = parent
living = brain.owner
living?.med_hud_set_status()
. = ..()

View File

@@ -1,24 +1,36 @@
/**
* ## DANGEROUS SURGICAL REMOVAL ELEMENT
* ## DANGEROUS ORGAN REMOVAL ELEMENT
*
* Makes the organ explode when removed surgically.
* Makes the organ explode when removed (potentially surgically!).
* That's about it.
*/
/datum/element/dangerous_surgical_removal
/datum/element/dangerous_organ_removal
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/datum/element/dangerous_surgical_removal/Attach(datum/target)
/// whether the removal needs to be surgical for it to explode. If you're adding more modes, just pass the signal directly instead
var/surgical
/datum/element/dangerous_organ_removal/Attach(datum/target, surgical = FALSE)
. = ..()
if(!isorgan(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_ORGAN_SURGICALLY_REMOVED, PROC_REF(on_surgical_removal))
/datum/element/dangerous_surgical_removal/Detach(datum/source)
src.surgical = surgical
if(surgical)
RegisterSignal(target, COMSIG_ORGAN_SURGICALLY_REMOVED, PROC_REF(on_removal))
else
RegisterSignal(target, COMSIG_ORGAN_REMOVED, PROC_REF(on_removal))
/datum/element/dangerous_organ_removal/Detach(datum/source)
. = ..()
UnregisterSignal(source, COMSIG_ORGAN_SURGICALLY_REMOVED)
/datum/element/dangerous_surgical_removal/proc/on_surgical_removal(obj/item/organ/source, mob/living/user, mob/living/carbon/old_owner, target_zone, obj/item/tool)
UnregisterSignal(source, list(COMSIG_ORGAN_SURGICALLY_REMOVED, COMSIG_ORGAN_REMOVED))
/datum/element/dangerous_organ_removal/proc/on_removal(obj/item/organ/source, mob/living/user, mob/living/carbon/old_owner, target_zone, obj/item/tool)
SIGNAL_HANDLER
if(source.organ_flags & (ORGAN_FAILING|ORGAN_EMP))
if(surgical && source.organ_flags & (ORGAN_FAILING|ORGAN_EMP))
return
if(user?.Adjacent(source))
source.audible_message("[source] explodes on [user]'s face!")

View File

@@ -540,6 +540,13 @@
var/datum/addiction/affected_addiction = SSaddiction.all_addictions[type]
return affected_addiction.on_lose_addiction_points(src)
/// Whether or not we can roll for midrounds, specifically checking if we have any major antag datums that should block it
/datum/mind/proc/can_roll_midround()
for(var/datum/antagonist/antag as anything in antag_datums)
if(antag.block_midrounds)
return FALSE
return TRUE
/// Setter for the assigned_role job datum.
/datum/mind/proc/set_assigned_role(datum/job/new_role)

View File

@@ -204,6 +204,8 @@ Medical HUD! Basic mode needs suit sensors on.
else if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
if(HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE) || can_defib_client())
holder.icon_state = "huddefib"
else if(HAS_TRAIT(src, TRAIT_GHOSTROLE_ON_REVIVE))
holder.icon_state = "hudghost"
else
holder.icon_state = "huddead"
else

View File

@@ -63,6 +63,8 @@ GLOBAL_LIST_EMPTY(antagonists)
var/hardcore_random_bonus = FALSE
/// A path to the audio stinger that plays upon gaining this datum.
var/stinger_sound
/// Whether this antag datum blocks rolling new antag datums
var/block_midrounds = TRUE
//ANTAG UI

View File

@@ -191,3 +191,12 @@
contains = list(/obj/item/organ/internal/cyberimp/arm/strongarm = 2)
crate_name = "Strong-Arm implant crate"
discountable = SUPPLY_PACK_RARE_DISCOUNTABLE
/datum/supply_pack/medical/lost_crew
name = "Recovered NT Employee corpse"
desc = "We don't have the accomodations to bring them back, could we send them to you? Please revive and employ them. \
The price is a downpayment, we will reimburse the medical budget once our employee has been alive again for a bit."
cost = CARGO_CRATE_VALUE * 5
contains = list(/obj/structure/closet/body_bag/lost_crew/with_body)
crate_name = "body freezer"
crate_type = /obj/structure/closet/crate/secure/freezer

View File

@@ -108,7 +108,7 @@ GLOBAL_LIST_INIT(non_ruleset_antagonists, list(
for(var/antag_key in GLOB.non_ruleset_antagonists)
var/datum/antagonist/antag = GLOB.non_ruleset_antagonists[antag_key]
var/antag_flag = initial(antag.job_rank)
var/days_needed = preferences.parent?.get_remaining_days(
GLOB.special_roles[antag_flag]
)

View File

@@ -132,6 +132,8 @@
/// Minimal character age for this job
var/required_character_age
/// If set, look for a policy with this instead of the job title
var/policy_override
/datum/job/New()
. = ..()
@@ -304,7 +306,7 @@
SHOULD_CALL_PARENT(TRUE)
var/list/info = list()
info += "<b>You are the [title].</b>\n"
var/related_policy = get_policy(title)
var/related_policy = get_policy(policy_override || title)
var/radio_info = get_radio_information()
if(related_policy)
info += related_policy

View File

@@ -0,0 +1,50 @@
/obj/item/bodybag/lost_crew
name = "long-term body bag"
desc = "A folded bag designed for the long-term storage and transportation of cadavers."
unfoldedbag_path = /obj/structure/closet/body_bag/lost_crew
icon_state = "bodybag_lost_folded"
/obj/structure/closet/body_bag/lost_crew
name = "long-term body bag"
desc = "A plastic bag designed for the long-term storage and transportation of cadavers."
icon_state = "bodybag_lost"
foldedbag_path = /obj/item/bodybag/lost_crew
/// Filled with one body. If folded, gives the parent type so we dont make infinite corpses
/obj/structure/closet/body_bag/lost_crew/with_body
/// Whether or not we spawn a paper with everything thats happened to the body
var/debug = FALSE
/obj/structure/closet/body_bag/lost_crew/with_body/PopulateContents()
var/list/recovered_items = list()
var/list/protected_items = list()
var/list/lost_crew_data = list()
var/mob/living/corpse = GLOB.lost_crew_manager.create_lost_crew(revivable = TRUE, recovered_items = recovered_items, protected_items = protected_items, body_data = lost_crew_data)
corpse.mind_initialize()
corpse.forceMove(src)
// Drop stuff like dropped limbs and organs with them in the bag
for(var/obj/object in recovered_items)
object.forceMove(src)
// Spawn a mind lockbox with job stuffs for them to work with when they return
if(protected_items.len && corpse.mind)
var/obj/item/storage/lockbox/mind/box = new(src)
box.mind = corpse.mind
for(var/obj/object in protected_items)
object.forceMove(box)
process_data(lost_crew_data)
/obj/structure/closet/body_bag/lost_crew/with_body/proc/process_data(list/crew_data)
if(!debug)
return
var/obj/item/paper/paper = new(src)
paper.add_raw_text(english_list(crew_data), advanced_html = TRUE)
/// Subtype for debugging damage types
/obj/structure/closet/body_bag/lost_crew/with_body/debug
debug = TRUE

View File

@@ -0,0 +1,139 @@
/// Possible assignments corpses can have, both for flavor and to push them towards contributing to the round
/datum/corpse_assignment
/// Message we send to the player upon revival concerning their job
var/job_lore
/// Gear to give to the crewie in a special locked box
var/list/job_stuffs
/// Trim on the ID we give to the revived person (no trim = no id)
var/datum/id_trim/trim
/// Job datum to apply to the human
var/datum/job/job_datum
/datum/corpse_assignment/proc/apply_assignment(mob/living/carbon/human/working_dead, list/job_gear, list/datum/callback/on_revive_and_player_occupancy)
if(!job_gear)
return
for(var/item in job_stuffs)
job_gear += new item ()
job_gear += job_stuffs
if(job_datum)
on_revive_and_player_occupancy += CALLBACK(src, PROC_REF(assign_job), working_dead) //this needs to happen once the body has been succesfully occupied and revived
if(trim)
var/obj/item/card/id/advanced/card = new()
card.registered_name = working_dead.name
card.registered_age = working_dead.age
SSid_access.apply_trim_to_card(card, trim)
job_gear += card
/datum/corpse_assignment/proc/assign_job(mob/living/carbon/human/working_undead)
working_undead.mind.set_assigned_role_with_greeting(new job_datum (), working_undead.client)
/datum/corpse_assignment/engineer
job_lore = "I was employed as an engineer"
job_stuffs = list(/obj/item/clothing/under/rank/engineering/engineer)
trim = /datum/id_trim/job/visiting_engineer
job_datum = /datum/job/recovered_crew/engineer
/datum/id_trim/job/visiting_engineer
assignment = JOB_LOSTCREW_ENGINEER
trim_state = "trim_stationengineer"
department_color = COLOR_ENGINEERING_ORANGE
subdepartment_color = COLOR_ENGINEERING_ORANGE
sechud_icon_state = SECHUD_STATION_ENGINEER
minimal_access = list(
ACCESS_CONSTRUCTION,
ACCESS_EXTERNAL_AIRLOCKS,
ACCESS_MAINT_TUNNELS,
)
/datum/corpse_assignment/medical
job_lore = "I was employed as a doctor"
job_stuffs = list(/obj/item/clothing/under/rank/medical/doctor)
trim = /datum/id_trim/job/visiting_doctor
job_datum = /datum/job/recovered_crew/doctor
/datum/id_trim/job/visiting_doctor
assignment = JOB_LOSTCREW_MEDICAL
trim_state = "trim_medicaldoctor"
department_color = COLOR_MEDICAL_BLUE
subdepartment_color = COLOR_MEDICAL_BLUE
sechud_icon_state = SECHUD_MEDICAL_DOCTOR
minimal_access = list(
ACCESS_MEDICAL,
)
/datum/corpse_assignment/security
job_lore = "I was employed as security"
job_stuffs = list(/obj/item/clothing/under/rank/security/officer)
trim = /datum/id_trim/job/visiting_security
job_datum = /datum/job/recovered_crew/security
/datum/corpse_assignment/security/apply_assignment(mob/living/carbon/human/working_dead, list/job_gear)
. = ..()
var/obj/item/implant/mindshield/shield = new()
shield.implant(working_dead)
/datum/id_trim/job/visiting_security
assignment = JOB_LOSTCREW_SECURITY
trim_state = "trim_securityofficer"
department_color = COLOR_SECURITY_RED
subdepartment_color = COLOR_SECURITY_RED
sechud_icon_state = SECHUD_SECURITY_OFFICER
minimal_access = list(
ACCESS_BRIG_ENTRANCE,
)
/datum/corpse_assignment/science
job_lore = "I was employed as a scientist"
job_stuffs = list(/obj/item/clothing/under/rank/rnd/scientist)
trim = /datum/id_trim/job/visiting_scientist
job_datum = /datum/job/recovered_crew/scientist
/datum/id_trim/job/visiting_scientist
assignment = JOB_LOSTCREW_SCIENCE
trim_state = "trim_scientist"
department_color = COLOR_SCIENCE_PINK
subdepartment_color = COLOR_SCIENCE_PINK
sechud_icon_state = SECHUD_SCIENTIST
minimal_access = list(
ACCESS_AUX_BASE,
ACCESS_SCIENCE,
)
/datum/corpse_assignment/cargo
job_lore = "I was employed as a technician"
job_stuffs = list(/obj/item/clothing/under/rank/cargo/tech)
trim = /datum/id_trim/job/visiting_technician
job_datum = /datum/job/recovered_crew/cargo
/datum/id_trim/job/visiting_technician
assignment = JOB_LOSTCREW_CARGO
trim_state = "trim_cargotechnician"
department_color = COLOR_CARGO_BROWN
subdepartment_color = COLOR_CARGO_BROWN
sechud_icon_state = SECHUD_CARGO_TECHNICIAN
minimal_access = list(
ACCESS_CARGO,
ACCESS_MAINT_TUNNELS,
)
/datum/corpse_assignment/civillian
job_lore = "I was employed as a civllian"
job_stuffs = list(/obj/item/clothing/under/color/grey)
trim = /datum/id_trim/job/visiting_civillian
job_datum = /datum/job/recovered_crew/civillian
/datum/id_trim/job/visiting_civillian
assignment = JOB_LOSTCREW_CIVILLIAN
trim_state = "trim_assistant"
sechud_icon_state = SECHUD_ASSISTANT
minimal_access = list()
extra_access = list(
ACCESS_MAINT_TUNNELS,
ACCESS_SERVICE,
)

View File

@@ -0,0 +1,44 @@
/// Datum for controlling the base character, such as species, scarring, styles, augments etc
/datum/corpse_character
/// Species type to spawn with
var/datum/species/species_type = /datum/species/human
/datum/corpse_character/proc/apply_character(mob/living/carbon/human/fashionable_corpse, list/saved_objects, list/datum/callback/on_revive_and_player_occupancy)
fashionable_corpse.set_species(species_type)
fashionable_corpse.fully_replace_character_name(fashionable_corpse.real_name, fashionable_corpse.generate_random_mob_name())
/// Not really all roundstart species, but plasmaman is a bit too flamboyant and felinids aren't interesting
/datum/corpse_character/mostly_roundstart
var/list/possible_species = list(
/datum/species/human = 10,
/datum/species/lizard = 2,
/datum/species/ethereal = 1,
/datum/species/moth = 1,
)
/datum/corpse_character/mostly_roundstart/apply_character(mob/living/carbon/human/fashionable_corpse, list/saved_objects, list/datum/callback/on_revive_and_player_occupancy)
species_type = pick_weight(possible_species)
..()
/datum/corpse_character/human
species_type = /datum/species/human
/// used by the morgue trays to spawn bodies (obeying three different configs???????????????????? yes please daddy give me more config for benign features)
/datum/corpse_character/morgue
/datum/corpse_character/morgue/apply_character(mob/living/carbon/human/fashionable_corpse, list/saved_objects, list/datum/callback/on_revive_and_player_occupancy)
var/use_species = !(CONFIG_GET(flag/morgue_cadaver_disable_nonhumans))
var/species_probability = CONFIG_GET(number/morgue_cadaver_other_species_probability) * use_species
var/override_species = CONFIG_GET(string/morgue_cadaver_override_species)
if(override_species)
species_type = GLOB.species_list[override_species]
else if(prob(species_probability))
species_type = GLOB.species_list[pick(get_selectable_species())]
if(!species_type)
stack_trace("failed to spawn cadaver with species ID [species_type]") //if it's invalid they'll just be a human, so no need to worry too much aside from yelling at the server owner lol.
species_type = initial(species_type)
return ..()

View File

@@ -0,0 +1,33 @@
/datum/job/recovered_crew
policy_override = "Recovered Crew"
faction = FACTION_STATION
/datum/job/recovered_crew/doctor
title = JOB_LOSTCREW_MEDICAL
department_head = list(JOB_CHIEF_MEDICAL_OFFICER)
supervisors = SUPERVISOR_CMO
/datum/job/recovered_crew/engineer
title = JOB_LOSTCREW_ENGINEER
department_head = list(JOB_CHIEF_ENGINEER)
supervisors = SUPERVISOR_CE
/datum/job/recovered_crew/security
title = JOB_LOSTCREW_SECURITY
department_head = list(JOB_HEAD_OF_SECURITY)
supervisors = SUPERVISOR_HOS
/datum/job/recovered_crew/cargo
title = JOB_LOSTCREW_CARGO
department_head = list(JOB_QUARTERMASTER)
supervisors = SUPERVISOR_QM
/datum/job/recovered_crew/scientist
title = JOB_LOSTCREW_SCIENCE
department_head = list(JOB_RESEARCH_DIRECTOR)
supervisors = SUPERVISOR_RD
/datum/job/recovered_crew/civillian
title = JOB_LOSTCREW_CIVILLIAN
department_head = list(JOB_HEAD_OF_PERSONNEL)
supervisors = SUPERVISOR_HOP

View File

@@ -0,0 +1,110 @@
// Unique damage types we can apply for blacklisting, so we dont remove all organs twice etc
#define CORPSE_DAMAGE_ORGAN_DECAY "organ decay"
#define CORPSE_DAMAGE_ORGAN_LOSS "organ loss"
#define CORPSE_DAMAGE_LIMB_LOSS "limb loss"
/// The main style controller for new dead bodies! Determines the character, lore, possible causes of death, decay and every other modifier!
/datum/corpse_damage_class
/// Message sent to the recovered crew, constructed from the different death lores
var/list/death_lore = list()
/// Lore we give on revival, this is the first line
var/area_lore = "I was doing something"
/// Weight given to this class. Setting this is all that's needed for it to be rollable
var/weight
/// Different character archetypes we can spawn
var/list/possible_character_types
/// Assignments that can be given to the corpse
var/list/possible_character_assignments
/// Whatever killed us
var/list/possible_causes_of_death
/// Goddamn space vultures stealing my organs
var/list/post_mortem_effects
/// A random decay we apply. Defined here so we can vary it (i.e. a spaced body has less decay than one in a warm jungle)
var/list/decays = list(/datum/corpse_damage/post_mortem/organ_decay)
/// When healthscanned, this is the minimum time that shows
var/lore_death_time_min = 1 DAYS
/// When healthscanned, this is the maximum time that shows
var/lore_death_time_max = 5 YEARS
/// Generate and apply a possible character (species etc)
/datum/corpse_damage_class/proc/apply_character(mob/living/carbon/human/fashion_corpse, list/protected_objects, list/datum/callback/on_revive_and_player_occupancy, list/body_data)
var/datum/corpse_character/character = pick_weight(possible_character_types)
character = new character()
character.apply_character(fashion_corpse, protected_objects, on_revive_and_player_occupancy)
var/datum/corpse_assignment/assignment = pick_weight(possible_character_assignments)
if(assignment)
assignment = new assignment()
assignment.apply_assignment(fashion_corpse, protected_objects, on_revive_and_player_occupancy)
death_lore += assignment?.job_lore
body_data += character.type
body_data += assignment?.type
/// Set up injuries
/datum/corpse_damage_class/proc/apply_injuries(mob/living/carbon/human/victim, list/saved_objects, list/datum/callback/on_revive_and_player_occupancy, list/body_data)
var/datum/corpse_damage/cause_of_death/cause_of_death = pick_damage_type(possible_causes_of_death)
cause_of_death.apply_to_body(victim, rand(), saved_objects, on_revive_and_player_occupancy)
var/datum/corpse_damage/post_mortem = pick_damage_type(post_mortem_effects)
post_mortem?.apply_to_body(victim, rand(), saved_objects, on_revive_and_player_occupancy)
var/datum/corpse_damage/decay = pick_damage_type(decays)
decay?.apply_to_body(victim, rand(), saved_objects, on_revive_and_player_occupancy)
// Simulate bloodloss by dragging/moving
victim.blood_volume = max(victim.blood_volume - victim.bleedDragAmount() * rand(20, 100), 0)
set_death_date(victim)
body_data += cause_of_death.type
body_data += post_mortem?.type
body_data += decay.type
death_lore += area_lore + " " + cause_of_death.cause_of_death
/// Wrapped pickweight so we can have a bit more controle over how we pick our rules
/datum/corpse_damage_class/proc/pick_damage_type(list/damages, list/used_damage_types)
var/list/possible_damages = list()
for(var/datum/corpse_damage/damage as anything in damages)
if(isdatum(damage) && damage.damage_type && (damage.damage_type in used_damage_types))
continue
possible_damages[damage] = damages[damage]
var/datum/corpse_damage/chosen = pick_weight(possible_damages)
if(!ispath(chosen)) //can also be null for some variants
return null
chosen = new chosen()
if(chosen.damage_type)
used_damage_types += chosen.damage_type
return chosen
/// Soulfully give a date of death for health analyzers
/datum/corpse_damage_class/proc/set_death_date(mob/living/carbon/body)
var/died_how_long_ago = rand(lore_death_time_min, lore_death_time_max)
body.timeofdeath = world.time - died_how_long_ago
var/death_real_time = world.realtime - died_how_long_ago
var/current_date = time2text(death_real_time, "DD Month")
var/current_year = text2num(time2text(death_real_time, "YYYY")) + STATION_YEAR_OFFSET
body.station_timestamp_timeofdeath = "[current_date] [current_year]"
/// Main corpse damage type that's used to apply damages to a body
/datum/corpse_damage
/// When given, automatically blacklist corpse_damages with the same damage_type flag to avoid stuff like being delimbed twice (dragon ate me AND I got space vultures???)
var/damage_type
/// Tear IT UPPP!!! Apply any damages to the body that we need to
/datum/corpse_damage/proc/apply_to_body(mob/living/carbon/human/body, severity, list/storage)
return
/// This is the reason we died
/datum/corpse_damage/cause_of_death
/// I was in x, [when I ....]
var/cause_of_death = "when I tripped and died."
/// Some post mortem damages from space vultures
/datum/corpse_damage/post_mortem

View File

@@ -0,0 +1,36 @@
/datum/corpse_damage/cause_of_death/plasmafire
cause_of_death = "when I got caught in a plasmafire!"
/// The max tox damage we deal
var/tox_damage_max = 40
/// Guaranteed burn damage
var/burn_damage_base = 100
/// Burn damage that fluctuates with severity
var/burn_damage_max = 100
/datum/corpse_damage/cause_of_death/plasmafire/apply_to_body(mob/living/carbon/human/body, severity, list/storage, list/datum/callback/on_revive_and_player_occupancy)
body.apply_damage(burn_damage_base + burn_damage_max * severity, BURN, wound_bonus = 100 * severity, spread_damage = TRUE)
body.apply_damage(tox_damage_max * severity, TOX)
/datum/corpse_damage/cause_of_death/explosion
cause_of_death = "when I noticed a bomb!"
/// The explosion severity
var/severity = EXPLODE_HEAVY
/// The maximum amount of explosions we can proc
var/explosion_count_max = 4
/datum/corpse_damage/cause_of_death/explosion/apply_to_body(mob/living/carbon/human/body, severity, list/storage, list/datum/callback/on_revive_and_player_occupancy)
for(var/i in 1 to max(round(explosion_count_max * severity), 1))
body.ex_act(EXPLODE_HEAVY)
/datum/corpse_damage/cause_of_death/spaced
cause_of_death = "when I got spaced!"
/// Guaranteed brute and burn damage
var/base_damage = 90
/// Damage influenced by the severity
var/damage_max = 100
/datum/corpse_damage/cause_of_death/spaced/apply_to_body(mob/living/carbon/human/body, severity, list/storage, list/datum/callback/on_revive_and_player_occupancy)
body.apply_damage(base_damage + damage_max * (severity * rand(80, 120) * 0.01), BURN, spread_damage = TRUE)
body.apply_damage(base_damage + damage_max * (severity * rand(80, 120) * 0.01), BRUTE, spread_damage = TRUE)
body.set_coretemperature(TCMB)

View File

@@ -0,0 +1,23 @@
/// We simulate decay on bodies. This is always used, but severity can differ (though tending to the more severe)
/datum/corpse_damage/post_mortem/organ_decay
damage_type = CORPSE_DAMAGE_ORGAN_DECAY
/// The max amount of decay we can apply to organs, scaled by severity
var/max_decay_time = 40 MINUTES
/datum/corpse_damage/post_mortem/organ_decay/apply_to_body(mob/living/carbon/human/body, severity, list/sorage, list/datum/callback/on_revive_and_player_occupancy)
if(!body.organs)
return FALSE
// * 0.5 because life ticks happen about every 2 seconds (we really need a way to get the current life tickspeed)
var/decay_ticks = max_decay_time * severity * 0.5
for(var/obj/item/organ/internal/internal in body.organs)
internal.apply_organ_damage(decay_ticks * internal.decay_factor)
return TRUE
/datum/corpse_damage/post_mortem/organ_decay/light
max_decay_time = 15 MINUTES
/datum/corpse_damage/post_mortem/organ_decay/heavy
max_decay_time = 48 HOURS

View File

@@ -0,0 +1,45 @@
/// Simulates a melee attack with a specified weapon
/datum/corpse_damage/cause_of_death/melee_weapon
/// The weapon with which we hit
var/obj/item/weapon
/// The minimal amount of hits
var/min_hits = 5
/// The maximum amount of hits
var/max_hits = 15
/datum/corpse_damage/cause_of_death/melee_weapon/apply_to_body(mob/living/carbon/human/body, severity, list/storage, list/datum/callback/on_revive_and_player_occupancy)
weapon = get_weapon(body)
var/hits = ((max_hits - min_hits) * severity + min_hits)
for(var/i in 1 to hits)
weapon.attack(body, body) //needs an attacker, no reason it cant be the body as well
body.zone_selected = pick(GLOB.all_body_zones)
/datum/corpse_damage/cause_of_death/melee_weapon/proc/get_weapon(mob/living/carbon/human/body)
return new weapon(null)
/datum/corpse_damage/cause_of_death/melee_weapon/esword
weapon = /obj/item/melee/energy/sword
cause_of_death = "when I was attacked by a filthy traitor!"
/datum/corpse_damage/cause_of_death/melee_weapon/esword/get_weapon(mob/living/carbon/human/body)
. = ..()
var/obj/item/melee/energy/sword/esword = .
esword.attack_self() //need to activate it
/datum/corpse_damage/cause_of_death/melee_weapon/changeling
weapon = /obj/item/melee/arm_blade
cause_of_death = "when I was attacked by a terrifying changeling!"
/datum/corpse_damage/cause_of_death/melee_weapon/toolbox
cause_of_death = "when some worthless assistant toolboxed me!"
weapon = /obj/item/storage/toolbox
/datum/corpse_damage/cause_of_death/melee_weapon/heretic
cause_of_death = "when a flipping heretic attacked me!"
/datum/corpse_damage/cause_of_death/melee_weapon/heretic/get_weapon(mob/living/carbon/human/body)
var/obj/item/melee/sickly_blade/blade = pick(subtypesof(/obj/item/melee/sickly_blade)) //pick a random blade, can be a bunch of fun stuff
return new blade (null)

View File

@@ -0,0 +1,47 @@
/// Damn space vultures man! At least they dont go for the brain
/datum/corpse_damage/post_mortem/organ_loss
damage_type = CORPSE_DAMAGE_ORGAN_LOSS
/// Chance that the organ is stored and delivered with the body
var/organ_save_chance = 20
/// Minimum organs we can lose
var/min_organs = 2
/// Maximum organs we can lose
var/max_organs = 8
/datum/corpse_damage/post_mortem/organ_loss/apply_to_body(mob/living/carbon/human/body, severity, list/saved_movables, list/datum/callback/on_revive_and_player_occupancy)
var/organs_to_take = round(min_organs + (max_organs - min_organs) * severity)
var/list/organs_we_can_take = body.organs - body.get_organ_slot(ORGAN_SLOT_BRAIN)
for(var/i in 1 to organs_to_take)
var/obj/organ = pick(organs_we_can_take)
if(prob(organ_save_chance) && saved_movables) //if lucky, we can save the organ and have it be delivered with the body
organ.moveToNullspace()
saved_movables += organ
else
qdel(organ)
/// Damn space vultures man! At least they dont go for the chest or head, or they do but we don't get to see those bodies :O
/datum/corpse_damage/post_mortem/limb_loss
damage_type = CORPSE_DAMAGE_LIMB_LOSS
/// Chance that the limb is stored and delivered with the body
var/limb_save_chance = 20
/// Min limbs we can lose
var/min_limbs = 1
/// Max limbs we can lose
var/max_limbs = 4
/datum/corpse_damage/post_mortem/limb_loss/apply_to_body(mob/living/carbon/human/body, severity, list/saved_movables, list/datum/callback/on_revive_and_player_occupancy)
var/limbs_to_take = round(min_limbs + (max_limbs - min_limbs) * severity)
var/list/limbs_we_can_take = body.bodyparts - body.get_bodypart(BODY_ZONE_HEAD) - body.get_bodypart(BODY_ZONE_CHEST)
if(!limbs_we_can_take.len)
return
for(var/i in 1 to limbs_to_take)
var/obj/limb = pick(limbs_we_can_take)
if(prob(limb_save_chance) && saved_movables)
limb.moveToNullspace()
saved_movables += limb
else
qdel(limb)

View File

@@ -0,0 +1,46 @@
/// Default scenario, with normal species, assignments and damages etc
/datum/corpse_damage_class/station
area_lore = "I was working in a space station"
weight = 10
possible_character_types = list(/datum/corpse_character/mostly_roundstart = 1)
possible_character_assignments = list(
/datum/corpse_assignment/engineer = 1,
/datum/corpse_assignment/medical = 1,
/datum/corpse_assignment/security = 1,
/datum/corpse_assignment/science = 1,
/datum/corpse_assignment/cargo = 1,
/datum/corpse_assignment/civillian = 1,
)
possible_causes_of_death = list(
/datum/corpse_damage/cause_of_death/melee_weapon/esword = 1,
/datum/corpse_damage/cause_of_death/melee_weapon/changeling = 1,
/datum/corpse_damage/cause_of_death/melee_weapon/toolbox = 1,
/datum/corpse_damage/cause_of_death/melee_weapon/heretic = 1,
/datum/corpse_damage/cause_of_death/explosion = 1,
/datum/corpse_damage/cause_of_death/plasmafire = 1,
)
post_mortem_effects = list(
/datum/corpse_damage/post_mortem/limb_loss = 5,
/datum/corpse_damage/post_mortem/organ_loss = 5,
null = 1,
)
decays = list(
/datum/corpse_damage/post_mortem/organ_decay = 3,
/datum/corpse_damage/post_mortem/organ_decay/light = 1,
/datum/corpse_damage/post_mortem/organ_decay/heavy = 1,
)
/// Less decay, spread burn and brute damage
/datum/corpse_damage_class/station/spaced
weight = 2
possible_causes_of_death = list(/datum/corpse_damage/cause_of_death/spaced = 1)
decays = list(/datum/corpse_damage/post_mortem/organ_decay/light = 1)
/// Human morgue body
/datum/corpse_damage_class/station/morgue
weight = 0
possible_character_types = list(/datum/corpse_character/morgue = 1)
possible_character_assignments = list()

View File

@@ -0,0 +1,136 @@
/// 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 succesful revive
var/credits_on_succes = /datum/supply_pack/medical/lost_crew::cost + CARGO_CRATE_VALUE * 2
/// How long after succesful 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 = 100
/**
* Creates a body with random background and injuries
*
* Arguments:
* * revivable - Whether or not we can be revived to grand a ghost controle
* * 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(null)
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 ()
scenario.apply_character(new_body, protected_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
// 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/internal/brain/boombrain = new_body.get_organ_by_type(/obj/item/organ/internal/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/internal/brain/hersens = new_body.get_organ_by_type(/obj/item/organ/internal/brain)
hersens.AddComponent(
/datum/component/ghostrole_on_revive, \
/* refuse_revival_if_failed = */ TRUE, \
/*on_revival = */ CALLBACK(src, PROC_REF(on_succesful_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_succesful_revive(obj/item/organ/internal/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]cr 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)
var/obj/item/radio/headset/radio = new /obj/item/radio/headset/silicon/ai(revived_mind.current) //radio cant be in nullspace or brit shakes
radio.set_frequency(FREQ_MEDICAL)
radio.name = "Medical Announcer"
// i am incredibly disappointed in you
if(revived_mind.current.stat == DEAD)
radio.talk_into(radio, "Sensors indicate lifesigns of [revived_mind.name] have seized. Please inform their family of your failure.", RADIO_CHANNEL_MEDICAL)
return
// You are a credit to society
radio.talk_into(radio, "Sensors indicate continued survival of [revived_mind.name]. Well done, [credits_on_succes]cr has been transferred to the medical budget.", RADIO_CHANNEL_MEDICAL)
var/datum/bank_account/medical_budget = SSeconomy.get_dep_account(ACCOUNT_MED)
medical_budget.adjust_money(credits_on_succes)
qdel(radio)
/// 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_self(mob/user, modifiers)
. = ..()
if(user.mind == mind)
atom_storage.locked = STORAGE_NOT_LOCKED
balloon_alert(user, atom_storage.locked ? "locked" : "unlocked")
update_appearance()

View File

@@ -0,0 +1,9 @@
/// Revived crew ready to serve once more! Only here for tracking/admin reasons, otherwise hidden
/datum/antagonist/recovered_crew
name = "\improper Recovered Crew"
show_in_antagpanel = TRUE
job_rank = ROLE_RECOVERED_CREW
show_name_in_check_antagonists = TRUE
show_to_ghosts = FALSE
silent = TRUE
block_midrounds = FALSE

View File

@@ -874,11 +874,8 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_atoms_ontop)
var/admin_spawned
///number of bodies to spawn
var/bodycount = 3
/// These species IDs will be barred from spawning if morgue_cadaver_disable_nonhumans is disabled (In the future, we can also dehardcode this)
var/list/blacklisted_from_rng_placement = list(
SPECIES_ETHEREAL, // they revive on death which is bad juju
SPECIES_HUMAN, // already have a 50% chance of being selected
)
/// Corpse type we spawn thats always human
var/datum/corpse_damage_class/morgue_body_class = /datum/corpse_damage_class/station/morgue
/obj/effect/mapping_helpers/dead_body_placer/Initialize(mapload)
. = ..()
@@ -906,57 +903,16 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_atoms_ontop)
var/reuse_trays = (numtrays < bodycount) //are we going to spawn more trays than bodies?
var/use_species = !(CONFIG_GET(flag/morgue_cadaver_disable_nonhumans))
var/species_probability = CONFIG_GET(number/morgue_cadaver_other_species_probability)
var/override_species = CONFIG_GET(string/morgue_cadaver_override_species)
var/list/usable_races
if(use_species)
var/list/temp_list = get_selectable_species()
usable_races = temp_list.Copy()
LAZYREMOVE(usable_races, blacklisted_from_rng_placement)
if(!LAZYLEN(usable_races))
notice("morgue_cadaver_disable_nonhumans. There are no valid roundstart nonhuman races enabled. Defaulting to humans only!")
if(override_species)
warning("morgue_cadaver_override_species BEING OVERRIDEN since morgue_cadaver_disable_nonhumans is disabled.")
else if(override_species)
LAZYADD(usable_races, override_species)
var/guaranteed_human_spawned = FALSE
for (var/i in 1 to bodycount)
var/obj/structure/bodycontainer/morgue/morgue_tray = reuse_trays ? pick(trays) : pick_n_take(trays)
var/obj/structure/closet/body_bag/body_bag = new(morgue_tray.loc)
var/mob/living/carbon/human/new_human = new(morgue_tray.loc)
var/species_to_pick
if(guaranteed_human_spawned && use_species)
if(LAZYLEN(usable_races))
if(!isnum(species_probability))
species_probability = 50
stack_trace("WARNING: morgue_cadaver_other_species_probability CONFIG SET TO 0% WHEN SPAWNING. DEFAULTING TO [species_probability]%.")
if(prob(species_probability))
species_to_pick = pick(usable_races)
var/datum/species/new_human_species = GLOB.species_list[species_to_pick]
if(new_human_species)
new_human.set_species(new_human_species)
new_human.fully_replace_character_name(new_human.real_name, new_human.generate_random_mob_name())
else
stack_trace("failed to spawn cadaver with species ID [species_to_pick]") //if it's invalid they'll just be a human, so no need to worry too much aside from yelling at the server owner lol.
else
guaranteed_human_spawned = TRUE
var/mob/living/carbon/human/new_human = GLOB.lost_crew_manager.create_lost_crew(revivable = FALSE, forced_class = morgue_body_class)
body_bag.insert(new_human, TRUE)
body_bag.close()
body_bag.handle_tag("[new_human.real_name][species_to_pick ? " - [capitalize(species_to_pick)]" : " - Human"]")
body_bag.handle_tag("[new_human.real_name][new_human.dna?.species ? " - [new_human.dna.species.name]" : " - Human"]")
body_bag.forceMove(morgue_tray)
new_human.death() //here lies the mans, rip in pepperoni.
for (var/obj/item/organ/internal/part in new_human.organs) //randomly remove organs from each body, set those we keep to be in stasis
if (prob(40))
qdel(part)
else
part.organ_flags |= ORGAN_FROZEN
morgue_tray.update_appearance()
qdel(src)

View File

@@ -244,7 +244,7 @@
/mob/living/carbon/on_fall()
. = ..()
loc.handle_fall(src)//it's loc so it doesn't call the mob's handle_fall which does nothing
loc?.handle_fall(src)//it's loc so it doesn't call the mob's handle_fall which does nothing
/mob/living/carbon/resist_buckle()
if(!HAS_TRAIT(src, TRAIT_RESTRAINED))

View File

@@ -239,9 +239,12 @@
if(CONSCIOUS)
if(HAS_TRAIT(src, TRAIT_DUMB))
. += "[t_He] [t_has] a stupid expression on [t_his] face."
if(get_organ_by_type(/obj/item/organ/internal/brain) && isnull(ai_controller))
var/obj/item/organ/internal/brain/brain = get_organ_by_type(/obj/item/organ/internal/brain)
if(brain && isnull(ai_controller))
var/npc_message = ""
if(!key)
if(HAS_TRAIT(brain, TRAIT_GHOSTROLE_ON_REVIVE))
npc_message = "Soul is pending..."
else if(!key)
npc_message = "[t_He] [t_is] totally catatonic. The stresses of life in deep-space must have been too much for [t_him]. Any recovery is unlikely."
else if(!client)
npc_message ="[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon."
@@ -317,7 +320,10 @@
var/t_his = p_their()
var/t_is = p_are()
//This checks to see if the body is revivable
if(get_organ_by_type(/obj/item/organ/internal/brain) && (client || HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE) || (ghost?.can_reenter_corpse && ghost?.client)))
var/obj/item/organ/brain = get_organ_by_type(/obj/item/organ/internal/brain)
if(brain && HAS_TRAIT(brain, TRAIT_GHOSTROLE_ON_REVIVE))
return span_deadsay("[t_He] [t_is] limp and unresponsive; but [t_his] soul might yet come back...")
if(brain && (client || HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE) || (ghost?.can_reenter_corpse && ghost?.client)))
return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life...")
else
return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life and [t_his] soul has departed...")

View File

@@ -1,3 +1,4 @@
/// List of roundstart races' their species_id's
GLOBAL_LIST_EMPTY(roundstart_races)
///List of all roundstart languages by path except common
GLOBAL_LIST_EMPTY(uncommon_roundstart_languages)
@@ -192,7 +193,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
return ..()
/// Gets a list of all species available to choose in roundstart.
/// Gets a list of all species id's available to choose in roundstart.
/proc/get_selectable_species()
RETURN_TYPE(/list)

View File

@@ -251,6 +251,8 @@
owner = null
QDEL_LAZYLIST(scars)
for(var/atom/movable/movable in contents)
qdel(movable)

View File

@@ -70,7 +70,8 @@
if(org_zone != BODY_ZONE_CHEST)
continue
organ.Remove(chest_owner)
organ.forceMove(chest_owner.loc)
if(chest_owner.loc)
organ.forceMove(chest_owner.loc)
. += organ
if(cavity_item)

View File

@@ -5,6 +5,7 @@
* * typepath The typepath of the organ to get
*/
/mob/proc/get_organ_by_type(typepath)
RETURN_TYPE(/obj/item/organ)
return
/**

View File

@@ -285,7 +285,7 @@
//surplus organs are so awful that they explode when removed, unless failing
/obj/item/organ/internal/heart/cybernetic/surplus/Initialize(mapload)
. = ..()
AddElement(/datum/element/dangerous_surgical_removal)
AddElement(/datum/element/dangerous_organ_removal, /*surgical = */ TRUE)
/obj/item/organ/internal/heart/freedom
name = "heart of freedom"

View File

@@ -270,7 +270,7 @@
//surplus organs are so awful that they explode when removed, unless failing
/obj/item/organ/internal/liver/cybernetic/surplus/Initialize(mapload)
. = ..()
AddElement(/datum/element/dangerous_surgical_removal)
AddElement(/datum/element/dangerous_organ_removal, /*surgical = */ TRUE)
#undef LIVER_DEFAULT_TOX_TOLERANCE
#undef LIVER_DEFAULT_TOX_RESISTANCE

View File

@@ -945,7 +945,7 @@
//surplus organs are so awful that they explode when removed, unless failing
/obj/item/organ/internal/lungs/cybernetic/surplus/Initialize(mapload)
. = ..()
AddElement(/datum/element/dangerous_surgical_removal)
AddElement(/datum/element/dangerous_organ_removal, /*surgical = */ TRUE)
/obj/item/organ/internal/lungs/lavaland
name = "blackened frilled lungs" // blackened from necropolis exposure

View File

@@ -323,6 +323,6 @@
//surplus organs are so awful that they explode when removed, unless failing
/obj/item/organ/internal/stomach/cybernetic/surplus/Initialize(mapload)
. = ..()
AddElement(/datum/element/dangerous_surgical_removal)
AddElement(/datum/element/dangerous_organ_removal, /*surgical = */ TRUE)
#undef STOMACH_METABOLISM_CONSTANT

View File

@@ -6,6 +6,8 @@
var/list/all_closets = subtypesof(/obj/structure/closet)
//Supply pods. They are sent, crashed, opened and never closed again. They also cause exceptions in nullspace.
all_closets -= typesof(/obj/structure/closet/supplypod)
/// these bitches spawn specially crafted humans with gear and moving organs being shuffled around through the whole process
all_closets -= typesof(/obj/structure/closet/body_bag/lost_crew/with_body)
for(var/closet_type in all_closets)
var/obj/structure/closet/closet = allocate(closet_type)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1128,6 +1128,7 @@
#include "code\datums\components\gas_leaker.dm"
#include "code\datums\components\geiger_sound.dm"
#include "code\datums\components\ghost_direct_control.dm"
#include "code\datums\components\ghostrole_on_revive.dm"
#include "code\datums\components\glass_passer.dm"
#include "code\datums\components\gps.dm"
#include "code\datums\components\grillable.dm"
@@ -4577,6 +4578,18 @@
#include "code\modules\lootpanel\search_object.dm"
#include "code\modules\lootpanel\ss_looting.dm"
#include "code\modules\lootpanel\ui.dm"
#include "code\modules\lost_crew\body_bags.dm"
#include "code\modules\lost_crew\lost_crew_manager.dm"
#include "code\modules\lost_crew\recovered_crew.dm"
#include "code\modules\lost_crew\character\_assignment.dm"
#include "code\modules\lost_crew\character\_character.dm"
#include "code\modules\lost_crew\character\job_datums.dm"
#include "code\modules\lost_crew\damages\_damages.dm"
#include "code\modules\lost_crew\damages\accident.dm"
#include "code\modules\lost_crew\damages\decay.dm"
#include "code\modules\lost_crew\damages\melee.dm"
#include "code\modules\lost_crew\damages\post_mortem.dm"
#include "code\modules\lost_crew\damages\scenarios.dm"
#include "code\modules\mafia\_defines.dm"
#include "code\modules\mafia\controller.dm"
#include "code\modules\mafia\controller_ui.dm"

View File

@@ -20,6 +20,7 @@
export type Antagonist = {
// the antag_flag, made lowercase, and with non-alphanumerics removed.
// or from non_ruleset_antagonists
key: string;
name: string;