mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-21 06:56:50 +01:00
- separates the composition of cyborgs into modules, iconsets, chassis, upgrades - modules / chassis atm can provide items, so can upgrades; in the future we will want to probably generalize the item API a bit so components can work - chassis now can (unimplemented) easily be made to give different stats - reorganizes all the cyborg sprites - adds generalized item mounts with redirection support and tears out legacy cyborg items - entirely rebalances modules & writes some new lawsets in for future use --------- Co-authored-by: silicons <silicons@silicons.dev>
606 lines
16 KiB
Plaintext
606 lines
16 KiB
Plaintext
/mob/proc/applyMoveCooldown(amount)
|
|
move_delay = max(move_delay, world.time + amount)
|
|
|
|
/mob/proc/check_move_cooldown()
|
|
return move_delay <= world.time
|
|
|
|
/client/proc/client_dir(input, direction=-1)
|
|
return turn(input, direction*dir2angle(dir))
|
|
|
|
/client/verb/drop_item()
|
|
set hidden = 1
|
|
|
|
if(isrobot(mob))
|
|
return
|
|
if(mob.stat != CONSCIOUS)
|
|
return
|
|
if(!isturf(mob.loc) && !isbelly(mob.loc))
|
|
return
|
|
mob.drop_active_held_item()
|
|
|
|
/client/proc/Move_object(direct)
|
|
if(mob && mob.control_object)
|
|
if(mob.control_object.density)
|
|
step(mob.control_object,direct)
|
|
if(!mob.control_object) return
|
|
mob.control_object.dir = direct
|
|
else
|
|
mob.control_object.forceMove(get_step(mob.control_object,direct))
|
|
|
|
/mob/CanAllowThrough(atom/movable/mover, turf/target)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
// thrown things still hit us even when nondense
|
|
if(can_cross_under(mover))
|
|
return TRUE
|
|
|
|
/mob/CanPassThrough(atom/blocker, turf/target, blocker_opinion)
|
|
if((buckled?.loc == target) && ismovable(blocker))
|
|
var/atom/movable/AM = blocker
|
|
if(AM.pass_flags & ATOM_PASS_BUCKLED)
|
|
return TRUE
|
|
return ..()
|
|
|
|
/**
|
|
* Can something cross under us without being blocked by us?
|
|
*/
|
|
/mob/proc/can_cross_under(atom/movable/mover)
|
|
return !mover.density && !mover.throwing && !istype(mover, /obj/projectile)
|
|
|
|
/**
|
|
* 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_CAN_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(n, direct)
|
|
if(!mob.check_move_cooldown()) //do not move anything ahead of this check please
|
|
return FALSE
|
|
else
|
|
next_move_dir_add = 0
|
|
next_move_dir_sub = 0
|
|
// no tile, or new dir, or mob is transforming
|
|
if(!n || !direct || mob.transforming) // i'm moving something ahead of that check
|
|
return FALSE
|
|
// commented out - not needed without momentum preservation
|
|
var/old_delay = mob.move_delay
|
|
// prevent more than one move per tick if we get interrupted from normal movement
|
|
mob.move_delay = world.time + world.tick_lag
|
|
// admin control (?)
|
|
if(mob.control_object)
|
|
return Move_object(direct)
|
|
//* movement intercept
|
|
if(mob.movement_intercept?.intercept_mob_move(mob, direct))
|
|
return
|
|
// nonliving get handled differently
|
|
if(!isliving(mob))
|
|
mob.move_delay = world.time + mob.movement_delay()
|
|
return mob.Move(n, direct)
|
|
// autoghost if needed
|
|
if((mob.stat == DEAD) && isliving(mob) && !mob.forbid_seeing_deadchat)
|
|
mob.ghostize()
|
|
return FALSE
|
|
// don't move if there's a forced movement datum on us
|
|
/*
|
|
if(mob.force_moving)
|
|
return FALSE
|
|
*/
|
|
// already istype'd earlier ; cast for checks
|
|
var/mob/living/L = mob
|
|
// check for incorpmove aka move through walls
|
|
if(L.incorporeal_move)
|
|
Process_Incorpmove(direct)
|
|
return FALSE
|
|
// todo: proper relaymove system
|
|
// remote controlling something; relay move to that
|
|
/*
|
|
if(mob.remote_control) //we're controlling something, our movement is relayed to it
|
|
return mob.remote_control.relaymove(mob, direct)
|
|
*/
|
|
// we have an eye; relay to that
|
|
if(mob.eyeobj)
|
|
return mob.EyeMove(n,direct)
|
|
// ai move specials
|
|
/*
|
|
if(isAI(mob))
|
|
return AIMove(n,direct,mob)
|
|
*/
|
|
//! WARNING: LEGACY CODE START
|
|
// unzoom
|
|
// todo: component/element/signal/datum/anything but this
|
|
if(isliving(mob))
|
|
if(mob.client)
|
|
if(mob.client.view != world.view) // If mob moves while zoomed in with device, unzoom them.
|
|
for(var/obj/item/item in mob.contents)
|
|
if(item.zoom)
|
|
item.zoom(user = mob)
|
|
break
|
|
|
|
// restrained by someone's grip
|
|
// todo: move intercept at mob level
|
|
if(Process_Grab()) //are we restrained by someone's grip?
|
|
return
|
|
//! END
|
|
|
|
// todo: proper relaymove system
|
|
// buckled ; relaymove to buckled
|
|
if(mob.buckled)
|
|
return mob.buckled.relaymove_from_buckled(mob, direct)
|
|
|
|
// todo: move to mob
|
|
// mobility check
|
|
if(!CHECK_MOBILITY(mob, MOBILITY_CAN_MOVE))
|
|
return
|
|
|
|
// new mobility flags check todo
|
|
/*
|
|
if(!(L.mobility_flags & MOBILITY_CAN_MOVE))
|
|
return FALSE
|
|
*/
|
|
|
|
// todo: proper relaymove handling
|
|
// machine might process relaymove
|
|
if(mob.machine)
|
|
var/result = mob.machine.relaymove(mob, direct)
|
|
if(result)
|
|
return result
|
|
|
|
// todo: proper relaymove handling
|
|
// inside something ; relay to them
|
|
if(!isturf(mob.loc))
|
|
var/atom/A = mob.loc
|
|
return A.relaymove_from_contents(mob, direct)
|
|
|
|
// todo: this should probably be on mob or something
|
|
// check for gravity
|
|
if(!mob.Process_Spacemove(direct))
|
|
return FALSE
|
|
|
|
//! WARNING: SHITCODE
|
|
// .... why
|
|
if(!mob.lastarea)
|
|
mob.lastarea = get_area(mob.loc)
|
|
//! End
|
|
|
|
//? All exceptions / modifiers handled, proceed to proper movement
|
|
|
|
//! SIKE, MORE
|
|
if(mob.restrained() && mob.pulledby)//Why being pulled while cuffed prevents you from moving
|
|
to_chat(src, "<span class='warning'>You're restrained! You can't move!</span>")
|
|
mob.move_delay = world.time + 5 // 5 ds delay
|
|
return FALSE
|
|
// if(length(mob.pinned))
|
|
// mob.move_delay = world.time + 5 // 5 ds delay
|
|
// to_chat(src, "<font color=#4F49AF>You're pinned to a wall by [mob.pinned[1]]!</font>")
|
|
// return FALSE
|
|
//! End
|
|
|
|
//? NOW we try to move.
|
|
|
|
// get additional delay from this move
|
|
var/add_delay = max(world.tick_lag, mob.movement_delay())
|
|
// for grabs (legacy code moment)
|
|
var/add_delay_grab = 0
|
|
|
|
//! WARNING: LEGACY CODE; I don't know how this works and I'm afraid to ask.
|
|
if(mob.pulledby || mob.buckled) // Wheelchair driving! //this is shitcode
|
|
if(istype(mob.loc, /turf/space))
|
|
return // No wheelchair driving in space
|
|
if(istype(mob.pulledby, /obj/structure/bed/chair/wheelchair))
|
|
return mob.pulledby.relaymove(mob, direct)
|
|
else if(istype(mob.buckled, /obj/structure/bed/chair/wheelchair))
|
|
if(ishuman(mob))
|
|
var/mob/living/carbon/human/driver = mob
|
|
var/obj/item/organ/external/l_hand = driver.get_organ("l_hand")
|
|
var/obj/item/organ/external/r_hand = driver.get_organ("r_hand")
|
|
if((!l_hand || l_hand.is_stump()) && (!r_hand || r_hand.is_stump()))
|
|
return // No hands to drive your chair? Tough luck!
|
|
//drunk wheelchair driving
|
|
else if(mob.confused)
|
|
switch(mob.m_intent)
|
|
if("run")
|
|
if(prob(25))
|
|
to_chat(src, SPAN_WARNING("You stumble around confusedly."))
|
|
direct = turn(direct, pick(90, -90))
|
|
if("walk")
|
|
if(prob(10))
|
|
to_chat(src, SPAN_WARNING("You stumble around confusedly."))
|
|
direct = turn(direct, pick(90, -90))
|
|
add_delay += 2
|
|
return mob.buckled.relaymove(mob,direct)
|
|
//! oh god I hate this so much todo proper relaymove system for pulling fr fr
|
|
|
|
//! WARNING: LEGACY CODE I give up at this point
|
|
//We are now going to move
|
|
//Something with pulling things
|
|
if(locate(/obj/item/grab, mob))
|
|
add_delay_grab = 7
|
|
var/list/grabbed = mob.get_grabbing_recursive() + src // im fucking screaming; this is because old code always considers self as grabbed.
|
|
if(grabbed)
|
|
if(grabbed.len == 2)
|
|
grabbed -= mob
|
|
var/mob/M = grabbed[1]
|
|
if(M)
|
|
if ((get_dist(mob, M) <= 1 || M.loc == mob.loc))
|
|
var/turf/T = mob.loc
|
|
. = mob.SelfMove(n, direct)
|
|
if (isturf(M.loc))
|
|
var/diag = get_dir(mob, M)
|
|
if ((diag - 1) & diag)
|
|
else
|
|
diag = null
|
|
if ((get_dist(mob, M) > 1 || diag))
|
|
step(M, get_dir(M.loc, T))
|
|
else
|
|
for(var/mob/M in grabbed)
|
|
M.other_mobs = 1
|
|
if(mob != M)
|
|
M.animate_movement = 3
|
|
for(var/mob/M in grabbed)
|
|
spawn( 0 )
|
|
step(M, direct)
|
|
return
|
|
spawn( 1 )
|
|
M.other_mobs = null
|
|
M.animate_movement = 2
|
|
return
|
|
else
|
|
|
|
if(mob.confused && !IS_PRONE(mob))
|
|
switch(mob.m_intent)
|
|
if(MOVE_INTENT_RUN)
|
|
if(prob(20))
|
|
to_chat(src, SPAN_WARNING("You stumble around confusedly."))
|
|
direct = turn(direct, pick(90, -90))
|
|
n = get_step(mob, direct)
|
|
if(MOVE_INTENT_WALK)
|
|
if(prob(10))
|
|
to_chat(src, SPAN_WARNING("You stumble around confusedly."))
|
|
direct = turn(direct, pick(90, -90))
|
|
n = get_step(mob, direct)
|
|
. = mob.SelfMove(n, direct)
|
|
//! End
|
|
|
|
//! WARNING: MORE LEGACY CODE
|
|
for (var/obj/item/grab/G in mob)
|
|
if (G.state == GRAB_NECK)
|
|
mob.setDir(global.reverse_dir[direct])
|
|
G.adjust_position()
|
|
for (var/obj/item/grab/G in mob.grabbed_by)
|
|
G.adjust_position()
|
|
//! End
|
|
|
|
// take lower of the two
|
|
add_delay = max(add_delay, add_delay_grab)
|
|
|
|
// moved diagonally; obey euclidean dist
|
|
if((direct & (direct - 1)) && mob.loc == n)
|
|
add_delay *= SQRT_2
|
|
|
|
// round to tick to prevent lurching instead of preserving momentum
|
|
// mob.move_delay = world.time + round(add_delay, world.tick_lag)
|
|
|
|
// preserve momentum: for non-evenly-0.5-multiple movespeeds (HELLO, DIAGONAL MOVES),
|
|
// we need to store how much we're cheated out of our tick and carry it through
|
|
// make an intelligent guess at if they're trying to keep moving, tho!
|
|
if(mob.last_self_move > (world.time - add_delay * 1.25))
|
|
mob.move_delay = old_delay + add_delay
|
|
else
|
|
mob.move_delay = world.time + add_delay
|
|
|
|
SMOOTH_GLIDE_SIZE(mob, DELAY_TO_GLIDE_SIZE(add_delay))
|
|
|
|
mob.last_self_move = world.time
|
|
|
|
/mob/proc/SelfMove(turf/T, dir)
|
|
in_selfmove = TRUE
|
|
. = Move(T, dir)
|
|
in_selfmove = FALSE
|
|
if(.)
|
|
throwing?.terminate()
|
|
|
|
///Process_Incorpmove
|
|
///Called by client/Move()
|
|
///Allows mobs to run though walls
|
|
/client/proc/Process_Incorpmove(direct)
|
|
var/turf/mobloc = get_turf(mob)
|
|
|
|
switch(mob.incorporeal_move)
|
|
if(1)
|
|
var/turf/T = get_step(mob, direct)
|
|
if(!T)
|
|
return
|
|
mob.forceMove(get_step(mob, direct))
|
|
mob.setDir(direct)
|
|
if(2)
|
|
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
|
|
mob.forceMove(locate(locx,locy,mobloc.z))
|
|
spawn(0)
|
|
var/limit = 2//For only two trailing shadows.
|
|
for(var/turf/T in getline(mobloc, mob.loc))
|
|
spawn(0)
|
|
anim(T,mob,'icons/mob/mob.dmi',,"shadow",,mob.dir)
|
|
limit--
|
|
if(limit<=0) break
|
|
else
|
|
spawn(0)
|
|
anim(mobloc,mob,'icons/mob/mob.dmi',,"shadow",,mob.dir)
|
|
mob.forceMove(get_step(mob, direct))
|
|
mob.dir = direct
|
|
// Crossed is always a bit iffy
|
|
for(var/obj/S in mob.loc)
|
|
if(istype(S,/obj/effect/step_trigger) || istype(S,/obj/effect/beam))
|
|
S.Crossed(mob)
|
|
|
|
var/area/A = get_area_master(mob)
|
|
if(A)
|
|
A.Entered(mob)
|
|
if(isturf(mob.loc))
|
|
var/turf/T = mob.loc
|
|
T.Entered(mob)
|
|
mob.Post_Incorpmove()
|
|
return 1
|
|
|
|
/mob/proc/Post_Incorpmove()
|
|
return
|
|
|
|
///Process_Spacemove
|
|
///Called by /client/Move()
|
|
///For moving in space
|
|
///Return 1 for movement 0 for none
|
|
/mob/Process_Spacemove(direction)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
if(Check_Dense_Object())
|
|
update_floating(TRUE)
|
|
return TRUE
|
|
|
|
/mob/proc/Check_Dense_Object() //checks for anything to push off in the vicinity. also handles magboots on gravity-less floors tiles
|
|
|
|
var/dense_object = 0
|
|
var/shoegrip
|
|
|
|
for(var/turf/turf in oview(1,src))
|
|
if(istype(turf,/turf/space))
|
|
continue
|
|
|
|
if(istype(turf,/turf/simulated/floor)) // Floors don't count if they don't have gravity
|
|
var/area/A = turf.loc
|
|
if(istype(A) && A.has_gravity == 0)
|
|
if(shoegrip == null)
|
|
shoegrip = Check_Shoegrip() //Shoegrip is only ever checked when a zero-gravity floor is encountered to reduce load
|
|
if(!shoegrip)
|
|
continue
|
|
|
|
dense_object++
|
|
break
|
|
|
|
if(!dense_object && (locate(/obj/structure/lattice) in oview(1, src)))
|
|
dense_object++
|
|
|
|
if(!dense_object && (locate(/obj/structure/catwalk) in oview(1, src)))
|
|
dense_object++
|
|
|
|
|
|
//Lastly attempt to locate any dense objects we could push off of
|
|
//TODO: If we implement objects drifing in space this needs to really push them
|
|
//Due to a few issues only anchored and dense objects will now work.
|
|
if(!dense_object)
|
|
for(var/obj/O in oview(1, src))
|
|
if((O) && (O.density) && (O.anchored))
|
|
dense_object++
|
|
break
|
|
|
|
return dense_object
|
|
|
|
/mob/proc/Check_Shoegrip()
|
|
if(flying) // Checks to see if they are flying.
|
|
return 1
|
|
return 0
|
|
|
|
/mob/proc/mob_has_gravity(turf/T)
|
|
return has_gravity(src, T)
|
|
|
|
// Called when a mob successfully moves.
|
|
// Would've been an /atom/movable proc but it caused issues.
|
|
/mob/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
|
|
. = ..()
|
|
client?.parallax_holder?.Update()
|
|
for(var/obj/O in contents)
|
|
O.on_loc_moved(old_loc)
|
|
reset_pixel_shifting()
|
|
// todo: this is kinda laggy, innit?
|
|
update_gravity()
|
|
|
|
// Received from Moved(), useful for items that need to know that their loc just moved.
|
|
// todo: REMOVE, this is bad for performance.
|
|
/obj/proc/on_loc_moved(atom/oldloc)
|
|
return
|
|
|
|
/obj/item/storage/on_loc_moved(atom/oldloc)
|
|
for(var/obj/O in contents)
|
|
O.on_loc_moved(oldloc)
|
|
|
|
// facing verbs
|
|
/**
|
|
* Returns true if a mob can turn to face things
|
|
*
|
|
* Conditions:
|
|
* * client.last_turn > world.time
|
|
* * not dead or unconcious
|
|
* * not anchored
|
|
* * no transform not set
|
|
* * we are not restrained
|
|
*/
|
|
/mob/proc/canface()
|
|
if(world.time <= last_self_turn)
|
|
return FALSE
|
|
if(stat == DEAD || stat == UNCONSCIOUS)
|
|
return FALSE
|
|
if(transforming)
|
|
return FALSE
|
|
if(restrained())
|
|
return FALSE
|
|
return TRUE
|
|
|
|
///Hidden verb to turn east
|
|
/mob/verb/eastface()
|
|
set hidden = TRUE
|
|
set src = usr
|
|
if(!canface())
|
|
return FALSE
|
|
setDir(EAST)
|
|
last_self_turn = world.time
|
|
return TRUE
|
|
|
|
///Hidden verb to turn west
|
|
/mob/verb/westface()
|
|
set hidden = TRUE
|
|
set src = usr
|
|
if(!canface())
|
|
return FALSE
|
|
setDir(WEST)
|
|
last_self_turn = world.time
|
|
return TRUE
|
|
|
|
///Hidden verb to turn north
|
|
/mob/verb/northface()
|
|
set hidden = TRUE
|
|
set src = usr
|
|
if(!canface())
|
|
return FALSE
|
|
setDir(NORTH)
|
|
last_self_turn = world.time
|
|
return TRUE
|
|
|
|
///Hidden verb to turn south
|
|
/mob/verb/southface()
|
|
set hidden = TRUE
|
|
set src = usr
|
|
if(!canface())
|
|
return FALSE
|
|
setDir(SOUTH)
|
|
last_self_turn = world.time
|
|
return TRUE
|
|
|
|
//! Pixel Shifting
|
|
/mob/verb/eastshift()
|
|
set hidden = TRUE
|
|
set src = usr
|
|
if(!canface())
|
|
return FALSE
|
|
if(shift_pixel_x < 16)
|
|
adjust_pixel_shift_x(1)
|
|
|
|
/mob/verb/westshift()
|
|
set hidden = TRUE
|
|
set src = usr
|
|
if(!canface())
|
|
return FALSE
|
|
if(shift_pixel_x > -16)
|
|
adjust_pixel_shift_x(-1)
|
|
|
|
/mob/verb/northshift()
|
|
set hidden = TRUE
|
|
set src = usr
|
|
if(!canface())
|
|
return FALSE
|
|
if(shift_pixel_y < 16)
|
|
adjust_pixel_shift_y(1)
|
|
|
|
/mob/verb/southshift()
|
|
set hidden = TRUE
|
|
set src = usr
|
|
if(!canface())
|
|
return FALSE
|
|
if(shift_pixel_y > -16)
|
|
adjust_pixel_shift_y(-1)
|
|
|
|
//? Gravity
|
|
|
|
/mob/proc/update_gravity()
|
|
var/has_gravity = has_gravity()
|
|
if(has_gravity == in_gravity)
|
|
return
|
|
var/old_gravity = in_gravity
|
|
in_gravity = has_gravity
|
|
on_gravity_change(has_gravity, old_gravity)
|
|
|
|
/mob/proc/on_gravity_change(old_gravity, new_gravity)
|
|
update_movespeed()
|
|
|
|
//? Movement Intercepts
|
|
|
|
/mob/proc/request_movement_intercept(datum/requesting)
|
|
if(movement_intercept)
|
|
if(requesting == movement_intercept)
|
|
return TRUE
|
|
return FALSE
|
|
movement_intercept = requesting
|
|
return TRUE
|
|
|
|
/mob/proc/clear_movement_intercept()
|
|
movement_intercept = null
|
|
return TRUE
|
|
|
|
/datum/proc/intercept_mob_move(mob/moving, dir)
|
|
return
|