From ef50ca23cbba89c8d8e43c35b3fc00610ca12f80 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Tue, 13 Apr 2021 12:53:56 +0200 Subject: [PATCH] [MIRROR] Ports Cryopods (#4880) * Ports Cryopods (#58025) Co-authored-by: Mothblocks <35135081+Mothblocks@ users.noreply.github.com> Co-authored-by: Kokonut <38844529+maxymax13@ users.noreply.github.com> Co-authored-by: Aleksej Komarov * Ports Cryopods * aaaa * Update cryopod.dm Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Co-authored-by: Mothblocks <35135081+Mothblocks@ users.noreply.github.com> Co-authored-by: Kokonut <38844529+maxymax13@ users.noreply.github.com> Co-authored-by: Aleksej Komarov Co-authored-by: Gandalf --- code/controllers/subsystem/job.dm | 8 + code/datums/mind.dm | 1 + code/game/area/space_station_13_areas.dm | 4 + code/game/gamemodes/objective.dm | 2 + code/game/machinery/announcement_system.dm | 6 +- code/game/machinery/cryopod.dm | 401 ++++++++++++++ code/modules/antagonists/cult/cult.dm | 58 ++- code/modules/client/client_defines.dm | 3 +- icons/obj/machines/cryopod.dmi | Bin 0 -> 10843 bytes .../modules/cryosleep/code/cryopod.dm | 493 ------------------ modular_skyrat/modules/cryosleep/code/job.dm | 7 - tgstation.dme | 3 +- .../tgui/interfaces/CryopodConsole.js | 95 ++++ 13 files changed, 547 insertions(+), 534 deletions(-) create mode 100644 code/game/machinery/cryopod.dm create mode 100644 icons/obj/machines/cryopod.dmi delete mode 100644 modular_skyrat/modules/cryosleep/code/cryopod.dm delete mode 100644 modular_skyrat/modules/cryosleep/code/job.dm create mode 100644 tgui/packages/tgui/interfaces/CryopodConsole.js diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 825ae019bf5..35401aa8da7 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -145,6 +145,14 @@ SUBSYSTEM_DEF(job) JobDebug("AR has failed, Player: [player], Rank: [rank]") return FALSE +/datum/controller/subsystem/job/proc/FreeRole(rank) + if(!rank) + return + JobDebug("Freeing role: [rank]") + var/datum/job/job = GetJob(rank) + if(!job) + return FALSE + job.current_positions = max(0, job.current_positions - 1) /datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level, flag) JobDebug("Running FOC, Job: [job], Level: [level], Flag: [flag]") diff --git a/code/datums/mind.dm b/code/datums/mind.dm index edd8e6ce2c8..b0eceaead7e 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -41,6 +41,7 @@ var/assigned_role var/special_role var/list/restricted_roles = list() + var/list/datum/objective/objectives = list() var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. diff --git a/code/game/area/space_station_13_areas.dm b/code/game/area/space_station_13_areas.dm index a18dfab69e9..117b297ceaf 100644 --- a/code/game/area/space_station_13_areas.dm +++ b/code/game/area/space_station_13_areas.dm @@ -608,6 +608,10 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "Recreation Area" icon_state = "rec" +/area/commons/cryopods + name = "Cryopod Room" + icon_state = "cryopod" + // Commons - Vacant Rooms /area/commons/vacant_room diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 45c16de0608..d1d701298d1 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1,4 +1,5 @@ GLOBAL_LIST(admin_objective_list) //Prefilled admin assignable objective list +GLOBAL_LIST_EMPTY(objectives) /datum/objective var/datum/mind/owner //The primary owner of the objective. !!SOMEWHAT DEPRECATED!! Prefer using 'team' for new code. @@ -14,6 +15,7 @@ GLOBAL_LIST(admin_objective_list) //Prefilled admin assignable objective list var/martyr_compatible = FALSE //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. /datum/objective/New(text) + GLOB.objectives += src if(text) explanation_text = text diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm index 3d655f943c0..0c9ac785167 100644 --- a/code/game/machinery/announcement_system.dm +++ b/code/game/machinery/announcement_system.dm @@ -83,12 +83,10 @@ GLOBAL_LIST_EMPTY(announcement_systems) message = CompileText(arrival, user, rank) else if(message_type == "NEWHEAD" && newheadToggle) message = CompileText(newhead, user, rank) + else if(message_type == "CRYOSTORAGE") + message = "[user][rank ? ", [rank]" : ""] has been moved to cryo storage." else if(message_type == "ARRIVALS_BROKEN") message = "The arrivals shuttle has been damaged. Docking for repairs..." - //SKYRAT EDIT ADDITION BEGIN - CRYOSLEEP - else if(message_type == "CRYOSTORAGE") - message = CompileText("%PERSON, %RANK has been moved to cryo storage.", user, rank) - //SKYRAT EDIT ADDITION END broadcast(message, channels) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm new file mode 100644 index 00000000000..5bad45465e1 --- /dev/null +++ b/code/game/machinery/cryopod.dm @@ -0,0 +1,401 @@ +/* + * 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 = 'modular_skyrat/modules/cryosleep/icons/cryogenics.dmi' //SKYRAT EDIT CHANGE - ORIGINAL: 'icons/obj/machines/cryopod.dmi' + icon_state = "cellconsole_1" + // 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/frozen_items = list() + + var/storage_type = "crewmembers" + var/storage_name = "Cryogenic Oversight Control" + var/allow_items = TRUE + +/obj/machinery/computer/cryopod/Initialize() + . = ..() + GLOB.cryopod_computers += src + +/obj/machinery/computer/cryopod/Destroy() + GLOB.cryopod_computers -= src + ..() + +/obj/machinery/computer/cryopod/update_icon_state() + if(machine_stat & (NOPOWER|BROKEN)) + icon_state = "cellconsole" + return ..() + icon_state = "cellconsole_1" + return ..() + +/obj/machinery/computer/cryopod/ui_interact(mob/user, datum/tgui/ui) + if(machine_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["allow_items"] = allow_items + data["frozen_crew"] = frozen_crew + data["frozen_items"] = list() + + if(allow_items) + data["frozen_items"] = frozen_items + + 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 + + return data + +/obj/machinery/computer/cryopod/ui_act(action, params) + . = ..() + if(.) + return + + var/mob/user = usr + + add_fingerprint(user) + + switch(action) + if("one_item") + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + + if(!allow_items) return + + if(!params["item"]) + return + + var/obj/item/item = frozen_items[text2num(params["item"])] + if(!item) + to_chat(user, "[item] is no longer in storage.") + return + + visible_message("[src] beeps happily as it disgorges [item].") + item.forceMove(get_turf(src)) + frozen_items -= item + + if("all_items") + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + + if(!allow_items) return + + visible_message("[src] beeps happily as it disgorges the desired objects.") + + for(var/obj/item/item in frozen_items) + item.forceMove(get_turf(src)) + frozen_items -= item + + return TRUE + +// 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/machines/cryopod.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" + + // 3 minutes-ish safe period before being despawned. + var/time_till_despawn = 3 MINUTES // This is reduced to 30 seconds if a player manually enters cryo + var/fast_despawn = 30 SECONDS + var/despawn_world_time = null // Used to keep track of the safe period. + + var/obj/machinery/computer/cryopod/control_computer + COOLDOWN_DECLARE(last_no_computer_message) + +/obj/machinery/cryopod/Initialize() + ..() + 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 = 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 = 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 && 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 != null + +/obj/machinery/cryopod/close_machine(mob/user) + if(!control_computer) + find_control_computer(TRUE) + if((isnull(user) || istype(user)) && state_open && !panel_open) + ..(user) + var/mob/living/mob_occupant = occupant + if(mob_occupant && mob_occupant.stat != DEAD) + to_chat(occupant, "You feel cool air surround you. You go numb as your senses turn inward.") + if(mob_occupant.client || !mob_occupant.key) // Self cryos and SSD + despawn_world_time = world.time + (fast_despawn) // This gives them 30 seconds + else + despawn_world_time = world.time + time_till_despawn + icon_state = "cryopod" + +/obj/machinery/cryopod/open_machine() + ..() + icon_state = "cryopod-open" + density = TRUE + name = initial(name) + +/obj/machinery/cryopod/container_resist_act(mob/living/user) + visible_message("[occupant] emerges from [src]!", + "You climb out of [src]!") + open_machine() + +/obj/machinery/cryopod/relaymove(mob/user) + container_resist_act(user) + +/obj/machinery/cryopod/process() + if(!occupant) + return + + var/mob/living/mob_occupant = occupant + if(mob_occupant) + // Eject dead people + if(mob_occupant.stat == DEAD) + open_machine() + + if(world.time <= despawn_world_time) + return + + if(!mob_occupant.client && mob_occupant.stat <= SOFT_CRIT) // Occupant is living and has no client. + if(!control_computer) + find_control_computer(urgent = TRUE) // better hope you found it this time + + 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, "
Your target is no longer within reach. Objective removed!") + mind.announce_objectives() + else if(objective.target && istype(objective.target, /datum/mind)) + if(objective.target == mob_occupant.mind) + var/old_target = objective.target + objective.target = null + if(!objective) + return + objective.find_target() + if(!objective.target && objective.owner) + to_chat(objective.owner.current, "
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, "
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, "
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, "
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 && mob_occupant.mind.assigned_role) + // 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 + //SKYRAT EDIT ADDITION + if(SSticker.mode.name == "assaultops") + if(is_assaultops_target(mob_occupant.mind)) + remove_assaultops_target(mob_occupant.mind) //Remove them from the list of targets for the assops. + //SKYRAT EDIT END + else + crew_member["job"] = "N/A" + // Delete them from datacore. + var/announce_rank = null + for(var/datum/data/record/medical_record in GLOB.data_core.medical) + if(medical_record.fields["name"] == mob_occupant.real_name) + qdel(medical_record) + for(var/datum/data/record/security_record in GLOB.data_core.security) + if(security_record.fields["name"] == mob_occupant.real_name) + qdel(security_record) + for(var/datum/data/record/general_record in GLOB.data_core.general) + if(general_record.fields["name"] == mob_occupant.real_name) + announce_rank = general_record.fields["rank"] + qdel(general_record) + + 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("[src] hums and hisses as it moves [mob_occupant.real_name] into storage.") + + for(var/obj/item/item in mob_occupant.GetAllContents()) + if(item.loc.loc && (item.loc.loc == loc || item.loc.loc == control_computer)) + continue // means we already moved whatever this thing was in + // I'm a professional, okay + + if(!should_preserve_item(item)) + continue + + if(control_computer && control_computer.allow_items) + control_computer.frozen_items += item + mob_occupant.transferItemToLoc(item, control_computer, TRUE) + else + mob_occupant.transferItemToLoc(item, loc, TRUE) + + var/list/contents = mob_occupant.GetAllContents() + QDEL_LIST(contents) + + // Ghost and delete the mob. + if(!mob_occupant.get_ghost(TRUE)) + if(world.time < 15 MINUTES) // before the 15 minute mark + mob_occupant.ghostize(FALSE) // Players despawned too early may not re-enter the game + else + mob_occupant.ghostize(TRUE) + handle_objectives() + QDEL_NULL(occupant) + 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, "[src] is already occupied!") + return + + if(target.stat == DEAD) + to_chat(user, "Dead people can not be put into cryo.") + return + + if(target.client && user != target) + if(iscyborg(target)) + to_chat(user, "You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] online.") + else + to_chat(user, "You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] conscious.") + return + else if(target.client) + if(alert(target,"Would you like to enter cryosleep?",,"Yes","No") == "No") + return + + var/generic_plsnoleave_message = " Please adminhelp before leaving the round, even if there are no administrators online!" + + if(target == user && COOLDOWN_FINISHED(target.client, cryo_warned)) + var/caught = FALSE + var/datum/antagonist/antag = target.mind.has_antag_datum(/datum/antagonist) + var/datum/job/target_job = SSjob.GetJob(target.mind.assigned_role) + if(target_job.req_admin_notify) + alert("You're an important role![generic_plsnoleave_message]") + caught = TRUE + if(antag) + alert("You're \a [antag.name]![generic_plsnoleave_message]") + caught = TRUE + if(caught) + COOLDOWN_START(target.client, cryo_warned, 5 MINUTES) + return + + 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(target == user) + visible_message("[user] starts climbing into the cryo pod.") + else + visible_message("[user] starts putting [target] into the cryo pod.") + + if(occupant) + to_chat(user, "[src] is in use.") + return + close_machine(target) + + to_chat(target, "If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.") + name = "[name] ([occupant.name])" + 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) + +// Attacks/effects. +/obj/machinery/cryopod/blob_act() + return // Sorta gamey, but we don't really want these to be destroyed. diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index 6f56ac050b1..aca712df82b 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -283,23 +283,31 @@ /datum/team/cult/proc/ascend(cultist) if(ishuman(cultist)) - var/mob/living/carbon/human/H = cultist - new /obj/effect/temp_visual/cult/sparks(get_turf(H), H.dir) + var/mob/living/carbon/human/human = cultist + new /obj/effect/temp_visual/cult/sparks(get_turf(human), human.dir) var/istate = pick("halo1","halo2","halo3","halo4","halo5","halo6") var/mutable_appearance/new_halo_overlay = mutable_appearance('icons/effects/32x64.dmi', istate, -HALO_LAYER) - H.overlays_standing[HALO_LAYER] = new_halo_overlay - H.apply_overlay(HALO_LAYER) + human.overlays_standing[HALO_LAYER] = new_halo_overlay + human.apply_overlay(HALO_LAYER) -/datum/team/cult/proc/setup_objectives() - //SAC OBJECTIVE , todo: move this to objective internals +/datum/team/cult/proc/make_image(datum/objective/sacrifice/sac_objective) + var/datum/job/job_of_sacrifice = SSjob.GetJob(sac_objective.target.assigned_role) + var/datum/preferences/prefs_of_sacrifice = sac_objective.target.current.client.prefs + var/icon/reshape = get_flat_human_icon(null, job_of_sacrifice, prefs_of_sacrifice, list(SOUTH)) + reshape.Shift(SOUTH, 4) + reshape.Shift(EAST, 1) + reshape.Crop(7,4,26,31) + reshape.Crop(-5,-3,26,30) + sac_objective.sac_image = reshape + +/datum/objective/sacrifice/find_target(dupe_search_range) + if(!istype(team, /datum/team/cult)) + return + var/datum/team/cult/cult = team var/list/target_candidates = list() - var/datum/objective/sacrifice/sac_objective = new - sac_objective.team = src - for(var/mob/living/carbon/human/player in GLOB.player_list) if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && !is_convertable_to_cult(player) && player.stat != DEAD) target_candidates += player.mind - if(target_candidates.len == 0) message_admins("Cult Sacrifice: Could not find unconvertible target, checking for convertible target.") for(var/mob/living/carbon/human/player in GLOB.player_list) @@ -307,30 +315,26 @@ target_candidates += player.mind listclearnulls(target_candidates) if(LAZYLEN(target_candidates)) - sac_objective.target = pick(target_candidates) - sac_objective.update_explanation_text() - - var/datum/job/sacjob = SSjob.GetJob(sac_objective.target.assigned_role) - var/datum/preferences/sacface = sac_objective.target.current.client.prefs - var/icon/reshape = get_flat_human_icon(null, sacjob, sacface, list(SOUTH)) - reshape.Shift(SOUTH, 4) - reshape.Shift(EAST, 1) - reshape.Crop(7,4,26,31) - reshape.Crop(-5,-3,26,30) - sac_objective.sac_image = reshape - - objectives += sac_objective + target = pick(target_candidates) + update_explanation_text() else message_admins("Cult Sacrifice: Could not find unconvertible or convertible target. WELP!") + cult.make_image(src) + for(var/datum/mind/mind in cult.members) + if(mind.current) + mind.current.clear_alert("bloodsense") + mind.current.throw_alert("bloodsense", /atom/movable/screen/alert/bloodsense) +/datum/team/cult/proc/setup_objectives() + var/datum/objective/sacrifice/sacrifice_objective = new + sacrifice_objective.team = src + sacrifice_objective.find_target() + objectives += sacrifice_objective - //SUMMON OBJECTIVE - - var/datum/objective/eldergod/summon_objective = new() + var/datum/objective/eldergod/summon_objective = new summon_objective.team = src objectives += summon_objective - /datum/objective/sacrifice var/sacced = FALSE var/sac_image diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index c79aafe2fec..748de9c50d4 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -30,7 +30,8 @@ var/total_count_reset = 0 ///Internal counter for clients sending external (IRC/Discord) relay messages via ahelp to prevent spamming. Set to a number every time an admin reply is sent, decremented for every client send. var/externalreplyamount = 0 - + ///When was the last time we warned them about not cryoing without an ahelp, set to -5 minutes so that rounstart cryo still warns + COOLDOWN_DECLARE(cryo_warned) ///////// //OTHER// ///////// diff --git a/icons/obj/machines/cryopod.dmi b/icons/obj/machines/cryopod.dmi new file mode 100644 index 0000000000000000000000000000000000000000..e8c07222e19e3cf471fa2676c7b4588f0dc05638 GIT binary patch literal 10843 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

jq555(N?V;2H86(3Yn0v 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

rj8Q z=LuK&y2DDZWxYRw#<^1%vIL2^>-` zyW}ooQ%DfG6f;O;U<(5Yur@^AA8kVXz5GZ*t^y@Y#WoOAECOE ziPab2J&8RXa2cC#jTzDT6-HrN$URwL@+_B@a$ItzR786&;-rMM!eq;@oxwAmT~ZFY z=7Jj+-hH~kkw5P|2t5NFbt!yrj5hS4JuNAbJef?{18LMAccess Denied.") - playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - updateUsrDialog() - return - if(!allow_items) return - - if(frozen_items.len == 0) - to_chat(user, "There is nothing to recover from storage.") - playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - updateUsrDialog() - return - - var/obj/item/I = input(user, "Please choose which object to retrieve.","Object recovery",null) as null|anything in frozen_items - playsound(src, "terminal_type", 25, 0) - if(!I) - return - - if(!(I in frozen_items)) - to_chat(user, "\The [I] is no longer in storage.") - playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - updateUsrDialog() - return - - visible_message("The console beeps happily as it disgorges \the [I].") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - - I.forceMove(drop_location()) - if(user && Adjacent(user) && user.can_hold_items()) - user.put_in_hands(I) - frozen_items -= I - updateUsrDialog() - - else if(href_list["allitems"]) - playsound(src, "terminal_type", 25, 0) - if(!allowed(user)) - to_chat(user, "Access Denied.") - playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - updateUsrDialog() - return - if(!allow_items) - return - - if(frozen_items.len == 0) - to_chat(user, "There is nothing to recover from storage.") - playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - return - - visible_message("The console beeps happily as it disgorges the desired objects.") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - - for(var/obj/item/I in frozen_items) - I.forceMove(drop_location()) - frozen_items -= I - updateUsrDialog() - - else if (href_list["menu"]) - src.menu = text2num(href_list["menu"]) - playsound(src, "terminal_type", 25, 0) - updateUsrDialog() - - ui_interact(usr) - updateUsrDialog() - return - -/obj/item/circuitboard/cryopodcontrol - name = "Circuit board (Cryogenic Oversight Console)" - build_path = "/obj/machinery/computer/cryopod" - -/obj/machinery/computer/cryopod/contents_explosion() - return - -//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 = 'modular_skyrat/modules/cryosleep/icons/cryogenics.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" - - // 15 minutes-ish safe period before being despawned. - var/time_till_despawn = 15 * 600 // This is reduced by 90% if a player manually enters cryo - var/despawn_world_time // Used to keep track of the safe period. - - var/obj/machinery/computer/cryopod/control_computer - var/last_no_computer_message = 0 - - // These items are preserved when the process() despawn proc occurs. - var/static/list/preserve_items = typecacheof(list( - /obj/item/hand_tele, - /obj/item/card/id/advanced/gold/captains_spare, - /obj/item/aicard, - /obj/item/mmi, - /obj/item/paicard, - /obj/item/gun, - /obj/item/pinpointer, - /obj/item/clothing/shoes/magboots, - /obj/item/areaeditor/blueprints, - /obj/item/clothing/head/helmet/space, - /obj/item/clothing/suit/space, - /obj/item/clothing/suit/armor, - /obj/item/defibrillator/compact, - /obj/item/reagent_containers/hypospray/cmo, - /obj/item/clothing/accessory/medal/gold/captain, - /obj/item/clothing/gloves/krav_maga, - /obj/item/nullrod, - /obj/item/tank/jetpack, - /obj/item/documents, - /obj/item/nuke_core_container - )) - // These items will NOT be preserved - var/static/list/do_not_preserve_items = typecacheof(list( - /obj/item/mmi/posibrain, - /obj/item/gun/energy/laser/mounted, - /obj/item/gun/energy/e_gun/advtaser/mounted, - /obj/item/gun/ballistic/revolver/grenadelauncher/cyborg, - /obj/item/gun/energy/disabler/cyborg, - /obj/item/gun/energy/e_gun/advtaser/cyborg, - /obj/item/gun/energy/printer, - /obj/item/gun/energy/kinetic_accelerator/cyborg, - /obj/item/gun/energy/laser/cyborg - )) - -/obj/machinery/cryopod/Initialize(mapload) - . = ..() - update_icon() - find_control_computer(mapload) - -/obj/machinery/cryopod/proc/find_control_computer(urgent = FALSE) - for(var/obj/machinery/computer/cryopod/C in get_area(src)) - control_computer = C - if(C) - return C - 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 && urgent && last_no_computer_message + 5*60*10 < world.time) - 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 != null - -/obj/machinery/cryopod/close_machine(mob/user) - if(!control_computer) - find_control_computer(TRUE) - if((isnull(user) || istype(user)) && state_open && !panel_open) - START_PROCESSING(SSmachines, src) - ..(user) - var/mob/living/mob_occupant = occupant - if(mob_occupant && mob_occupant.stat != DEAD) - to_chat(occupant, "You feel cool air surround you. You go numb as your senses turn inward.") - if(mob_occupant.client)//if they're logged in - despawn_world_time = world.time + (time_till_despawn * 0.1) - else - despawn_world_time = world.time + time_till_despawn - icon_state = "cryopod" - -/obj/machinery/cryopod/open_machine() - ..() - STOP_PROCESSING(SSmachines, src) - icon_state = "cryopod-open" - density = TRUE - name = initial(name) - -/obj/machinery/cryopod/container_resist_act(mob/living/user) - visible_message("[occupant] emerges from [src]!", - "You climb out of [src]!") - open_machine() - -/obj/machinery/cryopod/relaymove(mob/user) - container_resist_act(user) - -/obj/machinery/cryopod/process() - if(!occupant) - return - - var/mob/living/mob_occupant = occupant - if(mob_occupant) - // Eject dead people - if(mob_occupant.stat == DEAD) - open_machine() - - if(!(world.time > despawn_world_time + 100))//+ 10 seconds - return - - if(!mob_occupant.client && mob_occupant.stat < 2) //Occupant is living and has no client. - if(!control_computer) - find_control_computer(urgent = TRUE)//better hope you found it this time - - despawn_occupant() - -#define CRYO_DESTROY 0 -#define CRYO_PRESERVE 1 -#define CRYO_OBJECTIVE 2 -#define CRYO_IGNORE 3 -#define CRYO_DESTROY_LATER 4 - -/obj/machinery/cryopod/proc/should_preserve_item(obj/item/I) - for(var/datum/objective_item/steal/T in control_computer.theft_cache) - if(istype(I, T.targetitem) && T.check_special_completion(I)) - return CRYO_OBJECTIVE - if(preserve_items[I] && !do_not_preserve_items[I]) - return CRYO_PRESERVE - return CRYO_DESTROY - -// This function can not be undone; do not call this unless you are sure -/obj/machinery/cryopod/proc/despawn_occupant() - if(!control_computer) - find_control_computer() - - var/mob/living/mob_occupant = occupant - var/list/obj/item/cryo_items = list() - - //Handle Borg stuff first - if(iscyborg(mob_occupant)) - var/mob/living/silicon/robot/R = mob_occupant - if(R.mmi?.brain) - cryo_items[R.mmi] = CRYO_DESTROY_LATER - cryo_items[R.mmi.brain] = CRYO_DESTROY_LATER - for(var/obj/item/I in R.model) // the tools the borg has; metal, glass, guns etc - for(var/obj/item/O in I) // the things inside the tools, if anything; mainly for janiborg trash bags - cryo_items[O] = should_preserve_item(O) - O.forceMove(src) - R.model.remove_module(I, TRUE) //delete the module itself so it doesn't transfer over. - - //Drop all items into the pod. - for(var/obj/item/I in mob_occupant) - if(cryo_items[I] == CRYO_IGNORE || cryo_items[I] ==CRYO_DESTROY_LATER) - continue - cryo_items[I] = should_preserve_item(I) - mob_occupant.transferItemToLoc(I, src, TRUE) - if(I.contents.len) //Make sure we catch anything not handled by qdel() on the items. - if(cryo_items[I] != CRYO_DESTROY) // Don't remove the contents of things that need preservation - continue - for(var/obj/item/O in I.contents) - cryo_items[O] = should_preserve_item(O) - O.forceMove(src) - - for(var/A in cryo_items) - var/obj/item/I = A - if(QDELETED(I)) //edge cases and DROPDEL. - continue - var/preserve = cryo_items[I] - if(preserve == CRYO_DESTROY_LATER) - continue - if(preserve != CRYO_IGNORE) - if(preserve == CRYO_DESTROY) - qdel(I) - else if(control_computer?.allow_items) - control_computer.frozen_items += I - if(preserve == CRYO_OBJECTIVE) - control_computer.objective_items += I - I.moveToNullspace() - else - I.forceMove(loc) - cryo_items -= I - - /* - //Update any existing objectives involving this mob. - for(var/datum/objective/O 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(O,/datum/objective/mutiny) && O.target == mob_occupant.mind) - qdel(O) - else if(O.target && istype(O.target, /datum/mind)) - if(O.target == mob_occupant.mind) - if(O.owner && O.owner.current) - to_chat(O.owner.current, "
You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!") - O.target = null - spawn(10) //This should ideally fire after the occupant is deleted. - if(!O) - return - O.find_target() - O.update_explanation_text() - if(!(O.target)) - qdel(O)*/ - - if(mob_occupant.mind) - //Handle job slot/tater cleanup. - if(mob_occupant.mind.assigned_role) - SSjob.FreeRole(mob_occupant.mind.assigned_role) - mob_occupant.mind.special_role = null - if(SSticker.mode.name == "assaultops") - if(is_assaultops_target(mob_occupant.mind)) - remove_assaultops_target(mob_occupant.mind) //Remove them from the list of targets for the assops. - - // Delete them from datacore. - - var/announce_rank = null - for(var/datum/data/record/R in GLOB.data_core.medical) - if((R.fields["name"] == mob_occupant.real_name)) - qdel(R) - break - for(var/datum/data/record/T in GLOB.data_core.security) - if((T.fields["name"] == mob_occupant.real_name)) - qdel(T) - break - for(var/datum/data/record/G in GLOB.data_core.general) - if((G.fields["name"] == mob_occupant.real_name)) - announce_rank = G.fields["rank"] - qdel(G) - break - - //Make an announcement and log the person entering storage. - if(control_computer) - control_computer.frozen_crew += "[mob_occupant.real_name]" - - 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("\The [src] hums and hisses as it moves [mob_occupant.real_name] into storage.") - - // Ghost and delete the mob. - if(!mob_occupant.get_ghost(TRUE)) - mob_occupant.ghostize(FALSE) - - QDEL_NULL(occupant) - for(var/I in cryo_items) //only "CRYO_DESTROY_LATER" atoms are left) - var/atom/A = I - if(!QDELETED(A)) - qdel(A) - open_machine() - name = initial(name) - -#undef CRYO_DESTROY -#undef CRYO_PRESERVE -#undef CRYO_OBJECTIVE -#undef CRYO_IGNORE -#undef CRYO_DESTROY_LATER - -/obj/machinery/cryopod/MouseDrop_T(mob/living/target, mob/user) - if(!istype(target) || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || !ismob(target) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled) - return - if(occupant) - to_chat(user, "The cryo pod is already occupied!") - return - if(target.stat == DEAD) - to_chat(user, "Dead people can not be put into cryo.") - return - if(target.client && user != target) - if(iscyborg(target)) - to_chat(user, "You can't put [target] into [src]. They're online.") - else - to_chat(user, "You can't put [target] into [src]. They're conscious.") - return - else if(target.client) - if(alert(target,"Would you like to enter cryosleep?",,"Yes","No") == "No") - return - if (user != target && round(((world.time - target.lastclienttime) / (1 MINUTES)),1) <= CONFIG_GET(number/cryo_min_ssd_time)) - to_chat(user, "You can't put [target] into [src]. They might wake up soon.") - return - var/generic_plsnoleave_message = " Please adminhelp before leaving the round, even if there are no administrators online!" - if(target == user && world.time - target.client.cryo_warned > 5 MINUTES)//if we haven't warned them in the last 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)) - LAZYADD(caught_string, "Cultist") - 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) - alert(target, "You're a [english_list(caught_string)]![generic_plsnoleave_message][addendum]") - target.client.cryo_warned = world.time - return - if(!target || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled) - return - //rerun the checks in case of shenanigans - if(occupant) - to_chat(user, "\The [src] is in use.") - return - if(target == user) - visible_message("[user] starts climbing into the cryo pod.") - else - visible_message("[user] starts putting [target] into the cryo pod.") - if(do_after(user, 3 SECONDS, target = target)) - if(occupant) - return - close_machine(target) - to_chat(target, "If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.") - name = "[name] ([occupant.name])" - if(target == user) - log_admin("[key_name(target)] entered a stasis pod.") - message_admins("[key_name_admin(target)] entered a stasis pod. (JMP)") - else - log_admin("[key_name(user)] put [key_name(target)] inside a stasis pod.") - message_admins("[key_name_admin(user)] put [key_name_admin(target)] inside a stasis pod. (JMP)") - add_fingerprint(target) - -//Attacks/effects. -/obj/machinery/cryopod/blob_act() - return //Sorta gamey, but we don't really want these to be destroyed. diff --git a/modular_skyrat/modules/cryosleep/code/job.dm b/modular_skyrat/modules/cryosleep/code/job.dm deleted file mode 100644 index 920c811a6e4..00000000000 --- a/modular_skyrat/modules/cryosleep/code/job.dm +++ /dev/null @@ -1,7 +0,0 @@ -/datum/controller/subsystem/job/proc/FreeRole(rank) - if(!rank) - return - var/datum/job/job = GetJob(rank) - if(!job) - return FALSE - job.current_positions = max(0, job.current_positions - 1) diff --git a/tgstation.dme b/tgstation.dme index c9fb8f8f509..eaebc2a1a53 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -929,6 +929,7 @@ #include "code\game\machinery\cell_charger.dm" #include "code\game\machinery\civilian_bounties.dm" #include "code\game\machinery\constructable_frame.dm" +#include "code\game\machinery\cryopod.dm" #include "code\game\machinery\dance_machine.dm" #include "code\game\machinery\defibrillator_mount.dm" #include "code\game\machinery\deployable.dm" @@ -3747,8 +3748,6 @@ #include "modular_skyrat\modules\cryosleep\code\ai.dm" #include "modular_skyrat\modules\cryosleep\code\area.dm" #include "modular_skyrat\modules\cryosleep\code\config.dm" -#include "modular_skyrat\modules\cryosleep\code\cryopod.dm" -#include "modular_skyrat\modules\cryosleep\code\job.dm" #include "modular_skyrat\modules\cum\jerkoff_verb.dm" #include "modular_skyrat\modules\cum\seaofmen_keg.dm" #include "modular_skyrat\modules\cum\chemistry\reagents\cum_reagent.dm" diff --git a/tgui/packages/tgui/interfaces/CryopodConsole.js b/tgui/packages/tgui/interfaces/CryopodConsole.js new file mode 100644 index 00000000000..0c3038fae9b --- /dev/null +++ b/tgui/packages/tgui/interfaces/CryopodConsole.js @@ -0,0 +1,95 @@ +import { useBackend } from '../backend'; +import { Stack, Button, Section, NoticeBox, LabeledList, Collapsible } from '../components'; +import { Window } from '../layouts'; + +export const CryopodConsole = (props, context) => { + const { data } = useBackend(context); + const { account_name, allow_items } = data; + + const welcomeTitle = `Hello, ${account_name || '[REDACTED]'}!`; + + return ( + + + +

+ This automated cryogenic freezing unit will safely store your + corporeal form until your next assignment. +
+ + {!!allow_items && } + + + + ); +}; + +const CrewList = (props, context) => { + const { data } = useBackend(context); + const { frozen_crew } = data; + + return ( + + {!frozen_crew.length ? ( + No stored crew! + ) : ( +
+ + {frozen_crew.map((person) => ( + + {person.job} + + ))} + +
+ )} +
+ ); +}; + +const ItemList = (props, context) => { + const { act, data } = useBackend(context); + const { frozen_items } = data; + + const replaceItemName = (item) => { + let itemName = item.toString(); + if (itemName.startsWith('the')) { + itemName = itemName.slice(4, itemName.length); + } + return itemName.replace(/^\w/, (c) => c.toUpperCase()); + }; + + return ( + + {!frozen_items.length ? ( + No stored items! + ) : ( + <> +
+ + {frozen_items.map((item, index) => ( + act('one_item', { item: index + 1 })} + /> + } + /> + ))} + +
+