mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 01:49:19 +00:00
501 lines
18 KiB
Plaintext
501 lines
18 KiB
Plaintext
#define AHELP_FIRST_MESSAGE "Please adminhelp before leaving the round, even if there are no administrators online!"
|
|
|
|
/*
|
|
* Cryogenic refrigeration unit. Basically a despawner.
|
|
* Stealing a lot of concepts/code from sleepers due to massive laziness.
|
|
* The despawn tick will only fire if it's been more than time_till_despawned ticks
|
|
* since time_entered, which is world.time when the occupant moves in.
|
|
* ~ Zuhayr
|
|
*/
|
|
GLOBAL_LIST_EMPTY(cryopod_computers)
|
|
|
|
//Main cryopod console.
|
|
|
|
/obj/machinery/computer/cryopod
|
|
name = "cryogenic oversight console"
|
|
desc = "An interface between crew and the cryogenic storage oversight systems."
|
|
icon = 'icons/obj/Cryogenic2.dmi'
|
|
icon_state = "cellconsole_1"
|
|
icon_keyboard = null
|
|
circuit = /obj/item/circuitboard/cryopodcontrol
|
|
density = FALSE
|
|
interaction_flags_machine = INTERACT_MACHINE_OFFLINE
|
|
req_one_access = list(ACCESS_HEADS, ACCESS_ARMORY) // Heads of staff or the warden can go here to claim recover items from their department that people went were cryodormed with.
|
|
var/mode = null
|
|
|
|
// Used for logging people entering cryosleep and important items they are carrying.
|
|
var/list/frozen_crew = list()
|
|
var/list/stored_packages = list()
|
|
/// Does this console store items? if NOT, will dump all items when the user cryo's instead
|
|
var/allow_items = TRUE
|
|
|
|
var/storage_type = "crewmembers"
|
|
var/storage_name = "Cryogenic Oversight Control"
|
|
|
|
/obj/machinery/computer/cryopod/deconstruct()
|
|
. = ..()
|
|
for(var/i in stored_packages)
|
|
var/obj/O = i
|
|
O.forceMove(drop_location())
|
|
|
|
/obj/machinery/computer/cryopod/Initialize(mapload)
|
|
. = ..()
|
|
GLOB.cryopod_computers += src
|
|
|
|
/obj/machinery/computer/cryopod/Destroy()
|
|
GLOB.cryopod_computers -= src
|
|
return ..()
|
|
|
|
/obj/machinery/computer/cryopod/update_icon_state()
|
|
if(stat & (NOPOWER|BROKEN))
|
|
icon_state = "cellconsole"
|
|
return ..()
|
|
icon_state = "cellconsole_1"
|
|
return ..()
|
|
|
|
/obj/machinery/computer/cryopod/ui_interact(mob/user, datum/tgui/ui)
|
|
if(stat & (NOPOWER|BROKEN))
|
|
return
|
|
|
|
add_fingerprint(user)
|
|
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "CryopodConsole", name)
|
|
ui.open()
|
|
|
|
/obj/machinery/computer/cryopod/ui_data(mob/user)
|
|
var/list/data = list()
|
|
data["frozen_crew"] = frozen_crew
|
|
var/list/item_meta = list()
|
|
|
|
for(var/obj/item/storage/box/blue/cryostorage_items/O as anything in stored_packages)
|
|
item_meta += list(list("name" = O.real_name, "ref" = REF(O))) // i am truely livid about byond lists
|
|
data["item_meta"] = item_meta
|
|
|
|
var/obj/item/card/id/id_card
|
|
var/datum/bank_account/current_user
|
|
if(isliving(user))
|
|
var/mob/living/person = user
|
|
id_card = person.get_idcard()
|
|
if(id_card?.registered_account)
|
|
current_user = id_card.registered_account
|
|
if(current_user)
|
|
data["account_name"] = current_user.account_holder // i do not know why but this uses budget?
|
|
|
|
return data
|
|
|
|
/obj/machinery/computer/cryopod/ui_act(action, params)
|
|
if(..())
|
|
return
|
|
|
|
if(action == "item")
|
|
if(!allowed(usr) && !(obj_flags & EMAGGED))
|
|
to_chat(usr, "<span class='warning'>Access Denied.</span>")
|
|
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
|
return
|
|
|
|
if(!allow_items)
|
|
return
|
|
|
|
if(stored_packages.len == 0)
|
|
to_chat(usr, "<span class='notice'>There is nothing to recover from storage.</span>")
|
|
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
|
return
|
|
|
|
var/obj/I = locate(params["item"])
|
|
playsound(src, "terminal_type", 25, 0)
|
|
|
|
if(!I)
|
|
return
|
|
|
|
if(!(I in stored_packages))
|
|
to_chat(usr, "<span class='notice'>\The [I] is no longer in storage.</span>")
|
|
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
|
return
|
|
|
|
visible_message("<span class='notice'>The console beeps happily as it disgorges \the [I].</span>")
|
|
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
|
|
|
I.forceMove(drop_location())
|
|
if(usr && Adjacent(usr) && usr.can_hold_items())
|
|
usr.put_in_hands(I)
|
|
stored_packages -= I
|
|
|
|
// Cryopods themselves.
|
|
/obj/machinery/cryopod
|
|
name = "cryogenic freezer"
|
|
desc = "Suited for Cyborgs and Humanoids, the pod is a safe place for personnel affected by the Space Sleep Disorder to get some rest."
|
|
icon = 'icons/obj/cryogenic2.dmi'
|
|
icon_state = "cryopod-open"
|
|
density = TRUE
|
|
anchored = TRUE
|
|
state_open = TRUE
|
|
|
|
var/on_store_message = "has entered long-term storage."
|
|
var/on_store_name = "Cryogenic Oversight"
|
|
|
|
/// Time until despawn when a mob enters a cryopod. You can cryo other people in pods.
|
|
var/time_till_despawn = 30 SECONDS
|
|
/// Cooldown for when it's now safe to try an despawn the player.
|
|
COOLDOWN_DECLARE(despawn_world_time)
|
|
|
|
///Weakref to our controller
|
|
var/datum/weakref/control_computer_weakref
|
|
COOLDOWN_DECLARE(last_no_computer_message)
|
|
|
|
/obj/machinery/cryopod/Initialize(mapload)
|
|
..()
|
|
return INITIALIZE_HINT_LATELOAD //Gotta populate the cryopod computer GLOB first
|
|
|
|
/obj/machinery/cryopod/LateInitialize()
|
|
update_icon()
|
|
find_control_computer()
|
|
|
|
// This is not a good situation
|
|
/obj/machinery/cryopod/Destroy()
|
|
control_computer_weakref = null
|
|
return ..()
|
|
|
|
/obj/machinery/cryopod/proc/find_control_computer(urgent = FALSE)
|
|
for(var/cryo_console as anything in GLOB.cryopod_computers)
|
|
var/obj/machinery/computer/cryopod/console = cryo_console
|
|
if(get_area(console) == get_area(src))
|
|
control_computer_weakref = WEAKREF(console)
|
|
break
|
|
|
|
// Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged
|
|
if(!control_computer_weakref && urgent && COOLDOWN_FINISHED(src, last_no_computer_message))
|
|
COOLDOWN_START(src, last_no_computer_message, 5 MINUTES)
|
|
log_admin("Cryopod in [get_area(src)] could not find control computer!")
|
|
message_admins("Cryopod in [get_area(src)] could not find control computer!")
|
|
last_no_computer_message = world.time
|
|
|
|
return control_computer_weakref != null
|
|
|
|
/obj/machinery/cryopod/close_machine(atom/movable/target)
|
|
if(!control_computer_weakref)
|
|
find_control_computer(TRUE)
|
|
if((isnull(target) || isliving(target)) && state_open && !panel_open)
|
|
..(target)
|
|
var/mob/living/mob_occupant = occupant
|
|
investigate_log("Cryogenics machine closed with occupant [key_name(occupant)] by user [key_name(target)].", INVESTIGATE_CRYOGENICS)
|
|
if(mob_occupant && mob_occupant.stat != DEAD)
|
|
to_chat(occupant, span_notice("<b>You feel cool air surround you. You go numb as your senses turn inward.</b>"))
|
|
|
|
COOLDOWN_START(src, despawn_world_time, time_till_despawn)
|
|
icon_state = "cryopod"
|
|
|
|
/obj/machinery/cryopod/open_machine()
|
|
if(occupant)
|
|
investigate_log("Cryogenics machine opened with occupant [key_name(occupant)] inside.", INVESTIGATE_CRYOGENICS)
|
|
..()
|
|
icon_state = "cryopod-open"
|
|
density = TRUE
|
|
name = initial(name)
|
|
|
|
/obj/machinery/cryopod/container_resist(mob/living/user)
|
|
investigate_log("Cryogenics machine container resisted by [key_name(user)] with occupant [key_name(occupant)].", INVESTIGATE_CRYOGENICS)
|
|
visible_message(span_notice("[occupant] emerges from [src]!"),
|
|
span_notice("You climb out of [src]!"))
|
|
open_machine()
|
|
|
|
/obj/machinery/cryopod/relaymove(mob/user)
|
|
container_resist(user)
|
|
|
|
/obj/machinery/cryopod/process()
|
|
if(!occupant)
|
|
return
|
|
|
|
var/mob/living/mob_occupant = occupant
|
|
if(mob_occupant.stat == DEAD)
|
|
open_machine()
|
|
|
|
if(!mob_occupant.client && COOLDOWN_FINISHED(src, despawn_world_time))
|
|
if(!control_computer_weakref)
|
|
find_control_computer(urgent = TRUE)
|
|
|
|
despawn_occupant()
|
|
|
|
/obj/machinery/cryopod/proc/handle_objectives()
|
|
var/mob/living/mob_occupant = occupant
|
|
// Update any existing objectives involving this mob.
|
|
for(var/datum/objective/objective in GLOB.objectives)
|
|
// We don't want revs to get objectives that aren't for heads of staff. Letting
|
|
// them win or lose based on cryo is silly so we remove the objective.
|
|
if(istype(objective,/datum/objective/mutiny) && objective.target == mob_occupant.mind)
|
|
objective.team.objectives -= objective
|
|
qdel(objective)
|
|
for(var/datum/mind/mind in objective.team.members)
|
|
to_chat(mind.current, "<BR>[span_userdanger("Your target is no longer within reach. Objective removed!")]")
|
|
mind.announce_objectives()
|
|
else if(istype(objective.target) && objective.target == mob_occupant.mind)
|
|
if(istype(objective, /datum/objective/contract))
|
|
var/datum/antagonist/traitor/affected_traitor = objective.owner.has_antag_datum(/datum/antagonist/traitor)
|
|
var/datum/contractor_hub/affected_contractor_hub = affected_traitor.contractor_hub
|
|
for(var/datum/syndicate_contract/affected_contract as anything in affected_contractor_hub.assigned_contracts)
|
|
if(affected_contract.contract == objective)
|
|
affected_contract.generate(affected_contractor_hub.assigned_targets)
|
|
affected_contractor_hub.assigned_targets.Add(affected_contract.contract.target)
|
|
to_chat(objective.owner.current, "<BR>[span_userdanger("Contract target out of reach. Contract rerolled.")]")
|
|
break
|
|
else
|
|
var/old_target = objective.target
|
|
objective.target = null
|
|
if(!objective)
|
|
return
|
|
objective.find_target()
|
|
if(!objective.target && objective.owner)
|
|
to_chat(objective.owner.current, "<BR>[span_userdanger("Your target is no longer within reach. Objective removed!")]")
|
|
for(var/datum/antagonist/antag in objective.owner.antag_datums)
|
|
antag.objectives -= objective
|
|
if (!objective.team)
|
|
objective.update_explanation_text()
|
|
objective.owner.announce_objectives()
|
|
to_chat(objective.owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
|
|
else
|
|
var/list/objectivestoupdate
|
|
for(var/datum/mind/objective_owner in objective.get_owners())
|
|
to_chat(objective_owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
|
|
for(var/datum/objective/update_target_objective in objective_owner.get_all_objectives())
|
|
LAZYADD(objectivestoupdate, update_target_objective)
|
|
objectivestoupdate += objective.team.objectives
|
|
for(var/datum/objective/update_objective in objectivestoupdate)
|
|
if(update_objective.target != old_target || !istype(update_objective,objective.type))
|
|
continue
|
|
update_objective.target = objective.target
|
|
update_objective.update_explanation_text()
|
|
to_chat(objective.owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
|
|
update_objective.owner.announce_objectives()
|
|
qdel(objective)
|
|
|
|
/obj/machinery/cryopod/proc/should_preserve_item(obj/item/item)
|
|
for(var/datum/objective_item/steal/possible_item in GLOB.possible_items)
|
|
if(istype(item, possible_item.targetitem))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
// This function can not be undone; do not call this unless you are sure
|
|
/obj/machinery/cryopod/proc/despawn_occupant()
|
|
var/mob/living/mob_occupant = occupant
|
|
var/list/crew_member = list()
|
|
|
|
crew_member["name"] = mob_occupant.real_name
|
|
|
|
if(mob_occupant.mind)
|
|
// Handle job slot/tater cleanup.
|
|
var/job = mob_occupant.mind.assigned_role
|
|
crew_member["job"] = job
|
|
SSjob.FreeRole(job)
|
|
// if(LAZYLEN(mob_occupant.mind.objectives))
|
|
// mob_occupant.mind.objectives.Cut()
|
|
mob_occupant.mind.special_role = null
|
|
else
|
|
crew_member["job"] = "N/A"
|
|
|
|
// Delete them from datacore.
|
|
var/announce_rank = null
|
|
for(var/datum/data/record/medical_record as anything in GLOB.data_core.medical)
|
|
if(medical_record.fields["name"] == mob_occupant.real_name)
|
|
qdel(medical_record)
|
|
for(var/datum/data/record/security_record as anything in GLOB.data_core.security)
|
|
if(security_record.fields["name"] == mob_occupant.real_name)
|
|
qdel(security_record)
|
|
for(var/datum/data/record/general_record as anything in GLOB.data_core.general)
|
|
if(general_record.fields["name"] == mob_occupant.real_name)
|
|
announce_rank = general_record.fields["rank"]
|
|
qdel(general_record)
|
|
|
|
var/obj/machinery/computer/cryopod/control_computer = control_computer_weakref?.resolve()
|
|
if(!control_computer)
|
|
control_computer_weakref = null
|
|
else
|
|
control_computer.frozen_crew += list(crew_member)
|
|
|
|
// Make an announcement and log the person entering storage.
|
|
if(GLOB.announcement_systems.len)
|
|
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
|
|
announcer.announce("CRYOSTORAGE", mob_occupant.real_name, announce_rank, list())
|
|
|
|
visible_message(span_notice("[src] hums and hisses as it moves [mob_occupant.real_name] into storage."))
|
|
|
|
/* ============================= */
|
|
var/list/obj/item/storing = list()
|
|
var/list/obj/item/destroying = list()
|
|
var/list/obj/item/destroy_later = list()
|
|
var/drop_to_ground = !istype(control_computer, /obj/machinery/computer/cryopod) || !control_computer.allow_items
|
|
var/mind_identity = mob_occupant.mind?.name
|
|
var/occupant_identity = mob_occupant.real_name
|
|
|
|
if(iscyborg(mob_occupant))
|
|
var/mob/living/silicon/robot/R = mob_occupant
|
|
if(R.mmi?.brain)
|
|
destroy_later += R.mmi
|
|
destroy_later += R.mmi.brain
|
|
for(var/i in R.module)
|
|
if(!isitem(i))
|
|
destroying += i
|
|
continue
|
|
var/obj/item/I = i
|
|
// let's be honest we only care about the trash bag don't beat around the bush
|
|
if(SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE))
|
|
storing += I.contents
|
|
for(var/atom/movable/AM in I.contents)
|
|
AM.forceMove(src)
|
|
R.module.remove_module(I, TRUE)
|
|
else
|
|
if(ishuman(mob_occupant))
|
|
var/mob/living/carbon/human/H = mob_occupant
|
|
if(H.mind && H.client && H.client.prefs && H == H.mind.original_character)
|
|
H.SaveTCGCards()
|
|
|
|
var/list/gear = list()
|
|
if(iscarbon(mob_occupant)) // sorry simp-le-mobs deserve no mercy
|
|
var/mob/living/carbon/C = mob_occupant
|
|
gear = C.get_all_gear()
|
|
|
|
for(var/obj/item/item_content as anything in gear)
|
|
if(!istype(item_content) || HAS_TRAIT(item_content, TRAIT_NODROP))
|
|
destroying += item_content
|
|
continue
|
|
if(item_content.item_flags & (DROPDEL | ABSTRACT))
|
|
destroying += item_content
|
|
continue
|
|
|
|
// destroying stays in mob for a bit
|
|
item_content.forceMove(src)
|
|
|
|
// WEE WOO SNOWFLAKE TIME
|
|
if(istype(item_content, /obj/item/pda))
|
|
var/obj/item/pda/P = item_content
|
|
if((P.owner == mind_identity) || (P.owner == occupant_identity))
|
|
destroying += P
|
|
else
|
|
storing += P
|
|
else if(istype(item_content, /obj/item/card/id))
|
|
var/obj/item/card/id/idcard = item_content
|
|
if((idcard.registered_name == mind_identity) || (idcard.registered_name == occupant_identity))
|
|
destroying += idcard
|
|
else
|
|
storing += idcard
|
|
else
|
|
storing += item_content
|
|
|
|
// get rid of mobs
|
|
for(var/mob/living/L in mob_occupant.GetAllContents() - mob_occupant)
|
|
L.forceMove(drop_location())
|
|
|
|
if(storing.len)
|
|
var/obj/item/storage/box/blue/cryostorage_items/O = new /obj/item/storage/box/blue/cryostorage_items
|
|
O.name = "cryogenic retrieval package: [mob_occupant.real_name]"
|
|
O.real_name = mob_occupant.real_name
|
|
for(var/i in storing)
|
|
var/obj/item/I = i
|
|
I.forceMove(O)
|
|
O.forceMove(drop_to_ground ? control_computer.drop_location() : control_computer)
|
|
if((control_computer == control_computer) && !drop_to_ground)
|
|
control_computer.stored_packages += O
|
|
/* ============================= */
|
|
|
|
// Ghost and delete the mob.
|
|
// they already did ghost verb
|
|
var/mob/dead/observer/G = mob_occupant.get_ghost(TRUE)
|
|
if(G)
|
|
G.voluntary_ghosted = TRUE
|
|
// they did not ghost verb
|
|
else
|
|
mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE)
|
|
|
|
QDEL_LIST(destroying)
|
|
handle_objectives()
|
|
QDEL_NULL(occupant)
|
|
QDEL_LIST(destroy_later)
|
|
open_machine()
|
|
name = initial(name)
|
|
|
|
/obj/machinery/cryopod/MouseDrop_T(mob/living/target, mob/user)
|
|
if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled)
|
|
return
|
|
|
|
if(occupant)
|
|
to_chat(user, span_notice("[src] is already occupied!"))
|
|
return
|
|
|
|
if(target.stat == DEAD)
|
|
to_chat(user, span_notice("Dead people can not be put into cryo."))
|
|
return
|
|
|
|
if(user != target && target.client)
|
|
if(iscyborg(target))
|
|
to_chat(user, span_danger("You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] online."))
|
|
else
|
|
to_chat(user, span_danger("You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] conscious."))
|
|
return
|
|
else if(target.client) // mob has client
|
|
if(tgalert(target, "Would you like to enter cryosleep?", "Enter Cryopod?", "Yes", "No") != "Yes")
|
|
return
|
|
|
|
if(target == user && world.time - target.client.cryo_warned > 5 MINUTES)
|
|
var/list/caught_string
|
|
var/addendum = ""
|
|
if(target.mind.assigned_role in GLOB.command_positions)
|
|
LAZYADD(caught_string, "Head of Staff")
|
|
addendum = " Be sure to put your locker items back into your locker!"
|
|
if(iscultist(target) || is_servant_of_ratvar(target))
|
|
LAZYADD(caught_string, "Cultist")
|
|
if(is_devil(target))
|
|
LAZYADD(caught_string, "Devil")
|
|
if(target.mind.has_antag_datum(/datum/antagonist/gang))
|
|
LAZYADD(caught_string, "Gangster")
|
|
if(target.mind.has_antag_datum(/datum/antagonist/rev/head))
|
|
LAZYADD(caught_string, "Head Revolutionary")
|
|
if(target.mind.has_antag_datum(/datum/antagonist/rev))
|
|
LAZYADD(caught_string, "Revolutionary")
|
|
|
|
if(caught_string)
|
|
tgui_alert(target, "You're a [english_list(caught_string)]! [AHELP_FIRST_MESSAGE][addendum]")
|
|
target.client.cryo_warned = world.time
|
|
|
|
if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled)
|
|
return
|
|
// rerun the checks in case of shenanigans
|
|
|
|
if(occupant)
|
|
to_chat(user, span_notice("[src] is already occupied!"))
|
|
return
|
|
|
|
if(target == user)
|
|
visible_message("<span class='infoplain'>[user] starts climbing into the cryo pod.</span>")
|
|
else
|
|
visible_message("<span class='infoplain'>[user] starts putting [target] into the cryo pod.</span>")
|
|
|
|
to_chat(target, span_warning("<b>If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.</b>"))
|
|
|
|
log_admin("[key_name(target)] entered a stasis pod.")
|
|
message_admins("[key_name_admin(target)] entered a stasis pod. [ADMIN_JMP(src)]")
|
|
add_fingerprint(target)
|
|
|
|
close_machine(target)
|
|
name = "[name] ([target.name])"
|
|
|
|
// Attacks/effects.
|
|
/obj/machinery/cryopod/blob_act()
|
|
return // Sorta gamey, but we don't really want these to be destroyed.
|
|
|
|
#undef AHELP_FIRST_MESSAGE
|
|
|
|
/obj/item/circuitboard/cryopodcontrol
|
|
name = "Circuit board (Cryogenic Oversight Console)"
|
|
build_path = /obj/machinery/computer/cryopod
|
|
|
|
/obj/machinery/computer/cryopod/contents_explosion()
|
|
return
|
|
|
|
/obj/machinery/computer/cryopod/contents_explosion()
|
|
return //don't blow everyone's shit up.
|
|
|
|
/// The box
|
|
/obj/item/storage/box/blue/cryostorage_items
|
|
w_class = WEIGHT_CLASS_HUGE
|
|
var/real_name = "fire coderbus"
|