Files
Batrachophreno 5277251959 Tool behaviors port (#21663)
For a robust crafting system, I need a new materials framework.
For a new materials framework, I need to clean up reagents.
To clean up reagents, I need to pare down foods from reagent holders.
To pare down foods from reagent holders, I need to port edibility
components.
To port edibility components, I need to port processing components.
To port processing components, I need to port tool behaviors.

This is all back-end code, no new features or functionality from this.
2026-01-27 08:44:49 +00:00

344 lines
11 KiB
Plaintext

/obj/structure/barricade
icon = 'icons/obj/barricades.dmi'
climbable = TRUE
anchored = TRUE
density = TRUE
atom_flags = ATOM_FLAG_CHECKS_BORDER
/// The type of stack the barricade dropped when disassembled if any.
var/stack_type
/// The amount of stack dropped when disassembled at full health
var/stack_amount = 5
/// to specify a non-zero amount of stack to drop when destroyed
var/destroyed_stack_amount
/// Pretty tough. Changes sprites at 300 and 150
var/health = 100
/// Basic code functions
var/maxhealth = 100
/// Used for calculating some stuff related to maxhealth as it constantly changes due to e.g. barbed wire. set to 100 to avoid possible divisions by zero
var/starting_maxhealth = 100
/// How much force an item needs to even damage it at all.
var/force_level_absorption = 5
var/barricade_hitsound
/// "metal", "plasteel", etc.
var/barricade_type = "barricade"
var/can_change_dmg_state = TRUE
var/damage_state = BARRICADE_DMG_NONE
var/closed = FALSE
var/can_wire = FALSE
var/is_wired = FALSE
var/metallic = TRUE
/obj/structure/barricade/Initialize(mapload, mob/user)
. = ..()
update_icon()
starting_maxhealth = maxhealth
/obj/structure/barricade/condition_hints(mob/user, distance, is_adjacent)
. += ..()
switch(damage_state)
if(BARRICADE_DMG_NONE)
. += SPAN_INFO("It appears to be in good shape.")
if(BARRICADE_DMG_SLIGHT)
. += SPAN_WARNING("It's slightly damaged, but still very functional.")
if(BARRICADE_DMG_MODERATE)
. += SPAN_WARNING("It's quite beat up, but it's holding together.")
if(BARRICADE_DMG_HEAVY)
. += SPAN_WARNING("It's crumbling apart, just a few more blows will tear it apart!")
/obj/structure/barricade/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "RIGHT-click on this to rotate it (if not anchored to the floor)."
if(!is_wired)
. += "Use a length of barbed wire on this barricade to prevent enemies from climbing it and damage them on attacking it at close range."
else
. += "Wirecutters could remove the barbed wire on this, but it will take several moments."
/obj/structure/barricade/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
. += SPAN_INFO("It is recommended to stand flush to a barricade or one tile away for maximum effectiveness.")
if(is_wired)
. += SPAN_INFO("There is a length of barbed wire strewn across the top of this barricade, preventing it from being climbed and making it hazardous to attack at close range.")
/obj/structure/barricade/update_icon()
CutOverlays()
if(!closed)
if(can_change_dmg_state)
icon_state = "[barricade_type]_[damage_state]"
else
icon_state = "[barricade_type]"
switch(dir)
if(SOUTH)
layer = ABOVE_HUMAN_LAYER
if(NORTH)
layer = initial(layer) - 0.01
else
reset_plane_and_layer()
if(!anchored)
reset_plane_and_layer()
else
if(can_change_dmg_state)
icon_state = "[barricade_type]_closed_[damage_state]"
else
icon_state = "[barricade_type]_closed"
layer = OBJ_LAYER
if(is_wired)
if(!closed)
AddOverlays(image('icons/obj/barricades.dmi', icon_state = "[src.barricade_type]_wire"))
else
AddOverlays(image('icons/obj/barricades.dmi', icon_state = "[src.barricade_type]_closed_wire"))
..()
/obj/structure/barricade/proc/handle_barrier_chance()
if(!anchored)
return FALSE
return prob(max(30,(100.0*health)/maxhealth))
/obj/structure/barricade/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(mover?.movement_type & PHASING)
return TRUE
if(air_group || (height==0))
return TRUE
if(istype(mover, /obj/projectile))
return (check_cover(mover,target))
if (get_dir(loc, target) == dir)
return !density
else
return TRUE
/obj/structure/barricade/proc/check_cover(obj/projectile/P, turf/from)
var/turf/cover = get_turf(src)
if(!cover)
return TRUE
if (get_dist(P.starting, loc) <= 1)
return TRUE
var/chance = handle_barrier_chance()
if(chance)
bullet_act(P)
return FALSE
return TRUE
/obj/structure/barricade/CheckExit(atom/movable/O as mob|obj, target as turf)
if(istype(O) && O.pass_flags & PASSTABLE)
return TRUE
if (get_dir(loc, target) == dir)
return !density
else
return TRUE
/obj/structure/barricade/can_climb(var/mob/living/user, post_climb_check=0)
if(is_wired)
to_chat(user, SPAN_WARNING("\The [src] has barbed wire over it, restricting you from climbing over!"))
return FALSE
return ..()
/obj/structure/barricade/attack_robot(mob/user)
return attack_hand(user)
/obj/structure/barricade/attack_hand(var/mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/shredding = H.species.can_shred(H)
if(shredding)
var/datum/unarmed_attack/UA = H.default_attack
if((UA.damage + UA.armor_penetration) > force_level_absorption)
var/attack_verb = pick("mangles", "slices", "slashes", "shreds")
attack_generic(user, UA.damage, attack_verb)
/obj/structure/barricade/attack_generic(mob/user, damage, attack_message, environment_smash, armor_penetration, attack_flags, damage_type)
if(!damage)
return FALSE
if(!isliving(user))
return
var/mob/living/L = user
L.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
visible_message(SPAN_DANGER("[L] [attack_verb] the [src]!"))
if(barricade_hitsound)
playsound(src, barricade_hitsound, 50, 1)
if(is_wired)
visible_message(SPAN_DANGER("\The [src]'s barbed wire slices into [L]!"))
L.apply_damage((5), DAMAGE_BRUTE, pick(BP_R_HAND, BP_L_HAND), "barbed wire", DAMAGE_FLAG_SHARP|DAMAGE_FLAG_EDGE, 25)
L.do_attack_animation(src)
take_damage(damage)
/obj/structure/barricade/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/stack/barbed_wire))
var/obj/item/stack/barbed_wire/B = attacking_item
if(can_wire)
user.visible_message(SPAN_NOTICE("[user] starts setting up [attacking_item.name] on [src]."),
SPAN_NOTICE("You start setting up [attacking_item.name] on [src]."))
if(do_after(user, 20, src, DO_REPAIR_CONSTRUCT) && can_wire)
// Make sure there's still enough wire in the stack
if(!B.use(1))
return
playsound(src.loc, 'sound/effects/barbed_wire_movement.ogg', 25, 1)
user.visible_message(SPAN_NOTICE("[user] sets up [attacking_item.name] on [src]."),
SPAN_NOTICE("You set up [attacking_item.name] on [src]."))
maxhealth += 50
update_health(-50)
can_wire = FALSE
is_wired = TRUE
climbable = FALSE
update_icon()
return
if(attacking_item.tool_behaviour == TOOL_WIRECUTTER)
if(is_wired)
user.visible_message(SPAN_NOTICE("[user] begin removing the barbed wire on [src]."),
SPAN_NOTICE("You begin removing the barbed wire on [src]."))
if(do_after(user, 20, src, DO_REPAIR_CONSTRUCT))
if(!is_wired)
return
playsound(src.loc, 'sound/items/Wirecutter.ogg', 25, 1)
user.visible_message(SPAN_NOTICE("[user] removes the barbed wire on [src]."),
SPAN_NOTICE("You remove the barbed wire on [src]."))
maxhealth -= 50
update_health(50)
can_wire = TRUE
is_wired = FALSE
climbable = TRUE
update_icon()
new /obj/item/stack/barbed_wire(loc)
return
if((attacking_item.force + attacking_item.armor_penetration) > force_level_absorption)
..()
if(barricade_hitsound)
playsound(src, barricade_hitsound, 25, 1)
hit_barricade(attacking_item, user)
/obj/structure/barricade/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
if(. != BULLET_ACT_HIT)
return .
bullet_ping(hitting_projectile)
var/damage_to_take = hitting_projectile.damage * hitting_projectile.anti_materiel_potential
take_damage(damage_to_take)
/obj/structure/barricade/proc/barricade_deconstruct(deconstruct)
if(deconstruct && is_wired)
new /obj/item/stack/barbed_wire(loc)
if(stack_type)
var/stack_amt
if(!deconstruct && destroyed_stack_amount)
stack_amt = destroyed_stack_amount
else
stack_amt = round(stack_amount * (health/starting_maxhealth)) //Get an amount of sheets back equivalent to remaining health. Obviously, fully destroyed means 0
if(stack_amt)
new stack_type (loc, stack_amt)
qdel(src)
/obj/structure/barricade/ex_act(severity, direction, cause_data)
for(var/obj/structure/barricade/B in get_step(src,dir)) //discourage double-stacking barricades by removing health from opposing barricade
if(B.dir == REVERSE_DIR(dir))
INVOKE_ASYNC(B, TYPE_PROC_REF(/atom, ex_act), severity, direction)
update_health(round(severity))
// This proc is called whenever the cade is moved, so I thought it was appropriate,
// especially since the barricade's direction needs to be handled when moving
// diagonally.
/obj/structure/barricade/Move()
. = ..()
if (dir & EAST)
set_dir(EAST)
else if(dir & WEST)
set_dir(WEST)
update_icon()
/obj/structure/barricade/set_dir(ndir)
. = ..()
update_icon()
/obj/structure/barricade/proc/hit_barricade(var/obj/item/I, var/mob/user)
if(!isliving(user))
return
var/mob/living/L = user
L.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
var/message = pick(I.attack_verb)
visible_message(SPAN_DANGER("[L] has [message] the [src]!"))
L.do_attack_animation(src)
take_damage(I.force)
/obj/structure/barricade/proc/take_damage(var/damage)
for(var/obj/structure/barricade/B in get_step(src,dir)) //discourage double-stacking barricades by removing health from opposing barricade
if(B.dir == REVERSE_DIR(dir))
B.update_health(damage)
update_health(damage)
/obj/structure/barricade/proc/update_health(damage, nomessage)
health -= damage
health = clamp(health, 0, maxhealth)
if(!health)
if(!nomessage)
visible_message(SPAN_DANGER("[src] falls apart!"))
barricade_deconstruct()
return
update_damage_state()
update_icon()
/obj/structure/barricade/proc/update_damage_state()
var/health_percent = round(health/maxhealth * 100)
switch(health_percent)
if(0 to 25) damage_state = BARRICADE_DMG_HEAVY
if(25 to 50) damage_state = BARRICADE_DMG_MODERATE
if(50 to 75) damage_state = BARRICADE_DMG_SLIGHT
if(75 to INFINITY) damage_state = BARRICADE_DMG_NONE
/obj/structure/barricade/proc/weld_cade(obj/item/weldingtool/WT, mob/user)
if(!metallic)
return FALSE
if(!(WT.use(2, user)))
return FALSE
user.visible_message(SPAN_NOTICE("[user] begins repairing damage to [src]."),
SPAN_NOTICE("You begin repairing the damage to [src]."))
if(WT.use_tool(src, user, 7 SECONDS, volume = 40))
user.visible_message(SPAN_NOTICE("[user] repairs some damage on [src]."),
SPAN_NOTICE("You repair \the [src]."))
update_health(-200)
return TRUE
/obj/structure/barricade/verb/count_rotate()
set name = "Rotate Barricade Counter-Clockwise"
set category = "Object"
set src in oview(1)
rotate(usr, 1)
/obj/structure/barricade/verb/clock_rotate()
set name = "Rotate Barricade Clockwise"
set category = "Object"
set src in oview(1)
rotate(usr, -1)
/obj/structure/barricade/rotate(var/mob/user, var/rotation_dir = -1)//-1 for clockwise, 1 for counter clockwise
if(world.time <= user.next_move || !ishuman(user) || use_check_and_message(user))
return
if(anchored)
to_chat(usr, SPAN_WARNING("\The [src] is fastened to the floor, you can't rotate it!"))
return
user.next_move = world.time + 3 //slight spam prevention? you don't want every metal cade to turn into a doorway
set_dir(turn(dir, 90 * rotation_dir))
update_icon()
/obj/structure/barricade/AltClick(mob/user)
rotate(user)