[READY] DRAMATIC SHUTTLES!! You can now fly around the shuttle (#71906)

You can move around shuttles during transport now! Instead of them
teleporting you instantly into deepspace, you can move around somewhat
depending on your space-mobility and grip-strength.


![image](https://user-images.githubusercontent.com/7501474/206866132-3fae024c-a8a2-4f4a-b4b8-37c96a254498.png)

**Please watch the demonstration aswell, it should answer most
questions:**
https://www.youtube.com/watch?v=Os77qDOVSXE

Interactions:
- Being within armsreach of a wall or solid object means you 'cling',
where the shuttle pull is very weak and you can basically run around the
shutt;e (but dont fuck up or you're gone)
- Being in range of nothing gives you a very heavy pull, you can barely
resist if you have a decent jetpack
- Objects are instantly power-yeeted
- Being pulled or riding something excempts you from hyperspace pull
- Touching a space tile while being on hyperspace dumps you in
deepspace, you either go back to the shuttle or enjoy deepspace
- On shuttle hyperspace tiles are a lot less dangerous, and will instead
launch and freeze you instead of teleporting you into deepspace
- In-case it wasn't obvious, you can rest outside the shuttle as long as
something is blocking your path. I think it's funny but I might nerf it

🆑
add: You can now fly around the shuttle during transit! Woohoo! You can
either cling to the side or grab a jetpack and try and keep up with the
shuttle! Carps can move around freely in hyperspace
qol: Increased shuttle hyperspace size from 8 tiles to 16
/🆑

- [x] Find a way to detect when a shuttle arrives and do something with
the shit left in hyperspace

Things I will do in another PR: 
- Engines spit fire and hurt (almost finished but I want to keep this
small)
- Random shuttle events. You might run into dust meteors or migrating
carps OR A CHANGELING INFILTRATOR
- Hyperspace turfs on the shuttle pull you under the shuttle

### Why it's good for the game
It's so much more immersive than being instantly teleported into
deepspace. It gives you a chance to recover if you get spaced or for
daredevils to look cool

It's also just very cool idk
This commit is contained in:
Time-Green
2023-01-03 11:10:56 -08:00
committed by GitHub
parent 9870f54e79
commit bf73344399
16 changed files with 263 additions and 41 deletions
@@ -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()
+2
View File
@@ -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
+1 -1
View File
@@ -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
+1
View File
@@ -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
+4
View File
@@ -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"
@@ -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
@@ -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()
@@ -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))
+158
View File
@@ -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
+5 -5
View File
@@ -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
+46 -29
View File
@@ -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
@@ -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)
@@ -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)
@@ -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)
@@ -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)
+2
View File
@@ -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"