From 49a4a727cbf46e7958f92c032d54fe6a458b1999 Mon Sep 17 00:00:00 2001 From: CRUNCH <143041327+CRUNCH-Borg@users.noreply.github.com> Date: Tue, 9 Dec 2025 02:50:53 +0000 Subject: [PATCH] Refactors Fence Code (#30931) * Update fence.dm * Update fence.dm * Update fence.dm * Update fence.dm * Update fence.dm * Update fence.dm * Update fence.dm * Update code/game/objects/structures/fence.dm Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: CRUNCH <143041327+CRUNCH-Borg@users.noreply.github.com> * Update fence.dm * Update fence.dm * Update fence.dm --------- Signed-off-by: CRUNCH <143041327+CRUNCH-Borg@users.noreply.github.com> Co-authored-by: Fordoxia <143041327+Fordoxia@users.noreply.github.com> Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> --- code/game/objects/structures/fence.dm | 298 ++++++++++++++------------ 1 file changed, 160 insertions(+), 138 deletions(-) diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index edc0c24bdd5..b3babf75ef3 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -1,29 +1,34 @@ -//Chain link fences //Sprites ported from /VG/ - -#define CUT_TIME 100 -#define CLIMB_TIME 150 -#define FULL_CUT_TIME 300 - +/// Time to climb through a fence with a `SMALL_HOLE`. +#define CLIMB_TIME 15 SECONDS +/// Time to cut fences with `can_have_holes` = `TRUE`. +#define CUT_TIME 10 SECONDS +/// Time to cut fences with `can_have_holes` = `FALSE`. +#define FULL_CUT_TIME 30 SECONDS +/// Fully intact fence. #define NO_HOLE 0 //section is intact -#define MEDIUM_HOLE 1 //medium hole in the section - can climb through -#define LARGE_HOLE 2 //large hole in the section - can walk through -#define MAX_HOLE_SIZE LARGE_HOLE -#define HOLE_REPAIR (hole_size * 2) //How many rods to fix these sections +/// Small hole in fence, can be climbed through. +#define SMALL_HOLE 1 +/// Large hole in fence, can be walked through. +#define LARGE_HOLE 2 +/// Material cost to fix a fence section. +#define HOLE_REPAIR (hole_size * 2) /obj/structure/fence name = "fence" desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out." - density = TRUE - anchored = TRUE - icon = 'icons/obj/fence.dmi' icon_state = "straight" - - var/cuttable = TRUE + density = TRUE + anchored = TRUE + /// Does this fence get cut in stages or all at once? + var/can_have_holes = TRUE + /// How big is the hole in this fence, if present? var/hole_size = NO_HOLE - var/invulnerable = FALSE - var/shock_cooldown = FALSE + /// Cooldown on the fence applying shocks. + var/shock_cooldown_duration = 1 SECONDS + // Cooldown exists to stop shock spam on bumping. + COOLDOWN_DECLARE(shock_cooldown) /obj/structure/fence/Initialize(mapload) . = ..() @@ -32,140 +37,110 @@ /obj/structure/fence/examine(mob/user) . = ..() switch(hole_size) - if(MEDIUM_HOLE) - . += "There is a large hole in \the [src]." + if(SMALL_HOLE) + . += "There is a large hole in [src]." if(LARGE_HOLE) - . += "\The [src] has been completely cut through." - -/obj/structure/fence/end - icon_state = "end" - cuttable = FALSE - -/obj/structure/fence/corner - icon_state = "corner" - cuttable = FALSE - -/obj/structure/fence/post - icon_state = "post" - cuttable = FALSE - -/obj/structure/fence/cut/medium - icon_state = "straight_cut2" - hole_size = MEDIUM_HOLE - climbable = TRUE - -/obj/structure/fence/cut/large - icon_state = "straight_cut3" - hole_size = LARGE_HOLE + . += "[src] has been completely cut through." /obj/structure/fence/CanPass(atom/movable/mover, border_dir) if(istype(mover) && mover.checkpass(PASSFENCE)) return TRUE + if(isprojectile(mover)) return TRUE + if(!density) return TRUE + return FALSE -/* - Shock user with probability prb (if all connections & power are working) - Returns TRUE if shocked, FALSE otherwise - Totally not stolen from code\game\objects\structures\grille.dm -*/ -/obj/structure/fence/proc/shock(mob/user, prb) - if(!prob(prb)) - return FALSE - if(!in_range(src, user)) //To prevent TK and mech users from getting shocked - return FALSE - var/turf/T = get_turf(src) - var/obj/structure/cable/C = T.get_cable_node() - if(C) - if(electrocute_mob(user, C, src, 1, TRUE)) - do_sparks(3, 1, src) - return TRUE - return FALSE +/obj/structure/fence/item_interaction(mob/living/user, obj/item/used, list/modifiers) + if(shock(user, 90)) + return ITEM_INTERACT_COMPLETE -/obj/structure/fence/wirecutter_act(mob/living/user, obj/item/W) + if(!istype(used, /obj/item/stack/rods)) + return NONE + + if(hole_size == NO_HOLE) + return ITEM_INTERACT_COMPLETE + + var/obj/item/stack/rods/R = used + if(R.get_amount() < HOLE_REPAIR) + to_chat(user, "You need [HOLE_REPAIR] rods to fix this fence!") + return ITEM_INTERACT_COMPLETE + + to_chat(user, "You begin repairing the fence...") + if(do_after_once(user, 3 SECONDS * used.toolspeed, target = src) && hole_size != NO_HOLE && R.use(HOLE_REPAIR)) + playsound(src, used.usesound, 80, 1) + hole_size = NO_HOLE + obj_integrity = max_integrity + to_chat(user, "You repair the fence.") + update_cut_status() + return ITEM_INTERACT_COMPLETE + +/obj/structure/fence/wirecutter_act(mob/living/user, obj/item/I) . = TRUE if(shock(user, 100)) return - if(invulnerable) + + if(flags & NODECONSTRUCT) to_chat(user, "This fence is too strong to cut through!") return - if(!cuttable) - user.visible_message("[user] starts dismantling [src] with [W].",\ - "You start dismantling [src] with [W].") - if(W.use_tool(src, user, FULL_CUT_TIME, volume = W.tool_volume)) - user.visible_message("[user] completely dismantles [src].",\ - "You completely dismantle [src].") - qdel(src) + + user.visible_message( + "[user] starts cutting through [src] with [I].", + "You start cutting through [src] with [I]." + ) + if(!can_have_holes) + if(I.use_tool(src, user, FULL_CUT_TIME, volume = I.tool_volume, do_after_once = TRUE)) + user.visible_message( + "[user] completely dismantles [src].", + "You completely dismantle [src]." + ) + deconstruct() return - var/current_stage = hole_size - user.visible_message("[user] starts cutting through [src] with [W].",\ - "You start cutting through [src] with [W].") - if(W.use_tool(src, user, CUT_TIME * W.toolspeed, volume = W.tool_volume)) - if(current_stage == hole_size) - switch(hole_size) - if(NO_HOLE) - user.visible_message("[user] cuts into [src] some more.",\ - "You could probably fit yourself through that hole now. Although climbing through would be much faster if you made it even bigger.") - hole_size = MEDIUM_HOLE - if(MEDIUM_HOLE) - user.visible_message("[user] completely cuts through [src].",\ - "The hole in [src] is now big enough to walk through.") - hole_size = LARGE_HOLE - if(LARGE_HOLE) - user.visible_message("[user] completely dismantles [src].",\ - "You completely take apart [src].") - qdel(src) - return - update_cut_status() -/obj/structure/fence/item_interaction(mob/living/user, obj/item/C, list/modifiers) - if(shock(user, 90)) - return ITEM_INTERACT_COMPLETE - if(istype(C, /obj/item/stack/rods)) - if(hole_size == NO_HOLE) - return ITEM_INTERACT_COMPLETE - var/obj/item/stack/rods/R = C - if(R.get_amount() < HOLE_REPAIR) - to_chat(user, "You need [HOLE_REPAIR] rods to fix this fence!") - return ITEM_INTERACT_COMPLETE - to_chat(user, "You begin repairing the fence...") - if(do_after(user, 3 SECONDS * C.toolspeed, target = src) && hole_size != NO_HOLE && R.use(HOLE_REPAIR)) - playsound(src, C.usesound, 80, 1) - hole_size = NO_HOLE - obj_integrity = max_integrity - to_chat(user, "You repair the fence.") - update_cut_status() - return ITEM_INTERACT_COMPLETE - -/obj/structure/fence/Bumped(atom/user) - if(!ismob(user)) + // Required to avoid tool spam from stacking and instantly cutting multiple stages at once. + if(!I.use_tool(src, user, CUT_TIME * I.toolspeed, volume = I.tool_volume, do_after_once = TRUE)) return - if(shock_cooldown) - return - shock(user, 70) - shock_cooldown = TRUE // We do not want bump shock spam! - addtimer(CALLBACK(src, PROC_REF(shock_cooldown)), 1 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) + + switch(hole_size) + if(NO_HOLE) + user.visible_message( + "[user] cuts a hole into [src].", + "You could probably fit yourself through that hole. Although climbing through would be much faster if you made it even bigger..." + ) + if(SMALL_HOLE) + user.visible_message( + "[user] completely cuts through [src].", + "The hole in [src] is now big enough to walk through." + ) + if(LARGE_HOLE) + user.visible_message( + "[user] completely dismantles [src].", + "You completely dismantle [src]." + ) + deconstruct() + return -/obj/structure/fence/proc/shock_cooldown() - shock_cooldown = FALSE + hole_size = hole_size + 1 + update_cut_status() -/obj/structure/fence/attack_animal(mob/user) +/obj/structure/fence/deconstruct(disassembled) . = ..() - if(. && !QDELETED(src) && !shock(user, 70)) - take_damage(rand(5,10), BRUTE, "melee", 1) + if(!(flags & NODECONSTRUCT)) + qdel(src) /obj/structure/fence/proc/update_cut_status() - if(!cuttable) + if(!can_have_holes) return + var/new_density = TRUE switch(hole_size) if(NO_HOLE) icon_state = initial(icon_state) climbable = FALSE - if(MEDIUM_HOLE) + if(SMALL_HOLE) icon_state = "straight_cut2" climbable = TRUE if(LARGE_HOLE) @@ -174,32 +149,80 @@ climbable = FALSE set_density(new_density) -//FENCE DOORS +/obj/structure/fence/Bumped(atom/user) + if(!ismob(user) || !COOLDOWN_FINISHED(src, shock_cooldown)) + return + + shock(user, 70) + COOLDOWN_START(src, shock_cooldown, shock_cooldown_duration) + +/obj/structure/fence/attack_hand(mob/user, list/modifiers) + shock(user, 70) + +/obj/structure/fence/attack_animal(mob/user) + . = ..() + if(. && !QDELETED(src) && !shock(user, 70)) + take_damage(rand(5,10), BRUTE, "melee", 1) + +/* + Shock `user` with probability `shock_chance` (if there's a powered wire node under the fence section). + Returns `TRUE` if shocked, `FALSE` otherwise. +*/ +/obj/structure/fence/proc/shock(mob/user, shock_chance) + if(!prob(shock_chance)) + return FALSE + + if(!in_range(src, user)) // To prevent TK and mech users from getting shocked + return FALSE + + var/turf/T = get_turf(src) + var/obj/structure/cable/C = T.get_cable_node() + if(C) + if(electrocute_mob(user, C, src, 1, TRUE)) + do_sparks(3, 1, src) + return TRUE + + return FALSE + +/obj/structure/fence/cut/medium + icon_state = "straight_cut2" + hole_size = SMALL_HOLE + climbable = TRUE + +/obj/structure/fence/cut/large + icon_state = "straight_cut3" + hole_size = LARGE_HOLE + +/obj/structure/fence/post + icon_state = "post" + can_have_holes = FALSE + +/obj/structure/fence/corner + icon_state = "corner" + can_have_holes = FALSE + +/obj/structure/fence/end + icon_state = "end" + can_have_holes = FALSE /obj/structure/fence/door name = "fence door" - desc = "Not very useful without a real lock." + desc = "A chain link fence door. Not very useful without a real lock." icon_state = "door_closed" - cuttable = FALSE + can_have_holes = FALSE var/open = FALSE /obj/structure/fence/door/Initialize(mapload) . = ..() update_door_status() -/obj/structure/fence/door/opened - icon_state = "door_opened" - open = TRUE - /obj/structure/fence/door/attack_hand(mob/user, list/modifiers) - shock(user, 70) - if(can_open(user)) - toggle(user) - return TRUE + ..() + toggle(user) /obj/structure/fence/door/proc/toggle(mob/user) open = !open - visible_message("\The [user] [open ? "opens" : "closes"] \the [src].") + visible_message("[user] [open ? "opens" : "closes"] [src].") update_door_status() playsound(src, 'sound/machines/door_open.ogg', 20, TRUE) @@ -207,15 +230,14 @@ set_density(!open) icon_state = open ? "door_opened" : "door_closed" -/obj/structure/fence/door/proc/can_open(mob/user) - return TRUE +/obj/structure/fence/door/opened + icon_state = "door_opened" + open = TRUE -#undef CUT_TIME #undef CLIMB_TIME +#undef CUT_TIME #undef FULL_CUT_TIME - #undef NO_HOLE -#undef MEDIUM_HOLE +#undef SMALL_HOLE #undef LARGE_HOLE -#undef MAX_HOLE_SIZE #undef HOLE_REPAIR