Files
Bubberstation/code/modules/power/cable.dm
_0Steven c49042b95d Allow reinforced tables to be hooked up to the grid like grilles, shocking people who try to disassemble them (#87003)
## About The Pull Request

This pr makes it so reinforced tables can be hooked up to the power
grid, requiring them to be on an open tile with a cable on it much like
grilles, shocking solely people who attempt any of its disassembly
steps.
Insulation obviously still applies.
## Why It's Good For The Game

Saw talk about someone wiring up tables and I think it's funny to have
your table hooked up to the grid.
Allows you to protect your reinforced tables from disassembly, if you're
fine with world's most obvious open cabled tile below your table.
## Changelog
🆑
add: Reinforced tables may now be hooked up directly to the grid much
like grilles, electrocuting people who attempt to disassemble it.
/🆑
2024-11-11 00:40:23 -08:00

801 lines
26 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/table/reinforced,
)))
#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/pipes_n_cables/layer_cable.dmi'
icon_state = "l2-1-2-4-8-node"
color = CABLE_HEX_COLOR_YELLOW
plane = FLOOR_PLANE
layer = WIRE_LAYER //Above hidden pipes, GAS_PIPE_HIDDEN_LAYER
anchored = TRUE
obj_flags = CAN_BE_HIT
var/linked_dirs = 0 //bitflag
var/node = FALSE //used for sprites display
var/cable_layer = CABLE_LAYER_2 //bitflag
var/datum/powernet/powernet
var/cable_color = CABLE_COLOR_YELLOW
var/is_fully_initialized = FALSE
/obj/structure/cable/layer1
color = CABLE_HEX_COLOR_RED
cable_color = CABLE_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 = CABLE_HEX_COLOR_BLUE
cable_color = CABLE_COLOR_BLUE
cable_layer = CABLE_LAYER_3
layer = WIRE_LAYER + 0.01
icon_state = "l4-1-2-4-8-node"
/obj/structure/cable/Initialize(mapload)
. = ..()
GLOB.cable_list += src //add it to the global cable list
Connect_cable()
AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE)
RegisterSignal(src, COMSIG_RAT_INTERACT, PROC_REF(on_rat_eat))
if(isturf(loc))
var/turf/turf_loc = loc
turf_loc.add_blueprints_preround(src)
return INITIALIZE_HINT_LATELOAD
/obj/structure/cable/LateInitialize()
update_appearance(UPDATE_ICON)
is_fully_initialized = TRUE
/obj/structure/cable/examine(mob/user)
. = ..()
if(isobserver(user))
. += get_power_info()
/obj/structure/cable/proc/on_rat_eat(datum/source, mob/living/basic/regal_rat/king)
SIGNAL_HANDLER
if(avail())
king.apply_damage(10)
playsound(king, 'sound/effects/sparks/sparks2.ogg', 100, TRUE)
deconstruct()
return COMPONENT_RAT_INTERACTED
///Set the linked indicator bitflags
/obj/structure/cable/proc/Connect_cable(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 = REVERSE_DIR(check_dir)
for(var/obj/structure/cable/C in TB)
if(C.cable_layer & cable_layer)
linked_dirs |= check_dir
C.linked_dirs |= inverse
// We will update on LateInitialize otherwise.
if (C.is_fully_initialized)
C.update_appearance(UPDATE_ICON)
if (is_fully_initialized)
update_appearance(UPDATE_ICON)
///Clear the linked indicator bitflags
/obj/structure/cable/proc/Disconnect_cable()
for(var/check_dir in GLOB.cardinals)
var/inverse = REVERSE_DIR(check_dir)
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_appearance()
/obj/structure/cable/Destroy() // called when a cable is deleted
Disconnect_cable()
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/atom_deconstruct(disassembled = TRUE)
var/obj/item/stack/cable_coil/cable = new(drop_location(), 1)
cable.set_cable_color(cable_color)
///////////////////////////////////
// General procedures
///////////////////////////////////
/obj/structure/cable/update_icon_state()
if(!linked_dirs)
icon_state = "l[cable_layer]-noconnection"
return ..()
// TODO: stop doing this shit in update_icon_state, this should be event based for the love of all that is holy
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 = "l[cable_layer]-[dir_string]"
icon_state = dir_string
return ..()
/obj/structure/cable/proc/handlecable(obj/item/W, mob/user, params)
var/turf/T = get_turf(src)
if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE)
return
if(W.tool_behaviour == TOOL_WIRECUTTER)
if (shock(user, 50))
return
user.visible_message(span_notice("[user] cuts the cable."), span_notice("You cut the cable."))
investigate_log("was cut by [key_name(usr)] in [AREACOORD(src)]", INVESTIGATE_WIRES)
deconstruct()
return
else if(W.tool_behaviour == TOOL_MULTITOOL)
to_chat(user, get_power_info())
shock(user, 5, 0.2)
add_fingerprint(user)
/obj/structure/cable/proc/get_power_info()
if(powernet?.avail > 0)
return span_danger("Total power: [display_power(powernet.avail)]\nLoad: [display_power(powernet.load)]\nExcess power: [display_power(surplus())]")
else
return span_danger("The cable is not powered.")
// 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(amount)
if(powernet)
return amount ? powernet.avail >= amount : 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 : REVERSE_DIR(direction) //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)
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_all_cable_connections(powernetless_only)
. = list()
var/turf/T
for(var/check_dir in GLOB.cardinals)
T = get_step(src, check_dir)
for(var/obj/structure/cable/C in T.contents - src)
. += 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_propagate_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 we already have one, just die
/obj/structure/cable/proc/propagate_if_no_network()
if(powernet)
return
var/datum/powernet/newPN = new()
propagate_network(src, newPN)
// 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_REF(auto_propagate_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
////////////////////////////////
#define CABLE_RESTRAINTS_COST 15
/obj/item/stack/cable_coil
name = "cable coil"
custom_price = PAYCHECK_LOWER * 0.8
gender = NEUTER //That's a cable coil sounds better than that's some cable coils
icon = 'icons/obj/stack_objects.dmi'
icon_state = "coil"
inhand_icon_state = "coil_yellow"
base_icon_state = "coil"
novariants = FALSE
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 = CABLE_HEX_COLOR_YELLOW
desc = "A coil of insulated power cable."
throwforce = 0
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 5
mats_per_unit = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*0.1, /datum/material/glass=SMALL_MATERIAL_AMOUNT*0.1)
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BELT
attack_verb_continuous = list("whips", "lashes", "disciplines", "flogs")
attack_verb_simple = list("whip", "lash", "discipline", "flog")
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'
cost = 1
source = /datum/robot_energy_storage/wire
var/cable_color = CABLE_COLOR_YELLOW
var/obj/structure/cable/target_type = /obj/structure/cable
var/target_layer = CABLE_LAYER_2
/obj/item/stack/cable_coil/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
pixel_x = base_pixel_x + rand(-2, 2)
pixel_y = base_pixel_y + rand(-2, 2)
AddElement(/datum/element/update_icon_updates_onmob)
update_appearance()
/obj/item/stack/cable_coil/examine(mob/user)
. = ..()
. += "<b>Use it in hand</b> to change the layer you are placing on, amongst other things."
/obj/item/stack/cable_coil/update_name()
. = ..()
name = "cable [(amount < 3) ? "piece" : "coil"]"
/obj/item/stack/cable_coil/update_desc()
. = ..()
desc = "A [(amount < 3) ? "piece" : "coil"] of insulated power cable."
/obj/item/stack/cable_coil/proc/set_cable_color(new_color)
color = GLOB.cable_colors[new_color]
cable_color = new_color
update_appearance(UPDATE_ICON)
/obj/item/stack/cable_coil/update_icon_state()
if(novariants)
return
. = ..()
icon_state = "[base_icon_state][amount < 3 ? amount : ""]"
inhand_icon_state = "coil_[cable_color]"
/obj/item/stack/cable_coil/suicide_act(mob/living/user)
if(locate(/obj/structure/chair/stool) in get_turf(user))
user.visible_message(span_suicide("[user] is making a noose with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
else
user.visible_message(span_suicide("[user] is strangling [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
return OXYLOSS
/obj/item/stack/cable_coil/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
if(!ISADVANCEDTOOLUSER(user))
to_chat(user, span_warning("You don't have the dexterity to do this!"))
return FALSE
if(user.incapacitated || !user.Adjacent(src))
return FALSE
return TRUE
/obj/item/stack/cable_coil/attack_self(mob/living/user)
if(!user)
return
var/image/restraints_icon = image(icon = 'icons/obj/weapons/restraints.dmi', icon_state = "cuff")
restraints_icon.maptext = MAPTEXT("<span [amount >= CABLE_RESTRAINTS_COST ? "" : "style='color: red'"]>[CABLE_RESTRAINTS_COST]</span>")
restraints_icon.color = color
var/list/radial_menu = list(
"Layer 1" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-red"),
"Layer 2" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-yellow"),
"Layer 3" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-blue"),
"Multilayer cable hub" = image(icon = 'icons/obj/pipes_n_cables/structures.dmi', icon_state = "cable_bridge"),
"Multi Z layer cable hub" = image(icon = 'icons/obj/pipes_n_cables/structures.dmi', icon_state = "cablerelay-broken-cable"),
"Cable restraints" = restraints_icon
)
var/layer_result = show_radial_menu(user, src, radial_menu, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE)
if(!check_menu(user))
return
switch(layer_result)
if("Layer 1")
set_cable_color(CABLE_COLOR_RED)
target_type = /obj/structure/cable/layer1
target_layer = CABLE_LAYER_1
novariants = FALSE
if("Layer 2")
set_cable_color(CABLE_COLOR_YELLOW)
target_type = /obj/structure/cable
target_layer = CABLE_LAYER_2
novariants = FALSE
if("Layer 3")
set_cable_color(CABLE_COLOR_BLUE)
target_type = /obj/structure/cable/layer3
target_layer = CABLE_LAYER_3
novariants = FALSE
if("Multilayer cable hub")
name = "multilayer cable hub"
desc = "A multilayer cable hub."
icon_state = "cable_bridge"
set_cable_color(CABLE_COLOR_WHITE)
target_type = /obj/structure/cable/multilayer
target_layer = CABLE_LAYER_2
novariants = TRUE
if("Multi Z layer cable hub")
name = "multi z layer cable hub"
desc = "A multi-z layer cable hub."
icon_state = "cablerelay-broken-cable"
set_cable_color(CABLE_COLOR_WHITE)
target_type = /obj/structure/cable/multilayer/multiz
target_layer = CABLE_LAYER_2
novariants = TRUE
if("Cable restraints")
if (amount >= CABLE_RESTRAINTS_COST)
if(use(CABLE_RESTRAINTS_COST))
var/obj/item/restraints/handcuffs/cable/restraints = new(null, cable_color)
user.put_in_hands(restraints)
update_appearance()
///////////////////////////////////
// General procedures
///////////////////////////////////
//you can use wires to heal robotics
/obj/item/stack/cable_coil/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!ishuman(interacting_with))
return NONE
if(user.combat_mode)
return NONE
return try_heal_loop(interacting_with, user)
/obj/item/stack/cable_coil/proc/try_heal_loop(atom/interacting_with, mob/living/user, repeating = FALSE)
var/mob/living/carbon/human/attacked_humanoid = interacting_with
var/obj/item/clothing/under/uniform = attacked_humanoid.w_uniform
if(uniform?.repair_sensors(src, user))
return ITEM_INTERACT_SUCCESS
var/obj/item/bodypart/affecting = attacked_humanoid.get_bodypart(check_zone(user.zone_selected))
if(isnull(affecting) || !IS_ROBOTIC_LIMB(affecting))
return NONE
if (!affecting.burn_dam)
balloon_alert(user, "limb not damaged")
return ITEM_INTERACT_BLOCKING
user.visible_message(span_notice("[user] starts to fix some of the wires in [attacked_humanoid == user ? user.p_their() : "[attacked_humanoid]'s"] [affecting.name]."),
span_notice("You start fixing some of the wires in [attacked_humanoid == user ? "your" : "[attacked_humanoid]'s"] [affecting.name]."))
var/use_delay = repeating ? 1 SECONDS : 0
if(user == attacked_humanoid)
use_delay = self_delay // SKYRAT EDIT CHANGE - ORIGINAL: use_delay = 5 SECONDS
// SKYRAT EDIT ADDITION START
else
use_delay = other_delay
// SKYRAT EDIT ADDITION END
if(!do_after(user, use_delay, attacked_humanoid))
return ITEM_INTERACT_BLOCKING
if (!attacked_humanoid.item_heal(user, brute_heal = 0, burn_heal = 15, heal_message_brute = "dents", heal_message_burn = "burnt wires", required_bodytype = BODYTYPE_ROBOTIC))
return ITEM_INTERACT_BLOCKING
if (use(1) && amount > 0)
INVOKE_ASYNC(src, PROC_REF(try_heal_loop), interacting_with, user, TRUE)
return ITEM_INTERACT_SUCCESS
///////////////////////////////////////////////
// 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.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !T.can_have_cabling())
to_chat(user, span_warning("You can only lay cables on catwalks and plating!"))
return
if(get_amount() < 1) // Out of cable
to_chat(user, span_warning("There is no cable left!"))
return
if(get_dist(T,user) > 1) // Too far
to_chat(user, span_warning("You can't lay cable at a place that far away!"))
return
for(var/obj/structure/cable/C in T)
if(C.cable_layer & target_layer)
to_chat(user, span_warning("There's already a cable at that position!"))
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
C.deconstruct()
return C
/obj/item/stack/cable_coil/five
amount = 5
/obj/item/stack/cable_coil/thirty
amount = 30
/obj/item/stack/cable_coil/cut
amount = null
icon_state = "coil2"
worn_icon_state = "coil"
base_icon_state = "coil2"
/obj/item/stack/cable_coil/cut/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
if(!amount)
amount = rand(1,2)
. = ..()
pixel_x = base_pixel_x + rand(-2, 2)
pixel_y = base_pixel_y + rand(-2, 2)
update_appearance()
#undef CABLE_RESTRAINTS_COST
#undef UNDER_SMES
#undef UNDER_TERMINAL
///multilayer cable to connect different layers
/obj/structure/cable/multilayer
name = "multilayer cable hub"
desc = "A flexible, superconducting insulated multilayer hub for heavy-duty multilayer power transfer."
icon = 'icons/obj/pipes_n_cables/structures.dmi'
icon_state = "cable_bridge"
cable_layer = CABLE_LAYER_2
layer = WIRE_LAYER - 0.02 //Below all cables Disabled layers can lay over hub
color = CABLE_COLOR_WHITE
/obj/structure/cable/multilayer/update_icon_state()
SHOULD_CALL_PARENT(FALSE)
return
/obj/structure/cable/multilayer/update_icon()
. = ..()
underlays.Cut()
var/mutable_appearance/cable_node_3 = mutable_appearance('icons/obj/pipes_n_cables/layer_cable.dmi', "l4-1-2-4-8-node")
cable_node_3.color = CABLE_COLOR_BLUE
cable_node_3?.alpha = cable_layer & CABLE_LAYER_3 ? 255 : 0
underlays += cable_node_3
var/mutable_appearance/cable_node_2 = mutable_appearance('icons/obj/pipes_n_cables/layer_cable.dmi', "l2-1-2-4-8-node")
cable_node_2.color = CABLE_COLOR_YELLOW
cable_node_2?.alpha = cable_layer & CABLE_LAYER_2 ? 255 : 0
underlays += cable_node_2
var/mutable_appearance/cable_node_1 = mutable_appearance('icons/obj/pipes_n_cables/layer_cable.dmi', "l1-1-2-4-8-node")
cable_node_1.color = CABLE_COLOR_RED
cable_node_1?.alpha = cable_layer & CABLE_LAYER_1 ? 255 : 0
underlays += cable_node_1
var/mutable_appearance/machinery_node = mutable_appearance('icons/obj/pipes_n_cables/layer_cable.dmi', "l2-noconnection")
machinery_node.color = "black"
underlays += machinery_node
/obj/structure/cable/multilayer/Initialize(mapload)
. = ..()
var/turf/T = get_turf(src)
for(var/obj/structure/cable/C in T.contents - src)
if(C.cable_layer & cable_layer)
C.deconstruct() // remove adversary cable
if(!mapload)
auto_propagate_cut_cable(src)
update_appearance()
/obj/structure/cable/multilayer/examine(mob/user)
. += ..()
. += span_notice("L1:[cable_layer & CABLE_LAYER_1 ? "Connect" : "Disconnect"].")
. += span_notice("L2:[cable_layer & CABLE_LAYER_2 ? "Connect" : "Disconnect"].")
. += span_notice("L3:[cable_layer & CABLE_LAYER_3 ? "Connect" : "Disconnect"].")
GLOBAL_LIST(hub_radial_layer_list)
/obj/structure/cable/multilayer/attack_robot(mob/user)
attack_hand(user)
/obj/structure/cable/multilayer/attack_hand(mob/living/user, list/modifiers)
if(!user)
return
if(!GLOB.hub_radial_layer_list)
GLOB.hub_radial_layer_list = list(
"Layer 1" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-red"),
"Layer 2" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-yellow"),
"Layer 3" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-blue")
)
var/layer_result = show_radial_menu(user, src, GLOB.hub_radial_layer_list, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE)
if(!check_menu(user))
return
var/CL
switch(layer_result)
if("Layer 1")
CL = CABLE_LAYER_1
to_chat(user, span_warning("You toggle L1 connection."))
if("Layer 2")
CL = CABLE_LAYER_2
to_chat(user, span_warning("You toggle L2 connection."))
if("Layer 3")
CL = CABLE_LAYER_3
to_chat(user, span_warning("You toggle L3 connection."))
cut_cable_from_powernet(FALSE)
Disconnect_cable()
cable_layer ^= CL
Connect_cable(TRUE)
Reload()
/obj/structure/cable/multilayer/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
if(!ISADVANCEDTOOLUSER(user))
to_chat(user, span_warning("You don't have the dexterity to do this!"))
return FALSE
if(user.incapacitated || !user.Adjacent(src))
return FALSE
return TRUE
///Reset powernet in this hub.
/obj/structure/cable/multilayer/proc/Reload()
var/turf/T = get_turf(src)
for(var/obj/structure/cable/C in T.contents - src)
if(C.cable_layer & cable_layer)
C.deconstruct() // remove adversary cable
auto_propagate_cut_cable(src) // update the powernets
/obj/structure/cable/multilayer/click_ctrl(mob/user)
to_chat(user, span_warning("You push the reset button."))
addtimer(CALLBACK(src, PROC_REF(Reload)), 10, TIMER_UNIQUE) //spam protect
return CLICK_ACTION_SUCCESS
// This is a mapping aid. In order for this to be placed on a map and function, all three layers need to have their nodes active
/obj/structure/cable/multilayer/connected
cable_layer = CABLE_LAYER_1 | CABLE_LAYER_2 | CABLE_LAYER_3