mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 10:43:20 +00:00
- Instead of using auto_use_power to re-tally up machinery's power usage every cycle, track the steady "static" load separately from the transient "oneoff" usage. Machines then only need to inform the area when they use oneoff power or *change* their steady usage. - Remove auto_use_power and stop SSmachines from calling it. - Add vars to track "static" usage for each of the three power channels to /area - Rename the existing three vars to "oneoff" so its clear what they mean (and to catch people accidentally updating them directly) - Update area power procs and APCs to use the new variables. - Rename /area/proc/use_power() to use_power_oneoff() to make it clear what it is doing. - Deprecate /obj/machinery/use_power() in favor of use_power_oneoff() but don't delete yet. Can transition gradually. - Add logic to the update_power procs on machines to calculate the deltas and update static area power whenever their usage changes. - Add logic to machines to update area power when they are created, destroyed, or move. - Moved /obj/machinery procs related to area power usage into machinery_power.dm to make them easier to find. - Added or updated comments in several places to explain what is going on and how to use it.
344 lines
10 KiB
Plaintext
344 lines
10 KiB
Plaintext
//////////////////////////////
|
|
// POWER MACHINERY BASE CLASS
|
|
//////////////////////////////
|
|
|
|
/////////////////////////////
|
|
// Definitions
|
|
/////////////////////////////
|
|
|
|
/obj/machinery/power
|
|
name = null
|
|
icon = 'icons/obj/power.dmi'
|
|
anchored = 1.0
|
|
var/datum/powernet/powernet = null
|
|
use_power = USE_POWER_OFF
|
|
idle_power_usage = 0
|
|
active_power_usage = 0
|
|
|
|
/obj/machinery/power/Destroy()
|
|
disconnect_from_network()
|
|
disconnect_terminal()
|
|
|
|
return ..()
|
|
|
|
///////////////////////////////
|
|
// General procedures
|
|
//////////////////////////////
|
|
|
|
// common helper procs for all power machines
|
|
/obj/machinery/power/drain_power(var/drain_check, var/surge, var/amount = 0)
|
|
if(drain_check)
|
|
return 1
|
|
|
|
if(powernet && powernet.avail)
|
|
powernet.trigger_warning()
|
|
return powernet.draw_power(amount)
|
|
|
|
/obj/machinery/power/proc/add_avail(var/amount)
|
|
if(powernet)
|
|
powernet.newavail += amount
|
|
|
|
/obj/machinery/power/proc/draw_power(var/amount)
|
|
if(powernet)
|
|
return powernet.draw_power(amount)
|
|
return 0
|
|
|
|
/obj/machinery/power/proc/surplus()
|
|
if(powernet)
|
|
return powernet.avail-powernet.load
|
|
else
|
|
return 0
|
|
|
|
/obj/machinery/power/proc/avail()
|
|
if(powernet)
|
|
return powernet.avail
|
|
else
|
|
return 0
|
|
|
|
/obj/machinery/power/proc/viewload()
|
|
if(powernet)
|
|
return powernet.viewload
|
|
else
|
|
return 0
|
|
|
|
/obj/machinery/power/proc/disconnect_terminal() // machines without a terminal will just return, no harm no fowl.
|
|
return
|
|
|
|
// connect the machine to a powernet if a node cable is present on the turf
|
|
/obj/machinery/power/proc/connect_to_network()
|
|
var/turf/T = src.loc
|
|
if(!T || !istype(T))
|
|
return 0
|
|
|
|
var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked
|
|
if(!C || !C.powernet)
|
|
return 0
|
|
|
|
C.powernet.add_machine(src)
|
|
return 1
|
|
|
|
// remove and disconnect the machine from its current powernet
|
|
/obj/machinery/power/proc/disconnect_from_network()
|
|
if(!powernet)
|
|
return 0
|
|
powernet.remove_machine(src)
|
|
return 1
|
|
|
|
// attach a wire to a power machine - leads from the turf you are standing on
|
|
//almost never called, overwritten by all power machines but terminal and generator
|
|
/obj/machinery/power/attackby(obj/item/weapon/W, mob/user)
|
|
|
|
if(istype(W, /obj/item/stack/cable_coil))
|
|
|
|
var/obj/item/stack/cable_coil/coil = W
|
|
|
|
var/turf/T = user.loc
|
|
|
|
if(!T.is_plating() || !istype(T, /turf/simulated/floor))
|
|
return
|
|
|
|
if(get_dist(src, user) > 1)
|
|
return
|
|
|
|
coil.turf_place(T, user)
|
|
return
|
|
else
|
|
..()
|
|
return
|
|
|
|
// Power machinery should also connect/disconnect from the network.
|
|
/obj/machinery/power/default_unfasten_wrench(var/mob/user, var/obj/item/weapon/W, var/time = 20)
|
|
if((. = ..()))
|
|
if(anchored)
|
|
connect_to_network()
|
|
else
|
|
disconnect_from_network()
|
|
|
|
// Used for power spikes by the engine, has specific effects on different machines.
|
|
/obj/machinery/power/proc/overload(var/obj/machinery/power/source)
|
|
return
|
|
|
|
// Used by the grid checker upon receiving a power spike.
|
|
/obj/machinery/power/proc/do_grid_check()
|
|
return
|
|
|
|
/obj/machinery/power/proc/power_spike()
|
|
return
|
|
|
|
///////////////////////////////////////////
|
|
// Powernet handling helpers
|
|
//////////////////////////////////////////
|
|
|
|
//returns all the cables WITHOUT a powernet in neighbors turfs,
|
|
//pointing towards the turf the machine is located at
|
|
/obj/machinery/power/proc/get_connections()
|
|
|
|
. = list()
|
|
|
|
var/cdir
|
|
var/turf/T
|
|
|
|
for(var/card in cardinal)
|
|
T = get_step(loc,card)
|
|
cdir = get_dir(T,loc)
|
|
|
|
for(var/obj/structure/cable/C in T)
|
|
if(C.powernet) continue
|
|
if(C.d1 == cdir || C.d2 == cdir)
|
|
. += C
|
|
return .
|
|
|
|
//returns all the cables in neighbors turfs,
|
|
//pointing towards the turf the machine is located at
|
|
/obj/machinery/power/proc/get_marked_connections()
|
|
|
|
. = list()
|
|
|
|
var/cdir
|
|
var/turf/T
|
|
|
|
for(var/card in cardinal)
|
|
T = get_step(loc,card)
|
|
cdir = get_dir(T,loc)
|
|
|
|
for(var/obj/structure/cable/C in T)
|
|
if(C.d1 == cdir || C.d2 == cdir)
|
|
. += C
|
|
return .
|
|
|
|
//returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at
|
|
/obj/machinery/power/proc/get_indirect_connections()
|
|
. = list()
|
|
for(var/obj/structure/cable/C in loc)
|
|
if(C.powernet) continue
|
|
if(C.d1 == 0) // the cable is a node cable
|
|
. += C
|
|
return .
|
|
|
|
///////////////////////////////////////////
|
|
// GLOBAL PROCS for powernets handling
|
|
//////////////////////////////////////////
|
|
|
|
|
|
// returns a list of all power-related objects (nodes, cable, junctions) in turf,
|
|
// excluding source, that match the direction d
|
|
// if unmarked==1, only return those with no powernet
|
|
/proc/power_list(var/turf/T, var/source, var/d, var/unmarked=0, var/cable_only = 0)
|
|
. = list()
|
|
|
|
var/reverse = d ? reverse_dir[d] : 0
|
|
for(var/AM in T)
|
|
if(AM == source) continue //we don't want to return source
|
|
|
|
if(!cable_only && istype(AM,/obj/machinery/power))
|
|
var/obj/machinery/power/P = AM
|
|
if(P.powernet == 0) continue // exclude APCs which have powernet=0
|
|
|
|
if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet
|
|
if(d == 0)
|
|
. += P
|
|
|
|
else if(istype(AM,/obj/structure/cable))
|
|
var/obj/structure/cable/C = AM
|
|
|
|
if(!unmarked || !C.powernet)
|
|
if(C.d1 == d || C.d2 == d || C.d1 == reverse || C.d2 == reverse )
|
|
. += C
|
|
return .
|
|
|
|
//remove the old powernet and replace it with a new one throughout the network.
|
|
/proc/propagate_network(var/obj/O, var/datum/powernet/PN)
|
|
//to_world_log("propagating new network")
|
|
var/list/worklist = list()
|
|
var/list/found_machines = list()
|
|
var/index = 1
|
|
var/obj/P = null
|
|
|
|
worklist+=O //start propagating from the passed object
|
|
|
|
while(index<=worklist.len) //until we've exhausted all power objects
|
|
P = worklist[index] //get the next power object found
|
|
index++
|
|
|
|
if( istype(P,/obj/structure/cable))
|
|
var/obj/structure/cable/C = P
|
|
if(C.powernet != PN) //add it to the powernet, if it isn't already there
|
|
PN.add_cable(C)
|
|
worklist |= C.get_connections() //get adjacents power objects, with or without a powernet
|
|
|
|
else if(P.anchored && istype(P,/obj/machinery/power))
|
|
var/obj/machinery/power/M = P
|
|
found_machines |= M //we wait until the powernet is fully propagates to connect the machines
|
|
|
|
else
|
|
continue
|
|
|
|
//now that the powernet is set, connect found machines to it
|
|
for(var/obj/machinery/power/PM in found_machines)
|
|
if(!PM.connect_to_network()) //couldn't find a node on its turf...
|
|
PM.disconnect_from_network() //... so disconnect if already on a powernet
|
|
|
|
|
|
//Merge two powernets, the bigger (in cable length term) absorbing the other
|
|
/proc/merge_powernets(var/datum/powernet/net1, var/datum/powernet/net2)
|
|
if(!net1 || !net2) //if one of the powernet doesn't exist, return
|
|
return
|
|
|
|
if(net1 == net2) //don't merge same powernets
|
|
return
|
|
|
|
//We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code.
|
|
if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around
|
|
var/temp = net1
|
|
net1 = net2
|
|
net2 = temp
|
|
|
|
//merge net2 into net1
|
|
for(var/obj/structure/cable/Cable in net2.cables) //merge cables
|
|
net1.add_cable(Cable)
|
|
|
|
if(!net2) return net1
|
|
|
|
for(var/obj/machinery/power/Node in net2.nodes) //merge power machines
|
|
if(!Node.connect_to_network())
|
|
Node.disconnect_from_network() //if somehow we can't connect the machine to the new powernet, disconnect it from the old nonetheless
|
|
|
|
return net1
|
|
|
|
//Determines how strong could be shock, deals damage to mob, uses power.
|
|
//M is a mob who touched wire/whatever
|
|
//power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null
|
|
//source is an object caused electrocuting (airlock, grille, etc)
|
|
//No animations will be performed by this proc.
|
|
/proc/electrocute_mob(mob/living/M as mob, var/power_source, var/obj/source, var/siemens_coeff = 1.0)
|
|
if(istype(M.loc,/obj/mecha)) return 0 //feckin mechs are dumb
|
|
if(issilicon(M)) return 0 //No more robot shocks from machinery
|
|
var/area/source_area
|
|
if(istype(power_source,/area))
|
|
source_area = power_source
|
|
power_source = source_area.get_apc()
|
|
if(istype(power_source,/obj/structure/cable))
|
|
var/obj/structure/cable/Cable = power_source
|
|
power_source = Cable.powernet
|
|
|
|
var/datum/powernet/PN
|
|
var/obj/item/weapon/cell/cell
|
|
|
|
if(istype(power_source,/datum/powernet))
|
|
PN = power_source
|
|
else if(istype(power_source,/obj/item/weapon/cell))
|
|
cell = power_source
|
|
else if(istype(power_source,/obj/machinery/power/apc))
|
|
var/obj/machinery/power/apc/apc = power_source
|
|
cell = apc.cell
|
|
if (apc.terminal)
|
|
PN = apc.terminal.powernet
|
|
else if (!power_source)
|
|
return 0
|
|
else
|
|
log_admin("ERROR: /proc/electrocute_mob([M], [power_source], [source]): wrong power_source")
|
|
return 0
|
|
//Triggers powernet warning, but only for 5 ticks (if applicable)
|
|
//If following checks determine user is protected we won't alarm for long.
|
|
if(PN)
|
|
PN.trigger_warning(5)
|
|
if(istype(M,/mob/living/carbon/human))
|
|
var/mob/living/carbon/human/H = M
|
|
if(H.species.siemens_coefficient <= 0)
|
|
return
|
|
if(H.gloves)
|
|
var/obj/item/clothing/gloves/G = H.gloves
|
|
if(G.siemens_coefficient == 0) return 0 //to avoid spamming with insulated glvoes on
|
|
|
|
//Checks again. If we are still here subject will be shocked, trigger standard 20 tick warning
|
|
//Since this one is longer it will override the original one.
|
|
if(PN)
|
|
PN.trigger_warning()
|
|
|
|
if (!cell && !PN)
|
|
return 0
|
|
var/PN_damage = 0
|
|
var/cell_damage = 0
|
|
if (PN)
|
|
PN_damage = PN.get_electrocute_damage()
|
|
if (cell)
|
|
cell_damage = cell.get_electrocute_damage()
|
|
var/shock_damage = 0
|
|
if (PN_damage>=cell_damage)
|
|
power_source = PN
|
|
shock_damage = PN_damage
|
|
else
|
|
power_source = cell
|
|
shock_damage = cell_damage
|
|
var/drained_hp = M.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap!
|
|
var/drained_energy = drained_hp*20
|
|
|
|
if (source_area)
|
|
source_area.use_power_oneoff(drained_energy/CELLRATE, EQUIP)
|
|
else if (istype(power_source,/datum/powernet))
|
|
var/drained_power = drained_energy/CELLRATE
|
|
drained_power = PN.draw_power(drained_power)
|
|
else if (istype(power_source, /obj/item/weapon/cell))
|
|
cell.use(drained_energy)
|
|
return drained_energy
|