mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 05:00:55 +01:00
815 lines
27 KiB
Plaintext
815 lines
27 KiB
Plaintext
/obj/docking_port/mobile
|
|
name = "shuttle"
|
|
icon_state = "mobile"
|
|
|
|
area_type = SHUTTLE_DEFAULT_SHUTTLE_AREA_TYPE
|
|
|
|
///List of all areas our shuttle holds.
|
|
var/list/shuttle_areas = list()
|
|
///List of all currently used engines that propels us.
|
|
var/list/obj/machinery/power/shuttle_engine/engine_list = list()
|
|
|
|
///How fast the shuttle should be, taking engine thrust into account.
|
|
var/engine_coeff = 1
|
|
///How much engine power (thrust) the shuttle currently has.
|
|
var/current_engine_power = 0
|
|
///How much engine power (thrust) the shuttle starts with at mapload.
|
|
var/initial_engine_power = 0
|
|
///Speed multiplier based on station alert level
|
|
var/alert_coeff = ALERT_COEFF_BLUE
|
|
///used as a timer (if you want time left to complete move, use timeLeft proc)
|
|
var/timer
|
|
var/last_timer_length
|
|
///current shuttle mode
|
|
var/mode = SHUTTLE_IDLE
|
|
///time spent in transit (deciseconds). Should not be lower then 10 seconds without editing the animation of the hyperspace ripples.
|
|
var/callTime = 10 SECONDS
|
|
/// time spent "starting the engines". Also rate limits how often we try to reserve transit space if its ever full of transiting shuttles.
|
|
var/ignitionTime = 5.5 SECONDS
|
|
/// time spent after arrival before being able to begin ignition
|
|
var/rechargeTime = 0 SECONDS
|
|
/// time spent after transit 'landing' before actually arriving
|
|
var/prearrivalTime = 0 SECONDS
|
|
|
|
/// The direction the shuttle prefers to travel in, ie what direction the animation will cause it to appear to be traveling in
|
|
var/preferred_direction = NORTH
|
|
/// relative direction of the docking port from the front of the shuttle. NORTH is towards front, EAST would be starboard side, WEST port, etc.
|
|
var/port_direction = NORTH
|
|
|
|
var/obj/docking_port/stationary/destination
|
|
var/obj/docking_port/stationary/previous
|
|
|
|
var/obj/docking_port/stationary/transit/assigned_transit
|
|
|
|
var/launch_status = NOLAUNCH
|
|
|
|
var/list/ripples = list()
|
|
///Whether or not you want your ship to knock people down, and also whether it will throw them several tiles upon launching.
|
|
var/list/movement_force = list(
|
|
"KNOCKDOWN" = 3,
|
|
"THROW" = 0,
|
|
)
|
|
|
|
///if this shuttle can move docking ports other than the one it is docked at
|
|
var/can_move_docking_ports = FALSE
|
|
var/list/hidden_turfs = list()
|
|
///List of shuttle events that can run or are running
|
|
var/list/datum/shuttle_event/event_list = list()
|
|
|
|
var/list/underlying_areas_by_turf = list()
|
|
|
|
///How many turfs this shuttle has. Used to check against max shuttle size when expanding expandable shuttles.
|
|
var/turf_count = 0
|
|
|
|
/obj/docking_port/mobile/Initialize(mapload, list/areas)
|
|
. = ..()
|
|
|
|
if(!shuttle_id)
|
|
shuttle_id = "shuttle"
|
|
if(!name)
|
|
name = "shuttle"
|
|
var/counter = 1
|
|
var/tmp_id = shuttle_id
|
|
var/tmp_name = name
|
|
while(Check_id(shuttle_id))
|
|
counter++
|
|
shuttle_id = "[tmp_id]_[counter]"
|
|
name = "[tmp_name] [counter]"
|
|
|
|
if(areas)
|
|
for(var/area/area as anything in areas)
|
|
shuttle_areas[area] = TRUE
|
|
else
|
|
var/list/all_turfs = return_ordered_turfs(x, y, z, dir)
|
|
for(var/i in 1 to all_turfs.len)
|
|
var/turf/curT = all_turfs[i]
|
|
var/area/cur_area = curT.loc
|
|
if(istype(cur_area, area_type))
|
|
turf_count++
|
|
shuttle_areas[cur_area] = TRUE
|
|
|
|
#ifdef TESTING
|
|
highlight("#0f0")
|
|
#endif
|
|
|
|
/obj/docking_port/mobile/Destroy(force)
|
|
unregister()
|
|
destination = null
|
|
previous = null
|
|
for(var/obj/machinery/power/shuttle_engine/engine as anything in engine_list)
|
|
engine.unsync_ship()
|
|
if(!QDELETED(assigned_transit))
|
|
qdel(assigned_transit, force = TRUE)
|
|
assigned_transit = null
|
|
shuttle_areas = null
|
|
remove_ripples()
|
|
return ..()
|
|
|
|
#define WORLDMAXX_CUTOFF (world.maxx + 1)
|
|
#define WORLDMAXY_CUTOFF (world.maxx + 1)
|
|
/**
|
|
* Calculated and populates the information used for docking and some internal vars.
|
|
* This can also be used to calculate from shuttle_areas so that you can expand/shrink shuttles!
|
|
*
|
|
* Arguments:
|
|
* * loading_from - The template that the shuttle was loaded from, if not given we iterate shuttle_areas to calculate information instead
|
|
*/
|
|
/obj/docking_port/mobile/proc/calculate_docking_port_information(datum/map_template/shuttle/loading_from)
|
|
var/port_x_offset = loading_from?.port_x_offset
|
|
var/port_y_offset = loading_from?.port_y_offset
|
|
var/width = loading_from?.width
|
|
var/height = loading_from?.height
|
|
if(!loading_from)
|
|
if(!length(shuttle_areas))
|
|
CRASH("Attempted to calculate a docking port's information without a template before it was assigned any areas!")
|
|
// no template given, use shuttle_areas to calculate width and height
|
|
var/min_x = WORLDMAXX_CUTOFF
|
|
var/min_y = WORLDMAXY_CUTOFF
|
|
var/max_x = -1
|
|
var/max_y = -1
|
|
for(var/area/shuttle_area as anything in shuttle_areas)
|
|
for (var/list/zlevel_turfs as anything in shuttle_area.get_zlevel_turf_lists())
|
|
for(var/turf/turf as anything in zlevel_turfs)
|
|
min_x = min(turf.x, min_x)
|
|
max_x = max(turf.x, max_x)
|
|
min_y = min(turf.y, min_y)
|
|
max_y = max(turf.y, max_y)
|
|
CHECK_TICK
|
|
|
|
if(min_x == WORLDMAXX_CUTOFF || max_x == -1)
|
|
CRASH("Failed to locate shuttle boundaries when iterating through shuttle areas, somehow.")
|
|
if(min_y == WORLDMAXY_CUTOFF || max_y == -1)
|
|
CRASH("Failed to locate shuttle boundaries when iterating through shuttle areas, somehow.")
|
|
|
|
width = (max_x - min_x) + 1
|
|
height = (max_y - min_y) + 1
|
|
port_x_offset = x - min_x + 1
|
|
port_y_offset = y - min_y + 1
|
|
|
|
if(dir in list(EAST, WEST))
|
|
src.width = height
|
|
src.height = width
|
|
else
|
|
src.width = width
|
|
src.height = height
|
|
|
|
switch(dir)
|
|
if(NORTH)
|
|
dwidth = port_x_offset - 1
|
|
dheight = port_y_offset - 1
|
|
if(EAST)
|
|
dwidth = height - port_y_offset
|
|
dheight = port_x_offset - 1
|
|
if(SOUTH)
|
|
dwidth = width - port_x_offset
|
|
dheight = height - port_y_offset
|
|
if(WEST)
|
|
dwidth = port_y_offset - 1
|
|
dheight = width - port_x_offset
|
|
#undef WORLDMAXX_CUTOFF
|
|
#undef WORLDMAXY_CUTOFF
|
|
|
|
/obj/docking_port/mobile/is_in_shuttle_bounds(atom/A)
|
|
. = ..()
|
|
if(. && !shuttle_areas[get_area(A)])
|
|
return FALSE
|
|
|
|
/**
|
|
* Actions to be taken after shuttle is loaded but before it has been moved out of transit z-level to its final location
|
|
*
|
|
* Arguments:
|
|
* * replace - TRUE if this shuttle is replacing an existing one. FALSE by default.
|
|
* * custom - TRUE if this shuttle should be added to the custom shuttle list. FALSE by default.
|
|
*/
|
|
/obj/docking_port/mobile/register(replace = FALSE, custom = FALSE)
|
|
. = ..()
|
|
if(!shuttle_id)
|
|
shuttle_id = "shuttle"
|
|
|
|
if(!name)
|
|
name = "shuttle"
|
|
|
|
var/counter = SSshuttle.assoc_mobile[shuttle_id]
|
|
if(!replace || !counter)
|
|
if(counter)
|
|
counter++
|
|
SSshuttle.assoc_mobile[shuttle_id] = counter
|
|
shuttle_id = "[shuttle_id]_[counter]"
|
|
name = "[name] [counter]"
|
|
//Re link machinery to new shuttle id
|
|
linkup()
|
|
else
|
|
SSshuttle.assoc_mobile[shuttle_id] = 1
|
|
|
|
SSshuttle.mobile_docking_ports += src
|
|
|
|
if(custom)
|
|
SSshuttle.custom_shuttles += src
|
|
|
|
/**
|
|
* Actions to be taken after shuttle is loaded and has been moved to its final location
|
|
*
|
|
* Arguments:
|
|
* * replace - TRUE if this shuttle is replacing an existing one. FALSE by default.
|
|
*/
|
|
/obj/docking_port/mobile/proc/postregister(replace = FALSE)
|
|
return
|
|
|
|
/obj/docking_port/mobile/unregister()
|
|
. = ..()
|
|
SSshuttle.mobile_docking_ports -= src
|
|
SSshuttle.custom_shuttles -= src
|
|
|
|
|
|
|
|
// Called after the shuttle is loaded from template, so we make sure they know it's from mapload.
|
|
/obj/docking_port/mobile/proc/linkup(obj/docking_port/stationary/dock)
|
|
for(var/area/place as anything in shuttle_areas)
|
|
place.connect_to_shuttle(TRUE, src, dock)
|
|
for(var/atom/individual_atoms in place)
|
|
individual_atoms.connect_to_shuttle(TRUE, src, dock)
|
|
|
|
//this is a hook for custom behaviour. Maybe at some point we could add checks to see if engines are intact
|
|
/obj/docking_port/mobile/proc/canMove()
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
if(SEND_SIGNAL(src, COMSIG_SHUTTLE_SHOULD_MOVE) & BLOCK_SHUTTLE_MOVE)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
//this is to check if this shuttle can physically dock at dock stationary_dock
|
|
/obj/docking_port/mobile/proc/canDock(obj/docking_port/stationary/stationary_dock)
|
|
if(!istype(stationary_dock))
|
|
return SHUTTLE_NOT_A_DOCKING_PORT
|
|
|
|
if(stationary_dock.override_can_dock_checks)
|
|
return SHUTTLE_CAN_DOCK
|
|
|
|
if(dwidth > stationary_dock.dwidth)
|
|
return SHUTTLE_DWIDTH_TOO_LARGE
|
|
|
|
if(width-dwidth > stationary_dock.width-stationary_dock.dwidth)
|
|
return SHUTTLE_WIDTH_TOO_LARGE
|
|
|
|
if(dheight > stationary_dock.dheight)
|
|
return SHUTTLE_DHEIGHT_TOO_LARGE
|
|
|
|
if(height-dheight > stationary_dock.height-stationary_dock.dheight)
|
|
return SHUTTLE_HEIGHT_TOO_LARGE
|
|
|
|
//check the dock isn't occupied
|
|
var/currently_docked = stationary_dock.get_docked()
|
|
if(currently_docked)
|
|
// by someone other than us
|
|
if(currently_docked != src)
|
|
return SHUTTLE_SOMEONE_ELSE_DOCKED
|
|
else
|
|
// This isn't an error, per se, but we can't let the shuttle code
|
|
// attempt to move us where we currently are, it will get weird.
|
|
return SHUTTLE_ALREADY_DOCKED
|
|
|
|
return SHUTTLE_CAN_DOCK
|
|
|
|
/obj/docking_port/mobile/proc/check_dock(obj/docking_port/stationary/S, silent = FALSE)
|
|
var/status = canDock(S)
|
|
if(status == SHUTTLE_CAN_DOCK)
|
|
return TRUE
|
|
else
|
|
if(status != SHUTTLE_ALREADY_DOCKED && !silent) // SHUTTLE_ALREADY_DOCKED is no cause for error
|
|
message_admins("Shuttle [src] cannot dock at [S], error: [status]")
|
|
// We're already docked there, don't need to do anything.
|
|
// Triggering shuttle movement code in place is weird
|
|
return FALSE
|
|
|
|
/obj/docking_port/mobile/proc/transit_failure()
|
|
message_admins("Shuttle [src] repeatedly failed to create transit zone.")
|
|
|
|
/**
|
|
* Calls the shuttle to the destination port, respecting its ignition and call timers
|
|
*
|
|
* Arguments:
|
|
* * destination_port - Stationary docking port to move the shuttle to
|
|
*/
|
|
/obj/docking_port/mobile/proc/request(obj/docking_port/stationary/destination_port)
|
|
if(!check_dock(destination_port))
|
|
testing("check_dock failed on request for [src]")
|
|
return
|
|
|
|
if(mode == SHUTTLE_IGNITING && destination == destination_port)
|
|
return
|
|
|
|
if(!canMove())
|
|
return
|
|
|
|
switch(mode)
|
|
if(SHUTTLE_CALL)
|
|
if(destination_port == destination)
|
|
if(timeLeft(1) < callTime * engine_coeff)
|
|
setTimer(callTime * engine_coeff)
|
|
else
|
|
destination = destination_port
|
|
setTimer(callTime * engine_coeff)
|
|
if(SHUTTLE_RECALL)
|
|
if(destination_port == destination)
|
|
setTimer(callTime * engine_coeff - timeLeft(1))
|
|
else
|
|
destination = destination_port
|
|
setTimer(callTime * engine_coeff)
|
|
mode = SHUTTLE_CALL
|
|
if(SHUTTLE_IDLE, SHUTTLE_IGNITING)
|
|
destination = destination_port
|
|
mode = SHUTTLE_IGNITING
|
|
// BUBBER EDIT ADDITION BEGIN - Shuttle doors bolt/sounds
|
|
bolt_all_doors()
|
|
play_engine_sound(src, TRUE)
|
|
// BUBBER EDIT ADDITION END - Shuttle doors bolt/sounds
|
|
setTimer(ignitionTime)
|
|
|
|
//recall the shuttle to where it was previously
|
|
/obj/docking_port/mobile/proc/cancel()
|
|
if(mode != SHUTTLE_CALL)
|
|
return
|
|
|
|
remove_ripples()
|
|
|
|
invertTimer()
|
|
mode = SHUTTLE_RECALL
|
|
|
|
/obj/docking_port/mobile/proc/enterTransit()
|
|
if((SSshuttle.lockdown && is_station_level(z)) || !canMove()) //emp went off, no escape
|
|
mode = SHUTTLE_IDLE
|
|
return
|
|
previous = null
|
|
if(!destination)
|
|
// sent to transit with no destination -> unlimited timer
|
|
timer = INFINITY
|
|
var/obj/docking_port/stationary/S0 = get_docked()
|
|
var/obj/docking_port/stationary/S1 = assigned_transit
|
|
if(S1)
|
|
if(initiate_docking(S1) != DOCKING_SUCCESS)
|
|
WARNING("shuttle \"[shuttle_id]\" could not enter transit space. Docked at [S0 ? S0.shuttle_id : "null"]. Transit dock [S1 ? S1.shuttle_id : "null"].")
|
|
else if(S0)
|
|
if(S0.delete_after)
|
|
qdel(S0, TRUE)
|
|
else
|
|
previous = S0
|
|
else
|
|
WARNING("shuttle \"[shuttle_id]\" could not enter transit space. S0=[S0 ? S0.shuttle_id : "null"] S1=[S1 ? S1.shuttle_id : "null"]")
|
|
|
|
|
|
/obj/docking_port/mobile/proc/jumpToNullSpace()
|
|
// Destroys the docking port and the shuttle contents.
|
|
// Not in a fancy way, it just ceases.
|
|
var/obj/docking_port/stationary/current_dock = get_docked()
|
|
|
|
var/underlying_area_type = SHUTTLE_DEFAULT_UNDERLYING_AREA
|
|
// If the shuttle is docked to a stationary port, restore its normal
|
|
// "empty" area and turf
|
|
if(current_dock?.area_type)
|
|
underlying_area_type = current_dock.area_type
|
|
|
|
var/list/old_turfs = return_ordered_turfs(x, y, z, dir)
|
|
|
|
var/area/underlying_area = GLOB.areas_by_type[underlying_area_type]
|
|
if(!underlying_area)
|
|
underlying_area = new underlying_area_type(null)
|
|
|
|
for(var/i in 1 to old_turfs.len)
|
|
var/turf/oldT = old_turfs[i]
|
|
if(!oldT || !istype(oldT.loc, area_type))
|
|
continue
|
|
oldT.change_area(oldT.loc, underlying_area)
|
|
oldT.empty(FALSE)
|
|
|
|
// Here we locate the bottommost shuttle boundary and remove all turfs above it
|
|
var/shuttle_tile_depth = oldT.depth_to_find_baseturf(/turf/baseturf_skipover/shuttle)
|
|
if (!isnull(shuttle_tile_depth))
|
|
oldT.ScrapeAway(shuttle_tile_depth)
|
|
|
|
qdel(src, force=TRUE)
|
|
|
|
/**
|
|
* Ghosts and marks as escaped (for greentext purposes) all mobs, then deletes the shuttle.
|
|
* Used by the Shuttle Manipulator
|
|
*/
|
|
/obj/docking_port/mobile/proc/intoTheSunset()
|
|
// Loop over mobs
|
|
for(var/turf/turfs as anything in return_turfs())
|
|
for(var/mob/living/sunset_mobs in turfs.get_all_contents())
|
|
// If they have a mind and they're not in the brig, they escaped
|
|
if(sunset_mobs.mind && !istype(get_area(sunset_mobs), /area/shuttle/escape/brig))
|
|
sunset_mobs.mind.force_escaped = TRUE
|
|
// Ghostize them and put them in nullspace stasis (for stat & possession checks)
|
|
ADD_TRAIT(sunset_mobs, TRAIT_NO_TRANSFORM, REF(src))
|
|
sunset_mobs.ghostize(FALSE)
|
|
sunset_mobs.moveToNullspace()
|
|
|
|
// Now that mobs are stowed, delete the shuttle
|
|
jumpToNullSpace()
|
|
|
|
/obj/docking_port/mobile/proc/create_ripples(obj/docking_port/stationary/S1, animate_time)
|
|
var/list/turfs = ripple_area(S1)
|
|
for(var/t in turfs)
|
|
ripples += new /obj/effect/abstract/ripple(t, animate_time)
|
|
|
|
/obj/docking_port/mobile/proc/remove_ripples()
|
|
QDEL_LIST(ripples)
|
|
|
|
/obj/docking_port/mobile/proc/ripple_area(obj/docking_port/stationary/S1)
|
|
var/list/L0 = return_ordered_turfs(x, y, z, dir)
|
|
var/list/L1 = return_ordered_turfs(S1.x, S1.y, S1.z, S1.dir)
|
|
|
|
var/list/ripple_turfs = list()
|
|
var/stop = min(L0.len, L1.len)
|
|
for(var/i in 1 to stop)
|
|
var/turf/T0 = L0[i]
|
|
var/turf/T1 = L1[i]
|
|
if(!istype(T0.loc, area_type) || istype(T0.loc, /area/shuttle/transit))
|
|
continue // not part of the shuttle
|
|
ripple_turfs += T1
|
|
|
|
return ripple_turfs
|
|
|
|
/obj/docking_port/mobile/proc/check_poddoors()
|
|
for(var/obj/machinery/door/poddoor/shuttledock/pod as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door/poddoor/shuttledock))
|
|
pod.check()
|
|
|
|
/obj/docking_port/mobile/proc/dock_id(id)
|
|
var/port = SSshuttle.getDock(id)
|
|
if(port)
|
|
. = initiate_docking(port)
|
|
else
|
|
. = null
|
|
|
|
//used by shuttle subsystem to check timers
|
|
/obj/docking_port/mobile/proc/check()
|
|
check_effects()
|
|
//process_events() if you were to add events to non-escape shuttles, uncomment this
|
|
|
|
if(mode == SHUTTLE_IGNITING)
|
|
check_transit_zone()
|
|
|
|
if(timeLeft(1) > 0)
|
|
return
|
|
// If we can't dock or we don't have a transit slot, wait for 20 ds,
|
|
// then try again
|
|
switch(mode)
|
|
if(SHUTTLE_CALL, SHUTTLE_PREARRIVAL)
|
|
if(prearrivalTime && mode != SHUTTLE_PREARRIVAL)
|
|
mode = SHUTTLE_PREARRIVAL
|
|
setTimer(prearrivalTime)
|
|
return
|
|
var/error = initiate_docking(destination, preferred_direction)
|
|
if(error && error & (DOCKING_NULL_DESTINATION | DOCKING_NULL_SOURCE))
|
|
var/msg = "A mobile dock in transit exited initiate_docking() with an error. This is most likely a mapping problem: Error: [error], ([src]) ([previous][ADMIN_JMP(previous)] -> [destination][ADMIN_JMP(destination)])"
|
|
WARNING(msg)
|
|
message_admins(msg)
|
|
mode = SHUTTLE_IDLE
|
|
return
|
|
else if(error)
|
|
setTimer(20)
|
|
return
|
|
if(rechargeTime)
|
|
mode = SHUTTLE_RECHARGING
|
|
unbolt_all_doors() // BUBBER EDIT ADDITION - Shuttle doors bolt/sounds
|
|
setTimer(rechargeTime)
|
|
return
|
|
if(SHUTTLE_RECALL)
|
|
if(initiate_docking(previous) != DOCKING_SUCCESS)
|
|
setTimer(20)
|
|
return
|
|
if(SHUTTLE_IGNITING)
|
|
if(check_transit_zone() != TRANSIT_READY)
|
|
setTimer(20)
|
|
return
|
|
else
|
|
mode = SHUTTLE_CALL
|
|
setTimer(callTime * engine_coeff)
|
|
enterTransit()
|
|
return
|
|
|
|
unbolt_all_doors() // BUBBER EDIT ADDITION - Shuttle doors bolt/sounds
|
|
mode = SHUTTLE_IDLE
|
|
timer = 0
|
|
destination = null
|
|
|
|
/obj/docking_port/mobile/proc/check_effects()
|
|
if(!ripples.len)
|
|
if((mode == SHUTTLE_CALL) || (mode == SHUTTLE_RECALL))
|
|
var/tl = timeLeft(1)
|
|
if(tl <= SHUTTLE_RIPPLE_TIME)
|
|
create_ripples(destination, tl)
|
|
play_engine_sound(src, FALSE) // BUBBER EDIT ADDITION - Shuttle doors bolt/sounds
|
|
play_engine_sound(destination, FALSE) // BUBBER EDIT ADDITION - Shuttle doors bolt/sounds
|
|
|
|
var/obj/docking_port/stationary/S0 = get_docked()
|
|
if(istype(S0, /obj/docking_port/stationary/transit) && timeLeft(1) <= PARALLAX_LOOP_TIME)
|
|
for(var/place in shuttle_areas)
|
|
var/area/shuttle/shuttle_area = place
|
|
if(shuttle_area.parallax_movedir)
|
|
parallax_slowdown()
|
|
|
|
/obj/docking_port/mobile/proc/parallax_slowdown()
|
|
for(var/place in shuttle_areas)
|
|
var/area/shuttle/shuttle_area = place
|
|
shuttle_area.parallax_movedir = FALSE
|
|
if(assigned_transit?.assigned_area)
|
|
assigned_transit.assigned_area.parallax_movedir = FALSE
|
|
var/list/L0 = return_ordered_turfs(x, y, z, dir)
|
|
for (var/thing in L0)
|
|
var/turf/T = thing
|
|
if(!T || !istype(T.loc, area_type))
|
|
continue
|
|
for (var/atom/movable/movable as anything in T)
|
|
if (movable.client_mobs_in_contents)
|
|
movable.update_parallax_contents()
|
|
|
|
/obj/docking_port/mobile/proc/check_transit_zone()
|
|
if(assigned_transit)
|
|
return TRANSIT_READY
|
|
else
|
|
SSshuttle.request_transit_dock(src)
|
|
|
|
/obj/docking_port/mobile/proc/setTimer(wait)
|
|
timer = world.time + wait
|
|
last_timer_length = wait
|
|
|
|
/obj/docking_port/mobile/proc/modTimer(multiple)
|
|
var/time_remaining = timer - world.time
|
|
if(time_remaining < 0 || !last_timer_length)
|
|
return
|
|
time_remaining *= multiple
|
|
last_timer_length *= multiple
|
|
setTimer(time_remaining)
|
|
|
|
/obj/docking_port/mobile/proc/alert_coeff_change(new_coeff)
|
|
if(isnull(new_coeff))
|
|
return
|
|
|
|
var/time_multiplier = new_coeff / alert_coeff
|
|
var/time_remaining = timer - world.time
|
|
if(time_remaining < 0 || !last_timer_length)
|
|
return
|
|
|
|
time_remaining *= time_multiplier
|
|
last_timer_length *= time_multiplier
|
|
alert_coeff = new_coeff
|
|
setTimer(time_remaining)
|
|
|
|
/obj/docking_port/mobile/proc/invertTimer()
|
|
if(!last_timer_length)
|
|
return
|
|
var/time_remaining = timer - world.time
|
|
if(time_remaining > 0)
|
|
var/time_passed = last_timer_length - time_remaining
|
|
setTimer(time_passed)
|
|
|
|
//returns timeLeft
|
|
/obj/docking_port/mobile/proc/timeLeft(divisor)
|
|
if(divisor <= 0)
|
|
divisor = 10
|
|
|
|
var/ds_remaining
|
|
if(!timer)
|
|
ds_remaining = callTime * engine_coeff
|
|
else
|
|
ds_remaining = max(0, timer - world.time)
|
|
|
|
. = round(ds_remaining / divisor, 1)
|
|
|
|
// returns 3-letter mode string, used by status screens and mob status panel
|
|
/obj/docking_port/mobile/proc/getModeStr()
|
|
switch(mode)
|
|
if(SHUTTLE_IGNITING)
|
|
return "IGN"
|
|
if(SHUTTLE_RECALL)
|
|
return "RCL"
|
|
if(SHUTTLE_CALL)
|
|
return "ETA"
|
|
if(SHUTTLE_DOCKED)
|
|
return "ETD"
|
|
if(SHUTTLE_ESCAPE)
|
|
return "ESC"
|
|
if(SHUTTLE_STRANDED)
|
|
return "ERR"
|
|
if(SHUTTLE_RECHARGING)
|
|
return "RCH"
|
|
if(SHUTTLE_PREARRIVAL)
|
|
return "LDN"
|
|
if(SHUTTLE_DISABLED)
|
|
return "DIS"
|
|
return ""
|
|
|
|
// returns 5-letter timer string, used by status screens and mob status panel
|
|
/obj/docking_port/mobile/proc/getTimerStr()
|
|
if(mode == SHUTTLE_STRANDED || mode == SHUTTLE_DISABLED)
|
|
return "--:--"
|
|
|
|
var/timeleft = timeLeft()
|
|
if(timeleft > 1 HOURS)
|
|
return "--:--"
|
|
else if(timeleft > 0)
|
|
return "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, "0")]"
|
|
else
|
|
return "00:00"
|
|
|
|
/**
|
|
* Gets shuttle location status in a form of string for tgui interfaces
|
|
*/
|
|
/obj/docking_port/mobile/proc/get_status_text_tgui()
|
|
var/obj/docking_port/stationary/dockedAt = get_docked()
|
|
var/docked_at = dockedAt?.name || "Unknown"
|
|
if(!istype(dockedAt, /obj/docking_port/stationary/transit))
|
|
return docked_at
|
|
if(timeLeft() > 1 HOURS)
|
|
return "Hyperspace"
|
|
else
|
|
var/obj/docking_port/stationary/dst = (mode == SHUTTLE_RECALL) ? previous : destination
|
|
return "In transit to [dst?.name || "unknown location"]"
|
|
|
|
/obj/docking_port/mobile/proc/getStatusText()
|
|
var/obj/docking_port/stationary/dockedAt = get_docked()
|
|
var/docked_at = dockedAt?.name || "unknown"
|
|
if(istype(dockedAt, /obj/docking_port/stationary/transit))
|
|
if (timeLeft() > 1 HOURS)
|
|
return "hyperspace"
|
|
else
|
|
var/obj/docking_port/stationary/dst
|
|
if(mode == SHUTTLE_RECALL)
|
|
dst = previous
|
|
else
|
|
dst = destination
|
|
. = "transit towards [dst?.name || "unknown location"] ([getTimerStr()])"
|
|
else if(mode == SHUTTLE_RECHARGING)
|
|
return "[docked_at], recharging [getTimerStr()]"
|
|
else
|
|
return docked_at
|
|
|
|
/obj/docking_port/mobile/proc/getDbgStatusText()
|
|
var/obj/docking_port/stationary/dockedAt = get_docked()
|
|
. = (dockedAt?.name) ? dockedAt.name : "unknown"
|
|
if(istype(dockedAt, /obj/docking_port/stationary/transit))
|
|
var/obj/docking_port/stationary/dst
|
|
if(mode == SHUTTLE_RECALL)
|
|
dst = previous
|
|
else
|
|
dst = destination
|
|
if(dst)
|
|
. = "(transit to) [dst.name || dst.shuttle_id]"
|
|
else
|
|
. = "(transit to) nowhere"
|
|
else if(dockedAt)
|
|
. = dockedAt.name || dockedAt.shuttle_id
|
|
else
|
|
. = "unknown"
|
|
|
|
|
|
// attempts to locate /obj/machinery/computer/shuttle with matching ID inside the shuttle
|
|
/obj/docking_port/mobile/proc/get_control_console()
|
|
for(var/area/shuttle/shuttle_area as anything in shuttle_areas)
|
|
var/obj/machinery/computer/shuttle/shuttle_computer = locate(/obj/machinery/computer/shuttle) in shuttle_area
|
|
if(!shuttle_computer)
|
|
continue
|
|
if(shuttle_computer.shuttleId == shuttle_id)
|
|
return shuttle_computer
|
|
return null
|
|
|
|
/obj/docking_port/mobile/proc/hyperspace_sound(phase, list/areas)
|
|
var/selected_sound
|
|
switch(phase)
|
|
if(HYPERSPACE_WARMUP)
|
|
selected_sound = "hyperspace_begin"
|
|
if(HYPERSPACE_LAUNCH)
|
|
selected_sound = "hyperspace_progress"
|
|
if(HYPERSPACE_END)
|
|
selected_sound = "hyperspace_end"
|
|
else
|
|
CRASH("Invalid hyperspace sound phase: [phase]")
|
|
// This previously was played from each door at max volume, and was one of the worst things I had ever seen.
|
|
// Now it's instead played from the nearest engine if close, or the first engine in the list if far since it doesn't really matter.
|
|
// Or a door if for some reason the shuttle has no engine, fuck oh hi daniel fuck it
|
|
var/range = (engine_coeff * max(width, height))
|
|
var/long_range = range * 2.5
|
|
var/atom/distant_source
|
|
|
|
if(engine_list.len)
|
|
distant_source = engine_list[1]
|
|
else
|
|
for(var/our_area in areas)
|
|
distant_source = locate(/obj/machinery/door) in our_area
|
|
if(distant_source)
|
|
break
|
|
|
|
if(!distant_source)
|
|
return
|
|
for(var/mob/zlevel_mobs as anything in SSmobs.clients_by_zlevel[z])
|
|
var/dist_far = get_dist(zlevel_mobs, distant_source)
|
|
if(dist_far <= long_range && dist_far > range)
|
|
zlevel_mobs.playsound_local(distant_source, "sound/runtime/hyperspace/[selected_sound]_distance.ogg", 100)
|
|
else if(dist_far <= range)
|
|
var/source
|
|
if(!engine_list.len)
|
|
source = distant_source
|
|
else
|
|
var/closest_dist = 10000
|
|
for(var/obj/machinery/power/shuttle_engine/engines as anything in engine_list)
|
|
var/dist_near = get_dist(zlevel_mobs, engines)
|
|
if(dist_near < closest_dist)
|
|
source = engines
|
|
closest_dist = dist_near
|
|
zlevel_mobs.playsound_local(source, "sound/runtime/hyperspace/[selected_sound].ogg", 100)
|
|
|
|
// Losing all initial engines should get you 2
|
|
// Adding another set of engines at 0.5 time
|
|
/obj/docking_port/mobile/proc/alter_engines(mod)
|
|
if(!mod)
|
|
return
|
|
var/old_coeff = engine_coeff
|
|
engine_coeff = get_engine_coeff(mod)
|
|
current_engine_power = max(0, current_engine_power + mod)
|
|
if(in_flight())
|
|
var/delta_coeff = engine_coeff / old_coeff
|
|
modTimer(delta_coeff)
|
|
|
|
// Double initial engines to get to 0.5 minimum
|
|
// Lose all initial engines to get to 2
|
|
//For 0 engine shuttles like BYOS 5 engines to get to doublespeed
|
|
/obj/docking_port/mobile/proc/get_engine_coeff(engine_mod)
|
|
var/new_value = max(0, current_engine_power + engine_mod)
|
|
if(new_value == initial_engine_power)
|
|
return 1
|
|
if(new_value > initial_engine_power)
|
|
var/delta = new_value - initial_engine_power
|
|
var/change_per_engine = (1 - ENGINE_COEFF_MIN) / ENGINE_DEFAULT_MAXSPEED_ENGINES // 5 by default
|
|
if(initial_engine_power > 0)
|
|
change_per_engine = (1 - ENGINE_COEFF_MIN) / initial_engine_power // or however many it had
|
|
return clamp(1 - delta * change_per_engine,ENGINE_COEFF_MIN, ENGINE_COEFF_MAX)
|
|
if(new_value < initial_engine_power)
|
|
var/delta = initial_engine_power - new_value
|
|
var/change_per_engine = 1 //doesn't really matter should not be happening for 0 engine shuttles
|
|
if(initial_engine_power > 0)
|
|
change_per_engine = (ENGINE_COEFF_MAX - 1) / initial_engine_power //just linear drop to max delay
|
|
return clamp(1 + delta * change_per_engine, ENGINE_COEFF_MIN, ENGINE_COEFF_MAX)
|
|
|
|
|
|
/obj/docking_port/mobile/proc/in_flight()
|
|
switch(mode)
|
|
if(SHUTTLE_CALL,SHUTTLE_RECALL,SHUTTLE_PREARRIVAL)
|
|
return TRUE
|
|
if(SHUTTLE_IDLE,SHUTTLE_IGNITING)
|
|
return FALSE
|
|
return FALSE // hmm
|
|
|
|
/obj/docking_port/mobile/emergency/in_flight()
|
|
switch(mode)
|
|
if(SHUTTLE_ESCAPE)
|
|
return TRUE
|
|
if(SHUTTLE_STRANDED,SHUTTLE_ENDGAME)
|
|
return FALSE
|
|
return ..()
|
|
|
|
//Called when emergency shuttle leaves the station
|
|
/obj/docking_port/mobile/proc/on_emergency_launch()
|
|
if(launch_status == UNLAUNCHED) //Pods will not launch from the mine/planet, and other ships won't launch unless we tell them to.
|
|
launch_status = ENDGAME_LAUNCHED
|
|
enterTransit()
|
|
|
|
///Let people know shits about to go down
|
|
/obj/docking_port/mobile/proc/announce_shuttle_events()
|
|
for(var/datum/shuttle_event/event as anything in event_list)
|
|
notify_ghosts("\The [src] has selected: [event.name]")
|
|
|
|
/obj/docking_port/mobile/emergency/on_emergency_launch()
|
|
return
|
|
|
|
//Called when emergency shuttle docks at centcom
|
|
/obj/docking_port/mobile/proc/on_emergency_dock()
|
|
// Mapping a new docking point for each ship mappers could potentially want docking with centcom would take up lots of space,
|
|
// just let them keep flying off "into the sunset" for their greentext.
|
|
if(launch_status == ENDGAME_LAUNCHED)
|
|
launch_status = ENDGAME_TRANSIT
|
|
|
|
/obj/docking_port/mobile/pod/on_emergency_dock()
|
|
if(launch_status == ENDGAME_LAUNCHED)
|
|
initiate_docking(SSshuttle.getDock("[shuttle_id]_away")) //Escape pods dock at centcom
|
|
mode = SHUTTLE_ENDGAME
|
|
|
|
/obj/docking_port/mobile/emergency/on_emergency_dock()
|
|
return
|
|
|
|
///Process all the shuttle events for every shuttle tick we get
|
|
/obj/docking_port/mobile/proc/process_events()
|
|
var/list/removees
|
|
for(var/datum/shuttle_event/event as anything in event_list)
|
|
if(event.event_process() == SHUTTLE_EVENT_CLEAR) //if we return SHUTTLE_EVENT_CLEAR, we clean them up
|
|
LAZYADD(removees, event)
|
|
for(var/item in removees)
|
|
event_list.Remove(item)
|
|
|
|
/// Give a typepath of a shuttle event to add to the shuttle. If added during endgame transit, will insta start the event
|
|
/obj/docking_port/mobile/proc/add_shuttle_event(typepath)
|
|
var/datum/shuttle_event/event = new typepath (src)
|
|
event_list.Add(event)
|
|
if(launch_status == ENDGAME_LAUNCHED)
|
|
event.start_up_event(0)
|
|
return event
|