Files
Bubberstation/code/game/atoms_movable.dm
phil235 15d8e0a96f I refactored how a mob resisting out of a closet that is itself inside something is done.
Fixes not being able to resist out of an unlocked unwelded locker. Now both moving and the resist button  work (except for the cardboard box).
You can no longer spam breakingout message ouf of a closet by moving while inside.
Wrapping and unwrapping a locker no longer unwelds it magically.
I refactored closet/crate/item package wrapping.
You can packagewrap belts and other storage items in which the package wrap item doesn't fit.(it does currently have the unintended side effect of giving you the "doesn't fit in X" message when wrapping there storage items though).
2016-03-03 20:20:34 +01:00

347 lines
9.4 KiB
Plaintext

/atom/movable
layer = 3
var/last_move = null
var/anchored = 0
var/throwing = 0
var/throw_speed = 2
var/throw_range = 7
var/mob/pulledby = null
var/languages = 0 //For say() and Hear()
var/verb_say = "says"
var/verb_ask = "asks"
var/verb_exclaim = "exclaims"
var/verb_yell = "yells"
var/inertia_dir = 0
var/pass_flags = 0
glide_size = 8
/atom/movable/Move(atom/newloc, direct = 0)
if(!loc || !newloc) return 0
var/atom/oldloc = loc
if(loc != newloc)
if (!(direct & (direct - 1))) //Cardinal move
. = ..()
else //Diagonal move, split it into cardinal moves
if (direct & 1)
if (direct & 4)
if (step(src, NORTH))
. = step(src, EAST)
else if (step(src, EAST))
. = step(src, NORTH)
else if (direct & 8)
if (step(src, NORTH))
. = step(src, WEST)
else if (step(src, WEST))
. = step(src, NORTH)
else if (direct & 2)
if (direct & 4)
if (step(src, SOUTH))
. = step(src, EAST)
else if (step(src, EAST))
. = step(src, SOUTH)
else if (direct & 8)
if (step(src, SOUTH))
. = step(src, WEST)
else if (step(src, WEST))
. = step(src, SOUTH)
if(!loc || (loc == oldloc && oldloc != newloc))
last_move = 0
return
if(.)
Moved(oldloc, direct)
last_move = direct
spawn(5) // Causes space drifting. /tg/station has no concept of speed, we just use 5
if(loc && direct && last_move == direct)
if(loc == newloc) //Remove this check and people can accelerate. Not opening that can of worms just yet.
newtonian_move(last_move)
if(. && buckled_mob && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob
. = 0
//Called after a successful Move(). By this point, we've already moved
/atom/movable/proc/Moved(atom/OldLoc, Dir)
return 1
/atom/movable/Destroy()
. = ..()
if(loc)
loc.handle_atom_del(src)
if(reagents)
qdel(reagents)
for(var/atom/movable/AM in contents)
qdel(AM)
loc = null
invisibility = 101
if (pulledby)
if (pulledby.pulling == src)
pulledby.pulling = null
pulledby = null
// Previously known as HasEntered()
// This is automatically called when something enters your square
/atom/movable/Crossed(atom/movable/AM)
return
/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump().
if((A && yes))
if(throwing)
throwing = 0
throw_impact(A)
. = 1
if(!A || qdeleted(A))
return
A.Bumped(src)
/atom/movable/proc/forceMove(atom/destination)
if(destination)
var/atom/oldloc = loc
if(oldloc)
oldloc.Exited(src, destination)
loc = destination
destination.Entered(src, oldloc)
var/area/old_area = get_area(oldloc)
var/area/destarea = get_area(destination)
if(old_area != destarea)
destarea.Entered(src)
for(var/atom/movable/AM in destination)
if(AM == src)
continue
AM.Crossed(src)
Moved(oldloc, 0)
return 1
return 0
/mob/living/forceMove(atom/destination)
stop_pulling()
if(pulledby)
pulledby.stop_pulling()
if(buckled)
buckled.unbuckle_mob()
if(buckled_mob)
unbuckle_mob(force=1)
. = ..()
if(client)
reset_perspective(destination)
update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall.
/mob/living/carbon/brain/forceMove(atom/destination)
if(container)
container.forceMove(destination)
else //something went very wrong.
CRASH("Brainmob without container.")
/mob/living/silicon/pai/forceMove(atom/destination)
if(card)
card.forceMove(destination)
else //something went very wrong.
CRASH("pAI without card")
//Called whenever an object moves and by mobs when they attempt to move themselves through space
//And when an object or action applies a force on src, see newtonian_move() below
//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting
//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm
//movement_dir == 0 when stopping or any dir when trying to move
/atom/movable/proc/Process_Spacemove(movement_dir = 0)
if(has_gravity(src))
return 1
if(pulledby)
return 1
if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier
return 1
return 0
/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity
if(!loc || Process_Spacemove(0))
inertia_dir = 0
return 0
inertia_dir = direction
if(!direction)
return 1
var/old_dir = dir
. = step(src, direction)
dir = old_dir
/atom/movable/proc/checkpass(passflag)
return pass_flags&passflag
/atom/movable/proc/throw_impact(atom/hit_atom)
return hit_atom.hitby(src)
/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked)
if(!anchored && hitpush)
step(src, AM.dir)
..()
/atom/movable/proc/throw_at_fast(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
set waitfor = 0
throw_at(target, range, speed, thrower, spin, diagonals_first)
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
if(!target || !src || (flags & NODROP))
return 0
//use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
throwing = 1
if(spin) //if we don't want the /atom/movable to spin.
SpinAnimation(5, 1)
var/dist_travelled = 0
var/dist_since_sleep = 0
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/dx = (target.x > src.x) ? EAST : WEST
var/dy = (target.y > src.y) ? NORTH : SOUTH
var/pure_diagonal = 0
if(dist_x == dist_y)
pure_diagonal = 1
if(dist_x <= dist_y)
var/olddist_x = dist_x
var/olddx = dx
dist_x = dist_y
dist_y = olddist_x
dx = dy
dy = olddx
var/error = dist_x/2 - dist_y //used to decide whether our next move should be forward or diagonal.
var/atom/finalturf = get_turf(target)
var/hit = 0
var/init_dir = get_dir(src, target)
while(target && ((dist_travelled < range && loc != finalturf) || !has_gravity(src))) //stop if we reached our destination (or max range) and aren't floating
if(!istype(loc, /turf))
hit = 1
break
var/atom/step
if(dist_travelled < max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction
step = get_step(src, get_dir(src, finalturf))
else
step = get_step(src, init_dir)
if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
if(error >= 0 && max(dist_x,dist_y) - dist_travelled != 1) //we do a step forward unless we're right before the target
step = get_step(src, dx)
error += (error < 0) ? dist_x/2 : -dist_y
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
Move(step, get_dir(loc, step))
if(!throwing) // we hit something during our move
hit = 1
break
dist_travelled++
dist_since_sleep++
if(dist_travelled > 600) //safety to prevent infinite while loop.
break
if(dist_since_sleep >= speed)
dist_since_sleep = 0
sleep(1)
if(!dist_since_sleep && hitcheck()) //to catch sneaky things moving on our tile during our sleep(1)
hit = 1
break
//done throwing, either because it hit something or it finished moving
throwing = 0
if(!hit)
for(var/atom/A in get_turf(src)) //looking for our target on the turf we land on.
if(A == target)
hit = 1
throw_impact(A)
return 1
throw_impact(get_turf(src)) // we haven't hit something yet and we still must, let's hit the ground.
return 1
/atom/movable/proc/hitcheck()
for(var/atom/movable/AM in get_turf(src))
if(AM == src)
continue
if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
throwing = 0
throw_impact(AM)
return 1
//Overlays
/atom/movable/overlay
var/atom/master = null
anchored = 1
/atom/movable/overlay/New()
verbs.Cut()
return
/atom/movable/overlay/attackby(a, b, c)
if (src.master)
return src.master.attackby(a, b, c)
return
/atom/movable/overlay/attack_paw(a, b, c)
if (src.master)
return src.master.attack_paw(a, b, c)
return
/atom/movable/overlay/attack_hand(a, b, c)
if (src.master)
return src.master.attack_hand(a, b, c)
return
/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
if(!buckled_mob.Move(newloc, direct))
loc = buckled_mob.loc
last_move = buckled_mob.last_move
inertia_dir = last_move
buckled_mob.inertia_dir = last_move
return 0
return 1
/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5)
if(buckled_mob == mover)
return 1
return ..()
/atom/movable/proc/get_spacemove_backup()
var/atom/movable/dense_object_backup
for(var/A in orange(1, get_turf(src)))
if(isarea(A))
continue
else if(isturf(A))
var/turf/turf = A
if(!turf.density)
continue
return turf
else
var/atom/movable/AM = A
if(!AM.CanPass(src) || AM.density)
if(AM.anchored)
return AM
dense_object_backup = AM
break
. = dense_object_backup
//called when a mob resists while inside a container that is itself inside something.
/atom/movable/proc/relay_container_resist(mob/living/user, obj/O)
return