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

217 lines
6.0 KiB
Plaintext

GLOBAL_DATUM(uplink, /datum/uplink)
/datum/uplink
var/list/items_assoc
var/list/datum/uplink_item/items
var/list/datum/uplink_category/categories
/datum/uplink/New()
items_assoc = list()
items = init_subtypes(/datum/uplink_item)
categories = init_subtypes(/datum/uplink_category)
sortTim(categories, GLOBAL_PROC_REF(cmp_uplink_category), FALSE)
for(var/datum/uplink_item/item in items)
if(!item.name)
items -= item
continue
items_assoc[item.type] = item
for(var/datum/uplink_category/category in categories)
if(item.category == category.type)
category.items += item
for(var/datum/uplink_category/category in categories)
sortTim(category.items, GLOBAL_PROC_REF(cmp_uplink_item), FALSE)
/datum/uplink_item
var/name
var/desc
/// A null telecrystal cost means that this item cannot be bought with bluecrystals.
var/telecrystal_cost
/// A null bluecrystal cost means that this item cannot be bought with bluecrystals.
var/bluecrystal_cost
/// How many times can this item be bought from an uplink (high limit is not shown in the uplink GUI).
var/item_limit = 999
/// Item category.
var/datum/uplink_category/category
/// Antag roles this item is displayed to. If empty, display to all.
var/list/datum/antagonist/antag_roles
/// Antag job this item is displayed to, if empty, display to all.
var/list/datum/antagonist/antag_job
/datum/uplink_item/item
var/path
/datum/uplink_item/proc/buy(var/obj/item/uplink/U, var/mob/user)
var/extra_args = extra_args(user)
if(!extra_args)
return
var/can_buy_telecrystals = can_buy_telecrystals(U)
var/can_buy_bluecrystals = can_buy_bluecrystals(U)
if(!can_buy_telecrystals && !can_buy_bluecrystals)
return
if(U.CanUseTopic(user, GLOB.inventory_state) != STATUS_INTERACTIVE)
return
var/goods = get_goods(U, get_turf(user), user, extra_args)
if(!goods)
log_admin("Bought item [name] for [user]'s uplink could not be obtained.")
return
var/cost
if(can_buy_bluecrystals)
cost = bluecrystal_cost(U.bluecrystals)
U.bluecrystals -= cost
U.used_bluecrystals += cost
else if(can_buy_telecrystals)
cost = telecrystal_cost(U.telecrystals)
U.telecrystals -= cost
U.used_telecrystals += cost
var/obj/item/implanter/implanter = goods
if(istype(implanter))
var/obj/item/implant/uplink/uplink_implant = implanter.imp
if(istype(uplink_implant))
var/obj/item/uplink/hidden/hidden_uplink = uplink_implant.hidden_uplink
if(istype(hidden_uplink))
hidden_uplink.purchase_log = U.purchase_log
purchase_log(U)
return goods
// Any additional arguments you wish to send to the get_goods
/datum/uplink_item/proc/extra_args(var/mob/user)
return 1
/datum/uplink_item/proc/can_buy_telecrystals(obj/item/uplink/U)
if(isnull(telecrystal_cost))
return FALSE
if(telecrystal_cost(U.telecrystals) > U.telecrystals)
return FALSE
if(items_left(U) <= 0)
return FALSE
return can_view(U)
/datum/uplink_item/proc/can_buy_bluecrystals(obj/item/uplink/U)
if(isnull(bluecrystal_cost))
return FALSE
if(bluecrystal_cost(U.bluecrystals) > U.bluecrystals)
return FALSE
if(items_left(U) <= 0)
return FALSE
return can_view(U)
/datum/uplink_item/proc/items_left(obj/item/uplink/U)
return item_limit - U.purchase_log[src]
/datum/uplink_item/proc/can_view(obj/item/uplink/U)
// Making the assumption that if no uplink was supplied, then we don't care about antag roles
if(!U || (!length(antag_roles) && !antag_job))
return TRUE
// With no owner, there's no need to check antag status.
if(!U.uplink_owner)
return FALSE
for(var/antag_role in antag_roles)
var/datum/antagonist/antag = GLOB.all_antag_types[antag_role]
if(antag.is_antagonist(U.uplink_owner))
return TRUE
if (antag_job == U.uplink_owner.assigned_role) //for a quick and easy list of the assigned_role, look in specialty.dm
return TRUE
return FALSE
/datum/uplink_item/proc/telecrystal_cost(var/telecrystals)
return telecrystal_cost
/datum/uplink_item/proc/bluecrystal_cost(var/bluecrystals)
return bluecrystal_cost
/datum/uplink_item/proc/description()
return desc
// get_goods does not necessarily return physical objects, it is simply a way to acquire the uplink item without paying
/datum/uplink_item/proc/get_goods(var/obj/item/uplink/U, var/loc)
return FALSE
/datum/uplink_item/proc/log_icon()
return
/datum/uplink_item/proc/purchase_log(obj/item/uplink/U)
feedback_add_details("traitor_uplink_items_bought", "[src]")
log_and_message_admins("used \the [U.loc] to buy \a [src]")
U.purchase_log[src] = U.purchase_log[src] + 1
/********************************
* *
* Physical Uplink Entries *
* *
********************************/
/datum/uplink_item/item/buy(var/obj/item/uplink/U, var/mob/user)
var/obj/item/I = ..()
if(!I)
return
if(istype(I, /list))
var/list/L = I
if(L.len) I = L[1]
if(istype(I) && ishuman(user))
var/mob/living/carbon/human/A = user
A.put_in_any_hand_if_possible(I)
return I
/datum/uplink_item/item/get_goods(var/obj/item/uplink/U, var/loc)
var/obj/I = new path(loc)
return I
/datum/uplink_item/item/description()
if(!desc)
// Fallback description
var/obj/temp = src.path
desc = initial(temp.desc)
return ..()
/datum/uplink_item/item/log_icon()
var/obj/I = path
return "[icon2html(I, usr)]"
/********************************
* *
* Abstract Uplink Entries *
* *
********************************/
/datum/uplink_item/abstract
var/static/image/default_abstract_uplink_icon
/datum/uplink_item/abstract/log_icon()
if(!default_abstract_uplink_icon)
default_abstract_uplink_icon = image('icons/obj/modular_computers/pda.dmi', "pda-syn")
return "[icon2html(default_abstract_uplink_icon, usr)]"
/****************
* Support procs *
****************/
/proc/get_random_uplink_items(var/obj/item/uplink/U, var/remaining_TC, var/loc)
var/list/bought_items = list()
while(remaining_TC)
var/datum/uplink_item/I = GLOB.default_uplink_selection.get_random_item(remaining_TC, U, bought_items)
if(!I)
break
bought_items += I
remaining_TC -= I.telecrystal_cost(remaining_TC)
return bought_items