Files
2026-06-02 20:16:50 +00:00

437 lines
14 KiB
Plaintext

///////////////////////////////////////////////
// The cable coil object, used for laying cable
///////////////////////////////////////////////
#define HEALPERCABLE 3
#define MAXCABLEPERHEAL 8
GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe/cable_restraints("cable restraints", /obj/item/restraints/handcuffs/cable, 15)))
/obj/item/stack/cable_coil
name = "cable coil"
desc = "A low-cost superconducting cable."
singular_name = "cable"
icon = 'icons/obj/power.dmi'
icon_state = "coil"
inhand_icon_state = "coil"
belt_icon = "cable_coil"
hitsound = 'sound/weapons/whip.ogg'
amount = MAXCOIL
max_amount = MAXCOIL
merge_type = /obj/item/stack/cable_coil // This is here to let its children merge between themselves
color = COLOR_RED
w_class = WEIGHT_CLASS_SMALL
throw_range = 5
materials = list(MAT_METAL = 15, MAT_GLASS = 10)
flags = CONDUCT
slot_flags = ITEM_SLOT_BELT
attack_verb = list("whipped", "lashed", "disciplined", "flogged")
usesound = 'sound/items/deconstruct.ogg'
/// Deletes the cable coil upon getting empty.
/// Used for RCL cable spools to stop the stack from being eaten.
/// Type of cable this coil makes
var/cable_type = /obj/structure/cable
/// Bitflag of the types of cable we can add cable to with this coil.
/// This is different to the cable connection flag since it only governs connecting on the same tile
var/cable_merge_id = CABLE_MERGE_LOW_POWER
/obj/item/stack/cable_coil/Initialize(mapload, length, paramcolor)
. = ..()
if(paramcolor)
color = paramcolor
update_icon()
recipes = GLOB.cable_coil_recipes
update_wclass()
/obj/item/stack/cable_coil/should_play_hitsound(damage)
return TRUE
/obj/item/stack/cable_coil/random/change_stack(mob/user, amount)
var/obj/item/stack/cable_coil/new_stack = ..()
new_stack.color = color
return new_stack
/// Used to decouple the physical color of the cable from the logical color. Used by RCL to stop its spool holder from being colored in.
/obj/item/stack/cable_coil/proc/get_cable_color()
return color
/obj/item/stack/cable_coil/update_name()
. = ..()
if(amount > 2)
name = "cable coil"
else
name = "cable piece"
/obj/item/stack/cable_coil/update_icon_state()
if(amount == 1)
icon_state = "coil1"
else if(amount == 2)
icon_state = "coil2"
else
icon_state = "coil"
/obj/item/stack/cable_coil/proc/update_wclass()
if(amount == 1)
w_class = WEIGHT_CLASS_TINY
else
w_class = WEIGHT_CLASS_SMALL
/obj/item/stack/cable_coil/can_merge(obj/item/stack/check, inhand = FALSE)
. = FALSE
if(istype(check, merge_type))
var/obj/item/stack/cable_coil/coil_check = check
. = coil_check.cable_merge_id == cable_merge_id
return . && ..()
//you can use wires to heal robotics
/obj/item/stack/cable_coil/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(!ishuman(target))
return ..()
var/mob/living/carbon/human/H = target
var/obj/item/organ/external/S = H.bodyparts_by_name[user.zone_selected]
if(!S && ismachineperson(target) && user.a_intent == INTENT_HELP)
to_chat(user, SPAN_NOTICE("[H.p_they(TRUE)] [H.p_are()] missing that limb!"))
return ITEM_INTERACT_COMPLETE
if(!S?.is_robotic() || user.a_intent != INTENT_HELP || S.open == ORGAN_SYNTHETIC_OPEN)
return ..()
if(S.burn_dam > ROBOLIMB_SELF_REPAIR_CAP)
to_chat(user, SPAN_DANGER("The damage is far too severe to patch over externally."))
return ITEM_INTERACT_COMPLETE
if(!S.burn_dam)
to_chat(user, SPAN_NOTICE("Nothing to fix!"))
return ITEM_INTERACT_COMPLETE
if(H == user)
if(!do_mob(user, H, 10))
return ITEM_INTERACT_COMPLETE
var/cable_used = 0
var/childlist
if(!isnull(S.children))
childlist = S.children.Copy()
var/parenthealed = FALSE
while(cable_used <= MAXCABLEPERHEAL && amount >= 1)
var/obj/item/organ/external/E
if(S.burn_dam)
E = S
else if(LAZYLEN(childlist))
E = pick_n_take(childlist)
if(!E.burn_dam || E.burn_dam >= ROBOLIMB_SELF_REPAIR_CAP || !E.is_robotic())
continue
else if(S.parent && !parenthealed)
E = S.parent
parenthealed = TRUE
if(!E.burn_dam || E.burn_dam >= ROBOLIMB_SELF_REPAIR_CAP || !E.is_robotic())
break
else
break
while(cable_used <= MAXCABLEPERHEAL && E.burn_dam && amount >= 1)
use(1, destroy_upon_empty)
cable_used += 1
E.heal_damage(0, HEALPERCABLE, 0, TRUE)
H.UpdateDamageIcon()
user.visible_message(SPAN_ALERT("[user] repairs some burn damage on [H]'s [E.name] with [src]."))
return ITEM_INTERACT_COMPLETE
/obj/item/stack/cable_coil/split()
var/obj/item/stack/cable_coil/C = ..()
C.color = color
return C
/obj/item/stack/cable_coil/item_interaction(mob/living/user, obj/item/used, list/modifiers)
if(istype(used, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/C = used
if(C.cable_merge_id != cable_merge_id)
to_chat(user, "These coils are of different types.")
return ITEM_INTERACT_COMPLETE
if(C.get_amount() >= C.max_amount)
to_chat(user, "The coil is as long as it will get.")
return ITEM_INTERACT_COMPLETE
// Cable merging is handled by parent proc.
if((C.get_amount() + get_amount() <= C.max_amount))
to_chat(user, "You join the cable coils together.")
return ..()
to_chat(user, "You transfer [get_amount_transferred()] length\s of cable from one coil to the other.")
return ..()
if(istype(used, /obj/item/toy/crayon))
var/obj/item/toy/crayon/C = used
cable_color(C.dye_color)
return ITEM_INTERACT_COMPLETE
return ..()
///////////////////////////////////////////////
// Cable laying procedures
//////////////////////////////////////////////
/obj/item/stack/cable_coil/proc/get_new_cable(location)
var/obj/structure/cable/C = new cable_type(location)
C.cable_color(get_cable_color())
return C
/// called when cable_coil is clicked on a turf/simulated/floor
/obj/item/stack/cable_coil/proc/place_turf(turf/T, mob/user, cable_direction)
if(!isturf(user.loc))
return
if(!isturf(T) || T.intact || !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
if(!cable_direction) //If we weren't given a direction, come up with one! (Called as null from catwalk.dm and floor.dm)
if(user.loc == T)
cable_direction = user.dir //If laying on the tile we're on, lay in the direction we're facing
else
cable_direction = get_dir(T, user) //otherwise get direction from us to the turf we've clicked
for(var/obj/structure/cable/LC in T)
if(LC.d2 == cable_direction && LC.d1 == NO_DIRECTION) //there's already a cable here that would be exactly what we just placed!
to_chat(user, SPAN_WARNING("There's already a cable at that position!"))
return
var/obj/structure/cable/C = get_new_cable(T)
//set up the new cable
C.d1 = NO_DIRECTION //it's a O-X node cable
C.d2 = cable_direction
C.add_fingerprint(user)
C.update_icon()
//create a new powernet with the cable, if needed it will be merged later
var/datum/regional_powernet/new_powernet = new()
new_powernet.add_cable(C)
C.merge_connected_networks(C.d2) //merge the powernet with adjacents powernets
C.merge_connected_networks_on_turf() //merge the powernet with on turf powernets
if(IS_DIR_DIAGONAL(C.d2))// if the cable is layed diagonally, check the others 2 possible directions
C.merge_diagonal_networks(C.d2)
use(1, destroy_upon_empty)
if(C.shock(user, 50))
if(prob(50)) //fail
C.deconstruct()
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CABLE_UPDATED, T)
return C
/// called when cable_coil is click on an installed obj/cable or click on a turf that already contains a "node" cable
/obj/item/stack/cable_coil/proc/cable_join(obj/structure/cable/C, mob/user)
if(C.merge_id != cable_merge_id)
return
var/turf/U = user.loc
if(!isturf(U))
return
var/turf/T = get_turf(C)
// sanity checks, also stop use interacting with T-scanner revealed cable
if(!isturf(T) || T.intact || T.transparent_floor)
return
// make sure it's close enough
if(get_dist(C, user) > 1)
to_chat(user, SPAN_WARNING("You can't lay cable at a place that far away!"))
return
//if clicked on the turf we're standing on, try to put a cable in the direction we're facing
if(U == T)
place_turf(T,user)
return
var/new_direction = get_dir(C, user)
// one end of the clicked cable is pointing towards us
if(C.d1 == new_direction || C.d2 == new_direction)
if(U.intact || U.transparent_floor) // can't place a cable if the floor is complete
to_chat(user, SPAN_WARNING("You can't lay cable there unless the floor tiles are removed!"))
return
// cable is pointing at us, we're standing on an open tile
// so create a stub pointing at the clicked cable on our tile
var/direction_flipped = turn(new_direction, 180) // the opposite direction
for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already
if(LC.d1 == direction_flipped || LC.d2 == direction_flipped)
to_chat(user, SPAN_WARNING("There's already a cable at that position!"))
return
var/obj/structure/cable/NC = get_new_cable (U)
NC.d1 = 0
NC.d2 = direction_flipped
NC.add_fingerprint(user)
NC.update_icon()
//create a new powernet with the cable, if needed it will be merged later
var/datum/regional_powernet/newPN = new()
newPN.add_cable(NC)
NC.merge_connected_networks(NC.d2) //merge the powernet with adjacents powernets
NC.merge_connected_networks_on_turf() //merge the powernet with on turf powernets
if(IS_DIR_DIAGONAL(NC.d2)) // if the cable is layed diagonally, check the others 2 possible directions
NC.merge_diagonal_networks(NC.d2)
use(1, destroy_upon_empty)
if(NC.shock(user, 50))
if(prob(50)) //fail
NC.deconstruct()
return
// exisiting cable doesn't point at our position, so see if it's a stub
else if(C.d1 == 0)
// if so, make it a full cable pointing from it's old direction to our new_direction
var/nd1 = C.d2 // these will be the new directions
var/nd2 = new_direction
if(nd1 > nd2) // swap directions to match icons/states
nd1 = new_direction
nd2 = C.d2
for(var/obj/structure/cable/LC in T) // check to make sure there's no matching cable
if(LC == C) // skip the cable we're interacting with
continue
if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1)) // make sure no cable matches either direction
to_chat(user, SPAN_WARNING("There's already a cable at that position!"))
return
C.cable_color(get_cable_color())
C.d1 = nd1
C.d2 = nd2
C.add_fingerprint()
C.update_icon()
C.merge_connected_networks(C.d1) //merge the powernets...
C.merge_connected_networks(C.d2) //...in the two new cable directions
C.merge_connected_networks_on_turf()
if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions
C.merge_connected_networks(C.d1)
if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions
C.merge_connected_networks(C.d2)
use(1, destroy_upon_empty)
if(C.shock(user, 50))
if(prob(50)) //fail
C.deconstruct()
return
C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets.
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CABLE_UPDATED, T)
//////////////////////////////
// Misc.
/////////////////////////////
/obj/item/stack/cable_coil/proc/cable_color(colorC)
if(!colorC)
color = COLOR_RED
else if(colorC == "rainbow")
color = color_rainbow()
else if(colorC == "orange") // byond only knows 16 colors by name, and orange isn't one of them
color = COLOR_ORANGE
else if(colorC == "pink")
color = COLOR_PINK // nor is pink.. thanks byond
else
color = colorC
/obj/item/stack/cable_coil/suicide_act(mob/user)
if(locate(/obj/structure/chair/stool) in user.loc)
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_themselves()] with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
return OXYLOSS
/obj/item/stack/cable_coil/proc/color_rainbow()
color = pick(COLOR_RED, COLOR_PINK, COLOR_ORANGE, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_WHITE)
return color
/obj/item/stack/cable_coil/five
amount = 5
/obj/item/stack/cable_coil/ten
amount = 10
/obj/item/stack/cable_coil/pink
color = COLOR_PINK
/obj/item/stack/cable_coil/yellow
color = COLOR_YELLOW
/obj/item/stack/cable_coil/orange
color = COLOR_ORANGE
/obj/item/stack/cable_coil/green
color = COLOR_GREEN
/obj/item/stack/cable_coil/cyan
color = COLOR_CYAN
/obj/item/stack/cable_coil/blue
color = COLOR_BLUE
/obj/item/stack/cable_coil/white
color = COLOR_WHITE
/obj/item/stack/cable_coil/random/Initialize(mapload)
. = ..()
color = pick(COLOR_RED, COLOR_PINK, COLOR_ORANGE, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_WHITE)
/obj/item/stack/cable_coil/extra_insulated
name = "heavy duty cable coil"
singular_name = "heavy duty cable"
icon = 'icons/obj/cable_coils/heavy_duty.dmi'
materials = list(MAT_METAL = 30, MAT_GLASS = 30)
color = null
cable_type = /obj/structure/cable/extra_insulated
cable_merge_id = CABLE_MERGE_HIGH_POWER
req_one_access = list(ACCESS_ENGINE, ACCESS_CE)
/obj/item/stack/cable_coil/extra_insulated/proc/toggle_connection(user)
if(allowed(user))
if(cable_type == /obj/structure/cable/extra_insulated/pre_connect)
cable_type = /obj/structure/cable/extra_insulated
icon = 'icons/obj/cable_coils/heavy_duty.dmi'
else
cable_type = /obj/structure/cable/extra_insulated/pre_connect
icon = 'icons/obj/cable_coils/heavy_duty_connected.dmi'
/obj/item/stack/cable_coil/extra_insulated/item_interaction(mob/living/user, obj/item/used, list/modifiers)
if(used.GetID())
toggle_connection(user)
return ITEM_INTERACT_COMPLETE
return ..()
/obj/item/stack/cable_coil/cut
icon_state = "coil2"
/obj/item/stack/cable_coil/cut/Initialize(mapload)
. = ..()
src.amount = rand(1,2)
update_appearance(UPDATE_NAME|UPDATE_ICON_STATE)
update_wclass()
#undef HEALPERCABLE
#undef MAXCABLEPERHEAL