mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-14 03:32:00 +00:00
"Security Implant" rework, prisoner management console updates (#79882)
## About The Pull Request For the vernacular purposes of the following PR body -- "Security Implant" refers to the existing subset of implants given, by security, to captured prisoners and such as a punitive, controlling measure. This includes the chemical, tracking, and maybe exile implants. This revamps the functionality of how "security" implants are displayed on huds, prisoner management console implant controls/readouts, and their instrumentality. It was also, ultimately, an attempt at nerfing the tracking implant that spiralled far out of control. Rather than only displaying chemical on the right and tracking on the left, all implants with the "security implant" flag will be trackable on SecHuds. A maximum of two can be implanted at once. This is both due to technical limitations, but also conveniently provides security a limit to consider when choosing implants. Implants now also occupy their HUD slot based on the order they were implanted in, rather than always occupying the same spot. Neat!  From two (three if you count the exile implant), there are now five security implants. _The tracker implant has been split into two of these implants._ <details> <summary>Summary of the implants, functions, changes:</summary> <br> - **Tracker (Red)** -- No longer grants teleporter beacon. Tracking radius has been increased from 20 to 35 tiles. The Prisoner Management Console will now list the area the prisoner is occupying as well. Disables after the implantee is dead for 10 minutes. - **Chemical (Blue)** -- No mechanical changes. The implant pad readout has been modified slightly. - **Exile (Green)** -- In addition to past functionality, station shuttle controls (public, mining, etc.) will be unresponsive for the implantee. Flimsy, but more effective than a stern warning not to come back from lavaland. - **Beacon (Yellow)** -- Implantee becomes a teleporter beacon. The prisoner console will report if their currently occupied area is hazardous or not, so half of the security team doesn't blindly teleport into space or lava. Disables after the implantee is dead for 10 minutes. Available from Cargo. - **Teleport Blocker (Deep Blue, not shown)** -- Prevents the implantee from being teleported. Ever wanted to keep a wizard or cultist in a cell? This is where you can start. Available from Cargo, expensive and scarce. Each of the implants has some application that would benefit security if used on a captured criminal. Their usefulness may overlap in some places, but the overall range of control these implants give security is broadened. </details> The implant control console has also been given a small facelift. Certain implants provide more useful readouts that can help officers locate, control, or capture an implantee, rewarding cooperation between officers. It has also been totally converted into TGUI by @MrMelbert. Kickass! Also, You can now remotely destroy implants, either to relieve criminals from their punishment or to make room for a different implant. Wardens should keep hold of their ID and remember to log out, since a motivated convict could use it to shed their implants!  Everything made in this PR _should_ be scaleable enough to allow for new security implant types to be implemented with relative ease. The teleport-blocker implant was a last minute attempt to prove it to myself. I had a few more ideas for implants in my head, but figured this PR was already getting big and ugly enough. That is all for another day. I truly apologize if there's anything I've missed in here. I did a lot of this over a long period of time and kind of just... sat on it for a while. If there's any confusing our unexplained changes, feel free to point them out and I'll try to give an explanation. ## Why It's Good For The Game The goal of this PR is to give a bit more depth to security's armory implants. The intent is to present a choice in what implants are given (rather than just tracker and maybe chem if you're feeling spiteful), and to make them more useful as punitive/monitoring tools. The tracker implant needed a nerf (and probably still does regardless of this PR's success). It's never used for tracking since the teleporter beacon is much more direct (+ gives a virtually free attack opportunity), and the tracking range was incredibly subpar. I'd rather not take toys away from security, but having the best option not be roundstart gear feels like a fair compromise. Warden content. Wardens have more gear to budget for and use at their own (or the HOSes) discretion. The changes to the prisoner console allow them to coordinate with officers to get good value out of the implants they've chosen for an implantee. Gives antagonists an alternate way to get de-implanted, without external help, that can only be granted at the fault of security. Wardens who dish out implants must keep an eye on the people carrying them! ## Changelog 🆑 Rhials, MrMelbert add: The Tracker implant has had its teleport beacon functionality migrated to the new (cargo accessible) Beacon implant. add: Teleport Blocker security implant, that prevents the implantee from teleporting by any means. Purchasable from cargo. add: Security implants may now be harmlessly self-destructed at the Prisoner Management Console. balance: The Tracker implant tracking radius has increased from 20 to 35 tiles. The Prisoner Management Console will track and display the area the implantee is in as well. balance: The exile implant now prevents implantees from operating shuttle controls. code: Various code improvements and removal of unused vars in the Prisoner Management Console code: The HUD slots for chem/tracking implants have been converted to display any implant with the IMPLANT_TYPE_SECURITY flag and an associated sprite. spellcheck: Modifies various implant pad readouts, removing false information and rewriting some sections. /🆑 --------- Co-authored-by: MrMelbert <kmelbert4@gmail.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
This commit is contained in:
@@ -14,9 +14,9 @@
|
|||||||
/// loyality implant
|
/// loyality implant
|
||||||
#define IMPLOYAL_HUD "5"
|
#define IMPLOYAL_HUD "5"
|
||||||
/// chemical implant
|
/// chemical implant
|
||||||
#define IMPCHEM_HUD "6"
|
#define IMPSEC_FIRST_HUD "6"
|
||||||
/// tracking implant
|
/// tracking implant
|
||||||
#define IMPTRACK_HUD "7"
|
#define IMPSEC_SECOND_HUD "7"
|
||||||
/// Silicon/Mech/Circuit Status
|
/// Silicon/Mech/Circuit Status
|
||||||
#define DIAG_STAT_HUD "8"
|
#define DIAG_STAT_HUD "8"
|
||||||
/// Silicon health bar
|
/// Silicon health bar
|
||||||
|
|||||||
5
code/__DEFINES/implants.dm
Normal file
5
code/__DEFINES/implants.dm
Normal file
@@ -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
|
||||||
@@ -50,10 +50,6 @@ GLOBAL_LIST_EMPTY(cooking_recipes_atoms)
|
|||||||
GLOBAL_LIST_EMPTY(rcd_list)
|
GLOBAL_LIST_EMPTY(rcd_list)
|
||||||
/// list of wallmounted intercom radios.
|
/// list of wallmounted intercom radios.
|
||||||
GLOBAL_LIST_EMPTY(intercoms_list)
|
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.
|
/// list of all pinpointers. Used to change stuff they are pointing to all at once.
|
||||||
GLOBAL_LIST_EMPTY(pinpointer_list)
|
GLOBAL_LIST_EMPTY(pinpointer_list)
|
||||||
/// A list of all zombie_infection organs, for any mass "animation"
|
/// A list of all zombie_infection organs, for any mass "animation"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
hud_icons = list(ID_HUD)
|
hud_icons = list(ID_HUD)
|
||||||
|
|
||||||
/datum/atom_hud/data/human/security/advanced
|
/datum/atom_hud/data/human/security/advanced
|
||||||
hud_icons = list(ID_HUD, IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD, WANTED_HUD)
|
hud_icons = list(ID_HUD, IMPSEC_FIRST_HUD, IMPLOYAL_HUD, IMPSEC_SECOND_HUD, WANTED_HUD)
|
||||||
|
|
||||||
/datum/atom_hud/data/human/fan_hud
|
/datum/atom_hud/data/human/fan_hud
|
||||||
hud_icons = list(FAN_HUD)
|
hud_icons = list(FAN_HUD)
|
||||||
@@ -279,25 +279,30 @@ Security HUDs! Basic mode shows only the job.
|
|||||||
|
|
||||||
/mob/living/proc/sec_hud_set_implants()
|
/mob/living/proc/sec_hud_set_implants()
|
||||||
var/image/holder
|
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 = hud_list[i]
|
||||||
holder.icon_state = null
|
holder.icon_state = null
|
||||||
set_hud_image_inactive(i)
|
set_hud_image_inactive(i)
|
||||||
|
|
||||||
for(var/obj/item/implant/I in implants)
|
var/security_slot = 1 //Which of the two security hud slots are we putting found security implants in?
|
||||||
if(istype(I, /obj/item/implant/tracking))
|
for(var/obj/item/implant/current_implant in implants)
|
||||||
holder = hud_list[IMPTRACK_HUD]
|
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)
|
var/icon/IC = icon(icon, icon_state, dir)
|
||||||
holder.pixel_y = IC.Height() - world.icon_size
|
holder.pixel_y = IC.Height() - world.icon_size
|
||||||
holder.icon_state = "hud_imp_tracking"
|
holder.icon_state = current_implant.hud_icon_state
|
||||||
set_hud_image_active(IMPTRACK_HUD)
|
set_hud_image_active(IMPSEC_FIRST_HUD)
|
||||||
|
security_slot++
|
||||||
|
|
||||||
else if(istype(I, /obj/item/implant/chem))
|
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[IMPCHEM_HUD]
|
holder = hud_list[IMPSEC_SECOND_HUD]
|
||||||
var/icon/IC = icon(icon, icon_state, dir)
|
var/icon/IC = icon(icon, icon_state, dir)
|
||||||
holder.pixel_y = IC.Height() - world.icon_size
|
holder.pixel_y = IC.Height() - world.icon_size
|
||||||
holder.icon_state = "hud_imp_chem"
|
holder.pixel_x = initial(holder.pixel_x) + 7 //Adds an offset that mirrors the hud blip to the other side of the mob.
|
||||||
set_hud_image_active(IMPCHEM_HUD)
|
holder.icon_state = current_implant.hud_icon_state
|
||||||
|
set_hud_image_active(IMPSEC_SECOND_HUD)
|
||||||
|
|
||||||
if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
|
if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
|
||||||
holder = hud_list[IMPLOYAL_HUD]
|
holder = hud_list[IMPLOYAL_HUD]
|
||||||
|
|||||||
@@ -1,53 +1,59 @@
|
|||||||
/obj/machinery/computer/prisoner
|
/obj/machinery/computer/prisoner
|
||||||
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE|INTERACT_MACHINE_REQUIRES_LITERACY
|
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/Destroy()
|
/obj/machinery/computer/prisoner/deconstruct(disassembled, mob/user)
|
||||||
if(contained_id)
|
contained_id?.forceMove(drop_location())
|
||||||
contained_id.forceMove(get_turf(src))
|
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
|
/obj/machinery/computer/prisoner/Destroy()
|
||||||
|
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)
|
/obj/machinery/computer/prisoner/examine(mob/user)
|
||||||
. = ..()
|
. = ..()
|
||||||
if(contained_id)
|
if(contained_id)
|
||||||
. += span_notice("<b>Alt-click</b> to eject the ID card.")
|
. += span_notice("<b>Alt-click</b> to eject the ID card.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/computer/prisoner/AltClick(mob/user)
|
/obj/machinery/computer/prisoner/AltClick(mob/user)
|
||||||
|
. = ..()
|
||||||
|
if(user.can_perform_action(src, ALLOW_SILICON_REACH))
|
||||||
id_eject(user)
|
id_eject(user)
|
||||||
return ..()
|
|
||||||
|
|
||||||
/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/P)
|
/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/new_id)
|
||||||
if(istype(P))
|
if(!istype(new_id))
|
||||||
if(contained_id)
|
|
||||||
to_chat(user, span_warning("There's already an ID card in the console!"))
|
|
||||||
return
|
return
|
||||||
if(!user.transferItemToLoc(P, src))
|
if(!isnull(contained_id))
|
||||||
|
balloon_alert(user, "no empty slot!")
|
||||||
return
|
return
|
||||||
contained_id = P
|
if(!user.transferItemToLoc(new_id, src))
|
||||||
user.visible_message(span_notice("[user] inserts an ID card into the console."), \
|
return
|
||||||
span_notice("You insert the ID card into the console."))
|
contained_id = new_id
|
||||||
|
balloon_alert_to_viewers("id inserted")
|
||||||
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
|
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
|
||||||
updateUsrDialog()
|
|
||||||
|
|
||||||
/obj/machinery/computer/prisoner/proc/id_eject(mob/user)
|
/obj/machinery/computer/prisoner/proc/id_eject(mob/user)
|
||||||
if(!contained_id)
|
if(isnull(contained_id))
|
||||||
to_chat(user, span_warning("There's no ID card in the console!"))
|
balloon_alert(user, "no id!")
|
||||||
return
|
return
|
||||||
else
|
|
||||||
contained_id.forceMove(drop_location())
|
|
||||||
if(!issilicon(user) && Adjacent(user))
|
if(!issilicon(user) && Adjacent(user))
|
||||||
user.put_in_hands(contained_id)
|
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
|
else
|
||||||
|
contained_id.forceMove(drop_location())
|
||||||
|
|
||||||
|
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 ..()
|
return ..()
|
||||||
|
|||||||
@@ -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
|
/obj/machinery/computer/prisoner/management
|
||||||
name = "prisoner management console"
|
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_screen = "explosive"
|
||||||
icon_keyboard = "security_key"
|
icon_keyboard = "security_key"
|
||||||
req_access = list(ACCESS_BRIG)
|
req_access = list(ACCESS_BRIG)
|
||||||
light_color = COLOR_SOFT_RED
|
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
|
circuit = /obj/item/circuitboard/computer/prisoner
|
||||||
|
|
||||||
|
/obj/machinery/computer/prisoner/management/ui_interact(mob/user, datum/tgui/ui)
|
||||||
/obj/machinery/computer/prisoner/management/ui_interact(mob/user)
|
|
||||||
. = ..()
|
. = ..()
|
||||||
if(isliving(user))
|
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
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
/obj/machinery/computer/prisoner/management/ui_act(action, list/params)
|
||||||
|
. = ..()
|
||||||
|
if(.)
|
||||||
|
return
|
||||||
|
|
||||||
|
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)
|
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||||
var/dat = ""
|
|
||||||
if(screen == 0)
|
|
||||||
dat += "<HR><A href='?src=[REF(src)];lock=1'>{Log In}</A>"
|
|
||||||
else if(screen == 1)
|
|
||||||
dat += "<H3>Prisoner ID Management</H3>"
|
|
||||||
if(contained_id)
|
|
||||||
dat += "<A href='?src=[REF(src)];id=eject'>[contained_id]</A><br>"
|
|
||||||
dat += "Collected Points: [contained_id.points]. <A href='?src=[REF(src)];id=reset'>Reset.</A><br>"
|
|
||||||
dat += "Card goal: [contained_id.goal]. <A href='?src=[REF(src)];id=setgoal'>Set </A><br>"
|
|
||||||
dat += "Space Law recommends quotas of 100 points per minute they would normally serve in the brig.<BR>"
|
|
||||||
else
|
|
||||||
dat += "<A href='?src=[REF(src)];id=insert'>Insert Prisoner ID.</A><br>"
|
|
||||||
dat += "<H3>Prisoner Implant Management</H3>"
|
|
||||||
dat += "<HR>Chemical Implants<BR>"
|
|
||||||
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] <BR>"
|
|
||||||
dat += "| Inject: "
|
|
||||||
dat += "<A href='?src=[REF(src)];inject1=[REF(C)]'>(<font class='bad'>(1)</font>)</A>"
|
|
||||||
dat += "<A href='?src=[REF(src)];inject5=[REF(C)]'>(<font class='bad'>(5)</font>)</A>"
|
|
||||||
dat += "<A href='?src=[REF(src)];inject10=[REF(C)]'>(<font class='bad'>(10)</font>)</A><BR>"
|
|
||||||
dat += "********************************<BR>"
|
|
||||||
dat += "<HR>Tracking Implants<BR>"
|
|
||||||
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"
|
switch(action)
|
||||||
var/mob/living/M = T.imp_in
|
if("login")
|
||||||
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]<BR>"
|
|
||||||
dat += "<A href='?src=[REF(src)];warn=[REF(T)]'>(<font class='bad'><i>Message Holder</i></font>)</A> |<BR>"
|
|
||||||
dat += "********************************<BR>"
|
|
||||||
dat += "<HR><A href='?src=[REF(src)];lock=1'>{Log Out}</A>"
|
|
||||||
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(..())
|
|
||||||
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(allowed(usr))
|
if(allowed(usr))
|
||||||
screen = !screen
|
authenticated = TRUE
|
||||||
playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
|
playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
|
||||||
else
|
else
|
||||||
to_chat(usr, span_danger("Unauthorized access."))
|
playsound(src, 'sound/machines/terminal_error.ogg', 50, FALSE)
|
||||||
|
return TRUE
|
||||||
|
|
||||||
else if(href_list["warn"])
|
if("logout")
|
||||||
var/warning = tgui_input_text(usr, "Enter your message here", "Messaging")
|
authenticated = FALSE
|
||||||
if(!warning)
|
playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
|
||||||
return
|
return TRUE
|
||||||
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)
|
if("insert_id")
|
||||||
src.updateUsrDialog()
|
id_insert(usr, usr.get_active_held_item())
|
||||||
return
|
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
|
||||||
|
|||||||
@@ -167,16 +167,16 @@
|
|||||||
var/area/area = get_area(beacon)
|
var/area/area = get_area(beacon)
|
||||||
targets[avoid_assoc_duplicate_keys(format_text(area.name), area_index)] = 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)
|
for (var/obj/item/implant/beacon/tracking_beacon in GLOB.tracked_implants)
|
||||||
if (!tracking_implant.imp_in || !isliving(tracking_implant.loc) || !tracking_implant.allow_teleport)
|
if (isnull(tracking_beacon.imp_in) || !isliving(tracking_beacon.loc))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var/mob/living/implanted = tracking_implant.loc
|
var/mob/living/implanted = tracking_beacon.loc
|
||||||
if (implanted.stat == DEAD && implanted.timeofdeath + tracking_implant.lifespan_postmortem < world.time)
|
if (implanted.stat == DEAD && implanted.timeofdeath + tracking_beacon.lifespan_postmortem < world.time)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (is_eligible(tracking_implant))
|
if (is_eligible(tracking_beacon))
|
||||||
targets[avoid_assoc_duplicate_keys("[implanted.real_name] ([format_text(get_area(implanted))])", area_index)] = tracking_implant
|
targets[avoid_assoc_duplicate_keys("[implanted.real_name] ([format_text(get_area(implanted))])", area_index)] = tracking_beacon
|
||||||
else
|
else
|
||||||
for (var/obj/machinery/teleport/station/station as anything in power_station.linked_stations)
|
for (var/obj/machinery/teleport/station/station as anything in power_station.linked_stations)
|
||||||
if (is_eligible(station) && station.teleporter_hub)
|
if (is_eligible(station) && station.teleporter_hub)
|
||||||
|
|||||||
@@ -19,6 +19,10 @@
|
|||||||
var/allow_multiple = FALSE
|
var/allow_multiple = FALSE
|
||||||
///how many times this can do something, only relevant for implants with limited uses
|
///how many times this can do something, only relevant for implants with limited uses
|
||||||
var/uses = -1
|
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()
|
/obj/item/implant/proc/activate()
|
||||||
@@ -60,12 +64,17 @@
|
|||||||
if(!force && !can_be_implanted_in(target))
|
if(!force && !can_be_implanted_in(target))
|
||||||
return FALSE
|
return FALSE
|
||||||
|
|
||||||
for(var/X in target.implants)
|
var/security_implants = 0 //Used to track how many implants with the "security" flag are in the user.
|
||||||
var/obj/item/implant/other_implant = X
|
for(var/obj/item/implant/other_implant as anything in target.implants)
|
||||||
var/flags = SEND_SIGNAL(other_implant, COMSIG_IMPLANT_OTHER, args, src)
|
var/flags = SEND_SIGNAL(other_implant, COMSIG_IMPLANT_OTHER, args, src)
|
||||||
if(flags & COMPONENT_STOP_IMPLANTING)
|
if(flags & COMPONENT_STOP_IMPLANTING)
|
||||||
UNSETEMPTY(target.implants)
|
UNSETEMPTY(target.implants)
|
||||||
return FALSE
|
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)
|
if(flags & COMPONENT_DELETE_NEW_IMPLANT)
|
||||||
UNSETEMPTY(target.implants)
|
UNSETEMPTY(target.implants)
|
||||||
qdel(src)
|
qdel(src)
|
||||||
@@ -100,6 +109,7 @@
|
|||||||
log_combat(user, target, "implanted", "\a [name]")
|
log_combat(user, target, "implanted", "\a [name]")
|
||||||
|
|
||||||
SEND_SIGNAL(src, COMSIG_IMPLANT_IMPLANTED, target, user, silent, force)
|
SEND_SIGNAL(src, COMSIG_IMPLANT_IMPLANTED, target, user, silent, force)
|
||||||
|
GLOB.tracked_implants += src
|
||||||
return TRUE
|
return TRUE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,12 +132,14 @@
|
|||||||
human_source.sec_hud_set_implants()
|
human_source.sec_hud_set_implants()
|
||||||
|
|
||||||
SEND_SIGNAL(src, COMSIG_IMPLANT_REMOVED, source, silent, special)
|
SEND_SIGNAL(src, COMSIG_IMPLANT_REMOVED, source, silent, special)
|
||||||
|
GLOB.tracked_implants -= src
|
||||||
return TRUE
|
return TRUE
|
||||||
|
|
||||||
/obj/item/implant/Destroy()
|
/obj/item/implant/Destroy()
|
||||||
if(imp_in)
|
if(imp_in)
|
||||||
removed(imp_in)
|
removed(imp_in)
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets implant specifications for the implant pad
|
* Gets implant specifications for the implant pad
|
||||||
*/
|
*/
|
||||||
@@ -137,3 +149,68 @@
|
|||||||
/obj/item/implant/dropped(mob/user)
|
/obj/item/implant/dropped(mob/user)
|
||||||
. = TRUE
|
. = 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
|
||||||
|
|||||||
@@ -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 = {"<b>Implant Specifications:</b><BR>
|
|
||||||
<b>Name:</b> Tracking Beacon<BR>
|
|
||||||
<b>Life:</b> 10 minutes after death of host.<BR>
|
|
||||||
<b>Important Notes:</b> Implant [allow_teleport ? "also works" : "does not work"] as a teleporter beacon.<BR>
|
|
||||||
<HR>
|
|
||||||
<b>Implant Details:</b> <BR>
|
|
||||||
<b>Function:</b> Continuously transmits low power signal. Useful for tracking.<BR>
|
|
||||||
<b>Special Features:</b><BR>
|
|
||||||
<i>Neuro-Safe</i>- 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.<BR>
|
|
||||||
<b>Integrity:</b> Gradient creates slight risk of being overcharged and frying the
|
|
||||||
circuitry. As a result neurotoxins can cause massive damage."}
|
|
||||||
return dat
|
|
||||||
43
code/game/objects/items/implants/security/implant_beacon.dm
Normal file
43
code/game/objects/items/implants/security/implant_beacon.dm
Normal file
@@ -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 = {"<b>Implant Specifications:</b><BR>
|
||||||
|
<b>Name:</b> Robust Corp JMP-21 Fugitive Retrieval Implant<BR>
|
||||||
|
<b>Life:</b> Deactivates upon death after ten minutes, but remains within the body.<BR>
|
||||||
|
<b>Important Notes: N/A</B><BR>
|
||||||
|
<HR>
|
||||||
|
<b>Implant Details: </b><BR>
|
||||||
|
<b>Function:</b> 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
|
||||||
@@ -3,6 +3,10 @@
|
|||||||
desc = "Injects things."
|
desc = "Injects things."
|
||||||
icon_state = "reagents"
|
icon_state = "reagents"
|
||||||
actions_types = null
|
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()
|
/obj/item/implant/chem/get_data()
|
||||||
var/dat = {"<b>Implant Specifications:</b><BR>
|
var/dat = {"<b>Implant Specifications:</b><BR>
|
||||||
@@ -10,24 +14,51 @@
|
|||||||
<b>Life:</b> Deactivates upon death but remains within the body.<BR>
|
<b>Life:</b> Deactivates upon death but remains within the body.<BR>
|
||||||
<b>Important Notes: Due to the system functioning off of nutrients in the implanted subject's body, the subject<BR>
|
<b>Important Notes: Due to the system functioning off of nutrients in the implanted subject's body, the subject<BR>
|
||||||
will suffer from an increased appetite.</B><BR>
|
will suffer from an increased appetite.</B><BR>
|
||||||
<HR>
|
|
||||||
<b>Implant Details:</b><BR>
|
<b>Implant Details:</b><BR>
|
||||||
<b>Function:</b> Contains a small capsule that can contain various chemicals. Upon receiving a specially encoded signal<BR>
|
<i>Function:</i> Contains a small capsule that can contain various chemicals. Upon receiving a specially encoded signal<BR>
|
||||||
the implant releases the chemicals directly into the blood stream.<BR>
|
the implant releases the chemicals directly into the blood stream.<BR>
|
||||||
<b>Special Features:</b>
|
|
||||||
<i>Micro-Capsule</i>- Can be loaded with any sort of chemical agent via the common syringe and can hold 50 units.<BR>
|
<i>Micro-Capsule</i>- Can be loaded with any sort of chemical agent via the common syringe and can hold 50 units.<BR>
|
||||||
Can only be loaded while still in its original case.<BR>
|
Can only be loaded while still in its original case.<BR>
|
||||||
<b>Integrity:</b> Implant will last so long as the subject is alive."}
|
<b>Integrity:</b> Implant will last so long as the subject is alive, breaking down and releasing all contents on death."}
|
||||||
return dat
|
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)
|
/obj/item/implant/chem/Initialize(mapload)
|
||||||
. = ..()
|
. = ..()
|
||||||
create_reagents(50, OPENCONTAINER)
|
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)
|
/obj/item/implant/chem/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE)
|
||||||
. = ..()
|
. = ..()
|
||||||
@@ -5,11 +5,14 @@
|
|||||||
name = "exile implant"
|
name = "exile implant"
|
||||||
desc = "Prevents you from returning from away missions."
|
desc = "Prevents you from returning from away missions."
|
||||||
actions_types = null
|
actions_types = null
|
||||||
|
implant_flags = IMPLANT_TYPE_SECURITY
|
||||||
|
hud_icon_state = "hud_imp_exile"
|
||||||
|
|
||||||
/obj/item/implant/exile/get_data()
|
/obj/item/implant/exile/get_data()
|
||||||
var/dat = {"<b>Implant Specifications:</b><BR>
|
var/dat = {"<b>Implant Specifications:</b><BR>
|
||||||
<b>Name:</b> Nanotrasen Employee Exile Implant<BR>
|
<b>Name:</b> Nanotrasen Employee Exile Implant<BR>
|
||||||
<b>Implant Details:</b> The onboard gateway system has been modified to reject entry by individuals containing this implant.<BR>"}
|
<b>Implant Details:</b> 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.<BR>"}
|
||||||
return dat
|
return dat
|
||||||
|
|
||||||
|
|
||||||
@@ -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 = {"<b>Implant Specifications:</b><BR>
|
||||||
|
<b>Name:</b> Robust Corp EXP-001 'Bluespace Grounder'<BR>
|
||||||
|
<b>Implant Details:</b> 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.<BR>"}
|
||||||
|
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
|
||||||
87
code/game/objects/items/implants/security/implant_track.dm
Normal file
87
code/game/objects/items/implants/security/implant_track.dm
Normal file
@@ -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 = {"<b>Implant Specifications:</b><BR>
|
||||||
|
<b>Name:</b> Robust Corp EYE-5 Convict Parole Implant<BR>
|
||||||
|
<b>Life:</b> 10 minutes after death of host.<BR>
|
||||||
|
<HR>
|
||||||
|
<b>Implant Details:</b> <BR>
|
||||||
|
<b>Function:</b> Continuously transmits low power signal. Can be tracked from a prisoner management console.<BR>
|
||||||
|
<b>Special Features:</b><BR>
|
||||||
|
<i>Neuro-Safe</i>- 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.<BR>"}
|
||||||
|
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
|
||||||
82
code/game/objects/items/storage/boxes/implant_boxes.dm
Normal file
82
code/game/objects/items/storage/boxes/implant_boxes.dm
Normal file
@@ -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)
|
||||||
@@ -69,60 +69,6 @@
|
|||||||
for(var/i in 1 to 5)
|
for(var/i in 1 to 5)
|
||||||
new /obj/item/grenade/empgrenade(src)
|
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
|
/obj/item/storage/box/prisoner
|
||||||
name = "box of prisoner IDs"
|
name = "box of prisoner IDs"
|
||||||
desc = "Take away their last shred of dignity, their name."
|
desc = "Take away their last shred of dignity, their name."
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
throw_speed = 3
|
throw_speed = 3
|
||||||
throw_range = 7
|
throw_range = 7
|
||||||
custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 4)
|
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)
|
/obj/item/locator/ui_interact(mob/user, datum/tgui/ui)
|
||||||
ui = SStgui.try_update_ui(user, src, ui)
|
ui = SStgui.try_update_ui(user, src, ui)
|
||||||
@@ -72,22 +72,22 @@
|
|||||||
|
|
||||||
var/list/track_implants = list()
|
var/list/track_implants = list()
|
||||||
|
|
||||||
for (var/obj/item/implant/tracking/W in GLOB.tracked_implants)
|
for (var/obj/item/implant/beacon/tracking_beacon in GLOB.tracked_implants)
|
||||||
if (!W.imp_in || !isliving(W.loc))
|
if (!tracking_beacon.imp_in || !isliving(tracking_beacon.loc))
|
||||||
continue
|
continue
|
||||||
else
|
else
|
||||||
var/mob/living/M = W.loc
|
var/mob/living/living_mob = tracking_beacon.loc
|
||||||
if (M.stat == DEAD)
|
if (living_mob.stat == DEAD)
|
||||||
if (M.timeofdeath + W.lifespan_postmortem < world.time)
|
if (living_mob.timeofdeath + tracking_beacon.lifespan_postmortem < world.time)
|
||||||
continue
|
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))
|
var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
|
||||||
|
|
||||||
if(distance > tracking_range)
|
if(distance > tracking_range)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var/D = dir2text(get_dir(sr, tr))
|
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
|
data["trackimplants"] = track_implants
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|||||||
@@ -340,3 +340,17 @@
|
|||||||
access_view = ACCESS_SECURITY
|
access_view = ACCESS_SECURITY
|
||||||
contains = list(/obj/item/clothing/glasses/sunglasses = 1)
|
contains = list(/obj/item/clothing/glasses/sunglasses = 1)
|
||||||
crate_name = "sunglasses crate"
|
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"
|
||||||
|
|||||||
@@ -103,6 +103,13 @@
|
|||||||
if (HAS_TRAIT(user, TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION) && !is_station_level(user.z))
|
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."))
|
to_chat(user, span_warning("You get the feeling you shouldn't mess with this."))
|
||||||
return
|
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 ..()
|
return ..()
|
||||||
|
|
||||||
/obj/machinery/computer/shuttle/mining/common
|
/obj/machinery/computer/shuttle/mining/common
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
icon = 'icons/mob/human/human.dmi'
|
icon = 'icons/mob/human/human.dmi'
|
||||||
icon_state = "human_basic"
|
icon_state = "human_basic"
|
||||||
appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE
|
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)
|
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)
|
||||||
hud_type = /datum/hud/human
|
hud_type = /datum/hud/human
|
||||||
pressure_resistance = 25
|
pressure_resistance = 25
|
||||||
can_buckle = TRUE
|
can_buckle = TRUE
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
/obj/item/ammo_box/c38/trac
|
/obj/item/ammo_box/c38/trac
|
||||||
name = "speed loader (.38 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_type = /obj/item/ammo_casing/c38/trac
|
||||||
ammo_band_color = "#7b6383"
|
ammo_band_color = "#7b6383"
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 10 KiB |
@@ -110,6 +110,7 @@
|
|||||||
#include "code\__DEFINES\hud.dm"
|
#include "code\__DEFINES\hud.dm"
|
||||||
#include "code\__DEFINES\icon_smoothing.dm"
|
#include "code\__DEFINES\icon_smoothing.dm"
|
||||||
#include "code\__DEFINES\id_cards.dm"
|
#include "code\__DEFINES\id_cards.dm"
|
||||||
|
#include "code\__DEFINES\implants.dm"
|
||||||
#include "code\__DEFINES\important_recursive_contents.dm"
|
#include "code\__DEFINES\important_recursive_contents.dm"
|
||||||
#include "code\__DEFINES\injection.dm"
|
#include "code\__DEFINES\injection.dm"
|
||||||
#include "code\__DEFINES\input.dm"
|
#include "code\__DEFINES\input.dm"
|
||||||
@@ -2401,10 +2402,8 @@
|
|||||||
#include "code\game\objects\items\grenades\syndieminibomb.dm"
|
#include "code\game\objects\items\grenades\syndieminibomb.dm"
|
||||||
#include "code\game\objects\items\implants\implant.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_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_clown.dm"
|
||||||
#include "code\game\objects\items\implants\implant_deathrattle.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_explosive.dm"
|
||||||
#include "code\game\objects\items\implants\implant_freedom.dm"
|
#include "code\game\objects\items\implants\implant_freedom.dm"
|
||||||
#include "code\game\objects\items\implants\implant_krav_maga.dm"
|
#include "code\game\objects\items\implants\implant_krav_maga.dm"
|
||||||
@@ -2413,12 +2412,16 @@
|
|||||||
#include "code\game\objects\items\implants\implant_spell.dm"
|
#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_stealth.dm"
|
||||||
#include "code\game\objects\items\implants\implant_storage.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\implantcase.dm"
|
||||||
#include "code\game\objects\items\implants\implantchair.dm"
|
#include "code\game\objects\items\implants\implantchair.dm"
|
||||||
#include "code\game\objects\items\implants\implanter.dm"
|
#include "code\game\objects\items\implants\implanter.dm"
|
||||||
#include "code\game\objects\items\implants\implantpad.dm"
|
#include "code\game\objects\items\implants\implantpad.dm"
|
||||||
#include "code\game\objects\items\implants\implantuplink.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\kirbyplants.dm"
|
||||||
#include "code\game\objects\items\kirby_plants\organic_plants.dm"
|
#include "code\game\objects\items\kirby_plants\organic_plants.dm"
|
||||||
#include "code\game\objects\items\kirby_plants\synthetic_plants.dm"
|
#include "code\game\objects\items\kirby_plants\synthetic_plants.dm"
|
||||||
@@ -2491,6 +2494,7 @@
|
|||||||
#include "code\game\objects\items\storage\boxes\clothes_boxes.dm"
|
#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\engineering_boxes.dm"
|
||||||
#include "code\game\objects\items\storage\boxes\food_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\job_boxes.dm"
|
||||||
#include "code\game\objects\items\storage\boxes\medical_boxes.dm"
|
#include "code\game\objects\items\storage\boxes\medical_boxes.dm"
|
||||||
#include "code\game\objects\items\storage\boxes\science_boxes.dm"
|
#include "code\game\objects\items\storage\boxes\science_boxes.dm"
|
||||||
|
|||||||
272
tgui/packages/tgui/interfaces/PrisonerManagement.tsx
Normal file
272
tgui/packages/tgui/interfaces/PrisonerManagement.tsx
Normal file
@@ -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<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ImplantInfo = {
|
||||||
|
info: Record<string, string>;
|
||||||
|
buttons: DMButton[];
|
||||||
|
category: string;
|
||||||
|
ref: byondRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Data = {
|
||||||
|
authorized: BooleanLike;
|
||||||
|
inserted_id: IDInfo | null;
|
||||||
|
implants: ImplantInfo[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const ImplantDisplay = (props: { implant: ImplantInfo }) => {
|
||||||
|
const { act } = useBackend<ImplantInfo>();
|
||||||
|
const { info, buttons, ref } = props.implant;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack vertical>
|
||||||
|
<Stack.Item>
|
||||||
|
<LabeledList>
|
||||||
|
{Object.entries(info).map(([key, value]) => (
|
||||||
|
<LabeledList.Item key={key} label={key}>
|
||||||
|
{value}
|
||||||
|
</LabeledList.Item>
|
||||||
|
))}
|
||||||
|
{buttons.length !== 0 && (
|
||||||
|
<LabeledList.Item label={'Options'}>
|
||||||
|
{buttons.map((button) => (
|
||||||
|
<Button
|
||||||
|
key={button.action_key}
|
||||||
|
icon={button.icon}
|
||||||
|
color={button.color}
|
||||||
|
tooltip={button.tooltip}
|
||||||
|
onClick={() =>
|
||||||
|
act('handle_implant', {
|
||||||
|
implant_ref: ref,
|
||||||
|
implant_action: button.action_key,
|
||||||
|
...button.action_params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{button.name}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</LabeledList.Item>
|
||||||
|
)}
|
||||||
|
<LabeledList.Divider />
|
||||||
|
</LabeledList>
|
||||||
|
</Stack.Item>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// When given a list of implants, sorts them by category
|
||||||
|
const sortImplants = (implants: ImplantInfo[]) => {
|
||||||
|
const implantsByCategory: Record<string, ImplantInfo[]> = 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<string, ImplantInfo[]> = sortImplants(
|
||||||
|
props.implants,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [implantTab, setImplantTab] = useSharedState(
|
||||||
|
'implantTab',
|
||||||
|
Object.keys(implantsByCategory)[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack fill vertical>
|
||||||
|
<Stack.Item>
|
||||||
|
<Tabs>
|
||||||
|
{Object.entries(implantsByCategory).map(([category, implants]) => (
|
||||||
|
<Tabs.Tab
|
||||||
|
key={category}
|
||||||
|
selected={implantTab === category}
|
||||||
|
onClick={() => setImplantTab(category)}
|
||||||
|
>
|
||||||
|
{formatCategory(category)}
|
||||||
|
</Tabs.Tab>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</Stack.Item>
|
||||||
|
<Stack.Item>
|
||||||
|
{implantTab && implantsByCategory && implantsByCategory[implantTab] ? (
|
||||||
|
implantsByCategory[implantTab].map((implant) => (
|
||||||
|
<ImplantDisplay key={implant.ref} implant={implant} />
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<NoticeBox>No implants detected.</NoticeBox>
|
||||||
|
)}
|
||||||
|
</Stack.Item>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const IdShowcase = (props: { id: IDInfo | null }) => {
|
||||||
|
const { act } = useBackend<IDInfo>();
|
||||||
|
const { id } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack fill vertical>
|
||||||
|
<Stack.Item>
|
||||||
|
<LabeledList>
|
||||||
|
{id ? (
|
||||||
|
<>
|
||||||
|
<LabeledList.Item label="ID">
|
||||||
|
<Button onClick={() => act('eject_id')} icon="eject" mr={1} />
|
||||||
|
{id.name}
|
||||||
|
</LabeledList.Item>
|
||||||
|
<LabeledList.Item label="Points">
|
||||||
|
<Button
|
||||||
|
onClick={() => act('reset_id')}
|
||||||
|
icon="times"
|
||||||
|
color="bad"
|
||||||
|
mr={1}
|
||||||
|
/>
|
||||||
|
{id.points}
|
||||||
|
</LabeledList.Item>
|
||||||
|
<LabeledList.Item label="Goal">
|
||||||
|
<Button
|
||||||
|
onClick={() => act('set_id_goal')}
|
||||||
|
icon="check"
|
||||||
|
mr={1}
|
||||||
|
/>
|
||||||
|
{id.goal}
|
||||||
|
</LabeledList.Item>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<LabeledList.Item label="ID">
|
||||||
|
<Button onClick={() => act('insert_id')}>No ID Inserted</Button>
|
||||||
|
</LabeledList.Item>
|
||||||
|
)}
|
||||||
|
</LabeledList>
|
||||||
|
</Stack.Item>
|
||||||
|
{!!id && (
|
||||||
|
<Stack.Item>
|
||||||
|
<NoticeBox>
|
||||||
|
Space Law recommends quotas of 100 points per minute they would
|
||||||
|
normally serve in the brig.
|
||||||
|
</NoticeBox>
|
||||||
|
</Stack.Item>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ManagementConsole = () => {
|
||||||
|
const { act, data } = useBackend<Data>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack fill vertical>
|
||||||
|
<Stack.Item>
|
||||||
|
<Section title="ID Management">
|
||||||
|
<IdShowcase id={data.inserted_id} />
|
||||||
|
</Section>
|
||||||
|
</Stack.Item>
|
||||||
|
<Stack.Item grow>
|
||||||
|
<Section title="Security Implants" scrollable fill>
|
||||||
|
<AllImplantDisplay implants={data.implants} />
|
||||||
|
</Section>
|
||||||
|
</Stack.Item>
|
||||||
|
<Stack.Item>
|
||||||
|
<NoticeBox align="right" info>
|
||||||
|
Secure Your Workspace.
|
||||||
|
<Button
|
||||||
|
align="right"
|
||||||
|
icon="lock"
|
||||||
|
color="good"
|
||||||
|
ml={2}
|
||||||
|
onClick={() => act('logout')}
|
||||||
|
>
|
||||||
|
Log Out
|
||||||
|
</Button>
|
||||||
|
</NoticeBox>
|
||||||
|
</Stack.Item>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// I copied this from security records,
|
||||||
|
// should probably make this a generic component
|
||||||
|
const LogIn = () => {
|
||||||
|
const { act } = useBackend<Data>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section fill>
|
||||||
|
<Stack fill vertical>
|
||||||
|
<Stack.Item grow />
|
||||||
|
<Stack.Item align="center" grow={2}>
|
||||||
|
<Icon color="average" name="exclamation-triangle" size={15} />
|
||||||
|
</Stack.Item>
|
||||||
|
<Stack.Item align="center" grow>
|
||||||
|
<Box color="red" fontSize="18px" bold mt={5}>
|
||||||
|
Nanotrasen SecurityHUB
|
||||||
|
</Box>
|
||||||
|
</Stack.Item>
|
||||||
|
<Stack.Item>
|
||||||
|
<NoticeBox align="right">
|
||||||
|
You are not logged in.
|
||||||
|
<Button ml={2} icon="lock-open" onClick={() => act('login')}>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
</NoticeBox>
|
||||||
|
</Stack.Item>
|
||||||
|
</Stack>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PrisonerManagement = () => {
|
||||||
|
const { data } = useBackend<Data>();
|
||||||
|
const { authorized } = data;
|
||||||
|
return (
|
||||||
|
<Window width={465} height={565} title="Prisoner Management">
|
||||||
|
<Window.Content>
|
||||||
|
{authorized ? <ManagementConsole /> : <LogIn />}
|
||||||
|
</Window.Content>
|
||||||
|
</Window>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user