mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
310 lines
9.0 KiB
Plaintext
310 lines
9.0 KiB
Plaintext
/atom/movable
|
|
layer = 3
|
|
appearance_flags = TILE_BOUND|PIXEL_SCALE
|
|
var/last_move = null
|
|
var/anchored = 0
|
|
// var/elevation = 2 - not used anywhere
|
|
var/move_speed = 10
|
|
var/l_move_time = 1
|
|
var/m_flag = 1
|
|
var/throwing = 0
|
|
var/thrower
|
|
var/turf/throw_source = null
|
|
var/throw_speed = 2
|
|
var/throw_range = 7
|
|
var/moved_recently = 0
|
|
var/mob/pulledby = null
|
|
var/item_state = null // Used to specify the item state for the on-mob overlays.
|
|
var/icon_scale = 1 // Used to scale icons up or down in update_transform().
|
|
var/old_x = 0
|
|
var/old_y = 0
|
|
var/auto_init = 1
|
|
|
|
/atom/movable/New()
|
|
..()
|
|
if(auto_init && ticker && ticker.current_state == GAME_STATE_PLAYING)
|
|
if(SScreation && SScreation.map_loading) // If a map is being loaded, newly created objects need to wait for it to finish.
|
|
SScreation.atoms_needing_initialize += src
|
|
else
|
|
initialize()
|
|
|
|
/atom/movable/Destroy()
|
|
. = ..()
|
|
if(reagents)
|
|
qdel(reagents)
|
|
reagents = null
|
|
for(var/atom/movable/AM in contents)
|
|
qdel(AM)
|
|
var/turf/un_opaque
|
|
if(opacity && isturf(loc))
|
|
un_opaque = loc
|
|
|
|
loc = null
|
|
if(un_opaque)
|
|
un_opaque.recalc_atom_opacity()
|
|
if (pulledby)
|
|
if (pulledby.pulling == src)
|
|
pulledby.pulling = null
|
|
pulledby = null
|
|
|
|
/atom/movable/proc/initialize()
|
|
if(QDELETED(src))
|
|
crash_with("GC: -- [type] had initialize() called after qdel() --")
|
|
|
|
/atom/movable/Bump(var/atom/A, yes)
|
|
if(src.throwing)
|
|
src.throw_impact(A)
|
|
src.throwing = 0
|
|
|
|
spawn(0)
|
|
if ((A && yes))
|
|
A.last_bumped = world.time
|
|
A.Bumped(src)
|
|
return
|
|
..()
|
|
return
|
|
|
|
/atom/movable/proc/forceMove(atom/destination)
|
|
if(loc == destination)
|
|
return 0
|
|
var/is_origin_turf = isturf(loc)
|
|
var/is_destination_turf = isturf(destination)
|
|
// It is a new area if:
|
|
// Both the origin and destination are turfs with different areas.
|
|
// When either origin or destination is a turf and the other is not.
|
|
var/is_new_area = (is_origin_turf ^ is_destination_turf) || (is_origin_turf && is_destination_turf && loc.loc != destination.loc)
|
|
|
|
var/atom/origin = loc
|
|
loc = destination
|
|
|
|
if(origin)
|
|
origin.Exited(src, destination)
|
|
if(is_origin_turf)
|
|
for(var/atom/movable/AM in origin)
|
|
AM.Uncrossed(src)
|
|
if(is_new_area && is_origin_turf)
|
|
origin.loc.Exited(src, destination)
|
|
|
|
if(destination)
|
|
destination.Entered(src, origin)
|
|
if(is_destination_turf) // If we're entering a turf, cross all movable atoms
|
|
for(var/atom/movable/AM in loc)
|
|
if(AM != src)
|
|
AM.Crossed(src)
|
|
if(is_new_area && is_destination_turf)
|
|
destination.loc.Entered(src, origin)
|
|
return 1
|
|
|
|
//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_full_window() && !(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
|
|
//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
|
|
|
|
if(usr)
|
|
if(HULK in usr.mutations)
|
|
src.throwing = 2 // really strong throw!
|
|
|
|
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/dist_travelled = 0
|
|
var/dist_since_sleep = 0
|
|
var/area/a = get_area(src.loc)
|
|
if(dist_x > dist_y)
|
|
var/error = dist_x/2 - dist_y
|
|
|
|
|
|
|
|
while(src && target &&((((src.x < target.x && dx == EAST) || (src.x > target.x && dx == WEST)) && dist_travelled < range) || (a && a.has_gravity == 0) || istype(src.loc, /turf/space)) && src.throwing && istype(src.loc, /turf))
|
|
// 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
|
|
if(error < 0)
|
|
var/atom/step = get_step(src, dy)
|
|
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)
|
|
error += dist_x
|
|
dist_travelled++
|
|
dist_since_sleep++
|
|
if(dist_since_sleep >= speed)
|
|
dist_since_sleep = 0
|
|
sleep(1)
|
|
else
|
|
var/atom/step = get_step(src, dx)
|
|
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)
|
|
error -= dist_y
|
|
dist_travelled++
|
|
dist_since_sleep++
|
|
if(dist_since_sleep >= speed)
|
|
dist_since_sleep = 0
|
|
sleep(1)
|
|
a = get_area(src.loc)
|
|
else
|
|
var/error = dist_y/2 - dist_x
|
|
while(src && target &&((((src.y < target.y && dy == NORTH) || (src.y > target.y && dy == SOUTH)) && dist_travelled < range) || (a && a.has_gravity == 0) || istype(src.loc, /turf/space)) && src.throwing && istype(src.loc, /turf))
|
|
// 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
|
|
if(error < 0)
|
|
var/atom/step = get_step(src, dx)
|
|
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)
|
|
error += dist_y
|
|
dist_travelled++
|
|
dist_since_sleep++
|
|
if(dist_since_sleep >= speed)
|
|
dist_since_sleep = 0
|
|
sleep(1)
|
|
else
|
|
var/atom/step = get_step(src, dy)
|
|
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)
|
|
error -= dist_x
|
|
dist_travelled++
|
|
dist_since_sleep++
|
|
if(dist_since_sleep >= speed)
|
|
dist_since_sleep = 0
|
|
sleep(1)
|
|
|
|
a = get_area(src.loc)
|
|
|
|
//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
|
|
|
|
|
|
//Overlays
|
|
/atom/movable/overlay
|
|
var/atom/master = null
|
|
anchored = 1
|
|
|
|
/atom/movable/overlay/New()
|
|
for(var/x in src.verbs)
|
|
src.verbs -= x
|
|
..()
|
|
|
|
/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 using_map.sealed_levels)
|
|
return
|
|
|
|
if(config.use_overmap)
|
|
overmap_spacetravel(get_turf(src), src)
|
|
return
|
|
|
|
var/move_to_z = src.get_transit_zlevel()
|
|
if(move_to_z)
|
|
z = move_to_z
|
|
|
|
if(x <= TRANSITIONEDGE)
|
|
x = world.maxx - TRANSITIONEDGE - 2
|
|
y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
|
|
|
|
else if (x >= (world.maxx - TRANSITIONEDGE + 1))
|
|
x = TRANSITIONEDGE + 1
|
|
y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
|
|
|
|
else if (y <= TRANSITIONEDGE)
|
|
y = world.maxy - TRANSITIONEDGE -2
|
|
x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
|
|
|
|
else if (y >= (world.maxy - TRANSITIONEDGE + 1))
|
|
y = TRANSITIONEDGE + 1
|
|
x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
|
|
|
|
if(ticker && istype(ticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear
|
|
var/datum/game_mode/nuclear/G = ticker.mode
|
|
G.check_nuke_disks()
|
|
|
|
spawn(0)
|
|
if(loc) loc.Entered(src)
|
|
|
|
//by default, transition randomly to another zlevel
|
|
/atom/movable/proc/get_transit_zlevel()
|
|
var/list/candidates = using_map.accessible_z_levels.Copy()
|
|
candidates.Remove("[src.z]")
|
|
|
|
if(!candidates.len)
|
|
return null
|
|
return text2num(pickweight(candidates))
|
|
|
|
/atom/movable/proc/update_transform()
|
|
var/matrix/M = matrix()
|
|
M.Scale(icon_scale)
|
|
src.transform = M
|
|
|
|
// Use this to set the object's scale.
|
|
/atom/movable/proc/adjust_scale(new_scale)
|
|
icon_scale = new_scale
|
|
update_transform()
|