Files
Bubberstation/code/modules/power/cable.dm
Qustinnus a394ccdc2b Material datum chairs & tables and applies materials to all items (Now with less pain for mining & RnD) (#46525)
removes materials list from items, uses custom_materials instead. This might introduce some bugs so we should testmerge this for a while (and Ill test stuff locally as much as I can)

this also adds material crafting to sheets. Test case being chairs. In the future we can add stuff like tables, walls, doors etc.

also applies materials to everything, with fixes, which can close #46299
2019-10-08 11:06:02 -04:00

591 lines
19 KiB
Plaintext

//Use this only for things that aren't a subtype of obj/machinery/power
//For things that are, override "should_have_node()" on them
GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/grille, /obj/structure/cable_bridge)))
#define UNDER_SMES -1
#define UNDER_TERMINAL 1
///////////////////////////////
//CABLE STRUCTURE
///////////////////////////////
////////////////////////////////
// Definitions
////////////////////////////////
/obj/structure/cable
name = "power cable"
desc = "A flexible, superconducting insulated cable for heavy-duty power transfer."
icon = 'icons/obj/power_cond/layer_cable.dmi'
icon_state = "l2-1-2-4-8-node"
color = "yellow"
level = 1 //is underfloor
layer = WIRE_LAYER //Above hidden pipes, GAS_PIPE_HIDDEN_LAYER
anchored = TRUE
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
var/linked_dirs = 0 //bitflag
var/node = FALSE //used for sprites display
var/cable_layer = CABLE_LAYER_2
var/datum/powernet/powernet
/obj/structure/cable/layer1
color = "red"
cable_layer = CABLE_LAYER_1
layer = WIRE_LAYER - 0.01
icon_state = "l1-1-2-4-8-node"
/obj/structure/cable/layer3
color = "blue"
cable_layer = CABLE_LAYER_3
layer = WIRE_LAYER + 0.01
icon_state = "l3-1-2-4-8-node"
/obj/structure/cable/Initialize(mapload)
. = ..()
var/turf/T = get_turf(src) // hide if turf is not intact
if(level==1)
hide(T.intact)
GLOB.cable_list += src //add it to the global cable list
connect_wire()
/obj/structure/cable/proc/connect_wire(clear_before_updating = FALSE)
var/under_thing = NONE
if(clear_before_updating)
linked_dirs = 0
var/obj/machinery/power/search_parent
for(var/obj/machinery/power/P in loc)
if(istype(P, /obj/machinery/power/terminal))
under_thing = UNDER_TERMINAL
search_parent = P
break
if(istype(P, /obj/machinery/power/smes))
under_thing = UNDER_SMES
search_parent = P
break
for(var/check_dir in GLOB.cardinals)
var/TB = get_step(src, check_dir)
//don't link from smes to its terminal
if(under_thing)
switch(under_thing)
if(UNDER_SMES)
var/obj/machinery/power/terminal/term = locate(/obj/machinery/power/terminal) in TB
//Why null or equal to the search parent?
//during map init it's possible for a placed smes terminal to not have initialized to the smes yet
//but the cable underneath it is ready to link.
//I don't believe null is even a valid state for a smes terminal while the game is actually running
//So in the rare case that this happens, we also shouldn't connect
//This might break.
if(term && (!term.master || term.master == search_parent))
continue
if(UNDER_TERMINAL)
var/obj/machinery/power/smes/S = locate(/obj/machinery/power/smes) in TB
if(S && (!S.terminal || S.terminal == search_parent))
continue
var/inverse = turn(check_dir, 180)
for(var/obj/structure/cable/C in TB)
if(C.cable_layer == cable_layer)
linked_dirs |= check_dir
C.linked_dirs |= inverse
C.update_icon()
update_icon()
/obj/structure/cable/Destroy() // called when a cable is deleted
//Clear the linked indicator bitflags
for(var/check_dir in GLOB.cardinals)
var/inverse = turn(check_dir, 180)
if(linked_dirs & check_dir)
var/TB = get_step(loc, check_dir)
for(var/obj/structure/cable/C in TB)
if(cable_layer == C.cable_layer)
C.linked_dirs &= ~inverse
C.update_icon()
if(powernet)
cut_cable_from_powernet() // update the powernets
GLOB.cable_list -= src //remove it from global cable list
return ..() // then go ahead and delete the cable
/obj/structure/cable/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
new /obj/item/stack/cable_coil(get_turf(loc), 1)
qdel(src)
///////////////////////////////////
// General procedures
///////////////////////////////////
//If underfloor, hide the cable
/obj/structure/cable/hide(i)
if(level == 1 && isturf(loc))
invisibility = i ? INVISIBILITY_MAXIMUM : 0
update_icon()
/obj/structure/cable/update_icon()
if(!linked_dirs)
icon_state = "[cable_layer]-noconnection"
else
var/list/dir_icon_list = list()
for(var/check_dir in GLOB.cardinals)
if(linked_dirs & check_dir)
dir_icon_list += "[check_dir]"
var/dir_string = dir_icon_list.Join("-")
if(dir_icon_list.len > 1)
for(var/obj/O in loc)
if(GLOB.wire_node_generating_types[O.type])
dir_string = "[dir_string]-node"
break
else if(istype(O, /obj/machinery/power))
var/obj/machinery/power/P = O
if(P.should_have_node())
dir_string = "[dir_string]-node"
break
dir_string = "[cable_layer]-[dir_string]"
icon_state = dir_string
/obj/structure/cable/proc/handlecable(obj/item/W, mob/user, params)
var/turf/T = get_turf(src)
if(T.intact)
return
if(W.tool_behaviour == TOOL_WIRECUTTER)
if (shock(user, 50))
return
user.visible_message("<span class='notice'>[user] cuts the cable.</span>", "<span class='notice'>You cut the cable.</span>")
investigate_log("was cut by [key_name(usr)] in [AREACOORD(src)]", INVESTIGATE_WIRES)
deconstruct()
return
else if(W.tool_behaviour == TOOL_MULTITOOL)
if(powernet && (powernet.avail > 0)) // is it powered?
to_chat(user, "<span class='danger'>Total power: [DisplayPower(powernet.avail)]\nLoad: [DisplayPower(powernet.load)]\nExcess power: [DisplayPower(surplus())]</span>")
else
to_chat(user, "<span class='danger'>The cable is not powered.</span>")
shock(user, 5, 0.2)
add_fingerprint(user)
// Items usable on a cable :
// - Wirecutters : cut it duh !
// - Multitool : get the power currently passing through the cable
//
/obj/structure/cable/attackby(obj/item/W, mob/user, params)
handlecable(W, user, params)
// shock the user with probability prb
/obj/structure/cable/proc/shock(mob/user, prb, siemens_coeff = 1)
if(!prob(prb))
return FALSE
if(electrocute_mob(user, powernet, src, siemens_coeff))
do_sparks(5, TRUE, src)
return TRUE
else
return FALSE
/obj/structure/cable/singularity_pull(S, current_size)
..()
if(current_size >= STAGE_FIVE)
deconstruct()
////////////////////////////////////////////
// Power related
///////////////////////////////////////////
// All power generation handled in add_avail()
// Machines should use add_load(), surplus(), avail()
// Non-machines should use add_delayedload(), delayed_surplus(), newavail()
/obj/structure/cable/proc/add_avail(amount)
if(powernet)
powernet.newavail += amount
/obj/structure/cable/proc/add_load(amount)
if(powernet)
powernet.load += amount
/obj/structure/cable/proc/surplus()
if(powernet)
return CLAMP(powernet.avail-powernet.load, 0, powernet.avail)
else
return 0
/obj/structure/cable/proc/avail()
if(powernet)
return powernet.avail
else
return 0
/obj/structure/cable/proc/add_delayedload(amount)
if(powernet)
powernet.delayedload += amount
/obj/structure/cable/proc/delayed_surplus()
if(powernet)
return CLAMP(powernet.newavail - powernet.delayedload, 0, powernet.newavail)
else
return 0
/obj/structure/cable/proc/newavail()
if(powernet)
return powernet.newavail
else
return 0
/////////////////////////////////////////////////
// Cable laying helpers
////////////////////////////////////////////////
// merge with the powernets of power objects in the given direction
/obj/structure/cable/proc/mergeConnectedNetworks(direction)
var/inverse_dir = (!direction)? 0 : turn(direction, 180) //flip the direction, to match with the source position on its turf
var/turf/TB = get_step(src, direction)
for(var/obj/structure/cable/C in TB)
if(!C)
continue
if(src == C)
continue
if(cable_layer != C.cable_layer)
continue
if(C.linked_dirs & inverse_dir) //we've got a matching cable in the neighbor turf
if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables)
var/datum/powernet/newPN = new()
newPN.add_cable(C)
if(powernet) //if we already have a powernet, then merge the two powernets
merge_powernets(powernet, C.powernet)
else
C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet
// merge with the powernets of power objects in the source turf
/obj/structure/cable/proc/mergeConnectedNetworksOnTurf()
var/list/to_connect = list()
node = FALSE
if(!powernet) //if we somehow have no powernet, make one (should not happen for cables)
var/datum/powernet/newPN = new()
newPN.add_cable(src)
//first let's add turf cables to our powernet
//then we'll connect machines on turf where a cable is present
for(var/atom/movable/AM in loc)
if(istype(AM, /obj/machinery/power/apc))
var/obj/machinery/power/apc/N = AM
if(!N.terminal)
continue // APC are connected through their terminal
if(N.terminal.powernet == powernet) //already connected
continue
to_connect += N.terminal //we'll connect the machines after all cables are merged
else if(istype(AM, /obj/machinery/power)) //other power machines
var/obj/machinery/power/M = AM
if(M.powernet == powernet)
continue
to_connect += M //we'll connect the machines after all cables are merged
//now that cables are done, let's connect found machines
for(var/obj/machinery/power/PM in to_connect)
node = TRUE
if(!PM.connect_to_network())
PM.disconnect_from_network() //if we somehow can't connect the machine to the new powernet, remove it from the old nonetheless
//////////////////////////////////////////////
// Powernets handling helpers
//////////////////////////////////////////////
/obj/structure/cable/proc/get_cable_connections(powernetless_only)
. = list()
var/turf/T = get_turf(src)
if(locate(/obj/structure/cable_bridge) in T)
for(var/obj/structure/cable/C in T)
if(C != src)
. += C
for(var/check_dir in GLOB.cardinals)
if(linked_dirs & check_dir)
T = get_step(src, check_dir)
for(var/obj/structure/cable/C in T)
if(cable_layer == C.cable_layer)
. += C
/obj/structure/cable/proc/get_machine_connections(powernetless_only)
. = list()
for(var/obj/machinery/power/P in get_turf(src))
if(!powernetless_only || !P.powernet)
if(P.anchored)
. += P
/obj/structure/cable/proc/auto_propogate_cut_cable(obj/O)
if(O && !QDELETED(O))
var/datum/powernet/newPN = new()// creates a new powernet...
propagate_network(O, newPN)//... and propagates it to the other side of the cable
//Makes a new network for the cable and propgates it.
//If it finds another network in the process, aborts and uses that one and propogates off of it instead
/obj/structure/cable/proc/propogate_if_no_network()
if(powernet)
return
var/datum/powernet/newPN = new()
propagate_network(src, newPN, TRUE)
// cut the cable's powernet at this cable and updates the powergrid
/obj/structure/cable/proc/cut_cable_from_powernet(remove = TRUE)
if(!powernet)
return
var/turf/T1 = loc
if(!T1)
return
//clear the powernet of any machines on tile first
for(var/obj/machinery/power/P in T1)
P.disconnect_from_network()
var/list/P_list = list()
for(var/dir_check in GLOB.cardinals)
if(linked_dirs & dir_check)
T1 = get_step(loc, dir_check)
P_list += locate(/obj/structure/cable) in T1
// remove the cut cable from its turf and powernet, so that it doesn't get count in propagate_network worklist
if(remove)
moveToNullspace()
powernet.remove_cable(src) //remove the cut cable from its powernet
var/first = TRUE
for(var/obj/O in P_list)
if(first)
first = FALSE
continue
addtimer(CALLBACK(O, .proc/auto_propogate_cut_cable, O), 0) //so we don't rebuild the network X times when singulo/explosion destroys a line of X cables
///////////////////////////////////////////////
// The cable coil object, used for laying cable
///////////////////////////////////////////////
////////////////////////////////
// Definitions
////////////////////////////////
GLOBAL_LIST_INIT(cable_coil_recipes, list(new/datum/stack_recipe("cable restraints", /obj/item/restraints/handcuffs/cable, 15),
new/datum/stack_recipe("cable bridge", /obj/structure/cable_bridge, 15)))
/obj/item/stack/cable_coil
name = "cable coil"
custom_price = 30
gender = NEUTER //That's a cable coil sounds better than that's some cable coils
icon = 'icons/obj/power.dmi'
icon_state = "coil"
item_state = "coil"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
max_amount = MAXCOIL
amount = MAXCOIL
merge_type = /obj/item/stack/cable_coil // This is here to let its children merge between themselves
color = "yellow"
var/cable_color = "yellow"
desc = "A coil of insulated power cable."
throwforce = 0
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 5
custom_materials = list(/datum/material/iron=10, /datum/material/glass=5)
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
attack_verb = list("whipped", "lashed", "disciplined", "flogged")
singular_name = "cable piece"
full_w_class = WEIGHT_CLASS_SMALL
grind_results = list(/datum/reagent/copper = 2) //2 copper per cable in the coil
usesound = 'sound/items/deconstruct.ogg'
var/obj/structure/cable/target_type = /obj/structure/cable
/obj/item/stack/cable_coil/Initialize(mapload, new_amount = null)
. = ..()
pixel_x = rand(-2,2)
pixel_y = rand(-2,2)
update_icon()
recipes = GLOB.cable_coil_recipes
/obj/item/stack/cable_coil/examine(mob/user)
. = ..()
. += "<b>Ctrl+Click</b> to change the layer you are placing on."
/obj/item/stack/cable_coil/suicide_act(mob/user)
if(locate(/obj/structure/chair/stool) in get_turf(user))
user.visible_message("<span class='suicide'>[user] is making a noose with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
else
user.visible_message("<span class='suicide'>[user] is strangling [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return(OXYLOSS)
/obj/item/stack/cable_coil/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
if(user.incapacitated() || !user.Adjacent(src))
return FALSE
return TRUE
/obj/item/stack/cable_coil/CtrlClick(mob/living/user)
if(!user)
return
var/list/layer_list = list(
"Layer 1" = image(icon = 'icons/mob/radial.dmi', icon_state = "coil-red"),
"Layer 2" = image(icon = 'icons/mob/radial.dmi', icon_state = "coil-yellow"),
"Layer 3" = image(icon = 'icons/mob/radial.dmi', icon_state = "coil-blue")
)
var/layer_result = show_radial_menu(user, src, layer_list, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE)
if(!check_menu(user))
return
switch(layer_result)
if("Layer 1")
color = "red"
target_type = /obj/structure/cable/layer1
if("Layer 2")
color = "yellow"
target_type = /obj/structure/cable
if("Layer 3")
color = "blue"
target_type = /obj/structure/cable/layer3
///////////////////////////////////
// General procedures
///////////////////////////////////
//you can use wires to heal robotics
/obj/item/stack/cable_coil/attack(mob/living/carbon/human/H, mob/user)
if(!istype(H))
return ..()
var/obj/item/bodypart/affecting = H.get_bodypart(check_zone(user.zone_selected))
if(affecting && affecting.status == BODYPART_ROBOTIC)
if(user == H)
user.visible_message("<span class='notice'>[user] starts to fix some of the wires in [H]'s [affecting.name].</span>", "<span class='notice'>You start fixing some of the wires in [H == user ? "your" : "[H]'s"] [affecting.name].</span>")
if(!do_mob(user, H, 50))
return
if(item_heal_robotic(H, user, 0, 15))
use(1)
return
else
return ..()
/obj/item/stack/cable_coil/update_icon()
icon_state = "[initial(item_state)][amount < 3 ? amount : ""]"
name = "cable [amount < 3 ? "piece" : "coil"]"
//add cables to the stack
/obj/item/stack/cable_coil/proc/give(extra)
if(amount + extra > max_amount)
amount = max_amount
else
amount += extra
update_icon()
///////////////////////////////////////////////
// Cable laying procedures
//////////////////////////////////////////////
// called when cable_coil is clicked on a turf
/obj/item/stack/cable_coil/proc/place_turf(turf/T, mob/user, dirnew)
if(!isturf(user.loc))
return
if(!isturf(T) || T.intact || !T.can_have_cabling())
to_chat(user, "<span class='warning'>You can only lay cables on catwalks and plating!</span>")
return
if(get_amount() < 1) // Out of cable
to_chat(user, "<span class='warning'>There is no cable left!</span>")
return
if(get_dist(T,user) > 1) // Too far
to_chat(user, "<span class='warning'>You can't lay cable at a place that far away!</span>")
return
for(var/obj/structure/cable/C in T)
if(target_type == C.type)
to_chat(user, "<span class='warning'>There's already a cable at that position!</span>")
return
var/obj/structure/cable/C = new target_type(T)
//create a new powernet with the cable, if needed it will be merged later
var/datum/powernet/PN = new()
PN.add_cable(C)
for(var/dir_check in GLOB.cardinals)
C.mergeConnectedNetworks(dir_check) //merge the powernet with adjacents powernets
C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets
use(1)
if(C.shock(user, 50))
if(prob(50)) //fail
new /obj/item/stack/cable_coil(get_turf(C), 1)
C.deconstruct()
return C
/obj/item/stack/cable_coil/five
amount = 5
/obj/item/stack/cable_coil/cut
amount = null
icon_state = "coil2"
/obj/item/stack/cable_coil/cut/Initialize(mapload)
. = ..()
if(!amount)
amount = rand(1,2)
pixel_x = rand(-2,2)
pixel_y = rand(-2,2)
update_icon()
/obj/item/stack/cable_coil/cyborg
is_cyborg = 1
custom_materials = list()
cost = 1
/obj/item/stack/cable_coil/cyborg/attack_self(mob/user)
var/picked = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white")
cable_color = picked
update_icon()
/obj/structure/cable_bridge
name = "cable bridge"
desc = "A bridge to connect different cable layers, or link terminals to incompatible cable layers"
icon = 'icons/obj/power.dmi'
icon_state = "cable_bridge"
level = 1 //is underfloor
layer = WIRE_LAYER + 0.02 //Above all the cables but below terminals
anchored = TRUE
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
/obj/structure/cable_bridge/Initialize()
. = ..()
var/first = TRUE
var/datum/powernet/PN
for(var/obj/structure/cable/C in get_turf(src))
C.update_icon()
if(first == TRUE)
first = FALSE
PN = C.powernet
continue
propagate_network(C, PN)
/obj/structure/cable_bridge/wirecutter_act(mob/living/user, obj/item/I)
. = ..()
qdel(src)
#undef UNDER_SMES
#undef UNDER_TERMINAL