Files
VOREStation/code/modules/integrated_electronics/core/assemblies.dm
Cameron Lennox d5849910e5 Begin clickcode attack_self fix (#18797)
* Begin clickcode attack_self fix

Begins the work to make everything call back to parent for attack_self so that signals are sacred.

* Makes MORE things call the attack_self() parent

Yes, I could make special_handling a var on obj/item HOWEVER i want it to be specific so it can be tracked down later and ONLY the objects that use it can be refactored instead of sitting there literally forever and it just becoming 'a thing'.

* Finishes making the rest of attack_self call parent.

As mentioned, things such as 'specialty_goggles' 'special_handling' and the such are only there to help with attack_self until the attack_self is recoded for those items.

* begone foul demon

* some more cleanup

* These

* GOD this was annoying

* yeh

* Fix this

* fLARES

* Thesee too

* toys!

* Even more!

* More fixes

* Even more

* rest of em

* these too

* Update syndie.dm

* hardref clear

* Update code/game/gamemodes/nuclear/pinpointer.dm

* Update code/game/objects/effects/mines.dm

* Update code/game/objects/items/blueprints_vr.dm

* Update code/game/objects/items/blueprints_vr.dm

* Update code/game/objects/items/contraband_vr.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/gunbox.dm

* Update code/game/objects/items/gunbox.dm

* Update code/game/objects/items/gunbox_vr.dm

* Update code/game/objects/items/gunbox_vr.dm

* Update code/game/objects/items/weapons/gift_wrappaper.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/gunbox.dm

* these too

* Update maintpanel_stack.dm

* angry warning

* Fixes packaged snacks.

Fixes improper var default.

* Special handling for these

* proper poly types

* Fixes magclaws

Makes the 'features' it had just part  of base magboots that can be adjusted via varswap.

* Fixes jackets

Fixes https://github.com/VOREStation/VOREStation/issues/18941

* Small bugfix

Makes p_Theyre properly capitialize
Makes examine show proper wording

* Update gift_wrappaper.dm
2025-12-29 13:21:10 -05:00

492 lines
16 KiB
Plaintext

// Here is where the base definition lives.
// Specific subtypes are in their own folder.
/obj/item/electronic_assembly
name = "electronic assembly"
desc = "It's a case, for building small electronics with."
w_class = ITEMSIZE_SMALL
icon = 'icons/obj/integrated_electronics/electronic_setups.dmi'
icon_state = "setup_small"
show_messages = TRUE
var/max_components = IC_COMPONENTS_BASE
var/max_complexity = IC_COMPLEXITY_BASE
var/opened = FALSE
var/can_anchor = FALSE // If true, wrenching it will anchor it.
var/obj/item/cell/device/battery = null // Internal cell which most circuits need to work.
var/net_power = 0 // Set every tick, to display how much power is being drawn in total.
var/detail_color = COLOR_ASSEMBLY_BLACK
var/locked = FALSE // If true, the assembly cannot be opened with a crowbar
var/obj/item/card/id/locked_by = null // The ID that locked this assembly
var/obj/item/card/id/access_card = null // ID card for door access
var/list/component_positions = list() // Stores circuit positions as list of lists: list("ref" = ref, "x" = x, "y" = y)
/obj/item/electronic_assembly/Initialize(mapload)
battery = new(src)
START_PROCESSING(SSobj, src)
return ..()
/obj/item/electronic_assembly/Destroy()
STOP_PROCESSING(SSobj, src)
battery = null // It will be qdel'd by ..() if still in our contents
return ..()
/obj/item/electronic_assembly/process()
handle_idle_power()
/obj/item/electronic_assembly/proc/handle_idle_power()
net_power = 0 // Reset this. This gets increased/decreased with [give/draw]_power() outside of this loop.
// First we handle passive sources. Most of these make power so they go first.
for(var/obj/item/integrated_circuit/passive/power/P in contents)
P.handle_passive_energy()
// Now we handle idle power draw.
for(var/obj/item/integrated_circuit/IC in contents)
if(IC.power_draw_idle)
if(!draw_power(IC.power_draw_idle))
IC.power_fail()
/obj/item/electronic_assembly/proc/check_interactivity(mob/user)
return tgui_status(user, GLOB.tgui_physical_state) == STATUS_INTERACTIVE
/obj/item/electronic_assembly/get_cell()
return battery
// TGUI
/obj/item/electronic_assembly/tgui_state(mob/user)
return GLOB.tgui_physical_state
/obj/item/electronic_assembly/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ICAssembly", name, parent_ui)
ui.open()
/obj/item/electronic_assembly/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/simple/circuit_assets)
)
/obj/item/electronic_assembly/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
var/total_parts = 0
var/total_complexity = 0
for(var/obj/item/integrated_circuit/part in contents)
total_parts += part.size
total_complexity = total_complexity + part.complexity
data["total_parts"] = total_parts
data["max_components"] = max_components
data["total_complexity"] = total_complexity
data["max_complexity"] = max_complexity
data["battery_charge"] = round(battery?.charge, 0.1)
data["battery_max"] = round(battery?.maxcharge, 0.1)
data["net_power"] = net_power / CELLRATE
// Include export data - the UI component will handle displaying it if needed
data["export_data"] = serialize_electronic_assembly()
data["assembly_name"] = name
var/list/circuits = list()
for(var/obj/item/integrated_circuit/circuit in contents)
UNTYPED_LIST_ADD(circuits, circuit.tgui_data(user, ui, state))
data["circuits"] = circuits
// Include component positions for UI restoration
data["component_positions"] = component_positions
return data
/obj/item/electronic_assembly/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
switch(action)
if("export_circuit")
if(!LAZYLEN(contents))
to_chat(ui.user, span_warning("There's nothing in the [src] to export!"))
return TRUE
var/datum/tgui/window = new(ui.user, src, "ICExport", "Circuit Export")
window.open()
return TRUE
// Actual assembly actions
if("rename")
rename(ui.user)
return TRUE
if("remove_cell")
if(!battery)
to_chat(ui.user, span_warning("There's no power cell to remove from \the [src]."))
return FALSE
var/turf/T = get_turf(src)
battery.forceMove(T)
playsound(T, 'sound/items/Crowbar.ogg', 50, 1)
to_chat(ui.user, span_notice("You pull \the [battery] out of \the [src]'s power supplier."))
battery = null
return TRUE
// Circuit actions
if("wire_internal")
var/datum/integrated_io/pin1 = locate(params["pin1"])
if(!istype(pin1))
return
var/datum/integrated_io/pin2 = locate(params["pin2"])
if(!istype(pin2))
return
var/obj/item/integrated_circuit/holder1 = pin1.holder
if(!istype(holder1) || holder1.loc != src || holder1.assembly != src)
return
var/obj/item/integrated_circuit/holder2 = pin2.holder
if(!istype(holder2) || holder2.loc != src || holder2.assembly != src)
return
// Wiring the same pin will unwire it
if(pin2 in pin1.linked)
pin1.linked -= pin2
pin2.linked -= pin1
else
pin1.linked |= pin2
pin2.linked |= pin1
return TRUE
if("remove_all_wires")
var/datum/integrated_io/pin1 = locate(params["pin"])
if(!istype(pin1))
return
var/obj/item/integrated_circuit/holder1 = pin1.holder
if(!istype(holder1) || holder1.loc != src || holder1.assembly != src)
return
for(var/datum/integrated_io/other as anything in pin1.linked)
other.linked -= pin1
pin1.linked.Cut()
return TRUE
if("open_circuit")
var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents
if(!istype(C))
return
C.tgui_interact(ui.user, null, ui)
return TRUE
if("remove_circuit")
var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents
if(!istype(C))
return
C.remove(ui.user)
return TRUE
if("update_component_position")
var/obj/item/integrated_circuit/C = locate(params["ref"]) in contents
if(!istype(C))
return FALSE
var/new_x = params["x"]
var/new_y = params["y"]
if(!isnum(new_x) || !isnum(new_y))
return FALSE
// Find existing position entry or create new one
var/found = FALSE
for(var/list/pos_data in component_positions)
if(pos_data["ref"] == REF(C))
pos_data["x"] = new_x
pos_data["y"] = new_y
found = TRUE
break
if(!found)
UNTYPED_LIST_ADD(component_positions, list("ref" = REF(C), "x" = new_x, "y" = new_y))
return TRUE
return FALSE
// End TGUI
/obj/item/electronic_assembly/verb/rename()
set name = "Rename Circuit"
set category = "Object"
set desc = "Rename your circuit, useful to stay organized."
var/mob/M = usr
if(!check_interactivity(M))
return
var/input = sanitizeSafe(tgui_input_text(usr, "What do you want to name this?", "Rename", src.name, MAX_NAME_LEN, encode = FALSE), MAX_NAME_LEN)
if(src && input)
to_chat(M, span_notice("The machine now has a label reading '[input]'."))
name = input
/obj/item/electronic_assembly/proc/can_move()
return FALSE
/obj/item/electronic_assembly/update_icon()
if(opened)
icon_state = initial(icon_state) + "-open"
// else if(locked)
// For when I finish the locked assembly sprites.
// icon_state = initial(icon_state) // + "-locked" would be added once sprites are made
else
icon_state = initial(icon_state)
cut_overlays()
if(detail_color == COLOR_ASSEMBLY_BLACK) //Black colored overlay looks almost but not exactly like the base sprite, so just cut the overlay and avoid it looking kinda off.
return
var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/integrated_electronics/electronic_setups.dmi', "[icon_state]-color")
detail_overlay.color = detail_color
add_overlay(detail_overlay)
/obj/item/electronic_assembly/examine(mob/user)
. = ..()
if(Adjacent(user))
for(var/obj/item/integrated_circuit/IC in contents)
// Make sure there's actually examine text to prevent empty lines being printed for EVERY component!
var/examine_text = IC.external_examine(user)
if (length(examine_text))
. += examine_text
if(opened)
tgui_interact(user)
/obj/item/electronic_assembly/proc/get_part_complexity()
. = 0
for(var/obj/item/integrated_circuit/part in contents)
. += part.complexity
/obj/item/electronic_assembly/proc/get_part_size()
. = 0
for(var/obj/item/integrated_circuit/part in contents)
. += part.size
// Returns true if the circuit made it inside.
/obj/item/electronic_assembly/proc/add_circuit(var/obj/item/integrated_circuit/IC, var/mob/user)
if(!opened)
to_chat(user, span_warning("\The [src] isn't opened, so you can't put anything inside. Try using a crowbar."))
return FALSE
if(IC.w_class > src.w_class)
to_chat(user, span_warning("\The [IC] is way too big to fit into \the [src]."))
return FALSE
var/total_part_size = get_part_size()
var/total_complexity = get_part_complexity()
if((total_part_size + IC.size) > max_components)
to_chat(user, span_warning("You can't seem to add the '[IC.name]', as there's insufficient space."))
return FALSE
if((total_complexity + IC.complexity) > max_complexity)
to_chat(user, span_warning("You can't seem to add the '[IC.name]', since this setup's too complicated for the case."))
return FALSE
if(!IC.forceMove(src))
return FALSE
IC.assembly = src
return TRUE
// Non-interactive version of above that always succeeds, intended for build-in circuits that get added on assembly initialization.
/obj/item/electronic_assembly/proc/force_add_circuit(var/obj/item/integrated_circuit/IC)
IC.forceMove(src)
IC.assembly = src
/obj/item/electronic_assembly/afterattack(atom/target, mob/user, proximity)
var/scanned = FALSE
if(proximity)
// Existing sensor support
for(var/obj/item/integrated_circuit/input/sensor/S in contents)
if(S.scan(target))
scanned = TRUE
if(scanned)
visible_message(span_infoplain(span_bold("\The [user]") + " waves \the [src] around [target]."))
// Support for reference grabber + future ranged circuitry.
for(var/obj/item/integrated_circuit/input/reference_grabber/G in contents)
G.afterattack(target, user, proximity, null)
/obj/item/electronic_assembly/attackby(var/obj/item/I, var/mob/user)
if(can_anchor && I.has_tool_quality(TOOL_WRENCH))
anchored = !anchored
to_chat(user, span_notice("You've [anchored ? "" : "un"]secured \the [src] to \the [get_turf(src)]."))
if(anchored)
on_anchored()
else
on_unanchored()
playsound(src, I.usesound, 50, 1)
return TRUE
else if(istype(I, /obj/item/integrated_circuit))
if(!user.unEquip(I) && !isrobot(user)) //Robots cannot de-equip items in grippers.
return FALSE
if(add_circuit(I, user))
to_chat(user, span_notice("You slide \the [I] inside \the [src]."))
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
tgui_interact(user)
return TRUE
else if(I.has_tool_quality(TOOL_CROWBAR))
if(locked)
to_chat(user, span_warning("\The [src] is locked! You cannot open it with a crowbar."))
return FALSE
playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
opened = !opened
to_chat(user, span_notice("You [opened ? "opened" : "closed"] \the [src]."))
update_icon()
return TRUE
else if((istype(I, /obj/item/card/id) || istype(I, /obj/item/pda)) && !opened)
var/obj/item/card/id/id_card = null
if(istype(I, /obj/item/card/id))
id_card = I
else
var/obj/item/pda/pda = I
id_card = pda.id
if(!id_card)
to_chat(user, span_warning("You need an ID card to lock this assembly!"))
return FALSE
if(locked)
// Trying to unlock
if(locked_by && id_card.registered_name == locked_by.registered_name)
locked = FALSE
locked_by = null
to_chat(user, span_notice("You unlock \the [src]."))
update_icon()
else
to_chat(user, span_warning("Access denied. This assembly was locked by [locked_by ? locked_by.registered_name : "someone else"]."))
return TRUE
else
// Trying to lock
locked = TRUE
locked_by = id_card
to_chat(user, span_notice("You lock \the [src]. Now only your ID card can unlock it."))
update_icon()
return TRUE
else if(istype(I, /obj/item/integrated_electronics/wirer) || istype(I, /obj/item/integrated_electronics/debugger) || I.has_tool_quality(TOOL_SCREWDRIVER))
if(opened)
tgui_interact(user)
return TRUE
else
to_chat(user, span_warning("\The [src] isn't opened, so you can't fiddle with the internal components. \
Try using a crowbar."))
return FALSE
else if(istype(I, /obj/item/integrated_electronics/detailer))
var/obj/item/integrated_electronics/detailer/D = I
detail_color = D.detail_color
update_icon()
else if(istype(I, /obj/item/cell/device))
if(!opened)
to_chat(user, span_warning("\The [src] isn't opened, so you can't put anything inside. Try using a crowbar."))
return FALSE
if(battery)
to_chat(user, span_warning("\The [src] already has \a [battery] inside. Remove it first if you want to replace it."))
return FALSE
var/obj/item/cell/device/cell = I
user.drop_item(cell)
cell.forceMove(src)
battery = cell
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
to_chat(user, span_notice("You slot \the [cell] inside \the [src]'s power supplier."))
tgui_interact(user)
return TRUE
else
return ..()
/obj/item/electronic_assembly/attack_self(mob/user)
. = ..(user)
if(.)
return TRUE
if(!check_interactivity(user))
return
if(opened)
tgui_interact(user)
var/list/input_selection = list()
var/list/available_inputs = list()
for(var/obj/item/integrated_circuit/input/input in contents)
if(input.can_be_asked_input)
available_inputs.Add(input)
var/i = 0
for(var/obj/item/integrated_circuit/s in available_inputs)
if(s.name == input.name && s.displayed_name == input.displayed_name && s != input)
i++
var/disp_name= "[input.displayed_name] \[[input.name]\]"
if(i)
disp_name += " ([i+1])"
input_selection.Add(disp_name)
var/obj/item/integrated_circuit/input/choice
if(available_inputs)
var/selection = tgui_input_list(user, "What do you want to interact with?", "Interaction", input_selection)
if(selection)
var/index = input_selection.Find(selection)
choice = available_inputs[index]
if(choice)
choice.ask_for_input(user)
/obj/item/electronic_assembly/attack_robot(mob/user as mob)
if(Adjacent(user))
return attack_self(user)
else
return ..()
// Returns true if power was successfully drawn.
/obj/item/electronic_assembly/proc/draw_power(amount)
if(battery)
var/lost = battery.use(amount * CELLRATE)
net_power -= lost
return lost
return FALSE
// Ditto for giving.
/obj/item/electronic_assembly/proc/give_power(amount)
if(battery)
var/gained = battery.give(amount * CELLRATE)
net_power += gained
return TRUE
return FALSE
/obj/item/electronic_assembly/proc/on_anchored()
for(var/obj/item/integrated_circuit/IC in contents)
IC.on_anchored()
/obj/item/electronic_assembly/proc/on_unanchored()
for(var/obj/item/integrated_circuit/IC in contents)
IC.on_unanchored()
// Bump functionality, for pathfinding circuits. (Droid circuit assembly types)
/obj/item/electronic_assembly/Bump(atom/AM)
..()
if(can_move())
// Check if it's an airlock or windoor. (Prevents opening blast doors and shutters)
if(istype(AM, /obj/machinery/door/airlock) || istype(AM, /obj/machinery/door/window))
var/obj/machinery/door/D = AM
// Only open doors that we have access to
if(D.check_access(src))
D.open()
/obj/item/electronic_assembly/check_access(obj/item/I)
if(access_card)
return access_card.check_access(I)
return ..() // Fall back to default behavior if no access_card
// Returns TRUE if I is something that could/should have a valid interaction. Used to tell circuitclothes to hit the circuit with something instead of the clothes
/obj/item/electronic_assembly/proc/is_valid_tool(var/obj/item/I)
return I.has_tool_quality(TOOL_CROWBAR) || I.has_tool_quality(TOOL_SCREWDRIVER) || istype(I, /obj/item/integrated_circuit) || istype(I, /obj/item/cell/device) || istype(I, /obj/item/integrated_electronics)