From ec9be3f0cb52f19911e91f8e99a7aa429705667f Mon Sep 17 00:00:00 2001 From: Lucy Date: Thu, 7 Dec 2023 15:55:33 -0500 Subject: [PATCH] Force-cryo refactor + improvements (#10235) * Force-cryo refactor + improvements * Address reviews. * Only use on-station pods * fix comment * forgot to fix this comment too * Fix player panel layout * Address reviews --- code/__HELPERS/game.dm | 17 +++- code/modules/admin/admin.dm | 2 + code/modules/admin/player_panel.dm | 4 + code/modules/admin/smites/forcecryo.dm | 15 +++- code/modules/admin/sql_ban_system.dm | 16 +++- code/modules/admin/topic.dm | 23 +++++ code/modules/admin/verbs/forcecryo.dm | 90 ++++++++++++++++++-- tgui/packages/tgui/interfaces/PlayerPanel.js | 5 +- 8 files changed, 150 insertions(+), 22 deletions(-) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index aa46493ddc753..3be00b32151a3 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -339,11 +339,22 @@ break /proc/get_mob_by_ckey(key) - var/ckey = ckey(key) //just to be safe + var/mob_ckey = ckey(key) //just to be safe + if(!mob_ckey) + return for(var/mob/M as() in GLOB.player_list) - if(M?.ckey == ckey) + if(M?.ckey == mob_ckey) return M - return null + +/proc/get_ckey_last_living(key, healthy = FALSE) + var/mob_ckey = ckey(key) //just to be safe + if(!mob_ckey) + return + for(var/mob/living/potential_target as() in GLOB.mob_living_list) + if(QDELETED(potential_target) || (healthy && potential_target.stat)) + continue + if(potential_target.ckey == mob_ckey || (!length(potential_target.ckey) && ckey(potential_target.mind?.key) == mob_ckey)) + return potential_target /proc/considered_alive(datum/mind/M, enforce_human = TRUE) if(M?.current) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 08b334555c40b..162f719fbb8e3 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -105,6 +105,8 @@ body += "Ban " body += "Notes" + if(isliving(M)) + body += " Force Cryo " if(M.client) body += " Prison " body += " Send to Lobby" diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 3cf74af1c77eb..8fd0cb957f441 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -296,6 +296,10 @@ if(!check_rights(R_ADMIN)) return holder.Topic(null, list("sendbacktolobby" = REF(target_mob), "admin_token" = holder.href_token)) + if("force_cryo") + if(!check_rights(R_ADMIN)) + return + holder.Topic(null, list("force_cryo" = isliving(target_mob) ? REF(target_mob) : target_ckey, "admin_token" = holder.href_token)) /datum/admins/proc/open_player_panel() diff --git a/code/modules/admin/smites/forcecryo.dm b/code/modules/admin/smites/forcecryo.dm index 9b4f04a33998b..fff82f0e3a680 100644 --- a/code/modules/admin/smites/forcecryo.dm +++ b/code/modules/admin/smites/forcecryo.dm @@ -1,7 +1,14 @@ /// Cryo's the target, opening up their job slot in the lobby -/datum/smite/forcecryo - name = "Force Cryo" +/datum/smite/force_cryo_pod + name = "Force Cryo (using centcom pod)" -/datum/smite/forcecryo/effect(client/user, mob/living/target) +/datum/smite/force_cryo_pod/effect(client/user, mob/living/target) . = ..() - forcecryo(target) + force_cryo(target) + +/datum/smite/force_cryo_instant + name = "Force Cryo (instant)" + +/datum/smite/force_cryo_instant/effect(client/user, mob/living/target) + . = ..() + instant_force_cryo(target) diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index 08fb99319c1f4..51b68e3dd4276 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -143,7 +143,7 @@ C.ban_cache[query_build_ban_cache.item[1]] = TRUE qdel(query_build_ban_cache) -/datum/admins/proc/ban_panel(player_key, player_ip, player_cid, role, duration = 1440, applies_to_admins, reason, edit_id, page, admin_key, global_ban = TRUE) +/datum/admins/proc/ban_panel(player_key, player_ip, player_cid, role, duration = 1440, applies_to_admins, reason, edit_id, page, admin_key, global_ban = TRUE, force_cryo_after = FALSE) var/suppressor if(check_rights(R_SUPPRESS, FALSE)) suppressor = TRUE @@ -181,7 +181,10 @@
+
@@ -399,6 +402,7 @@ var/page var/admin_key var/redact + var/force_cryo_after = FALSE var/list/changes = list() var/list/roles_to_ban = list() if(href_list["redactioncheck"]) @@ -438,6 +442,8 @@ if(redact) error_state += "Admin bans can not be suppressed." applies_to_admins = TRUE + if(href_list["forcecryo"]) + force_cryo_after = TRUE switch(href_list["radioservban"]) if("local") if(CONFIG_GET(flag/disable_local_bans)) @@ -514,9 +520,9 @@ if(edit_id) edit_ban(edit_id, player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, reason, global_ban, mirror_edit, old_key, old_ip, old_cid, old_applies, old_globalban, page, admin_key, changes) else - create_ban(player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, severity, reason, global_ban, roles_to_ban, redact) + create_ban(player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, severity, reason, global_ban, roles_to_ban, redact, force_cryo_after) -/datum/admins/proc/create_ban(player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, severity, reason, global_ban, list/roles_to_ban, redact = 0) +/datum/admins/proc/create_ban(player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, severity, reason, global_ban, list/roles_to_ban, redact = FALSE, force_cryo_after = FALSE) if(!check_rights(R_BAN)) return if(!SSdbcore.Connect()) @@ -686,6 +692,8 @@ is_admin = TRUE if(roles_to_ban[1] == "Server" && (!is_admin || (is_admin && applies_to_admins))) qdel(i) + if(force_cryo_after) + force_cryo_ckey(player_ckey) /datum/admins/proc/unban_panel(player_key, admin_key, player_ip, player_cid, page = 0) if(!check_rights(R_BAN)) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 04b7a43267539..9d69115188bd5 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1926,6 +1926,29 @@ return paper_to_show.ui_interact(usr) + else if(href_list["force_cryo"]) + if(!check_rights(R_ADMIN)) + return + var/param = href_list["force_cryo"] + // If it's a direct reference to a mob, use that. + var/mob/living/target = locate(param) + if(!istype(target)) + // Might be a ckey? Let's see. + target = get_ckey_last_living(target) + if(!istype(target)) + to_chat(usr, "This can only be used on instances of type /mob/living.") + return + var/method = tgui_alert(usr, "Select force-cryo method", "Cryo Express", list("Centcom Pod (recommended)", "Instant", "Cancel")) + switch(method) + if("Centcom Pod (recommended)") + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(force_cryo), target) + if("Instant") + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(instant_force_cryo), target) + else + return + message_admins("[key_name_admin(usr)] force-cryoed [ADMIN_LOOKUPFLW(target)]].") + log_admin("[key_name(usr)] force-cryoed [key_name(target)]].") + /datum/admins/proc/HandleCMode() if(!check_rights(R_ADMIN)) return diff --git a/code/modules/admin/verbs/forcecryo.dm b/code/modules/admin/verbs/forcecryo.dm index e36dced0e98f6..497d642628785 100644 --- a/code/modules/admin/verbs/forcecryo.dm +++ b/code/modules/admin/verbs/forcecryo.dm @@ -1,9 +1,81 @@ -/proc/forcecryo(mob/target) - var/turf/T = get_turf(target) - new /obj/effect/temp_visual/tornado(T) - sleep(20) - for(var/obj/machinery/cryopod/C in GLOB.machines) - if(!C.occupant) - C.close_machine(target) - C.despawn_occupant() - break +/proc/force_cryo_ckey(target_ckey, instant = FALSE) + var/mob/living/target = get_ckey_last_living(target_ckey, healthy = TRUE) + if(!target) + return + var/method = instant ? GLOBAL_PROC_REF(instant_force_cryo) : GLOBAL_PROC_REF(force_cryo) + INVOKE_ASYNC(GLOBAL_PROC, method, target) + +/proc/force_cryo(mob/living/target) + if(!istype(target)) + return + var/obj/machinery/cryopod/pod_loc = target.loc + if(istype(pod_loc) && pod_loc.occupant == target) + pod_loc.despawn_occupant() + return + var/turf/target_turf = get_turf(target) + target.ghostize(can_reenter_corpse = FALSE) + // unbuckle them from everything and release them from any pulls + target.unbuckle_all_mobs(force = TRUE) + target.stop_pulling() + target.pulledby?.stop_pulling() + target.buckled?.unbuckle_mob(target, force = TRUE) + // ensure that they don't move / get moved and cause any weirdness + target.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + target.Stun(INFINITY, ignore_canstun = TRUE) + target.move_resist = INFINITY + target.anchored = TRUE + target.status_flags |= GODMODE + // ensure they're on a turf + target.forceMove(target_turf) + // send a fancy centcom pod, so nobody ICly questions this + var/obj/structure/closet/supplypod/force_cryo/cryo_express = new + cryo_express.target = target + new /obj/effect/pod_landingzone(target_turf, cryo_express) + +/proc/instant_force_cryo(mob/living/target) + if(!istype(target)) + return + // unbuckle them from everything, and release them from any pulls + target.pulledby?.stop_pulling() + target.buckled?.unbuckle_mob(target, force = TRUE) + for(var/obj/machinery/cryopod/pod in GLOB.machines) + if(!is_station_level(pod.z) || !QDELETED(pod.occupant) || pod.panel_open) + continue + pod.close_machine(target) + pod.despawn_occupant() + return + message_admins("Failed to force-cryo [ADMIN_LOOKUPFLW(target)] (no valid cryopods)") + log_admin("Failed to force-cryo [key_name(target)] (no valid cryopods)") + +/obj/structure/closet/supplypod/force_cryo + name = "\improper CentCom employee retrieval pod" + desc = "A pod used by Central Command to retrieve certain employees from the station for long-term cryogenic storage." + style = STYLE_CENTCOM + bluespace = TRUE + reversing = TRUE + specialised = TRUE + explosionSize = list(0, 0, 0, 0) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + reverse_delays = list(POD_TRANSIT = 0.5 SECONDS, POD_FALLING = 0.5 SECONDS, POD_OPENING = 0.5 SECONDS, POD_LEAVING = 1.5 SECONDS) + var/mob/living/target + +/obj/structure/closet/supplypod/force_cryo/insert(mob/living/to_insert, atom/movable/holder) + if(!insertion_allowed(to_insert)) + return FALSE + // make SURE they aren't buckled or being pulled. + to_insert.pulledby?.stop_pulling() + to_insert.buckled?.unbuckle_mob(target, force = TRUE) + to_insert.forceMove(holder) + return TRUE + +/obj/structure/closet/supplypod/force_cryo/insertion_allowed(atom/to_insert) + return to_insert == target + +/obj/structure/closet/supplypod/force_cryo/preOpen() + // if we're going back to centcom, now we just cryo them + if(!reversing && !QDELETED(target)) + target.moveToNullspace() + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(instant_force_cryo), target) + qdel(src) + return + return ..() diff --git a/tgui/packages/tgui/interfaces/PlayerPanel.js b/tgui/packages/tgui/interfaces/PlayerPanel.js index 547e74f35b8bf..4f06d2cb3594c 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel.js @@ -46,7 +46,7 @@ const LOG_TYPES_REVERSE = { const LOG_TYPES_LIST = Object.keys(LOG_TYPES_REVERSE); -const PANEL_HEIGHT = 260; +const PANEL_HEIGHT = 300; /** -------------------- @@ -88,7 +88,7 @@ export const PlayerPanel = (_, context) => { return ( @@ -441,6 +441,7 @@ class PlayerDetailsActionButtons extends PureComponent { 'Ban': 'open_ban', 'Smite': 'smite', 'Prison': 'jail', + 'Cryo': 'force_cryo', }, }; if (!has_mind) {