mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-31 09:00:25 +01:00
This permits the impact drills to work for surgeries where a screwdriver would work. For the regular uses that would be opening the hatches on prosthetic limbs and prosthetic organ repairs. This was done as machinists, who primarily do those surgeries, usually print an impact drill for themself anyway so it was an annoyance to have a screwdriver just for that one thing in their entire workshop. While at it, I also changed a few hardcoded tool checks elsewhere to not be as hardcoded.
960 lines
30 KiB
Plaintext
960 lines
30 KiB
Plaintext
/obj/item/trap
|
|
name = "mechanical trap"
|
|
desc = "A mechanically activated leg trap. Low-tech, but reliable. Looks like it could really hurt if you set it off."
|
|
throw_speed = 2
|
|
throw_range = 1
|
|
gender = PLURAL
|
|
icon = 'icons/obj/item/traps/traps.dmi'
|
|
icon_state = "beartrap"
|
|
randpixel = 0
|
|
center_of_mass = null
|
|
throwforce = 0
|
|
w_class = WEIGHT_CLASS_NORMAL
|
|
origin_tech = list(TECH_ENGINEERING = 2)
|
|
matter = list(MATERIAL_STEEL = 18750)
|
|
can_buckle = list(/mob/living)
|
|
|
|
update_icon_on_init = TRUE
|
|
|
|
/// This boolean indicates that the trap is deployed and ready to trap a mob. It can only be deployed after being secured.
|
|
var/deployed = FALSE
|
|
|
|
/// This boolean determines whether the trap will unanchor itself after being disarmed
|
|
var/unsecure_on_disarm = TRUE
|
|
|
|
/// Time in deciseconds needed to escape from the trap
|
|
var/time_to_escape = 1 MINUTE
|
|
|
|
/// When the trap triggers and does damage, this will be the armor penetration value
|
|
var/activated_armor_penetration = 0
|
|
|
|
/obj/item/trap/Initialize()
|
|
. = ..()
|
|
update_icon()
|
|
|
|
var/static/list/loc_connections = list(
|
|
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
|
|
)
|
|
|
|
AddElement(/datum/element/connect_loc, loc_connections)
|
|
|
|
/obj/item/trap/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = ..()
|
|
. += get_trap_examine_text(user, distance, is_adjacent, infix, suffix)
|
|
|
|
/obj/item/trap/proc/get_trap_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = list()
|
|
if(buckled)
|
|
. += SPAN_NOTICE("You can free the creature inside the trap by clicking on it.")
|
|
else if(deployed)
|
|
. += SPAN_NOTICE("You can disarm the trap by clicking on it.")
|
|
else
|
|
. += SPAN_NOTICE("You can deploy the trap by using it in-hand.")
|
|
|
|
/obj/item/trap/proc/can_use(mob/user)
|
|
return (user.IsAdvancedToolUser() && !issilicon(user) && !user.stat && !user.restrained())
|
|
|
|
/obj/item/trap/attack_self(mob/user)
|
|
..()
|
|
if(!deployed && can_use(user))
|
|
if(deploy(user))
|
|
user.drop_from_inventory(src)
|
|
anchored = TRUE
|
|
|
|
/obj/item/trap/proc/deploy(mob/user)
|
|
user.visible_message("[SPAN_BOLD("[user]")] starts deploying \the [src]...", SPAN_NOTICE("You begin deploying \the [src]!"), SPAN_WARNING("You hear the slow creaking of a spring."))
|
|
|
|
if(do_after(user, 5 SECONDS))
|
|
playsound(src, 'sound/items/crank.ogg', 50, TRUE)
|
|
user.visible_message("[SPAN_BOLD("[user]")] deploys \the [src].", SPAN_WARNING("You deploy \the [src]!"), SPAN_WARNING("You hear a latch click loudly."))
|
|
deployed = TRUE
|
|
update_icon()
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
/obj/item/trap/user_unbuckle(mob/user)
|
|
if(!buckled || !can_use(user))
|
|
return
|
|
|
|
user.visible_message("[SPAN_BOLD("[user]")] begins freeing \the [buckled] from \the [src]...", SPAN_NOTICE("You carefully begin to free \the [buckled] from \the [src]..."), SPAN_NOTICE("You hear metal creaking."))
|
|
|
|
if(do_after(user, time_to_escape))
|
|
user.visible_message("[SPAN_BOLD("[user]")] frees \the [buckled] from \the [src].", SPAN_NOTICE("You free \the [buckled] from \the [src]."))
|
|
unbuckle()
|
|
anchored = FALSE
|
|
|
|
/obj/item/trap/attack_hand(mob/user)
|
|
if(can_use(user))
|
|
if(buckled)
|
|
user_unbuckle(user)
|
|
return
|
|
else if(deployed)
|
|
disarm_trap(user)
|
|
return
|
|
..()
|
|
|
|
/obj/item/trap/proc/disarm_trap(var/mob/user)
|
|
user.visible_message("[SPAN_BOLD("[user]")] starts disarming \the [src]...", SPAN_NOTICE("You begin disarming \the [src]..."), SPAN_WARNING("You hear a latch click followed by the slow creaking of a spring."))
|
|
if(do_after(user, 6 SECONDS))
|
|
user.visible_message("[SPAN_BOLD("[user]")] disarms \the [src]!", SPAN_NOTICE("You disarm \the [src]!"))
|
|
deployed = FALSE
|
|
if(unsecure_on_disarm)
|
|
anchored = FALSE
|
|
update_icon()
|
|
|
|
/obj/item/trap/proc/attack_mob(mob/living/L)
|
|
var/target_zone = L.lying ? ran_zone() : pick(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG)
|
|
|
|
var/success = L.apply_damage(30, DAMAGE_BRUTE, target_zone, used_weapon = src, armor_pen = activated_armor_penetration)
|
|
if(!success)
|
|
return FALSE
|
|
|
|
L.visible_message(SPAN_DANGER("\The [L] steps on \the [src]!"), FONT_LARGE(SPAN_DANGER("You step on \the [src]!")), SPAN_WARNING("<b>You hear a loud metallic snap!</b>"))
|
|
|
|
var/did_trap = TRUE
|
|
if(ishuman(L))
|
|
var/mob/living/carbon/human/H = L
|
|
var/obj/item/organ/external/limb = H.get_organ(check_zone(target_zone))
|
|
if(!limb || limb.is_stump()) // oops, we took the limb clean off
|
|
did_trap = FALSE
|
|
|
|
if(did_trap)
|
|
//trap the victim in place
|
|
buckle(L)
|
|
|
|
deployed = FALSE
|
|
to_chat(L, FONT_LARGE(SPAN_DANGER("The steel jaws of \the [src] bite into you, [did_trap ? "trapping you in place" : "cleaving your limb clean off"]!")))
|
|
playsound(src, 'sound/weapons/beartrap_shut.ogg', 100, TRUE) //Really loud snapping sound
|
|
|
|
if (istype(L, /mob/living/simple_animal/hostile/bear))
|
|
var/mob/living/simple_animal/hostile/bear/bear = L
|
|
bear.anger += 15//traps make bears really angry
|
|
bear.instant_aggro()
|
|
|
|
if(!buckled)
|
|
anchored = FALSE
|
|
deployed = FALSE
|
|
|
|
/obj/item/trap/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
SIGNAL_HANDLER
|
|
|
|
if(ishuman(arrived))
|
|
var/mob/living/carbon/human/H = arrived
|
|
if(H.shoes?.item_flags & ITEM_FLAG_LIGHT_STEP)
|
|
return
|
|
if(deployed && isliving(arrived))
|
|
var/mob/living/L = arrived
|
|
if(L.pass_flags & PASSTABLE)
|
|
return
|
|
INVOKE_ASYNC(src, PROC_REF(attack_mob), L)
|
|
update_icon()
|
|
shake_animation()
|
|
|
|
/obj/item/trap/update_icon()
|
|
icon_state = "[initial(icon_state)][deployed]"
|
|
|
|
|
|
/*
|
|
##################
|
|
Subtypes
|
|
##################
|
|
*/
|
|
|
|
/**
|
|
* # Sharpened trap
|
|
*
|
|
* This device has an even higher chance of penetrating armor and locking foes in place
|
|
*/
|
|
/obj/item/trap/sharpened
|
|
name = "sharpened mechanical trap"
|
|
activated_armor_penetration = 100
|
|
|
|
/obj/item/trap/sharpened/antagonist_hints(mob/user, distance, is_adjacent)
|
|
. += ..()
|
|
. += "This device has an even higher chance of penetrating armor and locking foes in place."
|
|
|
|
/**
|
|
* # Tripwire trap
|
|
*
|
|
* A trap that makes you fall over
|
|
*/
|
|
/obj/item/trap/tripwire
|
|
name = "tripwire trap"
|
|
desc = "A piece of cable coil strung between two metal rods. Low-tech, but reliable."
|
|
icon_state = "tripwire"
|
|
color = COLOR_RED
|
|
layer = UNDERDOOR // so you can't cover it with items
|
|
|
|
/obj/item/trap/tripwire/Initialize(mapload, var/new_cable_color)
|
|
. = ..()
|
|
if(!new_cable_color)
|
|
new_cable_color = COLOR_RED
|
|
color = new_cable_color
|
|
|
|
/obj/item/trap/tripwire/update_icon()
|
|
underlays = null
|
|
icon_state = "[initial(icon_state)][deployed]"
|
|
var/image/I = image(icon, null, "[icon_state]_base")
|
|
I.appearance_flags = RESET_COLOR
|
|
underlays = list(I)
|
|
|
|
/obj/item/trap/tripwire/deploy(mob/user)
|
|
user.visible_message(SPAN_WARNING("\The [user] starts to deploy \the [src]."), SPAN_WARNING("You begin deploying \the [src]!"))
|
|
if(do_after(user, 5 SECONDS))
|
|
playsound(src, 'sound/items/crank.ogg', 50, TRUE)
|
|
user.visible_message(SPAN_WARNING("\The [user] deploys \the [src]."), SPAN_WARNING("You deploy \the [src]!"))
|
|
deployed = TRUE
|
|
update_icon()
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/obj/item/trap/tripwire/disarm_trap(var/mob/user)
|
|
user.visible_message(SPAN_NOTICE("\The [user] starts to take \the [src] down..."), SPAN_NOTICE("You begin taking \the [src] down..."), SPAN_WARNING("You hear a latch click followed by the slow creaking of a spring."))
|
|
if(do_after(user, 6 SECONDS))
|
|
deployed = FALSE
|
|
if(unsecure_on_disarm)
|
|
anchored = FALSE
|
|
update_icon()
|
|
|
|
/obj/item/trap/tripwire/attack_mob(mob/living/L)
|
|
if(!ishuman(L))
|
|
return
|
|
|
|
var/mob/living/carbon/human/H = L
|
|
if(!H.organs_by_name[BP_L_LEG] && !H.organs_by_name[BP_R_LEG]) // tripwires are triggered by shin, so if you don't have legs, assume you fly or crawl
|
|
return
|
|
|
|
if(!L.lying && (L.m_intent == M_RUN) || prob(5))
|
|
L.visible_message(SPAN_DANGER("\The [L] trips over \the [src]!"), FONT_LARGE(SPAN_DANGER("You trip over \the [src]!")))
|
|
L.Weaken(3)
|
|
|
|
/**
|
|
* # Punji trap
|
|
*
|
|
* A trap that damages and gives an infection to the victim, can have a message attached
|
|
*/
|
|
/obj/item/trap/punji
|
|
name = "punji trap"
|
|
desc = "An horrendous trap."
|
|
icon = 'icons/obj/item/traps/punji.dmi'
|
|
icon_state = "punji"
|
|
var/message = null
|
|
|
|
/obj/item/trap/punji/feedback_hints(mob/user, distance, is_adjacent)
|
|
. += ..()
|
|
if(src.message && distance < 3)
|
|
. += SPAN_ALERT("You notice something written on a plate inside the trap:")
|
|
. += SPAN_BAD(message)
|
|
|
|
/obj/item/trap/punji/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
if(deployed && isliving(arrived))
|
|
var/mob/living/L = arrived
|
|
attack_mob(L)
|
|
update_icon()
|
|
|
|
/obj/item/trap/punji/attack_mob(mob/living/L)
|
|
|
|
//Reveal the trap, if not already visible
|
|
invisibility = 0
|
|
|
|
//Select a target zone
|
|
var/target_zone
|
|
if(L.lying)
|
|
target_zone = pick(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_L_HAND, BP_L_ARM, BP_R_HAND, BP_R_ARM)
|
|
else
|
|
target_zone = pick(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG)
|
|
|
|
//Try to apply the damage
|
|
var/success = L.apply_damage(50, DAMAGE_BRUTE, target_zone, used_weapon = src, armor_pen = activated_armor_penetration)
|
|
//Apply weakness, so the victim doesn't walk immediately back out of the trap
|
|
L.Weaken(10)
|
|
|
|
//If successfully applied, give the message
|
|
if(success)
|
|
|
|
//Show the leftover message, if any, after a little
|
|
addtimer(CALLBACK(src, PROC_REF(reveal_message), L), 3 SECONDS)
|
|
|
|
//Give a simple message and return if it's not a human
|
|
if(!ishuman(L))
|
|
L.visible_message(SPAN_DANGER("[L] steps on \the [src]!"))
|
|
return
|
|
|
|
var/mob/living/carbon/human/human = L
|
|
var/obj/item/organ/organ = human.get_organ(target_zone)
|
|
|
|
if(isipc(L) || isrobot(L))
|
|
playsound(src, 'sound/weapons/smash.ogg', 100, TRUE)
|
|
else
|
|
playsound(src, 'sound/weapons/heavysmash.ogg', 100, TRUE)
|
|
|
|
human.visible_message(SPAN_DANGER("\The [human] steps on \the [src]!"),
|
|
SPAN_WARNING(FONT_LARGE(SPAN_DANGER("You step on \the [src], feel your body fall, and something sharp penetrate your [organ.name]!"))),
|
|
SPAN_WARNING("<b>You feel your body fall, and something sharp penetrate your [organ.name]!</b>"))
|
|
|
|
//If it's a human and not an IPC, apply an infection
|
|
//We are returning early before this step in case something isn't a human, so this should be fine not to catch borgs/bot/exosuits/whatever
|
|
if(!isipc(L))
|
|
|
|
//If it's a Vaurca, there's a chance the spear wouldn't go in deep enough to apply an infection
|
|
//You're still damaged by falling on it though, which happens above, but at least you're spared the infection
|
|
//Glory to your carapace
|
|
|
|
//Also, don't infect robotic limbs with infections!!!!!!!!!!!
|
|
//Something something the certainty of steel
|
|
if(isvaurca(L) && prob(50) || organ.robotic == ROBOTIC_MECHANICAL)
|
|
return
|
|
|
|
organ.germ_level += INFECTION_LEVEL_TWO
|
|
|
|
/obj/item/trap/punji/proc/reveal_message(mob/living/victim)
|
|
if(!message)
|
|
return
|
|
|
|
//If the mob moved away and/or no longer sees the trap, do not show the message
|
|
if(!(src in oview(world.view, victim)))
|
|
return
|
|
|
|
victim.visible_message(SPAN_ALERT("You notice something written on a plate inside the trap: <br>")+SPAN_BAD(message))
|
|
|
|
/obj/item/trap/punji/verb/hide_under()
|
|
set src in oview(1)
|
|
set name = "Hide"
|
|
set desc = "Hide the trap under the cover."
|
|
set category = "Object"
|
|
|
|
if(use_check_and_message(usr, USE_DISALLOW_SILICONS))
|
|
return
|
|
|
|
to_chat(usr, SPAN_NOTICE("You begin hiding the trap..."))
|
|
if(!do_after(usr, 15 SECONDS))
|
|
return
|
|
|
|
hide(TRUE)
|
|
to_chat(usr, SPAN_ALERT("You hide \the [src], remember where you left it or suffer the very same warcrime you wanted to inflict!"))
|
|
|
|
/obj/item/trap/punji/verb/set_message()
|
|
set src in oview(1)
|
|
set name = "Set Message"
|
|
set desc = "Set a message for the victim of the trap."
|
|
set category = "Object"
|
|
|
|
if(src.message)
|
|
to_chat(usr, SPAN_NOTICE("There is already a carved message inside the trap, can't make more..."))
|
|
return
|
|
|
|
var/added_message = tgui_input_text(usr, "Leave your message here...", "Punji trap message", multiline = TRUE, encode = FALSE)
|
|
|
|
if(added_message)
|
|
to_chat(usr, SPAN_NOTICE("You begin carving the message inside the trap..."))
|
|
if(do_after(usr, 10 SECONDS))
|
|
src.message = strip_html_readd_newlines(added_message)
|
|
|
|
/obj/item/trap/punji/deployed
|
|
deployed = TRUE
|
|
anchored = TRUE
|
|
|
|
/obj/item/trap/punji/deployed/hidden
|
|
invisibility = INVISIBILITY_MAXIMUM
|
|
|
|
/obj/item/trap/jagged_rock
|
|
name = "treacherous rock"
|
|
desc = "A jagged and dangerous outcropping."
|
|
icon = 'icons/obj/flora/rocks_grey.dmi'
|
|
icon_state = "basalt"
|
|
anchored = TRUE
|
|
invisibility = 0
|
|
|
|
/obj/item/trap/jagged_rock/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
if(isliving(arrived))
|
|
var/mob/living/L = arrived
|
|
attack_mob(L)
|
|
update_icon()
|
|
|
|
/obj/item/trap/jagged_rock/attack_mob(mob/living/L)
|
|
|
|
//Select a target zone
|
|
var/target_zone
|
|
if(L.lying)
|
|
target_zone = pick(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_L_HAND, BP_L_ARM, BP_R_HAND, BP_R_ARM)
|
|
else
|
|
target_zone = pick(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG)
|
|
|
|
//Try to apply the damage
|
|
var/success = L.apply_damage(50, DAMAGE_BRUTE, target_zone, used_weapon = src, armor_pen = activated_armor_penetration)
|
|
//Apply weakness, so the victim doesn't walk immediately back out of the trap
|
|
L.Weaken(10)
|
|
|
|
//If successfully applied, give the message
|
|
if(success)
|
|
|
|
//Give a simple message and return if it's not a human
|
|
if(!ishuman(L))
|
|
L.visible_message(SPAN_DANGER("[L] slams into \the [src]!"))
|
|
return
|
|
|
|
var/mob/living/carbon/human/human = L
|
|
var/obj/item/organ/organ = human.get_organ(target_zone)
|
|
|
|
if(isipc(L) || isrobot(L))
|
|
playsound(src, 'sound/weapons/smash.ogg', 100, TRUE)
|
|
else
|
|
playsound(src, 'sound/weapons/heavysmash.ogg', 100, TRUE)
|
|
|
|
human.visible_message(SPAN_DANGER("\The [human] slams into \the [src]!"),
|
|
SPAN_WARNING(FONT_LARGE(SPAN_DANGER("You crash on \the [src], feel your body crumble, and something sharp penetrate your [organ.name]!"))),
|
|
SPAN_WARNING("<b>You feel your body crumble, and something sharp penetrate your [organ.name]!</b>"))
|
|
|
|
/**
|
|
* # Animal trap
|
|
*
|
|
* Used to catch small animals like rats, lizards, and chicks
|
|
*/
|
|
/obj/item/trap/animal
|
|
name = "small trap"
|
|
desc = "A small mechanical trap that's used to catch small animals like rats, lizards, and chicks."
|
|
icon_state = "small"
|
|
throwforce = 2
|
|
force = 1
|
|
w_class = WEIGHT_CLASS_SMALL
|
|
origin_tech = list(TECH_ENGINEERING = 1)
|
|
matter = list(MATERIAL_STEEL = 1750)
|
|
health = 100
|
|
can_astar_pass = CANASTARPASS_ALWAYS_PROC
|
|
time_to_escape = 3 MINUTES
|
|
unsecure_on_disarm = FALSE
|
|
|
|
/// Whether someone is actively breaking out of this trap
|
|
var/breakout = FALSE
|
|
|
|
/// The time in deciseconds between shakes when a user is trying to escape
|
|
var/shake_time = 5 SECONDS
|
|
|
|
/// The world.time of when the last breakout shake callback happened successfully
|
|
var/last_shake = 0
|
|
|
|
/// When a mob of this size crosses over the active trap, it'll be caught
|
|
var/max_mob_size = MOB_MINISCULE
|
|
|
|
/// When deconstructed, it will return these materials
|
|
var/list/resources = list(/obj/item/stack/rods = 6)
|
|
|
|
/// A weakref to the mob currently held inside the trap
|
|
var/datum/weakref/captured = null
|
|
|
|
/obj/item/trap/animal/get_trap_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = list()
|
|
if(captured)
|
|
var/mob/captured_mob = captured.resolve()
|
|
if(captured_mob)
|
|
. += SPAN_NOTICE("You can free the creature inside by right clicking the trap and selecting \"Release Animal\".")
|
|
if(deployed)
|
|
. += SPAN_NOTICE("You can disarm the trap by clicking on it, or by right clicking the trap and selecting \"Deploy / Undeploy Trap\".")
|
|
else if(anchored)
|
|
. += get_securing_text(secure = FALSE)
|
|
if(!captured)
|
|
. += SPAN_NOTICE("You can deploy the trap by clicking on it, or by right clicking the trap and selecting \"Deploy / Undeploy Trap\".")
|
|
else
|
|
. += get_securing_text(secure = TRUE)
|
|
. += SPAN_NOTICE("The trap can be deconstructed with a welding tool.")
|
|
|
|
/obj/item/trap/animal/proc/get_securing_text(var/secure = TRUE)
|
|
if(secure)
|
|
return SPAN_NOTICE("You can secure the trap by using a screwdriver on it. This will anchor it to the floor, and ready it for deployment.")
|
|
return SPAN_NOTICE("You can unsecure the trap by using a screwdriver on it. This will unanchor it from the floor, allowing it to be moved.")
|
|
|
|
/obj/item/trap/animal/mouse_drop_receive(atom/dropped, mob/user, params)
|
|
var/mob/living/capturing_mob = dropped
|
|
if(!istype(capturing_mob))
|
|
return
|
|
|
|
if(captured)
|
|
to_chat(user, SPAN_WARNING("\The [src] is already full!"))
|
|
return
|
|
|
|
if(capturing_mob.mob_size > max_mob_size)
|
|
to_chat(user, SPAN_WARNING("[capturing_mob] won't fit in there!"))
|
|
return
|
|
|
|
if(!do_mob(user, capturing_mob, 3 SECONDS, needhand = FALSE))
|
|
return
|
|
|
|
capture(capturing_mob)
|
|
|
|
/obj/item/trap/animal/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
if(!deployed || !anchored)
|
|
return
|
|
|
|
if(captured) // just in case but this shouldn't happen
|
|
return
|
|
|
|
capture(arrived)
|
|
|
|
/obj/item/trap/animal/proc/capture(var/atom/movable/movable_atom, var/msg = 1)
|
|
if(!isliving(movable_atom))
|
|
return
|
|
|
|
var/mob/living/capturing_mob = movable_atom
|
|
if(capturing_mob.mob_size > max_mob_size)
|
|
return
|
|
|
|
if(msg)
|
|
capturing_mob.visible_message(SPAN_DANGER("[capturing_mob] enters \the [src], and it snaps shut with a clatter!"), SPAN_DANGER("You enter \the [src], and it snaps shut with a clatter!"), SPAN_DANGER("You hear a loud metallic snap!"))
|
|
|
|
if(capturing_mob.loc != loc)
|
|
capturing_mob.forceMove(loc)
|
|
|
|
captured = WEAKREF(capturing_mob)
|
|
INVOKE_ASYNC(src, PROC_REF(buckle), capturing_mob)
|
|
layer = capturing_mob.layer + 0.1
|
|
|
|
playsound(src, 'sound/weapons/beartrap_shut.ogg', 100, 1)
|
|
|
|
deployed = FALSE
|
|
|
|
shake_animation()
|
|
update_icon()
|
|
|
|
/obj/item/trap/animal/proc/can_breakout()
|
|
if(deployed || !captured)
|
|
return FALSE // Trap-door is open, no one is captured.
|
|
if(breakout)
|
|
return FALSE //Already breaking out.
|
|
return TRUE
|
|
|
|
/obj/item/trap/animal/proc/breakout_callback(var/mob/living/escapee)
|
|
if (QDELETED(escapee))
|
|
return FALSE
|
|
|
|
if ((world.time - last_shake) > 5 SECONDS)
|
|
playsound(loc, 'sound/effects/grillehit.ogg', 100, TRUE)
|
|
shake_animation()
|
|
last_shake = world.time
|
|
|
|
return TRUE
|
|
|
|
// If we are stuck, and need to get out
|
|
/obj/item/trap/animal/user_unbuckle(var/mob/living/escapee)
|
|
if (!can_breakout())
|
|
return
|
|
|
|
escapee.next_move = world.time + 1 SECOND
|
|
escapee.last_special = world.time + 1 SECOND
|
|
|
|
to_chat(escapee, SPAN_WARNING("You begin to shake and bump the lock of \the [src]. (this will take about [time_to_escape / 600] minutes)."))
|
|
visible_message(SPAN_DANGER("\The [src] begins to shake violently! Something is attempting to escape it!"))
|
|
|
|
if (!do_after(escapee, time_to_escape, src, extra_checks = CALLBACK(src, PROC_REF(breakout_callback), escapee)))
|
|
breakout = FALSE
|
|
return
|
|
|
|
breakout = FALSE
|
|
to_chat(escapee, SPAN_WARNING("You successfully break out!"))
|
|
visible_message(SPAN_DANGER("\The [escapee] successfully breaks out of \the [src]!"))
|
|
playsound(loc, 'sound/effects/grillehit.ogg', 100, 1)
|
|
|
|
release()
|
|
|
|
/obj/item/trap/animal/CollidedWith(atom/bumped_atom)
|
|
if(deployed)
|
|
Crossed(bumped_atom)
|
|
return
|
|
return ..()
|
|
|
|
/obj/item/trap/animal/verb/deploy_trap()
|
|
set src in orange(1)
|
|
set category = "Object"
|
|
set name = "Deploy / Undeploy Trap"
|
|
|
|
if(use_check_and_message(usr))
|
|
return
|
|
|
|
if(!can_use(usr))
|
|
to_chat(usr, SPAN_WARNING("You cannot use \the [src]."))
|
|
return
|
|
|
|
if(deployed)
|
|
if(captured)
|
|
to_chat(usr, SPAN_WARNING("\The [src] already has something inside!"))
|
|
return
|
|
|
|
disarm_trap(usr)
|
|
else
|
|
if(!anchored)
|
|
to_chat(usr, SPAN_WARNING("\The [src] needs to be secured first!"))
|
|
return
|
|
|
|
if(captured)
|
|
to_chat(usr, SPAN_WARNING("\The [src] already has something inside!"))
|
|
return
|
|
|
|
deploy(usr)
|
|
|
|
/obj/item/trap/animal/verb/release_animal()
|
|
set src in orange(1)
|
|
set category = "Object"
|
|
set name = "Release Animal"
|
|
|
|
if(!usr.canmove || usr.stat || usr.restrained())
|
|
return
|
|
|
|
if(!ishuman(usr))
|
|
to_chat(usr, SPAN_WARNING("This mob type can't use this verb."))
|
|
return
|
|
|
|
var/mob/captured_mob = captured ? captured.resolve() : null
|
|
|
|
if(captured_mob)
|
|
if(tgui_alert(usr, "Do you want to open the cage and free \the [captured_mob]?", src.name, list("Yes", "No")) == "No")
|
|
return
|
|
|
|
if(!can_use(usr))
|
|
to_chat(usr, SPAN_WARNING("You cannot use \the [src]."))
|
|
return
|
|
|
|
if(usr == captured_mob)
|
|
to_chat(usr, SPAN_WARNING("You can't open \the [src] from the inside! You'll need to force it open."))
|
|
return
|
|
|
|
var/adj = src.Adjacent(usr)
|
|
if(!adj)
|
|
attack_self(src)
|
|
return
|
|
|
|
release(usr)
|
|
|
|
/obj/item/trap/animal/crush_act()
|
|
if(captured)
|
|
var/datum/L = captured ? captured.resolve() : null
|
|
if(L && isliving(L))
|
|
var/mob/living/LL = L
|
|
LL.gib()
|
|
new /obj/item/stack/material/steel(get_turf(src))
|
|
qdel(src)
|
|
|
|
/obj/item/trap/animal/ex_act(severity)
|
|
switch(severity)
|
|
if(1)
|
|
health -= rand(120, 240)
|
|
if(2)
|
|
health -= rand(60, 120)
|
|
if(3)
|
|
health -= rand(30, 60)
|
|
|
|
if (health <= 0)
|
|
if(captured)
|
|
release()
|
|
new /obj/item/stack/material/steel(get_turf(src))
|
|
qdel(src)
|
|
|
|
/obj/item/trap/animal/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
|
|
. = ..()
|
|
if(. != BULLET_ACT_HIT)
|
|
return .
|
|
|
|
var/mob/living/captured_mob = captured ? captured.resolve() : null
|
|
if(captured_mob)
|
|
captured_mob.bullet_act(arglist(args))
|
|
|
|
/obj/item/trap/animal/proc/release(var/mob/user, var/turf/target)
|
|
if(!target)
|
|
target = src.loc
|
|
if(user)
|
|
visible_message(SPAN_NOTICE("[user] opens \the [src]."))
|
|
|
|
var/mob/captured_mob = captured ? captured.resolve() : null
|
|
if (!captured_mob)
|
|
captured = null
|
|
return
|
|
|
|
var/msg
|
|
if (isliving(captured_mob))
|
|
var/mob/living/living_mob = captured_mob
|
|
msg = SPAN_WARNING("[living_mob] runs out of \the [src].")
|
|
|
|
unbuckle()
|
|
captured = null
|
|
visible_message(msg)
|
|
shake_animation()
|
|
update_icon()
|
|
layer = initial(layer)
|
|
|
|
/obj/item/trap/animal/attackby(obj/item/attacking_item, mob/user)
|
|
if(istype(attacking_item, /obj/item/grab))
|
|
var/obj/item/grab/grab = attacking_item
|
|
var/mob/living/capturing_mob = grab.affecting
|
|
|
|
if (grab.state == GRAB_PASSIVE || grab.state == GRAB_UPGRADING)
|
|
to_chat(user, SPAN_NOTICE("You need a better grip on \the [capturing_mob]!"))
|
|
return
|
|
|
|
user.visible_message("[SPAN_BOLD("[user]")] starts putting \the [capturing_mob] into \the [src].", SPAN_NOTICE("You start putting \the [capturing_mob] into \the [src]."))
|
|
|
|
if(capturing_mob.mob_size > max_mob_size)
|
|
to_chat(user, SPAN_WARNING("\The [capturing_mob] won't fit in there!"))
|
|
return
|
|
|
|
if (do_mob(user, capturing_mob, 3 SECONDS, needhand = FALSE))
|
|
if(captured?.resolve())
|
|
return
|
|
capture(capturing_mob)
|
|
|
|
else if(attacking_item.tool_behaviour == TOOL_WELDER)
|
|
var/obj/item/weldingtool/WT = attacking_item
|
|
if(!WT.isOn())
|
|
to_chat(user, SPAN_WARNING("\The [WT] is off!"))
|
|
return
|
|
|
|
user.visible_message(SPAN_NOTICE("[user] is trying to slice \the [src] open!"), SPAN_NOTICE("You are trying to slice \the [src] open!"))
|
|
|
|
if(WT.use_tool(src, user, 6 SECONDS, volume = 50))
|
|
if(WT.use(2, user))
|
|
user.visible_message(SPAN_NOTICE("[user] slices \the [src] open!"), SPAN_NOTICE("You slice \the [src] open!"))
|
|
for(var/resource_path in resources)
|
|
new resource_path(loc, resources[resource_path])
|
|
release(user)
|
|
qdel(src)
|
|
|
|
else if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER)
|
|
var/turf/T = get_turf(src)
|
|
if(!T)
|
|
to_chat(user, SPAN_WARNING("There is nothing to secure \the [src] to!"))
|
|
return
|
|
|
|
user.visible_message("[SPAN_BOLD("[user]")] starts [anchored ? "un" : "" ]securing \the [src]...", SPAN_NOTICE("You start [anchored ? "un" : "" ]securing \the [src]..."))
|
|
|
|
if(attacking_item.use_tool(src, user, 3 SECONDS, volume = 50))
|
|
density = !density
|
|
anchored = !anchored
|
|
if(!anchored)
|
|
deployed = FALSE
|
|
user.visible_message("[SPAN_BOLD("[user]")] [anchored ? "" : "un" ]secures \the [src]!", SPAN_NOTICE("You [anchored ? "" : "un" ]secure \the [src]!"))
|
|
update_icon()
|
|
|
|
else
|
|
..()
|
|
|
|
/obj/item/trap/animal/Move()
|
|
. = ..()
|
|
if(captured)
|
|
var/datum/M = captured.resolve()
|
|
if(isliving(M))
|
|
var/mob/living/L = M
|
|
if(L && buckled.buckled_to == src)
|
|
L.forceMove(loc)
|
|
else if(L)
|
|
captured = null
|
|
else
|
|
captured = null
|
|
|
|
/obj/item/trap/animal/attack_hand(mob/user)
|
|
if(user.loc == src || captured)
|
|
return
|
|
|
|
if(deployed)
|
|
disarm_trap(user)
|
|
else if(anchored)
|
|
deploy(user)
|
|
else
|
|
..()
|
|
|
|
/obj/item/trap/animal/proc/pass_without_trace(var/mob/user, var/chance_to_trigger = 0)
|
|
user.visible_message("[SPAN_BOLD("[user]")] tries moving around \the [src] without triggering it.", SPAN_NOTICE("You try to carefully move around \the [src] without triggering it."))
|
|
if(do_after(user, 2 SECONDS, src))
|
|
if(chance_to_trigger && prob(chance_to_trigger))
|
|
user.forceMove(loc)
|
|
user.visible_message(SPAN_WARNING("[user] accidentally triggers \the [src]!"), SPAN_WARNING("You accidentally trigger \the [src]!"))
|
|
capture(user)
|
|
else
|
|
user.forceMove(loc)
|
|
user.visible_message("[SPAN_BOLD("[user]")] successfully moves around \the [src] without triggering it.", SPAN_NOTICE("You successfully move around \the [src] without triggering it."))
|
|
|
|
/obj/item/trap/animal/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
|
|
if(!isliving(user) || !src.Adjacent(user))
|
|
return
|
|
|
|
if(captured)
|
|
pass_without_trace(user) // It's full
|
|
return
|
|
|
|
else if(iscarbon(user))
|
|
pass_without_trace(user)
|
|
return
|
|
|
|
return ..()
|
|
|
|
/obj/item/trap/animal/attack_self(mob/user)
|
|
if(!can_use(user))
|
|
to_chat(user, SPAN_WARNING("You cannot use \the [src]."))
|
|
return
|
|
|
|
if(captured)
|
|
release(user, user.loc)
|
|
|
|
/obj/item/trap/animal/attack(mob/living/target_mob, mob/living/user, target_zone)
|
|
if(!deployed)
|
|
return
|
|
|
|
if(captured) // It is full
|
|
return
|
|
|
|
if(isliving(target_mob))
|
|
var/mob/living/capturing_mob = target_mob
|
|
if(capturing_mob.mob_size > max_mob_size)
|
|
to_chat(user, SPAN_WARNING("\The [capturing_mob] doesn't fit in \the [src]!"))
|
|
return
|
|
user.visible_message(SPAN_WARNING("[user] traps \the [capturing_mob] inside of \the [src]."), SPAN_WARNING("You trap [capturing_mob] inside of the \the [src]!"), SPAN_DANGER("You hear a loud metallic snap!"))
|
|
capture(capturing_mob, FALSE)
|
|
else
|
|
..()
|
|
|
|
/obj/item/trap/animal/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
|
|
return TRUE
|
|
|
|
/obj/item/trap/animal/CanAStarPass(to_dir, datum/can_pass_info/pass_info)
|
|
return TRUE
|
|
|
|
|
|
/**
|
|
* # Animal trap (Medium)
|
|
*
|
|
* Used to catch medium animals like cats, monkeys, nymphs, and wayward maintenance drones
|
|
*/
|
|
/obj/item/trap/animal/medium
|
|
name = "medium trap"
|
|
desc = "A medium mechanical trap that is used to catch moderately-sized animals like cats, monkeys, nymphs, and wayward maintenance drones."
|
|
icon_state = "medium"
|
|
throwforce = 4
|
|
force = 11
|
|
w_class = WEIGHT_CLASS_BULKY
|
|
origin_tech = list(TECH_ENGINEERING = 3)
|
|
matter = list(MATERIAL_STEEL = 5750)
|
|
|
|
max_mob_size = MOB_SMALL
|
|
resources = list(/obj/item/stack/rods = 12)
|
|
|
|
/**
|
|
* # Animal trap (Large)
|
|
*
|
|
* Used to catch larger animals, from spiders and dogs to bears and even larger mammals
|
|
*/
|
|
/obj/item/trap/animal/large
|
|
name = "large trap"
|
|
desc = "A large mechanical trap that is used to catch larger animals, from spiders and dogs to bears and even larger mammals."
|
|
icon_state = "large"
|
|
throwforce = 6
|
|
force = 10
|
|
w_class = 6
|
|
density = TRUE
|
|
origin_tech = list(TECH_ENGINEERING = 3)
|
|
matter = list(MATERIAL_STEEL = 15750)
|
|
|
|
max_mob_size = 20
|
|
resources = list(/obj/item/stack/rods = 12, /obj/item/stack/material/steel = 4)
|
|
|
|
/obj/item/trap/animal/large/get_securing_text(var/secure = TRUE)
|
|
if(secure)
|
|
return SPAN_NOTICE("You can secure the trap by using a wrench on it. This will anchor it to the floor, and ready it for deployment.")
|
|
return SPAN_NOTICE("You can unsecure the trap by using a wrench on it. This will unanchor it from the floor, allowing it to be moved.")
|
|
|
|
/obj/item/trap/animal/large/attack_hand(mob/user)
|
|
if(user == buckled)
|
|
return
|
|
else if(!anchored)
|
|
to_chat(user, SPAN_WARNING("You need to anchor \the [src] first!"))
|
|
else if(captured)
|
|
to_chat(user, SPAN_WARNING("You can't deploy \the [src] with something caught!"))
|
|
else
|
|
..()
|
|
|
|
/obj/item/trap/animal/large/attackby(obj/item/attacking_item, mob/user)
|
|
if(attacking_item.tool_behaviour == TOOL_WRENCH)
|
|
var/turf/T = get_turf(src)
|
|
if(!T)
|
|
to_chat(user, SPAN_WARNING("There is nothing to secure \the [src] to!"))
|
|
return
|
|
|
|
if(anchored && deployed)
|
|
to_chat(user, SPAN_WARNING("You can't do that while \the [src] is deployed! Undeploy it first."))
|
|
return
|
|
|
|
user.visible_message(SPAN_NOTICE("[user] begins [anchored ? "un" : "" ]securing \the [src]!"),
|
|
SPAN_NOTICE("You begin [anchored ? "un" : "" ]securing \the [src]!"))
|
|
|
|
if(attacking_item.use_tool(src, user, 30, volume = 50))
|
|
anchored = !anchored
|
|
user.visible_message(SPAN_NOTICE("[user] [anchored ? "" : "un" ]secures \the [src]!"),
|
|
SPAN_NOTICE("You [anchored ? "" : "un" ]secure \the [src]!"))
|
|
|
|
else if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER)
|
|
// Unlike smaller traps, screwdriver shouldn't work on this.
|
|
return
|
|
else
|
|
..()
|
|
|
|
/obj/item/trap/animal/large/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
|
|
if(captured)
|
|
to_chat(user, SPAN_WARNING("The trap door's down, you can't get through there!"))
|
|
return
|
|
|
|
if(!src.Adjacent(user))
|
|
return
|
|
|
|
if(!ishuman(user))
|
|
..()
|
|
return
|
|
|
|
var/trigger_chance = 0
|
|
if(user.a_intent == I_HELP)
|
|
trigger_chance = 100
|
|
else if(user.a_intent != I_HURT)
|
|
trigger_chance = 50
|
|
|
|
pass_without_trace(user, trigger_chance)
|
|
|
|
/obj/item/trap/animal/large/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
|
|
if(deployed)
|
|
return TRUE
|
|
|
|
if(mover?.movement_type & PHASING)
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
/obj/item/large_trap_foundation
|
|
name = "large trap foundation"
|
|
desc = "A metal foundation for large trap, it is missing metals rods to hold the prey."
|
|
icon = 'icons/obj/item/traps/traps.dmi'
|
|
icon_state = "large_foundation"
|
|
throwforce = 4
|
|
force = 11
|
|
w_class = WEIGHT_CLASS_HUGE
|
|
|
|
/obj/item/large_trap_foundation/feedback_hints(mob/user, distance, is_adjacent)
|
|
. += ..()
|
|
. += SPAN_NOTICE("\The [src] can be turned into a large trap by attaching twelve metal rods to it.")
|
|
|
|
/obj/item/large_trap_foundation/attackby(obj/item/attacking_item, mob/user)
|
|
if(istype(attacking_item, /obj/item/stack/rods))
|
|
var/obj/item/stack/rods/O = attacking_item
|
|
if(O.get_amount() >= 12)
|
|
user.visible_message("[SPAN_BOLD("[user]")] starts adding metal bars to \the [src]...", SPAN_NOTICE("You start adding metal bars to \the [src]..."))
|
|
|
|
if (!do_after(user, 2 SECONDS, src))
|
|
return
|
|
O.use(12)
|
|
|
|
var/obj/item/trap/animal/large/new_trap = new /obj/item/trap/animal/large(src.loc)
|
|
user.visible_message("[SPAN_BOLD("[user]")] finishes constructing \the [new_trap].", SPAN_NOTICE("You finish constructing \the [new_trap]."))
|
|
|
|
qdel(src)
|
|
return
|
|
else
|
|
to_chat(user, SPAN_WARNING("You need at least twelve rods to complete \the [src]."))
|
|
return
|
|
else if(attacking_item.tool_behaviour == TOOL_SCREWDRIVER)
|
|
return
|
|
else
|
|
..()
|