Files
Batrachophreno 8111a8489b Restore many missing inhand sprites for items, add/port others (#21831)
Many, many, many items have inhand sprites in their .dmis but for
whatever reasons do not display them in-game. This PR:

1. Updates many item definitions to point to their already-existing
inhands correctly. This consists largely of held tools, but also gas
tanks and jetpacks mounted in the suit storage slot.

2. Adds a few codersprites made by me for objects with either missing
inhands or poorly matching mishands (IE, the tape recorder, which has a
black case, reused the white inhand sprites of the health analyzer). The
new sprites are modified or recolored variations of other inhand sprites
from our repo, except for circuitboards which are new.
<img width="444" height="400" alt="image"
src="https://github.com/user-attachments/assets/7f107b9a-fe24-4e31-8f16-4d34768ee117"
/>


3. Adds inhand sprites for Inflatables and Inflatable Boxes made by
Tomixcomics.
<img width="424" height="101" alt="image"
src="https://github.com/user-attachments/assets/434107c4-8577-49a2-a58e-d6b014c03933"
/>

4. Ports inhand sprites for the Hydraulic Rescue Tool from tg's Jaws of
Life.
<img width="224" height="94" alt="Screenshot 2026-02-07 172931"
src="https://github.com/user-attachments/assets/070c7956-f6a8-4fb5-870f-10c64afcc8b3"
/>

5. Some additional cleanup while in the area. The 'analyzer' has been
renamed the 'gas analyzer' to be consistent with the other analyzer
objects, standardized icon_state naming conventions where I saw
oddballs, updated code docs to use DMDocs when in the area, etc.

### Asset Licenses
The following assets that **have not** been created by myself are
included in this PR:

| Path | Original Author | License |
| --- | --- | --- |
| icons/obj/item/hydraulic_rescue_tool.dmi | [SomeAngryMiner (bee
station)](https://github.com/BeeStation/BeeStation-Hornet/pull/2487),
[maxymax
(/tg/station)](https://github.com/tgstation/tgstation/pull/58616) |
CC-BY-SA |
| icons/obj/item/inflatables.dmi |
[Tomixcomics](https://github.com/tomixcomics) | CC-BY-SA |
2026-02-17 17:51:22 +00:00

161 lines
4.4 KiB
Plaintext

#define OVERLAY_CACHE_LEN 50
/obj/item/t_scanner
name = "\improper T-ray scanner"
desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes."
icon = 'icons/obj/item/scanner.dmi'
icon_state = "t-ray0"
item_state = "t-ray"
contained_sprite = TRUE
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
matter = list(MATERIAL_PLASTIC = 100, MATERIAL_ALUMINIUM = 50)
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
action_button_name = "Toggle T-Ray scanner"
var/scan_range = 3
var/on = 0
/// Assoc list of objects being scanned, mapped to their overlay.
var/list/active_scanned = list()
/// Since making sure overlays are properly added and removed is pretty important, so we track the current user explicitly.
var/client/user_client
/// Cache recent overlays.
var/global/list/overlay_cache = list()
/obj/item/t_scanner/Destroy()
. = ..()
if(on)
set_active(FALSE)
/obj/item/t_scanner/update_icon()
icon_state = "t-ray[on]"
/obj/item/t_scanner/emp_act(severity)
. = ..()
audible_message(src, SPAN_NOTICE("\The [src] buzzes oddly."))
set_active(FALSE)
/obj/item/t_scanner/attack_self(mob/user)
set_active(!on)
user.update_action_buttons()
/obj/item/t_scanner/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
var/obj/structure/disposalpipe/D = target
if(istype(D))
to_chat(user, SPAN_INFO("Pipe segment integrity: [(D.health / 10) * 100]%"))
/obj/item/t_scanner/proc/set_active(var/active)
on = active
if(on)
START_PROCESSING(SSprocessing, src)
else
STOP_PROCESSING(SSprocessing, src)
set_user_client(null)
update_icon()
/// If reset is set, then assume the client has none of our overlays, otherwise we only send new overlays.
/obj/item/t_scanner/process()
if(!on)
return
//handle clients changing
var/client/loc_client = null
if(ismob(loc))
var/mob/M = loc
loc_client = M.client
set_user_client(loc_client)
//no sense processing if no-one is going to see it.
if(!user_client)
return
//get all objects in scan range
var/list/scanned = get_scanned_objects(scan_range)
var/list/update_add = scanned - active_scanned
var/list/update_remove = active_scanned - scanned
//Add new overlays
for(var/thing in update_add)
var/obj/O = thing
var/image/overlay = get_overlay(O)
active_scanned[O] = overlay
user_client.images += overlay
//Remove stale overlays
for(var/thing in update_remove)
var/obj/O = thing
user_client.images -= active_scanned[O]
active_scanned -= O
/// Creates a new overlay for a scanned object
/obj/item/t_scanner/proc/get_overlay(obj/scanned)
//Use a cache so we don't create a whole bunch of new images just because someone's walking back and forth in a room.
//Also means that images are reused if multiple people are using t-rays to look at the same objects.
if(scanned in overlay_cache)
. = overlay_cache[scanned]
else
var/image/I = image(scanned.icon, scanned.loc, scanned.icon_state, UNDER_HUD_LAYER, scanned.dir)
I.plane = HUD_PLANE
//Pipes are special
if(istype(scanned, /obj/machinery/atmospherics/pipe))
var/obj/machinery/atmospherics/pipe/P = scanned
I.color = P.pipe_color
I.overlays += P.overlays
I.underlays += P.underlays
else if(istype(scanned, /obj/structure/cable))
var/obj/structure/cable/C = scanned
I.color = C.color
I.alpha = 100
I.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
. = I
// Add it to cache, cutting old entries if the list is too long
overlay_cache[scanned] = .
if(overlay_cache.len > OVERLAY_CACHE_LEN)
overlay_cache.Cut(1, overlay_cache.len-OVERLAY_CACHE_LEN-1)
/obj/item/t_scanner/proc/get_scanned_objects(var/scan_dist)
. = list()
var/turf/center = get_turf(src.loc)
if(!center)
return
for(var/thing in RANGE_TURFS(scan_range, center))
var/turf/T = thing
if(!!T.is_plating())
continue
for(var/obj/O in T.contents)
if(O.level != 1)
continue
if(!O.invisibility)
continue //if it's already visible don't need an overlay for it
. += O
/obj/item/t_scanner/proc/set_user_client(var/client/new_client)
if(new_client == user_client)
return
if(user_client)
for(var/scanned in active_scanned)
user_client.images -= active_scanned[scanned]
if(new_client)
for(var/scanned in active_scanned)
new_client.images += active_scanned[scanned]
else
active_scanned.Cut()
user_client = new_client
/obj/item/t_scanner/dropped(mob/user)
set_user_client(null)
..()
#undef OVERLAY_CACHE_LEN