diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm index 1bc91b38464..a38e1b793ab 100644 --- a/code/__DEFINES/atom_hud.dm +++ b/code/__DEFINES/atom_hud.dm @@ -14,9 +14,9 @@ /// loyality implant #define IMPLOYAL_HUD "5" /// chemical implant -#define IMPCHEM_HUD "6" +#define IMPSEC_FIRST_HUD "6" /// tracking implant -#define IMPTRACK_HUD "7" +#define IMPSEC_SECOND_HUD "7" /// Silicon/Mech/Circuit Status #define DIAG_STAT_HUD "8" /// Silicon health bar diff --git a/code/__DEFINES/implants.dm b/code/__DEFINES/implants.dm new file mode 100644 index 00000000000..3693526e1f3 --- /dev/null +++ b/code/__DEFINES/implants.dm @@ -0,0 +1,5 @@ +///Denotes this as a "security" implant, limiting it to two per body +#define IMPLANT_TYPE_SECURITY (1<<0) + +///Maximum number of security implants in a mob at once. +#define SECURITY_IMPLANT_CAP 2 diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 8239dc29231..1d53a499322 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -50,10 +50,6 @@ GLOBAL_LIST_EMPTY(cooking_recipes_atoms) GLOBAL_LIST_EMPTY(rcd_list) /// list of wallmounted intercom radios. GLOBAL_LIST_EMPTY(intercoms_list) -/// list of all current implants that are tracked to work out what sort of trek everyone is on. Sadly not on lavaworld not implemented... -GLOBAL_LIST_EMPTY(tracked_implants) -/// list of implants the prisoner console can track and send inject commands too -GLOBAL_LIST_EMPTY(tracked_chem_implants) /// list of all pinpointers. Used to change stuff they are pointing to all at once. GLOBAL_LIST_EMPTY(pinpointer_list) /// A list of all zombie_infection organs, for any mass "animation" diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 043d9453908..62d19ccf9ab 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -47,7 +47,7 @@ hud_icons = list(ID_HUD) /datum/atom_hud/data/human/security/advanced - hud_icons = list(ID_HUD, IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD, WANTED_HUD, PERMIT_HUD, DNR_HUD) //SKYRAT EDIT ADDITION - PERMIT_HUD, DNR_HUD + hud_icons = list(ID_HUD, IMPSEC_FIRST_HUD, IMPLOYAL_HUD, IMPSEC_SECOND_HUD, WANTED_HUD, PERMIT_HUD, DNR_HUD) //SKYRAT EDIT ADDITION - PERMIT_HUD, DNR_HUD /datum/atom_hud/data/human/fan_hud hud_icons = list(FAN_HUD) @@ -287,25 +287,30 @@ Security HUDs! Basic mode shows only the job. /mob/living/proc/sec_hud_set_implants() var/image/holder - for(var/i in list(IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD)) + for(var/i in list(IMPSEC_FIRST_HUD, IMPLOYAL_HUD, IMPSEC_SECOND_HUD)) holder = hud_list[i] holder.icon_state = null set_hud_image_inactive(i) - for(var/obj/item/implant/I in implants) - if(istype(I, /obj/item/implant/tracking)) - holder = hud_list[IMPTRACK_HUD] - var/icon/IC = icon(icon, icon_state, dir) - holder.pixel_y = IC.Height() - world.icon_size - holder.icon_state = "hud_imp_tracking" - set_hud_image_active(IMPTRACK_HUD) - - else if(istype(I, /obj/item/implant/chem)) - holder = hud_list[IMPCHEM_HUD] - var/icon/IC = icon(icon, icon_state, dir) - holder.pixel_y = IC.Height() - world.icon_size - holder.icon_state = "hud_imp_chem" - set_hud_image_active(IMPCHEM_HUD) + var/security_slot = 1 //Which of the two security hud slots are we putting found security implants in? + for(var/obj/item/implant/current_implant in implants) + if(current_implant.implant_flags & IMPLANT_TYPE_SECURITY) + switch(security_slot) + if(1) + holder = hud_list[IMPSEC_FIRST_HUD] + var/icon/IC = icon(icon, icon_state, dir) + holder.pixel_y = IC.Height() - world.icon_size + holder.icon_state = current_implant.hud_icon_state + set_hud_image_active(IMPSEC_FIRST_HUD) + security_slot++ + + if(2) //Theoretically if we somehow get multiple sec implants, whatever the most recently implanted implant is will take over the 2nd position + holder = hud_list[IMPSEC_SECOND_HUD] + var/icon/IC = icon(icon, icon_state, dir) + holder.pixel_y = IC.Height() - world.icon_size + holder.pixel_x = initial(holder.pixel_x) + 7 //Adds an offset that mirrors the hud blip to the other side of the mob. + holder.icon_state = current_implant.hud_icon_state + set_hud_image_active(IMPSEC_SECOND_HUD) if(HAS_TRAIT(src, TRAIT_MINDSHIELD)) holder = hud_list[IMPLOYAL_HUD] diff --git a/code/game/machinery/computer/prisoner/_prisoner.dm b/code/game/machinery/computer/prisoner/_prisoner.dm index 970b5f4f9be..16369302c66 100644 --- a/code/game/machinery/computer/prisoner/_prisoner.dm +++ b/code/game/machinery/computer/prisoner/_prisoner.dm @@ -1,53 +1,59 @@ /obj/machinery/computer/prisoner interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE|INTERACT_MACHINE_REQUIRES_LITERACY - var/obj/item/card/id/advanced/prisoner/contained_id + /// ID card currently inserted into the computer. + VAR_FINAL/obj/item/card/id/advanced/prisoner/contained_id + +/obj/machinery/computer/prisoner/deconstruct(disassembled, mob/user) + contained_id?.forceMove(drop_location()) + return ..() /obj/machinery/computer/prisoner/Destroy() - if(contained_id) - contained_id.forceMove(get_turf(src)) + QDEL_NULL(contained_id) return ..() +/obj/machinery/computer/prisoner/Exited(atom/movable/gone, direction) + . = ..() + if(gone == contained_id) + contained_id = null /obj/machinery/computer/prisoner/examine(mob/user) . = ..() if(contained_id) . += span_notice("Alt-click to eject the ID card.") - - /obj/machinery/computer/prisoner/AltClick(mob/user) - id_eject(user) - return ..() + . = ..() + if(user.can_perform_action(src, ALLOW_SILICON_REACH)) + id_eject(user) -/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/P) - if(istype(P)) - if(contained_id) - to_chat(user, span_warning("There's already an ID card in the console!")) - return - if(!user.transferItemToLoc(P, src)) - return - contained_id = P - user.visible_message(span_notice("[user] inserts an ID card into the console."), \ - span_notice("You insert the ID card into the console.")) - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) - updateUsrDialog() +/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/new_id) + if(!istype(new_id)) + return + if(!isnull(contained_id)) + balloon_alert(user, "no empty slot!") + return + if(!user.transferItemToLoc(new_id, src)) + return + contained_id = new_id + balloon_alert_to_viewers("id inserted") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) /obj/machinery/computer/prisoner/proc/id_eject(mob/user) - if(!contained_id) - to_chat(user, span_warning("There's no ID card in the console!")) + if(isnull(contained_id)) + balloon_alert(user, "no id!") return + + if(!issilicon(user) && Adjacent(user)) + user.put_in_hands(contained_id) else contained_id.forceMove(drop_location()) - if(!issilicon(user) && Adjacent(user)) - user.put_in_hands(contained_id) - contained_id = null - user.visible_message(span_notice("[user] gets an ID card from the console."), \ - span_notice("You get the ID card from the console.")) - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) - updateUsrDialog() - -/obj/machinery/computer/prisoner/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/card/id/advanced/prisoner)) - id_insert(user, I) - else - return ..() + + balloon_alert_to_viewers("id ejected") + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + +/obj/machinery/computer/prisoner/attackby(obj/item/weapon, mob/user, params) + if(istype(weapon, /obj/item/card/id/advanced/prisoner)) + id_insert(user, weapon) + return TRUE + + return ..() diff --git a/code/game/machinery/computer/prisoner/management.dm b/code/game/machinery/computer/prisoner/management.dm index 7acac9b6610..ada71bad023 100644 --- a/code/game/machinery/computer/prisoner/management.dm +++ b/code/game/machinery/computer/prisoner/management.dm @@ -1,137 +1,97 @@ +/// List of all implants currently implanted into a mob +GLOBAL_LIST_EMPTY_TYPED(tracked_implants, /obj/item/implant) + /obj/machinery/computer/prisoner/management name = "prisoner management console" - desc = "Used to manage tracking implants placed inside criminals." + desc = "Used to modify prisoner IDs, as well as manage security implants placed inside convicts and parolees." icon_screen = "explosive" icon_keyboard = "security_key" req_access = list(ACCESS_BRIG) light_color = COLOR_SOFT_RED - var/id = 0 - var/temp = null - var/status = 0 - var/timeleft = 60 - var/stop = 0 - var/screen = 0 // 0 - No Access Denied, 1 - Access allowed circuit = /obj/item/circuitboard/computer/prisoner +/obj/machinery/computer/prisoner/management/ui_interact(mob/user, datum/tgui/ui) + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PrisonerManagement") + ui.open() + +/obj/machinery/computer/prisoner/management/ui_data(mob/user) + var/list/data = list() + + data["authorized"] = (authenticated && isliving(user)) || isAdminGhostAI(user) || issilicon(user) + data["inserted_id"] = null + if(!isnull(contained_id)) + data["inserted_id"] = list( + "name" = contained_id.name, + "points" = contained_id.points, + "goal" = contained_id.goal, + ) + + var/list/implants = list() + for(var/obj/item/implant/implant as anything in GLOB.tracked_implants) + if(!implant.is_shown_on_console(src)) + continue + var/list/implant_data = list() + implant_data["info"] = implant.get_management_console_data() + implant_data["buttons"] = implant.get_management_console_buttons() + implant_data["category"] = initial(implant.name) + implant_data["ref"] = REF(implant) + UNTYPED_LIST_ADD(implants, implant_data) + data["implants"] = implants -/obj/machinery/computer/prisoner/management/ui_interact(mob/user) + return data + +/obj/machinery/computer/prisoner/management/ui_act(action, list/params) . = ..() - if(isliving(user)) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) - var/dat = "" - if(screen == 0) - dat += "
{Log In}" - else if(screen == 1) - dat += "

Prisoner ID Management

" - if(contained_id) - dat += "[contained_id]
" - dat += "Collected Points: [contained_id.points]. Reset.
" - dat += "Card goal: [contained_id.goal]. Set
" - dat += "Space Law recommends quotas of 100 points per minute they would normally serve in the brig.
" - else - dat += "Insert Prisoner ID.
" - dat += "

Prisoner Implant Management

" - dat += "
Chemical Implants
" - var/turf/current_turf = get_turf(src) - for(var/obj/item/implant/chem/C in GLOB.tracked_chem_implants) - var/turf/implant_turf = get_turf(C) - if(!is_valid_z_level(current_turf, implant_turf)) - continue//Out of range - if(!C.imp_in) - continue - dat += "ID: [C.imp_in.name] | Remaining Units: [C.reagents.total_volume]
" - dat += "| Inject: " - dat += "((1))" - dat += "((5))" - dat += "((10))
" - dat += "********************************
" - dat += "
Tracking Implants
" - for(var/obj/item/implant/tracking/T in GLOB.tracked_implants) - if(!isliving(T.imp_in)) - continue - var/turf/implant_turf = get_turf(T) - if(!is_valid_z_level(current_turf, implant_turf)) - continue//Out of range - - var/loc_display = "Unknown" - var/mob/living/M = T.imp_in - if(is_station_level(implant_turf.z) && !isspaceturf(M.loc)) - var/turf/mob_loc = get_turf(M) - loc_display = mob_loc.loc - - dat += "ID: [T.imp_in.name] | Location: [loc_display]
" - dat += "(Message Holder) |
" - dat += "********************************
" - dat += "
{Log Out}" - var/datum/browser/popup = new(user, "computer", "Prisoner Management Console", 400, 500) - popup.set_content(dat) - popup.open() - return - -/obj/machinery/computer/prisoner/management/attackby(obj/item/I, mob/user, params) - if(isidcard(I)) - if(screen) - id_insert(user) - else - to_chat(user, span_danger("Unauthorized access.")) - else - return ..() - -/obj/machinery/computer/prisoner/management/process() - if(!..()) - src.updateDialog() - return - -/obj/machinery/computer/prisoner/management/Topic(href, href_list) - if(..()) + if(.) return - if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr)) - usr.set_machine(src) - - if(href_list["id"]) - if(href_list["id"] == "insert" && !contained_id) - id_insert(usr) - else if(contained_id) - switch(href_list["id"]) - if("eject") - id_eject(usr) - if("reset") - contained_id.points = 0 - if("setgoal") - var/num = tgui_input_text(usr, "Enter the prisoner's goal", "Prisoner Management", 1, 1000, 1) - if(isnull(num)) - return - contained_id.goal = round(num) - else if(href_list["inject1"]) - var/obj/item/implant/I = locate(href_list["inject1"]) in GLOB.tracked_chem_implants - if(I && istype(I)) - I.activate(1) - else if(href_list["inject5"]) - var/obj/item/implant/I = locate(href_list["inject5"]) in GLOB.tracked_chem_implants - if(I && istype(I)) - I.activate(5) - else if(href_list["inject10"]) - var/obj/item/implant/I = locate(href_list["inject10"]) in GLOB.tracked_chem_implants - if(I && istype(I)) - I.activate(10) - - else if(href_list["lock"]) + + if(!authenticated && action != "login") + CRASH("[usr] potentially spoofed ui action [action] on prisoner console without the console being logged in.") + + if(isliving(usr)) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) + + switch(action) + if("login") if(allowed(usr)) - screen = !screen + authenticated = TRUE playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE) else - to_chat(usr, span_danger("Unauthorized access.")) - - else if(href_list["warn"]) - var/warning = tgui_input_text(usr, "Enter your message here", "Messaging") - if(!warning) - return - var/obj/item/implant/I = locate(href_list["warn"]) in GLOB.tracked_implants - if(I && istype(I) && I.imp_in) - var/mob/living/R = I.imp_in - to_chat(R, span_hear("You hear a voice in your head saying: '[warning]'")) - log_directed_talk(usr, R, warning, LOG_SAY, "implant message") - - src.add_fingerprint(usr) - src.updateUsrDialog() - return + playsound(src, 'sound/machines/terminal_error.ogg', 50, FALSE) + return TRUE + + if("logout") + authenticated = FALSE + playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE) + return TRUE + + if("insert_id") + id_insert(usr, usr.get_active_held_item()) + return TRUE + + if("eject_id") + id_eject(usr) + return TRUE + + if("set_id_goal") + var/num = tgui_input_number(usr, "Enter the prisoner's goal", "Prisoner Management", 100, 1000, 1) + if(!isnum(num) || QDELETED(src) || QDELETED(contained_id) || QDELETED(usr)) + return TRUE + if(!is_operational || !usr.can_perform_action(src, NEED_DEXTERITY|ALLOW_SILICON_REACH)) + return TRUE + + contained_id.goal = num + return TRUE + + if("reset_id") + contained_id.points = 0 + return TRUE + + if("handle_implant") + var/obj/item/implant/affected_implant = locate(params["implant_ref"]) in GLOB.tracked_implants + if(affected_implant?.is_shown_on_console(src)) + affected_implant.handle_management_console_action(usr, params, src) + return TRUE diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm index dd8a051cc8e..0915d4d1d17 100644 --- a/code/game/machinery/computer/teleporter.dm +++ b/code/game/machinery/computer/teleporter.dm @@ -167,16 +167,16 @@ var/area/area = get_area(beacon) targets[avoid_assoc_duplicate_keys(format_text(area.name), area_index)] = beacon - for (var/obj/item/implant/tracking/tracking_implant in GLOB.tracked_implants) - if (!tracking_implant.imp_in || !isliving(tracking_implant.loc) || !tracking_implant.allow_teleport) + for (var/obj/item/implant/beacon/tracking_beacon in GLOB.tracked_implants) + if (isnull(tracking_beacon.imp_in) || !isliving(tracking_beacon.loc)) continue - var/mob/living/implanted = tracking_implant.loc - if (implanted.stat == DEAD && implanted.timeofdeath + tracking_implant.lifespan_postmortem < world.time) + var/mob/living/implanted = tracking_beacon.loc + if (implanted.stat == DEAD && implanted.timeofdeath + tracking_beacon.lifespan_postmortem < world.time) continue - if (is_eligible(tracking_implant)) - targets[avoid_assoc_duplicate_keys("[implanted.real_name] ([format_text(get_area(implanted))])", area_index)] = tracking_implant + if (is_eligible(tracking_beacon)) + targets[avoid_assoc_duplicate_keys("[implanted.real_name] ([format_text(get_area(implanted))])", area_index)] = tracking_beacon else for (var/obj/machinery/teleport/station/station as anything in power_station.linked_stations) if (is_eligible(station) && station.teleporter_hub) diff --git a/code/game/objects/items/implants/implant.dm b/code/game/objects/items/implants/implant.dm index 0a0c19aee96..cc64788e597 100644 --- a/code/game/objects/items/implants/implant.dm +++ b/code/game/objects/items/implants/implant.dm @@ -19,6 +19,10 @@ var/allow_multiple = FALSE ///how many times this can do something, only relevant for implants with limited uses var/uses = -1 + ///our implant flags + var/implant_flags = NONE + ///what icon state will we represent ourselves with on the hud? + var/hud_icon_state = null /obj/item/implant/proc/activate() @@ -60,12 +64,17 @@ if(!force && !can_be_implanted_in(target)) return FALSE - for(var/X in target.implants) - var/obj/item/implant/other_implant = X + var/security_implants = 0 //Used to track how many implants with the "security" flag are in the user. + for(var/obj/item/implant/other_implant as anything in target.implants) var/flags = SEND_SIGNAL(other_implant, COMSIG_IMPLANT_OTHER, args, src) if(flags & COMPONENT_STOP_IMPLANTING) UNSETEMPTY(target.implants) return FALSE + if(!force && (other_implant.implant_flags & IMPLANT_TYPE_SECURITY)) + security_implants++ + if(security_implants >= SECURITY_IMPLANT_CAP) //We've found too many security implants in this mob, and will reject implantation by normal means + balloon_alert(user, "too many security implants!") + return FALSE if(flags & COMPONENT_DELETE_NEW_IMPLANT) UNSETEMPTY(target.implants) qdel(src) @@ -100,6 +109,7 @@ log_combat(user, target, "implanted", "\a [name]") SEND_SIGNAL(src, COMSIG_IMPLANT_IMPLANTED, target, user, silent, force) + GLOB.tracked_implants += src return TRUE /** @@ -122,12 +132,14 @@ human_source.sec_hud_set_implants() SEND_SIGNAL(src, COMSIG_IMPLANT_REMOVED, source, silent, special) + GLOB.tracked_implants -= src return TRUE /obj/item/implant/Destroy() if(imp_in) removed(imp_in) return ..() + /** * Gets implant specifications for the implant pad */ @@ -137,3 +149,68 @@ /obj/item/implant/dropped(mob/user) . = TRUE ..() + +/// Determines if the implant is visible on the implant management console. +/// Note that this would only ever be called on implants currently inserted into a mob. +/obj/item/implant/proc/is_shown_on_console(obj/machinery/computer/prisoner/management/console) + return FALSE + +/** + * Returns a list of information to show on the implant management console for this implant + * + * Unlike normal UI data, the keys of the list are shown on the UI itself, so they should be human readable. + */ +/obj/item/implant/proc/get_management_console_data() + RETURN_TYPE(/list) + + var/list/info_shown = list() + info_shown["ID"] = imp_in.name + return info_shown + +/** + * Returns a list of "structs" that translate into buttons displayed on the implant management console + * + * The struct should have the following keys: + * * name - the name of the button, optional if button_icon is set + * * icon - the icon of the button, optional if button_name is set + * * color - the color of the button, optional + * * tooltip - the tooltip of the button, optional + * * action_key - the key that will be passed to handle_management_console_action when the button is clicked + * * action_params - optional, additional params passed when the button is clicked + */ +/obj/item/implant/proc/get_management_console_buttons() + SHOULD_CALL_PARENT(TRUE) + RETURN_TYPE(/list) + + var/list/buttons = list() + UNTYPED_LIST_ADD(buttons, list( + "name" = "Self Destruct", + "color" = "bad", + "tooltip" = "Destoys the implant from within the user harmlessly.", + "action_key" = "self_destruct", + )) + return buttons + +/** + * Handles a button click on the implant management console + * + * * user - the mob clicking the button + * * params - the params passed to the button, as if this were a ui_act handler. + * See params["implant_action"] for the action key passed to the button + * (which should correspond to a button returned by get_management_console_buttons) + * * console - the console the button was clicked on + */ +/obj/item/implant/proc/handle_management_console_action(mob/user, list/params, obj/machinery/computer/prisoner/management/console) + SHOULD_CALL_PARENT(TRUE) + + if(params["implant_action"] == "self_destruct") + var/warning = tgui_alert(user, "Activation will harmlessly self-destruct this implant. Proceed?", "You sure?", list("Yes", "No")) + if(warning != "Yes" || QDELETED(src) || QDELETED(user) || QDELETED(console) || isnull(imp_in)) + return TRUE + if(!console.is_operational || !user.can_perform_action(console, NEED_DEXTERITY|ALLOW_SILICON_REACH)) + return TRUE + + to_chat(imp_in, span_hear("You feel a tiny jolt from inside of you as one of your implants fizzles out.")) + do_sparks(number = 2, cardinal_only = FALSE, source = imp_in) + deconstruct() + return TRUE diff --git a/code/game/objects/items/implants/implant_track.dm b/code/game/objects/items/implants/implant_track.dm deleted file mode 100644 index 65f11c2519b..00000000000 --- a/code/game/objects/items/implants/implant_track.dm +++ /dev/null @@ -1,59 +0,0 @@ -/obj/item/implant/tracking - name = "tracking implant" - desc = "Track with this." - actions_types = null - - ///for how many deciseconds after user death will the implant work? - var/lifespan_postmortem = 6000 - ///will people implanted with this act as teleporter beacons? - var/allow_teleport = TRUE - -/obj/item/implant/tracking/c38 - name = "TRAC implant" - desc = "A smaller tracking implant that supplies power for only a few minutes." - var/lifespan = 3000 //how many deciseconds does the implant last? - ///The id of the timer that's qdeleting us - var/timerid - allow_teleport = FALSE - -/obj/item/implant/tracking/c38/implant(mob/living/target, mob/user, silent, force) - . = ..() - timerid = QDEL_IN_STOPPABLE(src, lifespan) - -/obj/item/implant/tracking/c38/removed(mob/living/source, silent, special) - . = ..() - deltimer(timerid) - timerid = null - -/obj/item/implant/tracking/c38/Destroy() - return ..() - -/obj/item/implant/tracking/Initialize(mapload) - . = ..() - GLOB.tracked_implants += src - -/obj/item/implant/tracking/Destroy() - GLOB.tracked_implants -= src - return ..() - -/obj/item/implanter/tracking - imp_type = /obj/item/implant/tracking - -/obj/item/implanter/tracking/gps - imp_type = /obj/item/gps/mining/internal - -/obj/item/implant/tracking/get_data() - var/dat = {"Implant Specifications:
- Name: Tracking Beacon
- Life: 10 minutes after death of host.
- Important Notes: Implant [allow_teleport ? "also works" : "does not work"] as a teleporter beacon.
-
- Implant Details:
- Function: Continuously transmits low power signal. Useful for tracking.
- Special Features:
- Neuro-Safe- Specialized shell absorbs excess voltages self-destructing the chip if - a malfunction occurs thereby securing safety of subject. The implant will melt and - disintegrate into bio-safe elements.
- Integrity: Gradient creates slight risk of being overcharged and frying the - circuitry. As a result neurotoxins can cause massive damage."} - return dat diff --git a/code/game/objects/items/implants/security/implant_beacon.dm b/code/game/objects/items/implants/security/implant_beacon.dm new file mode 100644 index 00000000000..dea6b4ed190 --- /dev/null +++ b/code/game/objects/items/implants/security/implant_beacon.dm @@ -0,0 +1,43 @@ +///Essentially, just turns the implantee into a teleport beacon. +/obj/item/implant/beacon + name = "beacon implant" + desc = "Teleports things." + actions_types = null + implant_flags = IMPLANT_TYPE_SECURITY + hud_icon_state = "hud_imp_beacon" + ///How long will the implant be teleportable to after death? + var/lifespan_postmortem = 10 MINUTES + +/obj/item/implant/beacon/get_data() + var/dat = {"Implant Specifications:
+ Name: Robust Corp JMP-21 Fugitive Retrieval Implant
+ Life: Deactivates upon death after ten minutes, but remains within the body.
+ Important Notes: N/A
+
+ Implant Details:
+ Function: Acts as a teleportation beacon that can be tracked by any standard bluespace transponder. + Using this, you can teleport directly to whoever has this implant inside of them."} + return dat + +/obj/item/implant/beacon/is_shown_on_console(obj/machinery/computer/prisoner/management/console) + return TRUE + +/obj/item/implant/beacon/get_management_console_data() + var/list/info_shown = ..() + + var/area/destination_area = get_area(imp_in) + if(isnull(destination_area) || (destination_area.area_flags & NOTELEPORT)) + info_shown["Status"] = "Implant carrier teleport signal cannot be reached!" + else + var/turf/turf_to_check = get_turf(imp_in) + info_shown["Status"] = "Implant carrier is in [is_safe_turf(turf_to_check, dense_atoms = TRUE) ? "a safe environment." : "a hazardous environment!"]" + + return info_shown + +/obj/item/implanter/beacon + imp_type = /obj/item/implant/beacon + +/obj/item/implantcase/beacon + name = "implant case - 'Beacon'" + desc = "A glass case containing a beacon implant." + imp_type = /obj/item/implant/beacon diff --git a/code/game/objects/items/implants/implant_chem.dm b/code/game/objects/items/implants/security/implant_chem.dm similarity index 62% rename from code/game/objects/items/implants/implant_chem.dm rename to code/game/objects/items/implants/security/implant_chem.dm index adcba5641cf..db41d8bcac2 100644 --- a/code/game/objects/items/implants/implant_chem.dm +++ b/code/game/objects/items/implants/security/implant_chem.dm @@ -3,6 +3,10 @@ desc = "Injects things." icon_state = "reagents" actions_types = null + implant_flags = IMPLANT_TYPE_SECURITY + hud_icon_state = "hud_imp_chem" + /// All possible injection sizes for the implant shown in the prisoner management console. + var/list/implant_sizes = list(1, 5, 10) /obj/item/implant/chem/get_data() var/dat = {"Implant Specifications:
@@ -10,24 +14,51 @@ Life: Deactivates upon death but remains within the body.
Important Notes: Due to the system functioning off of nutrients in the implanted subject's body, the subject
will suffer from an increased appetite.

-
Implant Details:
- Function: Contains a small capsule that can contain various chemicals. Upon receiving a specially encoded signal
+ Function: Contains a small capsule that can contain various chemicals. Upon receiving a specially encoded signal
the implant releases the chemicals directly into the blood stream.
- Special Features: Micro-Capsule- Can be loaded with any sort of chemical agent via the common syringe and can hold 50 units.
Can only be loaded while still in its original case.
- Integrity: Implant will last so long as the subject is alive."} + Integrity: Implant will last so long as the subject is alive, breaking down and releasing all contents on death."} return dat +/obj/item/implant/chem/is_shown_on_console(obj/machinery/computer/prisoner/management/console) + return is_valid_z_level(get_turf(console), get_turf(imp_in)) + +/obj/item/implant/chem/get_management_console_data() + var/list/info_shown = ..() + info_shown["Volume"] = "[reagents.total_volume]u" + return info_shown + +/obj/item/implant/chem/get_management_console_buttons() + var/list/buttons = ..() + for(var/i in implant_sizes) + UNTYPED_LIST_ADD(buttons, list( + "name" = "Inject [i]u", + "color" = "good", + "action_key" = "inject", + "action_params" = list("amount" = i), + )) + return buttons + +/obj/item/implant/chem/handle_management_console_action(mob/user, list/params, obj/machinery/computer/prisoner/management/console) + . = ..() + if(.) + return + + if(params["implant_action"] == "inject") + var/amount = text2num(params["amount"]) + if(!(amount in implant_sizes)) + return TRUE + + var/reagents_inside = reagents.get_reagent_log_string() + activate(amount) + log_combat(user, imp_in, "injected [amount] units of [reagents_inside]", src) + return TRUE + /obj/item/implant/chem/Initialize(mapload) . = ..() create_reagents(50, OPENCONTAINER) - GLOB.tracked_chem_implants += src - -/obj/item/implant/chem/Destroy() - GLOB.tracked_chem_implants -= src - return ..() /obj/item/implant/chem/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE) . = ..() diff --git a/code/game/objects/items/implants/implant_exile.dm b/code/game/objects/items/implants/security/implant_exile.dm similarity index 93% rename from code/game/objects/items/implants/implant_exile.dm rename to code/game/objects/items/implants/security/implant_exile.dm index 056ccd0ff9a..5c33d146b3a 100644 --- a/code/game/objects/items/implants/implant_exile.dm +++ b/code/game/objects/items/implants/security/implant_exile.dm @@ -5,11 +5,14 @@ name = "exile implant" desc = "Prevents you from returning from away missions." actions_types = null + implant_flags = IMPLANT_TYPE_SECURITY + hud_icon_state = "hud_imp_exile" /obj/item/implant/exile/get_data() var/dat = {"Implant Specifications:
Name: Nanotrasen Employee Exile Implant
- Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant.
"} + Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant. + Additionally, station mining shuttles will lock their controls if handled by someone with this implant.
"} return dat diff --git a/code/game/objects/items/implants/security/implant_noteleport.dm b/code/game/objects/items/implants/security/implant_noteleport.dm new file mode 100644 index 00000000000..be4ec29659a --- /dev/null +++ b/code/game/objects/items/implants/security/implant_noteleport.dm @@ -0,0 +1,45 @@ +///Blocks the implantee from being teleported +/obj/item/implant/teleport_blocker + name = "bluespace grounding implant" + desc = "Grounds your bluespace signature in baseline reality, whatever the hell that means." + actions_types = null + implant_flags = IMPLANT_TYPE_SECURITY + hud_icon_state = "hud_imp_noteleport" + +/obj/item/implant/teleport_blocker/get_data() + var/dat = {"Implant Specifications:
+ Name: Robust Corp EXP-001 'Bluespace Grounder'
+ Implant Details: Upon implantation, grounds the user's bluespace signature to their currently occupied plane of existence. + Most, if not all forms of teleportation on the implantee will be rendered ineffective. Useful for keeping especially slippery prisoners in place.
"} + return dat + +/obj/item/implant/teleport_blocker/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE) + . = ..() + if(!. || !isliving(target)) + return FALSE + RegisterSignal(target, COMSIG_MOVABLE_TELEPORTING, PROC_REF(on_teleport)) + return TRUE + +/obj/item/implant/teleport_blocker/removed(mob/target, silent = FALSE, special = FALSE) + . = ..() + if(!. || !isliving(target)) + return FALSE + UnregisterSignal(target, COMSIG_MOVABLE_TELEPORTING) + return TRUE + +/// Signal for COMSIG_MOVABLE_TELEPORTED that blocks teleports and stuns the would-be-teleportee. +/obj/item/implant/teleport_blocker/proc/on_teleport(mob/living/teleportee, atom/destination, channel) + SIGNAL_HANDLER + + to_chat(teleportee, span_holoparasite("You feel yourself teleporting, but are suddenly flung back to where you just were!")) + + teleportee.apply_status_effect(/datum/status_effect/incapacitating/paralyzed, 5 SECONDS) + var/datum/effect_system/spark_spread/quantum/spark_system = new() + spark_system.set_up(5, TRUE, teleportee) + spark_system.start() + return COMPONENT_BLOCK_TELEPORT + +/obj/item/implantcase/teleport_blocker + name = "implant case - 'Bluespace Grounding'" + desc = "A glass case containing a bluespace grounding implant." + imp_type = /obj/item/implant/teleport_blocker diff --git a/code/game/objects/items/implants/security/implant_track.dm b/code/game/objects/items/implants/security/implant_track.dm new file mode 100644 index 00000000000..652d9d6507a --- /dev/null +++ b/code/game/objects/items/implants/security/implant_track.dm @@ -0,0 +1,87 @@ +/obj/item/implant/tracking + name = "tracking implant" + desc = "Track with this." + actions_types = null + implant_flags = IMPLANT_TYPE_SECURITY + hud_icon_state = "hud_imp_tracking" + + ///How long will the implant continue to function after death? + var/lifespan_postmortem = 10 MINUTES + +/obj/item/implant/tracking/get_data() + var/dat = {"Implant Specifications:
+ Name: Robust Corp EYE-5 Convict Parole Implant
+ Life: 10 minutes after death of host.
+
+ Implant Details:
+ Function: Continuously transmits low power signal. Can be tracked from a prisoner management console.
+ Special Features:
+ Neuro-Safe- Specialized shell absorbs excess voltages self-destructing the chip if + a malfunction occurs thereby securing safety of subject. The implant will melt and + disintegrate into bio-safe elements.
"} + return dat + +/obj/item/implant/tracking/is_shown_on_console(obj/machinery/computer/prisoner/management/console) + if(imp_in.stat == DEAD && imp_in.timeofdeath + lifespan_postmortem < world.time) + return FALSE + if(!is_valid_z_level(get_turf(console), get_turf(imp_in))) + return FALSE + return TRUE + +/obj/item/implant/tracking/get_management_console_data() + var/list/info_shown = ..() + info_shown["Location"] = get_area_name(imp_in, format_text = TRUE) || "Unknown" + return info_shown + +/obj/item/implant/tracking/get_management_console_buttons() + var/list/buttons = ..() + UNTYPED_LIST_ADD(buttons, list( + "name" = "Warn", + "color" = "average", + "tooltip" = "Sends a message directly to the target's brain.", + "action_key" = "warn", + )) + return buttons + +/obj/item/implant/tracking/handle_management_console_action(mob/user, list/params, obj/machinery/computer/prisoner/management/console) + . = ..() + if(.) + return + + if(params["implant_action"] == "warn") + var/warning = tgui_input_text(user, "What warning do you want to send to [imp_in.name]?", "Messaging") + if(!warning || QDELETED(src) || QDELETED(user) || QDELETED(console) || isnull(imp_in)) + return TRUE + if(!console.is_operational || !user.can_perform_action(console, NEED_DEXTERITY|ALLOW_SILICON_REACH)) + return TRUE + + to_chat(imp_in, span_hear("You hear a voice in your head saying: '[warning]'")) + log_directed_talk(user, imp_in, warning, LOG_SAY, "implant message") + return TRUE + +/obj/item/implant/tracking/c38 + name = "TRAC implant" + desc = "A smaller tracking implant that supplies power for only a few minutes." + implant_flags = NONE + ///How long before this implant self-deletes? + var/lifespan = 5 MINUTES + ///The id of the timer that's qdeleting us + var/timerid + +/obj/item/implant/tracking/c38/implant(mob/living/target, mob/user, silent, force) + . = ..() + timerid = QDEL_IN_STOPPABLE(src, lifespan) + +/obj/item/implant/tracking/c38/removed(mob/living/source, silent, special) + . = ..() + deltimer(timerid) + timerid = null + +/obj/item/implant/tracking/c38/Destroy() + return ..() + +/obj/item/implanter/tracking + imp_type = /obj/item/implant/tracking + +/obj/item/implanter/tracking/gps + imp_type = /obj/item/gps/mining/internal diff --git a/code/game/objects/items/storage/boxes/implant_boxes.dm b/code/game/objects/items/storage/boxes/implant_boxes.dm new file mode 100644 index 00000000000..1da12ba3285 --- /dev/null +++ b/code/game/objects/items/storage/boxes/implant_boxes.dm @@ -0,0 +1,82 @@ +/obj/item/storage/box/trackimp + name = "boxed tracking implant kit" + desc = "Box full of scum-bag tracking utensils." + icon_state = "secbox" + illustration = "implant" + +/obj/item/storage/box/trackimp/PopulateContents() + var/static/items_inside = list( + /obj/item/implantcase/tracking = 4, + /obj/item/implanter = 1, + /obj/item/implantpad = 1, + /obj/item/locator = 1, + ) + generate_items_inside(items_inside,src) + +/obj/item/storage/box/minertracker + name = "boxed tracking implant kit" + desc = "For finding those who have died on the accursed lavaworld." + illustration = "implant" + +/obj/item/storage/box/minertracker/PopulateContents() + var/static/items_inside = list( + /obj/item/implantcase/tracking = 2, + /obj/item/implantcase/beacon = 2, + /obj/item/implanter = 1, + /obj/item/implantpad = 1, + /obj/item/locator = 1, + ) + generate_items_inside(items_inside,src) + +/obj/item/storage/box/chemimp + name = "boxed chemical implant kit" + desc = "Box of stuff used to implant chemicals." + illustration = "implant" + +/obj/item/storage/box/chemimp/PopulateContents() + var/static/items_inside = list( + /obj/item/implantcase/chem = 5, + /obj/item/implanter = 1, + /obj/item/implantpad = 1, + ) + generate_items_inside(items_inside,src) + +/obj/item/storage/box/exileimp + name = "boxed exile implant kit" + desc = "Box of exile implants. It has a picture of a clown being booted through the Gateway." + illustration = "implant" + +/obj/item/storage/box/exileimp/PopulateContents() + var/static/items_inside = list( + /obj/item/implantcase/exile = 5, + /obj/item/implanter = 1, + /obj/item/implantpad = 1, + ) + generate_items_inside(items_inside,src) + +/obj/item/storage/box/beaconimp + name = "boxed beacon implant kit" + desc = "Contains a set of beacon implants. There's a warning label on the side warning to always check the safety of your teleport destination, \ + accompanied by a cheery drawing of a security officer saying 'look before you leap!'" + illustration = "implant" + +/obj/item/storage/box/beaconimp/PopulateContents() + var/static/items_inside = list( + /obj/item/implantcase/beacon = 3, + /obj/item/implanter = 1, + /obj/item/implantpad = 1, + ) + generate_items_inside(items_inside,src) + +/obj/item/storage/box/teleport_blocker + name = "boxed bluespace grounding implant kit" + desc = "Box of bluespace grounding implants. There's a drawing on the side -- A figure wearing some intimidating robes, frowning and shedding a single tear." + illustration = "implant" + +/obj/item/storage/box/teleport_blocker/PopulateContents() + var/static/items_inside = list( + /obj/item/implantcase/teleport_blocker = 2, + /obj/item/implanter = 1, + /obj/item/implantpad = 1, + ) + generate_items_inside(items_inside,src) diff --git a/code/game/objects/items/storage/boxes/security_boxes.dm b/code/game/objects/items/storage/boxes/security_boxes.dm index 9c401f99907..8e55986fb40 100644 --- a/code/game/objects/items/storage/boxes/security_boxes.dm +++ b/code/game/objects/items/storage/boxes/security_boxes.dm @@ -69,60 +69,6 @@ for(var/i in 1 to 5) new /obj/item/grenade/empgrenade(src) -/obj/item/storage/box/trackimp - name = "boxed tracking implant kit" - desc = "Box full of scum-bag tracking utensils." - icon_state = "secbox" - illustration = "implant" - -/obj/item/storage/box/trackimp/PopulateContents() - var/static/items_inside = list( - /obj/item/implantcase/tracking = 4, - /obj/item/implanter = 1, - /obj/item/implantpad = 1, - /obj/item/locator = 1, - ) - generate_items_inside(items_inside,src) - -/obj/item/storage/box/minertracker - name = "boxed tracking implant kit" - desc = "For finding those who have died on the accursed lavaworld." - illustration = "implant" - -/obj/item/storage/box/minertracker/PopulateContents() - var/static/items_inside = list( - /obj/item/implantcase/tracking = 3, - /obj/item/implanter = 1, - /obj/item/implantpad = 1, - /obj/item/locator = 1, - ) - generate_items_inside(items_inside,src) - -/obj/item/storage/box/chemimp - name = "boxed chemical implant kit" - desc = "Box of stuff used to implant chemicals." - illustration = "implant" - -/obj/item/storage/box/chemimp/PopulateContents() - var/static/items_inside = list( - /obj/item/implantcase/chem = 5, - /obj/item/implanter = 1, - /obj/item/implantpad = 1, - ) - generate_items_inside(items_inside,src) - -/obj/item/storage/box/exileimp - name = "boxed exile implant kit" - desc = "Box of exile implants. It has a picture of a clown being booted through the Gateway." - illustration = "implant" - -/obj/item/storage/box/exileimp/PopulateContents() - var/static/items_inside = list( - /obj/item/implantcase/exile = 5, - /obj/item/implanter = 1, - ) - generate_items_inside(items_inside,src) - /obj/item/storage/box/prisoner name = "box of prisoner IDs" desc = "Take away their last shred of dignity, their name." diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm index 1711110dd7b..5317afe4a78 100644 --- a/code/game/objects/items/teleportation.dm +++ b/code/game/objects/items/teleportation.dm @@ -24,7 +24,7 @@ throw_speed = 3 throw_range = 7 custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 4) - var/tracking_range = 20 + var/tracking_range = 35 /obj/item/locator/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) @@ -72,22 +72,22 @@ var/list/track_implants = list() - for (var/obj/item/implant/tracking/W in GLOB.tracked_implants) - if (!W.imp_in || !isliving(W.loc)) + for (var/obj/item/implant/beacon/tracking_beacon in GLOB.tracked_implants) + if (!tracking_beacon.imp_in || !isliving(tracking_beacon.loc)) continue else - var/mob/living/M = W.loc - if (M.stat == DEAD) - if (M.timeofdeath + W.lifespan_postmortem < world.time) + var/mob/living/living_mob = tracking_beacon.loc + if (living_mob.stat == DEAD) + if (living_mob.timeofdeath + tracking_beacon.lifespan_postmortem < world.time) continue - var/turf/tr = get_turf(W) + var/turf/tr = get_turf(tracking_beacon) var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) if(distance > tracking_range) continue var/D = dir2text(get_dir(sr, tr)) - track_implants += list(list(name = W.imp_in.name, direction = D, distance = distance)) + track_implants += list(list(name = tracking_beacon.imp_in.name, direction = D, distance = distance)) data["trackimplants"] = track_implants return data diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm index 42a6c03544a..04129eb08a5 100644 --- a/code/modules/cargo/packs/security.dm +++ b/code/modules/cargo/packs/security.dm @@ -340,3 +340,17 @@ access_view = ACCESS_SECURITY contains = list(/obj/item/clothing/glasses/sunglasses = 1) crate_name = "sunglasses crate" + +/datum/supply_pack/security/armory/beacon_imp + name = "Beacon Implants Crate" + desc = "Contains five Beacon implants." + cost = CARGO_CRATE_VALUE * 5.5 + contains = list(/obj/item/storage/box/beaconimp) + crate_name = "beacon implant crate" + +/datum/supply_pack/security/armory/teleport_blocker_imp + name = "Bluespace Grounding Implants Crate" + desc = "Contains five Bluespace Grounding implants." + cost = CARGO_CRATE_VALUE * 7 + contains = list(/obj/item/storage/box/teleport_blocker) + crate_name = "bluespace grounding implant crate" diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index d265ea0f463..f1a661e6a48 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -103,6 +103,13 @@ if (HAS_TRAIT(user, TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION) && !is_station_level(user.z)) to_chat(user, span_warning("You get the feeling you shouldn't mess with this.")) return + + if(isliving(user)) + var/mob/living/living_user = user + for(var/obj/item/implant/exile/exile_implant in living_user.implants) + to_chat(living_user, span_warning("A warning flashes across the screen, and the shuttle controls lock in response to your exile implant.")) + return + return ..() /obj/machinery/computer/shuttle/mining/common diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index fcdc37fdbaa..c0a1836dcfb 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -5,7 +5,7 @@ icon = 'icons/mob/human/human.dmi' icon_state = "human_basic" appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE - hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD,PERMIT_HUD, DNR_HUD) //SKYRAT EDIT ADDITION - PERMIT_HUD, DNR_HUD + hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPSEC_FIRST_HUD,IMPSEC_SECOND_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD,PERMIT_HUD, DNR_HUD) //SKYRAT EDIT ADDITION - PERMIT_HUD, DNR_HUD hud_type = /datum/hud/human pressure_resistance = 25 can_buckle = TRUE diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm index e7870537805..b604c5f8eac 100644 --- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm +++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm @@ -42,7 +42,7 @@ /obj/item/ammo_box/c38/trac name = "speed loader (.38 TRAC)" - desc = "Designed to quickly reload revolvers. TRAC bullets embed a tracking implant within the target's body. The implant's signal is incompatible with teleporters." + desc = "Designed to quickly reload revolvers. TRAC bullets embed a tracking implant within the target's body." ammo_type = /obj/item/ammo_casing/c38/trac ammo_band_color = "#7b6383" diff --git a/icons/mob/huds/hud.dmi b/icons/mob/huds/hud.dmi index d71ba4b0940..18527378473 100644 Binary files a/icons/mob/huds/hud.dmi and b/icons/mob/huds/hud.dmi differ diff --git a/tgstation.dme b/tgstation.dme index b5f3fc0f289..0e2aaa172c1 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -109,6 +109,7 @@ #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\icon_smoothing.dm" #include "code\__DEFINES\id_cards.dm" +#include "code\__DEFINES\implants.dm" #include "code\__DEFINES\important_recursive_contents.dm" #include "code\__DEFINES\injection.dm" #include "code\__DEFINES\input.dm" @@ -2506,10 +2507,8 @@ #include "code\game\objects\items\grenades\syndieminibomb.dm" #include "code\game\objects\items\implants\implant.dm" #include "code\game\objects\items\implants\implant_abductor.dm" -#include "code\game\objects\items\implants\implant_chem.dm" #include "code\game\objects\items\implants\implant_clown.dm" #include "code\game\objects\items\implants\implant_deathrattle.dm" -#include "code\game\objects\items\implants\implant_exile.dm" #include "code\game\objects\items\implants\implant_explosive.dm" #include "code\game\objects\items\implants\implant_freedom.dm" #include "code\game\objects\items\implants\implant_krav_maga.dm" @@ -2518,12 +2517,16 @@ #include "code\game\objects\items\implants\implant_spell.dm" #include "code\game\objects\items\implants\implant_stealth.dm" #include "code\game\objects\items\implants\implant_storage.dm" -#include "code\game\objects\items\implants\implant_track.dm" #include "code\game\objects\items\implants\implantcase.dm" #include "code\game\objects\items\implants\implantchair.dm" #include "code\game\objects\items\implants\implanter.dm" #include "code\game\objects\items\implants\implantpad.dm" #include "code\game\objects\items\implants\implantuplink.dm" +#include "code\game\objects\items\implants\security\implant_beacon.dm" +#include "code\game\objects\items\implants\security\implant_chem.dm" +#include "code\game\objects\items\implants\security\implant_exile.dm" +#include "code\game\objects\items\implants\security\implant_noteleport.dm" +#include "code\game\objects\items\implants\security\implant_track.dm" #include "code\game\objects\items\kirby_plants\kirbyplants.dm" #include "code\game\objects\items\kirby_plants\organic_plants.dm" #include "code\game\objects\items\kirby_plants\synthetic_plants.dm" @@ -2596,6 +2599,7 @@ #include "code\game\objects\items\storage\boxes\clothes_boxes.dm" #include "code\game\objects\items\storage\boxes\engineering_boxes.dm" #include "code\game\objects\items\storage\boxes\food_boxes.dm" +#include "code\game\objects\items\storage\boxes\implant_boxes.dm" #include "code\game\objects\items\storage\boxes\job_boxes.dm" #include "code\game\objects\items\storage\boxes\medical_boxes.dm" #include "code\game\objects\items\storage\boxes\science_boxes.dm" diff --git a/tgui/packages/tgui/interfaces/PrisonerManagement.tsx b/tgui/packages/tgui/interfaces/PrisonerManagement.tsx new file mode 100644 index 00000000000..73c7ceadc93 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PrisonerManagement.tsx @@ -0,0 +1,272 @@ +import { useBackend, useSharedState } from '../backend'; +import { BooleanLike } from 'common/react'; +import { + Box, + Button, + Icon, + LabeledList, + NoticeBox, + Section, + Stack, + Tabs, +} from '../components'; +import { Window } from '../layouts'; + +type byondRef = string; + +type IDInfo = { + name: string; + points: number; + goal: number; +}; + +type DMButton = { + name?: string; + icon?: string; + color?: string; + tooltip?: string; + action_key: string; + action_params: Record; +}; + +type ImplantInfo = { + info: Record; + buttons: DMButton[]; + category: string; + ref: byondRef; +}; + +type Data = { + authorized: BooleanLike; + inserted_id: IDInfo | null; + implants: ImplantInfo[]; +}; + +const ImplantDisplay = (props: { implant: ImplantInfo }) => { + const { act } = useBackend(); + const { info, buttons, ref } = props.implant; + + return ( + + + + {Object.entries(info).map(([key, value]) => ( + + {value} + + ))} + {buttons.length !== 0 && ( + + {buttons.map((button) => ( + + ))} + + )} + + + + + ); +}; + +// When given a list of implants, sorts them by category +const sortImplants = (implants: ImplantInfo[]) => { + const implantsByCategory: Record = implants.reduce( + (acc, implant) => { + if (implant.category in acc) { + acc[implant.category].push(implant); + } else { + acc[implant.category] = [implant]; + } + return acc; + }, + {}, + ); + + return implantsByCategory; +}; + +// Converts a category ("tracking implant") to a more readable format ("Tracking") +// Does this by capitalizing the first letter of the first word and removing the rest +const formatCategory = (category: string) => { + const firstWord = category.split(' ')[0]; + return firstWord.charAt(0).toUpperCase() + firstWord.slice(1); +}; + +const AllImplantDisplay = (props: { implants: ImplantInfo[] }) => { + const implantsByCategory: Record = sortImplants( + props.implants, + ); + + const [implantTab, setImplantTab] = useSharedState( + 'implantTab', + Object.keys(implantsByCategory)[0], + ); + + return ( + + + + {Object.entries(implantsByCategory).map(([category, implants]) => ( + setImplantTab(category)} + > + {formatCategory(category)} + + ))} + + + + {implantTab && implantsByCategory && implantsByCategory[implantTab] ? ( + implantsByCategory[implantTab].map((implant) => ( + + )) + ) : ( + No implants detected. + )} + + + ); +}; + +const IdShowcase = (props: { id: IDInfo | null }) => { + const { act } = useBackend(); + const { id } = props; + + return ( + + + + {id ? ( + <> + + + + )} + + + {!!id && ( + + + Space Law recommends quotas of 100 points per minute they would + normally serve in the brig. + + + )} + + ); +}; + +const ManagementConsole = () => { + const { act, data } = useBackend(); + + return ( + + +
+ +
+
+ +
+ +
+
+ + + Secure Your Workspace. + + + +
+ ); +}; + +// I copied this from security records, +// should probably make this a generic component +const LogIn = () => { + const { act } = useBackend(); + + return ( +
+ + + + + + + + Nanotrasen SecurityHUB + + + + + You are not logged in. + + + + +
+ ); +}; + +export const PrisonerManagement = () => { + const { data } = useBackend(); + const { authorized } = data; + return ( + + + {authorized ? : } + + + ); +};