mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-19 05:26:28 +00:00
## About The Pull Request I felt bad about taking all the "easy" ones, so let's change it up with a decently difficult one. Revenants are now basic mobs! This alone doesn't change much of anything because it pretty much overrode much of simple_animal code. However we do a few new things. * Multiple code improvements! No more weird proc names that aren't `snake_case`, cleaner variable names, more dmdoccing, etc. * Files are now reorganized, Rather than a 800-line dogshit monofile, we're all nice now * Multiple logic improvements, like early returns to make stuff more readable and the like. many `isnull()` yes * Instead of abusing key ownership logic, we use `mind.transfer_to()` instead for much cleaner behavior when we need to change stuff * Instead of iterating over the entire list of mobs to find dead mobs, we use a new `revenant_relay()` system that automatically handles broadcasting revenant messages to all observers and fellow revenants (like `blob_talk()`) * Instead of having a weird invisible "corpse", we move the revenant inside the actual ectoplasm when they die. This drastically simplifies a lot of wacky code that was completely useless since we still had the mob to work with, but now it's a lot cleaner when it comes down to it. * Some more hooks into stuff like `Life()` that might be useful for other people in the same way this is. * Less <span> crap, more macros, wahoo * Uses an `examine_block()` for when we relay instructions to new revenants. also cache those instructions to save time in the long run * Revenants being stunned, inhibited, and revealed are now managed by status effects rather than being chicanery on `Life()` * Everything should now just in general be a lot nicer to work with. This list is not exhaustive but a lot of the junk 7-year-old code has been gutted and replaced with the modern standards. There are stuff that I explicitly didn't touch and will not be touching in this PR as it's already rather large. * Revenant actions. I touched some proc names but I didn't alter the key ways they work. * Revenant movement. That's another can of worms. * Revenant event code. I just made it use the transfer to system to make a closed loop and that's about it. * Revenant AI. They don't have any, it's an event/admin spawn. I'm not adding AI here. ## Why It's Good For The Game Revenants are more extensible now rather than being a real big clusterfuck of bullshit, should be much easier to tweak stuff here and there or logically follow how the code for this fella should go. The number of weird revenant bugs should go down since we do stuff more simply- but there could be some sleeper bugs that can bite us. ## Changelog 🆑 refactor: Revenants, the mob that's split between planes of Life and Death, have been refactored into a basic mob. While this alone shouldn't touch behavior, a lot of the backend code has been gutted and refactored to try and furnish a better antagonist experience. This might mean that some weird stuff can come up and around, report something if it's utterly broken. code: In order to better facilitate some code, you do not ghost outside of a revenant on death, you simply get transferred into the ectoplasm. You should still be able to speak with your ghost friends on how hard you got wrecked or if you'll be able to resurrect though. code: The timing on revenant stuff such as being revealed, stunned, and inhibited (by holy weapons) should be tweaked a bit to allow better management. This should mean that getting unstunned and such should be a bit more precise now. qol: Revenant instructions are now relayed in a neat little examine block. /🆑
570 lines
18 KiB
Plaintext
570 lines
18 KiB
Plaintext
/**
|
|
* If your mob is concious, drop the item in the active hand
|
|
*
|
|
* This is a hidden verb, likely for binding with winset for hotkeys
|
|
*/
|
|
/client/verb/drop_item()
|
|
set hidden = TRUE
|
|
if(!iscyborg(mob) && mob.stat == CONSCIOUS)
|
|
mob.dropItemToGround(mob.get_active_held_item())
|
|
return
|
|
|
|
/**
|
|
* force move the control_object of your client mob
|
|
*
|
|
* Used in admin possession and called from the client Move proc
|
|
* ensures the possessed object moves and not the admin mob
|
|
*
|
|
* Has no sanity other than checking density
|
|
*/
|
|
/client/proc/Move_object(direct)
|
|
if(mob?.control_object)
|
|
if(mob.control_object.density)
|
|
step(mob.control_object,direct)
|
|
if(!mob.control_object)
|
|
return
|
|
mob.control_object.setDir(direct)
|
|
else
|
|
mob.control_object.forceMove(get_step(mob.control_object,direct))
|
|
|
|
/**
|
|
* Move a client in a direction
|
|
*
|
|
* Huge proc, has a lot of functionality
|
|
*
|
|
* Mostly it will despatch to the mob that you are the owner of to actually move
|
|
* in the physical realm
|
|
*
|
|
* Things that stop you moving as a mob:
|
|
* * world time being less than your next move_delay
|
|
* * not being in a mob, or that mob not having a loc
|
|
* * missing the n and direction parameters
|
|
* * being in remote control of an object (calls Moveobject instead)
|
|
* * being dead (it ghosts you instead)
|
|
*
|
|
* Things that stop you moving as a mob living (why even have OO if you're just shoving it all
|
|
* in the parent proc with istype checks right?):
|
|
* * having incorporeal_move set (calls Process_Incorpmove() instead)
|
|
* * being grabbed
|
|
* * being buckled (relaymove() is called to the buckled atom instead)
|
|
* * having your loc be some other mob (relaymove() is called on that mob instead)
|
|
* * Not having MOBILITY_MOVE
|
|
* * Failing Process_Spacemove() call
|
|
*
|
|
* At this point, if the mob is is confused, then a random direction and target turf will be calculated for you to travel to instead
|
|
*
|
|
* Now the parent call is made (to the byond builtin move), which moves you
|
|
*
|
|
* Some final move delay calculations (doubling if you moved diagonally successfully)
|
|
*
|
|
* if mob throwing is set I believe it's unset at this point via a call to finalize
|
|
*
|
|
* Finally if you're pulling an object and it's dense, you are turned 180 after the move
|
|
* (if you ask me, this should be at the top of the move so you don't dance around)
|
|
*
|
|
*/
|
|
/client/Move(new_loc, direct)
|
|
if(world.time < move_delay) //do not move anything ahead of this check please
|
|
return FALSE
|
|
next_move_dir_add = 0
|
|
next_move_dir_sub = 0
|
|
var/old_move_delay = move_delay
|
|
move_delay = world.time + world.tick_lag //this is here because Move() can now be called mutiple times per tick
|
|
if(!direct || !new_loc)
|
|
return FALSE
|
|
if(!mob?.loc)
|
|
return FALSE
|
|
if(HAS_TRAIT(mob, TRAIT_NO_TRANSFORM))
|
|
return FALSE //This is sorta the goto stop mobs from moving trait
|
|
if(mob.control_object)
|
|
return Move_object(direct)
|
|
if(!isliving(mob))
|
|
return mob.Move(new_loc, direct)
|
|
if(mob.stat == DEAD)
|
|
mob.ghostize()
|
|
return FALSE
|
|
if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, new_loc, direct) & COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE)
|
|
return FALSE
|
|
|
|
var/mob/living/L = mob //Already checked for isliving earlier
|
|
if(L.incorporeal_move && !is_secret_level(mob.z)) //Move though walls
|
|
Process_Incorpmove(direct)
|
|
return FALSE
|
|
|
|
if(mob.remote_control) //we're controlling something, our movement is relayed to it
|
|
return mob.remote_control.relaymove(mob, direct)
|
|
|
|
if(isAI(mob))
|
|
return AIMove(new_loc,direct,mob)
|
|
|
|
if(Process_Grab()) //are we restrained by someone's grip?
|
|
return
|
|
|
|
if(mob.buckled) //if we're buckled to something, tell it we moved.
|
|
return mob.buckled.relaymove(mob, direct)
|
|
|
|
if(!(L.mobility_flags & MOBILITY_MOVE))
|
|
return FALSE
|
|
|
|
if(ismovable(mob.loc)) //Inside an object, tell it we moved
|
|
var/atom/loc_atom = mob.loc
|
|
return loc_atom.relaymove(mob, direct)
|
|
|
|
if(!mob.Process_Spacemove(direct))
|
|
return FALSE
|
|
|
|
if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_MOVE, args) & COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE)
|
|
return FALSE
|
|
|
|
//We are now going to move
|
|
var/add_delay = mob.cached_multiplicative_slowdown
|
|
var/new_glide_size = DELAY_TO_GLIDE_SIZE(add_delay * ( (NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? sqrt(2) : 1 ) )
|
|
mob.set_glide_size(new_glide_size) // set it now in case of pulled objects
|
|
//If the move was recent, count using old_move_delay
|
|
//We want fractional behavior and all
|
|
if(old_move_delay + world.tick_lag > world.time)
|
|
//Yes this makes smooth movement stutter if add_delay is too fractional
|
|
//Yes this is better then the alternative
|
|
move_delay = old_move_delay
|
|
else
|
|
move_delay = world.time
|
|
|
|
//Basically an optional override for our glide size
|
|
//Sometimes you want to look like you're moving with a delay you don't actually have yet
|
|
visual_delay = 0
|
|
var/old_dir = mob.dir
|
|
|
|
. = ..()
|
|
|
|
if((direct & (direct - 1)) && mob.loc == new_loc) //moved diagonally successfully
|
|
add_delay *= sqrt(2)
|
|
|
|
var/after_glide = 0
|
|
if(visual_delay)
|
|
after_glide = visual_delay
|
|
else
|
|
after_glide = DELAY_TO_GLIDE_SIZE(add_delay)
|
|
|
|
mob.set_glide_size(after_glide)
|
|
|
|
move_delay += add_delay
|
|
if(.) // If mob is null here, we deserve the runtime
|
|
if(mob.throwing)
|
|
mob.throwing.finalize(FALSE)
|
|
|
|
// At this point we've moved the client's attached mob. This is one of the only ways to guess that a move was done
|
|
// as a result of player input and not because they were pulled or any other magic.
|
|
SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVED, direct, old_dir)
|
|
|
|
var/atom/movable/P = mob.pulling
|
|
if(P && !ismob(P) && P.density)
|
|
mob.setDir(REVERSE_DIR(mob.dir))
|
|
|
|
/**
|
|
* Checks to see if you're being grabbed and if so attempts to break it
|
|
*
|
|
* Called by client/Move()
|
|
*/
|
|
/client/proc/Process_Grab()
|
|
if(!mob.pulledby)
|
|
return FALSE
|
|
if(mob.pulledby == mob.pulling && mob.pulledby.grab_state == GRAB_PASSIVE) //Don't autoresist passive grabs if we're grabbing them too.
|
|
return FALSE
|
|
if(HAS_TRAIT(mob, TRAIT_INCAPACITATED))
|
|
COOLDOWN_START(src, move_delay, 1 SECONDS)
|
|
return TRUE
|
|
else if(HAS_TRAIT(mob, TRAIT_RESTRAINED))
|
|
COOLDOWN_START(src, move_delay, 1 SECONDS)
|
|
to_chat(src, span_warning("You're restrained! You can't move!"))
|
|
return TRUE
|
|
return mob.resist_grab(TRUE)
|
|
|
|
|
|
/**
|
|
* Allows mobs to ignore density and phase through objects
|
|
*
|
|
* Called by client/Move()
|
|
*
|
|
* The behaviour depends on the incorporeal_move value of the mob
|
|
*
|
|
* * INCORPOREAL_MOVE_BASIC - forceMoved to the next tile with no stop
|
|
* * INCORPOREAL_MOVE_SHADOW - the same but leaves a cool effect path
|
|
* * INCORPOREAL_MOVE_JAUNT - the same but blocked by holy tiles
|
|
*
|
|
* You'll note this is another mob living level proc living at the client level
|
|
*/
|
|
/client/proc/Process_Incorpmove(direct)
|
|
var/turf/mobloc = get_turf(mob)
|
|
if(!isliving(mob))
|
|
return
|
|
var/mob/living/L = mob
|
|
switch(L.incorporeal_move)
|
|
if(INCORPOREAL_MOVE_BASIC)
|
|
var/T = get_step(L,direct)
|
|
if(T)
|
|
L.forceMove(T)
|
|
L.setDir(direct)
|
|
if(INCORPOREAL_MOVE_SHADOW)
|
|
if(prob(50))
|
|
var/locx
|
|
var/locy
|
|
switch(direct)
|
|
if(NORTH)
|
|
locx = mobloc.x
|
|
locy = (mobloc.y+2)
|
|
if(locy>world.maxy)
|
|
return
|
|
if(SOUTH)
|
|
locx = mobloc.x
|
|
locy = (mobloc.y-2)
|
|
if(locy<1)
|
|
return
|
|
if(EAST)
|
|
locy = mobloc.y
|
|
locx = (mobloc.x+2)
|
|
if(locx>world.maxx)
|
|
return
|
|
if(WEST)
|
|
locy = mobloc.y
|
|
locx = (mobloc.x-2)
|
|
if(locx<1)
|
|
return
|
|
else
|
|
return
|
|
var/target = locate(locx,locy,mobloc.z)
|
|
if(target)
|
|
L.forceMove(target)
|
|
var/limit = 2//For only two trailing shadows.
|
|
for(var/turf/T in get_line(mobloc, L.loc))
|
|
new /obj/effect/temp_visual/dir_setting/ninja/shadow(T, L.dir)
|
|
limit--
|
|
if(limit <= 0)
|
|
break
|
|
else
|
|
new /obj/effect/temp_visual/dir_setting/ninja/shadow(mobloc, L.dir)
|
|
var/T = get_step(L,direct)
|
|
if(T)
|
|
L.forceMove(T)
|
|
L.setDir(direct)
|
|
if(INCORPOREAL_MOVE_JAUNT) //Incorporeal move, but blocked by holy-watered tiles and salt piles.
|
|
var/turf/open/floor/stepTurf = get_step(L, direct)
|
|
if(stepTurf)
|
|
var/obj/effect/decal/cleanable/food/salt/salt = locate() in stepTurf
|
|
if(salt)
|
|
to_chat(L, span_warning("[salt] bars your passage!"))
|
|
if(isrevenant(L))
|
|
var/mob/living/basic/revenant/ghostie = L
|
|
ghostie.apply_status_effect(/datum/status_effect/revenant/revealed, 2 SECONDS)
|
|
ghostie.apply_status_effect(/datum/status_effect/incapacitating/paralyzed/revenant, 2 SECONDS)
|
|
return
|
|
if(stepTurf.turf_flags & NOJAUNT)
|
|
to_chat(L, span_warning("Some strange aura is blocking the way."))
|
|
return
|
|
if(locate(/obj/effect/blessing) in stepTurf)
|
|
to_chat(L, span_warning("Holy energies block your path!"))
|
|
return
|
|
|
|
L.forceMove(stepTurf)
|
|
L.setDir(direct)
|
|
return TRUE
|
|
|
|
/**
|
|
* Handles mob/living movement in space (or no gravity)
|
|
*
|
|
* Called by /client/Move()
|
|
*
|
|
* return TRUE for movement or FALSE for none
|
|
*
|
|
* You can move in space if you have a spacewalk ability
|
|
*/
|
|
/mob/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
|
|
. = ..()
|
|
if(. || HAS_TRAIT(src, TRAIT_SPACEWALK))
|
|
return TRUE
|
|
|
|
// FUCK OFF
|
|
if(buckled)
|
|
return TRUE
|
|
|
|
var/atom/movable/backup = get_spacemove_backup(movement_dir, continuous_move)
|
|
if(!backup)
|
|
return FALSE
|
|
if(continuous_move || !istype(backup) || !movement_dir || backup.anchored)
|
|
return TRUE
|
|
// last pushoff exists for one reason
|
|
// to ensure pushing a mob doesn't just lead to it considering us as backup, and failing
|
|
last_pushoff = world.time
|
|
if(backup.newtonian_move(REVERSE_DIR(movement_dir), instant = TRUE)) //You're pushing off something movable, so it moves
|
|
// We set it down here so future calls to Process_Spacemove by the same pair in the same tick don't lead to fucky
|
|
backup.last_pushoff = world.time
|
|
to_chat(src, span_info("You push off of [backup] to propel yourself."))
|
|
return TRUE
|
|
|
|
/**
|
|
* Finds a target near a mob that is viable for pushing off when moving.
|
|
* Takes the intended movement direction as input, alongside if the context is checking if we're allowed to continue drifting
|
|
*/
|
|
/mob/get_spacemove_backup(moving_direction, continuous_move)
|
|
for(var/atom/pushover as anything in range(1, get_turf(src)))
|
|
if(pushover == src)
|
|
continue
|
|
if(isarea(pushover))
|
|
continue
|
|
if(isturf(pushover))
|
|
var/turf/turf = pushover
|
|
if(isspaceturf(turf))
|
|
continue
|
|
if(!turf.density && !mob_negates_gravity())
|
|
continue
|
|
return pushover
|
|
|
|
var/atom/movable/rebound = pushover
|
|
if(rebound == buckled)
|
|
continue
|
|
if(ismob(rebound))
|
|
var/mob/lover = rebound
|
|
if(lover.buckled)
|
|
continue
|
|
|
|
var/pass_allowed = rebound.CanPass(src, get_dir(rebound, src))
|
|
if(!rebound.density && pass_allowed)
|
|
continue
|
|
//Sometime this tick, this pushed off something. Doesn't count as a valid pushoff target
|
|
if(rebound.last_pushoff == world.time)
|
|
continue
|
|
if(continuous_move && !pass_allowed)
|
|
var/datum/move_loop/move/rebound_engine = SSmove_manager.processing_on(rebound, SSspacedrift)
|
|
// If you're moving toward it and you're both going the same direction, stop
|
|
if(moving_direction == get_dir(src, pushover) && rebound_engine && moving_direction == rebound_engine.direction)
|
|
continue
|
|
else if(!pass_allowed)
|
|
if(moving_direction == get_dir(src, pushover)) // Can't push "off" of something that you're walking into
|
|
continue
|
|
if(rebound.anchored)
|
|
return rebound
|
|
if(pulling == rebound)
|
|
continue
|
|
return rebound
|
|
|
|
/mob/has_gravity(turf/gravity_turf)
|
|
return mob_negates_gravity() || ..()
|
|
|
|
/**
|
|
* Does this mob ignore gravity
|
|
*/
|
|
/mob/proc/mob_negates_gravity()
|
|
return FALSE
|
|
|
|
/**
|
|
* Called when this mob slips over, override as needed
|
|
*
|
|
* knockdown_amount - time (in deciseconds) the slip leaves them on the ground
|
|
* slipped_on - optional, what'd we slip on? if not set, we assume they just fell over
|
|
* lube - bitflag of "lube flags", see [mobs.dm] for more information
|
|
* paralyze - time (in deciseconds) the slip leaves them paralyzed / unable to move
|
|
* force_drop = the slip forces them to drop held items
|
|
*/
|
|
/mob/proc/slip(knockdown_amount, obj/slipped_on, lube_flags, paralyze, force_drop = FALSE)
|
|
add_mob_memory(/datum/memory/was_slipped, antagonist = slipped_on)
|
|
|
|
//bodypart selection verbs - Cyberboss
|
|
//8: repeated presses toggles through head - eyes - mouth
|
|
//9: eyes 8: head 7: mouth
|
|
//4: r-arm 5: chest 6: l-arm
|
|
//1: r-leg 2: groin 3: l-leg
|
|
|
|
///Validate the client's mob has a valid zone selected
|
|
/client/proc/check_has_body_select()
|
|
return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /atom/movable/screen/zone_sel)
|
|
|
|
/**
|
|
* Hidden verb to set the target zone of a mob to the head
|
|
*
|
|
* (bound to 8) - repeated presses toggles through head - eyes - mouth
|
|
*/
|
|
|
|
///Hidden verb to target the head, bound to 8
|
|
/client/verb/body_toggle_head()
|
|
set name = "body-toggle-head"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/next_in_line
|
|
switch(mob.zone_selected)
|
|
if(BODY_ZONE_HEAD)
|
|
next_in_line = BODY_ZONE_PRECISE_EYES
|
|
if(BODY_ZONE_PRECISE_EYES)
|
|
next_in_line = BODY_ZONE_PRECISE_MOUTH
|
|
else
|
|
next_in_line = BODY_ZONE_HEAD
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(next_in_line, mob)
|
|
|
|
///Hidden verb to target the eyes, bound to 7
|
|
/client/verb/body_eyes()
|
|
set name = "body-eyes"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(BODY_ZONE_PRECISE_EYES, mob)
|
|
|
|
///Hidden verb to target the mouth, bound to 9
|
|
/client/verb/body_mouth()
|
|
set name = "body-mouth"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(BODY_ZONE_PRECISE_MOUTH, mob)
|
|
|
|
///Hidden verb to target the right arm, bound to 4
|
|
/client/verb/body_r_arm()
|
|
set name = "body-r-arm"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(BODY_ZONE_R_ARM, mob)
|
|
|
|
///Hidden verb to target the chest, bound to 5
|
|
/client/verb/body_chest()
|
|
set name = "body-chest"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(BODY_ZONE_CHEST, mob)
|
|
|
|
///Hidden verb to target the left arm, bound to 6
|
|
/client/verb/body_l_arm()
|
|
set name = "body-l-arm"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(BODY_ZONE_L_ARM, mob)
|
|
|
|
///Hidden verb to target the right leg, bound to 1
|
|
/client/verb/body_r_leg()
|
|
set name = "body-r-leg"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(BODY_ZONE_R_LEG, mob)
|
|
|
|
///Hidden verb to target the groin, bound to 2
|
|
/client/verb/body_groin()
|
|
set name = "body-groin"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(BODY_ZONE_PRECISE_GROIN, mob)
|
|
|
|
///Hidden verb to target the left leg, bound to 3
|
|
/client/verb/body_l_leg()
|
|
set name = "body-l-leg"
|
|
set hidden = TRUE
|
|
|
|
if(!check_has_body_select())
|
|
return
|
|
|
|
var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
|
|
selector.set_selected_zone(BODY_ZONE_L_LEG, mob)
|
|
|
|
///Verb to toggle the walk or run status
|
|
/client/verb/toggle_walk_run()
|
|
set name = "toggle-walk-run"
|
|
set hidden = TRUE
|
|
set instant = TRUE
|
|
if(isliving(mob))
|
|
var/mob/living/user_mob = mob
|
|
user_mob.toggle_move_intent(usr)
|
|
|
|
/**
|
|
* Toggle the move intent of the mob
|
|
*
|
|
* triggers an update the move intent hud as well
|
|
*/
|
|
/mob/living/proc/toggle_move_intent(mob/user)
|
|
if(move_intent == MOVE_INTENT_RUN)
|
|
move_intent = MOVE_INTENT_WALK
|
|
else
|
|
move_intent = MOVE_INTENT_RUN
|
|
if(hud_used?.static_inventory)
|
|
for(var/atom/movable/screen/mov_intent/selector in hud_used.static_inventory)
|
|
selector.update_appearance()
|
|
update_move_intent_slowdown()
|
|
|
|
///Moves a mob upwards in z level
|
|
/mob/verb/up()
|
|
set name = "Move Upwards"
|
|
set category = "IC"
|
|
|
|
var/turf/current_turf = get_turf(src)
|
|
var/turf/above_turf = GET_TURF_ABOVE(current_turf)
|
|
|
|
var/ventcrawling_flag = HAS_TRAIT(src, TRAIT_MOVE_VENTCRAWLING) ? ZMOVE_VENTCRAWLING : 0
|
|
if(!above_turf)
|
|
to_chat(src, span_warning("There's nowhere to go in that direction!"))
|
|
return
|
|
|
|
if(ismovable(loc)) //Inside an object, tell it we moved
|
|
var/atom/loc_atom = loc
|
|
return loc_atom.relaymove(src, UP)
|
|
|
|
if(can_z_move(DOWN, above_turf, current_turf, ZMOVE_FALL_FLAGS|ventcrawling_flag)) //Will we fall down if we go up?
|
|
if(buckled)
|
|
to_chat(src, span_warning("[buckled] is is not capable of flight."))
|
|
else
|
|
to_chat(src, span_warning("You are not Superman."))
|
|
return
|
|
|
|
if(zMove(UP, z_move_flags = ZMOVE_FLIGHT_FLAGS|ZMOVE_FEEDBACK|ventcrawling_flag))
|
|
to_chat(src, span_notice("You move upwards."))
|
|
|
|
///Moves a mob down a z level
|
|
/mob/verb/down()
|
|
set name = "Move Down"
|
|
set category = "IC"
|
|
|
|
var/turf/current_turf = get_turf(src)
|
|
var/turf/below_turf = GET_TURF_BELOW(current_turf)
|
|
if(!below_turf)
|
|
to_chat(src, span_warning("There's nowhere to go in that direction!"))
|
|
return
|
|
|
|
if(ismovable(loc)) //Inside an object, tell it we moved
|
|
var/atom/loc_atom = loc
|
|
return loc_atom.relaymove(src, DOWN)
|
|
|
|
var/ventcrawling_flag = HAS_TRAIT(src, TRAIT_MOVE_VENTCRAWLING) ? ZMOVE_VENTCRAWLING : 0
|
|
if(zMove(DOWN, z_move_flags = ZMOVE_FLIGHT_FLAGS|ZMOVE_FEEDBACK|ventcrawling_flag))
|
|
to_chat(src, span_notice("You move down."))
|
|
return FALSE
|
|
|
|
/mob/abstract_move(atom/destination)
|
|
var/turf/new_turf = get_turf(destination)
|
|
if(new_turf && (istype(new_turf, /turf/cordon/secret) || is_secret_level(new_turf.z)) && !client?.holder)
|
|
return
|
|
return ..()
|