Files
Letter NandGitHub b378375888 update blueprints (#7499)
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request
title, robots also get to see the blueprints now
<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

## Why It's Good For The Game

<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->

## Changelog

<!-- If your PR modifies aspects of the game that can be concretely
observed by players or admins you should add a changelog. If your change
does NOT meet this description, remove this section. Please note that
maintainers freely reserve the right to remove and add tags should they
deem it appropriate. You can attempt to finagle the system all you want,
but it's best to shoot for clear communication right off the bat. -->

🆑
tweak: updated blueprints, engi module robots now get blueprints as well
/🆑

<!-- Both 🆑's are required for the changelog to work! You can put
your name to the right of the first 🆑 if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->
2026-01-24 14:34:39 -05:00

379 lines
11 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/Initialize(mapload)
. = ..()
if(isturf(loc))
var/turf/turf_loc = loc
turf_loc.add_blueprints_preround(src)
/obj/machinery/power/Destroy()
disconnect_from_network()
disconnect_terminal()
return ..()
///////////////////////////////
// General procedures
//////////////////////////////
// common helper procs for all power machines
/obj/machinery/power/drain_energy(datum/actor, amount, flags)
if(!powernet)
return 0
return powernet.drain_energy_handler(actor, amount, flags)
/obj/machinery/power/can_drain_energy(datum/actor, amount)
return TRUE
/**
* amount is in KW, NOT W
*/
/obj/machinery/power/proc/add_avail(amount)
if(powernet)
powernet.newavail += amount
/**
* amount is in KW, NOT W
*/
/obj/machinery/power/proc/draw_power(amount)
if(powernet)
return powernet.draw_power(amount)
return 0
/**
* amount is in KW, NOT W
*
* include amount to turn this into a boolean check.
*/
/obj/machinery/power/proc/surplus(amount)
if(!powernet)
return 0
. = powernet.avail - powernet.load
if(!isnull(amount))
. = . >= amount
/**
* amount is in KW, NOT W
*
* include amount to turn this into a boolean check.
*/
/obj/machinery/power/proc/avail(amount)
return isnull(amount)? (powernet?.avail || 0) : (powernet?.avail >= amount)
/**
* amount is in KW, NOT W
*/
/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/I, mob/living/user, list/params, clickchain_flags, damage_multiplier)
if(istype(I, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/coil = I
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/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 GLOB.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 GLOB.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 ? global.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_chat(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/vehicle/sealed/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/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/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
siemens_coeff *= H.inventory.query_simple_covered_siemens_coefficient(HANDS)
if(siemens_coeff <= 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/stun_calculation
if(shock_damage >= 100)
stun_calculation = 200
else if(shock_damage >= 50)
stun_calculation = rand(75, 100)
else if(shock_damage >= 30)
stun_calculation = rand(35, 50)
else if(shock_damage >= 20)
stun_calculation = rand(20, 35)
else
stun_calculation = shock_damage
var/list/shock_return = M.electrocute(0, shock_damage * siemens_coeff, stun_calculation & siemens_coeff, ELECTROCUTE_ACT_FLAG_IGNORE_ARMOR, null, source)
pass(shock_return)
// // 10kw per hp
// var/drained_energy = drained_hp * 10000
// if (source_area)
// source_area.use_power_oneoff(drained_energy)
// else if (istype(power_source,/datum/powernet))
// drained_energy = PN.draw_power(drained_energy * 0.001) * 1000
// else if (istype(power_source, /obj/item/cell))
// cell.use(DYNAMIC_W_TO_CELL_UNITS(drained_energy, 1))
// return drained_energy
return TRUE