diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 8072b9475a..fbfe6b5834 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -1,618 +1,618 @@
-
#ifndef PIXEL_SCALE
#define PIXEL_SCALE 0
#if DM_VERSION >= 512
#error HEY, PIXEL_SCALE probably exists now, remove this gross ass shim.
#endif
+#endif
-/atom/movable
- layer = OBJ_LAYER
- var/last_move = null
- var/anchored = 0
- var/datum/thrownthing/throwing = null
- var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported
- var/throw_range = 7
- var/mob/pulledby = null
- var/list/languages
- var/list/initial_languages = list(/datum/language/common)
- var/only_speaks_language = null
- var/verb_say = "says"
- var/verb_ask = "asks"
- var/verb_exclaim = "exclaims"
- var/verb_whisper = "whispers"
- var/verb_yell = "yells"
- var/inertia_dir = 0
- var/atom/inertia_last_loc
- var/inertia_moving = 0
- var/inertia_next_move = 0
- var/inertia_move_delay = 5
- var/pass_flags = 0
- var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
- var/list/client_mobs_in_contents // This contains all the client mobs within this container
- var/list/acted_explosions //for explosion dodging
- glide_size = 8
+/atom/movable
+ layer = OBJ_LAYER
+ var/last_move = null
+ var/anchored = 0
+ var/datum/thrownthing/throwing = null
+ var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported
+ var/throw_range = 7
+ var/mob/pulledby = null
+ var/list/languages
+ var/list/initial_languages = list(/datum/language/common)
+ var/only_speaks_language = null
+ var/verb_say = "says"
+ var/verb_ask = "asks"
+ var/verb_exclaim = "exclaims"
+ var/verb_whisper = "whispers"
+ var/verb_yell = "yells"
+ var/inertia_dir = 0
+ var/atom/inertia_last_loc
+ var/inertia_moving = 0
+ var/inertia_next_move = 0
+ var/inertia_move_delay = 5
+ var/pass_flags = 0
+ var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
+ var/list/client_mobs_in_contents // This contains all the client mobs within this container
+ var/list/acted_explosions //for explosion dodging
+ glide_size = 8
appearance_flags = TILE_BOUND|PIXEL_SCALE
- var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
- var/floating = FALSE
-
-/atom/movable/vv_edit_var(var_name, var_value)
- var/static/list/banned_edits = list("step_x", "step_y", "step_size")
- var/static/list/careful_edits = list("bound_x", "bound_y", "bound_width", "bound_height")
- if(var_name in banned_edits)
- return FALSE //PLEASE no.
- if((var_name in careful_edits) && (var_value % world.icon_size) != 0)
- return FALSE
- return ..()
-
-/atom/movable/Initialize(mapload)
- ..()
- for(var/L in initial_languages)
- grant_language(L)
-
-/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
- moving_diagonally = FIRST_DIAG_STEP
- if (direct & 1)
- if (direct & 4)
- if (step(src, NORTH))
- moving_diagonally = SECOND_DIAG_STEP
- . = step(src, EAST)
- else if (step(src, EAST))
- moving_diagonally = SECOND_DIAG_STEP
- . = step(src, NORTH)
- else if (direct & 8)
- if (step(src, NORTH))
- moving_diagonally = SECOND_DIAG_STEP
- . = step(src, WEST)
- else if (step(src, WEST))
- moving_diagonally = SECOND_DIAG_STEP
- . = step(src, NORTH)
- else if (direct & 2)
- if (direct & 4)
- if (step(src, SOUTH))
- moving_diagonally = SECOND_DIAG_STEP
- . = step(src, EAST)
- else if (step(src, EAST))
- moving_diagonally = SECOND_DIAG_STEP
- . = step(src, SOUTH)
- else if (direct & 8)
- if (step(src, SOUTH))
- moving_diagonally = SECOND_DIAG_STEP
- . = step(src, WEST)
- else if (step(src, WEST))
- moving_diagonally = SECOND_DIAG_STEP
- . = step(src, SOUTH)
- moving_diagonally = 0
- return
-
- if(!loc || (loc == oldloc && oldloc != newloc))
- last_move = 0
- return
-
- if(.)
- Moved(oldloc, direct)
-
- last_move = direct
- setDir(direct)
- if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s)
- . = 0
-
-//Called after a successful Move(). By this point, we've already moved
-/atom/movable/proc/Moved(atom/OldLoc, Dir)
- if (!inertia_moving)
- inertia_next_move = world.time + inertia_move_delay
- newtonian_move(Dir)
- if (length(client_mobs_in_contents))
- update_parallax_contents()
-
- if (orbiters)
- for (var/thing in orbiters)
- var/datum/orbit/O = thing
- O.Check()
- if (orbiting)
- orbiting.Check()
-
- if(flags & CLEAN_ON_MOVE)
- clean_on_move()
- return 1
-
-/atom/movable/proc/clean_on_move()
- var/turf/tile = loc
- if(isturf(tile))
- tile.clean_blood()
- for(var/A in tile)
- if(is_cleanable(A))
- qdel(A)
- else if(istype(A, /obj/item))
- var/obj/item/cleaned_item = A
- cleaned_item.clean_blood()
- else if(ishuman(A))
- var/mob/living/carbon/human/cleaned_human = A
- if(cleaned_human.lying)
- if(cleaned_human.head)
- cleaned_human.head.clean_blood()
- cleaned_human.update_inv_head()
- if(cleaned_human.wear_suit)
- cleaned_human.wear_suit.clean_blood()
- cleaned_human.update_inv_wear_suit()
- else if(cleaned_human.w_uniform)
- cleaned_human.w_uniform.clean_blood()
- cleaned_human.update_inv_w_uniform()
- if(cleaned_human.shoes)
- cleaned_human.shoes.clean_blood()
- cleaned_human.update_inv_shoes()
- cleaned_human.clean_blood()
- cleaned_human.wash_cream()
- to_chat(cleaned_human, "[src] cleans your face!")
-
-/atom/movable/Destroy(force)
- var/inform_admins = HAS_SECONDARY_FLAG(src, INFORM_ADMINS_ON_RELOCATE)
- var/stationloving = HAS_SECONDARY_FLAG(src, STATIONLOVING)
-
- if(inform_admins && force)
- var/turf/T = get_turf(src)
- message_admins("[src] has been !!force deleted!! in [ADMIN_COORDJMP(T)].")
- log_game("[src] has been !!force deleted!! in [COORD(T)].")
-
- if(stationloving && !force)
- var/turf/currentturf = get_turf(src)
- var/turf/targetturf = relocate()
- log_game("[src] has been destroyed in [COORD(currentturf)]. Moving it to [COORD(targetturf)].")
- if(inform_admins)
- message_admins("[src] has been destroyed in [ADMIN_COORDJMP(currentturf)]. Moving it to [ADMIN_COORDJMP(targetturf)].")
- return QDEL_HINT_LETMELIVE
-
- if(stationloving && force)
- STOP_PROCESSING(SSinbounds, src)
-
- . = ..()
- if(loc)
- loc.handle_atom_del(src)
- for(var/atom/movable/AM in contents)
- qdel(AM)
- loc = null
- invisibility = INVISIBILITY_ABSTRACT
- if(pulledby)
- pulledby.stop_pulling()
-
-
-// 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.hit_atom(A)
- . = 1
- if(!A || QDELETED(A))
- return
- A.Bumped(src)
-
-/atom/movable/proc/forceMove(atom/destination)
- if(destination)
- if(pulledby)
- pulledby.stop_pulling()
- var/atom/oldloc = loc
- var/same_loc = oldloc == destination
- var/area/old_area = get_area(oldloc)
- var/area/destarea = get_area(destination)
-
- if(oldloc && !same_loc)
- oldloc.Exited(src, destination)
- if(old_area)
- old_area.Exited(src, destination)
-
- loc = destination
-
- if(!same_loc)
- destination.Entered(src, oldloc)
- if(destarea && old_area != destarea)
- destarea.Entered(src, oldloc)
-
- 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(buckled)
- buckled.unbuckle_mob(src,force=1)
- if(has_buckled_mobs())
- unbuckle_all_mobs(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/brain/forceMove(atom/destination)
- if(container)
- return container.forceMove(destination)
- else //something went very wrong.
- CRASH("Brainmob without container.")
-
-//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(throwing)
- 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
- inertia_last_loc = loc
- SSspacedrift.processing[src] = src
- return 1
-
-/atom/movable/proc/checkpass(passflag)
- return pass_flags&passflag
-
-/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum)
- set waitfor = 0
- 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(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback)
- if (!target || (flags & NODROP) || speed <= 0)
- return
-
- if (pulledby)
- pulledby.stop_pulling()
-
- //They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw?
- if (thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag*2)
- var/user_momentum = thrower.movement_delay()
- if (!user_momentum) //no movement_delay, this means they move once per byond tick, lets calculate from that instead.
- user_momentum = world.tick_lag
-
- user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses.
-
- if (get_dir(thrower, target) & last_move)
- user_momentum = user_momentum //basically a noop, but needed
- else if (get_dir(target, thrower) & last_move)
- user_momentum = -user_momentum //we are moving away from the target, lets slowdown the throw accordingly
- else
- user_momentum = 0
-
-
- if (user_momentum)
- //first lets add that momentum to range.
- range *= (user_momentum / speed) + 1
- //then lets add it to speed
- speed += user_momentum
- if (speed <= 0)
- return //no throw speed, the user was moving too fast.
-
- var/datum/thrownthing/TT = new()
- TT.thrownthing = src
- TT.target = target
- TT.target_turf = get_turf(target)
- TT.init_dir = get_dir(src, target)
- TT.maxrange = range
- TT.speed = speed
- TT.thrower = thrower
- TT.diagonals_first = diagonals_first
- TT.callback = callback
-
- 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
-
- if (dist_x == dist_y)
- TT.pure_diagonal = 1
-
- else 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
- TT.dist_x = dist_x
- TT.dist_y = dist_y
- TT.dx = dx
- TT.dy = dy
- TT.diagonal_error = dist_x/2 - dist_y
- TT.start_time = world.time
-
- if(pulledby)
- pulledby.stop_pulling()
-
- throwing = TT
- if(spin)
- SpinAnimation(5, 1)
-
- SSthrowing.processing[src] = TT
- if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun))
- SSthrowing.currentrun[src] = TT
- TT.tick()
-
-
-/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- 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(mover in buckled_mobs)
- 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
-
-
-/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y)
- if(!no_effect && (visual_effect_icon || used_item))
- do_item_attack_animation(A, visual_effect_icon, used_item)
-
- var/pixel_x_diff = 0
- var/pixel_y_diff = 0
- var/final_pixel_y = initial(pixel_y)
- if(end_pixel_y)
- final_pixel_y = end_pixel_y
-
- var/direction = get_dir(src, A)
- if(direction & NORTH)
- pixel_y_diff = 8
- else if(direction & SOUTH)
- pixel_y_diff = -8
-
- if(direction & EAST)
- pixel_x_diff = 8
- else if(direction & WEST)
- pixel_x_diff = -8
-
- animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2)
- animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2)
-
-/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item)
- var/image/I
- if(visual_effect_icon)
- I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1)
- else if(used_item)
- I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1)
-
- // Scale the icon.
- I.transform *= 0.75
- // The icon should not rotate.
- I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
-
- // Set the direction of the icon animation.
- var/direction = get_dir(src, A)
- if(direction & NORTH)
- I.pixel_y = -16
- else if(direction & SOUTH)
- I.pixel_y = 16
-
- if(direction & EAST)
- I.pixel_x = -16
- else if(direction & WEST)
- I.pixel_x = 16
-
- if(!direction) // Attacked self?!
- I.pixel_z = 16
-
- if(!I)
- return
-
- flick_overlay(I, GLOB.clients, 5) // 5 ticks/half a second
-
- // And animate the attack!
- animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3)
-
-/atom/movable/vv_get_dropdown()
- . = ..()
- . -= "Jump to"
- .["Follow"] = "?_src_=holder;adminplayerobservefollow=\ref[src]"
-
-/atom/movable/proc/ex_check(ex_id)
- if(!ex_id)
- return TRUE
- LAZYINITLIST(acted_explosions)
- if(ex_id in acted_explosions)
- return FALSE
- acted_explosions += ex_id
- return TRUE
-
-/atom/movable/proc/float(on)
- if(throwing)
- return
- if(on && !floating)
- animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1)
- sleep(10)
- animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1)
- floating = TRUE
- else if (!on && floating)
- animate(src, pixel_y = initial(pixel_y), time = 10)
- floating = FALSE
-
-/* Stationloving
-*
-* A stationloving atom will always teleport back to the station
-* if it ever leaves the station z-levels or Centcom. It will also,
-* when Destroy() is called, will teleport to a random turf on the
-* station.
-*
-* The turf is guaranteed to be "safe" for normal humans, probably.
-* If the station is SUPER SMASHED UP, it might not work.
-*
-* Here are some important procs:
-* relocate()
-* moves the atom to a safe turf on the station
-*
-* check_in_bounds()
-* regularly called and checks if `in_bounds()` returns true. If false, it
-* triggers a `relocate()`.
-*
-* in_bounds()
-* By default, checks that the atom's z is the station z or centcom.
-*/
-
-/atom/movable/proc/set_stationloving(state, inform_admins=FALSE)
- var/currently = HAS_SECONDARY_FLAG(src, STATIONLOVING)
-
- if(inform_admins)
- SET_SECONDARY_FLAG(src, INFORM_ADMINS_ON_RELOCATE)
- else
- CLEAR_SECONDARY_FLAG(src, INFORM_ADMINS_ON_RELOCATE)
-
- if(state == currently)
- return
- else if(!state)
- STOP_PROCESSING(SSinbounds, src)
- CLEAR_SECONDARY_FLAG(src, STATIONLOVING)
- else
- START_PROCESSING(SSinbounds, src)
- SET_SECONDARY_FLAG(src, STATIONLOVING)
-
-/atom/movable/proc/relocate()
- var/targetturf = find_safe_turf(ZLEVEL_STATION)
- if(!targetturf)
- if(GLOB.blobstart.len > 0)
- targetturf = get_turf(pick(GLOB.blobstart))
- else
- throw EXCEPTION("Unable to find a blobstart landmark")
-
- if(ismob(loc))
- var/mob/M = loc
- M.transferItemToLoc(src, targetturf, TRUE) //nodrops disks when?
- else if(istype(loc, /obj/item/weapon/storage))
- var/obj/item/weapon/storage/S = loc
- S.remove_from_storage(src, targetturf)
- else
- forceMove(targetturf)
- // move the disc, so ghosts remain orbiting it even if it's "destroyed"
- return targetturf
-
-/atom/movable/proc/check_in_bounds()
- if(in_bounds())
- return
- else
- var/turf/currentturf = get_turf(src)
- get(src, /mob) << "You can't help but feel that you just lost something back there..."
- var/turf/targetturf = relocate()
- log_game("[src] has been moved out of bounds in [COORD(currentturf)]. Moving it to [COORD(targetturf)].")
- if(HAS_SECONDARY_FLAG(src, INFORM_ADMINS_ON_RELOCATE))
- message_admins("[src] has been moved out of bounds in [ADMIN_COORDJMP(currentturf)]. Moving it to [ADMIN_COORDJMP(targetturf)].")
-
-/atom/movable/proc/in_bounds()
- . = FALSE
- var/turf/currentturf = get_turf(src)
- if(currentturf && (currentturf.z == ZLEVEL_CENTCOM || currentturf.z == ZLEVEL_STATION))
- . = TRUE
-
-
-/* Language procs */
-/atom/movable/proc/grant_language(datum/language/dt)
- LAZYINITLIST(languages)
- languages[dt] = TRUE
-
-/atom/movable/proc/grant_all_languages(omnitongue=FALSE)
- for(var/la in subtypesof(/datum/language))
- grant_language(la)
-
- if(omnitongue)
- SET_SECONDARY_FLAG(src, OMNITONGUE)
-
-/atom/movable/proc/get_random_understood_language()
- var/list/possible = list()
- for(var/dt in languages)
- possible += dt
- . = safepick(possible)
-
-/atom/movable/proc/remove_language(datum/language/dt)
- LAZYREMOVE(languages, dt)
-
-/atom/movable/proc/remove_all_languages()
- LAZYCLEARLIST(languages)
-
-/atom/movable/proc/has_language(datum/language/dt)
- . = is_type_in_typecache(dt, languages)
-
-/atom/movable/proc/can_speak_in_language(datum/language/dt)
- . = has_language(dt)
- if(only_speaks_language && !HAS_SECONDARY_FLAG(src, OMNITONGUE))
- . = . && ispath(only_speaks_language, dt)
-
-/atom/movable/proc/get_default_language()
- // if no language is specified, and we want to say() something, which
- // language do we use?
- var/datum/language/chosen_langtype
- var/highest_priority
-
- for(var/lt in languages)
- var/datum/language/langtype = lt
- if(!can_speak_in_language(langtype))
- continue
-
- var/pri = initial(langtype.default_priority)
- if(!highest_priority || (pri > highest_priority))
- chosen_langtype = langtype
- highest_priority = pri
-
- . = chosen_langtype
+ var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
+ var/floating = FALSE
+
+/atom/movable/vv_edit_var(var_name, var_value)
+ var/static/list/banned_edits = list("step_x", "step_y", "step_size")
+ var/static/list/careful_edits = list("bound_x", "bound_y", "bound_width", "bound_height")
+ if(var_name in banned_edits)
+ return FALSE //PLEASE no.
+ if((var_name in careful_edits) && (var_value % world.icon_size) != 0)
+ return FALSE
+ return ..()
+
+/atom/movable/Initialize(mapload)
+ ..()
+ for(var/L in initial_languages)
+ grant_language(L)
+
+/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
+ moving_diagonally = FIRST_DIAG_STEP
+ if (direct & 1)
+ if (direct & 4)
+ if (step(src, NORTH))
+ moving_diagonally = SECOND_DIAG_STEP
+ . = step(src, EAST)
+ else if (step(src, EAST))
+ moving_diagonally = SECOND_DIAG_STEP
+ . = step(src, NORTH)
+ else if (direct & 8)
+ if (step(src, NORTH))
+ moving_diagonally = SECOND_DIAG_STEP
+ . = step(src, WEST)
+ else if (step(src, WEST))
+ moving_diagonally = SECOND_DIAG_STEP
+ . = step(src, NORTH)
+ else if (direct & 2)
+ if (direct & 4)
+ if (step(src, SOUTH))
+ moving_diagonally = SECOND_DIAG_STEP
+ . = step(src, EAST)
+ else if (step(src, EAST))
+ moving_diagonally = SECOND_DIAG_STEP
+ . = step(src, SOUTH)
+ else if (direct & 8)
+ if (step(src, SOUTH))
+ moving_diagonally = SECOND_DIAG_STEP
+ . = step(src, WEST)
+ else if (step(src, WEST))
+ moving_diagonally = SECOND_DIAG_STEP
+ . = step(src, SOUTH)
+ moving_diagonally = 0
+ return
+
+ if(!loc || (loc == oldloc && oldloc != newloc))
+ last_move = 0
+ return
+
+ if(.)
+ Moved(oldloc, direct)
+
+ last_move = direct
+ setDir(direct)
+ if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s)
+ . = 0
+
+//Called after a successful Move(). By this point, we've already moved
+/atom/movable/proc/Moved(atom/OldLoc, Dir)
+ if (!inertia_moving)
+ inertia_next_move = world.time + inertia_move_delay
+ newtonian_move(Dir)
+ if (length(client_mobs_in_contents))
+ update_parallax_contents()
+
+ if (orbiters)
+ for (var/thing in orbiters)
+ var/datum/orbit/O = thing
+ O.Check()
+ if (orbiting)
+ orbiting.Check()
+
+ if(flags & CLEAN_ON_MOVE)
+ clean_on_move()
+ return 1
+
+/atom/movable/proc/clean_on_move()
+ var/turf/tile = loc
+ if(isturf(tile))
+ tile.clean_blood()
+ for(var/A in tile)
+ if(is_cleanable(A))
+ qdel(A)
+ else if(istype(A, /obj/item))
+ var/obj/item/cleaned_item = A
+ cleaned_item.clean_blood()
+ else if(ishuman(A))
+ var/mob/living/carbon/human/cleaned_human = A
+ if(cleaned_human.lying)
+ if(cleaned_human.head)
+ cleaned_human.head.clean_blood()
+ cleaned_human.update_inv_head()
+ if(cleaned_human.wear_suit)
+ cleaned_human.wear_suit.clean_blood()
+ cleaned_human.update_inv_wear_suit()
+ else if(cleaned_human.w_uniform)
+ cleaned_human.w_uniform.clean_blood()
+ cleaned_human.update_inv_w_uniform()
+ if(cleaned_human.shoes)
+ cleaned_human.shoes.clean_blood()
+ cleaned_human.update_inv_shoes()
+ cleaned_human.clean_blood()
+ cleaned_human.wash_cream()
+ to_chat(cleaned_human, "[src] cleans your face!")
+
+/atom/movable/Destroy(force)
+ var/inform_admins = HAS_SECONDARY_FLAG(src, INFORM_ADMINS_ON_RELOCATE)
+ var/stationloving = HAS_SECONDARY_FLAG(src, STATIONLOVING)
+
+ if(inform_admins && force)
+ var/turf/T = get_turf(src)
+ message_admins("[src] has been !!force deleted!! in [ADMIN_COORDJMP(T)].")
+ log_game("[src] has been !!force deleted!! in [COORD(T)].")
+
+ if(stationloving && !force)
+ var/turf/currentturf = get_turf(src)
+ var/turf/targetturf = relocate()
+ log_game("[src] has been destroyed in [COORD(currentturf)]. Moving it to [COORD(targetturf)].")
+ if(inform_admins)
+ message_admins("[src] has been destroyed in [ADMIN_COORDJMP(currentturf)]. Moving it to [ADMIN_COORDJMP(targetturf)].")
+ return QDEL_HINT_LETMELIVE
+
+ if(stationloving && force)
+ STOP_PROCESSING(SSinbounds, src)
+
+ . = ..()
+ if(loc)
+ loc.handle_atom_del(src)
+ for(var/atom/movable/AM in contents)
+ qdel(AM)
+ loc = null
+ invisibility = INVISIBILITY_ABSTRACT
+ if(pulledby)
+ pulledby.stop_pulling()
+
+
+// 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.hit_atom(A)
+ . = 1
+ if(!A || QDELETED(A))
+ return
+ A.Bumped(src)
+
+/atom/movable/proc/forceMove(atom/destination)
+ if(destination)
+ if(pulledby)
+ pulledby.stop_pulling()
+ var/atom/oldloc = loc
+ var/same_loc = oldloc == destination
+ var/area/old_area = get_area(oldloc)
+ var/area/destarea = get_area(destination)
+
+ if(oldloc && !same_loc)
+ oldloc.Exited(src, destination)
+ if(old_area)
+ old_area.Exited(src, destination)
+
+ loc = destination
+
+ if(!same_loc)
+ destination.Entered(src, oldloc)
+ if(destarea && old_area != destarea)
+ destarea.Entered(src, oldloc)
+
+ 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(buckled)
+ buckled.unbuckle_mob(src,force=1)
+ if(has_buckled_mobs())
+ unbuckle_all_mobs(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/brain/forceMove(atom/destination)
+ if(container)
+ return container.forceMove(destination)
+ else //something went very wrong.
+ CRASH("Brainmob without container.")
+
+//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(throwing)
+ 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
+ inertia_last_loc = loc
+ SSspacedrift.processing[src] = src
+ return 1
+
+/atom/movable/proc/checkpass(passflag)
+ return pass_flags&passflag
+
+/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum)
+ set waitfor = 0
+ 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(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback)
+ if (!target || (flags & NODROP) || speed <= 0)
+ return
+
+ if (pulledby)
+ pulledby.stop_pulling()
+
+ //They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw?
+ if (thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag*2)
+ var/user_momentum = thrower.movement_delay()
+ if (!user_momentum) //no movement_delay, this means they move once per byond tick, lets calculate from that instead.
+ user_momentum = world.tick_lag
+
+ user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses.
+
+ if (get_dir(thrower, target) & last_move)
+ user_momentum = user_momentum //basically a noop, but needed
+ else if (get_dir(target, thrower) & last_move)
+ user_momentum = -user_momentum //we are moving away from the target, lets slowdown the throw accordingly
+ else
+ user_momentum = 0
+
+
+ if (user_momentum)
+ //first lets add that momentum to range.
+ range *= (user_momentum / speed) + 1
+ //then lets add it to speed
+ speed += user_momentum
+ if (speed <= 0)
+ return //no throw speed, the user was moving too fast.
+
+ var/datum/thrownthing/TT = new()
+ TT.thrownthing = src
+ TT.target = target
+ TT.target_turf = get_turf(target)
+ TT.init_dir = get_dir(src, target)
+ TT.maxrange = range
+ TT.speed = speed
+ TT.thrower = thrower
+ TT.diagonals_first = diagonals_first
+ TT.callback = callback
+
+ 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
+
+ if (dist_x == dist_y)
+ TT.pure_diagonal = 1
+
+ else 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
+ TT.dist_x = dist_x
+ TT.dist_y = dist_y
+ TT.dx = dx
+ TT.dy = dy
+ TT.diagonal_error = dist_x/2 - dist_y
+ TT.start_time = world.time
+
+ if(pulledby)
+ pulledby.stop_pulling()
+
+ throwing = TT
+ if(spin)
+ SpinAnimation(5, 1)
+
+ SSthrowing.processing[src] = TT
+ if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun))
+ SSthrowing.currentrun[src] = TT
+ TT.tick()
+
+
+/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
+ for(var/m in buckled_mobs)
+ var/mob/living/buckled_mob = m
+ 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(mover in buckled_mobs)
+ 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
+
+
+/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y)
+ if(!no_effect && (visual_effect_icon || used_item))
+ do_item_attack_animation(A, visual_effect_icon, used_item)
+
+ var/pixel_x_diff = 0
+ var/pixel_y_diff = 0
+ var/final_pixel_y = initial(pixel_y)
+ if(end_pixel_y)
+ final_pixel_y = end_pixel_y
+
+ var/direction = get_dir(src, A)
+ if(direction & NORTH)
+ pixel_y_diff = 8
+ else if(direction & SOUTH)
+ pixel_y_diff = -8
+
+ if(direction & EAST)
+ pixel_x_diff = 8
+ else if(direction & WEST)
+ pixel_x_diff = -8
+
+ animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2)
+ animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2)
+
+/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item)
+ var/image/I
+ if(visual_effect_icon)
+ I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1)
+ else if(used_item)
+ I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1)
+
+ // Scale the icon.
+ I.transform *= 0.75
+ // The icon should not rotate.
+ I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
+
+ // Set the direction of the icon animation.
+ var/direction = get_dir(src, A)
+ if(direction & NORTH)
+ I.pixel_y = -16
+ else if(direction & SOUTH)
+ I.pixel_y = 16
+
+ if(direction & EAST)
+ I.pixel_x = -16
+ else if(direction & WEST)
+ I.pixel_x = 16
+
+ if(!direction) // Attacked self?!
+ I.pixel_z = 16
+
+ if(!I)
+ return
+
+ flick_overlay(I, GLOB.clients, 5) // 5 ticks/half a second
+
+ // And animate the attack!
+ animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3)
+
+/atom/movable/vv_get_dropdown()
+ . = ..()
+ . -= "Jump to"
+ .["Follow"] = "?_src_=holder;adminplayerobservefollow=\ref[src]"
+
+/atom/movable/proc/ex_check(ex_id)
+ if(!ex_id)
+ return TRUE
+ LAZYINITLIST(acted_explosions)
+ if(ex_id in acted_explosions)
+ return FALSE
+ acted_explosions += ex_id
+ return TRUE
+
+/atom/movable/proc/float(on)
+ if(throwing)
+ return
+ if(on && !floating)
+ animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1)
+ sleep(10)
+ animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1)
+ floating = TRUE
+ else if (!on && floating)
+ animate(src, pixel_y = initial(pixel_y), time = 10)
+ floating = FALSE
+
+/* Stationloving
+*
+* A stationloving atom will always teleport back to the station
+* if it ever leaves the station z-levels or Centcom. It will also,
+* when Destroy() is called, will teleport to a random turf on the
+* station.
+*
+* The turf is guaranteed to be "safe" for normal humans, probably.
+* If the station is SUPER SMASHED UP, it might not work.
+*
+* Here are some important procs:
+* relocate()
+* moves the atom to a safe turf on the station
+*
+* check_in_bounds()
+* regularly called and checks if `in_bounds()` returns true. If false, it
+* triggers a `relocate()`.
+*
+* in_bounds()
+* By default, checks that the atom's z is the station z or centcom.
+*/
+
+/atom/movable/proc/set_stationloving(state, inform_admins=FALSE)
+ var/currently = HAS_SECONDARY_FLAG(src, STATIONLOVING)
+
+ if(inform_admins)
+ SET_SECONDARY_FLAG(src, INFORM_ADMINS_ON_RELOCATE)
+ else
+ CLEAR_SECONDARY_FLAG(src, INFORM_ADMINS_ON_RELOCATE)
+
+ if(state == currently)
+ return
+ else if(!state)
+ STOP_PROCESSING(SSinbounds, src)
+ CLEAR_SECONDARY_FLAG(src, STATIONLOVING)
+ else
+ START_PROCESSING(SSinbounds, src)
+ SET_SECONDARY_FLAG(src, STATIONLOVING)
+
+/atom/movable/proc/relocate()
+ var/targetturf = find_safe_turf(ZLEVEL_STATION)
+ if(!targetturf)
+ if(GLOB.blobstart.len > 0)
+ targetturf = get_turf(pick(GLOB.blobstart))
+ else
+ throw EXCEPTION("Unable to find a blobstart landmark")
+
+ if(ismob(loc))
+ var/mob/M = loc
+ M.transferItemToLoc(src, targetturf, TRUE) //nodrops disks when?
+ else if(istype(loc, /obj/item/weapon/storage))
+ var/obj/item/weapon/storage/S = loc
+ S.remove_from_storage(src, targetturf)
+ else
+ forceMove(targetturf)
+ // move the disc, so ghosts remain orbiting it even if it's "destroyed"
+ return targetturf
+
+/atom/movable/proc/check_in_bounds()
+ if(in_bounds())
+ return
+ else
+ var/turf/currentturf = get_turf(src)
+ get(src, /mob) << "You can't help but feel that you just lost something back there..."
+ var/turf/targetturf = relocate()
+ log_game("[src] has been moved out of bounds in [COORD(currentturf)]. Moving it to [COORD(targetturf)].")
+ if(HAS_SECONDARY_FLAG(src, INFORM_ADMINS_ON_RELOCATE))
+ message_admins("[src] has been moved out of bounds in [ADMIN_COORDJMP(currentturf)]. Moving it to [ADMIN_COORDJMP(targetturf)].")
+
+/atom/movable/proc/in_bounds()
+ . = FALSE
+ var/turf/currentturf = get_turf(src)
+ if(currentturf && (currentturf.z == ZLEVEL_CENTCOM || currentturf.z == ZLEVEL_STATION))
+ . = TRUE
+
+
+/* Language procs */
+/atom/movable/proc/grant_language(datum/language/dt)
+ LAZYINITLIST(languages)
+ languages[dt] = TRUE
+
+/atom/movable/proc/grant_all_languages(omnitongue=FALSE)
+ for(var/la in subtypesof(/datum/language))
+ grant_language(la)
+
+ if(omnitongue)
+ SET_SECONDARY_FLAG(src, OMNITONGUE)
+
+/atom/movable/proc/get_random_understood_language()
+ var/list/possible = list()
+ for(var/dt in languages)
+ possible += dt
+ . = safepick(possible)
+
+/atom/movable/proc/remove_language(datum/language/dt)
+ LAZYREMOVE(languages, dt)
+
+/atom/movable/proc/remove_all_languages()
+ LAZYCLEARLIST(languages)
+
+/atom/movable/proc/has_language(datum/language/dt)
+ . = is_type_in_typecache(dt, languages)
+
+/atom/movable/proc/can_speak_in_language(datum/language/dt)
+ . = has_language(dt)
+ if(only_speaks_language && !HAS_SECONDARY_FLAG(src, OMNITONGUE))
+ . = . && ispath(only_speaks_language, dt)
+
+/atom/movable/proc/get_default_language()
+ // if no language is specified, and we want to say() something, which
+ // language do we use?
+ var/datum/language/chosen_langtype
+ var/highest_priority
+
+ for(var/lt in languages)
+ var/datum/language/langtype = lt
+ if(!can_speak_in_language(langtype))
+ continue
+
+ var/pri = initial(langtype.default_priority)
+ if(!highest_priority || (pri > highest_priority))
+ chosen_langtype = langtype
+ highest_priority = pri
+
+ . = chosen_langtype
\ No newline at end of file