Files
Citadel-Station-13-RP/code/game/atoms_movable.dm
Zandario 5a7f357b85 CitadelRP (#4108)
* dme and dmb

* Heeeere we gooo

* fasease

* Continued.

* AAAAAAAAAAAAAAAAAAAAAAAA

* Brain going numb

* agh

* The suffering will be worth it... right?

* Going blank...

* Am I done?

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* Fixes

* Should be everything I care about right now.
2022-05-26 01:37:55 -07:00

502 lines
14 KiB
Plaintext

/atom/movable
layer = OBJ_LAYER
appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER
/// Whatever we're pulling.
var/atom/movable/pulling
/// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour
var/generic_canpass = TRUE
/// 0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
var/moving_diagonally = 0
/// attempt to resume grab after moving instead of before. This is what atom/movable is pulling us during move-from-pulling.
var/atom/movable/moving_from_pull
/// Direction of our last move.
var/last_move = NONE
/// Which direction we're drifting
var/inertia_dir = NONE
/// Only set while drifting, last location we were while drifting
var/atom/inertia_last_loc
/// If we're moving from no-grav drifting
var/inertia_moving = FALSE
/// Next world.time we should move from no-grav drifting
var/inertia_next_move = 0
/// Delay between each drifting move.
var/inertia_move_delay = 5
/// Movement types, see __DEFINES/flags/movement.dm
var/movement_type = GROUND
/// The orbiter component of the thing we're orbiting.
var/datum/component/orbiter/orbiting
///Used for the calculate_adjacencies proc for icon smoothing.
var/can_be_unanchored = FALSE
/// Our default glide_size.
var/default_glide_size = 0
/// our default perspective - if none, a temporary one will be generated when a mob requires it
var/datum/perspective/self_perspective
var/anchored = FALSE
var/move_speed = 10
var/l_move_time = 1
var/m_flag = 1
var/throwing = FALSE
var/thrower
var/turf/throw_source = null
var/throw_speed = 2
var/throw_range = 7
var/moved_recently = FALSE
var/mob/pulledby = null
/// Used to specify the item state for the on-mob overlays.
var/item_state = null
/// Used to scale icons up or down horizonally in update_transform().
var/icon_scale_x = 1
/// Used to scale icons up or down vertically in update_transform().
var/icon_scale_y = 1
/// Used to rotate icons in update_transform()
var/icon_rotation = 0
var/icon_expected_height = 32
var/icon_expected_width = 32
var/old_x = 0
var/old_y = 0
/// Used for vehicles and other things.
var/datum/riding/riding_datum
/// Does the atom spin when thrown.
var/does_spin = TRUE
///If we're cloaked or not.
var/cloaked = FALSE
/// The image we use for our client to let them see where we are.
var/image/cloaked_selfimage
/// Reference to atom being orbited.
var/atom/orbit_target
/atom/movable/Destroy(force)
. = ..()
if(reagents)
QDEL_NULL(reagents)
unbuckle_all_mobs(force = TRUE)
for(var/atom/movable/AM in contents)
qdel(AM)
var/turf/un_opaque
if(opacity && isturf(loc))
un_opaque = loc
// kick perspectives before moving
if(self_perspective)
QDEL_NULL(self_perspective)
moveToNullspace()
if(un_opaque)
un_opaque.recalc_atom_opacity()
if(pulledby)
pulledby.stop_pulling()
if(pulling)
stop_pulling()
if(riding_datum)
QDEL_NULL(riding_datum)
/atom/movable/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
if(locs && locs.len >= 2) // If something is standing on top of us, let them pass.
if(mover.loc in locs)
. = TRUE
return .
/////////////////////////////////////////////////////////////////
//called when src is thrown into hit_atom
/atom/movable/proc/throw_impact(atom/hit_atom, var/speed)
if(istype(hit_atom,/mob/living))
var/mob/living/M = hit_atom
if(M.buckled == src)
return // Don't hit the thing we're buckled to.
M.hitby(src,speed)
else if(isobj(hit_atom))
var/obj/O = hit_atom
if(!O.anchored)
step(O, src.last_move)
O.hitby(src,speed)
else if(isturf(hit_atom))
src.throwing = 0
var/turf/T = hit_atom
T.hitby(src,speed)
//decided whether a movable atom being thrown can pass through the turf it is in.
/atom/movable/proc/hit_check(var/speed)
if(src.throwing)
for(var/atom/A in get_turf(src))
if(A == src) continue
if(istype(A,/mob/living))
if(A:lying) continue
src.throw_impact(A,speed)
if(isobj(A))
if(!A.density || A.throwpass)
continue
// Special handling of windows, which are dense but block only from some directions
if(istype(A, /obj/structure/window))
var/obj/structure/window/W = A
if (!W.is_fulltile() && !(turn(src.last_move, 180) & A.dir))
continue
// Same thing for (closed) windoors, which have the same problem
else if(istype(A, /obj/machinery/door/window) && !(turn(src.last_move, 180) & A.dir))
continue
src.throw_impact(A,speed)
/atom/movable/proc/throw_at(atom/target, range, speed, thrower)
if(!target || !src)
return 0
if(target.z != src.z)
return 0
//use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
src.throwing = 1
src.thrower = thrower
src.throw_source = get_turf(src) //store the origin turf
src.pixel_z = 0
if(usr)
if(HULK in usr.mutations)
src.throwing = 2 // really strong throw!
var/dist_travelled = 0
var/dist_since_sleep = 0
var/area/a = get_area(src.loc)
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/dx
if (target.x > src.x)
dx = EAST
else
dx = WEST
var/dy
if (target.y > src.y)
dy = NORTH
else
dy = SOUTH
var/error
var/major_dir
var/major_dist
var/minor_dir
var/minor_dist
if(dist_x > dist_y)
error = dist_x/2 - dist_y
major_dir = dx
major_dist = dist_x
minor_dir = dy
minor_dist = dist_y
else
error = dist_y/2 - dist_x
major_dir = dy
major_dist = dist_y
minor_dir = dx
minor_dist = dist_x
while(src && target && src.throwing && istype(src.loc, /turf) \
&& ((abs(target.x - src.x)+abs(target.y - src.y) > 0 && dist_travelled < range) \
|| (a && a.has_gravity == 0) \
|| istype(src.loc, /turf/space)))
// only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up
var/atom/step
if(error >= 0)
step = get_step(src, major_dir)
error -= minor_dist
else
step = get_step(src, minor_dir)
error += major_dist
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
src.Move(step)
hit_check(speed)
dist_travelled++
dist_since_sleep++
if(dist_since_sleep >= speed)
dist_since_sleep = 0
sleep(1)
a = get_area(src.loc)
// and yet it moves
if(src.does_spin)
src.SpinAnimation(speed = 4, loops = 1)
//done throwing, either because it hit something or it finished moving
if(isobj(src)) src.throw_impact(get_turf(src),speed)
src.throwing = 0
src.thrower = null
src.throw_source = null
fall()
//Overlays
/atom/movable/overlay
var/atom/master = null
anchored = 1
/atom/movable/overlay/attackby(a, b)
if (src.master)
return src.master.attackby(a, b)
return
/atom/movable/overlay/attack_hand(a, b, c)
if (src.master)
return src.master.attack_hand(a, b, c)
return
/atom/movable/proc/touch_map_edge()
if(z in GLOB.using_map.sealed_levels)
return
if(GLOB.using_map.use_overmap)
overmap_spacetravel(get_turf(src), src)
return
var/move_to_z = src.get_transit_zlevel()
if(move_to_z)
var/new_z = move_to_z
var/new_x
var/new_y
if(x <= TRANSITIONEDGE)
new_x = world.maxx - TRANSITIONEDGE - 2
new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
else if (x >= (world.maxx - TRANSITIONEDGE + 1))
new_x = TRANSITIONEDGE + 1
new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
else if (y <= TRANSITIONEDGE)
new_y = world.maxy - TRANSITIONEDGE -2
new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
else if (y >= (world.maxy - TRANSITIONEDGE + 1))
new_y = TRANSITIONEDGE + 1
new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
if(SSticker && istype(SSticker.mode, /datum/game_mode/nuclear)) // Only really care if the game mode is nuclear
var/datum/game_mode/nuclear/G = SSticker.mode
G.check_nuke_disks()
var/turf/T = locate(new_x, new_y, new_z)
if(istype(T))
forceMove(T)
//by default, transition randomly to another zlevel
/atom/movable/proc/get_transit_zlevel()
var/list/candidates = GLOB.using_map.accessible_z_levels.Copy()
candidates.Remove("[src.z]")
if(!candidates.len)
return null
return text2num(pickweight(candidates))
// Returns the current scaling of the sprite.
// Note this DOES NOT measure the height or width of the icon, but returns what number is being multiplied with to scale the icons, if any.
/atom/movable/proc/get_icon_scale_x()
return icon_scale_x
/atom/movable/proc/get_icon_scale_y()
return icon_scale_y
/atom/movable/proc/update_transform()
var/matrix/M = matrix()
M.Scale(icon_scale_x, icon_scale_y)
M.Turn(icon_rotation)
src.transform = M
// Use this to set the object's scale.
/atom/movable/proc/adjust_scale(new_scale_x, new_scale_y)
if(isnull(new_scale_y))
new_scale_y = new_scale_x
if(new_scale_x != 0)
icon_scale_x = new_scale_x
if(new_scale_y != 0)
icon_scale_y = new_scale_y
update_transform()
/atom/movable/proc/adjust_rotation(new_rotation)
icon_rotation = new_rotation
update_transform()
// Called when touching a lava tile.
/atom/movable/proc/lava_act()
fire_act(null, 10000, 1000)
//Called when touching an acid pool.
/atom/movable/proc/acid_act()
//Called when touching a blood pool.
/atom/movable/proc/blood_act()
blood_act(null, 500, 50)
/**
* Sets our movement type.
*/
/atom/movable/proc/set_movement_type(new_movetype)
movement_type = new_movetype
/atom/movable/proc/Bump_vr(var/atom/A, yes)
return
/atom/movable/setDir(newdir)
. = ..(newdir)
if(riding_datum)
riding_datum.handle_vehicle_offsets()
/atom/movable/relaymove(mob/user, direction)
. = ..()
if(riding_datum)
riding_datum.handle_ride(user, direction)
// Procs to cloak/uncloak
/atom/movable/proc/cloak()
if(cloaked)
return FALSE
cloaked = TRUE
. = TRUE // We did work
var/static/animation_time = 1 SECOND
cloaked_selfimage = get_cloaked_selfimage()
//Wheeee
cloak_animation(animation_time)
//Needs to be last so people can actually see the effect before we become invisible
if(cloaked) // Ensure we are still cloaked after the animation delay
plane = CLOAKED_PLANE
/atom/movable/proc/uncloak()
if(!cloaked)
return FALSE
cloaked = FALSE
. = TRUE // We did work
var/static/animation_time = 1 SECOND
QDEL_NULL(cloaked_selfimage)
//Needs to be first so people can actually see the effect, so become uninvisible first
plane = initial(plane)
//Oooooo
uncloak_animation(animation_time)
// Animations for cloaking/uncloaking
/atom/movable/proc/cloak_animation(var/length = 1 SECOND)
//Save these
var/initial_alpha = alpha
//Animate alpha fade
animate(src, alpha = 0, time = length)
//Animate a cloaking effect
var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it.
filters += filter(type="wave", x = 0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS)
animate(filters[our_filter], offset = 1, size = 8, time = length, flags = ANIMATION_PARALLEL)
//Wait for animations to finish
sleep(length+5)
//Remove those
filters -= filter(type="wave", x = 0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS)
//Back to original alpha
alpha = initial_alpha
/atom/movable/proc/uncloak_animation(var/length = 1 SECOND)
//Save these
var/initial_alpha = alpha
//Put us back to normal, but no alpha
alpha = 0
//Animate alpha fade up
animate(src, alpha = initial_alpha, time = length)
//Animate a cloaking effect
var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it.
filters += filter(type="wave", x=0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS)
animate(filters[our_filter], offset = 0, size = 0, time = length, flags = ANIMATION_PARALLEL)
//Wait for animations to finish
sleep(length+5)
//Remove those
filters -= filter(type="wave", x=0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS)
// So cloaked things can see themselves, if necessary
/atom/movable/proc/get_cloaked_selfimage()
var/icon/selficon = icon(icon, icon_state)
selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White
var/image/selfimage = image(selficon)
selfimage.color = "#0000FF"
selfimage.alpha = 100
selfimage.layer = initial(layer)
selfimage.plane = initial(plane)
selfimage.loc = src
return selfimage
/atom/movable/proc/ghost_tag(text)
var/atom/movable/ghost_tag_container/G = locate() in vis_contents
if(!length(text) || !istext(text))
if(G)
qdel(G)
return
if(!G)
G = new(src)
G.master = src
// for the love of god macro this when we get runechat
G.maptext = "<center><span style=\"font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1;\">[text]</span></center>"
G.maptext_height = 256
G.maptext_width = 256
G.maptext_x = -128 + (world.icon_size * 0.5)
G.maptext_y = 32
G.plane = PLANE_GHOSTS
vis_contents += G
if(G.loc != src)
G.forceMove(src)
return G
/atom/movable/ghost_tag_container
var/atom/movable/master
/atom/movable/ghost_tag_container/Destroy()
if(istype(master))
master.vis_contents -= src
master = null
return ..()
/atom/movable/proc/get_bullet_impact_effect_type()
return BULLET_IMPACT_NONE
/**
* get perspective to use when shifting eye to us,
*/
/atom/movable/proc/get_perspective()
return self_perspective || temporary_perspective()
/**
* gets a tempoerary perspective for ourselves
*/
/atom/movable/proc/temporary_perspective()
var/datum/perspective/self/temporary/P = new
P.eye = src
return P
/**
* make a permanent self perspective
*/
/atom/movable/proc/make_perspective()
ASSERT(!self_perspective)
. = self_perspective = new /datum/perspective/self
self_perspective.eye = src
/**
* ensure we have a self perspective
*/
/atom/movable/proc/ensure_self_perspective()
if(!self_perspective)
make_perspective()