You can now move an object into nullspace, which will call exited on it's parent and parent area but will not call any entered. Note that you cannot pass null into forceMove it will crash, instead use the new proc that will call the underlying logic with a null destination Some of the force move procs have been refactored to check that their parent move succeeded before doing updates
721 lines
21 KiB
Plaintext
721 lines
21 KiB
Plaintext
/atom/movable
|
|
layer = OBJ_LAYER
|
|
var/last_move = null
|
|
var/anchored = FALSE
|
|
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/initial_language_holder = /datum/language_holder
|
|
var/datum/language_holder/language_holder
|
|
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
|
|
switch(var_name)
|
|
if("x")
|
|
var/turf/T = locate(var_value, y, z)
|
|
if(T)
|
|
forceMove(T)
|
|
return TRUE
|
|
return FALSE
|
|
if("y")
|
|
var/turf/T = locate(x, var_value, z)
|
|
if(T)
|
|
forceMove(T)
|
|
return TRUE
|
|
return FALSE
|
|
if("z")
|
|
var/turf/T = locate(x, y, var_value)
|
|
if(T)
|
|
forceMove(T)
|
|
return TRUE
|
|
return FALSE
|
|
if("loc")
|
|
if(var_value == null || istype(var_value, /atom))
|
|
forceMove(var_value)
|
|
return TRUE
|
|
return FALSE
|
|
return ..()
|
|
|
|
/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)
|
|
SendSignal(COMSIG_MOVABLE_MOVED, 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_1 & CLEAN_ON_MOVE_1)
|
|
clean_on_move()
|
|
|
|
var/datum/proximity_monitor/proximity_monitor = src.proximity_monitor
|
|
if(proximity_monitor)
|
|
proximity_monitor.HandleMove()
|
|
|
|
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, "<span class='danger'>[src] cleans your face!</span>")
|
|
|
|
/atom/movable/Destroy(force)
|
|
var/inform_admins = (flags_2 & INFORM_ADMINS_ON_RELOCATE_2)
|
|
var/stationloving = (flags_2 & STATIONLOVING_2)
|
|
|
|
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)
|
|
|
|
QDEL_NULL(proximity_monitor)
|
|
QDEL_NULL(language_holder)
|
|
|
|
unbuckle_all_mobs(force=1)
|
|
|
|
. = ..()
|
|
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
|
|
//oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called!
|
|
/atom/movable/Crossed(atom/movable/AM, oldloc)
|
|
SendSignal(COMSIG_MOVABLE_CROSSED, AM)
|
|
|
|
|
|
//This is tg's equivalent to the byond bump, it used to be called bump with a second arg
|
|
//to differentiate it, naturally everyone forgot about this immediately and so some things
|
|
//would bump twice, so now it's called Collide
|
|
/atom/movable/proc/Collide(atom/A)
|
|
SendSignal(COMSIG_MOVABLE_COLLIDE, A)
|
|
if(A)
|
|
if(throwing)
|
|
throwing.hit_atom(A)
|
|
. = 1
|
|
if(!A || QDELETED(A))
|
|
return
|
|
A.CollidedWith(src)
|
|
|
|
/atom/movable/proc/forceMove(atom/destination)
|
|
. = FALSE
|
|
if(destination)
|
|
. = doMove(destination)
|
|
else
|
|
CRASH("No valid destination passed into forceMove")
|
|
|
|
/atom/movable/proc/moveToNullspace()
|
|
return doMove(null)
|
|
|
|
/atom/movable/proc/doMove(atom/destination)
|
|
. = FALSE
|
|
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, oldloc)
|
|
|
|
Moved(oldloc, 0)
|
|
. = TRUE
|
|
|
|
//If no destination, move the atom into nullspace (don't do this unless you know what you're doing)
|
|
else
|
|
. = TRUE
|
|
var/atom/oldloc = loc
|
|
var/area/old_area = get_area(oldloc)
|
|
oldloc.Exited(src, null)
|
|
if(old_area)
|
|
old_area.Exited(src, null)
|
|
loc = null
|
|
|
|
/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(.)
|
|
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
|
|
SendSignal(COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
|
|
return hit_atom.hitby(src)
|
|
|
|
/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, 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 this returns FALSE then callback will not be called.
|
|
. = FALSE
|
|
if (!target || (flags_1 & NODROP_1) || 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.
|
|
|
|
. = TRUE // No failure conditions past this point.
|
|
|
|
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)
|
|
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)
|
|
|
|
if(A == src)
|
|
return //don't do an animation if attacking self
|
|
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;[HrefToken()];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 = (flags_2 & STATIONLOVING_2)
|
|
|
|
if(inform_admins)
|
|
flags_2 |= INFORM_ADMINS_ON_RELOCATE_2
|
|
else
|
|
flags_2 &= ~INFORM_ADMINS_ON_RELOCATE_2
|
|
|
|
if(state == currently)
|
|
return
|
|
else if(!state)
|
|
STOP_PROCESSING(SSinbounds, src)
|
|
flags_2 &= ~STATIONLOVING_2
|
|
else
|
|
START_PROCESSING(SSinbounds, src)
|
|
flags_2 |= STATIONLOVING_2
|
|
|
|
/atom/movable/proc/relocate()
|
|
var/targetturf = find_safe_turf(ZLEVEL_STATION_PRIMARY)
|
|
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/storage))
|
|
var/obj/item/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)
|
|
to_chat(get(src, /mob), "<span class='danger'>You can't help but feel that you just lost something back there...</span>")
|
|
var/turf/targetturf = relocate()
|
|
log_game("[src] has been moved out of bounds in [COORD(currentturf)]. Moving it to [COORD(targetturf)].")
|
|
if(flags_2 & INFORM_ADMINS_ON_RELOCATE_2)
|
|
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 in GLOB.station_z_levels) || currentturf.z == ZLEVEL_TRANSIT))
|
|
. = TRUE
|
|
|
|
|
|
/* Language procs */
|
|
/atom/movable/proc/get_language_holder(shadow=TRUE)
|
|
if(language_holder)
|
|
return language_holder
|
|
else
|
|
language_holder = new initial_language_holder(src)
|
|
return language_holder
|
|
|
|
/atom/movable/proc/grant_language(datum/language/dt)
|
|
var/datum/language_holder/H = get_language_holder()
|
|
H.grant_language(dt)
|
|
|
|
/atom/movable/proc/grant_all_languages(omnitongue=FALSE)
|
|
var/datum/language_holder/H = get_language_holder()
|
|
H.grant_all_languages(omnitongue)
|
|
|
|
/atom/movable/proc/get_random_understood_language()
|
|
var/datum/language_holder/H = get_language_holder()
|
|
. = H.get_random_understood_language()
|
|
|
|
/atom/movable/proc/remove_language(datum/language/dt)
|
|
var/datum/language_holder/H = get_language_holder()
|
|
H.remove_language(dt)
|
|
|
|
/atom/movable/proc/remove_all_languages()
|
|
var/datum/language_holder/H = get_language_holder()
|
|
H.remove_all_languages()
|
|
|
|
/atom/movable/proc/has_language(datum/language/dt)
|
|
var/datum/language_holder/H = get_language_holder()
|
|
. = H.has_language(dt)
|
|
|
|
/atom/movable/proc/copy_known_languages_from(thing, replace=FALSE)
|
|
var/datum/language_holder/H = get_language_holder()
|
|
. = H.copy_known_languages_from(thing, replace)
|
|
|
|
// Whether an AM can speak in a language or not, independent of whether
|
|
// it KNOWS the language
|
|
/atom/movable/proc/could_speak_in_language(datum/language/dt)
|
|
. = TRUE
|
|
|
|
/atom/movable/proc/can_speak_in_language(datum/language/dt)
|
|
var/datum/language_holder/H = get_language_holder()
|
|
|
|
if(!H.has_language(dt))
|
|
return FALSE
|
|
else if(H.omnitongue)
|
|
return TRUE
|
|
else if(could_speak_in_language(dt) && (!H.only_speaks_language || H.only_speaks_language == dt))
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
/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_holder/H = get_language_holder()
|
|
|
|
if(H.selected_default_language)
|
|
if(can_speak_in_language(H.selected_default_language))
|
|
return H.selected_default_language
|
|
else
|
|
H.selected_default_language = null
|
|
|
|
|
|
var/datum/language/chosen_langtype
|
|
var/highest_priority
|
|
|
|
for(var/lt in H.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
|
|
|
|
H.selected_default_language = .
|
|
. = chosen_langtype
|
|
|
|
/* End language procs */
|
|
/atom/movable/proc/ConveyorMove(movedir)
|
|
set waitfor = FALSE
|
|
if(!anchored && has_gravity())
|
|
step(src, movedir)
|
|
|
|
//Returns an atom's power cell, if it has one. Overload for individual items.
|
|
/atom/movable/proc/get_cell()
|
|
return
|
|
|
|
/atom/movable/proc/can_be_pulled(user)
|
|
if(src == user || !isturf(loc))
|
|
return FALSE
|
|
if(anchored || throwing)
|
|
return FALSE
|
|
return TRUE |