diff --git a/code/datums/elements/can_barricade.dm b/code/datums/elements/can_barricade.dm
new file mode 100644
index 00000000000..039e715dc33
--- /dev/null
+++ b/code/datums/elements/can_barricade.dm
@@ -0,0 +1,64 @@
+#define PLANK_BARRICADE_AMOUNT 2
+
+/datum/element/can_barricade
+ element_flags = ELEMENT_BESPOKE
+ id_arg_index = 2
+
+/datum/element/can_barricade/Attach(atom/target)
+ . = ..()
+
+ if(!isatom(target))
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(target, COMSIG_PARENT_ATTACKBY, .proc/on_start_barricade)
+ RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+
+ target.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
+ RegisterSignal(target, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, .proc/on_requesting_context_from_item)
+
+/datum/element/can_barricade/Detach(atom/target)
+ UnregisterSignal(target, list(COMSIG_PARENT_ATTACKBY, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM))
+ // We don't remove HAS_CONTEXTUAL_SCREENTIPS_1, since there could be other stuff still hooked to it,
+ // and being set without signals is not dangerous, just less performant.
+ // A lot of things don't do this, perhaps make a proc that checks if any signals are still set, and if not,
+ // remove the flag.
+ return ..()
+
+/datum/element/can_barricade/proc/on_examine(atom/source, mob/user, list/examine_texts)
+ SIGNAL_HANDLER
+ examine_texts += span_notice("This looks like it can be barricaded with planks of wood.")
+
+/datum/element/can_barricade/proc/on_start_barricade(atom/source, obj/item/stack/sheet/mineral/wood/plank, mob/living/user, params)
+ SIGNAL_HANDLER
+
+ if(user.combat_mode || !istype(plank) || !istype(user))
+ return
+
+ if(plank.get_amount() < PLANK_BARRICADE_AMOUNT)
+ source.balloon_alert(user, "need [PLANK_BARRICADE_AMOUNT] [plank] sheets!")
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+ source.balloon_alert(user, "constructing barricade...")
+ playsound(source, 'sound/items/hammering_wood.ogg', 50, vary = TRUE)
+ INVOKE_ASYNC(src, .proc/barricade, source, plank, user, params) //signal handlers can't have do_afters inside of them
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+/// when our element gets attacked by wooden planks it creates a barricade
+/datum/element/can_barricade/proc/barricade(atom/source, obj/item/stack/sheet/mineral/wood/plank, mob/living/user, params)
+ if(!do_after(user, 5 SECONDS, target = source) || !plank.use(2) || (locate(/obj/structure/barricade/wooden/crude) in source.loc))
+ return
+
+ source.balloon_alert(user, "barricade constructed")
+ var/obj/structure/barricade/wooden/crude/barricade = new (source.loc)
+ barricade.add_fingerprint(user)
+
+/datum/element/can_barricade/proc/on_requesting_context_from_item(atom/source, list/context, obj/item/held_item, mob/user)
+ SIGNAL_HANDLER
+
+ if(istype(held_item, /obj/item/stack/sheet/mineral/wood) && source.Adjacent(user))
+ context[SCREENTIP_CONTEXT_LMB] = "Construct barricade"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ return NONE
+
+#undef PLANK_BARRICADE_AMOUNT
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index 67f1abf7ee6..b5e833eb45a 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -51,10 +51,7 @@
return TRUE
return FALSE
-
-
/////BARRICADE TYPES///////
-
/obj/structure/barricade/wooden
name = "wooden barricade"
desc = "This space is blocked off by a wooden barricade."
@@ -63,6 +60,13 @@
bar_material = WOOD
var/drop_amount = 3
+/obj/structure/barricade/wooden/Initialize(mapload)
+ . = ..()
+
+ var/static/list/tool_behaviors = list(TOOL_CROWBAR = list(SCREENTIP_CONTEXT_LMB = "Deconstruct"))
+ AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
+ register_context()
+
/obj/structure/barricade/wooden/attackby(obj/item/I, mob/user)
if(istype(I,/obj/item/stack/sheet/mineral/wood))
var/obj/item/stack/sheet/mineral/wood/W = I
@@ -71,6 +75,7 @@
return
else
to_chat(user, span_notice("You start adding [I] to [src]..."))
+ playsound(src, 'sound/items/hammering_wood.ogg', 50, vary = TRUE)
if(do_after(user, 50, target=src))
W.use(5)
var/turf/T = get_turf(src)
@@ -79,6 +84,15 @@
return
return ..()
+/obj/structure/barricade/wooden/crowbar_act(mob/living/user, obj/item/tool)
+ balloon_alert(user, "deconstructing barricade...")
+ if(!tool.use_tool(src, user, 2 SECONDS, volume=50))
+ return
+ balloon_alert(user, "barricade deconstructed")
+ tool.play_tool_sound(src)
+ new /obj/item/stack/sheet/mineral/wood(get_turf(src), drop_amount)
+ qdel(src)
+ return TOOL_ACT_TOOLTYPE_SUCCESS
/obj/structure/barricade/wooden/crude
name = "crude plank barricade"
@@ -87,6 +101,7 @@
drop_amount = 1
max_integrity = 50
proj_pass_rate = 65
+ layer = SIGN_LAYER
/obj/structure/barricade/wooden/crude/snow
desc = "This space is blocked off by a crude assortment of planks. It seems to be covered in a layer of snow."
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 94d34c907e3..56fffcc63ed 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -68,6 +68,7 @@
COMSIG_ATOM_MAGICALLY_UNLOCKED = .proc/on_magic_unlock,
)
AddElement(/datum/element/connect_loc, loc_connections)
+ AddElement(/datum/element/can_barricade)
/obj/machinery/door/examine(mob/user)
. = ..()
@@ -87,7 +88,7 @@
if(isaicamera(user) || issilicon(user))
return .
- if (isnull(held_item) && Adjacent(user))
+ if(isnull(held_item) && Adjacent(user))
context[SCREENTIP_CONTEXT_LMB] = "Open"
return CONTEXTUAL_SCREENTIP_SET
@@ -274,6 +275,8 @@
return TRUE
else if(I.item_flags & NOBLUDGEON || user.combat_mode)
return ..()
+ else if(!user.combat_mode && istype(I, /obj/item/stack/sheet/mineral/wood))
+ return ..() // we need this so our can_barricade element can be called using COMSIG_PARENT_ATTACKBY
else if(try_to_activate_door(user))
return TRUE
return ..()
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index d9000afa596..01de086464e 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -34,19 +34,6 @@
/// If some inconsiderate jerk has had their blood spilled on this window, thus making it cleanable
var/bloodied = FALSE
-/obj/structure/window/examine(mob/user)
- . = ..()
- switch(state)
- if(WINDOW_SCREWED_TO_FRAME)
- . += span_notice("The window is screwed to the frame.")
- if(WINDOW_IN_FRAME)
- . += span_notice("The window is unscrewed but pried into the frame.")
- if(WINDOW_OUT_OF_FRAME)
- if (anchored)
- . += span_notice("The window is screwed to the floor.")
- else
- . += span_notice("The window is unscrewed from the floor, and could be deconstructed by wrenching.")
-
/obj/structure/window/Initialize(mapload, direct)
. = ..()
if(direct)
@@ -61,6 +48,7 @@
if(fulltile)
setDir()
+ AddElement(/datum/element/can_barricade)
//windows only block while reinforced and fulltile, so we'll use the proc
real_explosion_block = explosion_block
@@ -78,6 +66,19 @@
if (flags_1 & ON_BORDER_1)
AddElement(/datum/element/connect_loc, loc_connections)
+/obj/structure/window/examine(mob/user)
+ . = ..()
+ switch(state)
+ if(WINDOW_SCREWED_TO_FRAME)
+ . += span_notice("The window is screwed to the frame.")
+ if(WINDOW_IN_FRAME)
+ . += span_notice("The window is unscrewed but pried into the frame.")
+ if(WINDOW_OUT_OF_FRAME)
+ if (anchored)
+ . += span_notice("The window is screwed to the floor.")
+ else
+ . += span_notice("The window is unscrewed from the floor, and could be deconstructed by wrenching.")
+
/obj/structure/window/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
if(RCD_DECONSTRUCT)
diff --git a/sound/items/hammering_wood.ogg b/sound/items/hammering_wood.ogg
new file mode 100644
index 00000000000..37ecf90ec44
Binary files /dev/null and b/sound/items/hammering_wood.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 57ffbcabee3..1936d0d360a 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -987,6 +987,7 @@
#include "code\datums\elements\bed_tucking.dm"
#include "code\datums\elements\bsa_blocker.dm"
#include "code\datums\elements\bump_click.dm"
+#include "code\datums\elements\can_barricade.dm"
#include "code\datums\elements\chemical_transfer.dm"
#include "code\datums\elements\chewable.dm"
#include "code\datums\elements\cleaning.dm"