mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-11 16:07:36 +01:00
4ecb0bc21c
Repaths obj/machinery to obj/structure/machinery. **Note for reviewers:** the only meaningful changed code exists within **code/game/objects/structures.dm** and **code/game/objects/structures/_machinery.dm**, largely concerning damage procs. With the exception of moving airlock defines to their own file, ALL OTHER CHANGES ARE STRICTLY PATH CHANGES. Objects, _categorically_, are largely divided between those you can hold in your hand/inventory and those you can't. Machinery objects are already subtypes of Structures behaviorally, this PR just makes their pathing reflect that, and allows for future work (tool actions, more health/destruction functionality) to be developed without unnecessary code duplication. I have tested this PR by loading up the Horizon and dismantling various machines and structures with tools, shooting guns of various types throughout the ship, and detonating a bunch of explosions throughout the ship.
376 lines
11 KiB
Plaintext
376 lines
11 KiB
Plaintext
//////////////////////////////
|
|
// POWER MACHINERY BASE CLASS
|
|
//////////////////////////////
|
|
|
|
/////////////////////////////
|
|
// Definitions
|
|
/////////////////////////////
|
|
|
|
/obj/structure/machinery/power
|
|
name = null
|
|
icon = 'icons/obj/power.dmi'
|
|
anchored = 1.0
|
|
var/datum/powernet/powernet = null
|
|
use_power = POWER_USE_OFF
|
|
idle_power_usage = 0
|
|
active_power_usage = 0
|
|
|
|
/obj/structure/machinery/power/Destroy()
|
|
disconnect_from_network()
|
|
disconnect_terminal()
|
|
|
|
return ..()
|
|
|
|
///////////////////////////////
|
|
// General procedures
|
|
//////////////////////////////
|
|
/**
|
|
* Proc: power_wattage_readable()
|
|
* Parameters: 1 (amount - Power in Watts to be converted to W, kW or MW)
|
|
* Description: Helper proc that converts reading in Watts to kW or MW (returns string version of amount parameter)
|
|
* NOTE: This needs to be replaced by a common SIUnits proc once energy is normalized (watts, volts, joules, etc.)
|
|
*/
|
|
/proc/power_wattage_readable(var/amount = 0)
|
|
var/units = ""
|
|
// 10kW and less - Watts
|
|
if(amount < 10000)
|
|
units = "W"
|
|
// 10MW and less - KiloWatts
|
|
else if(amount < 10000000)
|
|
units = "kW"
|
|
amount = (round(amount/100) / 10)
|
|
// More than 10MW - MegaWatts
|
|
else
|
|
units = "MW"
|
|
amount = (round(amount/10000) / 100)
|
|
if (units == "W")
|
|
return "[amount] W"
|
|
else
|
|
return "[amount] [units]"
|
|
|
|
/**
|
|
* Proc: power_joules_readable()
|
|
* Parameters: 1 (amount - Power in Joules to be converted to J, kJ or MJ)
|
|
* Description: Helper proc that converts reading in Joules to kJ or MJ (returns string version of amount parameter)
|
|
* NOTE: This needs to be replaced by a common SIUnits proc once energy is normalized (watts, volts, joules, etc.)
|
|
*/
|
|
/proc/power_joules_readable(var/amount = 0)
|
|
var/units = ""
|
|
// 10kW and less - Watts
|
|
if(amount < 10000)
|
|
units = "W"
|
|
// 10MW and less - KiloWatts
|
|
else if(amount < 10000000)
|
|
units = "kJ"
|
|
amount = (round(amount/100) / 10)
|
|
// More than 10MW - MegaWatts
|
|
else
|
|
units = "MJ"
|
|
amount = (round(amount/10000) / 100)
|
|
if (units == "J")
|
|
return "[amount] J"
|
|
else
|
|
return "[amount] [units]"
|
|
|
|
/// Machines without a terminal will just return, no harm no fowl.
|
|
/obj/structure/machinery/power/proc/disconnect_terminal()
|
|
return
|
|
|
|
/// Connect the machine to a powernet if a node cable is present on the turf
|
|
/obj/structure/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/structure/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/structure/machinery/power/attackby(obj/item/attacking_item, mob/user)
|
|
|
|
if(attacking_item.tool_behaviour == TOOL_CABLECOIL)
|
|
|
|
var/obj/item/stack/cable_coil/coil = attacking_item
|
|
|
|
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
|
|
|
|
///////////////////////////////////////////
|
|
// Powernet handling helpers
|
|
//////////////////////////////////////////
|
|
|
|
/// Returns all the cables WITHOUT a powernet in neighbors turfs,
|
|
/// pointing towards the turf the machine is located at.
|
|
/obj/structure/machinery/power/proc/get_connections()
|
|
|
|
. = list()
|
|
|
|
var/cdir
|
|
var/turf/T
|
|
|
|
for(var/card in GLOB.cardinals)
|
|
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/structure/machinery/power/proc/get_marked_connections()
|
|
|
|
. = list()
|
|
|
|
var/cdir
|
|
var/turf/T
|
|
|
|
for(var/card in GLOB.cardinals)
|
|
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/structure/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/fdir = (!d)? 0 : turn(d, 180) // the opposite direction to d (or 0 if d==0)
|
|
///// Z-Level Stuff
|
|
var/Zdir
|
|
if(d==11)
|
|
Zdir = 11
|
|
else if (d==12)
|
|
Zdir = 12
|
|
else
|
|
Zdir = 999
|
|
///// Z-Level Stuff
|
|
for(var/AM in T)
|
|
if(AM == source) continue //we don't want to return source
|
|
|
|
if(!cable_only && istype(AM,/obj/structure/machinery/power))
|
|
var/obj/structure/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)
|
|
///// Z-Level Stuff
|
|
if(C.d1 == fdir || C.d2 == fdir || C.d1 == Zdir || C.d2 == Zdir)
|
|
///// Z-Level Stuff
|
|
. += C
|
|
else if(C.d1 == d || C.d2 == d)
|
|
. += 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)
|
|
//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/structure/machinery/power))
|
|
var/obj/structure/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/structure/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/structure/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
|
|
*
|
|
* No animations will be performed by this proc
|
|
*
|
|
* * victim - The mob who has to be shocked
|
|
* * power_source - is a source of electricity, can be power cell, area, apc, cable, powernet or null
|
|
* * source - An object caused electrocuting (airlock, grille, etc)
|
|
* * siemens_coeff - Layman's terms, conductivity
|
|
* * contact_zone - Where the electrocution is happening, see BP_* defines in `code\__DEFINES\mobs.dm`
|
|
*
|
|
* Returns the amount of energy that was used to electrocute the mob, or `null` / `FALSE` if the electrocution didn't happen
|
|
*/
|
|
/proc/electrocute_mob(mob/living/carbon/victim, power_source, obj/source, siemens_coeff = 1.0, contact_zone = "hand")
|
|
|
|
if (!victim)
|
|
return 0
|
|
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/cell/cell
|
|
|
|
if(istype(power_source,/datum/powernet))
|
|
PN = power_source
|
|
else if(istype(power_source,/obj/item/cell))
|
|
cell = power_source
|
|
else if(istype(power_source,/obj/structure/machinery/power/apc))
|
|
var/obj/structure/machinery/power/apc/apc = power_source
|
|
cell = apc.cell
|
|
if (apc.terminal)
|
|
PN = apc.terminal.powernet
|
|
else if (!power_source)
|
|
return FALSE
|
|
else
|
|
log_admin("ERROR: /proc/electrocute_mob([victim], [power_source], [source]): wrong power_source")
|
|
return FALSE
|
|
//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)
|
|
var/mob/living/carbon/human/H
|
|
if(ishuman(victim))
|
|
H = victim
|
|
if(H)
|
|
if(H.species.siemens_coefficient == 0)
|
|
return
|
|
if(H.species.is_naturally_insulated())
|
|
return
|
|
if(H.gloves && contact_zone == "hand")
|
|
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
|
|
var/touchy_hand
|
|
if(contact_zone == "hand")
|
|
if(victim.hand)
|
|
touchy_hand = BP_R_HAND
|
|
else
|
|
touchy_hand = BP_L_HAND
|
|
if(!touchy_hand)
|
|
drained_hp = victim.electrocute_act(shock_damage, source, siemens_coeff, ground_zero = contact_zone) //zzzzzzap!
|
|
else
|
|
drained_hp = victim.electrocute_act(shock_damage, source, siemens_coeff, ground_zero = touchy_hand) //zzzzzzap!
|
|
var/drained_energy = drained_hp*20
|
|
|
|
if (source_area)
|
|
source_area.use_power_oneoff(drained_energy/CELLRATE)
|
|
else if (istype(power_source,/datum/powernet))
|
|
var/drained_power = drained_energy/CELLRATE
|
|
drained_power = POWERNET_POWER_DRAW(PN, drained_power)
|
|
DRAW_FROM_POWERNET(PN, drained_power)
|
|
else if (istype(power_source, /obj/item/cell))
|
|
cell.use(drained_energy)
|
|
return drained_energy
|