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 00000000000..e8c07222e19 Binary files /dev/null and b/icons/obj/machines/cryopod.dmi differ diff --git a/modular_skyrat/modules/cryosleep/code/cryopod.dm b/modular_skyrat/modules/cryosleep/code/cryopod.dm deleted file mode 100644 index 0aab8fb8424..00000000000 --- a/modular_skyrat/modules/cryosleep/code/cryopod.dm +++ /dev/null @@ -1,493 +0,0 @@ -/client - var/cryo_warned = 0 - -/* - * 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 - */ - -//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' - 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/menu = 1 //Which menu screen to display - - //Used for logging people entering cryosleep and important items they are carrying. - var/list/frozen_crew = list() - var/list/frozen_items = list() - - // Used for containing rare items traitors need to steal, so it's not - // game-over if they get iced - var/list/objective_items = list() - // A cache of theft datums so you don't have to re-create them for - // each item check - var/list/theft_cache = list() - - var/allow_items = TRUE - -/obj/machinery/computer/cryopod/attack_ai() - attack_hand() - -/obj/machinery/computer/cryopod/ui_interact(mob/user = usr) - . = ..() - user.set_machine(src) - add_fingerprint(user) - - var/dat = "" - - dat += "

Welcome, [user.real_name].


" - dat += "

" - - switch(src.menu) - if(1) - dat += "View crew storage log

" - if(allow_items) - dat += "View objects storage log

" - dat += "Recover object

" - dat += "Recover all objects
" - if(2) - dat += "<< Back

" - dat += "

Recently stored Crew




" - if(!frozen_crew.len) - dat += "There has been no storage usage at this terminal.
" - else - for(var/person in frozen_crew) - dat += "[person]
" - dat += "
" - if(3) - dat += "<< Back

" - dat += "

Recently stored objects




" - if(!frozen_items.len) - dat += "There has been no storage usage at this terminal.
" - else - for(var/obj/item/I in frozen_items) - dat += "[I.name]
" - dat += "
" - - var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control") - popup.set_content(dat) - popup.open() - -/obj/machinery/computer/cryopod/Topic(href, href_list) - if(..()) - return TRUE - - var/mob/user = usr - - add_fingerprint(user) - - if(href_list["item"]) - 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) - 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 })} + /> + } + /> + ))} + +
+