Files
GhomandGitHub 6eb2d1674a Implementing materials checks for techweb designs. (#96257)
## About The Pull Request
In similar fashion to what was done with crafting recipes last year,
this year it's time for techweb designs and printed items to be audited.
This is mostly just about consistency, we've a lot of items that can be
printed by protolathes, autolathes, circuit printers and techfab etc.
However they almost all (except most stacks, mainly) have custom
materials that do not match in one way or another with the materials
used by the design, which is what this PR is for.

"But items printed from lathes etc. already get the mats used to make
them." Yes, they do, however that isn't the case for items of the same
type that were spawned in some other way (cargo shuttle, space/maints
loot, mapped, admins), this create a subtle discrepancy. It isn't a huge
deal (in spite of the size of this PR, ton of designs), but given that I
have done something similar with crafting recipes before, I may as well
give it a second arc of some sort and bring things to completion. And
fix a few possible oversights.

TL;DR consistency and stuff

## Why It's Good For The Game
Consistency, unit test checks to make it harder not to be consistent in
the future. Still has a few TODOs like:

- [x] Fixed newly printed, fully charged RCDs costing less than the RCD
cartridges required to fully charge one. EDIT: I had to tweak the newly
added RDD as well because it suffered from the same fundamental issue.
- [x] Fixed plates being made of iron and yet shattering like ceramic
ones. A new subtype for metallic ones has been made.

## Changelog

🆑
refactor: Refactored a few things with techweb designs (the ones for
autolathes, protolathes, circuit printers, mechfabs etc.) to make sure
that the materials of items that can be made from these designs more
closely match the materials used to make them.
fix: Lizard fries no longer need a plate to be made, like all other
treats that used to require plates in a distant past.
balance: Tweaked the materials cost of RCD, RDD and RCD cartridges.
image: Oven trays now have a more metallic hue.
balance: Plates printed printed from lathes won't shatter like ceramic
ones, in virtue of them being made out of iron instead.
/🆑
2026-07-06 00:13:02 -07:00

267 lines
8.8 KiB
Plaintext

//This represents the amount of materials (both iron and glass that the max_amount of cable would amount to
#define MAX_CABLE_AMOUNT (SMALL_MATERIAL_AMOUNT * 0.1 * /obj/item/rwd/loaded::max_amount)
/obj/item/rwd
name = "rapid wiring device"
desc = "A device used to rapidly lay cable & pick up stray cable pieces laying around."
icon = 'icons/obj/tools.dmi'
icon_state = "rcl-0"
inhand_icon_state = "rcl-0"
opacity = FALSE
force = 5 //Plastic is soft
throwforce = 5
throw_speed = 1
throw_range = 7
w_class = WEIGHT_CLASS_NORMAL
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5 - MAX_CABLE_AMOUNT, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5 - MAX_CABLE_AMOUNT)
/// maximum amount of cable this device can hold
var/max_amount = 210
/// current amount of cable in the machine
var/current_amount = 0
/// are we dual wielding this machine
var/active = FALSE
/// the player currently holding this device.
var/mob/listeningTo
/// what layer of cable are we working with
var/cable_layer = CABLE_LAYER_2
/// cached reference of the cable used in the device
var/obj/item/stack/cable_coil/cable
/// radial menu to select cable layer
var/list/radial_menu = null
#undef MAX_CABLE_AMOUNT
/obj/item/rwd/Initialize(mapload)
. = ..()
AddComponent(/datum/component/two_handed, wield_callback = CALLBACK(src, PROC_REF(on_wield)), unwield_callback = CALLBACK(src, PROC_REF(on_unwield)))
update_appearance(UPDATE_ICON_STATE)
/obj/item/rwd/Destroy(force)
. = ..()
if(!QDELETED(cable))
QDEL_NULL(cable)
/obj/item/rwd/examine(mob/user)
. = ..()
. += "Dual wield & walk over floors to lay cable."
. += "It has [current_amount] pieces remaining."
. += "Right click on it to dispense a custom amount of cable."
. += "Alt click to change cable layer."
/obj/item/rwd/update_icon_state()
switch(current_amount)
if(61 to INFINITY)
icon_state = "rwd-30-layer[cable_layer]"
inhand_icon_state = "rwd-layer[cable_layer]"
if(31 to 60)
icon_state = "rwd-20-layer[cable_layer]"
inhand_icon_state = "rwd-layer[cable_layer]"
if(1 to 30)
icon_state = "rwd-10-layer[cable_layer]"
inhand_icon_state = "rwd-layer[cable_layer]"
else
icon_state = "rcl-0"
inhand_icon_state = "rcl-0"
return ..()
/obj/item/rwd/attack_self_secondary(mob/user, modifiers)
if(current_amount <= 0)
balloon_alert(user, "nothing to dispense!")
return
var/amount = tgui_input_number(user = user, message = "Enter amount to dispense", title = "Custom cable", default = 0, max_value = min(30, current_amount), min_value = min(1, current_amount), timeout = 0, round_value = TRUE)
if(isnull(amount) || amount > current_amount)
return
/**
* if the user say requested 22 pieces but the cached cable reference has only 5 pieces then it wont be an exact multiple
* So the while loop runs twice i.e 1st iteration it uses 5 pieces and it has 22-5= 17 pieces left to consume from this device
* Finally 2nd iteration it creates a new cached cable & consumes the remaining 17 pieces and now the device will use the cached cable containing 30-17 = 30 pieces.
*/
var/amount_to_consume = amount
while(amount_to_consume)
var/obj/item/stack/cable_coil/the_cable = get_cable()
if(!the_cable)
return
var/consumed = min(amount_to_consume, the_cable.amount)
if(!the_cable.use(consumed))
return
delta_cable(consumed, decrement = TRUE)
amount_to_consume -= consumed
//spawn the cable. if it merged with the stak below then you pick that up else put it in the user's hand
var/obj/item/stack/cable_coil/new_cable = new(user.drop_location(), amount)
if(QDELETED(new_cable))
balloon_alert(user, "merged with stack below!")
else
user.put_in_active_hand(modify_cable(new_cable))
update_appearance(UPDATE_ICON_STATE)
/// triggered on wield of two handed item
/obj/item/rwd/proc/on_wield(obj/item/source, mob/user)
active = TRUE
/// triggered on unwield of two handed item
/obj/item/rwd/proc/on_unwield(obj/item/source, mob/user)
active = FALSE
/obj/item/rwd/pickup(mob/to_hook)
. = ..()
if(listeningTo == to_hook)
return .
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
RegisterSignal(to_hook, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
listeningTo = to_hook
/obj/item/rwd/dropped(mob/wearer)
. = ..()
UnregisterSignal(wearer, COMSIG_MOVABLE_MOVED)
listeningTo = null
/// for inserting cable into the rwd
/obj/item/rwd/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/stack/cable_coil))
return NONE
add_cable(user, tool)
return ITEM_INTERACT_SUCCESS
/obj/item/rwd/click_alt(mob/user)
if(!radial_menu)
radial_menu = list(
"Layer 1" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-red"),
"Layer 2" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-yellow"),
"Layer 3" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-blue"),
)
var/layer_result = show_radial_menu(user, src, radial_menu, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE)
if(!check_menu(user))
return CLICK_ACTION_BLOCKING
switch(layer_result)
if("Layer 1")
cable_layer = CABLE_LAYER_1
if("Layer 2")
cable_layer = CABLE_LAYER_2
if("Layer 3")
cable_layer = CABLE_LAYER_3
update_appearance(UPDATE_ICON_STATE)
return CLICK_ACTION_SUCCESS
/obj/item/rwd/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
if(!ISADVANCEDTOOLUSER(user))
to_chat(user, span_warning("You don't have the dexterity to do this!"))
return FALSE
if(user.incapacitated || !user.Adjacent(src))
return FALSE
return TRUE
/// insert cable into the rwd
/obj/item/rwd/proc/add_cable(mob/user, obj/item/stack/cable_coil/cable)
if(current_amount == max_amount)
balloon_alert(user, "device is full!")
return
var/insert_amount = min(cable.amount, max_amount - current_amount)
if(!cable.use(insert_amount))
return
delta_cable(insert_amount, decrement = FALSE)
update_appearance(UPDATE_ICON_STATE)
balloon_alert(user, "inserted [insert_amount] cable")
/// modify cable properties according to its layer
/obj/item/rwd/proc/modify_cable(obj/item/stack/cable_coil/target_cable)
switch(cable_layer)
if(CABLE_LAYER_1)
target_cable.set_cable_color(CABLE_COLOR_RED)
target_cable.target_type = /obj/structure/cable/layer1
target_cable.target_layer = CABLE_LAYER_1
if(CABLE_LAYER_2)
target_cable.set_cable_color(CABLE_COLOR_YELLOW)
target_cable.target_type = /obj/structure/cable
target_cable.target_layer = CABLE_LAYER_2
else
target_cable.set_cable_color(CABLE_COLOR_BLUE)
target_cable.target_type = /obj/structure/cable/layer3
target_cable.target_layer = CABLE_LAYER_3
return target_cable
/// get cached reference of cable which gets used over time
/obj/item/rwd/proc/get_cable()
if(QDELETED(cable))
var/create_amount = min(30, current_amount)
if(create_amount <= 0)
return null
cable = new/obj/item/stack/cable_coil(src, create_amount)
return modify_cable(cable)
/// check if the turf has the same cable layer as this design. If it does don't put cable here
/obj/item/rwd/proc/cable_allowed_here(turf/the_turf)
// infer our intended cable design from the layer
var/obj/structure/cable/design_type
switch(cable_layer)
if(CABLE_LAYER_1)
design_type = /obj/structure/cable/layer1
if(CABLE_LAYER_2)
design_type = /obj/structure/cable
else
design_type = /obj/structure/cable/layer3
for(var/obj/structure/cable/cable as anything in the_turf)
// cable layer on the turf is the same as our intended design layer so nope
if(cable.type == design_type)
return FALSE
return TRUE
/// extra safe modify just to be sure
/obj/item/rwd/proc/delta_cable(amount, decrement)
if(decrement)
current_amount -= amount
else
current_amount += amount
current_amount = clamp(current_amount, 0, max_amount)
/// stuff to do when moving
/obj/item/rwd/proc/on_move(mob/user)
SIGNAL_HANDLER
if(!isturf(user.loc))
return
var/turf/the_turf = user.loc
/**
* Lay cable only if
* - device is active
* - the turf can hold cable
* - there is no cable on the turf or there is cable on the turf but its not the same layer we are gonna put on the turf
*/
if(active && the_turf.can_have_cabling() && the_turf.can_lay_cable() && cable_allowed_here(the_turf))
var/obj/item/stack/cable_coil/coil = get_cable()
if(!coil)
return
coil.place_turf(the_turf, user)
delta_cable(1, decrement = TRUE)
update_appearance(UPDATE_ICON_STATE)
// pick up any stray cable pieces lying on the floor
for(var/obj/item/stack/cable_coil/cable_piece in the_turf)
add_cable(user, cable_piece)
/obj/item/rwd/loaded
icon_state = "rwd-30-layer2"
current_amount = 210
/obj/item/rwd/admin
name = "admin RWD"
max_amount = INFINITY
current_amount = INFINITY