mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 16:05:07 +00:00
[MIRROR] "Security Implant" rework, prisoner management console updates [MDB IGNORE] (#25525)
* "Security Implant" rework, prisoner management console updates * Fix conflicts --------- Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com> Co-authored-by: SomeRandomOwl <somerandomowl@ratchtnet.com> Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
This commit is contained in:
@@ -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
|
||||
|
||||
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)
|
||||
/// 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"
|
||||
|
||||
@@ -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/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 = "hud_imp_tracking"
|
||||
set_hud_image_active(IMPTRACK_HUD)
|
||||
holder.icon_state = current_implant.hud_icon_state
|
||||
set_hud_image_active(IMPSEC_FIRST_HUD)
|
||||
security_slot++
|
||||
|
||||
else if(istype(I, /obj/item/implant/chem))
|
||||
holder = hud_list[IMPCHEM_HUD]
|
||||
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.icon_state = "hud_imp_chem"
|
||||
set_hud_image_active(IMPCHEM_HUD)
|
||||
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]
|
||||
|
||||
@@ -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/Destroy()
|
||||
if(contained_id)
|
||||
contained_id.forceMove(get_turf(src))
|
||||
/obj/machinery/computer/prisoner/deconstruct(disassembled, mob/user)
|
||||
contained_id?.forceMove(drop_location())
|
||||
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)
|
||||
. = ..()
|
||||
if(contained_id)
|
||||
. += span_notice("<b>Alt-click</b> to eject the ID card.")
|
||||
|
||||
|
||||
|
||||
/obj/machinery/computer/prisoner/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(user.can_perform_action(src, ALLOW_SILICON_REACH))
|
||||
id_eject(user)
|
||||
return ..()
|
||||
|
||||
/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!"))
|
||||
/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/new_id)
|
||||
if(!istype(new_id))
|
||||
return
|
||||
if(!user.transferItemToLoc(P, src))
|
||||
if(!isnull(contained_id))
|
||||
balloon_alert(user, "no empty slot!")
|
||||
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."))
|
||||
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)
|
||||
updateUsrDialog()
|
||||
|
||||
/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
|
||||
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
|
||||
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 ..()
|
||||
|
||||
@@ -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)
|
||||
/obj/machinery/computer/prisoner/management/ui_interact(mob/user, datum/tgui/ui)
|
||||
. = ..()
|
||||
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)
|
||||
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"
|
||||
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]<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"])
|
||||
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."))
|
||||
playsound(src, 'sound/machines/terminal_error.ogg', 50, FALSE)
|
||||
return TRUE
|
||||
|
||||
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")
|
||||
if("logout")
|
||||
authenticated = FALSE
|
||||
playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
|
||||
return TRUE
|
||||
|
||||
src.add_fingerprint(usr)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."
|
||||
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 = {"<b>Implant Specifications:</b><BR>
|
||||
@@ -10,24 +14,51 @@
|
||||
<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>
|
||||
will suffer from an increased appetite.</B><BR>
|
||||
<HR>
|
||||
<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>
|
||||
<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>
|
||||
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
|
||||
|
||||
/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)
|
||||
. = ..()
|
||||
@@ -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 = {"<b>Implant Specifications:</b><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
|
||||
|
||||
|
||||
@@ -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)
|
||||
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."
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 10 KiB |
@@ -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"
|
||||
@@ -2497,10 +2498,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"
|
||||
@@ -2509,12 +2508,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"
|
||||
@@ -2587,6 +2590,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"
|
||||
|
||||
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