mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 05:30:05 +01:00
[MIRROR] Meat Hook Rework (Accidental Features) [MDB IGNORE] (#25343)
* Meat Hook Rework (Accidental Features) * Update meat_hook.dm * Update meat_hook.dm --------- Co-authored-by: san7890 <the@san7890.com> Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
This commit is contained in:
co-authored by
san7890
Bloop
parent
6c11df99a4
commit
1f9fc79c7d
@@ -23,7 +23,6 @@
|
||||
RegisterSignal(target, COMSIG_ATOM_ATTACK_HAND, PROC_REF(attack_hand))
|
||||
RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
|
||||
RegisterSignal(target, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(mousedrop_receive))
|
||||
RegisterSignal(target, COMSIG_ATOM_BUMPED, PROC_REF(try_speedrun))
|
||||
ADD_TRAIT(target, TRAIT_CLIMBABLE, ELEMENT_TRAIT(type))
|
||||
|
||||
/datum/element/climbable/Detach(datum/target)
|
||||
@@ -104,8 +103,6 @@
|
||||
var/x_dist = (text2num(LAZYACCESS(modifiers, ICON_X)) - world.icon_size/2) * (climbed_thing.dir & WEST ? -1 : 1)
|
||||
var/y_dist = (text2num(LAZYACCESS(modifiers, ICON_Y)) - world.icon_size/2) * (climbed_thing.dir & SOUTH ? -1 : 1)
|
||||
dir_step = (x_dist >= y_dist ? (EAST|WEST) : (NORTH|SOUTH)) & climbed_thing.dir
|
||||
else //user is being moved by a forced_movement datum. dir_step will be the direction to the forced movement target.
|
||||
dir_step = get_dir(user, user.force_moving.target)
|
||||
else
|
||||
dir_step = get_dir(user, get_step(climbed_thing, climbed_thing.dir))
|
||||
. = step(user, dir_step)
|
||||
@@ -121,11 +118,3 @@
|
||||
var/mob/living/living_target = dropped_atom
|
||||
if(living_target.mobility_flags & MOBILITY_MOVE)
|
||||
INVOKE_ASYNC(src, PROC_REF(climb_structure), climbed_thing, living_target, params)
|
||||
|
||||
///Tries to climb onto the target if the forced movement of the mob allows it
|
||||
/datum/element/climbable/proc/try_speedrun(datum/source, mob/bumpee)
|
||||
SIGNAL_HANDLER
|
||||
if(!istype(bumpee))
|
||||
return
|
||||
if(bumpee.force_moving?.allow_climbing)
|
||||
do_climb(source, bumpee)
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
//Just new and forget
|
||||
//Depricated, use movement loops instead. Exists to support things that want to move more then 10 times a second
|
||||
/datum/forced_movement
|
||||
var/atom/movable/victim
|
||||
var/atom/target
|
||||
var/last_processed
|
||||
var/steps_per_tick
|
||||
var/allow_climbing
|
||||
var/datum/callback/on_step
|
||||
var/moved_at_all = FALSE
|
||||
//as fast as ssfastprocess
|
||||
/datum/forced_movement/New(atom/movable/_victim, atom/_target, _steps_per_tick = 0.5, _allow_climbing = FALSE, datum/callback/_on_step = null)
|
||||
victim = _victim
|
||||
target = _target
|
||||
steps_per_tick = _steps_per_tick
|
||||
allow_climbing = _allow_climbing
|
||||
on_step = _on_step
|
||||
|
||||
. = ..()
|
||||
|
||||
if(_victim && _target && _steps_per_tick && !_victim.force_moving)
|
||||
last_processed = world.time
|
||||
_victim.force_moving = src
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
else
|
||||
qdel(src) //if you want to overwrite the current forced movement, call qdel(victim.force_moving) before creating this
|
||||
|
||||
/datum/forced_movement/Destroy()
|
||||
if(victim.force_moving == src)
|
||||
victim.force_moving = null
|
||||
if(moved_at_all)
|
||||
victim.forceMove(victim.loc) //get the side effects of moving here that require us to currently not be force_moving aka reslipping on ice
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
victim = null
|
||||
target = null
|
||||
on_step = null
|
||||
return ..()
|
||||
|
||||
//Todo: convert
|
||||
/datum/forced_movement/process()
|
||||
if(QDELETED(victim) || !victim.loc || QDELETED(target) || !target.loc)
|
||||
qdel(src)
|
||||
return
|
||||
var/steps_to_take = round(steps_per_tick * (world.time - last_processed))
|
||||
if(steps_to_take)
|
||||
for(var/i in 1 to steps_to_take)
|
||||
if(TryMove())
|
||||
moved_at_all = TRUE
|
||||
if(on_step)
|
||||
on_step.InvokeAsync()
|
||||
else
|
||||
qdel(src)
|
||||
return
|
||||
last_processed = world.time
|
||||
|
||||
/datum/forced_movement/proc/TryMove(recursive = FALSE)
|
||||
if(QDELETED(src)) //Our previous step caused deletion of this datum
|
||||
return
|
||||
|
||||
var/atom/movable/vic = victim //sanic
|
||||
var/atom/tar = target
|
||||
|
||||
if(!recursive)
|
||||
. = step_towards(vic, tar)
|
||||
|
||||
//shit way for getting around corners
|
||||
if(!.) //If stepping towards the target failed
|
||||
if(tar.x > vic.x) //If we're going x, step x
|
||||
if(step(vic, EAST))
|
||||
. = TRUE
|
||||
else if(tar.x < vic.x)
|
||||
if(step(vic, WEST))
|
||||
. = TRUE
|
||||
|
||||
if(!.) //If the x step failed, go y
|
||||
if(tar.y > vic.y)
|
||||
if(step(vic, NORTH))
|
||||
. = TRUE
|
||||
else if(tar.y < vic.y)
|
||||
if(step(vic, SOUTH))
|
||||
. = TRUE
|
||||
|
||||
if(!.) //If both failed, try again for some reason
|
||||
if(recursive)
|
||||
return FALSE
|
||||
else
|
||||
. = TryMove(TRUE)
|
||||
|
||||
. = . && (vic.loc != tar.loc)
|
||||
@@ -45,7 +45,6 @@
|
||||
var/atom/movable/moving_from_pull
|
||||
///Holds information about any movement loops currently running/waiting to run on the movable. Lazy, will be null if nothing's going on
|
||||
var/datum/movement_packet/move_packet
|
||||
var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
|
||||
/**
|
||||
* an associative lazylist of relevant nested contents by "channel", the list is of the form: list(channel = list(important nested contents of that type))
|
||||
* each channel has a specific purpose and is meant to replace potentially expensive nested contents iteration.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//Meat Hook
|
||||
#define TRAIT_HOOKED "hooked"
|
||||
#define IMMOBILIZATION_TIMER (0.25 SECONDS) //! How long we immobilize the firer after firing - we do cancel the immobilization early if nothing is hit.
|
||||
|
||||
/// Meat Hook
|
||||
/obj/item/gun/magic/hook
|
||||
name = "meat hook"
|
||||
desc = "Mid or feed."
|
||||
@@ -22,8 +24,22 @@
|
||||
/obj/item/gun/magic/hook/can_trigger_gun(mob/living/user, akimbo_usage) // This isn't really a gun, so it shouldn't be checking for TRAIT_NOGUNS, a firing pin (pinless), or a trigger guard (guardless)
|
||||
if(akimbo_usage)
|
||||
return FALSE //this would be kinda weird while shooting someone down.
|
||||
if(HAS_TRAIT(user, TRAIT_IMMOBILIZED))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/gun/magic/hook/suicide_act(mob/living/user)
|
||||
var/obj/item/bodypart/head/removable = user.get_bodypart(BODY_ZONE_HEAD)
|
||||
if(isnull(removable))
|
||||
user.visible_message(span_suicide("[user] stuffs the chain of the [src] down the hole where their head should be! It looks like [user.p_theyre()] trying to commit suicide!"))
|
||||
return OXYLOSS
|
||||
|
||||
playsound(get_turf(src), fire_sound, 50, TRUE, -1)
|
||||
user.visible_message(span_suicide("[user] is using the [src] on their [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!"))
|
||||
playsound(get_turf(src), 'sound/weapons/bladeslice.ogg', 70)
|
||||
removable.dismember(silent = FALSE)
|
||||
return BRUTELOSS
|
||||
|
||||
/obj/item/ammo_casing/magic/hook
|
||||
name = "hook"
|
||||
desc = "A hook."
|
||||
@@ -41,37 +57,154 @@
|
||||
armour_penetration = 60
|
||||
damage_type = BRUTE
|
||||
hitsound = 'sound/effects/splat.ogg'
|
||||
var/chain
|
||||
var/knockdown_time = (0.5 SECONDS)
|
||||
var/chain_icon = 'icons/effects/beam.dmi' //SKYRAT ADDITION
|
||||
/// The chain we send out while we are in motion, referred to as "initial" to not get confused with the chain we use to reel the victim in.
|
||||
var/datum/beam/initial_chain
|
||||
var/chain_icon = 'icons/effects/beam.dmi' // SKYRAT EDIT ADDITION
|
||||
|
||||
/obj/projectile/hook/fire(setAngle)
|
||||
if(firer)
|
||||
chain = firer.Beam(src, icon_state = "chain", icon = chain_icon, emissive = FALSE) //SKYRAT EDIT, original: chain = firer.Beam(src, icon_state = "chain", emissive = FALSE)
|
||||
..()
|
||||
//TODO: root the firer until the chain returns
|
||||
initial_chain = firer.Beam(src, icon_state = "chain", icon = chain_icon, emissive = FALSE) // SKYRAT EDIT CHANGE - Adds icon arg - ORIGINAL: chain = firer.Beam(src, icon_state = "chain", emissive = FALSE)
|
||||
ADD_TRAIT(firer, TRAIT_IMMOBILIZED, REF(src))
|
||||
addtimer(TRAIT_CALLBACK_REMOVE(firer, TRAIT_IMMOBILIZED, REF(src)), IMMOBILIZATION_TIMER) // safety if we miss, if we get a hit we stay immobilized
|
||||
return ..()
|
||||
|
||||
/obj/projectile/hook/on_hit(atom/target, blocked = 0, pierce_hit)
|
||||
. = ..()
|
||||
if(ismovable(target))
|
||||
var/atom/movable/A = target
|
||||
if(A.anchored)
|
||||
return
|
||||
A.visible_message(span_danger("[A] is snagged by [firer]'s hook!"))
|
||||
//Should really be a movement loop, but I don't want to support moving 5 tiles a tick
|
||||
//It just looks bad
|
||||
new /datum/forced_movement(A, get_turf(firer), 5, TRUE)
|
||||
if (isliving(target))
|
||||
var/mob/living/fresh_meat = target
|
||||
fresh_meat.Knockdown(knockdown_time)
|
||||
return
|
||||
//TODO: keep the chain beamed to A
|
||||
//TODO: needs a callback to delete the chain
|
||||
if(!ismovable(target))
|
||||
return
|
||||
|
||||
/obj/projectile/hook/Destroy()
|
||||
qdel(chain)
|
||||
var/atom/movable/victim = target
|
||||
if(victim.anchored || HAS_TRAIT_FROM(victim, TRAIT_HOOKED, REF(firer)))
|
||||
return
|
||||
|
||||
victim.visible_message(span_danger("[victim] is snagged by [firer]'s hook!"))
|
||||
|
||||
var/datum/hook_and_move/puller = new
|
||||
puller.begin_pulling(firer, victim, get_turf(firer))
|
||||
REMOVE_TRAIT(firer, TRAIT_IMMOBILIZED, REF(src))
|
||||
|
||||
/obj/projectile/hook/Destroy(force)
|
||||
QDEL_NULL(initial_chain)
|
||||
return ..()
|
||||
|
||||
/// Lightweight datum that just handles moving a target for the hook.
|
||||
/// For the love of God, do not use this outside this file.
|
||||
/datum/hook_and_move
|
||||
/// Weakref to the victim we are dragging
|
||||
var/datum/weakref/victim_ref = null
|
||||
/// Weakref of the destination that the victim is heading towards.
|
||||
var/datum/weakref/destination_ref = null
|
||||
/// Weakref to the firer of the hook
|
||||
var/datum/weakref/firer_ref = null
|
||||
/// String to the REF() of the dude that fired us so we can ensure we always cleanup our traits
|
||||
var/firer_ref_string = null
|
||||
|
||||
/// The last time our movement fired.
|
||||
var/last_movement = 0
|
||||
/// The chain beam we currently own.
|
||||
var/datum/beam/return_chain = null
|
||||
|
||||
/// How many steps we force the victim to take per tick
|
||||
var/steps_per_tick = 5
|
||||
/// How long we knockdown the victim for.
|
||||
var/knockdown_time = (0.5 SECONDS)
|
||||
|
||||
/// List of traits that prevent the user from moving. More restrictive than attempting to fire the hook by design.
|
||||
var/static/list/prevent_movement_traits = list(
|
||||
TRAIT_IMMOBILIZED,
|
||||
TRAIT_UI_BLOCKED,
|
||||
)
|
||||
|
||||
/datum/hook_and_move/Destroy(force)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
QDEL_NULL(return_chain)
|
||||
return ..()
|
||||
|
||||
/// Uses fastprocessing to move our victim to the destination at a rather fast speed.
|
||||
/datum/hook_and_move/proc/begin_pulling(atom/movable/firer, atom/movable/victim, atom/destination)
|
||||
return_chain = firer.Beam(victim, icon_state = "chain", emissive = FALSE)
|
||||
|
||||
firer_ref_string = REF(firer)
|
||||
ADD_TRAIT(victim, TRAIT_HOOKED, firer_ref_string)
|
||||
firer.add_traits(prevent_movement_traits, REF(src))
|
||||
if(isliving(victim))
|
||||
var/mob/living/fresh_meat = victim
|
||||
fresh_meat.Knockdown(knockdown_time)
|
||||
|
||||
destination_ref = WEAKREF(destination)
|
||||
victim_ref = WEAKREF(victim)
|
||||
firer_ref = WEAKREF(firer)
|
||||
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/// Cancels processing and removes the trait from the victim.
|
||||
/datum/hook_and_move/proc/end_movement()
|
||||
var/atom/movable/firer = firer_ref?.resolve()
|
||||
if(!QDELETED(firer))
|
||||
firer.remove_traits(prevent_movement_traits, REF(src))
|
||||
|
||||
var/atom/movable/victim = victim_ref?.resolve()
|
||||
if(!QDELETED(victim))
|
||||
REMOVE_TRAIT(victim, TRAIT_HOOKED, firer_ref_string)
|
||||
|
||||
qdel(src)
|
||||
|
||||
/datum/hook_and_move/process(seconds_per_tick)
|
||||
var/atom/movable/victim = victim_ref?.resolve()
|
||||
var/atom/destination = destination_ref?.resolve()
|
||||
if(QDELETED(victim) || QDELETED(destination))
|
||||
end_movement()
|
||||
return
|
||||
|
||||
var/steps_to_take = round(steps_per_tick * (world.time - last_movement))
|
||||
if(steps_to_take <= 0)
|
||||
return
|
||||
|
||||
var/movement_result = attempt_movement(victim, destination)
|
||||
if(!movement_result || (victim.loc == destination.loc)) // either we failed our movement or our mission is complete
|
||||
end_movement()
|
||||
|
||||
/// Attempts to move the victim towards the destination. Returns TRUE if we do a successful movement, FALSE otherwise.
|
||||
/// second_attempt is a boolean to prevent infinite recursion.
|
||||
/// If this whole series of events wasn't reliant on SSfastprocess firing as fast as it does, it would have been more useful to make this a move loop datum. But, we need the speed.
|
||||
/datum/hook_and_move/proc/attempt_movement(atom/movable/subject, atom/target, second_attempt = FALSE)
|
||||
var/actually_moved = FALSE
|
||||
if(!second_attempt)
|
||||
actually_moved = step_towards(subject, target)
|
||||
|
||||
if(actually_moved)
|
||||
return TRUE
|
||||
|
||||
// alright now the code fucking sucks
|
||||
var/subject_x = subject.x
|
||||
var/subject_y = subject.y
|
||||
var/target_x = target.x
|
||||
var/target_y = target.y
|
||||
|
||||
//If we're going x, step x
|
||||
if((target_x > subject_x) && step(subject, EAST))
|
||||
actually_moved = TRUE
|
||||
else if((target_x < subject_x) && step(subject, WEST))
|
||||
actually_moved = TRUE
|
||||
|
||||
if(actually_moved)
|
||||
return TRUE
|
||||
|
||||
//If the x step failed, go y
|
||||
if((target_y > subject_y) && step(subject, NORTH))
|
||||
actually_moved = TRUE
|
||||
else if((target_y < subject_y) && step(subject, SOUTH))
|
||||
actually_moved = TRUE
|
||||
|
||||
if(actually_moved)
|
||||
return TRUE
|
||||
|
||||
// if we fail twice, abort. otherwise queue up the second attempt.
|
||||
if(second_attempt)
|
||||
return FALSE
|
||||
|
||||
return attempt_movement(subject, target, second_attempt = TRUE)
|
||||
|
||||
//just a nerfed version of the real thing for the bounty hunters.
|
||||
/obj/item/gun/magic/hook/bounty
|
||||
name = "hook"
|
||||
@@ -83,3 +216,12 @@
|
||||
/obj/projectile/hook/bounty
|
||||
damage = 0
|
||||
stamina = 40
|
||||
|
||||
/// Debug hook for fun (AKA admin abuse). doesn't do any more damage or anything just lets you wildfire it.
|
||||
/obj/item/gun/magic/hook/debug
|
||||
name = "super meat hook"
|
||||
max_charges = 100
|
||||
recharge_rate = 1
|
||||
|
||||
#undef TRAIT_HOOKED
|
||||
#undef IMMOBILIZATION_TIMER
|
||||
|
||||
@@ -834,7 +834,6 @@
|
||||
#include "code\datums\ductnet.dm"
|
||||
#include "code\datums\emotes.dm"
|
||||
#include "code\datums\ert.dm"
|
||||
#include "code\datums\forced_movement.dm"
|
||||
#include "code\datums\hailer_phrase.dm"
|
||||
#include "code\datums\holocall.dm"
|
||||
#include "code\datums\hotkeys_help.dm"
|
||||
|
||||
Reference in New Issue
Block a user