Files
Bubberstation/code/modules/power/pipecleaners.dm
MrMelbert 5261efb67f Re-refactors batons / Refactors attack chain force modifiers (#90809)
## About The Pull Request

Melee attack chain now has a list passed along with it,
`attack_modifiers`, which you can stick force modifiers to change the
resulting attack

This is basically a soft implementation of damage packets until a more
definitive pr, but one that only applies to item attack chain, and not
unarmed attacks.

This change was done to facilitate a baton refactor - batons no longer
hack together their own attack chain, and are now integrated straight
into the real attack chain. This refactor itself was done because batons
don't send any attack signals, which has been annoying in the past (for
swing combat).

## Changelog

🆑 Melbert
refactor: Batons have been refactored again. Baton stuns now properly
count as an attack, when before it was a nothing. Report any oddities,
particularly in regards to harmbatonning vs normal batonning.
refactor: The method of adjusting item damage mid-attack has been
refactored - some affected items include the Nullblade and knives.
Report any strange happenings with damage numbers.
refactor: A few objects have been moved to the new interaction chain -
records consoles, mawed crucible, alien weeds and space vines, hedges,
restaurant portals, and some mobs - to name a few.
fix: Spears only deal bonus damage against secure lockers, not all
closet types (including crates)
/🆑
2025-05-19 13:32:12 +10:00

537 lines
16 KiB
Plaintext

//This is the old cable code, but minus any actual powernet logic
//Wireart is fun
///////////////////////////////
//CABLE STRUCTURE
///////////////////////////////
////////////////////////////////
// Definitions
////////////////////////////////
/* Cable directions (d1 and d2)
* 9 1 5
* \ | /
* 8 - 0 - 4
* / | \
* 10 2 6
If d1 = 0 and d2 = 0, there's no pipe_cleaner
If d1 = 0 and d2 = dir, it's a O-X pipe_cleaner, getting from the center of the tile to dir (knot pipe_cleaner)
If d1 = dir1 and d2 = dir2, it's a full X-X pipe_cleaner, getting from dir1 to dir2
By design, d1 is the smallest direction and d2 is the highest
*/
/obj/structure/pipe_cleaner
name = "pipe cleaner"
desc = "A bendable piece of wire covered in fuzz. Fun for arts and crafts!"
icon = 'icons/obj/pipes_n_cables/pipe_cleaner.dmi'
icon_state = "0-1"
layer = WIRE_LAYER //Above hidden pipes, GAS_PIPE_HIDDEN_LAYER
plane = FLOOR_PLANE
anchored = TRUE
obj_flags = CAN_BE_HIT
color = CABLE_HEX_COLOR_RED
///For updating inhand icons.
var/pipecleaner_color = CABLE_COLOR_RED
/// Pipe_cleaner direction 1 (see above)
var/d1 = 0
/// pipe_cleaner direction 2 (see above)
var/d2 = 1
/// Internal cable stack
var/obj/item/stack/pipe_cleaner_coil/stored
/obj/structure/pipe_cleaner/yellow
color = CABLE_HEX_COLOR_YELLOW
pipecleaner_color = CABLE_COLOR_YELLOW
/obj/structure/pipe_cleaner/green
color = CABLE_HEX_COLOR_GREEN
pipecleaner_color = CABLE_COLOR_GREEN
/obj/structure/pipe_cleaner/blue
color = CABLE_HEX_COLOR_BLUE
pipecleaner_color = CABLE_COLOR_BLUE
/obj/structure/pipe_cleaner/pink
color = CABLE_HEX_COLOR_PINK
pipecleaner_color = CABLE_COLOR_YELLOW
/obj/structure/pipe_cleaner/orange
color = CABLE_HEX_COLOR_ORANGE
pipecleaner_color = CABLE_COLOR_ORANGE
/obj/structure/pipe_cleaner/cyan
color = CABLE_HEX_COLOR_CYAN
pipecleaner_color = CABLE_COLOR_CYAN
/obj/structure/pipe_cleaner/white
color = CABLE_HEX_COLOR_WHITE
pipecleaner_color = CABLE_COLOR_WHITE
/obj/structure/pipe_cleaner/brown
color = CABLE_HEX_COLOR_BROWN
pipecleaner_color = CABLE_COLOR_BROWN
// the power pipe_cleaner object
/obj/structure/pipe_cleaner/Initialize(mapload, param_color)
. = ..()
// ensure d1 & d2 reflect the icon_state for entering and exiting pipe_cleaner
var/dash = findtext(icon_state, "-")
d1 = text2num(copytext(icon_state, 1, dash))
d2 = text2num(copytext(icon_state, dash + length(icon_state[dash])))
if(d1)
stored = new/obj/item/stack/pipe_cleaner_coil(null, 2, null, null, null)
else
stored = new/obj/item/stack/pipe_cleaner_coil(null, 1, null, null, null)
if(!param_color)
param_color = "white"
color = GLOB.cable_colors[param_color]
pipecleaner_color = param_color
stored?.set_pipecleaner_color(pipecleaner_color)
update_appearance()
if(isturf(loc))
var/turf/turf_loc = loc
turf_loc.add_blueprints_preround(src)
/obj/structure/pipe_cleaner/Destroy() // called when a pipe_cleaner is deleted
//If we have a stored item at this point, lets just delete it, since that should be
//handled by deconstruction
if(stored)
QDEL_NULL(stored)
return ..() // then go ahead and delete the pipe_cleaner
/obj/structure/pipe_cleaner/atom_deconstruct(disassembled = TRUE)
var/turf/location = get_turf(loc)
if(location)
stored.forceMove(location)
stored = null
else
qdel(stored)
///////////////////////////////////
// General procedures
///////////////////////////////////
/obj/structure/pipe_cleaner/update_icon_state()
icon_state = "[d1]-[d2]"
return ..()
/obj/structure/pipe_cleaner/update_icon()
. = ..()
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
// Items usable on a pipe_cleaner :
// - Wirecutters : cut it duh !
// - pipe cleaner coil : merge pipe cleaners
//
/obj/structure/pipe_cleaner/proc/handlecable(obj/item/W, mob/user, params)
if(W.tool_behaviour == TOOL_WIRECUTTER)
cut_pipe_cleaner(user)
return
else if(istype(W, /obj/item/stack/pipe_cleaner_coil))
var/obj/item/stack/pipe_cleaner_coil/coil = W
if (coil.get_amount() < 1)
to_chat(user, span_warning("Not enough pipe cleaner!"))
return
coil.pipe_cleaner_join(src, user)
add_fingerprint(user)
/obj/structure/pipe_cleaner/proc/cut_pipe_cleaner(mob/user)
user.visible_message(span_notice("[user] pulls up the pipe cleaner."), span_notice("You pull up the pipe cleaner."))
stored.add_fingerprint(user)
investigate_log("was pulled up by [key_name(usr)] in [AREACOORD(src)]", INVESTIGATE_WIRES)
deconstruct()
/obj/structure/pipe_cleaner/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
handlecable(W, user, modifiers)
/obj/structure/pipe_cleaner/singularity_pull(atom/singularity, current_size)
..()
if(current_size >= STAGE_FIVE)
deconstruct()
/obj/structure/pipe_cleaner/proc/update_stored(length = 1, colorC = COLOR_RED)
stored.amount = length
stored.color = colorC
stored.update_appearance()
/obj/structure/pipe_cleaner/click_alt(mob/living/user)
cut_pipe_cleaner(user)
return CLICK_ACTION_SUCCESS
///////////////////////////////////////////////
// The pipe cleaner coil object, used for laying pipe cleaner
///////////////////////////////////////////////
////////////////////////////////
// Definitions
////////////////////////////////
/obj/item/stack/pipe_cleaner_coil
name = "pipe cleaner coil"
desc = "A coil of pipe cleaners. Good for arts and crafts, not to build with."
custom_price = PAYCHECK_CREW * 0.5
gender = NEUTER //That's a pipe_cleaner coil sounds better than that's some pipe_cleaner coils
icon = 'icons/obj/stack_objects.dmi'
icon_state = "pipecleaner"
inhand_icon_state = "coil_red"
worn_icon_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/pipe_cleaner_coil // This is here to let its children merge between themselves
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 = "pipe cleaner piece"
full_w_class = WEIGHT_CLASS_SMALL
grind_results = list(/datum/reagent/copper = 2) //2 copper per pipe_cleaner in the coil
usesound = 'sound/items/deconstruct.ogg'
cost = 1
source = /datum/robot_energy_storage/pipe_cleaner
color = CABLE_HEX_COLOR_RED
///For updating inhand icons.
var/pipecleaner_color = CABLE_COLOR_RED
/obj/item/stack/pipe_cleaner_coil/cyborg/attack_self(mob/user)
var/list/pipe_cleaner_colors = GLOB.cable_colors
var/list/possible_colors = list()
for(var/color in pipe_cleaner_colors)
var/image/pipe_icon = image(icon = src.icon, icon_state = src.icon_state)
pipe_icon.color = pipe_cleaner_colors[color]
possible_colors += list("[color]" = pipe_icon)
var/selected_color = show_radial_menu(user, src, possible_colors, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 40, require_near = TRUE)
if(!selected_color)
return
set_pipecleaner_color(selected_color)
/**
* Checks if we are allowed to interact with a radial menu
*
* Arguments:
* * user The mob interacting with the menu
*/
/obj/item/stack/pipe_cleaner_coil/cyborg/proc/check_menu(mob/user)
if(!istype(user))
return FALSE
if(!user.is_holding(src))
return FALSE
if(user.incapacitated)
return FALSE
return TRUE
/obj/item/stack/pipe_cleaner_coil/proc/set_pipecleaner_color(new_color)
color = GLOB.cable_colors[new_color]
pipecleaner_color = new_color
update_appearance()
/obj/item/stack/pipe_cleaner_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/pipe_cleaner_coil/Initialize(mapload, new_amount = null, list/mat_override=null, mat_amt=1, param_color = null)
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
if(param_color)
set_pipecleaner_color(param_color)
if(!color)
var/list/pipe_cleaner_colors = GLOB.cable_colors
var/random_color = pick(pipe_cleaner_colors)
set_pipecleaner_color(random_color)
pixel_x = base_pixel_x + rand(-2, 2)
pixel_y = base_pixel_y + rand(-2, 2)
update_appearance()
///////////////////////////////////
// General procedures
///////////////////////////////////
/obj/item/stack/pipe_cleaner_coil/update_name()
. = ..()
name = "pipe cleaner [amount < 3 ? "piece" : "coil"]"
/obj/item/stack/pipe_cleaner_coil/update_icon_state()
. = ..()
icon_state = "[initial(icon_state)][amount < 3 ? amount : ""]"
inhand_icon_state = "coil_[pipecleaner_color]"
/obj/item/stack/pipe_cleaner_coil/update_icon()
. = ..()
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
/obj/item/stack/pipe_cleaner_coil/attack_hand(mob/user, list/modifiers)
. = ..()
if(!.)
return
var/obj/item/stack/pipe_cleaner_coil/new_pipe_cleaner = .
if(istype(new_pipe_cleaner))
new_pipe_cleaner.set_pipecleaner_color(pipecleaner_color)
//add pipe_cleaners to the stack
/obj/item/stack/pipe_cleaner_coil/proc/give(extra)
if(amount + extra > max_amount)
amount = max_amount
else
amount += extra
update_appearance()
///////////////////////////////////////////////
// Cable laying procedures
//////////////////////////////////////////////
/obj/item/stack/pipe_cleaner_coil/proc/get_new_pipe_cleaner(location)
return new /obj/structure/pipe_cleaner(location, pipecleaner_color)
// called when pipe_cleaner_coil is clicked on a turf
/obj/item/stack/pipe_cleaner_coil/proc/place_turf(turf/T, mob/user, dirnew)
if(!isturf(user.loc))
return
if(!isturf(T) || !T.can_have_cabling())
to_chat(user, span_warning("You can only lay pipe cleaners on a solid floor!"))
return
if(get_amount() < 1) // Out of pipe_cleaner
to_chat(user, span_warning("There is no pipe cleaner left!"))
return
if(get_dist(T,user) > 1) // Too far
to_chat(user, span_warning("You can't lay pipe cleaner at a place that far away!"))
return
var/dirn
if(!dirnew) //If we weren't given a direction, come up with one! (Called as null from catwalk.dm and floor.dm)
if(user.loc == T)
dirn = user.dir //If laying on the tile we're on, lay in the direction we're facing
else
dirn = get_dir(T, user)
else
dirn = dirnew
for(var/obj/structure/pipe_cleaner/LC in T)
if(LC.d2 == dirn && LC.d1 == 0)
to_chat(user, span_warning("There's already a pipe cleaner at that position!"))
return
var/obj/structure/pipe_cleaner/C = get_new_pipe_cleaner(T)
//set up the new pipe_cleaner
C.d1 = 0 //it's a O-X node pipe_cleaner
C.d2 = dirn
C.add_fingerprint(user)
C.update_appearance()
use(1)
return C
// called when pipe_cleaner_coil is click on an installed obj/pipe_cleaner
// or click on a turf that already contains a "node" pipe_cleaner
/obj/item/stack/pipe_cleaner_coil/proc/pipe_cleaner_join(obj/structure/pipe_cleaner/C, mob/user, showerror = TRUE, forceddir)
var/turf/U = user.loc
if(!isturf(U))
return
var/turf/T = C.loc
if(!isturf(T)) // sanity check
return
if(get_dist(C, user) > 1) // make sure it's close enough
to_chat(user, span_warning("You can't lay pipe cleaner at a place that far away!"))
return
if(U == T && !forceddir) //if clicked on the turf we're standing on and a direction wasn't supplied, try to put a pipe_cleaner in the direction we're facing
place_turf(T,user)
return
var/dirn = get_dir(C, user)
if(forceddir)
dirn = forceddir
// one end of the clicked pipe_cleaner is pointing towards us and no direction was supplied
if((C.d1 == dirn || C.d2 == dirn) && !forceddir)
if(!U.can_have_cabling()) //checking if it's a plating or catwalk
if (showerror)
to_chat(user, span_warning("You can only lay pipe cleaners on catwalks and plating!"))
return
else
// pipe_cleaner is pointing at us, we're standing on an open tile
// so create a stub pointing at the clicked pipe_cleaner on our tile
var/fdirn = REVERSE_DIR(dirn) // the opposite direction
for(var/obj/structure/pipe_cleaner/LC in U) // check to make sure there's not a pipe_cleaner there already
if(LC.d1 == fdirn || LC.d2 == fdirn)
if (showerror)
to_chat(user, span_warning("There's already a pipe cleaner at that position!"))
return
var/obj/structure/pipe_cleaner/NC = get_new_pipe_cleaner(U)
NC.d1 = 0
NC.d2 = fdirn
NC.add_fingerprint(user)
NC.update_appearance()
use(1)
return
// exisiting pipe_cleaner doesn't point at our position or we have a supplied direction, so see if it's a stub
else if(C.d1 == 0)
// if so, make it a full pipe_cleaner pointing from its old direction to our dirn
var/nd1 = C.d2 // these will be the new directions
var/nd2 = dirn
if(nd1 > nd2) // swap directions to match icons/states
nd1 = dirn
nd2 = C.d2
for(var/obj/structure/pipe_cleaner/LC in T) // check to make sure there's no matching pipe_cleaner
if(LC == C) // skip the pipe_cleaner we're interacting with
continue
if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no pipe_cleaner matches either direction
if (showerror)
to_chat(user, span_warning("There's already a pipe cleaner at that position!"))
return
C.update_appearance()
C.d1 = nd1
C.d2 = nd2
//updates the stored pipe_cleaner coil
C.update_stored(2, color)
C.add_fingerprint(user)
C.update_appearance()
use(1)
return
//////////////////////////////
// Misc.
/////////////////////////////
/obj/item/stack/pipe_cleaner_coil/red
color = CABLE_HEX_COLOR_RED
pipecleaner_color = CABLE_COLOR_RED
/obj/item/stack/pipe_cleaner_coil/yellow
color = CABLE_HEX_COLOR_YELLOW
pipecleaner_color = CABLE_COLOR_YELLOW
/obj/item/stack/pipe_cleaner_coil/blue
color = CABLE_HEX_COLOR_BLUE
pipecleaner_color = CABLE_COLOR_BLUE
/obj/item/stack/pipe_cleaner_coil/green
color = CABLE_HEX_COLOR_GREEN
pipecleaner_color = CABLE_COLOR_GREEN
/obj/item/stack/pipe_cleaner_coil/pink
color = CABLE_HEX_COLOR_PINK
pipecleaner_color = CABLE_COLOR_PINK
/obj/item/stack/pipe_cleaner_coil/orange
color =CABLE_HEX_COLOR_ORANGE
pipecleaner_color = CABLE_COLOR_ORANGE
/obj/item/stack/pipe_cleaner_coil/cyan
color = CABLE_HEX_COLOR_CYAN
pipecleaner_color = CABLE_COLOR_CYAN
/obj/item/stack/pipe_cleaner_coil/white
color = CABLE_HEX_COLOR_WHITE
pipecleaner_color = CABLE_COLOR_WHITE
/obj/item/stack/pipe_cleaner_coil/brown
color = CABLE_HEX_COLOR_BROWN
pipecleaner_color = CABLE_COLOR_BROWN
/obj/item/stack/pipe_cleaner_coil/random
color = null
/obj/item/stack/pipe_cleaner_coil/random/five
amount = 5
/obj/item/stack/pipe_cleaner_coil/cut
amount = null
icon_state = "pipecleaner2"
/obj/item/stack/pipe_cleaner_coil/cut/Initialize(mapload)
if(!amount)
amount = rand(1,2)
. = ..()
pixel_x = base_pixel_x + rand(-2, 2)
pixel_y = base_pixel_y + rand(-2, 2)
update_appearance()
/obj/item/stack/pipe_cleaner_coil/cut/red
color = CABLE_HEX_COLOR_RED
pipecleaner_color = CABLE_COLOR_RED
/obj/item/stack/pipe_cleaner_coil/cut/yellow
color = CABLE_HEX_COLOR_YELLOW
pipecleaner_color = CABLE_COLOR_YELLOW
/obj/item/stack/pipe_cleaner_coil/cut/blue
color = CABLE_HEX_COLOR_BLUE
pipecleaner_color = CABLE_COLOR_BLUE
/obj/item/stack/pipe_cleaner_coil/cut/green
color = CABLE_HEX_COLOR_GREEN
pipecleaner_color = CABLE_COLOR_GREEN
/obj/item/stack/pipe_cleaner_coil/cut/pink
color = CABLE_HEX_COLOR_PINK
pipecleaner_color = CABLE_COLOR_PINK
/obj/item/stack/pipe_cleaner_coil/cut/orange
color = CABLE_HEX_COLOR_ORANGE
pipecleaner_color = CABLE_COLOR_ORANGE
/obj/item/stack/pipe_cleaner_coil/cut/cyan
color = CABLE_HEX_COLOR_CYAN
pipecleaner_color = CABLE_COLOR_CYAN
/obj/item/stack/pipe_cleaner_coil/cut/white
color = CABLE_HEX_COLOR_WHITE
pipecleaner_color = CABLE_COLOR_WHITE
/obj/item/stack/pipe_cleaner_coil/cut/brown
color = CABLE_HEX_COLOR_BROWN
pipecleaner_color = CABLE_COLOR_BROWN
/obj/item/stack/pipe_cleaner_coil/cut/random
color = null