mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-20 14:45:05 +00:00
[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.  **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 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 Co-authored-by: Time-Green <timkoster1@hotmail.com>
116 lines
4.5 KiB
Plaintext
116 lines
4.5 KiB
Plaintext
SUBSYSTEM_DEF(movement)
|
|
name = "Movement Loops"
|
|
flags = SS_NO_INIT|SS_BACKGROUND|SS_TICKER
|
|
wait = 1 //Fire each tick
|
|
/*
|
|
A breif aside about the bucketing system here
|
|
|
|
The goal is to allow for higher loads of semi long delays while reducing cpu usage
|
|
Bucket insertion and management are much less complex then what you might see in SStimer
|
|
This is intentional, as we loop our delays much more often then that ss is designed for
|
|
We also have much shorter term timers, so we need to worry about redundant buckets much less
|
|
*/
|
|
///Assoc list of "target time" -> list(things to process). Used for quick lookup
|
|
var/list/buckets = list()
|
|
///Sorted list of list(target time, bucket to process)
|
|
var/list/sorted_buckets = list()
|
|
///The time we started our last fire at
|
|
var/canonical_time = 0
|
|
///The visual delay of the subsystem
|
|
var/visual_delay = 1
|
|
|
|
/datum/controller/subsystem/movement/stat_entry(msg)
|
|
var/total_len = 0
|
|
for(var/list/bucket as anything in sorted_buckets)
|
|
total_len += length(bucket[MOVEMENT_BUCKET_LIST])
|
|
msg = "B:[length(sorted_buckets)] E:[total_len]"
|
|
return ..()
|
|
|
|
/datum/controller/subsystem/movement/Recover()
|
|
//Get ready this is gonna be horrible
|
|
//We need to do this to support subtypes by the by
|
|
var/list/typenames = return_typenames(src.type)
|
|
var/our_name = typenames[length(typenames)] //Get the last name in the list, IE the subsystem identifier
|
|
|
|
var/datum/controller/subsystem/movement/old_version = global.vars["SS[our_name]"]
|
|
buckets = old_version.buckets
|
|
sorted_buckets = old_version.sorted_buckets
|
|
|
|
/datum/controller/subsystem/movement/fire(resumed)
|
|
if(!resumed)
|
|
canonical_time = world.time
|
|
|
|
for(var/list/bucket_info as anything in sorted_buckets)
|
|
var/time = bucket_info[MOVEMENT_BUCKET_TIME]
|
|
if(time > canonical_time || MC_TICK_CHECK)
|
|
return
|
|
pour_bucket(bucket_info)
|
|
|
|
/// Processes a bucket of movement loops (This should only ever be called by fire(), it exists to prevent runtime fuckery)
|
|
/datum/controller/subsystem/movement/proc/pour_bucket(list/bucket_info)
|
|
var/list/processing = bucket_info[MOVEMENT_BUCKET_LIST] // Cache for lookup speed
|
|
while(processing.len)
|
|
var/datum/move_loop/loop = processing[processing.len]
|
|
processing.len--
|
|
loop.process() //This shouldn't get nulls, if it does, runtime
|
|
if(!QDELETED(loop)) //Re-Insert the loop
|
|
loop.timer = world.time + loop.delay
|
|
queue_loop(loop)
|
|
if (MC_TICK_CHECK)
|
|
break
|
|
|
|
if(length(processing))
|
|
return // Still work to be done
|
|
var/bucket_time = bucket_info[MOVEMENT_BUCKET_TIME]
|
|
smash_bucket(1, bucket_time) // We assume we're the first bucket in the queue right now
|
|
visual_delay = MC_AVERAGE_FAST(visual_delay, max((world.time - canonical_time) / wait, 1))
|
|
|
|
/// Removes a bucket from our system. You only need to pass in the time, but if you pass in the index of the list you save us some work
|
|
/datum/controller/subsystem/movement/proc/smash_bucket(index, bucket_time)
|
|
var/sorted_length = length(sorted_buckets)
|
|
if(!index)
|
|
index = sorted_length + 1 // let's setup the failure condition
|
|
for(var/i in 1 to sorted_length)
|
|
var/list/bucket_info = sorted_buckets[i]
|
|
if(bucket_info[MOVEMENT_BUCKET_TIME] != bucket_time)
|
|
continue
|
|
index = i
|
|
break
|
|
//This is technically possible, if our bucket is smashed inside the loop's process
|
|
//Let's be nice, the cost of doing it is cheap
|
|
if(index > sorted_length || !buckets["[bucket_time]"])
|
|
return
|
|
|
|
sorted_buckets.Cut(index, index + 1) //Removes just this list
|
|
//Removes the assoc lookup too
|
|
buckets -= "[bucket_time]"
|
|
|
|
/datum/controller/subsystem/movement/proc/queue_loop(datum/move_loop/loop)
|
|
var/target_time = loop.timer
|
|
var/string_time = "[target_time]"
|
|
// If there's no bucket for this, lets set them up
|
|
if(!buckets[string_time])
|
|
buckets[string_time] = list()
|
|
// This makes assoc buckets and sorted buckets point to the same place, allowing for quicker inserts
|
|
var/list/new_bucket = list(list(target_time, buckets[string_time]))
|
|
BINARY_INSERT_DEFINE(new_bucket, sorted_buckets, SORT_VAR_NO_TYPE, list(target_time), SORT_FIRST_INDEX, COMPARE_KEY)
|
|
|
|
buckets[string_time] += loop
|
|
|
|
/datum/controller/subsystem/movement/proc/dequeue_loop(datum/move_loop/loop)
|
|
var/list/our_entries = buckets["[loop.timer]"]
|
|
our_entries -= loop
|
|
if(!length(our_entries))
|
|
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.loop_started()
|
|
if(QDELETED(add))
|
|
return
|
|
queue_loop(add)
|
|
|
|
/datum/controller/subsystem/movement/proc/remove_loop(datum/move_loop/remove)
|
|
dequeue_loop(remove)
|
|
remove.loop_stopped()
|
|
|