mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-21 03:51:49 +01:00
Ported SSThrowing from TG, to handle throwings. Updated movement system to the latest iteration, made it a datum as per latest iteration. Updated pass/hit handling of atoms, introduced pass_flag_self to determine what atoms allow to pass. Moved procs and defines around to make them more organized.
796 lines
27 KiB
Plaintext
796 lines
27 KiB
Plaintext
/atom/movable
|
|
layer = OBJ_LAYER
|
|
glide_size = 6
|
|
animate_movement = SLIDE_STEPS
|
|
appearance_flags = DEFAULT_APPEARANCE_FLAGS | TILE_BOUND
|
|
|
|
var/last_move = null
|
|
var/anchored = 0
|
|
var/movable_flags
|
|
|
|
///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/move_speed = 10
|
|
var/l_move_time = 1
|
|
var/datum/thrownthing/throwing = null
|
|
var/throw_speed = 2
|
|
var/throw_range = 7
|
|
var/moved_recently = 0
|
|
var/atom/movable/pulledby = null
|
|
|
|
///Base name of the image used for when the item is in someone's hand. Suffixes are added to this. Doubles as legacy overlay_state.
|
|
var/item_state = null
|
|
|
|
///Base name of the image used for when the item is worn. Suffixes are added to this. Important for icon flipping as _flip is added at the end of the value.
|
|
var/overlay_state = null
|
|
|
|
///Also used on holdable mobs for the same info related to their held version
|
|
var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P)
|
|
|
|
var/can_hold_mob = FALSE
|
|
var/list/contained_mobs
|
|
|
|
///Are we moving with inertia? Mostly used as an optimization
|
|
var/inertia_moving = FALSE
|
|
///Delay in deciseconds between inertia based movement
|
|
var/inertia_move_delay = 5
|
|
|
|
///Holds information about any movement loops currently running/waiting to run on the movable. Lazy, will be null if nothing's going on
|
|
var/datum/movement_packet/move_packet
|
|
|
|
/**
|
|
* an associative lazylist of relevant nested contents by "channel", the list is of the form: list(channel = list(important nested contents of that type))
|
|
* each channel has a specific purpose and is meant to replace potentially expensive nested contents iteration
|
|
* do NOT add channels to this for little reason as it can add considerable memory usage.
|
|
*/
|
|
var/list/important_recursive_contents
|
|
|
|
/// String representing the spatial grid groups we want to be held in.
|
|
/// acts as a key to the list of spatial grid contents types we exist in via SSspatial_grid.spatial_grid_categories.
|
|
/// We do it like this to prevent people trying to mutate them and to save memory on holding the lists ourselves
|
|
var/spatial_grid_key
|
|
|
|
/// Either [EMISSIVE_BLOCK_NONE], [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE]
|
|
var/blocks_emissive = EMISSIVE_BLOCK_NONE
|
|
///Internal holder for emissive blocker object, DO NOT USE DIRECTLY. Use blocks_emissive
|
|
var/mutable_appearance/em_block
|
|
|
|
/atom/movable/Initialize(mapload, ...)
|
|
. = ..()
|
|
update_emissive_blocker()
|
|
if (em_block)
|
|
AddOverlays(em_block)
|
|
|
|
/atom/movable/Destroy(force)
|
|
if(orbiting)
|
|
stop_orbit()
|
|
GLOB.moved_event.unregister_all_movement(loc, src)
|
|
|
|
//Recalculate opacity
|
|
var/turf/T = loc
|
|
if(opacity && istype(T))
|
|
T.recalc_atom_opacity()
|
|
T.reconsider_lights()
|
|
|
|
if(move_packet)
|
|
if(!QDELETED(move_packet))
|
|
qdel(move_packet)
|
|
move_packet = null
|
|
|
|
if(spatial_grid_key)
|
|
SSspatial_grid.force_remove_from_grid(src)
|
|
|
|
QDEL_LAZYLIST(contained_mobs)
|
|
|
|
. = ..()
|
|
|
|
for(var/movable_content in contents)
|
|
qdel(movable_content)
|
|
|
|
//Pretend this is moveToNullspace()
|
|
moveToNullspace()
|
|
loc = null
|
|
|
|
//This absolutely must be after moveToNullspace()
|
|
//We rely on Entered and Exited to manage this list, and the copy of this list that is on any /atom/movable "Containers"
|
|
//If we clear this before the nullspace move, a ref to this object will be hung in any of its movable containers
|
|
LAZYNULL(important_recursive_contents)
|
|
|
|
|
|
vis_locs = null //clears this atom out of all viscontents
|
|
|
|
// Checking length(vis_contents) before cutting has significant speed benefits
|
|
if (length(vis_contents))
|
|
vis_contents.Cut()
|
|
|
|
screen_loc = null
|
|
if(ismob(pulledby))
|
|
var/mob/M = pulledby
|
|
if(M.pulling == src)
|
|
M.pulling = null
|
|
pulledby = null
|
|
|
|
if (bound_overlay)
|
|
QDEL_NULL(bound_overlay)
|
|
|
|
if(em_block)
|
|
QDEL_NULL(em_block)
|
|
|
|
/atom/movable/proc/moveToNullspace()
|
|
. = TRUE
|
|
|
|
var/atom/oldloc = loc
|
|
|
|
if (oldloc)
|
|
loc = null
|
|
var/area/old_area = get_area(oldloc)
|
|
if(isturf(oldloc)) //This checked if it's a multitile, which i have no clue what would be here, so just check for turf
|
|
for(var/atom/old_loc as anything in locs)
|
|
old_loc.Exited(src, NONE)
|
|
else
|
|
oldloc.Exited(src, NONE)
|
|
|
|
if(old_area)
|
|
old_area.Exited(src, NONE)
|
|
|
|
Moved(oldloc, TRUE)
|
|
|
|
// This is called when this atom is prevented from moving by atom/A.
|
|
/atom/movable/proc/Collide(atom/A)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
if(airflow_speed > 0 && airflow_dest)
|
|
airflow_hit(A)
|
|
else
|
|
airflow_speed = 0
|
|
airflow_time = 0
|
|
|
|
if(!QDELETED(throwing))
|
|
. = TRUE
|
|
if(!QDELETED(A))
|
|
throw_impact(A, throwing)
|
|
A.CollidedWith(src)
|
|
throwing?.finalize(hit = TRUE, target = A)
|
|
|
|
else if (!QDELETED(A))
|
|
A.CollidedWith(src)
|
|
|
|
//called when src is thrown into hit_atom
|
|
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
|
if(isliving(hit_atom))
|
|
var/mob/living/M = hit_atom
|
|
M.hitby(src, throwingdatum.speed)
|
|
|
|
else if(isobj(hit_atom))
|
|
var/obj/O = hit_atom
|
|
if(!O.anchored)
|
|
step(O, src.last_move)
|
|
O.hitby(src, throwingdatum.speed)
|
|
|
|
else if(isturf(hit_atom))
|
|
throwing?.finalize(hit = FALSE)
|
|
var/turf/T = hit_atom
|
|
if(T.density)
|
|
step(src, turn(src.last_move, 180))
|
|
if(isliving(src))
|
|
var/mob/living/M = src
|
|
M.turf_collision(T, throwingdatum.speed)
|
|
|
|
//decided whether a movable atom being thrown can pass through the turf it is in.
|
|
/atom/movable/proc/hit_check(var/speed, var/target)
|
|
if(throwing)
|
|
for(var/atom/A in get_turf(src))
|
|
if(A == src)
|
|
continue
|
|
if(isliving(A))
|
|
var/mob/living/M = A
|
|
if(M.lying && M != target)
|
|
continue
|
|
throw_impact(A, speed)
|
|
if(isobj(A))
|
|
if(A.density && !A.throwpass && !A.CanPass(src, target))
|
|
src.throw_impact(A,speed)
|
|
|
|
// Prevents robots dropping their modules
|
|
/atom/movable/proc/dropsafety()
|
|
if(!istype(src.loc))
|
|
return TRUE
|
|
|
|
if (issilicon(src.loc))
|
|
return FALSE
|
|
|
|
if (istype(src.loc, /obj/item/rig_module))
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE, quickstart = TRUE)
|
|
. = FALSE
|
|
|
|
if(QDELETED(src))
|
|
CRASH("Qdeleted thing being thrown around.")
|
|
|
|
if (!target || speed <= 0)
|
|
return
|
|
|
|
if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_THROW, args) & COMPONENT_CANCEL_THROW)
|
|
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 = world.tick_lag //Used to be thrower.cached_multiplicative_slowdown but we don't have this
|
|
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/target_zone
|
|
if(QDELETED(thrower))
|
|
thrower = null //Let's not pass a qdeleting reference if any.
|
|
else
|
|
target_zone = thrower.zone_sel
|
|
|
|
var/datum/thrownthing/thrown_thing = new(src, target, get_dir(src, target), range, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
|
|
|
|
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)
|
|
thrown_thing.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
|
|
thrown_thing.dist_x = dist_x
|
|
thrown_thing.dist_y = dist_y
|
|
thrown_thing.dx = dx
|
|
thrown_thing.dy = dy
|
|
thrown_thing.diagonal_error = dist_x/2 - dist_y
|
|
thrown_thing.start_time = world.time
|
|
|
|
// if(pulledby)
|
|
// pulledby.stop_pulling()
|
|
if (quickstart && (throwing || SSthrowing.state == SS_RUNNING)) //Avoid stack overflow edgecases.
|
|
quickstart = FALSE
|
|
throwing = thrown_thing
|
|
if(spin)
|
|
SpinAnimation(5, 1)
|
|
|
|
SEND_SIGNAL(src, COMSIG_MOVABLE_POST_THROW, thrown_thing, spin)
|
|
SSthrowing.processing[src] = thrown_thing
|
|
if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun))
|
|
SSthrowing.currentrun[src] = thrown_thing
|
|
if (quickstart)
|
|
thrown_thing.tick()
|
|
|
|
/atom/movable/proc/throw_at_random(var/include_own_turf, var/maxrange, var/speed)
|
|
var/list/turfs = RANGE_TURFS(maxrange, src)
|
|
if(!maxrange)
|
|
maxrange = 1
|
|
|
|
if(!include_own_turf)
|
|
turfs -= get_turf(src)
|
|
src.throw_at(pick(turfs), maxrange, speed)
|
|
|
|
/atom/movable/proc/update_emissive_blocker()
|
|
switch (blocks_emissive)
|
|
if (EMISSIVE_BLOCK_GENERIC)
|
|
em_block = fast_emissive_blocker(src)
|
|
if (EMISSIVE_BLOCK_UNIQUE)
|
|
if (!em_block && !QDELING(src))
|
|
appearance_flags |= KEEP_TOGETHER
|
|
render_target = ref(src)
|
|
em_block = emissive_blocker(
|
|
icon = icon,
|
|
icon_state = icon_state,
|
|
appearance_flags = appearance_flags,
|
|
source = render_target
|
|
)
|
|
return em_block
|
|
|
|
/atom/movable/update_icon()
|
|
..()
|
|
UPDATE_OO_IF_PRESENT
|
|
if (em_block)
|
|
CutOverlays(em_block)
|
|
update_emissive_blocker()
|
|
if (em_block)
|
|
AddOverlays(em_block)
|
|
|
|
//Overlays
|
|
/atom/movable/overlay
|
|
var/atom/master = null
|
|
anchored = 1
|
|
|
|
/atom/movable/overlay/New()
|
|
verbs.Cut()
|
|
..()
|
|
|
|
/atom/movable/overlay/Destroy(force)
|
|
master = null
|
|
. = ..()
|
|
|
|
/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 SSatlas.current_map.sealed_levels)
|
|
return
|
|
|
|
if(anchored)
|
|
return
|
|
|
|
if(!GLOB.universe.OnTouchMapEdge(src))
|
|
return
|
|
|
|
if(SSatlas.current_map.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(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()
|
|
|
|
if(loc)
|
|
var/turf/T = loc
|
|
loc.Entered(src)
|
|
if(!T.is_hole)
|
|
fall_impact(text2num(pickweight(list("1" = 60, "2" = 30, "3" = 10))))
|
|
|
|
//by default, transition randomly to another zlevel
|
|
/atom/movable/proc/get_transit_zlevel()
|
|
return SSatlas.current_map.get_transit_zlevel()
|
|
|
|
// 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()
|
|
|
|
// Core movement hooks & procs.
|
|
/atom/movable/proc/forceMove(atom/destination)
|
|
if(!destination)
|
|
return FALSE
|
|
if(loc)
|
|
loc.Exited(src, destination)
|
|
var/old_loc = loc
|
|
loc = destination
|
|
loc.Entered(src, old_loc)
|
|
Moved(old_loc, TRUE)
|
|
|
|
//Zmimic
|
|
if(bound_overlay)
|
|
// The overlay will handle cleaning itself up on non-openspace turfs.
|
|
if (isturf(destination))
|
|
bound_overlay.forceMove(get_step(src, UP))
|
|
if (dir != bound_overlay.dir)
|
|
bound_overlay.set_dir(dir)
|
|
else // Not a turf, so we need to destroy immediately instead of waiting for the destruction timer to proc.
|
|
qdel(bound_overlay)
|
|
|
|
if(opacity)
|
|
updateVisibility(src)
|
|
|
|
//Lighting recalculation
|
|
var/datum/light_source/L
|
|
var/thing
|
|
for (thing in light_sources)
|
|
L = thing
|
|
L.source_atom.update_light()
|
|
|
|
return TRUE
|
|
|
|
/atom/movable/proc/Moved(atom/old_loc, forced)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, forced)
|
|
|
|
update_grid_location(old_loc, src)
|
|
|
|
/atom/movable/proc/update_grid_location(atom/old_loc)
|
|
if(!HAS_SPATIAL_GRID_CONTENTS(src) || !SSspatial_grid.initialized)
|
|
return
|
|
|
|
var/turf/old_turf = get_turf(old_loc)
|
|
var/turf/new_turf = get_turf(src)
|
|
|
|
if(old_turf && new_turf && (old_turf.z != new_turf.z \
|
|
|| ROUND_UP(old_turf.x / SPATIAL_GRID_CELLSIZE) != ROUND_UP(new_turf.x / SPATIAL_GRID_CELLSIZE) \
|
|
|| ROUND_UP(old_turf.y / SPATIAL_GRID_CELLSIZE) != ROUND_UP(new_turf.y / SPATIAL_GRID_CELLSIZE)))
|
|
|
|
SSspatial_grid.exit_cell(src, old_turf)
|
|
SSspatial_grid.enter_cell(src, new_turf)
|
|
|
|
else if(old_turf && !new_turf)
|
|
SSspatial_grid.exit_cell(src, old_turf)
|
|
|
|
else if(new_turf && !old_turf)
|
|
SSspatial_grid.enter_cell(src, new_turf)
|
|
|
|
/atom/movable/Exited(atom/movable/gone, direction)
|
|
. = ..()
|
|
|
|
if(LAZYLEN(gone.important_recursive_contents))
|
|
var/list/nested_locs = get_nested_locs(src) + src
|
|
for(var/channel in gone.important_recursive_contents)
|
|
for(var/atom/movable/location as anything in nested_locs)
|
|
LAZYINITLIST(location.important_recursive_contents)
|
|
var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
|
|
LAZYINITLIST(recursive_contents[channel])
|
|
recursive_contents[channel] -= gone.important_recursive_contents[channel]
|
|
switch(channel)
|
|
if(RECURSIVE_CONTENTS_CLIENT_MOBS, RECURSIVE_CONTENTS_HEARING_SENSITIVE)
|
|
if(!length(recursive_contents[channel]))
|
|
// This relies on a nice property of the linked recursive and gridmap types
|
|
// They're defined in relation to each other, so they have the same value
|
|
SSspatial_grid.remove_grid_awareness(location, channel)
|
|
ASSOC_UNSETEMPTY(recursive_contents, channel)
|
|
UNSETEMPTY(location.important_recursive_contents)
|
|
|
|
if(LAZYLEN(gone.stored_chat_text))
|
|
return_floating_text(gone)
|
|
|
|
if(GLOB.moved_event.is_listening(src, gone, TYPE_PROC_REF(/atom/movable, recursive_move)))
|
|
GLOB.moved_event.unregister(src, gone)
|
|
|
|
GLOB.dir_set_event.unregister(src, gone, TYPE_PROC_REF(/atom, recursive_dir_set))
|
|
|
|
/atom/movable/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
. = ..()
|
|
|
|
if(LAZYLEN(arrived.important_recursive_contents))
|
|
var/list/nested_locs = get_nested_locs(src) + src
|
|
for(var/channel in arrived.important_recursive_contents)
|
|
for(var/atom/movable/location as anything in nested_locs)
|
|
LAZYINITLIST(location.important_recursive_contents)
|
|
var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
|
|
LAZYINITLIST(recursive_contents[channel])
|
|
switch(channel)
|
|
if(RECURSIVE_CONTENTS_CLIENT_MOBS, RECURSIVE_CONTENTS_HEARING_SENSITIVE)
|
|
if(!length(recursive_contents[channel]))
|
|
SSspatial_grid.add_grid_awareness(location, channel)
|
|
recursive_contents[channel] |= arrived.important_recursive_contents[channel]
|
|
|
|
if (LAZYLEN(arrived.stored_chat_text))
|
|
give_floating_text(arrived)
|
|
|
|
if(GLOB.moved_event.has_listeners(arrived) && !GLOB.moved_event.is_listening(src, arrived))
|
|
GLOB.moved_event.register(src, arrived, TYPE_PROC_REF(/atom/movable, recursive_move))
|
|
|
|
if(GLOB.dir_set_event.has_listeners(arrived))
|
|
GLOB.dir_set_event.register(src, arrived, TYPE_PROC_REF(/atom, recursive_dir_set))
|
|
|
|
///allows this movable to hear and adds itself to the important_recursive_contents list of itself and every movable loc its in
|
|
/atom/movable/proc/become_hearing_sensitive(trait_source = TRAIT_GENERIC)
|
|
ADD_TRAIT(src, TRAIT_HEARING_SENSITIVE, trait_source)
|
|
if(!HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
|
|
return
|
|
|
|
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
|
|
LAZYINITLIST(location.important_recursive_contents)
|
|
var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
|
|
if(!length(recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE]))
|
|
SSspatial_grid.add_grid_awareness(location, SPATIAL_GRID_CONTENTS_TYPE_HEARING)
|
|
recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE] += list(src)
|
|
|
|
var/turf/our_turf = get_turf(src)
|
|
SSspatial_grid.add_grid_membership(src, our_turf, SPATIAL_GRID_CONTENTS_TYPE_HEARING)
|
|
|
|
/**
|
|
* removes the hearing sensitivity channel from the important_recursive_contents list of this and all nested locs containing us if there are no more sources of the trait left
|
|
* since RECURSIVE_CONTENTS_HEARING_SENSITIVE is also a spatial grid content type, removes us from the spatial grid if the trait is removed
|
|
*
|
|
* * trait_source - trait source define or ALL, if ALL, force removes hearing sensitivity. if a trait source define, removes hearing sensitivity only if the trait is removed
|
|
*/
|
|
/atom/movable/proc/lose_hearing_sensitivity(trait_source = TRAIT_GENERIC)
|
|
if(!HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
|
|
return
|
|
REMOVE_TRAIT(src, TRAIT_HEARING_SENSITIVE, trait_source)
|
|
if(HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
|
|
return
|
|
|
|
var/turf/our_turf = get_turf(src)
|
|
/// We get our awareness updated by the important recursive contents stuff, here we remove our membership
|
|
SSspatial_grid.remove_grid_membership(src, our_turf, SPATIAL_GRID_CONTENTS_TYPE_HEARING)
|
|
|
|
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
|
|
var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
|
|
recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE] -= src
|
|
if(!length(recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE]))
|
|
SSspatial_grid.remove_grid_awareness(location, SPATIAL_GRID_CONTENTS_TYPE_HEARING)
|
|
ASSOC_UNSETEMPTY(recursive_contents, RECURSIVE_CONTENTS_HEARING_SENSITIVE)
|
|
UNSETEMPTY(location.important_recursive_contents)
|
|
|
|
///allows this movable to know when it has "entered" another area no matter how many movable atoms its stuffed into, uses important_recursive_contents
|
|
/atom/movable/proc/become_area_sensitive(trait_source = TRAIT_GENERIC)
|
|
if(!HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
|
|
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
|
|
LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE, src)
|
|
ADD_TRAIT(src, TRAIT_AREA_SENSITIVE, trait_source)
|
|
|
|
///removes the area sensitive channel from the important_recursive_contents list of this and all nested locs containing us if there are no more source of the trait left
|
|
/atom/movable/proc/lose_area_sensitivity(trait_source = TRAIT_GENERIC)
|
|
if(!HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
|
|
return
|
|
REMOVE_TRAIT(src, TRAIT_AREA_SENSITIVE, trait_source)
|
|
if(HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
|
|
return
|
|
|
|
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
|
|
LAZYREMOVEASSOC(location.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE, src)
|
|
|
|
///propogates ourselves through our nested contents, similar to other important_recursive_contents procs
|
|
///main difference is that client contents need to possibly duplicate recursive contents for the clients mob AND its eye
|
|
/mob/proc/enable_client_mobs_in_contents()
|
|
for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src)
|
|
LAZYINITLIST(movable_loc.important_recursive_contents)
|
|
var/list/recursive_contents = movable_loc.important_recursive_contents // blue hedgehog velocity
|
|
if(!length(recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS]))
|
|
SSspatial_grid.add_grid_awareness(movable_loc, SPATIAL_GRID_CONTENTS_TYPE_CLIENTS)
|
|
LAZYINITLIST(recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS])
|
|
recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS] |= src
|
|
|
|
var/turf/our_turf = get_turf(src)
|
|
/// We got our awareness updated by the important recursive contents stuff, now we add our membership
|
|
SSspatial_grid.add_grid_membership(src, our_turf, SPATIAL_GRID_CONTENTS_TYPE_CLIENTS)
|
|
|
|
///Clears the clients channel of this mob
|
|
/mob/proc/clear_important_client_contents()
|
|
var/turf/our_turf = get_turf(src)
|
|
SSspatial_grid.remove_grid_membership(src, our_turf, SPATIAL_GRID_CONTENTS_TYPE_CLIENTS)
|
|
|
|
for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src)
|
|
LAZYINITLIST(movable_loc.important_recursive_contents)
|
|
var/list/recursive_contents = movable_loc.important_recursive_contents // blue hedgehog velocity
|
|
LAZYINITLIST(recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS])
|
|
recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS] -= src
|
|
if(!length(recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS]))
|
|
SSspatial_grid.remove_grid_awareness(movable_loc, SPATIAL_GRID_CONTENTS_TYPE_CLIENTS)
|
|
ASSOC_UNSETEMPTY(recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS)
|
|
UNSETEMPTY(movable_loc.important_recursive_contents)
|
|
|
|
// This proc adds atom/movables to the AI targetable list, i.e. things that the AI (turrets, hostile animals) will attempt to target
|
|
/atom/movable/proc/add_to_target_grid()
|
|
for (var/atom/movable/location as anything in get_nested_locs(src) + src)
|
|
LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_AI_TARGETS, src)
|
|
|
|
var/turf/our_turf = get_turf(src)
|
|
if(our_turf && SSspatial_grid.initialized)
|
|
SSspatial_grid.add_grid_awareness(src, RECURSIVE_CONTENTS_AI_TARGETS)
|
|
SSspatial_grid.add_grid_membership(src, our_turf, RECURSIVE_CONTENTS_AI_TARGETS)
|
|
|
|
else if(our_turf && !SSspatial_grid.initialized)//SSspatial_grid isnt init'd yet, add ourselves to the queue
|
|
SSspatial_grid.enter_pre_init_queue(src, RECURSIVE_CONTENTS_AI_TARGETS)
|
|
|
|
/atom/movable/proc/clear_from_target_grid()
|
|
var/turf/our_turf = get_turf(src)
|
|
if(our_turf && SSspatial_grid.initialized)
|
|
SSspatial_grid.exit_cell(src, our_turf, RECURSIVE_CONTENTS_AI_TARGETS)
|
|
else if(our_turf && !SSspatial_grid.initialized)
|
|
SSspatial_grid.remove_from_pre_init_queue(src, RECURSIVE_CONTENTS_AI_TARGETS)
|
|
|
|
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
|
|
LAZYREMOVEASSOC(location.important_recursive_contents, RECURSIVE_CONTENTS_AI_TARGETS, src)
|
|
|
|
/atom/movable/proc/do_simple_ranged_interaction(var/mob/user)
|
|
return FALSE
|
|
|
|
/atom/movable/proc/get_bullet_impact_effect_type()
|
|
return BULLET_IMPACT_NONE
|
|
|
|
// /atom/movable/proc/do_pickup_animation(atom/target, var/image/pickup_animation = image(icon, loc, icon_state, ABOVE_ALL_MOB_LAYER, dir, pixel_x, pixel_y))
|
|
/obj/item/proc/do_pickup_animation(atom/target, turf/source)
|
|
if(!source)
|
|
if(!istype(loc, /turf))
|
|
return
|
|
source = loc
|
|
var/image/pickup_animation = image(icon = src)
|
|
pickup_animation.transform.Scale(0.75)
|
|
pickup_animation.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
|
|
|
|
var/direction = get_dir(source, target)
|
|
var/to_x = target.pixel_x
|
|
var/to_y = target.pixel_y
|
|
|
|
if(direction & NORTH)
|
|
to_y += 32
|
|
else if(direction & SOUTH)
|
|
to_y -= 32
|
|
if(direction & EAST)
|
|
to_x += 32
|
|
else if(direction & WEST)
|
|
to_x -= 32
|
|
if(!direction)
|
|
to_y += 10
|
|
pickup_animation.pixel_x += 6 * (prob(50) ? 1 : -1) //6 to the right or left, helps break up the straight upward move
|
|
|
|
var/atom/movable/flick_visual/pickup = source.flick_overlay_view(pickup_animation, 0.4 SECONDS)
|
|
var/matrix/animation_matrix = new(pickup.transform)
|
|
animation_matrix.Turn(pick(-30, 30))
|
|
animation_matrix.Scale(0.65)
|
|
|
|
animate(pickup, alpha = 175, pixel_x = to_x, pixel_y = to_y, time = 0.3 SECONDS, transform = animation_matrix, easing = CUBIC_EASING)
|
|
animate(alpha = 0, transform = matrix().Scale(0.7), time = 0.1 SECONDS)
|
|
|
|
/atom/movable/proc/do_drop_animation(atom/moving_from)
|
|
if(!isturf(loc))
|
|
return
|
|
var/turf/current_turf = get_turf(src)
|
|
var/direction = get_dir(moving_from, current_turf)
|
|
var/from_x = 0
|
|
var/from_y = 0
|
|
|
|
if(direction & NORTH)
|
|
from_y -= 32
|
|
else if(direction & SOUTH)
|
|
from_y += 32
|
|
if(direction & EAST)
|
|
from_x -= 32
|
|
else if(direction & WEST)
|
|
from_x += 32
|
|
if(!direction)
|
|
from_y += 10
|
|
from_x += 6 * (prob(50) ? 1 : -1) //6 to the right or left, helps break up the straight upward move
|
|
|
|
//We're moving from these chords to our current ones
|
|
var/old_x = pixel_x
|
|
var/old_y = pixel_y
|
|
var/old_alpha = alpha
|
|
var/matrix/old_transform = transform
|
|
var/matrix/animation_matrix = new(old_transform)
|
|
animation_matrix.Turn(pick(-30, 30))
|
|
animation_matrix.Scale(0.7) // Shrink to start, end up normal sized
|
|
|
|
pixel_x = from_x
|
|
pixel_y = from_y
|
|
alpha = 0
|
|
transform = animation_matrix
|
|
|
|
// This is instant on byond's end, but to our clients this looks like a quick drop
|
|
animate(src, alpha = old_alpha, pixel_x = old_x, pixel_y = old_y, transform = old_transform, time = 3, easing = CUBIC_EASING)
|
|
|
|
/atom/movable/proc/get_floating_chat_x_offset()
|
|
return 0
|
|
|
|
/atom/movable/proc/get_floating_chat_y_offset()
|
|
return 8
|
|
|
|
/atom/movable/proc/can_attach_sticker(var/mob/user, var/obj/item/sticker/S)
|
|
return TRUE
|
|
|
|
/atom/movable/proc/too_heavy_to_throw()
|
|
return FALSE
|
|
|
|
/atom/movable/proc/begin_falling(var/lastloc, var/below)
|
|
return
|
|
|
|
/atom/movable/proc/show_message(msg, type, alt, alt_type) //Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
|
|
return
|
|
|
|
/**
|
|
* 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][/atom/movable/proc/newtonian_move]
|
|
*
|
|
* Return FALSE to have src start/keep drifting in a no-grav area and TRUE to stop/not start drifting
|
|
*
|
|
* Mobs should return 1 if they should be able to move of their own volition, see [/client/proc/Move]
|
|
*
|
|
* Arguments:
|
|
* * movement_dir - 0 when stopping or any dir when trying to move
|
|
* * continuous_move - If this check is coming from something in the context of already drifting
|
|
*/
|
|
/atom/movable/proc/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
|
|
if(has_gravity())
|
|
return TRUE
|
|
|
|
if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE)
|
|
return TRUE
|
|
|
|
if(pulledby && (pulledby.pulledby != src)) //Different from TG
|
|
return TRUE
|
|
|
|
if(throwing)
|
|
return TRUE
|
|
|
|
if(!isturf(loc))
|
|
return TRUE
|
|
|
|
if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
/// Only moves the object if it's under no gravity
|
|
/// Accepts the direction to move, if the push should be instant, and an optional parameter to fine tune the start delay
|
|
/atom/movable/proc/newtonian_move(direction, instant = FALSE, start_delay = 0)
|
|
if(!isturf(loc) || Process_Spacemove(direction, continuous_move = TRUE))
|
|
return FALSE
|
|
|
|
if(SEND_SIGNAL(src, COMSIG_MOVABLE_NEWTONIAN_MOVE, direction, start_delay) & COMPONENT_MOVABLE_NEWTONIAN_BLOCK)
|
|
return TRUE
|
|
|
|
AddComponent(/datum/component/drift, direction, instant, start_delay)
|
|
|
|
return TRUE
|