Skip to content

Commit

Permalink
[MIRROR] "Security Implant" rework, prisoner management console updat…
Browse files Browse the repository at this point in the history
…es [MDB IGNORE] (#25525) (#1061)

* "Security Implant" rework, prisoner management console updates

* Fix conflicts

---------

Co-authored-by: SkyratBot <[email protected]>
Co-authored-by: Rhials <[email protected]>
Co-authored-by: SomeRandomOwl <[email protected]>
Co-authored-by: Bloop <[email protected]>
  • Loading branch information
5 people authored Dec 11, 2023
1 parent c60cf81 commit e46ac28
Show file tree
Hide file tree
Showing 24 changed files with 847 additions and 323 deletions.
4 changes: 2 additions & 2 deletions code/__DEFINES/atom_hud.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions code/__DEFINES/implants.dm
Original file line number Diff line number Diff line change
@@ -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
4 changes: 0 additions & 4 deletions code/_globalvars/lists/objects.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
37 changes: 21 additions & 16 deletions code/game/data_huds.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -287,25 +287,30 @@ Security HUDs! Basic mode shows only the job.

/mob/living/proc/sec_hud_set_implants()
var/image/holder
for(var/i in list(IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD))
for(var/i in list(IMPSEC_FIRST_HUD, IMPLOYAL_HUD, IMPSEC_SECOND_HUD))
holder = hud_list[i]
holder.icon_state = null
set_hud_image_inactive(i)

for(var/obj/item/implant/I in implants)
if(istype(I, /obj/item/implant/tracking))
holder = hud_list[IMPTRACK_HUD]
var/icon/IC = icon(icon, icon_state, dir)
holder.pixel_y = IC.Height() - world.icon_size
holder.icon_state = "hud_imp_tracking"
set_hud_image_active(IMPTRACK_HUD)

else if(istype(I, /obj/item/implant/chem))
holder = hud_list[IMPCHEM_HUD]
var/icon/IC = icon(icon, icon_state, dir)
holder.pixel_y = IC.Height() - world.icon_size
holder.icon_state = "hud_imp_chem"
set_hud_image_active(IMPCHEM_HUD)
var/security_slot = 1 //Which of the two security hud slots are we putting found security implants in?
for(var/obj/item/implant/current_implant in implants)
if(current_implant.implant_flags & IMPLANT_TYPE_SECURITY)
switch(security_slot)
if(1)
holder = hud_list[IMPSEC_FIRST_HUD]
var/icon/IC = icon(icon, icon_state, dir)
holder.pixel_y = IC.Height() - world.icon_size
holder.icon_state = current_implant.hud_icon_state
set_hud_image_active(IMPSEC_FIRST_HUD)
security_slot++

if(2) //Theoretically if we somehow get multiple sec implants, whatever the most recently implanted implant is will take over the 2nd position
holder = hud_list[IMPSEC_SECOND_HUD]
var/icon/IC = icon(icon, icon_state, dir)
holder.pixel_y = IC.Height() - world.icon_size
holder.pixel_x = initial(holder.pixel_x) + 7 //Adds an offset that mirrors the hud blip to the other side of the mob.
holder.icon_state = current_implant.hud_icon_state
set_hud_image_active(IMPSEC_SECOND_HUD)

if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
holder = hud_list[IMPLOYAL_HUD]
Expand Down
74 changes: 40 additions & 34 deletions code/game/machinery/computer/prisoner/_prisoner.dm
Original file line number Diff line number Diff line change
@@ -1,53 +1,59 @@
/obj/machinery/computer/prisoner
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE|INTERACT_MACHINE_REQUIRES_LITERACY
var/obj/item/card/id/advanced/prisoner/contained_id
/// ID card currently inserted into the computer.
VAR_FINAL/obj/item/card/id/advanced/prisoner/contained_id

/obj/machinery/computer/prisoner/deconstruct(disassembled, mob/user)
contained_id?.forceMove(drop_location())
return ..()

/obj/machinery/computer/prisoner/Destroy()
if(contained_id)
contained_id.forceMove(get_turf(src))
QDEL_NULL(contained_id)
return ..()

/obj/machinery/computer/prisoner/Exited(atom/movable/gone, direction)
. = ..()
if(gone == contained_id)
contained_id = null

/obj/machinery/computer/prisoner/examine(mob/user)
. = ..()
if(contained_id)
. += span_notice("<b>Alt-click</b> to eject the ID card.")



/obj/machinery/computer/prisoner/AltClick(mob/user)
id_eject(user)
return ..()
. = ..()
if(user.can_perform_action(src, ALLOW_SILICON_REACH))
id_eject(user)

/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/P)
if(istype(P))
if(contained_id)
to_chat(user, span_warning("There's already an ID card in the console!"))
return
if(!user.transferItemToLoc(P, src))
return
contained_id = P
user.visible_message(span_notice("[user] inserts an ID card into the console."), \
span_notice("You insert the ID card into the console."))
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
updateUsrDialog()
/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/new_id)
if(!istype(new_id))
return
if(!isnull(contained_id))
balloon_alert(user, "no empty slot!")
return
if(!user.transferItemToLoc(new_id, src))
return
contained_id = new_id
balloon_alert_to_viewers("id inserted")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)

/obj/machinery/computer/prisoner/proc/id_eject(mob/user)
if(!contained_id)
to_chat(user, span_warning("There's no ID card in the console!"))
if(isnull(contained_id))
balloon_alert(user, "no id!")
return

if(!issilicon(user) && Adjacent(user))
user.put_in_hands(contained_id)
else
contained_id.forceMove(drop_location())
if(!issilicon(user) && Adjacent(user))
user.put_in_hands(contained_id)
contained_id = null
user.visible_message(span_notice("[user] gets an ID card from the console."), \
span_notice("You get the ID card from the console."))
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
updateUsrDialog()

/obj/machinery/computer/prisoner/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/card/id/advanced/prisoner))
id_insert(user, I)
else
return ..()

balloon_alert_to_viewers("id ejected")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)

/obj/machinery/computer/prisoner/attackby(obj/item/weapon, mob/user, params)
if(istype(weapon, /obj/item/card/id/advanced/prisoner))
id_insert(user, weapon)
return TRUE

return ..()
206 changes: 83 additions & 123 deletions code/game/machinery/computer/prisoner/management.dm
Original file line number Diff line number Diff line change
@@ -1,137 +1,97 @@
/// List of all implants currently implanted into a mob
GLOBAL_LIST_EMPTY_TYPED(tracked_implants, /obj/item/implant)

/obj/machinery/computer/prisoner/management
name = "prisoner management console"
desc = "Used to manage tracking implants placed inside criminals."
desc = "Used to modify prisoner IDs, as well as manage security implants placed inside convicts and parolees."
icon_screen = "explosive"
icon_keyboard = "security_key"
req_access = list(ACCESS_BRIG)
light_color = COLOR_SOFT_RED
var/id = 0
var/temp = null
var/status = 0
var/timeleft = 60
var/stop = 0
var/screen = 0 // 0 - No Access Denied, 1 - Access allowed
circuit = /obj/item/circuitboard/computer/prisoner

/obj/machinery/computer/prisoner/management/ui_interact(mob/user, datum/tgui/ui)
. = ..()
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PrisonerManagement")
ui.open()

/obj/machinery/computer/prisoner/management/ui_data(mob/user)
var/list/data = list()

data["authorized"] = (authenticated && isliving(user)) || isAdminGhostAI(user) || issilicon(user)
data["inserted_id"] = null
if(!isnull(contained_id))
data["inserted_id"] = list(
"name" = contained_id.name,
"points" = contained_id.points,
"goal" = contained_id.goal,
)

var/list/implants = list()
for(var/obj/item/implant/implant as anything in GLOB.tracked_implants)
if(!implant.is_shown_on_console(src))
continue
var/list/implant_data = list()
implant_data["info"] = implant.get_management_console_data()
implant_data["buttons"] = implant.get_management_console_buttons()
implant_data["category"] = initial(implant.name)
implant_data["ref"] = REF(implant)
UNTYPED_LIST_ADD(implants, implant_data)
data["implants"] = implants

/obj/machinery/computer/prisoner/management/ui_interact(mob/user)
return data

/obj/machinery/computer/prisoner/management/ui_act(action, list/params)
. = ..()
if(isliving(user))
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
var/dat = ""
if(screen == 0)
dat += "<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(..())
if(.)
return
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr))
usr.set_machine(src)

if(href_list["id"])
if(href_list["id"] == "insert" && !contained_id)
id_insert(usr)
else if(contained_id)
switch(href_list["id"])
if("eject")
id_eject(usr)
if("reset")
contained_id.points = 0
if("setgoal")
var/num = tgui_input_text(usr, "Enter the prisoner's goal", "Prisoner Management", 1, 1000, 1)
if(isnull(num))
return
contained_id.goal = round(num)
else if(href_list["inject1"])
var/obj/item/implant/I = locate(href_list["inject1"]) in GLOB.tracked_chem_implants
if(I && istype(I))
I.activate(1)
else if(href_list["inject5"])
var/obj/item/implant/I = locate(href_list["inject5"]) in GLOB.tracked_chem_implants
if(I && istype(I))
I.activate(5)
else if(href_list["inject10"])
var/obj/item/implant/I = locate(href_list["inject10"]) in GLOB.tracked_chem_implants
if(I && istype(I))
I.activate(10)

else if(href_list["lock"])

if(!authenticated && action != "login")
CRASH("[usr] potentially spoofed ui action [action] on prisoner console without the console being logged in.")

if(isliving(usr))
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)

switch(action)
if("login")
if(allowed(usr))
screen = !screen
authenticated = TRUE
playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
else
to_chat(usr, span_danger("Unauthorized access."))

else if(href_list["warn"])
var/warning = tgui_input_text(usr, "Enter your message here", "Messaging")
if(!warning)
return
var/obj/item/implant/I = locate(href_list["warn"]) in GLOB.tracked_implants
if(I && istype(I) && I.imp_in)
var/mob/living/R = I.imp_in
to_chat(R, span_hear("You hear a voice in your head saying: '[warning]'"))
log_directed_talk(usr, R, warning, LOG_SAY, "implant message")

src.add_fingerprint(usr)
src.updateUsrDialog()
return
playsound(src, 'sound/machines/terminal_error.ogg', 50, FALSE)
return TRUE

if("logout")
authenticated = FALSE
playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
return TRUE

if("insert_id")
id_insert(usr, usr.get_active_held_item())
return TRUE

if("eject_id")
id_eject(usr)
return TRUE

if("set_id_goal")
var/num = tgui_input_number(usr, "Enter the prisoner's goal", "Prisoner Management", 100, 1000, 1)
if(!isnum(num) || QDELETED(src) || QDELETED(contained_id) || QDELETED(usr))
return TRUE
if(!is_operational || !usr.can_perform_action(src, NEED_DEXTERITY|ALLOW_SILICON_REACH))
return TRUE

contained_id.goal = num
return TRUE

if("reset_id")
contained_id.points = 0
return TRUE

if("handle_implant")
var/obj/item/implant/affected_implant = locate(params["implant_ref"]) in GLOB.tracked_implants
if(affected_implant?.is_shown_on_console(src))
affected_implant.handle_management_console_action(usr, params, src)
return TRUE
Loading

0 comments on commit e46ac28

Please sign in to comment.