Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Rhials committed Jan 3, 2025
2 parents 4089764 + b9998f2 commit 5f0f909
Show file tree
Hide file tree
Showing 8 changed files with 384 additions and 5 deletions.
198 changes: 198 additions & 0 deletions code/game/objects/items/forensicsspoofer.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/obj/item/forensics_spoofer
name = /obj/item/detective_scanner::name
desc = "Used to adjacently scan objects and biomass for fibers and fingerprints. Can replicate the findings."
icon = /obj/item/detective_scanner::icon
icon_state = /obj/item/detective_scanner::icon_state
w_class = WEIGHT_CLASS_SMALL
inhand_icon_state = /obj/item/detective_scanner::inhand_icon_state
worn_icon_state = /obj/item/detective_scanner::worn_icon_state
lefthand_file = /obj/item/detective_scanner::lefthand_file
righthand_file = /obj/item/detective_scanner::righthand_file
obj_flags = CONDUCTS_ELECTRICITY
item_flags = NOBLUDGEON
slot_flags = ITEM_SLOT_BELT
/// stored fibers in memory
var/list/fibers = list()
/// stored fingerprints in memory
var/list/fingerprints = list()
/// chosen fiber to add to target
var/chosen_fiber
/// chosen fingerprint to add to target
var/chosen_fingerprint
/// max storage for fibers/fingerprints seperate for each
var/max_storage = 5
/// do we scan for new material? if false will tamper
var/scan_mode = TRUE
/// do we make forensics scanner messages and sounds
var/silent_mode = FALSE
/// tamper cooldown time so people dont spam it on every single wall and thing ever
var/tamper_cooldown_time = 1 SECONDS
COOLDOWN_DECLARE(tamper_cooldown)

/obj/item/forensics_spoofer/Initialize(mapload)
. = ..()
// most things have add_fingerprint in their item interaction because lol lmao
// tl;dr cut off the chain before anything fires so we dont add user fingerprints to target
RegisterSignal(src, COMSIG_ITEM_INTERACTING_WITH_ATOM, PROC_REF(do_interact))

/obj/item/forensics_spoofer/attack_self_secondary(mob/user, modifiers)
. = ..()
if(.)
return
scan_mode = !scan_mode
balloon_alert(user, "now [scan_mode ? "scanning" : "applying"]")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN

// ok due to shenanigans basically every item interact adds your fingerprints to it which isnt ideal so we have this
/obj/item/forensics_spoofer/proc/do_interact(datum/source, mob/living/user, atom/interacting_with, list/modifiers)
SIGNAL_HANDLER
if(scan_mode)
INVOKE_ASYNC(src, PROC_REF(scan), interacting_with, user)
else
tamper(interacting_with, user, do_fibers = !isnull(chosen_fiber))
return ITEM_INTERACT_SUCCESS

/obj/item/forensics_spoofer/proc/do_fake_scan(atom/target, mob/user)
if(silent_mode)
return
playsound(src, SFX_INDUSTRIAL_SCAN, 20, TRUE, -2, TRUE, FALSE)
user.visible_message(
span_notice("\The [user] points the [name] at \the [target] and performs a forensic scan.")
)

/obj/item/forensics_spoofer/proc/clear_values(list/the_list)
for(var/key in the_list)
the_list[key] = ""

/obj/item/forensics_spoofer/proc/scan(atom/target, mob/living/user)
do_fake_scan(target, user)
if(isnull(target.forensics))
target.balloon_alert(user, "nothing!")
return ITEM_INTERACT_FAILURE
var/list/new_fibers = LAZYCOPY(target.forensics.fibers) - fibers
var/list/new_prints = LAZYCOPY(target.forensics.fingerprints) - fingerprints
var/new_len = length(new_fibers) + length(new_prints)
balloon_alert(user, "[new_len ? new_len : "no"] new prints/fibers")
if(new_len)
var/list/message = list(span_bold("Scan results (Unstored Only):"))
for(var/text in new_fibers)
message += span_notice("Fiber: [text]")
if(length(fibers) > max_storage)
message += span_boldwarning("Fiber storage full.")
for(var/text in new_prints)
message += span_notice("Fingerprint: [text]")
if(length(fingerprints) > max_storage)
message += span_boldwarning("Fingerprint storage full.")
to_chat(user, boxed_message(jointext(message, "\n")), type = MESSAGE_TYPE_INFO)
if(length(fingerprints) < max_storage)
while(length(fingerprints) + length(new_prints) > max_storage)
var/to_remove = tgui_input_list(user, "Too many prints, cancel to discard all", "What to discard", new_fibers)
if(isnull(to_remove))
return ITEM_INTERACT_FAILURE
new_prints -= to_remove
clear_values(new_prints)
fingerprints += new_prints
for(var/fingerprint in fingerprints)
fingerprints[fingerprint] = get_name_from_fingerprint(fingerprint)
if(length(fibers) < max_storage)
while(length(fibers) + length(new_fibers) > max_storage)
var/to_remove = tgui_input_list(user, "Too many prints, cancel to discard all", "What to discard", new_fibers)
if(isnull(to_remove))
return ITEM_INTERACT_FAILURE
new_fibers -= to_remove
clear_values(new_fibers)
fibers += new_fibers
return ITEM_INTERACT_SUCCESS

/obj/item/forensics_spoofer/proc/tamper(atom/target, mob/living/user, do_fibers = FALSE)
do_fake_scan(target, user)
if((!do_fibers && isnull(chosen_fingerprint)) || (do_fibers && isnull(chosen_fiber)))
balloon_alert(user, "no [do_fibers ? "fiber" : "fingerprint"] selected!") // we CAN automatically select it but if they dont have it selected then they likely didnt know of it in the first place so they learn it now
return ITEM_INTERACT_FAILURE
if(!COOLDOWN_FINISHED(src, tamper_cooldown))
balloon_alert(user, "please wait!")
return ITEM_INTERACT_FAILURE
if(!isnull(target.forensics) && LAZYFIND(do_fibers ? target.forensics.fibers : target.forensics.fingerprints, do_fibers ? chosen_fiber : chosen_fingerprint))
balloon_alert(user, "already present!")
return ITEM_INTERACT_FAILURE

if(do_fibers)
target.add_fiber_list(list(chosen_fiber))
user.log_message("has tampered with the fingerprints/fibers of [src]. Added [chosen_fiber]", LOG_ATTACK)
else
target.add_fingerprint_list(list(chosen_fingerprint))
user.log_message("has tampered with the fingerprints/fibers of [src]. Added [chosen_fingerprint]", LOG_ATTACK)

target.balloon_alert(user, "[do_fibers ? "fiber" : "fingerprint"] added")
target.add_hiddenprint(user)
COOLDOWN_START(src, tamper_cooldown, tamper_cooldown_time)

return ITEM_INTERACT_SUCCESS

/obj/item/forensics_spoofer/proc/get_name_from_fingerprint(fingerprint)
. = "Unknown"
for(var/datum/record/crew/player_record as anything in GLOB.manifest.general)
if(player_record.fingerprint != fingerprint)
continue
return player_record.name

/obj/item/forensics_spoofer/ui_state(mob/user)
return GLOB.hands_state

/obj/item/forensics_spoofer/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ForensicsSpoofer", name)
ui.open()

/obj/item/forensics_spoofer/ui_static_data(mob/user)
. = list(
"max_storage" = max_storage,
)

/obj/item/forensics_spoofer/ui_data(mob/user)
return list(
"scanmode" = scan_mode,
"silent" = silent_mode,
"fibers" = fibers,
"fingerprints" = fingerprints,
"chosen_fiber" = chosen_fiber,
"chosen_fingerprint" = chosen_fingerprint,
)

/obj/item/forensics_spoofer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(!isnull(params["chosen"])) //fiber/print actions
var/chosen = params["chosen"]
switch(action)
if("delete")
if(chosen in fibers)
if(chosen_fiber == chosen)
chosen_fiber = null
fibers -= chosen
else
if(chosen_fingerprint == chosen)
chosen_fingerprint = null
fingerprints -= chosen
return TRUE
if("choose")
var/is_fiber = !!(chosen in fibers)
chosen_fiber = is_fiber ? chosen : null
chosen_fingerprint = is_fiber ? null : chosen
return TRUE
if("make_note")
if(chosen in fibers)
fibers[chosen] = params["note"]
else
fingerprints[chosen] = params["note"]
return TRUE
else
switch(action)
if("scanmode")
scan_mode = !scan_mode
return TRUE
if("stealth")
silent_mode = !silent_mode
return TRUE
16 changes: 16 additions & 0 deletions code/game/objects/items/storage/uplink_kits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,22 @@
new /obj/item/gun/ballistic/rifle/rebarxbow/syndie(src)
new /obj/item/storage/bag/rebar_quiver/syndicate(src)

/obj/item/paper/syndicate_forensics_spoofer
name = "Forensics Spoofer Guide"
default_raw_text = {"
<b>Forensics Spoofer Info:</b><br>
The spoofer has two modes: <b>SCAN</b> which scans for fingerprints and fibers, and <b>APPLY</b> which applies the currently chosen fingerprint/fiber to your target.<br>
The spoofer can only store 5 fingerprints and 5 fibers, and may not store or report fibers/prints already stored. Additionally, it taps into the stations network to associate scanned fingerprints with names.<br>
The spoofer will make the same sounds and sights as a forensics scanner, when <b>silent mode</b> is <b>off</b>.<br>
"}

/obj/item/storage/box/syndie_kit/forensics_spoofer
name = "forensics spoofing kit"

/obj/item/storage/box/syndie_kit/forensics_spoofer/PopulateContents()
new /obj/item/forensics_spoofer(src)
new /obj/item/paper/syndicate_forensics_spoofer(src)

/obj/item/storage/box/syndie_kit/origami_bundle
name = "origami kit"
desc = "A box full of a number of rather masterfully engineered paper planes and a manual on \"The Art of Origami\"."
Expand Down
9 changes: 4 additions & 5 deletions code/game/turfs/baseturfs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,16 @@
/// Replaces all instances of needle_type in baseturfs with replacement_type
/turf/proc/replace_baseturf(needle_type, replacement_type)
if (islist(baseturfs))
var/list/new_baseturfs
var/list/new_baseturfs = baseturfs.Copy()

while (TRUE)
var/found_index = baseturfs.Find(needle_type)
for(var/base_i in 1 to length(new_baseturfs))
var/found_index = new_baseturfs.Find(needle_type)
if (found_index == 0)
break

new_baseturfs ||= baseturfs.Copy()
new_baseturfs[found_index] = replacement_type

if (!isnull(new_baseturfs))
if (length(new_baseturfs))
baseturfs = baseturfs_string_list(new_baseturfs, src)
else if (baseturfs == needle_type)
baseturfs = replacement_type
Expand Down
7 changes: 7 additions & 0 deletions code/modules/uplink/uplink_items/stealthy_tools.dm
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@
cost = 1
surplus = 30

/datum/uplink_item/stealthy_tools/forensics_spofer
name = "Forensics Spoofing Kit"
desc = "A box that contains the forensics spoofer (and instructions) which can scan and replicate fingerprints and fibers \
and apply them to a target object. Helpful for framing crew. Recommend buying soap with your purchase."
item = /obj/item/storage/box/syndie_kit/forensics_spoofer
cost = 5

/datum/uplink_item/stealthy_tools/telecomm_blackout
name = "Disable Telecomms"
desc = "When purchased, a virus will be uploaded to the telecommunication processing servers to temporarily disable themselves."
Expand Down
4 changes: 4 additions & 0 deletions html/changelogs/AutoChangeLog-pr-88357.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
author: "mc-oofert"
delete-after: True
changes:
- rscadd: "forensics spoofing kit for traitors/whoever with an uplink"
4 changes: 4 additions & 0 deletions html/changelogs/AutoChangeLog-pr-88794.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
author: "Kylerace"
delete-after: True
changes:
- bugfix: "holodeck is slightly less likely to explode the server"
1 change: 1 addition & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -2403,6 +2403,7 @@
#include "code\game\objects\items\fireaxe.dm"
#include "code\game\objects\items\flamethrower.dm"
#include "code\game\objects\items\flatpacks.dm"
#include "code\game\objects\items\forensicsspoofer.dm"
#include "code\game\objects\items\frog_statue.dm"
#include "code\game\objects\items\gift.dm"
#include "code\game\objects\items\gun_maintenance.dm"
Expand Down
Loading

0 comments on commit 5f0f909

Please sign in to comment.