mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-09 16:12:17 +00:00
555 lines
17 KiB
Plaintext
555 lines
17 KiB
Plaintext
/*
|
|
Overview:
|
|
Used to create objects that need a per step proc call. Default definition of 'New()'
|
|
stores a reference to src machine in global 'machines list'. Default definition
|
|
of 'Del' removes reference to src machine in global 'machines list'.
|
|
|
|
Class Variables:
|
|
|
|
power_init_complete (boolean)
|
|
Indicates that we have have registered our static power usage with the area.
|
|
|
|
use_power (num)
|
|
current state of auto power use.
|
|
Possible Values:
|
|
USE_POWER_OFF:0 -- no auto power use
|
|
USE_POWER_IDLE:1 -- machine is using power at its idle power level
|
|
USE_POWER_ACTIVE:2 -- machine is using power at its active power level
|
|
|
|
active_power_usage (num)
|
|
Value for the amount of power to use when in active power mode
|
|
|
|
idle_power_usage (num)
|
|
Value for the amount of power to use when in idle power mode
|
|
|
|
power_channel (num)
|
|
What channel to draw from when drawing power for power mode
|
|
Possible Values:
|
|
EQUIP:0 -- Equipment Channel
|
|
LIGHT:2 -- Lighting Channel
|
|
ENVIRON:3 -- Environment Channel
|
|
|
|
component_parts (list)
|
|
A list of component parts of machine used by frame based machines.
|
|
|
|
panel_open (num)
|
|
Whether the panel is open
|
|
|
|
uid (num)
|
|
Unique id of machine across all machines.
|
|
|
|
gl_uid (global num)
|
|
Next uid value in sequence
|
|
|
|
stat (bitflag)
|
|
Machine status bit flags.
|
|
Possible bit flags:
|
|
BROKEN:1 -- Machine is broken
|
|
NOPOWER:2 -- No power is being supplied to machine.
|
|
POWEROFF:4 -- tbd
|
|
MAINT:8 -- machine is currently under going maintenance.
|
|
EMPED:16 -- temporary broken by EMP pulse
|
|
|
|
Class Procs:
|
|
Initialize(mapload) 'game/machinery/machine.dm'
|
|
|
|
Destroy() 'game/machinery/machine.dm'
|
|
|
|
get_power_usage() 'game/machinery/machinery_power.dm'
|
|
Returns the amount of power this machine uses every SSmachines cycle.
|
|
Default definition uses 'use_power', 'active_power_usage', 'idle_power_usage'
|
|
|
|
powered(chan = CURRENT_CHANNEL) 'game/machinery/machinery_power.dm'
|
|
Checks to see if area that contains the object has power available for power
|
|
channel given in 'chan'.
|
|
|
|
use_power_oneoff(amount, chan=CURRENT_CHANNEL) 'game/machinery/machinery_power.dm'
|
|
Deducts 'amount' from the power channel 'chan' of the area that contains the object.
|
|
|
|
power_change() 'game/machinery/machinery_power.dm'
|
|
Called by the area that contains the object when ever that area under goes a
|
|
power state change (area runs out of power, or area channel is turned off).
|
|
|
|
RefreshParts() 'game/machinery/machine.dm'
|
|
Called to refresh the variables in the machine that are contributed to by parts
|
|
contained in the component_parts list. (example: glass and material amounts for
|
|
the autolathe)
|
|
|
|
Default definition does nothing.
|
|
|
|
assign_uid() 'game/machinery/machine.dm'
|
|
Called by machine to assign a value to the uid variable.
|
|
|
|
process() 'game/machinery/machine.dm'
|
|
Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list.
|
|
|
|
|
|
Compiled by Aygar
|
|
*/
|
|
|
|
/obj/machinery
|
|
name = "machinery"
|
|
icon = 'icons/obj/stationobjs.dmi'
|
|
w_class = ITEMSIZE_NO_CONTAINER
|
|
layer = UNDER_JUNK_LAYER
|
|
|
|
var/stat = 0
|
|
var/emagged = 0
|
|
var/use_power = USE_POWER_IDLE
|
|
//0 = dont run the auto
|
|
//1 = run auto, use idle
|
|
//2 = run auto, use active
|
|
var/idle_power_usage = 0
|
|
var/active_power_usage = 0
|
|
var/power_channel = EQUIP //EQUIP, ENVIRON or LIGHT
|
|
var/power_init_complete = FALSE
|
|
var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames.
|
|
var/uid
|
|
var/panel_open = FALSE
|
|
var/global/gl_uid = 1
|
|
var/clicksound // sound played on succesful interface. Just put it in the list of vars at the start.
|
|
var/clickvol = 40 // volume
|
|
var/interact_offline = 0 // Can the machine be interacted with while de-powered.
|
|
var/obj/item/circuitboard/circuit = null
|
|
|
|
// 0.0 - 1.0 multipler for prob() based on bullet structure damage
|
|
// So if this is 1.0 then a 100 damage bullet will always break this structure
|
|
// If this is 0.5 then a 50 damage bullet will break this structure 25% of the time
|
|
var/bullet_vulnerability = 0.25
|
|
|
|
var/speed_process = FALSE //If false, SSmachines. If true, SSfastprocess.
|
|
|
|
blocks_emissive = EMISSIVE_BLOCK_GENERIC
|
|
|
|
/obj/machinery/Initialize(mapload, d=0)
|
|
. = ..()
|
|
if(isnum(d))
|
|
set_dir(d)
|
|
SSmachines.all_machines += src
|
|
if(ispath(circuit))
|
|
circuit = new circuit(src)
|
|
if(!speed_process)
|
|
START_MACHINE_PROCESSING(src)
|
|
else
|
|
START_PROCESSING(SSfastprocess, src)
|
|
if(!mapload)
|
|
power_change()
|
|
|
|
/obj/machinery/Destroy()
|
|
if(!speed_process)
|
|
STOP_MACHINE_PROCESSING(src)
|
|
else
|
|
STOP_PROCESSING(SSfastprocess, src)
|
|
SSmachines.all_machines -= src
|
|
if(component_parts)
|
|
for(var/atom/A in component_parts)
|
|
if(A.loc == src) // If the components are inside the machine, delete them.
|
|
qdel(A)
|
|
else // Otherwise we assume they were dropped to the ground during deconstruction, and were not removed from the component_parts list by deconstruction code.
|
|
WARNING("[A] was still in [src]'s component_parts when it was Destroy()'d")
|
|
component_parts.Cut()
|
|
component_parts = null
|
|
if(contents) // The same for contents.
|
|
for(var/atom/A in contents)
|
|
if(ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
H.forceMove(loc)
|
|
H.reset_perspective()
|
|
else
|
|
qdel(A)
|
|
return ..()
|
|
|
|
/obj/machinery/process() // Steady power usage is handled separately. If you dont use process why are you here?
|
|
return PROCESS_KILL
|
|
|
|
/obj/machinery/emp_act(severity, recursive)
|
|
if(use_power && stat == 0)
|
|
use_power(7500/severity)
|
|
|
|
var/obj/effect/overlay/pulse2 = new /obj/effect/overlay(src.loc)
|
|
pulse2.icon = 'icons/effects/effects.dmi'
|
|
pulse2.icon_state = "empdisable"
|
|
pulse2.name = "emp sparks"
|
|
pulse2.anchored = TRUE
|
|
pulse2.set_dir(pick(GLOB.cardinal))
|
|
QDEL_IN(pulse2, 1 SECOND)
|
|
..()
|
|
|
|
/obj/machinery/ex_act(severity)
|
|
switch(severity)
|
|
if(1.0)
|
|
fall_apart(severity)
|
|
return
|
|
if(2.0)
|
|
if(prob(50))
|
|
fall_apart(severity)
|
|
return
|
|
if(3.0)
|
|
if(prob(25))
|
|
fall_apart(severity)
|
|
return
|
|
return
|
|
|
|
/obj/machinery/vv_edit_var(var/var_name, var/new_value)
|
|
if(var_name == NAMEOF(src, use_power))
|
|
update_use_power(new_value)
|
|
return TRUE
|
|
else if(var_name == NAMEOF(src, power_channel))
|
|
update_power_channel(new_value)
|
|
return TRUE
|
|
else if(var_name == NAMEOF(src, idle_power_usage))
|
|
update_idle_power_usage(new_value)
|
|
return TRUE
|
|
else if(var_name == NAMEOF(src, active_power_usage))
|
|
update_active_power_usage(new_value)
|
|
return TRUE
|
|
return ..()
|
|
|
|
/obj/machinery/proc/operable(var/additional_flags = 0)
|
|
return !inoperable(additional_flags)
|
|
|
|
/obj/machinery/proc/inoperable(var/additional_flags = 0)
|
|
return (stat & (NOPOWER | BROKEN | additional_flags))
|
|
|
|
// Duplicate of below because we don't want to fuck around with CanUseTopic in TGUI
|
|
// TODO: Replace this with can_interact from /tg/
|
|
/obj/machinery/tgui_status(mob/user)
|
|
if(!interact_offline && (stat & (NOPOWER | BROKEN)))
|
|
return STATUS_CLOSE
|
|
return ..()
|
|
|
|
/obj/machinery/CanUseTopic(var/mob/user)
|
|
if(!interact_offline && (stat & (NOPOWER | BROKEN)))
|
|
return STATUS_CLOSE
|
|
return ..()
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/obj/machinery/attack_ai(mob/user as mob)
|
|
if(isrobot(user))
|
|
// For some reason attack_robot doesn't work
|
|
// This is to stop robots from using cameras to remotely control machines.
|
|
if(user.client && !user.is_remote_viewing())
|
|
return attack_hand(user)
|
|
else
|
|
return attack_hand(user)
|
|
|
|
/obj/machinery/attack_hand(mob/user as mob)
|
|
|
|
if(inoperable(MAINT))
|
|
return 1
|
|
if(user.lying || user.stat)
|
|
return 1
|
|
if(!user.IsAdvancedToolUser()) //Vorestation edit
|
|
to_chat(user, span_warning("You don't have the dexterity to do this!"))
|
|
return 1
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
if(H.getBrainLoss() >= 55)
|
|
visible_message(span_warning("[H] stares cluelessly at [src]."))
|
|
return 1
|
|
else if(prob(H.getBrainLoss()))
|
|
to_chat(user, span_warning("You momentarily forget how to use [src]."))
|
|
return 1
|
|
|
|
if(clicksound && istype(user, /mob/living/carbon))
|
|
playsound(src, clicksound, clickvol)
|
|
|
|
add_fingerprint(user)
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames.
|
|
return
|
|
|
|
/obj/machinery/proc/assign_uid()
|
|
uid = gl_uid
|
|
gl_uid++
|
|
|
|
/obj/machinery/proc/state(var/msg)
|
|
for(var/mob/O in hearers(src, null))
|
|
O.show_message("[icon2html(src,O.client)] " + span_notice("[msg]"), 2)
|
|
|
|
/obj/machinery/proc/ping(text=null)
|
|
if(!text)
|
|
text = "\The [src] pings."
|
|
|
|
state(text, "blue")
|
|
playsound(src, 'sound/machines/ping.ogg', 50, 0)
|
|
|
|
/obj/machinery/proc/shock(mob/user, prb)
|
|
if(inoperable())
|
|
return 0
|
|
if(!prob(prb))
|
|
return 0
|
|
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
|
s.set_up(5, 1, src)
|
|
s.start()
|
|
if(electrocute_mob(user, get_area(src), src, 0.7))
|
|
var/area/temp_area = get_area(src)
|
|
if(temp_area)
|
|
var/obj/machinery/power/apc/temp_apc = temp_area.get_apc()
|
|
|
|
if(temp_apc && temp_apc.terminal && temp_apc.terminal.powernet)
|
|
temp_apc.terminal.powernet.trigger_warning()
|
|
if(user.stunned)
|
|
return 1
|
|
return 0
|
|
|
|
/obj/machinery/proc/default_apply_parts()
|
|
var/obj/item/circuitboard/CB = circuit
|
|
if(!istype(CB))
|
|
return
|
|
CB.apply_default_parts(src)
|
|
RefreshParts()
|
|
|
|
/obj/machinery/proc/default_use_hicell()
|
|
var/obj/item/cell/C = locate(/obj/item/cell) in component_parts
|
|
if(C)
|
|
component_parts -= C
|
|
qdel(C)
|
|
C = new /obj/item/cell/high(src)
|
|
component_parts += C
|
|
RefreshParts()
|
|
return C
|
|
|
|
/obj/machinery/proc/default_part_replacement(var/mob/user, var/obj/item/storage/part_replacer/R)
|
|
var/parts_replaced = FALSE
|
|
if(!istype(R))
|
|
return 0
|
|
if(!component_parts)
|
|
return 0
|
|
to_chat(user, span_notice("Following parts detected in [src]:"))
|
|
for(var/obj/item/C in component_parts)
|
|
to_chat(user, span_notice(" [C.name]"))
|
|
if(panel_open || !R.panel_req)
|
|
var/obj/item/circuitboard/CB = circuit
|
|
var/P
|
|
for(var/obj/item/A in component_parts)
|
|
for(var/T in CB.req_components)
|
|
if(ispath(A.type, T))
|
|
P = T
|
|
break
|
|
for(var/obj/item/B in R.contents)
|
|
if(istype(B, P) && istype(A, P))
|
|
if(B.get_rating() > A.get_rating())
|
|
R.remove_from_storage(B, src)
|
|
R.handle_item_insertion(A, 1)
|
|
component_parts -= A
|
|
component_parts += B
|
|
B.loc = null
|
|
to_chat(user, span_notice("[A.name] replaced with [B.name]."))
|
|
parts_replaced = TRUE
|
|
break
|
|
update_icon()
|
|
RefreshParts()
|
|
if(parts_replaced)
|
|
R.play_rped_sound()
|
|
return 1
|
|
|
|
// This is it's own proc so it can be more easily found when looking for machines that can upgrade themselves from mapped parts
|
|
// Should be called from LateInitialize()
|
|
/obj/machinery/proc/apply_mapped_upgrades()
|
|
return
|
|
|
|
// Default behavior for wrenching down machines. Supports both delay and instant modes.
|
|
/obj/machinery/proc/default_unfasten_wrench(var/mob/user, var/obj/item/W, var/time = 0)
|
|
if(!W.has_tool_quality(TOOL_WRENCH))
|
|
return FALSE
|
|
if(panel_open)
|
|
return FALSE // Close panel first!
|
|
playsound(src, W.usesound, 50, 1)
|
|
var/actual_time = W.toolspeed * time
|
|
if(actual_time != 0)
|
|
user.visible_message( \
|
|
span_warning("\The [user] begins [anchored ? "un" : ""]securing \the [src]."), \
|
|
span_notice("You start [anchored ? "un" : ""]securing \the [src]."))
|
|
if(actual_time == 0 || do_after(user, actual_time, target = src))
|
|
user.visible_message( \
|
|
span_warning("\The [user] has [anchored ? "un" : ""]secured \the [src]."), \
|
|
span_notice("You [anchored ? "un" : ""]secure \the [src]."))
|
|
anchored = !anchored
|
|
power_change() //Turn on or off the machine depending on the status of power in the new area.
|
|
update_icon()
|
|
return TRUE
|
|
|
|
/obj/machinery/proc/default_deconstruction_crowbar(var/mob/user, var/obj/item/C)
|
|
if(!C.has_tool_quality(TOOL_CROWBAR))
|
|
return 0
|
|
if(!panel_open)
|
|
return 0
|
|
. = dismantle()
|
|
|
|
/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/obj/item/S)
|
|
if(!S.has_tool_quality(TOOL_SCREWDRIVER))
|
|
return 0
|
|
playsound(src, S.usesound, 50, 1)
|
|
panel_open = !panel_open
|
|
to_chat(user, span_notice("You [panel_open ? "open" : "close"] the maintenance hatch of [src]."))
|
|
update_icon()
|
|
return 1
|
|
|
|
/obj/machinery/proc/computer_deconstruction_screwdriver(var/mob/user, var/obj/item/S)
|
|
if(!S.has_tool_quality(TOOL_SCREWDRIVER))
|
|
return 0
|
|
if(!circuit)
|
|
return 0
|
|
to_chat(user, span_notice("You start disconnecting the monitor."))
|
|
playsound(src, S.usesound, 50, 1)
|
|
if(do_after(user, 2 SECONDS * S.toolspeed, target = src))
|
|
if(stat & BROKEN)
|
|
to_chat(user, span_notice("The broken glass falls out."))
|
|
new /obj/item/material/shard(src.loc)
|
|
else
|
|
to_chat(user, span_notice("You disconnect the monitor."))
|
|
. = dismantle()
|
|
|
|
/obj/machinery/proc/alarm_deconstruction_screwdriver(var/mob/user, var/obj/item/S)
|
|
if(!S.has_tool_quality(TOOL_SCREWDRIVER))
|
|
return 0
|
|
playsound(src, S.usesound, 50, 1)
|
|
panel_open = !panel_open
|
|
to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]")
|
|
update_icon()
|
|
return 1
|
|
|
|
/obj/machinery/proc/alarm_deconstruction_wirecutters(var/mob/user, var/obj/item/W)
|
|
if(!W.has_tool_quality(TOOL_WIRECUTTER))
|
|
return 0
|
|
if(!panel_open)
|
|
return 0
|
|
user.visible_message(span_warning("[user] has cut the wires inside \the [src]!"), "You have cut the wires inside \the [src].")
|
|
playsound(src, W.usesound, 50, 1)
|
|
new/obj/item/stack/cable_coil(get_turf(src), 5)
|
|
. = dismantle()
|
|
|
|
/obj/machinery/proc/dismantle()
|
|
SEND_SIGNAL(src, COMSIG_OBJ_DECONSTRUCT)
|
|
playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
|
|
for(var/obj/I in contents)
|
|
if(istype(I,/obj/item/card/id))
|
|
I.forceMove(src.loc)
|
|
|
|
if(!circuit)
|
|
return 0
|
|
var/obj/structure/frame/A = new /obj/structure/frame(src.loc)
|
|
var/obj/item/circuitboard/M = circuit
|
|
A.circuit = M
|
|
A.anchored = TRUE
|
|
A.frame_type = M.board_type
|
|
if(A.frame_type.circuit)
|
|
A.need_circuit = 0
|
|
|
|
if(A.frame_type.frame_class == FRAME_CLASS_ALARM || A.frame_type.frame_class == FRAME_CLASS_DISPLAY)
|
|
A.density = FALSE
|
|
else
|
|
A.density = TRUE
|
|
|
|
if(A.frame_type.frame_class == FRAME_CLASS_MACHINE)
|
|
for(var/obj/D in component_parts)
|
|
D.forceMove(src.loc)
|
|
if(A.components)
|
|
A.components.Cut()
|
|
else
|
|
A.components = list()
|
|
component_parts = list()
|
|
A.check_components()
|
|
|
|
if(A.frame_type.frame_class == FRAME_CLASS_ALARM)
|
|
A.state = FRAME_FASTENED
|
|
else if(A.frame_type.frame_class == FRAME_CLASS_COMPUTER || A.frame_type.frame_class == FRAME_CLASS_DISPLAY)
|
|
if(stat & BROKEN)
|
|
A.state = FRAME_WIRED
|
|
else
|
|
A.state = FRAME_PANELED
|
|
else
|
|
A.state = FRAME_WIRED
|
|
|
|
A.set_dir(dir)
|
|
A.pixel_x = pixel_x
|
|
A.pixel_y = pixel_y
|
|
A.update_desc()
|
|
A.update_icon()
|
|
M.loc = null
|
|
M.deconstruct(src)
|
|
qdel(src)
|
|
return 1
|
|
|
|
/obj/machinery/bullet_act(obj/item/projectile/P, def_zone)
|
|
. = ..()
|
|
if(prob(P.get_structure_damage() * bullet_vulnerability))
|
|
fall_apart()
|
|
|
|
/**
|
|
* Like an angrier dismantle, where it destroys some of the parts and doesn't give you a frame
|
|
** severity: Same severities as ex_act (so lower is more destructive)
|
|
** scatter: If you want the parts to slide around 1 turf in random directions
|
|
*/
|
|
/obj/machinery/proc/fall_apart(var/severity = 3, var/scatter = TRUE)
|
|
var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
|
|
spark_system.set_up(5, 0, src)
|
|
spark_system.attach(src)
|
|
|
|
var/atom/droploc = drop_location()
|
|
if(!droploc || !contents.len) // not even a circuit?
|
|
playsound(src, 'sound/machines/machine_die_short.ogg')
|
|
spark_system.start()
|
|
qdel(spark_system)
|
|
qdel(src)
|
|
return
|
|
|
|
var/list/surviving_parts = list()
|
|
// Deleting IDs is lame, unless this is like nuclear severity
|
|
if(severity != 1)
|
|
for(var/obj/item/card/id/I in contents)
|
|
surviving_parts |= I
|
|
|
|
// May populate some items to throw around
|
|
if(!LAZYLEN(component_parts) && circuit)
|
|
circuit.apply_default_parts(src)
|
|
|
|
var/survivability
|
|
switch(severity)
|
|
// No survivors
|
|
if(1)
|
|
survivability = 0
|
|
|
|
// 1 part survives
|
|
if(2)
|
|
survivability = 0
|
|
var/atom/movable/picked_part = pick(contents)
|
|
if(istype(picked_part))
|
|
surviving_parts |= picked_part
|
|
|
|
// 50% of parts destroyed on average
|
|
if(3)
|
|
survivability = 50
|
|
|
|
// No parts destroyed, but you lose the frame
|
|
else
|
|
survivability = 100
|
|
|
|
for(var/atom/movable/P in contents)
|
|
if(prob(survivability))
|
|
surviving_parts |= P
|
|
|
|
if(circuit && severity >= 2)
|
|
var/datum/frame/frame_types/FT = circuit.board_type
|
|
if(istype(FT))
|
|
// Some steel from the frame, but about half
|
|
surviving_parts += new /obj/item/stack/material/steel(null, max(1,round(FT.frame_size/2)))
|
|
// Two bits of cable, but not the 5 required to rebuild
|
|
surviving_parts += new /obj/item/stack/cable_coil(null, 1)
|
|
surviving_parts += new /obj/item/stack/cable_coil(null, 1)
|
|
|
|
for(var/atom/movable/A as anything in surviving_parts)
|
|
A.forceMove(droploc)
|
|
if(scatter && isturf(droploc))
|
|
var/turf/T = droploc
|
|
A.Move(get_step(T, pick(GLOB.alldirs)))
|
|
|
|
playsound(src, 'sound/machines/machine_die_short.ogg')
|
|
spark_system.start()
|
|
qdel(spark_system)
|
|
qdel(src)
|