Skip to content

Commit

Permalink
Moving structure interactions into alt interactions.
Browse files Browse the repository at this point in the history
  • Loading branch information
MistakeNot4892 committed Dec 28, 2024
1 parent 260a77f commit bc7a147
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 102 deletions.
8 changes: 8 additions & 0 deletions code/game/atoms_movable.dm
Original file line number Diff line number Diff line change
Expand Up @@ -591,3 +591,11 @@
if(ATOM_IS_OPEN_CONTAINER(src))
return loc?.take_vaporized_reagent(reagent, amount)
return null

// TODO: make everything use this.
/atom/movable/proc/set_anchored(new_anchored)
SHOULD_CALL_PARENT(TRUE)
if(anchored != new_anchored)
anchored = new_anchored
return TRUE
return FALSE
183 changes: 101 additions & 82 deletions code/game/objects/structures/_structure_construction.dm
Original file line number Diff line number Diff line change
Expand Up @@ -41,81 +41,27 @@

/obj/structure/proc/handle_default_wrench_attackby(var/mob/user, var/obj/item/wrench)
if((tool_interaction_flags & TOOL_INTERACTION_ANCHOR) && can_unanchor(user))
playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
visible_message(SPAN_NOTICE("\The [user] begins [anchored ? "unsecuring [src]" : "securing [src] in place"] with \the [wrench]."))
if(!do_after(user, 4 SECONDS, src) || QDELETED(src))
return TRUE
playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
anchored = !anchored
visible_message(SPAN_NOTICE("\The [user] has [anchored ? "secured" : "unsecured"] \the [src] with \the [wrench]."))
update_icon()
return TRUE
return tool_toggle_anchors(user, wrench)
return FALSE

/obj/structure/proc/handle_default_welder_attackby(var/mob/user, var/obj/item/weldingtool/welder)
if((tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) && can_dismantle(user))
if(material && !material.removed_by_welder)
to_chat(user, SPAN_WARNING("\The [src] is too delicate to be dismantled with \the [welder]; try a crowbar."))
return TRUE
if(!welder.isOn())
to_chat(user, SPAN_WARNING("Try lighting \the [welder] first."))
return TRUE
if(welder.get_fuel() < 5)
to_chat(user, SPAN_WARNING("You need more fuel to complete this task."))
return TRUE
playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
visible_message(SPAN_NOTICE("\The [user] starts slicing apart \the [src] with \the [welder]."))
if(!do_after(user, 3 SECONDS, src) || QDELETED(src) || !welder.weld(5, user))
return TRUE
playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [welder]."))
dismantle_structure(user)
return TRUE
return welder_dismantle(user, welder)
return FALSE

/obj/structure/proc/handle_default_crowbar_attackby(var/mob/user, var/obj/item/crowbar)
if((tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) && can_dismantle(user))
if(material && material.removed_by_welder)
to_chat(user, SPAN_WARNING("\The [src] is too robust to be dismantled with \the [crowbar]; try a welding tool."))
return TRUE
playsound(loc, 'sound/items/Crowbar.ogg', 50, 1)
visible_message(SPAN_NOTICE("\The [user] starts levering apart \the [src] with \the [crowbar]."))
if(!do_after(user, 5 SECONDS, src) || QDELETED(src))
return TRUE
playsound(loc, 'sound/items/Crowbar.ogg', 50, 1)
visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [crowbar]."))
dismantle_structure(user)
return TRUE
return tool_dismantle(user, crowbar)
return FALSE

/obj/structure/proc/handle_default_cable_attackby(var/mob/user, var/obj/item/stack/cable_coil/coil)
if((tool_interaction_flags & TOOL_INTERACTION_WIRING) && anchored)
if(wired)
to_chat(user, SPAN_WARNING("\The [src] has already been wired."))
return TRUE
var/obj/item/stack/cable_coil/cable = coil
if(cable.get_amount() < 1)
to_chat(user, SPAN_WARNING("You need one length of coil to wire \the [src]."))
else
visible_message(SPAN_NOTICE("\The [user] starts to wire \the [src]."))
if(do_after(user, 4 SECONDS, src) && !wired && anchored && !QDELETED(src) && cable.use(1))
wired = TRUE
visible_message(SPAN_NOTICE("\The [user] finishes wiring \the [src]."))
return TRUE
return install_wiring(user, coil)
return FALSE

/obj/structure/proc/handle_default_wirecutter_attackby(var/mob/user, var/obj/item/wirecutters/wirecutters)
/obj/structure/proc/handle_default_wirecutter_attackby(var/mob/user, var/obj/item/wirecutters)
if((tool_interaction_flags & TOOL_INTERACTION_WIRING) && anchored)
if(!wired)
to_chat(user, SPAN_WARNING("\The [src] has not been wired."))
return TRUE
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
visible_message(SPAN_NOTICE("\The [user] begins stripping the wiring out of \the [src]."))
if(do_after(user, 4 SECONDS, src) && !QDELETED(src) && wired)
visible_message(SPAN_NOTICE("\The [user] finishes stripping the wiring from \the [src]."))
new/obj/item/stack/cable_coil(src.loc, 1)
wired = FALSE
return TRUE
return strip_wiring(user, wirecutters)
return FALSE

/obj/structure/proc/handle_default_screwdriver_attackby(var/mob/user, var/obj/item/screwdriver)
Expand Down Expand Up @@ -151,35 +97,39 @@
current_health = clamp(current_health + used*DOOR_REPAIR_AMOUNT, current_health, current_max_health)

/obj/structure/attackby(obj/item/used_item, mob/user)

if(used_item.user_can_attack_with(user, silent = TRUE))
var/force = used_item.get_attack_force(user)
if(force && user.check_intent(I_FLAG_HARM))
attack_animation(user)
visible_message(SPAN_DANGER("\The [src] has been [pick(used_item.attack_verb)] with \the [used_item] by \the [user]!"))
take_damage(force, used_item.atom_damage_type)
. = TRUE

else if(IS_HAMMER(used_item))
. = handle_default_hammer_attackby(user, used_item)
else if(IS_WRENCH(used_item))
. = handle_default_wrench_attackby(user, used_item)
else if(IS_SCREWDRIVER(used_item))
. = handle_default_screwdriver_attackby(user, used_item)
else if(IS_WELDER(used_item))
. = handle_default_welder_attackby(user, used_item)
else if(IS_CROWBAR(used_item))
. = handle_default_crowbar_attackby(user, used_item)
else if(IS_COIL(used_item))
. = handle_default_cable_attackby(user, used_item)
else if(IS_WIRECUTTER(used_item))
. = handle_default_wirecutter_attackby(user, used_item)
else if(can_repair_with(used_item) && can_repair(user))
. = handle_repair(user, used_item)
if(.)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
add_fingerprint(user)
return .
return ..()
return TRUE

// This handles standard tool interactions (anchoring, dismantling), calls below are for legacy purposes.
. = ..()
if(.)
return

if(IS_WRENCH(used_item))
. = handle_default_wrench_attackby(user, used_item)
else if(IS_SCREWDRIVER(used_item))
. = handle_default_screwdriver_attackby(user, used_item)
else if(IS_WELDER(used_item))
. = handle_default_welder_attackby(user, used_item)
else if(IS_CROWBAR(used_item))
. = handle_default_crowbar_attackby(user, used_item)
else if(IS_COIL(used_item))
. = handle_default_cable_attackby(user, used_item)
else if(IS_WIRECUTTER(used_item))
. = handle_default_wirecutter_attackby(user, used_item)
else if(can_repair_with(used_item) && can_repair(user))
. = handle_repair(user, used_item)
if(.)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
add_fingerprint(user)

/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb, var/environment_smash)
if(environment_smash >= 1)
Expand All @@ -195,4 +145,73 @@
take_damage(damage)
else
visible_message(SPAN_NOTICE("\The [user] bonks \the [src] harmlessly."))
return TRUE
return TRUE

/obj/structure/proc/strip_wiring(mob/user, obj/item/wirecutters)
if(!wired)
to_chat(user, SPAN_WARNING("\The [src] has not been wired."))
return TRUE
playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)
visible_message(SPAN_NOTICE("\The [user] begins stripping the wiring out of \the [src]."))
if(do_after(user, 4 SECONDS, src) && !QDELETED(src) && wired)
visible_message(SPAN_NOTICE("\The [user] finishes stripping the wiring from \the [src]."))
new/obj/item/stack/cable_coil(src.loc, 1)
wired = FALSE
return TRUE

/obj/structure/proc/install_wiring(mob/user, obj/item/stack/cable_coil/coil)
if(wired)
to_chat(user, SPAN_WARNING("\The [src] has already been wired."))
return TRUE
var/obj/item/stack/cable_coil/cable = coil
if(cable.get_amount() < 1)
to_chat(user, SPAN_WARNING("You need one length of coil to wire \the [src]."))
else
visible_message(SPAN_NOTICE("\The [user] starts to wire \the [src]."))
if(do_after(user, 4 SECONDS, src) && !wired && anchored && !QDELETED(src) && cable.use(1))
wired = TRUE
visible_message(SPAN_NOTICE("\The [user] finishes wiring \the [src]."))
return TRUE

/obj/structure/proc/tool_dismantle(mob/user, obj/item/tool, dismantle_sound = 'sound/items/Crowbar.ogg')
if(material && material.removed_by_welder)
to_chat(user, SPAN_WARNING("\The [src] is too robust to be dismantled with \the [tool]; try a welding tool."))
return TRUE
playsound(loc, dismantle_sound, 50, 1)
visible_message(SPAN_NOTICE("\The [user] starts levering apart \the [src] with \the [tool]."))
if(!do_after(user, 5 SECONDS, src) || QDELETED(src))
return TRUE
playsound(loc, dismantle_sound, 50, 1)
visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [tool]."))
dismantle_structure(user)
return TRUE

/obj/structure/proc/welder_dismantle(mob/user, obj/item/weldingtool/welder)
if(material && !material.removed_by_welder)
to_chat(user, SPAN_WARNING("\The [src] is too delicate to be dismantled with \the [welder]; try a crowbar."))
return TRUE
if(!welder.isOn())
to_chat(user, SPAN_WARNING("Try lighting \the [welder] first."))
return TRUE
if(welder.get_fuel() < 5)
to_chat(user, SPAN_WARNING("You need more fuel to complete this task."))
return TRUE
playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
visible_message(SPAN_NOTICE("\The [user] starts slicing apart \the [src] with \the [welder]."))
if(!do_after(user, 3 SECONDS, src) || QDELETED(src) || !welder.weld(5, user))
return TRUE
playsound(loc, pick('sound/items/Welder.ogg', 'sound/items/Welder2.ogg'), 50, 1)
visible_message(SPAN_NOTICE("\The [user] completely dismantles \the [src] with \the [welder]."))
dismantle_structure(user)
return TRUE

/obj/structure/proc/tool_toggle_anchors(mob/user, obj/item/tool)
playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
visible_message(SPAN_NOTICE("\The [user] begins [anchored ? "unsecuring [src]" : "securing [src] in place"] with \the [tool]."))
if(!do_after(user, 4 SECONDS, src) || QDELETED(src))
return TRUE
playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
anchored = !anchored
visible_message(SPAN_NOTICE("\The [user] has [anchored ? "secured" : "unsecured"] \the [src] with \the [tool]."))
update_icon()
return TRUE
74 changes: 74 additions & 0 deletions code/game/objects/structures/_structure_interactions.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Anchoring or unanchoring with a hammer or a wrench.
/decl/interaction_handler/structure
abstract_type = /decl/interaction_handler/structure
expected_target_type = /obj/structure

/decl/interaction_handler/structure/unanchor
name = "Toggle Anchoring"
examine_desc = "anchor or unanchor $TARGET_THEM$"

/decl/interaction_handler/structure/unanchor/is_possible(atom/target, mob/user, obj/item/prop)
if(!(. = ..()))
return
var/obj/structure/struct = target
if(!(struct.tool_interaction_flags & TOOL_INTERACTION_ANCHOR) || !struct.can_unanchor(user))
return FALSE
return istype(prop) && (IS_WRENCH(prop) || IS_HAMMER(prop))

/decl/interaction_handler/structure/unanchor/invoked(atom/target, mob/user, obj/item/prop)
. = ..()
var/obj/structure/struct = target
return struct.tool_toggle_anchors(user, prop)

// Removing wiring with wirecutters or installing it with a cable coil.
/decl/interaction_handler/structure/wiring
name = "Modify Wiring"
examine_desc = "strip or install wiring"

/decl/interaction_handler/structure/wiring/is_possible(atom/target, mob/user, obj/item/prop)
if(!(. = ..()))
return
var/obj/structure/struct = target
if(!(struct.tool_interaction_flags & TOOL_INTERACTION_WIRING))
return FALSE
if(struct.wired)
return IS_WIRECUTTER(prop)
return IS_COIL(prop)

/decl/interaction_handler/structure/wiring/invoked(atom/target, mob/user, obj/item/prop)
var/obj/structure/struct = target
if(struct.wired)
return struct.strip_wiring(user, prop)
return struct.install_wiring(user, prop)

// Dismantling a structure.
/decl/interaction_handler/structure/dismantle
name = "Dismantle Structure"
examine_desc = "dismantle $TARGET_THEM$"

/decl/interaction_handler/structure/dismantle/is_possible(atom/target, mob/user, obj/item/prop)
if(!(. = ..()))
return
var/obj/structure/struct = target
if(!(struct.tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT) || !struct.can_dismantle(user))
return FALSE
return IS_WELDER(prop) || IS_CROWBAR(prop) || IS_HAMMER(prop)

/decl/interaction_handler/structure/dismantle/invoked(atom/target, mob/user, obj/item/prop)
var/obj/structure/struct = target
if(IS_WELDER(prop))
return struct.welder_dismantle(user, prop)
return struct.tool_dismantle(user, prop)

/decl/interaction_handler/put_in_storage
name = "Put In Storage"

/decl/interaction_handler/put_in_storage/is_possible(atom/target, mob/user, obj/item/prop)
return ..() && istype(prop) && target.storage

// Boilerplate from /atom/proc/attackby(), replicated here so tool interactions can be bypassed.
/decl/interaction_handler/put_in_storage/invoked(atom/target, mob/user, obj/item/prop)
if((isrobot(user) && (prop == user.get_active_held_item())) || !target.storage.can_be_inserted(prop, user))
return FALSE
prop.add_fingerprint(user)
return target.storage.handle_item_insertion(user, prop)
32 changes: 32 additions & 0 deletions code/game/objects/structures/barrels/barrel.dm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
// Rivets, bands, etc. Currently just cosmetic.
var/decl/material/metal_material = /decl/material/solid/metal/iron

// Overrides due to wonky reagent_dispeners opencontainer flag handling.
/obj/structure/reagent_dispensers/barrel/can_be_poured_from(mob/user, atom/target)
return (reagents?.maximum_volume > 0)
/obj/structure/reagent_dispensers/barrel/can_be_poured_into(mob/user, atom/target)
return (reagents?.maximum_volume > 0)

// Override to skip open container check.
/obj/structure/reagent_dispensers/barrel/can_drink_from(mob/user)
return reagents?.total_volume && user.check_has_mouth()

/obj/structure/reagent_dispensers/barrel/Initialize()
if(ispath(metal_material))
metal_material = GET_DECL(metal_material)
Expand Down Expand Up @@ -80,6 +90,28 @@
if(reagents?.maximum_volume)
LAZYADD(., global._reagent_interactions)

// Disambiguation actions, since barrels can have several different potential interactions for
// the same item. It would be nice to enable this on /obj/structure in general but there are a
// ton of really bespoke overrides of the standard tool methods (windows, AI core, etc.).
if(tool_interaction_flags & TOOL_INTERACTION_ANCHOR)
LAZYADD(., /decl/interaction_handler/structure/unanchor)
if(tool_interaction_flags & TOOL_INTERACTION_WIRING)
LAZYADD(., /decl/interaction_handler/structure/wiring)
if(tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT)
LAZYADD(., /decl/interaction_handler/structure/dismantle)
if(LAZYLEN(.) && storage)
LAZYADD(., /decl/interaction_handler/put_in_storage)

// Copy of above - maybe we should just have a single 'get interactions' proc at this point?
/obj/structure/reagent_dispensers/barrel/get_alt_interactions(mob/user)
. = ..()
if(tool_interaction_flags & TOOL_INTERACTION_ANCHOR)
LAZYADD(., /decl/interaction_handler/structure/unanchor)
if(tool_interaction_flags & TOOL_INTERACTION_WIRING)
LAZYADD(., /decl/interaction_handler/structure/wiring)
if(tool_interaction_flags & TOOL_INTERACTION_DECONSTRUCT)
LAZYADD(., /decl/interaction_handler/structure/dismantle)

/obj/structure/reagent_dispensers/barrel/ebony
material = /decl/material/solid/organic/wood/ebony
color = /decl/material/solid/organic/wood/ebony::color
Expand Down
19 changes: 6 additions & 13 deletions code/game/objects/structures/well.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,6 @@
can_toggle_open = FALSE
var/auto_refill

/obj/structure/reagent_dispensers/well/get_alt_interactions(mob/user)
. = ..()
if(reagents?.total_volume >= FLUID_PUDDLE)
LAZYADD(., /decl/interaction_handler/dip_item)
LAZYADD(., /decl/interaction_handler/fill_from)
if(user?.get_active_held_item())
LAZYADD(., /decl/interaction_handler/empty_into)

// Overrides due to wonky reagent_dispeners opencontainer flag handling.
/obj/structure/reagent_dispensers/well/can_be_poured_from(mob/user, atom/target)
return (reagents?.maximum_volume > 0)
/obj/structure/reagent_dispensers/well/can_be_poured_into(mob/user, atom/target)
return (reagents?.maximum_volume > 0)
// Override to skip open container check.
/obj/structure/reagent_dispensers/well/can_drink_from(mob/user)
return reagents?.total_volume && user.check_has_mouth()
Expand Down Expand Up @@ -61,6 +48,12 @@
if(!is_processing && auto_refill)
START_PROCESSING(SSobj, src)

// Overrides due to wonky reagent_dispeners opencontainer flag handling.
/obj/structure/reagent_dispensers/well/can_be_poured_from(mob/user, atom/target)
return (reagents?.maximum_volume > 0)
/obj/structure/reagent_dispensers/well/can_be_poured_into(mob/user, atom/target)
return (reagents?.maximum_volume > 0)

/obj/structure/reagent_dispensers/well/get_standard_interactions(var/mob/user)
. = ..()
if(reagents?.maximum_volume)
Expand Down
Loading

0 comments on commit bc7a147

Please sign in to comment.