diff --git a/code/__DEFINES/dcs/signals/signals_turf.dm b/code/__DEFINES/dcs/signals/signals_turf.dm index 61142cea565..36e6920b8d2 100644 --- a/code/__DEFINES/dcs/signals/signals_turf.dm +++ b/code/__DEFINES/dcs/signals/signals_turf.dm @@ -14,6 +14,8 @@ #define COMSIG_TURF_MULTIZ_NEW "turf_multiz_new" ///from base of turf/proc/onShuttleMove(): (turf/new_turf) #define COMSIG_TURF_ON_SHUTTLE_MOVE "turf_on_shuttle_move" +///from base of /datum/turf_reservation/proc/Release: (datum/turf_reservation/reservation) +#define COMSIG_TURF_RESERVATION_RELEASED "turf_reservation_released" ///from /turf/open/temperature_expose(datum/gas_mixture/air, exposed_temperature) #define COMSIG_TURF_EXPOSE "turf_expose" ///from /turf/proc/immediate_calculate_adjacent_turfs() diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm index ac56ca7097c..041e2bc2b24 100644 --- a/code/__DEFINES/movement.dm +++ b/code/__DEFINES/movement.dm @@ -34,6 +34,8 @@ GLOBAL_VAR_INIT(glide_size_multiplier, 1.0) #define MOVEMENT_LOOP_IGNORE_PRIORITY (1<<1) ///Should we override the loop's glide? #define MOVEMENT_LOOP_IGNORE_GLIDE (1<<2) +///Should we not update our movables dir on move? +#define MOVEMENT_LOOP_NO_DIR_UPDATE (1<<3) //Index defines for movement bucket data packets #define MOVEMENT_BUCKET_TIME 1 diff --git a/code/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm index 5b906d97bf3..1d721ef3f1d 100644 --- a/code/__DEFINES/shuttles.dm +++ b/code/__DEFINES/shuttles.dm @@ -44,7 +44,7 @@ #define TRANSIT_REQUEST 1 #define TRANSIT_READY 2 -#define SHUTTLE_TRANSIT_BORDER 8 +#define SHUTTLE_TRANSIT_BORDER 16 #define PARALLAX_LOOP_TIME 25 #define HYPERSPACE_END_TIME 5 diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index ee5d29cd8b2..5e9514b4732 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -200,6 +200,7 @@ #define FIRE_PRIORITY_FLUIDS 20 #define FIRE_PRIORITY_AIR 20 #define FIRE_PRIORITY_NPC 20 +#define FIRE_PRIORITY_HYPERSPACE_DRIFT 20 #define FIRE_PRIORITY_NPC_MOVEMENT 21 #define FIRE_PRIORITY_NPC_ACTIONS 22 #define FIRE_PRIORITY_PATHFINDING 23 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 7e1c8a7d862..58e278d4b3a 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -317,6 +317,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Increases chance of getting special traumas, makes them harder to cure #define TRAIT_SPECIAL_TRAUMA_BOOST "special_trauma_boost" #define TRAIT_SPACEWALK "spacewalk" +/// Sanity trait to keep track of when we're in hyperspace and add the appropriate element if we werent +#define TRAIT_HYPERSPACED "hyperspaced" +///Gives the movable free hyperspace movement without being pulled during shuttle transit +#define TRAIT_FREE_HYPERSPACE_MOVEMENT "free_hyperspace_movement" /// Gets double arcade prizes #define TRAIT_GAMERGOD "gamer-god" #define TRAIT_GIANT "giant" diff --git a/code/controllers/subsystem/movement/hyperspace_drift.dm b/code/controllers/subsystem/movement/hyperspace_drift.dm new file mode 100644 index 00000000000..bd1ac67a6f0 --- /dev/null +++ b/code/controllers/subsystem/movement/hyperspace_drift.dm @@ -0,0 +1,6 @@ +///This subsystem handles the hyperspace shuttle pull movement loops +MOVEMENT_SUBSYSTEM_DEF(hyperspace_drift) + name = "Hyperspace Drift" + priority = FIRE_PRIORITY_HYPERSPACE_DRIFT + flags = SS_NO_INIT|SS_TICKER + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME diff --git a/code/controllers/subsystem/movement/movement.dm b/code/controllers/subsystem/movement/movement.dm index 39761b633a8..c43debda543 100644 --- a/code/controllers/subsystem/movement/movement.dm +++ b/code/controllers/subsystem/movement/movement.dm @@ -104,12 +104,12 @@ SUBSYSTEM_DEF(movement) smash_bucket(bucket_time = loop.timer) // We can't pass an index in for context because we don't know our position /datum/controller/subsystem/movement/proc/add_loop(datum/move_loop/add) - add.start_loop() + add.loop_started() if(QDELETED(add)) return queue_loop(add) /datum/controller/subsystem/movement/proc/remove_loop(datum/move_loop/remove) dequeue_loop(remove) - remove.stop_loop() + remove.loop_stopped() diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm index a3d1165b8a3..2d3f3ba84b0 100644 --- a/code/controllers/subsystem/movement/movement_types.dm +++ b/code/controllers/subsystem/movement/movement_types.dm @@ -25,6 +25,8 @@ var/timer = 0 ///Is this loop running or not var/running = FALSE + ///Track if we're currently paused + var/paused = FALSE /datum/move_loop/New(datum/movement_packet/owner, datum/controller/subsystem/movement/controller, atom/moving, priority, flags, datum/extra_info) src.owner = owner @@ -51,7 +53,8 @@ return TRUE return FALSE -/datum/move_loop/proc/start_loop() +///Called when a loop is starting by a movement subsystem +/datum/move_loop/proc/loop_started() SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_MOVELOOP_START) running = TRUE @@ -62,7 +65,8 @@ return timer = world.time + delay -/datum/move_loop/proc/stop_loop() +///Called when a loop is stopped, doesn't stop the loop itself +/datum/move_loop/proc/loop_stopped() SHOULD_CALL_PARENT(TRUE) running = FALSE SEND_SIGNAL(src, COMSIG_MOVELOOP_STOP) @@ -126,6 +130,25 @@ /datum/move_loop/proc/move() return FALSE + +///Pause our loop untill restarted with resume_loop() +/datum/move_loop/proc/pause_loop() + if(!controller || !running || paused) //we dead + return + + //Dequeue us from our current bucket + controller.dequeue_loop(src) + paused = TRUE + +///Resume our loop after being paused by pause_loop() +/datum/move_loop/proc/resume_loop() + if(!controller || !running || !paused) + return + + controller.queue_loop(src) + timer = world.time + paused = FALSE + ///Removes the atom from some movement subsystem. Defaults to SSmovement /datum/controller/subsystem/move_manager/proc/stop_looping(atom/movable/moving, datum/controller/subsystem/movement/subsystem = SSmovement) var/datum/movement_packet/our_info = moving.move_packet @@ -168,7 +191,7 @@ /datum/move_loop/move/move() var/atom/old_loc = moving.loc - moving.Move(get_step(moving, direction), direction) + moving.Move(get_step(moving, direction), direction, FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) // We cannot rely on the return value of Move(), we care about teleports and it doesn't // Moving also can be null on occasion, if the move deleted it and therefor us return old_loc != moving?.loc @@ -376,7 +399,7 @@ return TRUE return FALSE -/datum/move_loop/has_target/jps/start_loop() +/datum/move_loop/has_target/jps/loop_started() . = ..() INVOKE_ASYNC(src, PROC_REF(recalculate_path)) diff --git a/code/datums/components/shuttle_cling.dm b/code/datums/components/shuttle_cling.dm new file mode 100644 index 00000000000..0f2042ede68 --- /dev/null +++ b/code/datums/components/shuttle_cling.dm @@ -0,0 +1,158 @@ + +//Below defines are for the is_holding_on proc to see how well they're holding on and respond accordingly +///Instead of a high move force we just get launched away dramatically because we're that hopeless +#define SUPER_NOT_HOLDING_ON 0 +///We're not holdin on and will get thrown off +#define NOT_HOLDING_ON 1 +///We're holding on, but will be pulled slowly +#define CLINGING 2 +///We're holding on really well and aren't suffering from any pull +#define ALL_GOOD 3 + +///Gets added to all movables that enter hyperspace and are supposed to suffer from "hyperspace drift" +///This lets people fly around shuttles during transit using jetpacks, or cling to the side if they got a spacesuit +///Dumping into deepspace is handled by the hyperspace turf, not the component. +///Not giving something this component while on hyperspace is safe, it just means free movement like carps +/datum/component/shuttle_cling + ///The direction we push stuff towards + var/direction + ///Path to the hyperspace tile, so we know if we're in hyperspace + var/hyperspace_type = /turf/open/space/transit + + ///Our moveloop, handles the transit pull + var/datum/move_loop/move/hyperloop + + ///If we can "hold on", how often do we move? + var/clinging_move_delay = 1 SECONDS + ///If we can't hold onto anything, how fast do we get pulled away? + var/not_clinging_move_delay = 0.2 SECONDS + +/datum/component/shuttle_cling/Initialize(direction) + . = ..() + + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + + src.direction = direction + + ADD_TRAIT(parent, TRAIT_HYPERSPACED, src) + + RegisterSignals(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_UNBUCKLE, COMSIG_ATOM_NO_LONGER_PULLED), PROC_REF(update_state)) + + hyperloop = SSmove_manager.move(moving = parent, direction = direction, delay = not_clinging_move_delay, subsystem = SShyperspace_drift, priority = MOVEMENT_ABOVE_SPACE_PRIORITY, flags = MOVEMENT_LOOP_START_FAST | MOVEMENT_LOOP_NO_DIR_UPDATE) + + update_state(parent) //otherwise we'll get moved 1 tile before we can correct ourselves, which isnt super bad but just looks jank + +///Check if we're in hyperspace and our state in hyperspace +/datum/component/shuttle_cling/proc/update_state() + SIGNAL_HANDLER + + if(!is_on_hyperspace(parent)) + qdel(src) + return + + var/should_loop = FALSE + + switch(is_holding_on(parent)) + if(SUPER_NOT_HOLDING_ON) + launch_very_hard(parent) + should_loop = TRUE + if(NOT_HOLDING_ON) + hyperloop.set_delay(not_clinging_move_delay) + should_loop = TRUE + hyperloop.direction = direction //we're not close to anything so reset direction if we got diagonalized + if(CLINGING) + hyperloop.set_delay(clinging_move_delay) + should_loop = TRUE + update_drift_direction(parent) + if(ALL_GOOD) + should_loop = FALSE + + //Do pause/unpause/nothing for the hyperloop + if(should_loop && hyperloop.paused) + hyperloop.resume_loop() + else if(!should_loop && !hyperloop.paused) + hyperloop.pause_loop() + +///Check if we're "holding on" to the shuttle +/datum/component/shuttle_cling/proc/is_holding_on(atom/movable/movee) + if(movee.pulledby || !isturf(movee.loc)) + return ALL_GOOD + + if(!isliving(movee)) + if(is_tile_solid(get_step(movee, direction))) //something is blocking us so do the cool drift + return CLINGING + return SUPER_NOT_HOLDING_ON + + var/mob/living/living = movee + + //Check if we can interact with stuff (checks for alive, arms, stun, etc) + if(!living.canUseTopic(living, be_close = TRUE, no_dexterity = FALSE, no_tk = TRUE, need_hands = TRUE)) + return NOT_HOLDING_ON + + if(living.buckled) + return ALL_GOOD + + for(var/atom/handlebar in range(living, 1)) + if(isclosedturf(handlebar)) + return CLINGING + if(isobj(handlebar)) + var/obj/object = handlebar + if(object.anchored && object.density) + return CLINGING + return NOT_HOLDING_ON + +///Are we on a hyperspace tile? There's some special bullshit with lattices so we just wrap this check +/datum/component/shuttle_cling/proc/is_on_hyperspace(atom/movable/clinger) + if(istype(clinger.loc, hyperspace_type) && !(locate(/obj/structure/lattice) in clinger.loc)) + return TRUE + return FALSE + +///Launch the atom very hard, away from hyperspace +/datum/component/shuttle_cling/proc/launch_very_hard(atom/movable/byebye) + byebye.safe_throw_at(get_edge_target_turf(byebye, direction), 200, 1, spin = TRUE, force = MOVE_FORCE_EXTREMELY_STRONG) + +///Check if we arent just being blocked, and if we are give us some diagonal push so we cant just infinitely cling to the front +/datum/component/shuttle_cling/proc/update_drift_direction(atom/movable/clinger) + var/turf/potential_blocker = get_step(clinger, direction) + //We are not being blocked, so just give us cardinal drift + if(!is_tile_solid(potential_blocker)) + hyperloop.direction = direction + return + + //We're already moving diagonally + if(hyperloop.direction != direction) + var/side_dir = hyperloop.direction - direction + + if(is_tile_solid(get_step(clinger, side_dir))) + hyperloop.direction = direction + turn(side_dir, 180) //We're bumping a wall to the side, so switch to the other side_dir (yes this adds pingpong protocol) + return + + //Get the directions from the side of our current drift direction (so if we have drift south, get all cardinals and remove north and south, leaving only east and west) + var/side_dirs = shuffle(GLOB.cardinals - direction - turn(direction, 180)) + + //We check if one side is solid + if(!is_tile_solid(get_step(clinger, side_dirs[1]))) + hyperloop.direction = direction + side_dirs[1] + else //if one side isnt solid, send it to the other side (it can also be solid but we dont care cause we're boxed in then and not like itll matter much then) + hyperloop.direction = direction + side_dirs[2] + +///Check if it's a closed turf or contains a dense object +/datum/component/shuttle_cling/proc/is_tile_solid(turf/maybe_solid) + if(isclosedturf(maybe_solid)) + return TRUE + for(var/obj/blocker in maybe_solid.contents) + if(blocker.density) + return TRUE + return FALSE + +/datum/component/shuttle_cling/Destroy(force, silent) + REMOVE_TRAIT(parent, TRAIT_HYPERSPACED, src) + QDEL_NULL(hyperloop) + + return ..() + +#undef SUPER_NOT_HOLDING_ON +#undef NOT_HOLDING_ON +#undef CLINGING +#undef ALL_GOOD diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index ec82721febb..5bdd159a523 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -504,7 +504,7 @@ // Here's where we rewrite how byond handles movement except slightly different // To be removed on step_ conversion // All this work to prevent a second bump -/atom/movable/Move(atom/newloc, direction, glide_size_override = 0) +/atom/movable/Move(atom/newloc, direction, glide_size_override = 0, update_dir = TRUE) . = FALSE if(!newloc || newloc == loc) return @@ -512,7 +512,7 @@ if(!direction) direction = get_dir(src, newloc) - if(set_dir_on_move && dir != direction) + if(set_dir_on_move && dir != direction && update_dir) setDir(direction) var/is_multi_tile_object = bound_width > 32 || bound_height > 32 @@ -577,7 +577,7 @@ //////////////////////////////////////// -/atom/movable/Move(atom/newloc, direct, glide_size_override = 0) +/atom/movable/Move(atom/newloc, direct, glide_size_override = 0, update_dir = TRUE) var/atom/movable/pullee = pulling var/turf/current_turf = loc if(!moving_from_pull) @@ -638,7 +638,7 @@ moving_diagonally = SECOND_DIAG_STEP . = step(src, SOUTH) if(moving_diagonally == SECOND_DIAG_STEP) - if(!. && set_dir_on_move) + if(!. && set_dir_on_move && update_dir) setDir(first_step_dir) else if(!inertia_moving) newtonian_move(direct) @@ -679,7 +679,7 @@ last_move = direct - if(set_dir_on_move && dir != direct) + if(set_dir_on_move && dir != direct && update_dir) setDir(direct) if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) //movement failed due to buckled mob(s) . = FALSE diff --git a/code/game/turfs/open/space/transit.dm b/code/game/turfs/open/space/transit.dm index bf79168cec2..e0a9981a83d 100644 --- a/code/game/turfs/open/space/transit.dm +++ b/code/game/turfs/open/space/transit.dm @@ -7,36 +7,54 @@ flags_1 = NOJAUNT //This line goes out to every wizard that ever managed to escape the den. I'm sorry. explosion_block = INFINITY +/turf/open/space/transit/Initialize(mapload) + . = ..() + update_appearance() + RegisterSignal(src, COMSIG_TURF_RESERVATION_RELEASED, PROC_REF(launch_contents)) + + for(var/atom/movable/movable in src) + throw_atom(movable) + +/turf/open/space/transit/clear_signal_refs() + //Signals are NOT removed from turfs upon replacement, and we get replaced ALOT, so unregister our signal + UnregisterSignal(src, COMSIG_TURF_RESERVATION_RELEASED) + /turf/open/space/transit/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) . = ..() underlay_appearance.icon_state = "speedspace_ns_[get_transit_state(asking_turf)]" underlay_appearance.transform = turn(matrix(), get_transit_angle(asking_turf)) -/turf/open/space/transit/south - dir = SOUTH +/turf/open/space/transit/update_icon() + . = ..() + transform = turn(matrix(), get_transit_angle(src)) -/turf/open/space/transit/north - dir = NORTH - -/turf/open/space/transit/horizontal - dir = WEST - -/turf/open/space/transit/west - dir = WEST - -/turf/open/space/transit/east - dir = EAST +/turf/open/space/transit/update_icon_state() + icon_state = "speedspace_ns_[get_transit_state(src)]" + return ..() /turf/open/space/transit/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) . = ..() - if(!locate(/obj/structure/lattice) in src) - throw_atom(arrived) + + if(!HAS_TRAIT(arrived, TRAIT_HYPERSPACED) && !HAS_TRAIT(arrived, TRAIT_FREE_HYPERSPACE_MOVEMENT)) + arrived.AddComponent(/datum/component/shuttle_cling, turn(dir, 180), old_loc) + +/turf/open/space/transit/Exited(atom/movable/gone, direction) + . = ..() + + var/turf/location = gone.loc + if(istype(location, /turf/open/space) && !istype(location, src.type))//they got forced out of transit area into default space tiles + throw_atom(gone) //launch them into game space, away from transitspace + +///Get rid of all our contents, called when our reservation is released (which in our case means the shuttle arrived) +/turf/open/space/transit/proc/launch_contents(datum/turf_reservation/reservation) + SIGNAL_HANDLER + + for(var/atom/movable/movable in contents) + throw_atom(movable) /turf/open/space/transit/proc/throw_atom(atom/movable/AM) if(!AM || istype(AM, /obj/docking_port) || istype(AM, /obj/effect/abstract)) return - if(AM.loc != src) // Multi-tile objects are "in" multiple locs but its loc is it's true placement. - return // Don't move multi tile objects if their origin isn't in transit var/max = world.maxx-TRANSITIONEDGE var/min = 1+TRANSITIONEDGE @@ -71,24 +89,23 @@ var/turf/T = locate(_x, _y, _z) AM.forceMove(T) - /turf/open/space/transit/CanBuildHere() return SSshuttle.is_in_shuttle_bounds(src) +/turf/open/space/transit/south + dir = SOUTH -/turf/open/space/transit/Initialize(mapload) - . = ..() - update_appearance() - for(var/atom/movable/AM in src) - throw_atom(AM) +/turf/open/space/transit/north + dir = NORTH -/turf/open/space/transit/update_icon() - . = ..() - transform = turn(matrix(), get_transit_angle(src)) +/turf/open/space/transit/horizontal + dir = WEST -/turf/open/space/transit/update_icon_state() - icon_state = "speedspace_ns_[get_transit_state(src)]" - return ..() +/turf/open/space/transit/west + dir = WEST + +/turf/open/space/transit/east + dir = EAST /proc/get_transit_state(turf/T) var/p = 9 diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index acb8709bd34..a2613f4477f 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -71,6 +71,7 @@ AddElement(/datum/element/simple_flying) ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_SIXTHSENSE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_FREE_HYPERSPACE_MOVEMENT, INNATE_TRAIT) // Starting spells var/datum/action/cooldown/spell/night_vision/revenant/vision = new(src) diff --git a/code/modules/mapping/space_management/space_reservation.dm b/code/modules/mapping/space_management/space_reservation.dm index 48811b63b6a..b6ec8801a7e 100644 --- a/code/modules/mapping/space_management/space_reservation.dm +++ b/code/modules/mapping/space_management/space_reservation.dm @@ -17,6 +17,10 @@ var/list/reserved_copy = reserved_turfs.Copy() SSmapping.used_turfs -= reserved_turfs reserved_turfs = list() + + for(var/turf/reserved_turf as anything in reserved_copy) + SEND_SIGNAL(reserved_turf, COMSIG_TURF_RESERVATION_RELEASED, src) + // Makes the linter happy, even tho we don't await this INVOKE_ASYNC(SSmapping, TYPE_PROC_REF(/datum/controller/subsystem/mapping, reserve_turfs), reserved_copy) diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm index 686192daf6f..205f199375e 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm @@ -86,6 +86,7 @@ apply_colour() ADD_TRAIT(src, TRAIT_HEALS_FROM_CARP_RIFTS, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_FREE_HYPERSPACE_MOVEMENT, INNATE_TRAIT) if (cell_line) AddElement(/datum/element/swabable, cell_line, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) diff --git a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm index 8971a45a01e..bfce764fa7b 100644 --- a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm +++ b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm @@ -86,6 +86,7 @@ . = ..() AddElement(/datum/element/simple_flying) ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_FREE_HYPERSPACE_MOVEMENT, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_HEALS_FROM_CARP_RIFTS, INNATE_TRAIT) AddElement(/datum/element/content_barfer) diff --git a/tgstation.dme b/tgstation.dme index 097afaba85c..5a6168f71fb 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -596,6 +596,7 @@ #include "code\controllers\subsystem\weather.dm" #include "code\controllers\subsystem\wiremod_composite.dm" #include "code\controllers\subsystem\movement\ai_movement.dm" +#include "code\controllers\subsystem\movement\hyperspace_drift.dm" #include "code\controllers\subsystem\movement\move_handler.dm" #include "code\controllers\subsystem\movement\movement.dm" #include "code\controllers\subsystem\movement\movement_types.dm" @@ -921,6 +922,7 @@ #include "code\datums\components\shell.dm" #include "code\datums\components\shielded.dm" #include "code\datums\components\shrink.dm" +#include "code\datums\components\shuttle_cling.dm" #include "code\datums\components\shy.dm" #include "code\datums\components\shy_in_room.dm" #include "code\datums\components\sign_language.dm"