////////////////////////////// // 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