Replaced "area" shuttles with "landmark" shuttles.

Largely ported from the work done at Baystation in https://github.com/Baystation12/Baystation12/pull/17460 and later commits.

 - Shuttles no longer require a separate area for each location they jump to.
   Instead destinations are indicated by landmark objects, which are not necessarily exclusive to that shuttle.
   This means that more than one shuttle could use the same docking port (not at the same time of course).
 - Enhanced shuttle control computers to use nanoui if they didn't.
 - Organizes shuttle datum code a bit better so there is less re-inventing the wheel in subtypes.
 - Allows the possibility of shuttles (or destinations) that start on late-loaded maps.
 - Deprecate the "extra" shuttle areas that are no longer needed and update shuttle areas in unit tests

This all required a bit of infrastructure improvements.

 - ChangeArea proc, for changing the area of a turf.
 - Fixed lighting overlays actually being able to be destroyed.
 - Added a few utility macros and procs.
 - Added "turf translation" procs which are like move_contents_to but more flexible.
This commit is contained in:
Leshana
2020-02-28 16:23:51 -05:00
parent caeb0de720
commit c837078105
69 changed files with 2474 additions and 1451 deletions

View File

@@ -15,6 +15,9 @@
//#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile.
#define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support!
// Comment/Uncomment this to turn off/on shuttle code debugging logs
#define DEBUG_SHUTTLES
// If we are doing the map test build, do not include the main maps, only the submaps.
#if MAP_TEST
#define USING_MAP_DATUM /datum/map

View File

@@ -11,10 +11,12 @@
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
// Adds I to L, initalizing I if necessary
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
// Adds I to L, initalizing L if necessary, if I is not already in L
#define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I;
#define LAZYFIND(L, V) L ? L.Find(V) : 0
// Reads I from L safely - Works with both associative and traditional lists.
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
// Turns LAZYINITLIST(L) L[K] = V into ... for associated lists
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
// Reads the length of L, returning 0 if null

View File

@@ -26,6 +26,7 @@
#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE)
#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)
#define QDEL_NULL(item) qdel(item); item = null
#define QDEL_NULL_LIST QDEL_LIST_NULL
#define QDEL_LIST_NULL(x) if(x) { for(var/y in x) { qdel(y) } ; x = null }
#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE)

View File

@@ -308,6 +308,11 @@ proc/listclearnulls(list/list)
else
L[key] = temp[key]
// Return a list of the values in an assoc list (including null)
/proc/list_values(var/list/L)
. = list()
for(var/e in L)
. += L[e]
//Mergesort: divides up the list into halves to begin the sort
/proc/sortKey(var/list/client/L, var/order = 1)

View File

@@ -38,4 +38,120 @@
var/pressure = environment ? environment.return_pressure() : 0
if(pressure < SOUND_MINIMUM_PRESSURE)
return TRUE
return FALSE
return FALSE
/*
Turf manipulation
*/
//Returns an assoc list that describes how turfs would be changed if the
//turfs in turfs_src were translated by shifting the src_origin to the dst_origin
/proc/get_turf_translation(turf/src_origin, turf/dst_origin, list/turfs_src)
var/list/turf_map = list()
for(var/turf/source in turfs_src)
var/x_pos = (source.x - src_origin.x)
var/y_pos = (source.y - src_origin.y)
var/z_pos = (source.z - src_origin.z)
var/turf/target = locate(dst_origin.x + x_pos, dst_origin.y + y_pos, dst_origin.z + z_pos)
if(!target)
error("Null turf in translation @ ([dst_origin.x + x_pos], [dst_origin.y + y_pos], [dst_origin.z + z_pos])")
turf_map[source] = target //if target is null, preserve that information in the turf map
return turf_map
/proc/translate_turfs(var/list/translation, var/area/base_area = null, var/turf/base_turf)
for(var/turf/source in translation)
var/turf/target = translation[source]
if(target)
if(base_area) ChangeArea(target, get_area(source))
var/leave_turf = base_turf ? base_turf : get_base_turf_by_area(base_area ? base_area : source)
translate_turf(source, target, leave_turf)
if(base_area) ChangeArea(source, base_area)
//change the old turfs (Currently done by translate_turf for us)
//for(var/turf/source in translation)
// source.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(source), 1, 1)
// Parmaters for stupid historical reasons are:
// T - Origin
// B - Destination
/proc/translate_turf(var/turf/T, var/turf/B, var/turftoleave = null)
//You can stay, though.
if(istype(T,/turf/space))
error("Tried to translate a space turf: src=[log_info_line(T)] dst=[log_info_line(B)]")
return FALSE // TODO - Is this really okay to do nothing?
var/turf/X //New Destination Turf
//Are we doing shuttlework? Just to save another type check later.
var/shuttlework = 0
//Shuttle turfs handle their own fancy moving.
if(istype(T,/turf/simulated/shuttle))
shuttlework = 1
var/turf/simulated/shuttle/SS = T
if(!SS.landed_holder) SS.landed_holder = new(turf = SS)
X = SS.landed_holder.land_on(B)
//Generic non-shuttle turf move.
else
var/old_dir1 = T.dir
var/old_icon_state1 = T.icon_state
var/old_icon1 = T.icon
var/old_underlays = T.underlays.Copy()
var/old_decals = T.decals ? T.decals.Copy() : null
X = B.ChangeTurf(T.type)
X.set_dir(old_dir1)
X.icon_state = old_icon_state1
X.icon = old_icon1
X.copy_overlays(T, TRUE)
X.underlays = old_underlays
X.decals = old_decals
//Move the air from source to dest
var/turf/simulated/ST = T
if(istype(ST) && ST.zone)
var/turf/simulated/SX = X
if(!SX.air)
SX.make_air()
SX.air.copy_from(ST.zone.air)
ST.zone.remove(ST)
var/z_level_change = FALSE
if(T.z != X.z)
z_level_change = TRUE
//Move the objects. Not forceMove because the object isn't "moving" really, it's supposed to be on the "same" turf.
for(var/obj/O in T)
if(O.simulated)
O.loc = X
O.update_light()
if(z_level_change) // The objects still need to know if their z-level changed.
O.onTransitZ(T.z, X.z)
//Move the mobs unless it's an AI eye or other eye type.
for(var/mob/M in T)
if(isEye(M)) continue // If we need to check for more mobs, I'll add a variable
M.loc = X
if(z_level_change) // Same goes for mobs.
M.onTransitZ(T.z, X.z)
if(istype(M, /mob/living))
var/mob/living/LM = M
LM.check_shadow() // Need to check their Z-shadow, which is normally done in forceMove().
if(shuttlework)
var/turf/simulated/shuttle/SS = T
SS.landed_holder.leave_turf(turftoleave)
else if(turftoleave)
T.ChangeTurf(turftoleave)
else
T.ChangeTurf(get_base_turf_by_area(T))
return TRUE

View File

@@ -25,6 +25,8 @@
#define qdel_null(x) if(x) { qdel(x) ; x = null }
#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key)
#define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id)
#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") }

View File

@@ -1,8 +1,5 @@
//Config stuff
#define SUPPLY_DOCKZ 2 //Z-level of the Dock.
#define SUPPLY_STATIONZ 1 //Z-level of the Station.
#define SUPPLY_STATION_AREATYPE "/area/supply/station" //Type of the supply shuttle area for station
#define SUPPLY_DOCK_AREATYPE "/area/supply/dock" //Type of the supply shuttle area for dock
// TODO - Refactor to use the Supply Subsystem (SSsupply)
//Supply packs are in /code/datums/supplypacks
//Computers are in /code/game/machinery/computer/supply.dm
@@ -43,7 +40,7 @@ var/datum/controller/supply/supply_controller = new()
var/list/adm_export_history = list() // Complete history of all crates sent back on the shuttle, for admin use
//shuttle movement
var/movetime = 1200
var/datum/shuttle/ferry/supply/shuttle
var/datum/shuttle/autodock/ferry/supply/shuttle
var/list/material_points_conversion = list( // Any materials not named in this list are worth 0 points
"phoron" = 5,
"platinum" = 5
@@ -90,83 +87,98 @@ var/datum/controller/supply/supply_controller = new()
//Selling
/datum/controller/supply/proc/sell()
var/area/area_shuttle = shuttle.get_location_area()
if(!area_shuttle)
return
// Loop over each area in the supply shuttle
for(var/area/subarea in shuttle.shuttle_area)
callHook("sell_shuttle", list(subarea));
for(var/atom/movable/MA in subarea)
if(MA.anchored)
continue
callHook("sell_shuttle", list(area_shuttle));
var/datum/exported_crate/EC = new /datum/exported_crate()
EC.name = "\proper[MA.name]"
EC.value = 0
EC.contents = list()
var/base_value = 0
for(var/atom/movable/MA in area_shuttle)
if(MA.anchored)
continue
// Must be in a crate!
if(istype(MA,/obj/structure/closet/crate))
var/obj/structure/closet/crate/CR = MA
callHook("sell_crate", list(CR, subarea))
var/datum/exported_crate/EC = new /datum/exported_crate()
EC.name = "\proper[MA.name]"
EC.value = 0
EC.contents = list()
var/base_value = 0
points += CR.points_per_crate
if(CR.points_per_crate)
base_value = CR.points_per_crate
var/find_slip = 1
// Must be in a crate!
if(istype(MA,/obj/structure/closet/crate))
var/obj/structure/closet/crate/CR = MA
callHook("sell_crate", list(CR, area_shuttle))
for(var/atom/A in CR)
EC.contents[++EC.contents.len] = list(
"object" = "\proper[A.name]",
"value" = 0,
"quantity" = 1
)
points += CR.points_per_crate
if(CR.points_per_crate)
base_value = CR.points_per_crate
var/find_slip = 1
// Sell manifests
if(find_slip && istype(A,/obj/item/weapon/paper/manifest))
var/obj/item/weapon/paper/manifest/slip = A
if(!slip.is_copy && slip.stamped && slip.stamped.len) //yes, the clown stamp will work. clown is the highest authority on the station, it makes sense
points += points_per_slip
EC.contents[EC.contents.len]["value"] = points_per_slip
find_slip = 0
continue
for(var/atom/A in CR)
EC.contents[++EC.contents.len] = list(
"object" = "\proper[A.name]",
"value" = 0,
"quantity" = 1
// Sell phoron and platinum
if(istype(A, /obj/item/stack))
var/obj/item/stack/P = A
if(material_points_conversion[P.get_material_name()])
EC.contents[EC.contents.len]["value"] = P.get_amount() * material_points_conversion[P.get_material_name()]
EC.contents[EC.contents.len]["quantity"] = P.get_amount()
EC.value += EC.contents[EC.contents.len]["value"]
//Sell spacebucks
if(istype(A, /obj/item/weapon/spacecash))
var/obj/item/weapon/spacecash/cashmoney = A
EC.contents[EC.contents.len]["value"] = cashmoney.worth * points_per_money
EC.contents[EC.contents.len]["quantity"] = cashmoney.worth
EC.value += EC.contents[EC.contents.len]["value"]
// Make a log of it, but it wasn't shipped properly, and so isn't worth anything
else
EC.contents = list(
"error" = "Error: Product was improperly packaged. Payment rendered null under terms of agreement."
)
// Sell manifests
if(find_slip && istype(A,/obj/item/weapon/paper/manifest))
var/obj/item/weapon/paper/manifest/slip = A
if(!slip.is_copy && slip.stamped && slip.stamped.len) //yes, the clown stamp will work. clown is the highest authority on the station, it makes sense
points += points_per_slip
EC.contents[EC.contents.len]["value"] = points_per_slip
find_slip = 0
exported_crates += EC
points += EC.value
EC.value += base_value
// Duplicate the receipt for the admin-side log
var/datum/exported_crate/adm = new()
adm.name = EC.name
adm.value = EC.value
adm.contents = deepCopyList(EC.contents)
adm_export_history += adm
qdel(MA)
/datum/controller/supply/proc/get_clear_turfs()
var/list/clear_turfs = list()
for(var/area/subarea in shuttle.shuttle_area)
for(var/turf/T in subarea)
if(T.density)
continue
var/occupied = 0
for(var/atom/A in T.contents)
if(!A.simulated)
continue
occupied = 1
break
if(!occupied)
clear_turfs += T
// Sell phoron and platinum
if(istype(A, /obj/item/stack))
var/obj/item/stack/P = A
if(material_points_conversion[P.get_material_name()])
EC.contents[EC.contents.len]["value"] = P.get_amount() * material_points_conversion[P.get_material_name()]
EC.contents[EC.contents.len]["quantity"] = P.get_amount()
EC.value += EC.contents[EC.contents.len]["value"]
//Sell spacebucks
if(istype(A, /obj/item/weapon/spacecash))
var/obj/item/weapon/spacecash/cashmoney = A
EC.contents[EC.contents.len]["value"] = cashmoney.worth * points_per_money
EC.contents[EC.contents.len]["quantity"] = cashmoney.worth
EC.value += EC.contents[EC.contents.len]["value"]
// Make a log of it, but it wasn't shipped properly, and so isn't worth anything
else
EC.contents = list(
"error" = "Error: Product was improperly packaged. Payment rendered null under terms of agreement."
)
exported_crates += EC
points += EC.value
EC.value += base_value
// Duplicate the receipt for the admin-side log
var/datum/exported_crate/adm = new()
adm.name = EC.name
adm.value = EC.value
adm.contents = deepCopyList(EC.contents)
adm_export_history += adm
qdel(MA)
return clear_turfs
//Buying
/datum/controller/supply/proc/buy()
@@ -177,26 +189,9 @@ var/datum/controller/supply/supply_controller = new()
if(!shoppinglist.len)
return
var/orderedamount = shoppinglist.len
var/area/area_shuttle = shuttle.get_location_area()
if(!area_shuttle)
return
var/list/clear_turfs = list()
for(var/turf/T in area_shuttle)
if(T.density)
continue
var/contcount
for(var/atom/A in T.contents)
if(!A.simulated)
continue
contcount++
if(contcount)
continue
clear_turfs += T
var/list/clear_turfs = get_clear_turfs()
for(var/datum/supply_order/SO in shoppinglist)
if(!clear_turfs.len)

View File

@@ -5,7 +5,7 @@
var/global/datum/emergency_shuttle_controller/emergency_shuttle
/datum/emergency_shuttle_controller
var/datum/shuttle/ferry/emergency/shuttle
var/datum/shuttle/autodock/ferry/emergency/shuttle // Set in shuttle_emergency.dm TODO - is it really?
var/list/escape_pods
var/launch_time //the time at which the shuttle will be launched
@@ -36,8 +36,8 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
if (!shuttle.location) //leaving from the station
//launch the pods!
for (var/EP in escape_pods)
var/datum/shuttle/ferry/escape_pod/pod
if(istype(escape_pods[EP], /datum/shuttle/ferry/escape_pod))
var/datum/shuttle/autodock/ferry/escape_pod/pod
if(istype(escape_pods[EP], /datum/shuttle/autodock/ferry/escape_pod))
pod = escape_pods[EP]
else
continue
@@ -63,8 +63,8 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
//arm the escape pods
if (evac)
for (var/EP in escape_pods)
var/datum/shuttle/ferry/escape_pod/pod
if(istype(escape_pods[EP], /datum/shuttle/ferry/escape_pod))
var/datum/shuttle/autodock/ferry/escape_pod/pod
if(istype(escape_pods[EP], /datum/shuttle/autodock/ferry/escape_pod))
pod = escape_pods[EP]
else
continue
@@ -215,11 +215,11 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
//returns 1 if the shuttle is currently in transit (or just leaving) to the station
/datum/emergency_shuttle_controller/proc/going_to_station()
return (!shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE)
return shuttle && (!shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE)
//returns 1 if the shuttle is currently in transit (or just leaving) to centcom
/datum/emergency_shuttle_controller/proc/going_to_centcom()
return (shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE)
return shuttle && (shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE)
/datum/emergency_shuttle_controller/proc/get_status_panel_eta()

View File

@@ -1,6 +1,8 @@
//
// SSshuttles subsystem - Handles initialization and processing of shuttles.
//
// Also handles initialization and processing of overmap sectors.
//
// This global variable exists for legacy support so we don't have to rename every shuttle_controller to SSshuttles yet.
var/global/datum/controller/subsystem/shuttles/shuttle_controller
@@ -13,71 +15,168 @@ SUBSYSTEM_DEF(shuttles)
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
var/list/shuttles = list() // Maps shuttle tags to shuttle datums, so that they can be looked up.
var/list/process_shuttles = list() // Simple list of shuttles, for processing
var/list/current_run = list() // Shuttles remaining to process this fire() tick
var/list/docks_init_callbacks // List of callbacks to run when we finish setting up shuttle docks.
var/docks_initialized = FALSE
// TODO OVERMAP - These two are unused for now
var/overmap_halted = FALSE // Whether ships can move on the overmap; used for adminbus.
var/list/ships = list() // List of all ships.
var/list/shuttles = list() // Maps shuttle tags to shuttle datums, so that they can be looked up.
var/list/process_shuttles = list() // Simple list of shuttles, for processing
var/list/registered_shuttle_landmarks = list() // Maps shuttle landmark tags to instances
var/last_landmark_registration_time // world.time of most recent addition to registered_shuttle_landmarks
var/list/shuttle_logs = list() // (Not Implemented) Keeps records of shuttle movement, format is list(datum/shuttle = datum/shuttle_log)
var/list/shuttle_areas = list() // All the areas of all shuttles.
var/list/docking_registry = list() // Docking controller tag -> docking controller program, mostly for init purposes.
var/list/landmarks_awaiting_sector = list() // Stores automatic landmarks that are waiting for a sector to finish loading.
var/list/landmarks_still_needed = list() // Stores landmark_tags that need to be assigned to the sector (landmark_tag = sector) when registered.
var/list/shuttles_to_initialize // A queue for shuttles to initialize at the appropriate time.
var/list/sectors_to_initialize // Used to find all sector objects at the appropriate time.
var/block_init_queue = TRUE // Block initialization of new shuttles/sectors
var/tmp/list/current_run // Shuttles remaining to process this fire() tick
/datum/controller/subsystem/shuttles/PreInit()
global.shuttle_controller = src // TODO - Remove this! Change everything to point at SSshuttles intead
/datum/controller/subsystem/shuttles/Initialize(timeofday)
global.shuttle_controller = src
setup_shuttle_docks()
for(var/I in docks_init_callbacks)
var/datum/callback/cb = I
cb.InvokeAsync()
LAZYCLEARLIST(docks_init_callbacks)
docks_init_callbacks = null
last_landmark_registration_time = world.time
// Find all declared shuttle datums and initailize them. (Okay, queue them for initialization a few lines further down)
for(var/shuttle_type in subtypesof(/datum/shuttle)) // This accounts for most shuttles, though away maps can queue up more.
var/datum/shuttle/shuttle = shuttle_type
if(initial(shuttle.category) == shuttle_type)
continue // Its an "abstract class" datum, not for a real shuttle.
if(!initial(shuttle.defer_initialisation)) // Skip if it asks not to be initialized at startup.
LAZYDISTINCTADD(shuttles_to_initialize, shuttle_type)
block_init_queue = FALSE
process_init_queues()
return ..()
/datum/controller/subsystem/shuttles/fire(resumed = 0)
do_process_shuttles(resumed)
/datum/controller/subsystem/shuttles/stat_entry()
var/msg = list()
msg += "AS:[shuttles.len]|"
msg += "PS:[process_shuttles.len]|"
..(jointext(msg, null))
/datum/controller/subsystem/shuttles/proc/do_process_shuttles(resumed = 0)
if (!resumed)
src.current_run = process_shuttles.Copy()
var/list/current_run = src.current_run // Cache for sanic speed
while(current_run.len)
var/datum/shuttle/S = current_run[current_run.len]
current_run.len--
if(istype(S) && !QDELETED(S))
if(istype(S, /datum/shuttle/ferry)) // Ferry shuttles get special treatment
var/datum/shuttle/ferry/F = S
if(F.process_state || F.always_process)
F.process()
else
S.process()
else
var/list/working_shuttles = src.current_run // Cache for sanic speed
while(working_shuttles.len)
var/datum/shuttle/S = working_shuttles[working_shuttles.len]
working_shuttles.len--
if(!istype(S) || QDELETED(S))
error("Bad entry in SSshuttles.process_shuttles - [log_info_line(S)] ")
process_shuttles -= S
continue
// NOTE - In old system, /datum/shuttle/ferry was processed only if (F.process_state || F.always_process)
if(S.process_state && (S.process(wait, times_fired, src) == PROCESS_KILL))
process_shuttles -= S
if(MC_TICK_CHECK)
return
// This should be called after all the machines and radio frequencies have been properly initialized
/datum/controller/subsystem/shuttles/proc/setup_shuttle_docks()
// Find all declared shuttle datums and initailize them.
for(var/shuttle_type in subtypesof(/datum/shuttle))
var/datum/shuttle/shuttle = shuttle_type
if(initial(shuttle.category) == shuttle_type)
continue
/datum/controller/subsystem/shuttles/proc/process_init_queues()
if(block_init_queue)
return
initialize_shuttles()
initialize_sectors()
// Initializes all shuttles in shuttles_to_initialize
/datum/controller/subsystem/shuttles/proc/initialize_shuttles()
var/list/shuttles_made = list()
for(var/shuttle_type in shuttles_to_initialize)
var/shuttle = initialize_shuttle(shuttle_type)
if(shuttle)
shuttles_made += shuttle
hook_up_motherships(shuttles_made)
shuttles_to_initialize = null
/datum/controller/subsystem/shuttles/proc/initialize_sectors()
for(var/sector in sectors_to_initialize)
initialize_sector(sector)
sectors_to_initialize = null
/datum/controller/subsystem/shuttles/proc/register_landmark(shuttle_landmark_tag, obj/effect/shuttle_landmark/shuttle_landmark)
if (registered_shuttle_landmarks[shuttle_landmark_tag])
CRASH("Attempted to register shuttle landmark with tag [shuttle_landmark_tag], but it is already registered!")
if (istype(shuttle_landmark))
registered_shuttle_landmarks[shuttle_landmark_tag] = shuttle_landmark
last_landmark_registration_time = world.time
// TODO - Uncomment once overmap sectors are ported
//var/obj/effect/overmap/visitable/O = landmarks_still_needed[shuttle_landmark_tag]
//if(O) //These need to be added to sectors, which we handle.
// try_add_landmark_tag(shuttle_landmark_tag, O)
// landmarks_still_needed -= shuttle_landmark_tag
//else if(istype(shuttle_landmark, /obj/effect/shuttle_landmark/automatic)) //These find their sector automatically
// O = map_sectors["[shuttle_landmark.z]"]
// O ? O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) : (landmarks_awaiting_sector += shuttle_landmark)
/datum/controller/subsystem/shuttles/proc/get_landmark(var/shuttle_landmark_tag)
return registered_shuttle_landmarks[shuttle_landmark_tag]
//Checks if the given sector's landmarks have initialized; if so, registers them with the sector, if not, marks them for assignment after they come in.
//Also adds automatic landmarks that were waiting on their sector to spawn.
/datum/controller/subsystem/shuttles/proc/initialize_sector(obj/effect/overmap/visitable/given_sector)
return // TODO - Uncomment once overmap sectors are ported
// given_sector.populate_sector_objects() // This is a late init operation that sets up the sector's map_z and does non-overmap-related init tasks.
// for(var/landmark_tag in given_sector.initial_generic_waypoints)
// if(!try_add_landmark_tag(landmark_tag, given_sector))
// landmarks_still_needed[landmark_tag] = given_sector // Landmark isn't registered yet, queue it to be added once it is.
// for(var/shuttle_name in given_sector.initial_restricted_waypoints)
// for(var/landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name])
// if(!try_add_landmark_tag(landmark_tag, given_sector))
// landmarks_still_needed[landmark_tag] = given_sector // Landmark isn't registered yet, queue it to be added once it is.
// var/landmarks_to_check = landmarks_awaiting_sector.Copy()
// for(var/thing in landmarks_to_check)
// var/obj/effect/shuttle_landmark/automatic/landmark = thing
// if(landmark.z in given_sector.map_z)
// given_sector.add_landmark(landmark, landmark.shuttle_restricted)
// landmarks_awaiting_sector -= landmark
// TODO - Uncomment once overmap sectors are ported
//// Attempts to add a landmark instance with a sector (returns false if landmark isn't registered yet)
///datum/controller/subsystem/shuttles/proc/try_add_landmark_tag(landmark_tag, obj/effect/overmap/visitable/given_sector)
// var/obj/effect/shuttle_landmark/landmark = get_landmark(landmark_tag)
// if(!landmark)
// return
// if(landmark.landmark_tag in given_sector.initial_generic_waypoints)
// given_sector.add_landmark(landmark)
// . = 1
// for(var/shuttle_name in given_sector.initial_restricted_waypoints)
// if(landmark.landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name])
// given_sector.add_landmark(landmark, shuttle_name)
// . = 1
/datum/controller/subsystem/shuttles/proc/initialize_shuttle(var/shuttle_type)
var/datum/shuttle/shuttle = shuttle_type
if(initial(shuttle.category) != shuttle_type) // Skip if its an "abstract class" datum
shuttle = new shuttle()
shuttle.init_docking_controllers()
shuttle.dock() //makes all shuttles docked to something at round start go into the docked state
CHECK_TICK
shuttle_areas |= shuttle.shuttle_area
log_debug("Initialized shuttle [shuttle] ([shuttle.type])")
return shuttle
// Historical note: No need to call shuttle.init_docking_controllers(), controllers register themselves
// and shuttles fetch refs in New(). Shuttles also dock() themselves in new if they want.
for(var/obj/machinery/embedded_controller/C in machines)
if(istype(C.program, /datum/computer/file/embedded_program/docking))
C.program.tag = null //clear the tags, 'cause we don't need 'em anymore
docks_initialized = TRUE
// TODO - Leshana to hook up more of this when overmap is ported.
/datum/controller/subsystem/shuttles/proc/hook_up_motherships(shuttles_list)
for(var/datum/shuttle/S in shuttles_list)
if(S.mothershuttle && !S.motherdock)
var/datum/shuttle/mothership = shuttles[S.mothershuttle]
if(mothership)
S.motherdock = S.current_location.landmark_tag
mothership.shuttle_area |= S.shuttle_area
else
error("Shuttle [S] was unable to find mothership [mothership]!")
// Register a callback that will be invoked once the shuttles have been initialized
/datum/controller/subsystem/shuttles/proc/OnDocksInitialized(datum/callback/cb)
if(!docks_initialized)
LAZYADD(docks_init_callbacks, cb)
else
cb.InvokeAsync()
// Admin command to halt/resume overmap
// /datum/controller/subsystem/shuttles/proc/toggle_overmap(new_setting)
// if(overmap_halted == new_setting)
// return
// overmap_halted = !overmap_halted
// for(var/ship in ships)
// var/obj/effect/overmap/visitable/ship/ship_effect = ship
// overmap_halted ? ship_effect.halt() : ship_effect.unhalt()
/datum/controller/subsystem/shuttles/stat_entry()
..("Shuttles:[process_shuttles.len]/[shuttles.len], Ships:[ships.len], L:[registered_shuttle_landmarks.len][overmap_halted ? ", HALT" : ""]")

View File

@@ -0,0 +1,22 @@
// Observer Pattern Implementation: Shuttle Added
// Registration type: /datum/shuttle (register for the global event only)
//
// Raised when: After a shuttle is initialized.
//
// Arguments that the called proc should expect:
// /datum/shuttle/shuttle: the new shuttle
GLOBAL_DATUM_INIT(shuttle_added, /decl/observ/shuttle_added, new)
/decl/observ/shuttle_added
name = "Shuttle Added"
expected_type = /datum/shuttle
/*****************************
* Shuttle Added Handling *
*****************************/
/datum/controller/subsystem/shuttles/initialize_shuttle()
. = ..()
if(.)
GLOB.shuttle_added.raise_event(.)

View File

@@ -0,0 +1,38 @@
// Observer Pattern Implementation: Shuttle Moved
// Registration type: /datum/shuttle/autodock
//
// Raised when: A shuttle has moved to a new landmark.
//
// Arguments that the called proc should expect:
// /datum/shuttle/shuttle: the shuttle moving
// /obj/effect/shuttle_landmark/old_location: the old location's shuttle landmark
// /obj/effect/shuttle_landmark/new_location: the new location's shuttle landmark
// Observer Pattern Implementation: Shuttle Pre Move
// Registration type: /datum/shuttle/autodock
//
// Raised when: A shuttle is about to move to a new landmark.
//
// Arguments that the called proc should expect:
// /datum/shuttle/shuttle: the shuttle moving
// /obj/effect/shuttle_landmark/old_location: the old location's shuttle landmark
// /obj/effect/shuttle_landmark/new_location: the new location's shuttle landmark
GLOBAL_DATUM_INIT(shuttle_moved_event, /decl/observ/shuttle_moved, new)
/decl/observ/shuttle_moved
name = "Shuttle Moved"
expected_type = /datum/shuttle
GLOBAL_DATUM_INIT(shuttle_pre_move_event, /decl/observ/shuttle_pre_move, new)
/decl/observ/shuttle_pre_move
name = "Shuttle Pre Move"
expected_type = /datum/shuttle
/*****************
* Shuttle Moved/Pre Move Handling *
*****************/
// Located in modules/shuttle/shuttle.dm
// Proc: /datum/shuttle/proc/attempt_move()

View File

@@ -62,8 +62,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
////////////
//SHUTTLES//
////////////
//shuttle areas must contain at least two areas in a subgroup if you want to move a shuttle from one
//place to another. Look at escape shuttle for example.
//Shuttles only need starting area, movement is handled by landmarks
//All shuttles should now be under shuttle since we have smooth-wall code.
/area/shuttle
@@ -76,160 +75,55 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "\improper Arrival Shuttle"
ambience = AMBIENCE_ARRIVALS
/area/shuttle/arrival/pre_game
/area/shuttle/supply
name = "\improper Supply Shuttle"
icon_state = "shuttle2"
/area/shuttle/arrival/station
icon_state = "shuttle"
dynamic_lighting = 0
ambience = AMBIENCE_ARRIVALS
/area/shuttle/escape
name = "\improper Emergency Shuttle"
music = "music/escape.ogg"
/area/shuttle/escape/station
name = "\improper Emergency Shuttle Station"
icon_state = "shuttle2"
dynamic_lighting = 0
/area/shuttle/escape/centcom
name = "\improper Emergency Shuttle CentCom"
icon_state = "shuttle"
/area/shuttle/escape/transit // the area to pass through for 3 minute transit
name = "\improper Emergency Shuttle Transit"
icon_state = "shuttle"
/area/shuttle/escape_pod1
name = "\improper Escape Pod One"
music = "music/escape.ogg"
/area/shuttle/escape_pod1/station
icon_state = "shuttle2"
/area/shuttle/escape_pod1/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod1/transit
icon_state = "shuttle"
/area/shuttle/escape_pod2
name = "\improper Escape Pod Two"
music = "music/escape.ogg"
/area/shuttle/escape_pod2/station
icon_state = "shuttle2"
/area/shuttle/escape_pod2/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod2/transit
icon_state = "shuttle"
/area/shuttle/escape_pod3
name = "\improper Escape Pod Three"
music = "music/escape.ogg"
/area/shuttle/escape_pod3/station
icon_state = "shuttle2"
/area/shuttle/escape_pod3/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod3/transit
icon_state = "shuttle"
/area/shuttle/escape_pod4
name = "\improper Escape Pod Four"
music = "music/escape.ogg"
/area/shuttle/escape_pod4/station
icon_state = "shuttle2"
/area/shuttle/escape_pod4/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod4/transit
icon_state = "shuttle"
/area/shuttle/escape_pod5
name = "\improper Escape Pod Five"
music = "music/escape.ogg"
/area/shuttle/escape_pod5/station
icon_state = "shuttle2"
/area/shuttle/escape_pod5/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod5/transit
icon_state = "shuttle"
/area/shuttle/escape_pod6
name = "\improper Escape Pod Six"
music = "music/escape.ogg"
/area/shuttle/escape_pod6/station
icon_state = "shuttle2"
/area/shuttle/escape_pod6/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod6/transit
icon_state = "shuttle"
/area/shuttle/large_escape_pod1
name = "\improper Large Escape Pod One"
music = "music/escape.ogg"
/area/shuttle/large_escape_pod1/station
icon_state = "shuttle2"
/area/shuttle/large_escape_pod1/centcom
icon_state = "shuttle"
/area/shuttle/large_escape_pod1/transit
icon_state = "shuttle"
/area/shuttle/large_escape_pod2
name = "\improper Large Escape Pod Two"
music = "music/escape.ogg"
/area/shuttle/large_escape_pod2/station
icon_state = "shuttle2"
/area/shuttle/large_escape_pod2/centcom
icon_state = "shuttle"
/area/shuttle/large_escape_pod2/transit
icon_state = "shuttle"
/area/shuttle/cryo
name = "\improper Cryogenic Storage"
/area/shuttle/cryo/station
icon_state = "shuttle2"
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/cryo/centcom
icon_state = "shuttle"
/area/shuttle/cryo/transit
icon_state = "shuttle"
/area/shuttle/mining
name = "\improper Mining Elevator"
music = "music/escape.ogg"
dynamic_lighting = 0
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/mining/station
icon_state = "shuttle2"
/area/shuttle/mining/outpost
icon_state = "shuttle"
/area/shuttle/transport1/centcom
icon_state = "shuttle"
name = "\improper Transport Shuttle CentCom"
@@ -286,54 +180,15 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "red"
dynamic_lighting = 0
/area/shuttle/trade/centcom
name = "\improper Trade Shuttle CentCom"
icon_state = "shuttlered"
/area/shuttle/trade/station
name = "\improper Trade Shuttle"
icon_state = "shuttlered"
/area/shuttle/thunderdome
name = "honk"
/area/shuttle/thunderdome/grnshuttle
name = "\improper Thunderdome GRN Shuttle"
icon_state = "green"
/area/shuttle/thunderdome/grnshuttle/dome
name = "\improper GRN Shuttle"
icon_state = "shuttlegrn"
/area/shuttle/thunderdome/grnshuttle/station
name = "\improper GRN Station"
icon_state = "shuttlegrn2"
/area/shuttle/thunderdome/redshuttle
name = "\improper Thunderdome RED Shuttle"
icon_state = "red"
/area/shuttle/thunderdome/redshuttle/dome
name = "\improper RED Shuttle"
icon_state = "shuttlered"
/area/shuttle/thunderdome/redshuttle/station
name = "\improper RED Station"
icon_state = "shuttlered2"
// === Trying to remove these areas:
/area/shuttle/research
name = "\improper Research Elevator"
music = "music/escape.ogg"
dynamic_lighting = 0
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/research/station
icon_state = "shuttle2"
/area/shuttle/research/outpost
icon_state = "shuttle"
/area/airtunnel1/ // referenced in airtunnel.dm:759
/area/dummy/ // Referenced in engine.dm:261
@@ -2040,17 +1895,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "\improper Cargo Mining Dock"
icon_state = "mining"
/area/supply/station
name = "Supply Shuttle"
icon_state = "shuttle3"
requires_power = 0
base_turf = /turf/space
/area/supply/dock
name = "Supply Shuttle"
icon_state = "shuttle3"
requires_power = 0
base_turf = /turf/space
// SCIENCE

View File

@@ -71,6 +71,31 @@
power_change() // all machines set to current power level, also updates lighting icon
return INITIALIZE_HINT_LATELOAD
// Changes the area of T to A. Do not do this manually.
// Area is expected to be a non-null instance.
/proc/ChangeArea(var/turf/T, var/area/A)
if(!istype(A))
CRASH("Area change attempt failed: invalid area supplied.")
var/area/old_area = get_area(T)
if(old_area == A)
return
// NOTE: BayStation calles area.Exited/Entered for the TURF T. So far we don't do that.s
// NOTE: There probably won't be any atoms in these turfs, but just in case we should call these procs.
A.contents.Add(T)
if(old_area)
// Handle dynamic lighting update if
if(T.dynamic_lighting && old_area.dynamic_lighting != A.dynamic_lighting)
if(A.dynamic_lighting)
T.lighting_build_overlay()
else
T.lighting_clear_overlay()
for(var/atom/movable/AM in T)
old_area.Exited(AM, A)
for(var/atom/movable/AM in T)
A.Entered(AM, old_area)
for(var/obj/machinery/M in T)
M.power_change()
/area/proc/get_contents()
return contents

View File

@@ -0,0 +1,164 @@
//
// Shuttles formerly required at least two areas in a subgroup if you want to move a shuttle from one
// place to another. Since shuttles now used landmarks instead these areas are deprecated!
// They are left here for the moment in order to make existing maps loadable, but should be phased out.
//
/area/shuttle/arrival/pre_game
icon_state = "shuttle2"
/area/shuttle/arrival/station
icon_state = "shuttle"
dynamic_lighting = 0
ambience = AMBIENCE_ARRIVALS
/area/shuttle/escape/station
name = "\improper Emergency Shuttle Station"
icon_state = "shuttle2"
dynamic_lighting = 0
/area/shuttle/escape/centcom
name = "\improper Emergency Shuttle CentCom"
icon_state = "shuttle"
/area/shuttle/escape/transit // the area to pass through for 3 minute transit
name = "\improper Emergency Shuttle Transit"
icon_state = "shuttle"
/area/shuttle/escape_pod1/station
icon_state = "shuttle2"
/area/shuttle/escape_pod1/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod1/transit
icon_state = "shuttle"
/area/shuttle/escape_pod2/station
icon_state = "shuttle2"
/area/shuttle/escape_pod2/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod2/transit
icon_state = "shuttle"
/area/shuttle/escape_pod3/station
icon_state = "shuttle2"
/area/shuttle/escape_pod3/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod3/transit
icon_state = "shuttle"
/area/shuttle/escape_pod4/station
icon_state = "shuttle2"
/area/shuttle/escape_pod4/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod4/transit
icon_state = "shuttle"
/area/shuttle/escape_pod5/station
icon_state = "shuttle2"
/area/shuttle/escape_pod5/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod5/transit
icon_state = "shuttle"
/area/shuttle/escape_pod6/station
icon_state = "shuttle2"
/area/shuttle/escape_pod6/centcom
icon_state = "shuttle"
/area/shuttle/escape_pod6/transit
icon_state = "shuttle"
/area/shuttle/large_escape_pod1/station
icon_state = "shuttle2"
/area/shuttle/large_escape_pod1/centcom
icon_state = "shuttle"
/area/shuttle/large_escape_pod1/transit
icon_state = "shuttle"
/area/shuttle/large_escape_pod2/station
icon_state = "shuttle2"
/area/shuttle/large_escape_pod2/centcom
icon_state = "shuttle"
/area/shuttle/large_escape_pod2/transit
icon_state = "shuttle"
/area/shuttle/cryo/station
icon_state = "shuttle2"
base_turf = /turf/simulated/mineral/floor/ignore_mapgen
/area/shuttle/cryo/centcom
icon_state = "shuttle"
/area/shuttle/cryo/transit
icon_state = "shuttle"
/area/shuttle/mining/station
icon_state = "shuttle2"
/area/shuttle/mining/outpost
icon_state = "shuttle"
/area/shuttle/trade/centcom
name = "\improper Trade Shuttle CentCom"
icon_state = "shuttlered"
/area/shuttle/trade/station
name = "\improper Trade Shuttle"
icon_state = "shuttlered"
/area/shuttle/thunderdome/grnshuttle
name = "\improper Thunderdome GRN Shuttle"
icon_state = "green"
/area/shuttle/thunderdome/grnshuttle/dome
name = "\improper GRN Shuttle"
icon_state = "shuttlegrn"
/area/shuttle/thunderdome/grnshuttle/station
name = "\improper GRN Station"
icon_state = "shuttlegrn2"
/area/shuttle/thunderdome/redshuttle
name = "\improper Thunderdome RED Shuttle"
icon_state = "red"
/area/shuttle/thunderdome/redshuttle/dome
name = "\improper RED Shuttle"
icon_state = "shuttlered"
/area/shuttle/thunderdome/redshuttle/station
name = "\improper RED Station"
icon_state = "shuttlered2"
/area/shuttle/research/station
icon_state = "shuttle2"
/area/shuttle/research/outpost
icon_state = "shuttle"
/area/supply/station
name = "Supply Shuttle"
icon_state = "shuttle3"
requires_power = 0
base_turf = /turf/space
/area/supply/dock
name = "Supply Shuttle"
icon_state = "shuttle3"
requires_power = 0
base_turf = /turf/space

View File

@@ -453,7 +453,7 @@
if(z in using_map.sealed_levels)
return
if(config.use_overmap)
if(using_map.use_overmap)
overmap_spacetravel(get_turf(src), src)
return

View File

@@ -56,7 +56,7 @@
var/orders[0]
var/receipts[0]
var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle
var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle
if(shuttle)
if(shuttle.has_arrive_time())
shuttle_status["location"] = "In transit"
@@ -66,8 +66,8 @@
else
shuttle_status["time"] = 0
if(shuttle.at_station())
if(shuttle.docking_controller)
switch(shuttle.docking_controller.get_docking_status())
if(shuttle.shuttle_docking_controller)
switch(shuttle.shuttle_docking_controller.get_docking_status())
if("docked")
shuttle_status["location"] = "Docked"
shuttle_status["mode"] = SUP_SHUTTLE_DOCKED
@@ -192,7 +192,7 @@
if(!supply_controller)
to_world_log("## ERROR: The supply_controller datum is missing.")
return
var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle
var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle
if (!shuttle)
to_world_log("## ERROR: The supply shuttle datum is missing.")
return

View File

@@ -178,6 +178,14 @@
icon = 'icons/obj/doors/Doorext.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_ext
/obj/machinery/door/airlock/external/glass/bolted
icon_state = "door_locked" // So it looks visibly bolted in map editor
locked = 1
// For convenience in making docking ports: one that is pre-bolted with frequency set!
/obj/machinery/door/airlock/external/glass/bolted/cycling
frequency = 1379
/obj/machinery/door/airlock/glass_external
name = "External Airlock"
icon = 'icons/obj/doors/Doorextglass.dmi'

View File

@@ -2,6 +2,7 @@
/obj/machinery/embedded_controller/radio/airlock
// Setup parameters only
radio_filter = RADIO_AIRLOCK
program = /datum/computer/file/embedded_program/airlock
var/tag_exterior_door
var/tag_interior_door
var/tag_airpump
@@ -11,11 +12,22 @@
var/tag_airlock_mech_sensor
var/tag_shuttle_mech_sensor
var/tag_secure = 0
var/list/dummy_terminals = list()
var/cycle_to_external_air = 0
/obj/machinery/embedded_controller/radio/airlock/Initialize()
. = ..()
program = new/datum/computer/file/embedded_program/airlock(src)
/obj/machinery/embedded_controller/radio/airlock/Destroy()
// TODO - Leshana - Implement dummy terminals
//for(var/thing in dummy_terminals)
// var/obj/machinery/dummy_airlock_controller/dummy = thing
// dummy.master_controller = null
//dummy_terminals.Cut()
return ..()
/obj/machinery/embedded_controller/radio/airlock/CanUseTopic(var/mob/user)
if(!allowed(user))
return min(STATUS_UPDATE, ..())
else
return ..()
//Advanced airlock controller for when you want a more versatile airlock controller - useful for turning simple access control rooms into airlocks
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller
@@ -37,43 +49,20 @@
if (!ui)
ui = new(user, src, ui_key, "advanced_airlock_console.tmpl", name, 470, 290)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/Topic(href, href_list)
if(..())
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext")
clean = 1
if("cycle_int")
clean = 1
if("force_ext")
clean = 1
if("force_int")
clean = 1
if("abort")
clean = 1
if("purge")
clean = 1
if("secure")
clean = 1
if(clean)
program.receive_user_command(href_list["command"])
if("cycle_ext", "cycle_int", "force_ext", "force_int", "abort", "purge", "secure")
program.receive_user_command(href_list["command"])
return 1
//Airlock controller for airlock control - most airlocks on the station use this
/obj/machinery/embedded_controller/radio/airlock/airlock_controller
name = "Airlock Controller"
@@ -90,23 +79,16 @@
)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "simple_airlock_console.tmpl", name, 470, 290)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/Topic(href, href_list)
if(..())
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext")
@@ -125,7 +107,6 @@
return 1
//Access controller for door control - used in virology and the like
/obj/machinery/embedded_controller/radio/airlock/access_controller
icon = 'icons/obj/airlock_machines.dmi'
@@ -154,23 +135,16 @@
)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "door_access_console.tmpl", name, 330, 220)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/airlock/access_controller/Topic(href, href_list)
if(..())
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext_door")

View File

@@ -0,0 +1,51 @@
// Provides remote access to a controller (since they must be unique).
/obj/machinery/dummy_airlock_controller
name = "airlock control terminal"
icon = 'icons/obj/airlock_machines.dmi'
icon_state = "airlock_control_standby"
layer = ABOVE_OBJ_LAYER
var/id_tag
var/datum/topic_state/remote/remote_state
var/obj/machinery/embedded_controller/radio/airlock/master_controller
/obj/machinery/dummy_airlock_controller/process()
if(master_controller)
appearance = master_controller
. = ..()
/obj/machinery/dummy_airlock_controller/Initialize()
. = ..()
if(id_tag)
for(var/obj/machinery/embedded_controller/radio/airlock/_master in SSmachines.machinery)
if(_master.id_tag == id_tag)
master_controller = _master
master_controller.dummy_terminals += src
break
if(!master_controller)
qdel(src)
else
remote_state = new /datum/topic_state/remote(src, master_controller)
/obj/machinery/dummy_airlock_controller/Destroy()
if(master_controller)
master_controller.dummy_terminals -= src
if(remote_state)
qdel(remote_state)
remote_state = null
return ..()
/obj/machinery/dummy_airlock_controller/interface_interact(var/mob/user)
open_remote_ui(user)
return TRUE
/obj/machinery/dummy_airlock_controller/proc/open_remote_ui(var/mob/user)
if(master_controller)
appearance = master_controller
return master_controller.ui_interact(user, state = remote_state)
/obj/machinery/dummy_airlock_controller/powered(var/chan = -1, var/area/check_area = null)
if(master_controller)
var/area/A = get_area(master_controller)
return master_controller.powered(chan, A)
return ..()

View File

@@ -1,8 +1,17 @@
/*
* NOTE - This file defines both these datums: Yes, you read that right. Its confusing. Lets try and break it down.
* /datum/computer/file/embedded_program/docking/airlock
* - A docking controller for an airlock based docking port
* /datum/computer/file/embedded_program/airlock/docking
* - An extension to the normal airlock program allows disabling of the regular airlock functions when docking
*/
//a docking port based on an airlock
/obj/machinery/embedded_controller/radio/airlock/docking_port
name = "docking port controller"
var/datum/computer/file/embedded_program/airlock/docking/airlock_program
var/datum/computer/file/embedded_program/docking/airlock/docking_program
var/display_name // For mappers to override docking_program.display_name (how would it show up on docking monitoring program)
tag_secure = 1
/obj/machinery/embedded_controller/radio/airlock/docking_port/Initialize()
@@ -10,9 +19,25 @@
airlock_program = new/datum/computer/file/embedded_program/airlock/docking(src)
docking_program = new/datum/computer/file/embedded_program/docking/airlock(src, airlock_program)
program = docking_program
if(display_name)
docking_program.display_name = display_name
/obj/machinery/embedded_controller/radio/airlock/docking_port/attackby(obj/item/W, mob/user)
if(istype(W,/obj/item/device/multitool)) //give them part of code, would take few tries to get full
var/datum/computer/file/embedded_program/docking/airlock/docking_program = program
var/code = docking_program.docking_codes
if(!code)
code = "N/A"
else
code = stars(code)
to_chat(user, "[W]'s screen displays '[code]'")
else
..()
/obj/machinery/embedded_controller/radio/airlock/docking_port/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/computer/file/embedded_program/docking/airlock/docking_program = program
var/datum/computer/file/embedded_program/airlock/docking/airlock_program = docking_program.airlock_program
data = list(
"chamber_pressure" = round(airlock_program.memory["chamber_sensor_pressure"]),
@@ -22,6 +47,8 @@
"docking_status" = docking_program.get_docking_status(),
"airlock_disabled" = !(docking_program.undocked() || docking_program.override_enabled),
"override_enabled" = docking_program.override_enabled,
"docking_codes" = docking_program.docking_codes,
"name" = docking_program.get_name()
)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
@@ -33,12 +60,9 @@
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/airlock/docking_port/Topic(href, href_list)
if(..())
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext")
@@ -60,11 +84,13 @@
return 1
///////////////////////////////////////////////////////////////////////////////
//A docking controller for an airlock based docking port
//
/datum/computer/file/embedded_program/docking/airlock
var/datum/computer/file/embedded_program/airlock/docking/airlock_program
/datum/computer/file/embedded_program/docking/airlock/New(var/obj/machinery/embedded_controller/M, var/datum/computer/file/embedded_program/airlock/docking/A)
..(M)
airlock_program = A
@@ -76,10 +102,10 @@
disable_override()
else
enable_override()
return
return TRUE
..(command)
airlock_program.receive_user_command(command) //pass along to subprograms
. = ..(command)
. = airlock_program.receive_user_command(command) || . //pass along to subprograms; bypass shortcircuit
/datum/computer/file/embedded_program/docking/airlock/process()
airlock_program.process()
@@ -91,7 +117,7 @@
//tell the docking port to start getting ready for docking - e.g. pressurize
/datum/computer/file/embedded_program/docking/airlock/prepare_for_docking()
airlock_program.begin_cycle_in()
airlock_program.begin_dock_cycle()
//are we ready for docking?
/datum/computer/file/embedded_program/docking/airlock/ready_for_docking()
@@ -99,14 +125,14 @@
//we are docked, open the doors or whatever.
/datum/computer/file/embedded_program/docking/airlock/finish_docking()
airlock_program.enable_mech_regulators()
airlock_program.enable_mech_regulation()
airlock_program.open_doors()
//tell the docking port to start getting ready for undocking - e.g. close those doors.
/datum/computer/file/embedded_program/docking/airlock/prepare_for_undocking()
airlock_program.stop_cycling()
airlock_program.close_doors()
airlock_program.disable_mech_regulators()
airlock_program.disable_mech_regulation()
//are we ready for undocking?
/datum/computer/file/embedded_program/docking/airlock/ready_for_undocking()
@@ -114,20 +140,22 @@
var/int_closed = airlock_program.check_interior_door_secured()
return (ext_closed || int_closed)
///////////////////////////////////////////////////////////////////////////////
//An airlock controller to be used by the airlock-based docking port controller.
//Same as a regular airlock controller but allows disabling of the regular airlock functions when docking
//
/datum/computer/file/embedded_program/airlock/docking
var/datum/computer/file/embedded_program/docking/airlock/master_prog
/datum/computer/file/embedded_program/airlock/docking/Destroy()
if(master_prog)
master_prog.airlock_program = null
master_prog = null
return ..()
/datum/computer/file/embedded_program/airlock/docking/receive_user_command(command)
if (master_prog.undocked() || master_prog.override_enabled) //only allow the port to be used as an airlock if nothing is docked here or the override is enabled
..(command)
/datum/computer/file/embedded_program/airlock/docking/proc/enable_mech_regulators()
enable_mech_regulation()
/datum/computer/file/embedded_program/airlock/docking/proc/disable_mech_regulators()
disable_mech_regulation()
return ..(command)
/datum/computer/file/embedded_program/airlock/docking/proc/open_doors()
toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "open")

View File

@@ -2,21 +2,15 @@
//this is the master controller, that things will try to dock with.
/obj/machinery/embedded_controller/radio/docking_port_multi
name = "docking port controller"
program = /datum/computer/file/embedded_program/docking/multi
var/child_tags_txt
var/child_names_txt
var/list/child_names = list()
var/datum/computer/file/embedded_program/docking/multi/docking_program
/obj/machinery/embedded_controller/radio/docking_port_multi/Initialize()
. = ..()
docking_program = new/datum/computer/file/embedded_program/docking/multi(src)
program = docking_program
var/list/names = splittext(child_names_txt, ";")
var/list/tags = splittext(child_tags_txt, ";")
if (names.len == tags.len)
for (var/i = 1; i <= tags.len; i++)
child_names[tags[i]] = names[i]
@@ -24,6 +18,7 @@
/obj/machinery/embedded_controller/radio/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/computer/file/embedded_program/docking/multi/docking_program = program // Cast to proper type
var/list/airlocks[child_names.len]
var/i = 1
@@ -44,24 +39,21 @@
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/docking_port_multi/Topic(href, href_list)
return
return 1 // Apparently we swallow all input (this is corrected legacy code)
//a docking port based on an airlock
// This is the actual controller that will be commanded by the master defined above
/obj/machinery/embedded_controller/radio/airlock/docking_port_multi
name = "docking port controller"
program = /datum/computer/file/embedded_program/airlock/multi_docking
var/master_tag //for mapping
var/datum/computer/file/embedded_program/airlock/multi_docking/airlock_program
tag_secure = 1
/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/Initialize()
. = ..()
airlock_program = new/datum/computer/file/embedded_program/airlock/multi_docking(src)
program = airlock_program
/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/computer/file/embedded_program/airlock/multi_docking/airlock_program // Cast to proper type
data = list(
"chamber_pressure" = round(airlock_program.memory["chamber_sensor_pressure"]),
@@ -82,12 +74,9 @@
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/Topic(href, href_list)
if(..())
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
if("cycle_ext")

View File

@@ -52,8 +52,8 @@
tag_interior_door = controller.tag_interior_door? controller.tag_interior_door : "[id_tag]_inner"
tag_airpump = controller.tag_airpump? controller.tag_airpump : "[id_tag]_pump"
tag_chamber_sensor = controller.tag_chamber_sensor? controller.tag_chamber_sensor : "[id_tag]_sensor"
tag_exterior_sensor = controller.tag_exterior_sensor
tag_interior_sensor = controller.tag_interior_sensor
tag_exterior_sensor = controller.tag_exterior_sensor || "[id_tag]_exterior_sensor"
tag_interior_sensor = controller.tag_interior_sensor || "[id_tag]_interior_sensor"
tag_airlock_mech_sensor = controller.tag_airlock_mech_sensor? controller.tag_airlock_mech_sensor : "[id_tag]_airlock_mech"
tag_shuttle_mech_sensor = controller.tag_shuttle_mech_sensor? controller.tag_shuttle_mech_sensor : "[id_tag]_shuttle_mech"
memory["secure"] = controller.tag_secure
@@ -117,6 +117,7 @@
/datum/computer/file/embedded_program/airlock/receive_user_command(command)
var/shutdown_pump = 0
. = TRUE
switch(command)
if("cycle_ext")
//If airlock is already cycled in this direction, just toggle the doors.
@@ -163,6 +164,8 @@
else
signalDoor(tag_interior_door, "unlock")
signalDoor(tag_exterior_door, "unlock")
else
. = FALSE
if(shutdown_pump)
signalPump(tag_airpump, 0) //send a signal to stop pressurizing
@@ -273,6 +276,9 @@
target_state = TARGET_INOPEN
memory["purge"] = cycle_to_external_air
/datum/computer/file/embedded_program/airlock/proc/begin_dock_cycle()
state = STATE_IDLE
target_state = TARGET_INOPEN
/datum/computer/file/embedded_program/airlock/proc/begin_cycle_out()
state = STATE_IDLE
target_state = TARGET_OUTOPEN

View File

@@ -12,51 +12,51 @@
/*
*** STATE TABLE ***
MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply.
MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready.
MODE_CLIENT|STATE_DOCKED idle - docked as client.
MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking.
MODE_SERVER|STATE_UNDOCKED should never happen.
MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready.
MODE_SERVER|STATE_DOCKED idle - docked as server
MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready.
MODE_NONE|STATE_UNDOCKED idle - not docked.
MODE_NONE|anything else should never happen.
*** Docking Signals ***
Docking
Client sends request_dock
Server sends confirm_dock to say that yes, we will serve your request
When client is ready, sends confirm_dock
Server sends confirm_dock back to indicate that docking is complete
Undocking
Client sends request_undock
When client is ready, sends confirm_undock
Server sends confirm_undock back to indicate that docking is complete
Note that in both cases each side exchanges confirm_dock before the docking operation is considered done.
The client first sends a confirm message to indicate it is ready, and then finally the server will send it's
The client first sends a confirm message to indicate it is ready, and then finally the server will send it's
confirm message to indicate that the operation is complete.
Note also that when docking, the server sends an additional confirm message. This is because before docking,
the server and client do not have a defined relationship. Before undocking, the server and client are already
related to each other, thus the extra confirm message is not needed.
*** Override, what is it? ***
The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking.
Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle.
This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called.
The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced
launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock
launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock
(which usually just means closing and locking the doors).
In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled.
*/
@@ -67,27 +67,31 @@
var/control_mode = MODE_NONE
var/response_sent = 0 //so we don't spam confirmation messages
var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed
var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually
var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client
var/docking_codes //would only allow docking when receiving signal with these, if set
var/display_name //Override the name shown on docking monitoring program; defaults to area name + coordinates if unset
/datum/computer/file/embedded_program/docking/New()
..()
var/datum/existing = locate(id_tag) //in case a datum already exists with our tag
if(existing)
existing.tag = null //take it from them
tag = id_tag //Greatly simplifies shuttle initialization
if(id_tag)
if(SSshuttles.docking_registry[id_tag])
crash_with("Docking controller tag [id_tag] had multiple associated programs.")
SSshuttles.docking_registry[id_tag] = src
/datum/computer/file/embedded_program/docking/Destroy()
SSshuttles.docking_registry -= id_tag
return ..()
/datum/computer/file/embedded_program/docking/receive_signal(datum/signal/signal, receive_method, receive_param)
var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id
var/command = signal.data["command"]
var/recipient = signal.data["recipient"] //the intended recipient of the docking signal
if (recipient != id_tag)
return //this signal is not for us
switch (command)
if ("confirm_dock")
if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target)
@@ -95,7 +99,7 @@
broadcast_docking_status()
if (!override_enabled)
prepare_for_docking()
else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target)
dock_state = STATE_DOCKED
broadcast_docking_status()
@@ -104,19 +108,27 @@
response_sent = 0
else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process
received_confirm = 1
if ("request_dock")
if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED)
tag_target = receive_tag
if(docking_codes)
var/code = signal.data["code"]
if(code != docking_codes)
testing("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]")
return
control_mode = MODE_SERVER
dock_state = STATE_DOCKING
broadcast_docking_status()
tag_target = receive_tag
if (!override_enabled)
prepare_for_docking()
send_docking_command(tag_target, "confirm_dock") //acknowledge the request
if ("confirm_undock")
if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target)
if (!override_enabled)
@@ -129,7 +141,7 @@
if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target)
dock_state = STATE_UNDOCKING
broadcast_docking_status()
if (!override_enabled)
prepare_for_undocking()
@@ -145,38 +157,38 @@
if (!response_sent)
send_docking_command(tag_target, "confirm_dock") //tell the server we're ready
response_sent = 1
else if (control_mode == MODE_SERVER && received_confirm)
send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking.
dock_state = STATE_DOCKED
broadcast_docking_status()
if (!override_enabled)
finish_docking() //server done docking!
response_sent = 0
received_confirm = 0
if (STATE_UNDOCKING)
if (ready_for_undocking())
if (control_mode == MODE_CLIENT)
if (!response_sent)
send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock.
response_sent = 1
else if (control_mode == MODE_SERVER && received_confirm)
send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking.
if (!override_enabled)
finish_undocking()
reset() //server is done undocking!
if (response_sent || resend_counter > 0)
resend_counter++
if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING))
response_sent = 0
resend_counter = 0
//handle invalid states
if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED)
if (tag_target)
@@ -189,22 +201,22 @@
/datum/computer/file/embedded_program/docking/proc/initiate_docking(var/target)
if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake
return
tag_target = target
control_mode = MODE_CLIENT
send_docking_command(tag_target, "request_dock")
/datum/computer/file/embedded_program/docking/proc/initiate_undocking()
if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking
return
dock_state = STATE_UNDOCKING
broadcast_docking_status()
if (!override_enabled)
prepare_for_undocking()
send_docking_command(tag_target, "request_undock")
//tell the docking port to start getting ready for docking - e.g. pressurize
@@ -240,7 +252,7 @@
/datum/computer/file/embedded_program/docking/proc/reset()
dock_state = STATE_UNDOCKED
broadcast_docking_status()
control_mode = MODE_NONE
tag_target = null
response_sent = 0
@@ -267,6 +279,7 @@
signal.data["tag"] = id_tag
signal.data["command"] = command
signal.data["recipient"] = recipient
signal.data["code"] = docking_codes
post_signal(signal)
/datum/computer/file/embedded_program/docking/proc/broadcast_docking_status()
@@ -283,6 +296,8 @@
if (STATE_UNDOCKING) return "undocking"
if (STATE_DOCKED) return "docked"
/datum/computer/file/embedded_program/docking/proc/get_name()
return display_name ? display_name : "[get_area(master)] ([master.x], [master.y])"
#undef STATE_UNDOCKED
#undef STATE_DOCKING

View File

@@ -1,18 +1,20 @@
/obj/machinery/embedded_controller
var/datum/computer/file/embedded_program/program //the currently executing program
name = "Embedded Controller"
anchored = 1
use_power = 1
idle_power_usage = 10
var/datum/computer/file/embedded_program/program //the currently executing program
var/on = 1
obj/machinery/embedded_controller/radio/Destroy()
if(radio_controller)
radio_controller.remove_object(src,frequency)
..()
/obj/machinery/embedded_controller/Initialize()
if(ispath(program))
program = new program(src)
return ..()
/obj/machinery/embedded_controller/Destroy()
if(istype(program))
qdel(program) // the program will clear the ref in its Destroy
return ..()
/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line)
return 0
@@ -24,6 +26,17 @@ obj/machinery/embedded_controller/radio/Destroy()
program.receive_signal(signal, receive_method, receive_param)
//spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0
/obj/machinery/embedded_controller/Topic(href, href_list)
if((. = ..()))
return
if(usr)
usr.set_machine(src)
src.add_fingerprint(usr)
// We would now pass it to the program, except that some of our embedded controller types want to block certain commands.
// Until/unless that is refactored differently, we rely on subtypes to pass it on.
//if(program)
// return program.receive_user_command(href_list["command"])
/obj/machinery/embedded_controller/process()
if(program)
program.process()
@@ -40,14 +53,16 @@ obj/machinery/embedded_controller/radio/Destroy()
src.ui_interact(user)
/obj/machinery/embedded_controller/ui_interact()
return
//
// Embedded controller with a radio! (Most things (All things?) use this)
//
/obj/machinery/embedded_controller/radio
icon = 'icons/obj/airlock_machines.dmi'
icon_state = "airlock_control_standby"
power_channel = ENVIRON
density = 0
unacidable = 1
var/id_tag
//var/radio_power_use = 50 //power used to xmit signals
@@ -55,11 +70,15 @@ obj/machinery/embedded_controller/radio/Destroy()
var/frequency = 1379
var/radio_filter = null
var/datum/radio_frequency/radio_connection
unacidable = 1
/obj/machinery/embedded_controller/radio/Initialize()
set_frequency(frequency) // Set it before parent instantiates program
. = ..()
set_frequency(frequency)
/obj/machinery/embedded_controller/radio/Destroy()
if(radio_controller)
radio_controller.remove_object(src,frequency)
..()
/obj/machinery/embedded_controller/radio/update_icon()
if(on && program)

View File

@@ -11,8 +11,15 @@
var/obj/machinery/embedded_controller/radio/R = M
id_tag = R.id_tag
/datum/computer/file/embedded_program/Destroy()
if(master)
master.program = null
master = null
return ..()
// Return TRUE if was a command for us, otherwise return FALSE (so controllers with multiple programs can try each in turn until one accepts)
/datum/computer/file/embedded_program/proc/receive_user_command(command)
return
return FALSE
/datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param)
return

View File

@@ -1,16 +1,12 @@
//a docking port that uses a single door
/obj/machinery/embedded_controller/radio/simple_docking_controller
name = "docking hatch controller"
program = /datum/computer/file/embedded_program/docking/simple
var/tag_door
var/datum/computer/file/embedded_program/docking/simple/docking_program
/obj/machinery/embedded_controller/radio/simple_docking_controller/Initialize()
. = ..()
docking_program = new/datum/computer/file/embedded_program/docking/simple(src)
program = docking_program
/obj/machinery/embedded_controller/radio/simple_docking_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type
data = list(
"docking_status" = docking_program.get_docking_status(),
@@ -28,11 +24,8 @@
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/simple_docking_controller/Topic(href, href_list)
if(..())
return 1
usr.set_machine(src)
src.add_fingerprint(usr)
if((. = ..()))
return
var/clean = 0
switch(href_list["command"]) //anti-HTML-hacking checks
@@ -44,8 +37,7 @@
if(clean)
program.receive_user_command(href_list["command"])
return 0
return
//A docking controller program for a simple door based docking port
/datum/computer/file/embedded_program/docking/simple
@@ -76,6 +68,7 @@
..(signal, receive_method, receive_param)
/datum/computer/file/embedded_program/docking/simple/receive_user_command(command)
. = TRUE
switch(command)
if("force_door")
if (override_enabled)
@@ -88,7 +81,8 @@
disable_override()
else
enable_override()
else
. = FALSE
/datum/computer/file/embedded_program/docking/simple/proc/signal_door(var/command)
var/datum/signal/signal = new

View File

@@ -210,7 +210,7 @@
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
/obj/machinery/status_display/proc/get_supply_shuttle_timer()
var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle
var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle
if(!shuttle)
return "Error"

View File

@@ -6,7 +6,7 @@
message1 = "CARGO"
message2 = ""
var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle
var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle
if(!shuttle)
message2 = "Error"
else if(shuttle.has_arrive_time())

View File

@@ -425,7 +425,7 @@ var/list/civilian_cartridges = list(
if(mode==47)
var/supplyData[0]
var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle
var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle
if (shuttle)
supplyData["shuttle_moving"] = shuttle.has_arrive_time()
supplyData["shuttle_eta"] = shuttle.eta_minutes()

View File

@@ -394,7 +394,7 @@
// code\game\machinery\computer\supply.dm, starting at line 55
/obj/item/weapon/commcard/proc/get_supply_shuttle_status()
var/shuttle_status[0]
var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle
var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle
if(shuttle)
if(shuttle.has_arrive_time())
shuttle_status["location"] = "In transit"
@@ -404,8 +404,8 @@
else
shuttle_status["time"] = 0
if(shuttle.at_station())
if(shuttle.docking_controller)
switch(shuttle.docking_controller.get_docking_status())
if(shuttle.shuttle_docking_controller)
switch(shuttle.shuttle_docking_controller.get_docking_status())
if("docked")
shuttle_status["location"] = "Docked"
shuttle_status["mode"] = SUP_SHUTTLE_DOCKED

View File

@@ -55,7 +55,7 @@
return new_dest
/obj/landed_holder/proc/leave_turf()
/obj/landed_holder/proc/leave_turf(var/turf/base_turf = null)
var/turf/new_source
//Change our source to whatever it was before
if(turf_type)
@@ -67,7 +67,7 @@
new_source.underlays = underlays
new_source.decals = decals
else
new_source = my_turf.ChangeTurf(get_base_turf_by_area(my_turf),,1)
new_source = my_turf.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(my_turf),,1)
return new_source

View File

@@ -24,6 +24,9 @@
for(var/obj/O in src)
O.hide(0)
/turf/space/is_solid_structure()
return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice
/turf/space/proc/update_starlight()
if(!config.starlight)
return

View File

@@ -64,6 +64,10 @@
/turf/proc/is_intact()
return 0
// Used by shuttle code to check if this turf is empty enough to not crush want it lands on.
/turf/proc/is_solid_structure()
return 1
/turf/attack_hand(mob/user)
if(!(user.canmove) || user.restrained() || !(user.pulling))
return 0

View File

@@ -2,17 +2,17 @@
name = "Jump a Shuttle"
/datum/admin_secret_item/admin_secret/jump_shuttle/can_execute(var/mob/user)
if(!shuttle_controller) return 0
if(!SSshuttles) return 0
return ..()
/datum/admin_secret_item/admin_secret/jump_shuttle/execute(var/mob/user)
. = ..()
if(!.)
return
var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles
var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in SSshuttles.shuttles
if (!shuttle_tag) return
var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag]
var/origin_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!origin_area) return

View File

@@ -2,7 +2,7 @@
name = "Launch a Shuttle"
/datum/admin_secret_item/admin_secret/launch_shuttle/can_execute(var/mob/user)
if(!shuttle_controller) return 0
if(!SSshuttles) return 0
return ..()
/datum/admin_secret_item/admin_secret/launch_shuttle/execute(var/mob/user)
@@ -10,15 +10,15 @@
if(!.)
return
var/list/valid_shuttles = list()
for (var/shuttle_tag in shuttle_controller.shuttles)
if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry))
for (var/shuttle_tag in SSshuttles.shuttles)
if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock))
valid_shuttles += shuttle_tag
var/shuttle_tag = input(user, "Which shuttle do you want to launch?") as null|anything in valid_shuttles
if (!shuttle_tag)
return
var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag]
if (S.can_launch())
S.launch(user)
log_and_message_admins("launched the [shuttle_tag] shuttle", user)

View File

@@ -2,7 +2,7 @@
name = "Launch a Shuttle (Forced)"
/datum/admin_secret_item/admin_secret/launch_shuttle_forced/can_execute(var/mob/user)
if(!shuttle_controller) return 0
if(!SSshuttles) return 0
return ..()
/datum/admin_secret_item/admin_secret/launch_shuttle_forced/execute(var/mob/user)
@@ -10,15 +10,15 @@
if(!.)
return
var/list/valid_shuttles = list()
for (var/shuttle_tag in shuttle_controller.shuttles)
if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry))
for (var/shuttle_tag in SSshuttles.shuttles)
if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock))
valid_shuttles += shuttle_tag
var/shuttle_tag = input(user, "Which shuttle's launch do you want to force?") as null|anything in valid_shuttles
if (!shuttle_tag)
return
var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag]
if (S.can_force())
S.force_launch(user)
log_and_message_admins("forced the [shuttle_tag] shuttle", user)

View File

@@ -2,7 +2,7 @@
name = "Move a Shuttle"
/datum/admin_secret_item/admin_secret/move_shuttle/can_execute(var/mob/user)
if(!shuttle_controller) return 0
if(!SSshuttles) return 0
return ..()
/datum/admin_secret_item/admin_secret/move_shuttle/execute(var/mob/user)
@@ -13,16 +13,15 @@
if (confirm == "Cancel")
return
var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles
var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in SSshuttles.shuttles
if (!shuttle_tag) return
var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag]
var/origin_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!origin_area) return
var/destination_tag = input(user, "Which landmark do you want to jump to? (IF YOU GET THIS WRONG THINGS WILL BREAK)") as null|anything in SSshuttles.registered_shuttle_landmarks
if (!destination_tag) return
var/destination_location = SSshuttles.get_landmark(destination_tag)
if (!destination_location) return
var/destination_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!destination_area) return
S.move(origin_area, destination_area)
S.attempt_move(destination_location)
log_and_message_admins("moved the [shuttle_tag] shuttle", user)

View File

@@ -22,7 +22,7 @@
/turf/proc/lighting_clear_overlay()
if(lighting_overlay)
qdel(lighting_overlay)
qdel(lighting_overlay, force = TRUE)
for(var/datum/lighting_corner/C in corners)
C.update_active()

View File

@@ -39,6 +39,9 @@
if (SSatoms.initialized == INITIALIZATION_INSSATOMS)
return // let proper initialisation handle it later
var/prev_shuttle_queue_state = SSshuttles.block_init_queue
SSshuttles.block_init_queue = TRUE
var/list/atom/atoms = list()
var/list/area/areas = list()
var/list/obj/structure/cable/cables = list()
@@ -71,6 +74,9 @@
var/area/A = I
A.power_change()
SSshuttles.block_init_queue = prev_shuttle_queue_state
SSshuttles.process_init_queues() // We will flush the queue unless there were other blockers, in which case they will do it.
admin_notice("<span class='danger'>Submap initializations finished.</span>", R_DEBUG)
/datum/map_template/proc/load_new_z(var/centered = FALSE, var/orientation = 0)

View File

@@ -150,6 +150,9 @@
var/turf/below = GetBelow(src)
return !below || below.is_space()
/turf/simulated/open/is_solid_structure()
return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice (same as space)
/turf/simulated/open/is_safe_to_enter(mob/living/L)
if(L.can_fall())
if(!locate(/obj/structure/stairs) in GetBelow(src))

View File

@@ -3,6 +3,57 @@
//How far from the edge of overmap zlevel could randomly placed objects spawn
#define OVERMAP_EDGE 7
//Dimension of overmap (squares 4 lyfe)
var/global/list/map_sectors = list()
/area/overmap/
name = "System Map"
icon_state = "start"
requires_power = 0
base_turf = /turf/unsimulated/map
/turf/unsimulated/map
icon = 'icons/turf/space.dmi'
icon_state = "map"
/turf/unsimulated/map/edge
opacity = 1
density = 1
/turf/unsimulated/map/New()
..()
name = "[x]-[y]"
var/list/numbers = list()
if(x == 1 || x == global.using_map.overmap_size)
numbers += list("[round(y/10)]","[round(y%10)]")
if(y == 1 || y == global.using_map.overmap_size)
numbers += "-"
if(y == 1 || y == global.using_map.overmap_size)
numbers += list("[round(x/10)]","[round(x%10)]")
for(var/i = 1 to numbers.len)
var/image/I = image('icons/effects/numbers.dmi',numbers[i])
I.pixel_x = 5*i - 2
I.pixel_y = world.icon_size/2 - 3
if(y == 1)
I.pixel_y = 3
I.pixel_x = 5*i + 4
if(y == global.using_map.overmap_size)
I.pixel_y = world.icon_size - 9
I.pixel_x = 5*i + 4
if(x == 1)
I.pixel_x = 5*i - 2
if(x == global.using_map.overmap_size)
I.pixel_x = 5*i + 2
overlays += I
//list used to track which zlevels are being 'moved' by the proc below
var/list/moving_levels = list()
//Proc to 'move' stars in spess
@@ -35,6 +86,7 @@ proc/toggle_move_stars(zlevel, direction)
AM.throw_at(get_step(T,reverse_direction(direction)), 5, 1)
/*
//list used to cache empty zlevels to avoid nedless map bloat
var/list/cached_space = list()
@@ -99,3 +151,4 @@ proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A)
testing("Catching [M] for future use")
source.loc = null
cached_space += source
*/

View File

@@ -0,0 +1,39 @@
/obj/effect/overmap
name = "map object"
icon = 'icons/obj/overmap.dmi'
icon_state = "object"
var/known = 1 //shows up on nav computers automatically
var/scannable //if set to TRUE will show up on ship sensors for detailed scans
//Overlay of how this object should look on other skyboxes
/obj/effect/overmap/proc/get_skybox_representation()
return
/obj/effect/overmap/proc/get_scan_data(mob/user)
return desc
/obj/effect/overmap/Initialize()
. = ..()
if(!global.using_map.use_overmap)
return INITIALIZE_HINT_QDEL
if(known)
//layer = ABOVE_LIGHTING_LAYER
plane = PLANE_LIGHTING_ABOVE
// TODO - Leshana HELM
// for(var/obj/machinery/computer/ship/helm/H in global.machines)
// H.get_known_sectors()
/*
TODO - Leshana - No need for this, we don't have skyboxes
/obj/effect/overmap/Crossed(var/obj/effect/overmap/visitable/other)
if(istype(other))
for(var/obj/effect/overmap/visitable/O in loc)
SSskybox.rebuild_skyboxes(O.map_z)
/obj/effect/overmap/Uncrossed(var/obj/effect/overmap/visitable/other)
if(istype(other))
SSskybox.rebuild_skyboxes(other.map_z)
for(var/obj/effect/overmap/visitable/O in loc)
SSskybox.rebuild_skyboxes(O.map_z)
*/

View File

@@ -1,124 +1,136 @@
//===================================================================================
//Hook for building overmap
//Overmap object representing zlevel(s)
//===================================================================================
var/global/list/map_sectors = list()
/hook/startup/proc/build_map()
if(!config.use_overmap)
return 1
testing("Building overmap...")
var/obj/effect/mapinfo/data
for(var/level in 1 to world.maxz)
data = locate("sector[level]")
if (data)
testing("Located sector \"[data.name]\" at [data.mapx],[data.mapy] corresponding to zlevel [level]")
map_sectors["[level]"] = new data.obj_type(data)
return 1
//===================================================================================
//Metaobject for storing information about sector this zlevel is representing.
//Should be placed only once on every zlevel.
//===================================================================================
/obj/effect/mapinfo/
name = "map info metaobject"
icon = 'icons/mob/screen1.dmi'
icon_state = "x2"
invisibility = 101
var/obj_type //type of overmap object it spawns
var/landing_area //type of area used as inbound shuttle landing, null if no shuttle landing area
var/zlevel
var/mapx //coordinates on the
var/mapy //overmap zlevel
var/known = 1
/obj/effect/mapinfo/New()
tag = "sector[z]"
zlevel = z
loc = null
/obj/effect/mapinfo/sector
name = "generic sector"
obj_type = /obj/effect/map/sector
/obj/effect/mapinfo/ship
name = "generic ship"
obj_type = /obj/effect/map/ship
//===================================================================================
//Overmap object representing zlevel
//===================================================================================
/obj/effect/map
/obj/effect/overmap/visitable
name = "map object"
icon = 'icons/obj/items.dmi'
icon_state = "sheet-plasteel"
var/map_z = 0
var/area/shuttle/shuttle_landing
var/always_known = 1
scannable = TRUE
/obj/effect/map/New(var/obj/effect/mapinfo/data)
map_z = data.zlevel
name = data.name
always_known = data.known
if (data.icon != 'icons/mob/screen1.dmi')
icon = data.icon
icon_state = data.icon_state
if(data.desc)
desc = data.desc
var/new_x = data.mapx ? data.mapx : rand(OVERMAP_EDGE, world.maxx - OVERMAP_EDGE)
var/new_y = data.mapy ? data.mapy : rand(OVERMAP_EDGE, world.maxy - OVERMAP_EDGE)
loc = locate(new_x, new_y, OVERMAP_ZLEVEL)
var/list/map_z = list()
if(data.landing_area)
shuttle_landing = locate(data.landing_area)
var/list/initial_generic_waypoints //store landmark_tag of landmarks that should be added to the actual lists below on init.
var/list/initial_restricted_waypoints //For use with non-automatic landmarks (automatic ones add themselves).
/obj/effect/map/CanPass(atom/movable/A)
testing("[A] attempts to enter sector\"[name]\"")
return 1
var/list/generic_waypoints = list() //waypoints that any shuttle can use
var/list/restricted_waypoints = list() //waypoints for specific shuttles
var/docking_codes
/obj/effect/map/Crossed(atom/movable/A)
testing("[A] has entered sector\"[name]\"")
if (istype(A,/obj/effect/map/ship))
var/obj/effect/map/ship/S = A
S.current_sector = src
var/start_x //Coordinates for self placing
var/start_y //will use random values if unset
/obj/effect/map/Uncrossed(atom/movable/A)
testing("[A] has left sector\"[name]\"")
if (istype(A,/obj/effect/map/ship))
var/obj/effect/map/ship/S = A
S.current_sector = null
var/base = 0 //starting sector, counts as station_levels
var/in_space = 1 //can be accessed via lucky EVA
/obj/effect/map/sector
var/hide_from_reports = FALSE
var/has_distress_beacon
/obj/effect/overmap/visitable/Initialize()
. = ..()
if(. == INITIALIZE_HINT_QDEL)
return
find_z_levels() // This populates map_z and assigns z levels to the ship.
register_z_levels() // This makes external calls to update global z level information.
if(!global.using_map.overmap_z)
build_overmap()
start_x = start_x || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
start_y = start_y || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
forceMove(locate(start_x, start_y, global.using_map.overmap_z))
docking_codes = "[ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))]"
testing("Located sector \"[name]\" at [start_x],[start_y], containing Z [english_list(map_z)]")
LAZYADD(SSshuttles.sectors_to_initialize, src) //Queued for further init. Will populate the waypoint lists; waypoints not spawned yet will be added in as they spawn.
SSshuttles.process_init_queues()
//This is called later in the init order by SSshuttles to populate sector objects. Importantly for subtypes, shuttles will be created by then.
/obj/effect/overmap/visitable/proc/populate_sector_objects()
// TODO - Leshana - Implement
///obj/effect/overmap/visitable/proc/get_areas()
// return get_filtered_areas(list(/proc/area_belongs_to_zlevels = map_z))
/obj/effect/overmap/visitable/proc/find_z_levels()
map_z = GetConnectedZlevels(z)
/obj/effect/overmap/visitable/proc/register_z_levels()
for(var/zlevel in map_z)
map_sectors["[zlevel]"] = src
global.using_map.player_levels |= map_z
if(!in_space)
global.using_map.sealed_levels |= map_z
if(base)
global.using_map.station_levels |= map_z
global.using_map.contact_levels |= map_z
global.using_map.map_levels |= map_z
//Helper for init.
/obj/effect/overmap/visitable/proc/check_ownership(obj/object)
if((object.z in map_z) && !(get_area(object) in SSshuttles.shuttle_areas))
return 1
//If shuttle_name is false, will add to generic waypoints; otherwise will add to restricted. Does not do checks.
/obj/effect/overmap/visitable/proc/add_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name)
landmark.sector_set(src, shuttle_name)
if(shuttle_name)
LAZYADD(restricted_waypoints[shuttle_name], landmark)
else
generic_waypoints += landmark
/obj/effect/overmap/visitable/proc/remove_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name)
if(shuttle_name)
var/list/shuttles = restricted_waypoints[shuttle_name]
LAZYREMOVE(shuttles, landmark)
else
generic_waypoints -= landmark
/obj/effect/overmap/visitable/proc/get_waypoints(var/shuttle_name)
. = list()
for(var/obj/effect/overmap/visitable/contained in src)
. += contained.get_waypoints(shuttle_name)
for(var/thing in generic_waypoints)
.[thing] = name
if(shuttle_name in restricted_waypoints)
for(var/thing in restricted_waypoints[shuttle_name])
.[thing] = name
/obj/effect/overmap/visitable/proc/generate_skybox()
return
/obj/effect/overmap/visitable/sector
name = "generic sector"
desc = "Sector with some stuff in it."
icon_state = "sector"
anchored = 1
//Space stragglers go here
// Because of the way these are spawned, they will potentially have their invisibility adjusted by the turfs they are mapped on
// prior to being moved to the overmap. This blocks that. Use set_invisibility to adjust invisibility as needed instead.
/obj/effect/overmap/visitable/sector/hide()
/obj/effect/map/sector/temporary
name = "Deep Space"
icon_state = ""
always_known = 0
/proc/build_overmap()
if(!global.using_map.use_overmap)
return 1
/obj/effect/map/sector/temporary/New(var/nx, var/ny, var/nz)
loc = locate(nx, ny, OVERMAP_ZLEVEL)
map_z = nz
map_sectors["[map_z]"] = src
testing("Temporary sector at [x],[y] was created, corresponding zlevel is [map_z].")
testing("Building overmap...")
world.maxz++
global.using_map.overmap_z = world.maxz
/obj/effect/map/sector/temporary/Destroy()
map_sectors["[map_z]"] = null
testing("Temporary sector at [x],[y] was deleted.")
if (can_die())
testing("Associated zlevel disappeared.")
world.maxz--
testing("Putting overmap on [global.using_map.overmap_z]")
var/area/overmap/A = new
for (var/square in block(locate(1,1,global.using_map.overmap_z), locate(global.using_map.overmap_size,global.using_map.overmap_size,global.using_map.overmap_z)))
var/turf/T = square
if(T.x == global.using_map.overmap_size || T.y == global.using_map.overmap_size)
T = T.ChangeTurf(/turf/unsimulated/map/edge)
else
T = T.ChangeTurf(/turf/unsimulated/map)
ChangeArea(T, A)
/obj/effect/map/sector/temporary/proc/can_die(var/mob/observer)
testing("Checking if sector at [map_z] can die.")
for(var/mob/M in player_list)
if(M != observer && M.z == map_z)
testing("There are people on it.")
return 0
global.using_map.sealed_levels |= global.using_map.overmap_z
testing("Overmap build complete.")
return 1

View File

@@ -0,0 +1,114 @@
//list used to cache empty zlevels to avoid nedless map bloat
var/list/cached_space = list()
//Space stragglers go here
/obj/effect/overmap/visitable/sector/temporary
name = "Deep Space"
invisibility = 101
known = 0
/obj/effect/overmap/visitable/sector/temporary/New(var/nx, var/ny, var/nz)
loc = locate(nx, ny, global.using_map.overmap_z)
x = nx
y = ny
map_z += nz
map_sectors["[nz]"] = src
testing("Temporary sector at [x],[y] was created, corresponding zlevel is [nz].")
/obj/effect/overmap/visitable/sector/temporary/Destroy()
map_sectors["[map_z]"] = null
testing("Temporary sector at [x],[y] was deleted.")
/obj/effect/overmap/visitable/sector/temporary/proc/can_die(var/mob/observer)
testing("Checking if sector at [map_z[1]] can die.")
for(var/mob/M in global.player_list)
if(M != observer && (M.z in map_z))
testing("There are people on it.")
return 0
return 1
proc/get_deepspace(x,y)
var/obj/effect/overmap/visitable/sector/temporary/res = locate(x,y,global.using_map.overmap_z)
if(istype(res))
return res
else if(cached_space.len)
res = cached_space[cached_space.len]
cached_space -= res
res.x = x
res.y = y
return res
else
return new /obj/effect/overmap/visitable/sector/temporary(x, y, global.using_map.get_empty_zlevel())
/atom/movable/proc/lost_in_space()
for(var/atom/movable/AM in contents)
if(!AM.lost_in_space())
return FALSE
return TRUE
/mob/lost_in_space()
return isnull(client)
/mob/living/carbon/human/lost_in_space()
return isnull(client) && !key && stat == DEAD
proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A)
if (!T || !A)
return
var/obj/effect/overmap/visitable/M = map_sectors["[T.z]"]
if (!M)
return
if(A.lost_in_space())
if(!QDELETED(A))
qdel(A)
return
var/nx = 1
var/ny = 1
var/nz = 1
if(T.x <= TRANSITIONEDGE)
nx = world.maxx - TRANSITIONEDGE - 2
ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
else if (A.x >= (world.maxx - TRANSITIONEDGE - 1))
nx = TRANSITIONEDGE + 2
ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
else if (T.y <= TRANSITIONEDGE)
ny = world.maxy - TRANSITIONEDGE -2
nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
else if (A.y >= (world.maxy - TRANSITIONEDGE - 1))
ny = TRANSITIONEDGE + 2
nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
testing("[A] spacemoving from [M] ([M.x], [M.y]).")
var/turf/map = locate(M.x,M.y,global.using_map.overmap_z)
var/obj/effect/overmap/visitable/TM
for(var/obj/effect/overmap/visitable/O in map)
if(O != M && O.in_space && prob(50))
TM = O
break
if(!TM)
TM = get_deepspace(M.x,M.y)
nz = pick(TM.map_z)
var/turf/dest = locate(nx,ny,nz)
if(dest)
A.forceMove(dest)
if(ismob(A))
var/mob/D = A
if(D.pulling)
D.pulling.forceMove(dest)
if(istype(M, /obj/effect/overmap/visitable/sector/temporary))
var/obj/effect/overmap/visitable/sector/temporary/source = M
if (source.can_die())
testing("Caching [M] for future use")
source.forceMove(null)
cached_space += source

View File

@@ -22,9 +22,12 @@
/obj/machinery/computer/roguezones/Initialize()
. = ..()
shuttle_control = locate(/obj/machinery/computer/shuttle_control/belter)
return INITIALIZE_HINT_LATELOAD
/obj/machinery/computer/roguezones/LateInitialize()
if(!rm_controller)
rm_controller = new /datum/controller/rogue()
shuttle_control = locate(/obj/machinery/computer/shuttle_control/belter)
/obj/machinery/computer/roguezones/attack_ai(mob/user as mob)
return attack_hand(user)
@@ -116,8 +119,9 @@
var/datum/rogue/zonemaster/ZM_target = rm_controller.prepare_new_zone()
//Update shuttle destination.
var/datum/shuttle/ferry/S = shuttle_controller.shuttles["Belter"]
S.area_offsite = ZM_target.myshuttle
var/datum/shuttle/autodock/ferry/S = SSshuttles.shuttles["Belter"]
S.landmark_offsite = ZM_target.myshuttle_landmark
S.next_location = S.get_location_waypoint(!S.location)
//Re-enable shuttle.
shuttle_control.shuttle_tag = "Belter"
@@ -145,7 +149,7 @@
if(rm_controller.current_zone && rm_controller.current_zone.is_occupied())
return // Not usable if shuttle is in occupied zone
// Okay do it
var/datum/shuttle/ferry/S = shuttle_controller.shuttles["Belter"]
var/datum/shuttle/autodock/ferry/S = SSshuttles.shuttles["Belter"]
S.launch(usr)
/obj/item/weapon/circuitboard/roguezones

View File

@@ -6,7 +6,8 @@
/datum/rogue/zonemaster
//our area
var/area/asteroid/rogue/myarea
var/area/shuttle/belter/myshuttle
// var/area/shuttle/belter/myshuttle
var/obj/effect/shuttle_landmark/myshuttle_landmark
//world.time
var/prepared_at = 0
@@ -32,7 +33,9 @@
/datum/rogue/zonemaster/New(var/area/A)
ASSERT(A)
myarea = A
myshuttle = locate(myarea.shuttle_area)
myshuttle_landmark = locate(/obj/effect/shuttle_landmark) in myarea
if(!istype(myshuttle_landmark))
warning("Zonemaster cannot find a shuttle landmark in its area '[A]'")
spawn(10) //This is called from controller New() and freaks out if this calls back too fast.
rm_controller.mark_clean(src)
@@ -46,7 +49,7 @@
if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it.
continue
var/area/A = get_area(H)
if((A == myarea) || (A == myshuttle)) //The loc of a turf is the area it is in.
if(A == myarea) //The loc of a turf is the area it is in.
humans++
return humans
@@ -380,6 +383,7 @@
var/ignored = list(
/obj/asteroid_spawner,
/obj/rogue_mobspawner,
/obj/effect/shuttle_landmark,
/obj/effect/step_trigger/teleporter/roguemine_loop/north,
/obj/effect/step_trigger/teleporter/roguemine_loop/south,
/obj/effect/step_trigger/teleporter/roguemine_loop/east,
@@ -389,6 +393,8 @@
if(I.type == /turf/space)
I.overlays.Cut()
continue
else if(!I.simulated)
continue
else if(I.type in ignored)
continue
qdel(I)
@@ -399,6 +405,8 @@
if(I.type == /turf/space)
I.overlays.Cut()
continue
else if(!I.simulated)
continue
else if(I.type in ignored)
continue
qdel(I)

View File

@@ -1,4 +1,22 @@
#define SHUTTLE_FLAGS_NONE 0
#define SHUTTLE_FLAGS_PROCESS 1
#define SHUTTLE_FLAGS_SUPPLY 2
#define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE)
// Shuttle flags
#define SHUTTLE_FLAGS_NONE 0
#define SHUTTLE_FLAGS_PROCESS 1 // Should be processed by shuttle subsystem
#define SHUTTLE_FLAGS_SUPPLY 2 // This is the supply shuttle. Why is this a tag?
#define SHUTTLE_FLAGS_ZERO_G 4 // Shuttle has no internal gravity generation
#define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE)
// shuttle_landmark flags
#define SLANDMARK_FLAG_AUTOSET 1 // If set, will set base area and turf type to same as where it was spawned at
#define SLANDMARK_FLAG_ZERO_G 2 // Zero-G shuttles moved here will lose gravity unless the area has ambient gravity.
// Ferry shuttle location constants
#define FERRY_LOCATION_STATION 0
#define FERRY_LOCATION_OFFSITE 1
#define FERRY_GOING_TO_STATION 0
#define FERRY_GOING_TO_OFFSITE 1
#ifndef DEBUG_SHUTTLES
#define log_shuttle(M)
#else
#define log_shuttle(M) log_debug("[M]")
#endif

View File

@@ -3,30 +3,34 @@
//
/datum/shuttle
var/list/crash_areas = null
var/list/crash_locations = null
var/crash_message = "Oops. The shuttle blew up." // Announcement made when shuttle crashes
/datum/shuttle/New()
if(crash_areas)
for(var/i in 1 to crash_areas.len)
crash_areas[i] = locate(crash_areas[i])
if(crash_locations)
var/crash_location_ids = crash_locations
crash_locations = list()
for(var/location_tag in crash_location_ids)
var/obj/effect/shuttle_landmark/L = SSshuttles.get_landmark(location_tag)
if(L)
crash_locations += L
..()
// Return 0 to let the jump continue, 1 to abort the jump.
// Default implementation checks if the shuttle should crash and if so crashes it.
/datum/shuttle/proc/process_longjump(var/area/origin, var/area/intended_destination, var/direction)
if(should_crash())
do_crash(origin)
/datum/shuttle/proc/process_longjump(var/obj/effect/shuttle_landmark/intended_destination)
if(should_crash(intended_destination))
do_crash(intended_destination)
return 1
// Decide if this is the time we crash. Return true for yes
/datum/shuttle/proc/should_crash(var/area/origin, var/area/intended_destination, var/direction)
/datum/shuttle/proc/should_crash(var/obj/effect/shuttle_landmark/intended_destination)
return FALSE
// Actually crash the shuttle
/datum/shuttle/proc/do_crash(var/area/source)
/datum/shuttle/proc/do_crash(var/obj/effect/shuttle_landmark/intended_destination)
// Choose the target
var/area/target = pick(crash_areas)
var/obj/effect/shuttle_landmark/target = pick(crash_locations)
ASSERT(istype(target))
// Blow up the target area?
@@ -34,16 +38,18 @@
//What people are we dealing with here
var/list/victims = list()
for(var/mob/living/L in source)
victims += L
spawn(0)
shake_camera(L,2 SECONDS,4)
for(var/area/A in shuttle_area)
for(var/mob/living/L in A)
victims += L
spawn(0)
shake_camera(L,2 SECONDS,4)
//SHAKA SHAKA SHAKA
sleep(2 SECONDS)
// Move the shuttle
move(source, target)
if (!attempt_move(target))
return // Lucky!
// Hide people
for(var/living in victims)
@@ -54,9 +60,11 @@
L.loc = null
// Blow up the shuttle
var/list/area_turfs = get_area_turfs(target)
var/turf/epicenter = pick(area_turfs)
var/boomsize = area_turfs.len / 10 // Bigger shuttle = bigger boom
var/list/shuttle_turfs = list()
for(var/area/A in shuttle_area)
shuttle_turfs += get_area_turfs(A)
var/turf/epicenter = pick(shuttle_turfs)
var/boomsize = shuttle_turfs.len / 10 // Bigger shuttle = bigger boom
explosion(epicenter, 0, boomsize, boomsize*2, boomsize*3)
moving_status = SHUTTLE_CRASHED
command_announcement.Announce("[crash_message]", "Shuttle Alert")

View File

@@ -1,50 +1,55 @@
/datum/shuttle/ferry/escape_pod
var/datum/computer/file/embedded_program/docking/simple/escape_pod/arming_controller
category = /datum/shuttle/ferry/escape_pod
/datum/shuttle/autodock/ferry/escape_pod
var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/arming_controller
category = /datum/shuttle/autodock/ferry/escape_pod
/datum/shuttle/ferry/escape_pod/New()
/datum/shuttle/autodock/ferry/escape_pod/New()
move_time = move_time + rand(-30, 60)
if(name in emergency_shuttle.escape_pods)
CRASH("An escape pod with the name '[name]' has already been defined.")
emergency_shuttle.escape_pods[name] = src
..()
/datum/shuttle/ferry/escape_pod/init_docking_controllers()
..()
arming_controller = locate(dock_target_station)
//find the arming controller (berth) - If not configured directly, try to read it from current location landmark
var/arming_controller_tag = arming_controller
if(!arming_controller && active_docking_controller)
arming_controller_tag = active_docking_controller.id_tag
arming_controller = SSshuttles.docking_registry[arming_controller_tag]
if(!istype(arming_controller))
warning("warning: escape pod with station dock tag [dock_target_station] could not find it's dock target!")
CRASH("Could not find arming controller for escape pod \"[name]\", tag was '[arming_controller_tag]'.")
if(docking_controller)
var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/controller_master = docking_controller.master
if(!istype(controller_master))
warning("warning: escape pod with docking tag [docking_controller_tag] could not find it's controller master!")
else
controller_master.pod = src
//find the pod's own controller
var/datum/computer/file/embedded_program/docking/simple/prog = SSshuttles.docking_registry[docking_controller_tag]
var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/controller_master = prog.master
if(!istype(controller_master))
CRASH("Escape pod \"[name]\" could not find it's controller master! docking_controller_tag=[docking_controller_tag]")
controller_master.pod = src
/datum/shuttle/ferry/escape_pod/can_launch()
/datum/shuttle/autodock/ferry/escape_pod/can_launch()
if(arming_controller && !arming_controller.armed) //must be armed
return 0
if(location)
return 0 //it's a one-way trip.
return ..()
/datum/shuttle/ferry/escape_pod/can_force()
/datum/shuttle/autodock/ferry/escape_pod/can_force()
if (arming_controller.eject_time && world.time < arming_controller.eject_time + 50)
return 0 //dont allow force launching until 5 seconds after the arming controller has reached it's countdown
return ..()
/datum/shuttle/ferry/escape_pod/can_cancel()
/datum/shuttle/autodock/ferry/escape_pod/can_cancel()
return 0
//This controller goes on the escape pod itself
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod
name = "escape pod controller"
var/datum/shuttle/ferry/escape_pod/pod
program = /datum/computer/file/embedded_program/docking/simple
var/datum/shuttle/autodock/ferry/escape_pod/pod
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type
data = list(
"docking_status" = docking_program.get_docking_status(),
@@ -64,17 +69,18 @@
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/Topic(href, href_list)
if(..())
return 1
if((. = ..()))
return
if("manual_arm")
pod.arming_controller.arm()
return TOPIC_REFRESH
if("force_launch")
if (pod.can_force())
pod.force_launch(src)
else if (emergency_shuttle.departed && pod.can_launch()) //allow players to manually launch ahead of time if the shuttle leaves
pod.launch(src)
return TOPIC_REFRESH
return 0
@@ -82,18 +88,15 @@
//This controller is for the escape pod berth (station side)
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth
name = "escape pod berth controller"
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/Initialize()
. = ..()
docking_program = new/datum/computer/file/embedded_program/docking/simple/escape_pod(src)
program = docking_program
program = /datum/computer/file/embedded_program/docking/simple/escape_pod_berth
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type
var/armed = null
if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod))
var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program
if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod_berth))
var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/P = docking_program
armed = P.armed
data = list(
@@ -114,44 +117,44 @@
if (!emagged)
to_chat(user, "<span class='notice'>You emag the [src], arming the escape pod!</span>")
emagged = 1
if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod))
var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program
if (istype(program, /datum/computer/file/embedded_program/docking/simple/escape_pod_berth))
var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/P = program
if (!P.armed)
P.arm()
return 1
//A docking controller program for a simple door based docking port
/datum/computer/file/embedded_program/docking/simple/escape_pod
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth
var/armed = 0
var/eject_delay = 10 //give latecomers some time to get out of the way if they don't make it onto the pod
var/eject_time = null
var/closing = 0
/datum/computer/file/embedded_program/docking/simple/escape_pod/proc/arm()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/proc/arm()
if(!armed)
armed = 1
open_door()
/datum/computer/file/embedded_program/docking/simple/escape_pod/receive_user_command(command)
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/receive_user_command(command)
if (!armed)
return
..(command)
return TRUE // Eat all commands.
return ..(command)
/datum/computer/file/embedded_program/docking/simple/escape_pod/process()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/process()
..()
if (eject_time && world.time >= eject_time && !closing)
close_door()
closing = 1
/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_docking()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/prepare_for_docking()
return
/datum/computer/file/embedded_program/docking/simple/escape_pod/ready_for_docking()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/ready_for_docking()
return 1
/datum/computer/file/embedded_program/docking/simple/escape_pod/finish_docking()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/finish_docking()
return //don't do anything - the doors only open when the pod is armed.
/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_undocking()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/prepare_for_undocking()
eject_time = world.time + eject_delay*10

View File

@@ -0,0 +1,187 @@
//making this separate from /obj/effect/landmark until that mess can be dealt with
/obj/effect/shuttle_landmark
name = "Nav Point"
icon = 'icons/effects/effects.dmi'
icon_state = "energynet"
anchored = 1
unacidable = 1
simulated = 0
invisibility = 101
//ID of the landmark
var/landmark_tag
//ID of the controller on the dock side (intialize to id_tag, becomes reference)
var/datum/computer/file/embedded_program/docking/docking_controller
//Map of shuttle names to ID of controller used for this landmark for shuttles with multiple ones.
var/list/special_dock_targets
//When the shuttle leaves this landmark, it will leave behind the base area
//also used to determine if the shuttle can arrive here without obstruction
var/area/base_area
//Will also leave this type of turf behind if set.
var/turf/base_turf
//Name of the shuttle, null for generic waypoint
var/shuttle_restricted
// var/flags = 0 - Already defined on /atom ? Is it being used for anything? Can we reuse it safely?
/obj/effect/shuttle_landmark/Initialize()
. = ..()
if(docking_controller)
. = INITIALIZE_HINT_LATELOAD
if(flags & SLANDMARK_FLAG_AUTOSET)
base_area = get_area(src)
var/turf/T = get_turf(src)
if(T)
base_turf = T.type
else
base_area = locate(base_area || world.area)
name = (name + " ([x],[y])")
SSshuttles.register_landmark(landmark_tag, src)
/obj/effect/shuttle_landmark/LateInitialize()
if(!docking_controller)
return
var/docking_tag = docking_controller
docking_controller = SSshuttles.docking_registry[docking_tag]
if(!istype(docking_controller))
log_error("Could not find docking controller for shuttle waypoint '[name]', docking tag was '[docking_tag]'.")
if(using_map.use_overmap)
var/obj/effect/overmap/visitable/location = map_sectors["[z]"]
if(location && location.docking_codes)
docking_controller.docking_codes = location.docking_codes
/obj/effect/shuttle_landmark/forceMove()
var/obj/effect/overmap/visitable/map_origin = map_sectors["[z]"]
. = ..()
var/obj/effect/overmap/visitable/map_destination = map_sectors["[z]"]
if(map_origin != map_destination)
if(map_origin)
map_origin.remove_landmark(src, shuttle_restricted)
if(map_destination)
map_destination.add_landmark(src, shuttle_restricted)
//Called when the landmark is added to an overmap sector.
/obj/effect/shuttle_landmark/proc/sector_set(var/obj/effect/overmap/visitable/O, shuttle_name)
shuttle_restricted = shuttle_name
/obj/effect/shuttle_landmark/proc/is_valid(var/datum/shuttle/shuttle)
if(shuttle.current_location == src)
return FALSE
for(var/area/A in shuttle.shuttle_area)
var/list/translation = get_turf_translation(get_turf(shuttle.current_location), get_turf(src), A.contents)
if(check_collision(base_area, list_values(translation)))
return FALSE
var/conn = GetConnectedZlevels(z)
for(var/w in (z - shuttle.multiz) to z)
if(!(w in conn))
return FALSE
return TRUE
// This creates a graphical warning to where the shuttle is about to land in approximately five seconds.
/obj/effect/shuttle_landmark/proc/create_warning_effect(var/datum/shuttle/shuttle)
if(shuttle.current_location == src)
return // TOO LATE!
for(var/area/A in shuttle.shuttle_area)
var/list/translation = get_turf_translation(get_turf(shuttle.current_location), get_turf(src), A.contents)
for(var/T in list_values(translation))
new /obj/effect/temporary_effect/shuttle_landing(T) // It'll delete itself when needed.
return
// Should return a readable description of why not if it can't depart.
/obj/effect/shuttle_landmark/proc/cannot_depart(datum/shuttle/shuttle)
return FALSE
/obj/effect/shuttle_landmark/proc/shuttle_departed(datum/shuttle/shuttle)
return
/obj/effect/shuttle_landmark/proc/shuttle_arrived(datum/shuttle/shuttle)
return
/proc/check_collision(area/target_area, list/target_turfs)
for(var/target_turf in target_turfs)
var/turf/target = target_turf
if(!target)
return TRUE //collides with edge of map
if(target.loc != target_area)
return TRUE //collides with another area
if(target.density)
return TRUE //dense turf
return FALSE
//
//Self-naming/numbering ones.
//
/obj/effect/shuttle_landmark/automatic
name = "Navpoint"
landmark_tag = "navpoint"
flags = SLANDMARK_FLAG_AUTOSET
/obj/effect/shuttle_landmark/automatic/Initialize()
landmark_tag += "-[x]-[y]-[z]-[random_id("landmarks",1,9999)]"
return ..()
/obj/effect/shuttle_landmark/automatic/sector_set(var/obj/effect/overmap/visitable/O)
..()
name = ("[O.name] - [initial(name)] ([x],[y])")
//Subtype that calls explosion on init to clear space for shuttles
/obj/effect/shuttle_landmark/automatic/clearing
var/radius = 10
/obj/effect/shuttle_landmark/automatic/clearing/Initialize()
..()
return INITIALIZE_HINT_LATELOAD
/obj/effect/shuttle_landmark/automatic/clearing/LateInitialize()
..()
for(var/turf/T in range(radius, src))
if(T.density)
T.ChangeTurf(get_base_turf_by_area(T))
// Subtype that also queues a shuttle datum (for shuttles starting on maps loaded at runtime)
/obj/effect/shuttle_landmark/shuttle_initializer
var/datum/shuttle/shuttle_type
/obj/effect/shuttle_landmark/shuttle_initializer/Initialize()
. = ..()
LAZYADD(SSshuttles.shuttles_to_initialize, shuttle_type) // queue up for init.
//
// Bluespace flare landmark beacon
//
/obj/item/device/spaceflare
name = "bluespace flare"
desc = "Burst transmitter used to broadcast all needed information for shuttle navigation systems. Has a flare attached for marking the spot where you probably shouldn't be standing."
icon_state = "bluflare"
light_color = "#3728ff"
var/active
/obj/item/device/spaceflare/attack_self(var/mob/user)
if(!active)
visible_message("<span class='notice'>[user] pulls the cord, activating the [src].</span>")
activate()
/obj/item/device/spaceflare/proc/activate()
if(active)
return
var/turf/T = get_turf(src)
var/mob/M = loc
if(istype(M) && !M.unEquip(src, T))
return
active = 1
anchored = 1
var/obj/effect/shuttle_landmark/automatic/mark = new(T)
mark.name = ("Beacon signal ([T.x],[T.y])")
T.hotspot_expose(1500, 5)
update_icon()
/obj/item/device/spaceflare/update_icon()
. = ..()
if(active)
icon_state = "bluflare_on"
set_light(0.3, 0.1, 6, 2, "85d1ff")

View File

@@ -1,6 +1,3 @@
//These lists are populated in /datum/controller/subsystem/shuttles/proc/setup_shuttle_docks()
//Shuttle subsystem is instantiated in shuttles.dm.
//shuttle moving state defines are in setup.dm
/datum/shuttle
@@ -8,48 +5,78 @@
var/warmup_time = 0
var/moving_status = SHUTTLE_IDLE
var/docking_controller_tag //tag of the controller used to coordinate docking
var/datum/computer/file/embedded_program/docking/docking_controller //the controller itself. (micro-controller, not game controller)
var/list/shuttle_area // Initial value can be either a single area type or a list of area types
var/obj/effect/shuttle_landmark/current_location //This variable is type-abused initially: specify the landmark_tag, not the actual landmark.
var/arrive_time = 0 //the time at which the shuttle arrives when long jumping
var/depart_time = 0 //Similar to above, set when the shuttle leaves when long jumping, to compare against arrive time.
var/flags = SHUTTLE_FLAGS_PROCESS
var/tmp/arrive_time = 0 //the time at which the shuttle arrives when long jumping
var/flags = SHUTTLE_FLAGS_NONE
var/process_state = IDLE_STATE // Used with SHUTTLE_FLAGS_PROCESS, as well as to store current state.
var/category = /datum/shuttle
var/multiz = 0 //how many multiz levels, starts at 0 TODO Leshana - Are we porting this?
var/ceiling_type = /turf/unsimulated/floor/shuttle_ceiling
var/ceiling_type // Type path of turf to roof over the shuttle when at multi-z landmarks. Ignored if null.
/datum/shuttle/New()
var/sound_takeoff = 'sound/effects/shuttles/shuttle_takeoff.ogg'
var/sound_landing = 'sound/effects/shuttles/shuttle_landing.ogg'
var/knockdown = 1 //whether shuttle downs non-buckled people when it moves
var/defer_initialisation = FALSE //If this this shuttle should be initialised automatically.
//If set to true, you are responsible for initialzing the shuttle manually.
//Useful for shuttles that are initialized by map_template loading, or shuttles that are created in-game or not used.
var/mothershuttle //tag of mothershuttle
var/motherdock //tag of mothershuttle landmark, defaults to starting location
var/tmp/depart_time = 0 //Similar to above, set when the shuttle leaves when long jumping. Used for progress bars.
// Future Thoughts: Baystation put "docking" stuff in a subtype, leaving base type pure and free of docking stuff. Is this best?
/datum/shuttle/New(_name, var/obj/effect/shuttle_landmark/initial_location)
..()
if(src.name in shuttle_controller.shuttles)
if(_name)
src.name = _name
var/list/areas = list()
if(!islist(shuttle_area))
shuttle_area = list(shuttle_area)
for(var/T in shuttle_area)
var/area/A = locate(T)
if(!istype(A))
CRASH("Shuttle \"[name]\" couldn't locate area [T].")
areas += A
shuttle_area = areas
if(initial_location)
current_location = initial_location
else
current_location = SSshuttles.get_landmark(current_location)
if(!istype(current_location))
log_debug("UM whoops, no initial? [src]")
CRASH("Shuttle '[name]' could not find its starting location landmark [current_location].")
if(src.name in SSshuttles.shuttles)
CRASH("A shuttle with the name '[name]' is already defined.")
shuttle_controller.shuttles[src.name] = src
SSshuttles.shuttles[src.name] = src
if(flags & SHUTTLE_FLAGS_PROCESS)
shuttle_controller.process_shuttles += src
SSshuttles.process_shuttles += src
if(flags & SHUTTLE_FLAGS_SUPPLY)
if(supply_controller.shuttle)
CRASH("A supply shuttle is already defined.")
supply_controller.shuttle = src
/datum/shuttle/Destroy()
shuttle_controller.shuttles -= src.name
shuttle_controller.process_shuttles -= src
current_location = null
SSshuttles.shuttles -= src.name
SSshuttles.process_shuttles -= src
SSshuttles.shuttle_logs -= src
if(supply_controller.shuttle == src)
supply_controller.shuttle = null
. = ..()
/datum/shuttle/process()
return
/datum/shuttle/proc/init_docking_controllers()
if(docking_controller_tag)
docking_controller = locate(docking_controller_tag)
if(!istype(docking_controller))
to_world("<span class='danger'>warning: shuttle with docking tag [docking_controller_tag] could not find it's controller!</span>")
// This creates a graphical warning to where the shuttle is about to land, in approximately five seconds.
/datum/shuttle/proc/create_warning_effect(area/landing_area)
for(var/turf/T in landing_area)
new /obj/effect/temporary_effect/shuttle_landing(T) // It'll delete itself when needed.
/datum/shuttle/proc/create_warning_effect(var/obj/effect/shuttle_landmark/destination)
destination.create_warning_effect(src)
// Return false to abort a jump, before the 'warmup' phase.
/datum/shuttle/proc/pre_warmup_checks()
@@ -60,197 +87,274 @@
return TRUE
// If you need an event to occur when the shuttle jumps in short or long jump, override this.
/datum/shuttle/proc/on_shuttle_departure(var/area/origin)
origin.shuttle_departed()
// Keep in mind that destination is the intended destination, the shuttle may or may not actually reach it.s
/datum/shuttle/proc/on_shuttle_departure(var/obj/effect/shuttle_landmark/origin, var/obj/effect/shuttle_landmark/destination)
return
// Similar to above, but when it finishes moving to the target. Short jump generally makes this occur immediately after the above proc.
/datum/shuttle/proc/on_shuttle_arrival(var/area/destination)
destination.shuttle_arrived()
// Keep in mind we might not actually have gotten to destination. Check current_location to be sure where we ended up.
/datum/shuttle/proc/on_shuttle_arrival(var/obj/effect/shuttle_landmark/origin, var/obj/effect/shuttle_landmark/destination)
return
/datum/shuttle/proc/short_jump(var/area/origin,var/area/destination)
/datum/shuttle/proc/short_jump(var/obj/effect/shuttle_landmark/destination)
if(moving_status != SHUTTLE_IDLE)
return
if(!pre_warmup_checks())
return
var/obj/effect/shuttle_landmark/start_location = current_location
// TODO - Figure out exactly when to play sounds. Before warmup_time delay? Should there be a sleep for waiting for sounds? or no?
moving_status = SHUTTLE_WARMUP
spawn(warmup_time*10)
make_sounds(origin, HYPERSPACE_WARMUP)
make_sounds(HYPERSPACE_WARMUP)
create_warning_effect(destination)
sleep(5 SECONDS) // so the sound finishes.
if(!post_warmup_checks())
moving_status = SHUTTLE_IDLE
cancel_launch(null)
if(!fuel_check()) //fuel error (probably out of fuel) occured, so cancel the launch
cancel_launch(null)
if (moving_status == SHUTTLE_IDLE)
make_sounds(origin, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
return //someone cancelled the launch
on_shuttle_departure(origin)
moving_status = SHUTTLE_INTRANSIT //shouldn't matter but just to be safe
move(origin, destination)
on_shuttle_departure(start_location, destination)
attempt_move(destination)
moving_status = SHUTTLE_IDLE
on_shuttle_arrival(start_location, destination)
on_shuttle_arrival(destination)
make_sounds(HYPERSPACE_END)
make_sounds(destination, HYPERSPACE_END)
/datum/shuttle/proc/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction)
//to_world("shuttle/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]")
// TODO - Far Future - Would be great if this was driven by process too.
/datum/shuttle/proc/long_jump(var/obj/effect/shuttle_landmark/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time)
//to_world("shuttle/long_jump: current_location=[current_location], destination=[destination], interim=[interim], travel_time=[travel_time]")
if(moving_status != SHUTTLE_IDLE)
return
if(!pre_warmup_checks())
return
//it would be cool to play a sound here
var/obj/effect/shuttle_landmark/start_location = current_location
// TODO - Figure out exactly when to play sounds. Before warmup_time delay? Should there be a sleep for waiting for sounds? or no?
moving_status = SHUTTLE_WARMUP
spawn(warmup_time*10)
make_sounds(departing, HYPERSPACE_WARMUP)
make_sounds(HYPERSPACE_WARMUP)
create_warning_effect(interim) // Really doubt someone is gonna get crushed in the interim area but for completeness's sake we'll make the warning.
sleep(5 SECONDS) // so the sound finishes.
if(!post_warmup_checks())
moving_status = SHUTTLE_IDLE
cancel_launch(null)
if (moving_status == SHUTTLE_IDLE)
make_sounds(departing, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
return //someone cancelled the launch
arrive_time = world.time + travel_time*10
depart_time = world.time
moving_status = SHUTTLE_INTRANSIT
on_shuttle_departure(start_location, destination)
on_shuttle_departure(departing)
if(attempt_move(interim, TRUE))
interim.shuttle_arrived()
move(departing, interim, direction)
interim.shuttle_arrived()
if(process_longjump(current_location, destination)) //VOREStation Edit - To hook custom shuttle code in
return //VOREStation Edit - It handled it for us (shuttle crash or such)
if(process_longjump(departing, destination)) //VOREStation Edit - To hook custom shuttle code in
return //VOREStation Edit - It handled it for us (shuttle crash or such)
var/last_progress_sound = 0
var/made_warning = FALSE
while (world.time < arrive_time)
// Make the shuttle make sounds every four seconds, since the sound file is five seconds.
if(last_progress_sound + 4 SECONDS < world.time)
make_sounds(HYPERSPACE_PROGRESS)
last_progress_sound = world.time
var/last_progress_sound = 0
var/made_warning = FALSE
while (world.time < arrive_time)
// Make the shuttle make sounds every four seconds, since the sound file is five seconds.
if(last_progress_sound + 4 SECONDS < world.time)
make_sounds(interim, HYPERSPACE_PROGRESS)
last_progress_sound = world.time
if(arrive_time - world.time <= 5 SECONDS && !made_warning)
made_warning = TRUE
create_warning_effect(destination)
sleep(5)
if(arrive_time - world.time <= 5 SECONDS && !made_warning)
made_warning = TRUE
create_warning_effect(destination)
sleep(5)
if(!attempt_move(destination))
attempt_move(start_location) //try to go back to where we started. If that fails, I guess we're stuck in the interim location
interim.shuttle_departed()
move(interim, destination, direction)
moving_status = SHUTTLE_IDLE
on_shuttle_arrival(start_location, destination)
make_sounds(HYPERSPACE_END)
on_shuttle_arrival(destination)
make_sounds(destination, HYPERSPACE_END)
//////////////////////////////
// Forward declarations of public procs. They do nothing because this is not auto-dock.
/datum/shuttle/proc/fuel_check()
return 1 //fuel check should always pass in non-overmap shuttles (they have magic engines)
/datum/shuttle/proc/cancel_launch(var/user)
// If we are past warming up its too late to cancel.
if (moving_status == SHUTTLE_WARMUP)
moving_status = SHUTTLE_IDLE
/*
Docking stuff
*/
/datum/shuttle/proc/dock()
if (!docking_controller)
return
var/dock_target = current_dock_target()
if (!dock_target)
return
docking_controller.initiate_docking(dock_target)
return
/datum/shuttle/proc/undock()
if (!docking_controller)
return
docking_controller.initiate_undocking()
return
/datum/shuttle/proc/current_dock_target()
return null
/datum/shuttle/proc/force_undock()
return
/datum/shuttle/proc/skip_docking_checks()
if (!docking_controller || !current_dock_target())
return 1 //shuttles without docking controllers or at locations without docking ports act like old-style shuttles
return 0
// Check if we are docked (or never dock) and thus have properly arrived.
/datum/shuttle/proc/check_docked()
return TRUE
//just moves the shuttle from A to B, if it can be moved
//A note to anyone overriding move in a subtype. move() must absolutely not, under any circumstances, fail to move the shuttle.
// Check if we are undocked and thus probably ready to depart.
/datum/shuttle/proc/check_undocked()
return TRUE
/*****************
* Shuttle Moved Handling * (Observer Pattern Implementation: Shuttle Moved)
* Shuttle Pre Move Handling * (Observer Pattern Implementation: Shuttle Pre Move)
*****************/
// Move the shuttle to destination if possible.
// Returns TRUE if we actually moved, otherwise FALSE.
/datum/shuttle/proc/attempt_move(var/obj/effect/shuttle_landmark/destination, var/interim = FALSE)
if(current_location == destination)
log_shuttle("Shuttle [src] attempted to move to [destination] but is already there!")
return FALSE
if(!destination.is_valid(src))
log_shuttle("Shuttle [src] aborting attempt_move() because destination=[destination] is not valid")
return FALSE
if(current_location.cannot_depart(src))
log_shuttle("Shuttle [src] aborting attempt_move() because current_location=[current_location] refuses.")
return FALSE
log_shuttle("[src] moving to [destination]. Areas are [english_list(shuttle_area)]")
var/list/translation = list()
for(var/area/A in shuttle_area)
log_shuttle("Translating [A]")
translation += get_turf_translation(get_turf(current_location), get_turf(destination), A.contents)
var/old_location = current_location
// Observer pattern pre-move
GLOB.shuttle_pre_move_event.raise_event(src, old_location, destination)
current_location.shuttle_departed(src)
// Actually do it! (This never fails)
perform_shuttle_move(destination, translation)
// Observer pattern post-move
destination.shuttle_arrived(src)
GLOB.shuttle_moved_event.raise_event(src, old_location, destination)
return TRUE
//just moves the shuttle from A to B
//A note to anyone overriding move in a subtype. perform_shuttle_move() must absolutely not, under any circumstances, fail to move the shuttle.
//If you want to conditionally cancel shuttle launches, that logic must go in short_jump() or long_jump()
/datum/shuttle/proc/move(var/area/origin, var/area/destination, var/direction=null)
/datum/shuttle/proc/perform_shuttle_move(var/obj/effect/shuttle_landmark/destination, var/list/turf_translation)
log_shuttle("perform_shuttle_move() current=[current_location] destination=[destination]")
//to_world("move_shuttle() called for [name] leaving [origin] en route to [destination].")
//to_world("area_coming_from: [origin]")
//to_world("destination: [destination]")
ASSERT(current_location != destination)
if(origin == destination)
//to_world("cancelling move, shuttle will overlap.")
return
// If shuttle has no internal gravity, update our gravity with destination gravity
if((flags & SHUTTLE_FLAGS_ZERO_G))
var/new_grav = 1
if(destination.flags & SLANDMARK_FLAG_ZERO_G)
var/area/new_area = get_area(destination)
new_grav = new_area.has_gravity
for(var/area/our_area in shuttle_area)
if(our_area.has_gravity != new_grav)
our_area.gravitychange(new_grav)
if (docking_controller && !docking_controller.undocked())
docking_controller.force_undock()
// TODO - Old code used to throw stuff out of the way instead of squashing. Should we?
var/list/dstturfs = list()
var/throwy = world.maxy
for(var/turf/T in destination)
dstturfs += T
if(T.y < throwy)
throwy = T.y
for(var/turf/T in dstturfs)
var/turf/D = locate(T.x, throwy - 1, T.z)
for(var/atom/movable/AM as mob|obj in T)
AM.Move(D)
for(var/mob/living/carbon/bug in destination)
bug.gib()
for(var/mob/living/simple_mob/pest in destination)
pest.gib()
origin.move_contents_to(destination, direction=direction)
for(var/mob/M in destination)
if(M.client)
spawn(0)
if(M.buckled)
to_chat(M, "<font color='red'>Sudden acceleration presses you into \the [M.buckled]!</font>")
shake_camera(M, 3, 1)
// Move, gib, or delete everything in our way!
for(var/turf/src_turf in turf_translation)
var/turf/dst_turf = turf_translation[src_turf]
if(src_turf.is_solid_structure()) // in case someone put a hole in the shuttle and you were lucky enough to be under it
for(var/atom/movable/AM in dst_turf)
//if(AM.movable_flags & MOVABLE_FLAG_DEL_SHUTTLE)
// qdel(AM)
// continue
if(!AM.simulated)
continue
if(isliving(AM))
var/mob/living/bug = AM
bug.gib()
else
to_chat(M, "<font color='red'>The floor lurches beneath you!</font>")
shake_camera(M, 10, 1)
if(istype(M, /mob/living/carbon))
if(!M.buckled)
M.Weaken(3)
qdel(AM) //it just gets atomized I guess? TODO throw it into space somewhere, prevents people from using shuttles as an atom-smasher
var/list/powernets = list()
for(var/area/A in shuttle_area)
// If there was a zlevel above our origin and we own the ceiling, erase our ceiling now we're leaving
if(ceiling_type && HasAbove(current_location.z))
for(var/turf/TO in A.contents)
var/turf/TA = GetAbove(TO)
if(istype(TA, ceiling_type))
TA.ChangeTurf(get_base_turf_by_area(TA), 1, 1)
if(knockdown)
for(var/mob/living/M in A)
spawn(0)
if(M.buckled)
to_chat(M, "<font color='red'>Sudden acceleration presses you into \the [M.buckled]!</font>")
shake_camera(M, 3, 1)
else
to_chat(M, "<font color='red'>The floor lurches beneath you!</font>")
shake_camera(M, 10, 1)
// TODO - tossing?
//M.visible_message("<span class='warning'>[M.name] is tossed around by the sudden acceleration!</span>")
//M.throw_at_random(FALSE, 4, 1)
if(istype(M, /mob/living/carbon))
M.Weaken(3)
// We only need to rebuild powernets for our cables. No need to check machines because they are on top of cables.
for(var/obj/structure/cable/C in A)
powernets |= C.powernet
// Actually do the movement of everything - This replaces origin.move_contents_to(destination)
translate_turfs(turf_translation, current_location.base_area, current_location.base_turf)
current_location = destination
// If there's a zlevel above our destination, paint in a ceiling on it so we retain our air
if(ceiling_type && HasAbove(current_location.z))
for(var/area/A in shuttle_area)
for(var/turf/TD in A.contents)
var/turf/TA = GetAbove(TD)
if(istype(TA, get_base_turf_by_area(TA)) || isopenspace(TA))
if(get_area(TA) in shuttle_area)
continue
TA.ChangeTurf(ceiling_type, TRUE, TRUE, TRUE)
// Power-related checks. If shuttle contains power related machinery, update powernets.
var/update_power = 0
for(var/obj/machinery/power/P in destination)
update_power = 1
break
// Note: Old way was to rebuild ALL powernets: if(powernets.len) SSmachines.makepowernets()
// New way only rebuilds the powernets we have to
var/list/cables = list()
for(var/datum/powernet/P in powernets)
cables |= P.cables
qdel(P)
SSmachines.setup_powernets_for_cables(cables)
for(var/obj/structure/cable/C in destination)
update_power = 1
break
if(update_power)
SSmachines.makepowernets()
return
//returns 1 if the shuttle has a valid arrive time
/datum/shuttle/proc/has_arrive_time()
return (moving_status == SHUTTLE_INTRANSIT)
/datum/shuttle/proc/make_sounds(var/area/A, var/sound_type)
/datum/shuttle/proc/make_sounds(var/sound_type)
var/sound_to_play = null
switch(sound_type)
if(HYPERSPACE_WARMUP)
@@ -259,9 +363,29 @@
sound_to_play = 'sound/effects/shuttles/hyperspace_progress.ogg'
if(HYPERSPACE_END)
sound_to_play = 'sound/effects/shuttles/hyperspace_end.ogg'
for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice
playsound(E, sound_to_play, 50, FALSE)
for(var/area/A in shuttle_area)
for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice
playsound(E, sound_to_play, 50, FALSE)
/datum/shuttle/proc/message_passengers(area/A, var/message)
for(var/mob/M in A)
M.show_message(message, 2)
/datum/shuttle/proc/message_passengers(var/message)
for(var/area/A in shuttle_area)
for(var/mob/M in A)
M.show_message(message, 2)
/datum/shuttle/proc/find_children()
. = list()
for(var/shuttle_name in SSshuttles.shuttles)
var/datum/shuttle/shuttle = SSshuttles.shuttles[shuttle_name]
if(shuttle.mothershuttle == name)
. += shuttle
//Returns the areas in shuttle_area that are not actually child shuttles.
/datum/shuttle/proc/find_childfree_areas()
. = shuttle_area.Copy()
for(var/datum/shuttle/child in find_children())
. -= child.shuttle_area
/datum/shuttle/proc/get_location_name()
if(moving_status == SHUTTLE_INTRANSIT)
return "In transit"
return current_location.name

View File

@@ -0,0 +1,220 @@
#define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up.
// Subtype of shuttle that handles docking with docking controllers
// Consists of code pulled down from the old /datum/shuttle and up from /datum/shuttle/ferry
// Note: Since all known shuttles extend this type, this really could just be built into /datum/shuttle
// Why isn't it you ask? Eh, baystation did it this way and its convenient to keep the files smaller I guess.
/datum/shuttle/autodock
var/in_use = null // Tells the controller whether this shuttle needs processing, also attempts to prevent double-use
var/last_dock_attempt_time = 0
var/docking_controller_tag = null // ID of the controller on the shuttle (If multiple, this is the default one)
var/datum/computer/file/embedded_program/docking/shuttle_docking_controller // Controller on the shuttle (the one in use)
var/docking_codes
var/tmp/obj/effect/shuttle_landmark/next_location //This is only used internally.
var/datum/computer/file/embedded_program/docking/active_docking_controller // Controller we are docked with (or trying to)
var/obj/effect/shuttle_landmark/landmark_transition //This variable is type-abused initially: specify the landmark_tag, not the actual landmark.
var/move_time = 240 //the time spent in the transition area
category = /datum/shuttle/autodock
flags = SHUTTLE_FLAGS_PROCESS | SHUTTLE_FLAGS_ZERO_G
/datum/shuttle/autodock/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint)
..(_name, start_waypoint)
//Initial dock
active_docking_controller = current_location.docking_controller
update_docking_target(current_location)
if(active_docking_controller)
set_docking_codes(active_docking_controller.docking_codes)
else if(global.using_map.use_overmap)
var/obj/effect/overmap/visitable/location = map_sectors["[current_location.z]"]
if(location && location.docking_codes)
set_docking_codes(location.docking_codes)
dock()
//Optional transition area
if(landmark_transition)
landmark_transition = SSshuttles.get_landmark(landmark_transition)
/datum/shuttle/autodock/Destroy()
in_use = null
next_location = null
active_docking_controller = null
landmark_transition = null
return ..()
/datum/shuttle/autodock/proc/set_docking_codes(var/code)
docking_codes = code
if(shuttle_docking_controller)
shuttle_docking_controller.docking_codes = code
/datum/shuttle/autodock/perform_shuttle_move()
force_undock() //bye!
..()
// Despite the name this actually updates the SHUTTLE docking conroller, not the active.
/datum/shuttle/autodock/proc/update_docking_target(var/obj/effect/shuttle_landmark/location)
var/current_dock_target
if(location && location.special_dock_targets && location.special_dock_targets[name])
current_dock_target = location.special_dock_targets[name]
else
current_dock_target = docking_controller_tag
shuttle_docking_controller = SSshuttles.docking_registry[current_dock_target]
if(current_dock_target && !shuttle_docking_controller)
to_world("<span class='danger'>warning: shuttle [src] can't find its controller with tag [current_dock_target]!</span>")
/*
Docking stuff
*/
/datum/shuttle/autodock/dock()
if(active_docking_controller && shuttle_docking_controller)
shuttle_docking_controller.initiate_docking(active_docking_controller.id_tag)
last_dock_attempt_time = world.time
/datum/shuttle/autodock/undock()
if(shuttle_docking_controller)
shuttle_docking_controller.initiate_undocking()
/datum/shuttle/autodock/force_undock()
if(shuttle_docking_controller)
shuttle_docking_controller.force_undock()
/datum/shuttle/autodock/check_docked()
if(shuttle_docking_controller)
return shuttle_docking_controller.docked()
return TRUE
/datum/shuttle/autodock/check_undocked()
if(shuttle_docking_controller)
return shuttle_docking_controller.can_launch()
return TRUE
// You also could just directly reference active_docking_controller
/datum/shuttle/autodock/proc/current_dock_target()
if(active_docking_controller)
return active_docking_controller.id_tag
return null
// These checks are built into the check_docked() and check_undocked() procs
/datum/shuttle/autodock/proc/skip_docking_checks()
if (!shuttle_docking_controller || !current_dock_target())
return TRUE //shuttles without docking controllers or at locations without docking ports act like old-style shuttles
return FALSE
/*
Please ensure that long_jump() and short_jump() are only called from here. This applies to subtypes as well.
Doing so will ensure that multiple jumps cannot be initiated in parallel.
*/
/datum/shuttle/autodock/process()
switch(process_state)
if (WAIT_LAUNCH)
if(check_undocked())
//*** ready to go
process_launch()
if (FORCE_LAUNCH)
process_launch()
if (WAIT_ARRIVE)
if (moving_status == SHUTTLE_IDLE)
//*** we made it to the destination, update stuff
process_arrived()
process_state = WAIT_FINISH
if (WAIT_FINISH)
if (world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT || check_docked())
//*** all done here
process_state = IDLE_STATE
arrived()
//not to be confused with the arrived() proc
/datum/shuttle/autodock/proc/process_arrived()
active_docking_controller = next_location.docking_controller
update_docking_target(next_location)
dock()
next_location = null
in_use = null //release lock
/datum/shuttle/autodock/proc/get_travel_time()
return move_time
/datum/shuttle/autodock/proc/process_launch()
if(!next_location || !next_location.is_valid(src) || current_location.cannot_depart(src))
process_state = IDLE_STATE
in_use = null
return
if (get_travel_time() && landmark_transition)
. = long_jump(next_location, landmark_transition, get_travel_time())
else
. = short_jump(next_location)
process_state = WAIT_ARRIVE
/*
Guards - (These don't take docking status into account, just the state machine and move safety)
*/
/datum/shuttle/autodock/proc/can_launch()
return (next_location && next_location.is_valid(src) && !current_location.cannot_depart(src) && moving_status == SHUTTLE_IDLE && !in_use)
/datum/shuttle/autodock/proc/can_force()
return (next_location && next_location.is_valid(src) && !current_location.cannot_depart(src) && moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH)
/datum/shuttle/autodock/proc/can_cancel()
return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH)
/*
"Public" procs
*/
// Queue shuttle for undock and launch by shuttle subsystem.
/datum/shuttle/autodock/proc/launch(var/user)
if (!can_launch()) return
in_use = user //obtain an exclusive lock on the shuttle
process_state = WAIT_LAUNCH
undock()
// Queue shuttle for forced undock and launch by shuttle subsystem.
/datum/shuttle/autodock/proc/force_launch(var/user)
if (!can_force()) return
in_use = user //obtain an exclusive lock on the shuttle
process_state = FORCE_LAUNCH
// Cancel queued launch.
/datum/shuttle/autodock/cancel_launch(var/user)
if (!can_cancel()) return
moving_status = SHUTTLE_IDLE
process_state = WAIT_FINISH
in_use = null
//whatever we were doing with docking: stop it, then redock
force_undock()
spawn(1 SECOND)
dock()
//returns 1 if the shuttle is getting ready to move, but is not in transit yet
/datum/shuttle/autodock/proc/is_launching()
return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH)
// /datum/shuttle/autodock/get_location_name() defined in shuttle.dm
/datum/shuttle/autodock/proc/get_destination_name()
if(!next_location)
return "None"
return next_location.name
//This gets called when the shuttle finishes arriving at it's destination
//This can be used by subtypes to do things when the shuttle arrives.
//Note that this is called when the shuttle leaves the WAIT_FINISHED state, the proc name is a little misleading
/datum/shuttle/autodock/proc/arrived()
return //do nothing for now
/obj/effect/shuttle_landmark/transit
flags = SLANDMARK_FLAG_ZERO_G

View File

@@ -8,23 +8,20 @@
var/shuttle_tag // Used to coordinate data in shuttle controller.
var/hacked = 0 // Has been emagged, no access restrictions.
var/ui_template = "shuttle_control_console.tmpl"
/obj/machinery/computer/shuttle_control/attack_hand(user as mob)
if(..(user))
return
//src.add_fingerprint(user) //shouldn't need fingerprints just for looking at it.
if(!allowed(user))
to_chat(user, "<font color='red'>Access Denied.</font>")
to_chat(user, "<span class='warning'>Access Denied.</span>")
return 1
ui_interact(user)
/obj/machinery/computer/shuttle_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag]
if (!istype(shuttle))
return
/obj/machinery/computer/shuttle_control/proc/get_ui_data(var/datum/shuttle/autodock/shuttle)
var/shuttle_state
switch(shuttle.moving_status)
if(SHUTTLE_IDLE) shuttle_state = "idle"
@@ -34,55 +31,100 @@
var/shuttle_status
switch (shuttle.process_state)
if(IDLE_STATE)
var/cannot_depart = shuttle.current_location.cannot_depart(shuttle)
if (shuttle.in_use)
shuttle_status = "Busy."
else if (!shuttle.location)
shuttle_status = "Standing-by at station."
else if(cannot_depart)
shuttle_status = cannot_depart
else
shuttle_status = "Standing-by at offsite location."
shuttle_status = "Standing-by at \the [shuttle.get_location_name()]."
if(WAIT_LAUNCH, FORCE_LAUNCH)
shuttle_status = "Shuttle has received command and will depart shortly."
if(WAIT_ARRIVE)
shuttle_status = "Proceeding to destination."
shuttle_status = "Proceeding to \the [shuttle.get_destination_name()]."
if(WAIT_FINISH)
shuttle_status = "Arriving at destination now."
data = list(
return list(
"shuttle_status" = shuttle_status,
"shuttle_state" = shuttle_state,
"has_docking" = shuttle.docking_controller? 1 : 0,
"docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null,
"docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null,
"has_docking" = shuttle.shuttle_docking_controller ? 1 : 0,
"docking_status" = shuttle.shuttle_docking_controller?.get_docking_status(),
"docking_override" = shuttle.shuttle_docking_controller?.override_enabled,
"can_launch" = shuttle.can_launch(),
"can_cancel" = shuttle.can_cancel(),
"can_force" = shuttle.can_force(),
"docking_codes" = shuttle.docking_codes
)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "shuttle_control_console.tmpl", "[shuttle_tag] Shuttle Control", 470, 310)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
// This is a subset of the actual checks; contains those that give messages to the user.
// This enables us to give nice error messages as well as not even bother proceeding if we can't.
/obj/machinery/computer/shuttle_control/proc/can_move(var/datum/shuttle/autodock/shuttle, var/user)
var/cannot_depart = shuttle.current_location.cannot_depart(shuttle)
if(cannot_depart)
to_chat(user, "<span class='warning'>[cannot_depart]</span>")
log_shuttle("Shuttle [shuttle] cannot depart [shuttle.current_location] because: [cannot_depart].")
return FALSE
if(!shuttle.next_location.is_valid(shuttle))
to_chat(user, "<span class='warning'>Destination zone is invalid or obstructed.</span>")
log_shuttle("Shuttle [shuttle] destination [shuttle.next_location] is invalid.")
return FALSE
return TRUE
/obj/machinery/computer/shuttle_control/Topic(href, href_list)
if(..())
return 1
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag]
if (!istype(shuttle))
return
var/datum/shuttle/autodock/shuttle = SSshuttles.shuttles[shuttle_tag]
if(!shuttle)
to_chat(usr, "<span class='warning'>Unable to establish link with the shuttle.</span>")
return handle_topic_href(shuttle, href_list, usr)
/obj/machinery/computer/shuttle_control/proc/handle_topic_href(var/datum/shuttle/autodock/shuttle, var/list/href_list, var/user)
if(!istype(shuttle))
return TOPIC_NOACTION
if(href_list["move"])
shuttle.launch(src)
if(can_move(shuttle, user))
shuttle.launch(src)
return TOPIC_REFRESH
return TOPIC_HANDLED
if(href_list["force"])
shuttle.force_launch(src)
else if(href_list["cancel"])
if(can_move(shuttle, user))
shuttle.force_launch(src)
return TOPIC_REFRESH
return TOPIC_HANDLED
if(href_list["cancel"])
shuttle.cancel_launch(src)
return TOPIC_REFRESH
if(href_list["set_codes"])
var/newcode = input("Input new docking codes", "Docking codes", shuttle.docking_codes) as text|null
if (newcode && CanInteract(usr, global.default_state))
shuttle.set_docking_codes(uppertext(newcode))
return TOPIC_REFRESH
// We delegate populating data to another proc to make it easier for overriding types to add their data.
/obj/machinery/computer/shuttle_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/datum/shuttle/autodock/shuttle = SSshuttles.shuttles[shuttle_tag]
if (!istype(shuttle))
to_chat(user, "<span class='warning'>Unable to establish link with the shuttle.</span>")
return
var/list/data = get_ui_data(shuttle)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, ui_template, "[shuttle_tag] Shuttle Control", 470, 310)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/shuttle_control/emag_act(var/remaining_charges, var/mob/user)
if (!hacked)

View File

@@ -0,0 +1,34 @@
/obj/machinery/computer/shuttle_control/multi
ui_template = "shuttle_control_console_multi.tmpl"
/obj/machinery/computer/shuttle_control/multi/get_ui_data(var/datum/shuttle/autodock/multi/shuttle)
. = ..()
if(istype(shuttle))
. += list(
"destination_name" = shuttle.next_location ? shuttle.next_location.name : "No destination set.",
"can_pick" = shuttle.moving_status == SHUTTLE_IDLE,
"can_cloak" = shuttle.can_cloak ? 1 : 0,
"cloaked" = shuttle.cloaked ? 1 : 0,
"legit" = shuttle.legit ? 1 : 0,
// "engines_charging" = ((shuttle.last_move + (shuttle.cooldown SECONDS)) > world.time), // Replaced by longer warmup_time
)
/obj/machinery/computer/shuttle_control/multi/handle_topic_href(var/datum/shuttle/autodock/multi/shuttle, var/list/href_list)
if((. = ..()) != null)
return
if(href_list["pick"])
var/dest_key = input("Choose shuttle destination", "Shuttle Destination") as null|anything in shuttle.get_destinations()
if(dest_key && CanInteract(usr, global.default_state))
shuttle.set_destination(dest_key, usr)
return TOPIC_REFRESH
if(href_list["toggle_cloaked"])
if(!shuttle.can_cloak)
return TOPIC_HANDLED
shuttle.cloaked = !shuttle.cloaked
if(shuttle.legit)
to_chat(usr, "<span class='notice'>Ship ATC inhibitor systems have been [(shuttle.cloaked ? "activated. The station will not" : "deactivated. The station will")] be notified of our arrival.</span>")
else
to_chat(usr, "<span class='warning'>Ship stealth systems have been [(shuttle.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival.</span>")
return TOPIC_REFRESH

View File

@@ -1,21 +1,22 @@
/datum/shuttle/ferry/emergency
category = /datum/shuttle/ferry/emergency
// Formerly /datum/shuttle/ferry/emergency
/datum/shuttle/autodock/ferry/emergency
category = /datum/shuttle/autodock/ferry/emergency
/datum/shuttle/ferry/emergency/New()
/datum/shuttle/autodock/ferry/emergency/New()
..()
if(emergency_shuttle.shuttle)
CRASH("An emergency shuttle has already been defined.")
emergency_shuttle.shuttle = src
..()
/datum/shuttle/ferry/emergency/arrived()
/datum/shuttle/autodock/ferry/emergency/arrived()
. = ..()
if (istype(in_use, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = in_use
C.reset_authorization()
emergency_shuttle.shuttle_arrived()
/datum/shuttle/ferry/emergency/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction)
//to_world("shuttle/ferry/emergency/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]")
/datum/shuttle/autodock/ferry/emergency/long_jump(var/destination, var/interim, var/travel_time)
if (!location)
travel_time = SHUTTLE_TRANSIT_DURATION_RETURN
else
@@ -25,28 +26,28 @@
move_time = travel_time
emergency_shuttle.launch_time = world.time
..(destination, interim, travel_time, direction)
/datum/shuttle/autodock/ferry/emergency/perform_shuttle_move()
if (current_location == landmark_station) //leaving the station
spawn(0)
emergency_shuttle.departed = 1
var/estimated_time = round(emergency_shuttle.estimate_arrival_time()/60,1)
if (emergency_shuttle.evac)
priority_announcement.Announce(replacetext(replacetext(using_map.emergency_shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"))
else
priority_announcement.Announce(replacetext(replacetext(using_map.shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"))
..()
/datum/shuttle/ferry/emergency/move(var/area/origin,var/area/destination)
..(origin, destination)
if (origin == area_station) //leaving the station
emergency_shuttle.departed = 1
var/estimated_time = round(emergency_shuttle.estimate_arrival_time()/60,1)
if (emergency_shuttle.evac)
priority_announcement.Announce(replacetext(replacetext(using_map.emergency_shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"))
else
priority_announcement.Announce(replacetext(replacetext(using_map.shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"))
/datum/shuttle/ferry/emergency/can_launch(var/user)
/datum/shuttle/autodock/ferry/emergency/can_launch(var/user)
if (istype(user, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = user
if (!C.has_authorization())
return 0
return ..()
/datum/shuttle/ferry/emergency/can_force(var/user)
/datum/shuttle/autodock/ferry/emergency/can_force(var/user)
if (istype(user, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = user
@@ -56,14 +57,14 @@
return 0
return ..()
/datum/shuttle/ferry/emergency/can_cancel(var/user)
/datum/shuttle/autodock/ferry/emergency/can_cancel(var/user)
if (istype(user, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = user
if (!C.has_authorization())
return 0
return ..()
/datum/shuttle/ferry/emergency/launch(var/user)
/datum/shuttle/autodock/ferry/emergency/launch(var/user)
if (!can_launch(user)) return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
@@ -77,7 +78,7 @@
..(user)
/datum/shuttle/ferry/emergency/force_launch(var/user)
/datum/shuttle/autodock/ferry/emergency/force_launch(var/user)
if (!can_force(user)) return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
@@ -91,7 +92,7 @@
..(user)
/datum/shuttle/ferry/emergency/cancel_launch(var/user)
/datum/shuttle/autodock/ferry/emergency/cancel_launch(var/user)
if (!can_cancel(user)) return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
@@ -177,7 +178,7 @@
/obj/machinery/computer/shuttle_control/emergency/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/shuttle/ferry/emergency/shuttle = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/ferry/emergency/shuttle = SSshuttles.shuttles[shuttle_tag]
if (!istype(shuttle))
return
@@ -222,9 +223,9 @@
data = list(
"shuttle_status" = shuttle_status,
"shuttle_state" = shuttle_state,
"has_docking" = shuttle.docking_controller? 1 : 0,
"docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null,
"docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null,
"has_docking" = shuttle.active_docking_controller? 1 : 0,
"docking_status" = shuttle.active_docking_controller? shuttle.active_docking_controller.get_docking_status() : null,
"docking_override" = shuttle.active_docking_controller? shuttle.active_docking_controller.override_enabled : null,
"can_launch" = shuttle.can_launch(src),
"can_cancel" = shuttle.can_cancel(src),
"can_force" = shuttle.can_force(src),

View File

@@ -1,177 +1,50 @@
#define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up.
/datum/shuttle/ferry
var/location = 0 //0 = at area_station, 1 = at area_offsite
var/direction = 0 //0 = going to station, 1 = going to offsite.
var/process_state = IDLE_STATE
var/always_process = FALSE
/datum/shuttle/autodock/ferry
var/location = FERRY_LOCATION_STATION //0 = at area_station, 1 = at area_offsite
var/direction = FERRY_GOING_TO_STATION //0 = going to station, 1 = going to offsite.
var/in_use = null //tells the controller whether this shuttle needs processing
var/always_process = FALSE // TODO -why should this exist?
var/area_transition
var/move_time = 0 //the time spent in the transition area
var/transit_direction = null //needed for area/move_contents_to() to properly handle shuttle corners - not exactly sure how it works.
var/obj/effect/shuttle_landmark/landmark_station //This variable is type-abused initially: specify the landmark_tag, not the actual landmark.
var/obj/effect/shuttle_landmark/landmark_offsite //This variable is type-abused initially: specify the landmark_tag, not the actual landmark.
var/area/area_station
var/area/area_offsite
//TODO: change location to a string and use a mapping for area and dock targets.
var/dock_target_station
var/dock_target_offsite
category = /datum/shuttle/autodock/ferry
var/last_dock_attempt_time = 0
category = /datum/shuttle/ferry
/datum/shuttle/autodock/ferry/New(var/_name)
if(landmark_station)
landmark_station = SSshuttles.get_landmark(landmark_station)
if(landmark_offsite)
landmark_offsite = SSshuttles.get_landmark(landmark_offsite)
/datum/shuttle/ferry/New()
area_offsite = locate(area_offsite)
area_station = locate(area_station)
if(area_transition)
area_transition = locate(area_transition)
..()
..(_name, get_location_waypoint(location))
/datum/shuttle/ferry/short_jump(var/area/origin,var/area/destination)
if(isnull(location))
return
next_location = get_location_waypoint(!location)
if(!destination)
destination = get_location_area(!location)
if(!origin)
origin = get_location_area(location)
direction = !location
..(origin, destination)
/datum/shuttle/ferry/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction)
//to_world("shuttle/ferry/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]")
if(isnull(location))
return
if(!destination)
destination = get_location_area(!location)
if(!departing)
departing = get_location_area(location)
direction = !location
..(departing, destination, interim, travel_time, direction)
/datum/shuttle/ferry/move(var/area/origin,var/area/destination)
..(origin, destination)
if (destination == area_station) location = 0
if (destination == area_offsite) location = 1
//if this is a long_jump retain the location we were last at until we get to the new one
/datum/shuttle/ferry/dock()
..()
last_dock_attempt_time = world.time
/datum/shuttle/ferry/proc/get_location_area(location_id = null)
//Gets the shuttle landmark associated with the given location (defaults to current location)
/datum/shuttle/autodock/ferry/proc/get_location_waypoint(location_id = null)
if (isnull(location_id))
location_id = location
if (!location_id)
return area_station
return area_offsite
if (location_id == FERRY_LOCATION_STATION)
return landmark_station
return landmark_offsite
/*
Please ensure that long_jump() and short_jump() are only called from here. This applies to subtypes as well.
Doing so will ensure that multiple jumps cannot be initiated in parallel.
*/
/datum/shuttle/ferry/process()
switch(process_state)
if (WAIT_LAUNCH)
if (skip_docking_checks() || docking_controller.can_launch())
/datum/shuttle/autodock/ferry/short_jump(var/destination)
direction = !location // Heading away from where we currently are
. = ..()
//to_world("shuttle/ferry/process: area_transition=[area_transition], travel_time=[travel_time]")
if (move_time && area_transition)
long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction)
else
short_jump()
/datum/shuttle/autodock/ferry/long_jump(var/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time)
direction = !location // Heading away from where we currently are
. = ..()
process_state = WAIT_ARRIVE
if (FORCE_LAUNCH)
if (move_time && area_transition)
long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction)
else
short_jump()
process_state = WAIT_ARRIVE
if (WAIT_ARRIVE)
if (moving_status == SHUTTLE_IDLE)
dock()
in_use = null //release lock
process_state = WAIT_FINISH
if (WAIT_FINISH)
if (skip_docking_checks() || docking_controller.docked() || world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT)
process_state = IDLE_STATE
arrived()
/datum/shuttle/ferry/current_dock_target()
var/dock_target
if (!location) //station
dock_target = dock_target_station
else
dock_target = dock_target_offsite
return dock_target
/datum/shuttle/ferry/proc/launch(var/user)
if (!can_launch()) return
in_use = user //obtain an exclusive lock on the shuttle
process_state = WAIT_LAUNCH
undock()
/datum/shuttle/ferry/proc/force_launch(var/user)
if (!can_force()) return
in_use = user //obtain an exclusive lock on the shuttle
process_state = FORCE_LAUNCH
/datum/shuttle/ferry/proc/cancel_launch(var/user)
if (!can_cancel()) return
moving_status = SHUTTLE_IDLE
process_state = WAIT_FINISH
in_use = null
if (docking_controller && !docking_controller.undocked())
docking_controller.force_undock()
spawn(10)
dock()
return
/datum/shuttle/ferry/proc/can_launch()
if (moving_status != SHUTTLE_IDLE)
return 0
if (in_use)
return 0
return 1
/datum/shuttle/ferry/proc/can_force()
if (moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH)
return 1
return 0
/datum/shuttle/ferry/proc/can_cancel()
if (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH)
return 1
return 0
//returns 1 if the shuttle is getting ready to move, but is not in transit yet
/datum/shuttle/ferry/proc/is_launching()
return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH)
//This gets called when the shuttle finishes arriving at it's destination
//This can be used by subtypes to do things when the shuttle arrives.
/datum/shuttle/ferry/proc/arrived()
return //do nothing for now
/datum/shuttle/autodock/ferry/perform_shuttle_move()
..()
if (current_location == landmark_station) location = FERRY_LOCATION_STATION
if (current_location == landmark_offsite) location = FERRY_LOCATION_OFFSITE
// Once we have arrived where we are going, plot a course back!
/datum/shuttle/autodock/ferry/process_arrived()
..()
next_location = get_location_waypoint(!location)

View File

@@ -4,39 +4,11 @@
req_access = list(access_cent_specops)
/obj/machinery/computer/shuttle_control/specops/attack_ai(user as mob)
to_chat(user, "<font color='red'>Access Denied.</font>")
to_chat(user, "<span class='warning'>Access Denied.</span>")
return 1
//for shuttles that may use a different docking port at each location
/datum/shuttle/ferry/multidock
var/docking_controller_tag_station
var/docking_controller_tag_offsite
var/datum/computer/file/embedded_program/docking/docking_controller_station
var/datum/computer/file/embedded_program/docking/docking_controller_offsite
category = /datum/shuttle/ferry/multidock
/datum/shuttle/ferry/multidock/init_docking_controllers()
if(docking_controller_tag_station)
docking_controller_station = locate(docking_controller_tag_station)
if(!istype(docking_controller_station))
warning("warning: shuttle with docking tag [docking_controller_station] could not find it's controller!")
if(docking_controller_tag_offsite)
docking_controller_offsite = locate(docking_controller_tag_offsite)
if(!istype(docking_controller_offsite))
warning("warning: shuttle with docking tag [docking_controller_offsite] could not find it's controller!")
if (!location)
docking_controller = docking_controller_station
else
docking_controller = docking_controller_offsite
/datum/shuttle/ferry/multidock/move(var/area/origin,var/area/destination)
..(origin, destination)
if (!location)
docking_controller = docking_controller_station
else
docking_controller = docking_controller_offsite
/datum/shuttle/ferry/multidock/specops
// Formerly /datum/shuttle/ferry/multidock/specops
/datum/shuttle/autodock/ferry/specops
var/specops_return_delay = 6000 //After moving, the amount of time that must pass before the shuttle may move again
var/specops_countdown_time = 600 //Length of the countdown when moving the shuttle
@@ -44,19 +16,19 @@
var/reset_time = 0 //the world.time at which the shuttle will be ready to move again.
var/launch_prep = 0
var/cancel_countdown = 0
category = /datum/shuttle/ferry/multidock/specops
category = /datum/shuttle/autodock/ferry/specops
/datum/shuttle/ferry/multidock/specops/New()
/datum/shuttle/autodock/ferry/specops/New()
..()
announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky.
announcer.config(list("Response Team" = 0))
/datum/shuttle/ferry/multidock/specops/proc/radio_announce(var/message)
/datum/shuttle/autodock/ferry/specops/proc/radio_announce(var/message)
if(announcer)
announcer.autosay(message, "A.L.I.C.E.", "Response Team")
/datum/shuttle/ferry/multidock/specops/launch(var/user)
/datum/shuttle/autodock/ferry/specops/launch(var/user)
if (!can_launch())
return
@@ -64,14 +36,14 @@
var/obj/machinery/computer/C = user
if(world.time <= reset_time)
C.visible_message("<font color='blue'>[using_map.boss_name] will not allow the Special Operations shuttle to launch yet.</font>")
C.visible_message("<span class='notice'>[global.using_map.boss_name] will not allow the Special Operations shuttle to launch yet.</span>")
if (((world.time - reset_time)/10) > 60)
C.visible_message("<font color='blue'>[-((world.time - reset_time)/10)/60] minutes remain!</font>")
C.visible_message("<span class='notice'>[-((world.time - reset_time)/10)/60] minutes remain!</span>")
else
C.visible_message("<font color='blue'>[-(world.time - reset_time)/10] seconds remain!</font>")
C.visible_message("<span class='notice'>[-(world.time - reset_time)/10] seconds remain!</span>")
return
C.visible_message("<font color='blue'>The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.</font>")
C.visible_message("<span class='notice'>The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.</span>")
if (location) //returning
radio_announce("THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN")
@@ -81,31 +53,31 @@
sleep_until_launch()
if (location)
var/obj/machinery/light/small/readylight/light = locate() in get_location_area()
var/obj/machinery/light/small/readylight/light = locate() in shuttle_area
if(light) light.set_state(0)
//launch
radio_announce("ALERT: INITIATING LAUNCH SEQUENCE")
..(user)
/datum/shuttle/ferry/multidock/specops/move(var/area/origin,var/area/destination)
..(origin, destination)
/datum/shuttle/autodock/ferry/specops/perform_shuttle_move()
..()
spawn(20)
spawn(2 SECONDS)
if (!location) //just arrived home
for(var/turf/T in get_area_turfs(destination))
for(var/turf/T in get_area_turfs(shuttle_area))
var/mob/M = locate(/mob) in T
to_chat(M, "<span class='danger'>You have arrived at [using_map.boss_name]. Operation has ended!</span>")
else //just left for the station
launch_mauraders()
for(var/turf/T in get_area_turfs(destination))
for(var/turf/T in get_area_turfs(shuttle_area))
var/mob/M = locate(/mob) in T
to_chat(M, "<span class='danger'>You have arrived at [station_name()]. Commence operation!</span>")
var/obj/machinery/light/small/readylight/light = locate() in T
if(light) light.set_state(1)
/datum/shuttle/ferry/multidock/specops/cancel_launch()
/datum/shuttle/autodock/ferry/specops/cancel_launch()
if (!can_cancel())
return
@@ -113,27 +85,26 @@
radio_announce("ALERT: LAUNCH SEQUENCE ABORTED")
if (istype(in_use, /obj/machinery/computer))
var/obj/machinery/computer/C = in_use
C.visible_message("<font color='red'>Launch sequence aborted.</font>")
C.visible_message("<span class='warning'>Launch sequence aborted.</span>")
..()
/datum/shuttle/ferry/multidock/specops/can_launch()
/datum/shuttle/autodock/ferry/specops/can_launch()
if(launch_prep)
return 0
return ..()
//should be fine to allow forcing. process_state only becomes WAIT_LAUNCH after the countdown is over.
///datum/shuttle/ferry/multidock/specops/can_force()
///datum/shuttle/autodock/ferry/specops/can_force()
// return 0
/datum/shuttle/ferry/multidock/specops/can_cancel()
/datum/shuttle/autodock/ferry/specops/can_cancel()
if(launch_prep)
return 1
return ..()
/datum/shuttle/ferry/multidock/specops/proc/sleep_until_launch()
/datum/shuttle/autodock/ferry/specops/proc/sleep_until_launch()
var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values.
var/launch_time = world.time + specops_countdown_time

View File

@@ -1,82 +1,81 @@
/datum/shuttle/ferry/supply
var/away_location = 1 //the location to hide at while pretending to be in-transit
// Formerly /datum/shuttle/ferry/supply
/datum/shuttle/autodock/ferry/supply
var/away_location = FERRY_LOCATION_OFFSITE //the location to hide at while pretending to be in-transit
var/late_chance = 80
var/max_late_time = 300
category = /datum/shuttle/ferry/supply
flags = SHUTTLE_FLAGS_PROCESS|SHUTTLE_FLAGS_SUPPLY
category = /datum/shuttle/autodock/ferry/supply
/datum/shuttle/ferry/supply/short_jump(var/area/origin,var/area/destination)
/datum/shuttle/autodock/ferry/supply/short_jump(var/obj/effect/shuttle_landmark/destination)
if(moving_status != SHUTTLE_IDLE)
return
if(isnull(location))
return
if(!destination)
destination = get_location_area(!location)
if(!origin)
origin = get_location_area(location)
//it would be cool to play a sound here
moving_status = SHUTTLE_WARMUP
spawn(warmup_time*10)
make_sounds(origin, HYPERSPACE_WARMUP)
make_sounds(HYPERSPACE_WARMUP)
sleep(5 SECONDS) // so the sound finishes.
if (moving_status == SHUTTLE_IDLE)
make_sounds(origin, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
return //someone cancelled the launch
if (at_station() && forbidden_atoms_check())
//cancel the launch because of forbidden atoms. announce over supply channel?
moving_status = SHUTTLE_IDLE
make_sounds(origin, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
return
if (!at_station()) //at centcom
supply_controller.buy()
//We pretend it's a long_jump by making the shuttle stay at centcom for the "in-transit" period.
var/area/away_area = get_location_area(away_location)
var/obj/effect/shuttle_landmark/away_waypoint = get_location_waypoint(away_location)
moving_status = SHUTTLE_INTRANSIT
//If we are at the away_area then we are just pretending to move, otherwise actually do the move
if (origin != away_area)
move(origin, away_area)
//If we are at the away_landmark then we are just pretending to move, otherwise actually do the move
if (next_location == away_waypoint)
attempt_move(away_waypoint)
//wait ETA here.
arrive_time = world.time + supply_controller.movetime
while (world.time <= arrive_time)
sleep(5)
if (destination != away_area)
if (next_location != away_waypoint)
//late
if (prob(late_chance))
sleep(rand(0,max_late_time))
move(away_area, destination)
attempt_move(destination)
moving_status = SHUTTLE_IDLE
make_sounds(destination, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
if (!at_station()) //at centcom
supply_controller.sell()
// returns 1 if the supply shuttle should be prevented from moving because it contains forbidden atoms
/datum/shuttle/ferry/supply/proc/forbidden_atoms_check()
/datum/shuttle/autodock/ferry/supply/proc/forbidden_atoms_check()
if (!at_station())
return 0 //if badmins want to send mobs or a nuke on the supply shuttle from centcom we don't care
return supply_controller.forbidden_atoms_check(get_location_area())
for(var/area/A in shuttle_area)
if(supply_controller.forbidden_atoms_check(A))
return 1
/datum/shuttle/ferry/supply/proc/at_station()
/datum/shuttle/autodock/ferry/supply/proc/at_station()
return (!location)
//returns 1 if the shuttle is idle and we can still mess with the cargo shopping list
/datum/shuttle/ferry/supply/proc/idle()
/datum/shuttle/autodock/ferry/supply/proc/idle()
return (moving_status == SHUTTLE_IDLE)
//returns the ETA in minutes
/datum/shuttle/ferry/supply/proc/eta_minutes()
/datum/shuttle/autodock/ferry/supply/proc/eta_minutes()
var/ticksleft = arrive_time - world.time
return round(ticksleft/600,1)

View File

@@ -1,13 +1,15 @@
//This is a holder for things like the Skipjack and Nuke shuttle.
/datum/shuttle/multi_shuttle
// Formerly /datum/shuttle/multi_shuttle
/datum/shuttle/autodock/multi
var/list/destination_tags
var/list/destinations_cache = list()
var/last_cache_rebuild_time = 0
category = /datum/shuttle/autodock/multi
flags = SHUTTLE_FLAGS_NONE
var/cloaked = FALSE
var/can_cloak = FALSE
var/at_origin = 1
var/returned_home = 0
// var/move_time = 240
var/move_time = 60
var/cooldown = 20
var/last_move = 0 //the time at which we last moved
@@ -15,276 +17,46 @@
var/arrival_message
var/departure_message
var/area/interim
var/area/last_departed
var/start_location
var/last_location
var/list/destinations
var/list/destination_dock_controller_tags = list() //optional, in case the shuttle has multiple docking ports like the ERT shuttle (even though that isn't a multi_shuttle)
var/list/destination_dock_controllers = list()
var/list/destination_dock_targets = list()
var/area/origin
var/return_warning = 0
var/legit = 0 //VOREStation Add - Whether or not a shuttle is a legit NT shuttle.
category = /datum/shuttle/multi_shuttle
var/legit = FALSE
/datum/shuttle/multi_shuttle/New()
origin = locate(origin)
interim = locate(interim)
for(var/destination in destinations)
destinations[destination] = locate(destinations[destination])
/datum/shuttle/autodock/multi/New()
..()
start_location = current_location
last_location = current_location
/datum/shuttle/multi_shuttle/init_docking_controllers()
..()
for(var/destination in destinations)
var/controller_tag = destination_dock_controller_tags[destination]
if(!controller_tag)
destination_dock_controllers[destination] = docking_controller
else
var/datum/computer/file/embedded_program/docking/C = locate(controller_tag)
/datum/shuttle/autodock/multi/proc/set_destination(var/destination_key, mob/user)
if(moving_status != SHUTTLE_IDLE)
return
next_location = destinations_cache[destination_key]
if(!next_location)
warning("Shuttle [src] set to destination we can't find: [destination_key]")
if(!istype(C))
warning("warning: shuttle with docking tag [controller_tag] could not find it's controller!")
else
destination_dock_controllers[destination] = C
/datum/shuttle/autodock/multi/proc/get_destinations()
if (last_cache_rebuild_time < SSshuttles.last_landmark_registration_time)
build_destinations_cache()
return destinations_cache
//might as well set this up here.
if(origin) last_departed = origin
last_location = start_location
//VOREStation Add - Set up origin dock controller
if(!(start_location in destination_dock_controller_tags))
destination_dock_controllers[start_location] = docking_controller
//VOREStation Add End
/datum/shuttle/multi_shuttle/current_dock_target()
return destination_dock_targets[last_location]
/datum/shuttle/autodock/multi/proc/build_destinations_cache()
last_cache_rebuild_time = world.time
destinations_cache.Cut()
for(var/destination_tag in destination_tags)
var/obj/effect/shuttle_landmark/landmark = SSshuttles.get_landmark(destination_tag)
if (istype(landmark))
destinations_cache["[landmark.name]"] = landmark
/datum/shuttle/multi_shuttle/move(var/area/origin, var/area/destination)
/datum/shuttle/autodock/multi/perform_shuttle_move()
..()
last_move = world.time
if (destination == src.origin)
returned_home = 1
docking_controller = destination_dock_controllers[last_location]
/datum/shuttle/multi_shuttle/proc/announce_departure()
/datum/shuttle/autodock/multi/proc/announce_departure()
if(cloaked || isnull(departure_message))
return
command_announcement.Announce(departure_message, (announcer ? announcer : "[using_map.boss_name]"))
command_announcement.Announce(departure_message,(announcer ? announcer : "[using_map.boss_name]"))
/datum/shuttle/multi_shuttle/proc/announce_arrival()
/datum/shuttle/autodock/multi/proc/announce_arrival()
if(cloaked || isnull(arrival_message))
return
command_announcement.Announce(arrival_message,(announcer ? announcer : "[using_map.boss_name]"))
/obj/machinery/computer/shuttle_control/multi
icon_keyboard = "syndie_key"
icon_screen = "syndishuttle"
/obj/machinery/computer/shuttle_control/multi/attack_hand(user as mob)
if(..(user))
return
src.add_fingerprint(user)
var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag]
if(!istype(MS)) return
var/dat
dat = "<center>[shuttle_tag] Ship Control<hr>"
if(MS.moving_status != SHUTTLE_IDLE)
dat += "Location: <font color='red'>Moving</font> <br>"
else
var/area/areacheck = get_area(src)
dat += "Location: [areacheck.name]<br>"
if((MS.last_move + MS.cooldown*10) > world.time)
dat += "<font color='red'>Engines charging.</font><br>"
else
dat += "<font color='green'>Engines ready.</font><br>"
//dat += "<br><b><A href='?src=\ref[src];toggle_cloak=[1]'>[MS.legit ? "Inhibit ATC":"Toggle cloak"]</A></b><br>" //VOREStation Edit - Adds legit shuttles. //SYNC Edit: Below deals with cloak.
if(MS.can_cloak)
dat += "<br><b><A href='?src=\ref[src];toggle_cloak=[1]'>Toggle cloaking field</A></b><br>"
dat += "<b><A href='?src=\ref[src];move_multi=[1]'>Move ship</A></b><br>"
dat += "<b><A href='?src=\ref[src];start=[1]'>Return to base</A></b></center>"
//Docking
dat += "<center><br><br>"
if(MS.skip_docking_checks())
dat += "Docking Status: <font color='grey'>Not in use.</font>"
else
var/override_en = MS.docking_controller.override_enabled
var/docking_status = MS.docking_controller.get_docking_status()
dat += "Docking Status: "
switch(docking_status)
if("undocked")
dat += "<font color='[override_en? "red" : "grey"]'>Undocked</font>"
if("docking")
dat += "<font color='[override_en? "red" : "yellow"]'>Docking</font>"
if("undocking")
dat += "<font color='[override_en? "red" : "yellow"]'>Undocking</font>"
if("docked")
dat += "<font color='[override_en? "red" : "green"]'>Docked</font>"
if(override_en) dat += " <font color='red'>(Override Enabled)</font>"
dat += ". <A href='?src=\ref[src];refresh=[1]'>\[Refresh\]</A><br><br>"
switch(docking_status)
if("undocked")
dat += "<b><A href='?src=\ref[src];dock_command=[1]'>Dock</A></b>"
if("docked")
dat += "<b><A href='?src=\ref[src];undock_command=[1]'>Undock</A></b>"
dat += "</center>"
user << browse("[dat]", "window=[shuttle_tag]shuttlecontrol;size=300x600")
//check if we're undocked, give option to force launch
/obj/machinery/computer/shuttle_control/proc/check_docking(datum/shuttle/multi_shuttle/MS)
if(MS.skip_docking_checks() || MS.docking_controller.can_launch())
return 1
var/choice = alert("The shuttle is currently docked! Please undock before continuing.","Error","Cancel","Force Launch")
if(choice == "Cancel")
return 0
choice = alert("Forcing a shuttle launch while docked may result in severe injury, death and/or damage to property. Are you sure you wish to continue?", "Force Launch", "Force Launch", "Cancel")
if(choice == "Cancel")
return 0
return 1
/obj/machinery/computer/shuttle_control/multi/Topic(href, href_list)
if(..())
return 1
usr.set_machine(src)
src.add_fingerprint(usr)
var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag]
if(!istype(MS)) return
//to_world("multi_shuttle: last_departed=[MS.last_departed], origin=[MS.origin], interim=[MS.interim], travel_time=[MS.move_time]")
if(href_list["refresh"])
updateUsrDialog()
return
if (MS.moving_status != SHUTTLE_IDLE)
to_chat(usr, "<font color='blue'>[shuttle_tag] vessel is moving.</font>")
return
if(href_list["dock_command"])
MS.dock()
return
if(href_list["undock_command"])
MS.undock()
return
if(href_list["start"])
if(MS.at_origin)
to_chat(usr, "<font color='red'>You are already at the home base.</font>")
return
if((MS.last_move + MS.cooldown*10) > world.time)
to_chat(usr, "<font color='red'>The ship's drive is inoperable while the engines are charging.</font>")
return
if(!check_docking(MS))
updateUsrDialog()
return
if(!MS.return_warning && !MS.legit) //VOREStation Add - Criminals only!
to_chat(usr, "<font color='red'>Returning to your home base will end your mission. If you are sure, press the button again.</font>")
//TODO: Actually end the mission.
MS.return_warning = 1
return
//VOREStation Add - Only long-jump if that's a thing you do
if(MS.interim)
MS.long_jump(MS.last_departed, MS.origin, MS.interim, MS.move_time)
else
MS.short_jump(MS.last_departed, MS.origin)
//VOREStation Add End
// No point giving a warning if it does literally nothing.
// if(!MS.return_warning)
// to_chat(usr, "<font color='red'>Returning to your home base will end your mission. If you are sure, press the button again.</font>")
// //TODO: Actually end the mission.
// MS.return_warning = 1
// return
//MS.long_jump(MS.last_departed, MS.origin, MS.interim, MS.move_time) //VOREStation Edit
MS.last_departed = MS.origin
MS.last_location = MS.start_location
MS.at_origin = 1
if(href_list["toggle_cloak"])
if(!MS.can_cloak)
return
MS.cloaked = !MS.cloaked
to_chat(usr, "<font color='red'> Ship [MS.legit ? "ATC inhibitor":"stealth"] systems have been [(MS.cloaked ? "activated. The station will not" : "deactivated. The station will")] be [MS.legit ? "notified":"warned"] of our arrival.</font>") //VOREStation Edit - Adds legit shuttles.
//to_chat(usr, "<font color='red'>Ship stealth systems have been [(MS.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival.</font>") //VOREStation Edit.
if(href_list["move_multi"])
if((MS.last_move + MS.cooldown*10) > world.time)
to_chat(usr, "<font color='red'>The ship's drive is inoperable while the engines are charging.</font>")
return
if(!check_docking(MS))
updateUsrDialog()
return
var/choice = input("Select a destination.") as null|anything in MS.destinations
if(!choice) return
to_chat(usr, "<font color='blue'>[shuttle_tag] main computer received message.</font>")
if(MS.at_origin)
MS.announce_arrival()
MS.last_departed = MS.origin
MS.at_origin = 0
//VOREStation Add - Only long-jump if that's a thing you do
if(MS.interim)
MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time)
else
MS.short_jump(MS.last_departed, MS.destinations[choice])
//VOREStation Add End
MS.last_departed = MS.destinations[choice]
MS.last_location = choice
return
else if(choice == MS.origin)
MS.announce_departure()
//VOREStation Add - Only long-jump if that's a thing you do
if(MS.interim)
MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time)
else
MS.short_jump(MS.last_departed, MS.destinations[choice])
return
//VOREStation Add End
//VOREStation Add - Only long-jump if that's a thing you do
if(MS.interim)
MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time)
else
MS.short_jump(MS.last_departed, MS.destinations[choice])
//VOREStation Add End
//MS.short_jump(MS.last_departed, MS.destinations[choice]) //VOREStation Removal
MS.last_departed = MS.destinations[choice]
MS.last_location = choice
updateUsrDialog()
command_announcement.Announce(arrival_message, (announcer ? announcer : "[using_map.boss_name]"))

View File

@@ -1,12 +1,11 @@
//This shuttle traverses a "web" of route_datums to have a wider range of places to go and make flying feel like movement is actually occuring.
/datum/shuttle/web_shuttle
flags = SHUTTLE_FLAGS_NONE
/datum/shuttle/autodock/web_shuttle
flags = SHUTTLE_FLAGS_ZERO_G
var/visible_name = null // The pretty name shown to people in announcements, since the regular name var is used internally for other things.
var/cloaked = FALSE
var/can_cloak = FALSE
var/cooldown = 0
var/last_move = 0 //the time at which we last moved
var/area/current_area = null
var/datum/shuttle_web_master/web_master = null
var/web_master_type = null
var/flight_time_modifier = 1.0
@@ -15,11 +14,10 @@
var/autopilot_delay = 60 // How many ticks to not do anything when not following an autopath. Should equal two minutes.
var/autopilot_first_delay = null // If your want your shuttle to stay for a different amount of time for the first time, set this.
var/can_rename = TRUE // Lets the pilot rename the shuttle. Only available once.
category = /datum/shuttle/web_shuttle
category = /datum/shuttle/autodock/web_shuttle
var/list/obj/item/clothing/head/pilot/helmets
/datum/shuttle/web_shuttle/New()
current_area = locate(current_area)
/datum/shuttle/autodock/web_shuttle/New()
web_master = new web_master_type(src)
build_destinations()
if(autopilot)
@@ -31,42 +29,45 @@
helmets = list()
..()
/datum/shuttle/web_shuttle/Destroy()
qdel(web_master)
/datum/shuttle/autodock/web_shuttle/Destroy()
QDEL_NULL(web_master)
helmets.Cut()
return ..()
/datum/shuttle/autodock/web_shuttle/current_dock_target()
// TODO - Probably don't even need to override this right? Debug testing code below will check!
. = web_master?.get_current_destination()?.my_landmark?.docking_controller?.id_tag
if (. != ..())
warning("Web shuttle [src] had current_dock_target()=[.] but autodock.current_dock_target() = [..()]")
/datum/shuttle/web_shuttle/current_dock_target()
if(web_master)
return web_master.current_dock_target()
/datum/shuttle/web_shuttle/move(var/area/origin, var/area/destination)
/datum/shuttle/autodock/web_shuttle/perform_shuttle_move()
..()
last_move = world.time
active_docking_controller = current_location.docking_controller
update_docking_target(current_location)
/datum/shuttle/web_shuttle/short_jump()
/datum/shuttle/autodock/web_shuttle/short_jump()
. = ..()
update_helmets()
/datum/shuttle/web_shuttle/long_jump()
/datum/shuttle/autodock/web_shuttle/long_jump()
. = ..()
update_helmets()
/datum/shuttle/web_shuttle/on_shuttle_departure()
/datum/shuttle/autodock/web_shuttle/on_shuttle_departure()
. = ..()
web_master.on_shuttle_departure()
update_helmets()
/datum/shuttle/web_shuttle/on_shuttle_arrival()
/datum/shuttle/autodock/web_shuttle/on_shuttle_arrival()
. = ..()
web_master.on_shuttle_arrival()
update_helmets()
/datum/shuttle/web_shuttle/proc/build_destinations()
/datum/shuttle/autodock/web_shuttle/proc/build_destinations()
return
/datum/shuttle/web_shuttle/process()
/datum/shuttle/autodock/web_shuttle/process()
update_helmets()
if(moving_status == SHUTTLE_IDLE)
@@ -76,8 +77,8 @@
else // Otherwise we are about to start one or just finished one.
if(autopilot_delay > 0) // Wait for awhile so people can get on and off.
if(docking_controller && !skip_docking_checks()) // Dock to the destination if possible.
var/docking_status = docking_controller.get_docking_status()
if(active_docking_controller && shuttle_docking_controller) // Dock to the destination if possible.
var/docking_status = shuttle_docking_controller.get_docking_status()
if(docking_status == "undocked")
dock()
autopilot_say("Docking.")
@@ -96,8 +97,8 @@
autopilot_delay--
else // Time to go.
if(docking_controller && !skip_docking_checks()) // Undock if possible.
var/docking_status = docking_controller.get_docking_status()
if(active_docking_controller && shuttle_docking_controller) // Undock if possible.
var/docking_status = shuttle_docking_controller.get_docking_status()
if(docking_status == "docked")
undock()
autopilot_say("Undocking.")
@@ -109,13 +110,13 @@
autopilot_say("Taking off.")
web_master.process_autopath()
/datum/shuttle/web_shuttle/proc/update_helmets()
/datum/shuttle/autodock/web_shuttle/proc/update_helmets()
for(var/helm in helmets)
if(!helm)
helmets -= helm
continue
var/obj/item/clothing/head/pilot/H = helm
if(!H.shuttle_comp || get_area(H.shuttle_comp) != get_area(H))
if(QDELETED(H))
helmets -= H
continue
if(!H.shuttle_comp || !(get_area(H) in shuttle_area))
H.shuttle_comp = null
H.audible_message("<span class='warning'>\The [H] pings as it loses it's connection with the ship.</span>")
H.update_hud("discon")
@@ -123,24 +124,24 @@
else
H.update_hud(moving_status)
/datum/shuttle/web_shuttle/proc/adjust_autopilot(on)
/datum/shuttle/autodock/web_shuttle/proc/adjust_autopilot(on)
if(on)
if(autopilot)
return
autopilot = TRUE
autopilot_delay = initial(autopilot_delay)
shuttle_controller.process_shuttles += src
shuttle_controller.process_shuttles |= src
else
if(!autopilot)
return
autopilot = FALSE
shuttle_controller.process_shuttles -= src
/datum/shuttle/web_shuttle/proc/autopilot_say(message) // Makes the autopilot 'talk' to the passengers.
/datum/shuttle/autodock/web_shuttle/proc/autopilot_say(message) // Makes the autopilot 'talk' to the passengers.
var/padded_message = "<span class='game say'><span class='name'>shuttle autopilot</span> states, \"[message]\"</span>"
message_passengers(current_area, padded_message)
message_passengers(padded_message)
/datum/shuttle/web_shuttle/proc/rename_shuttle(mob/user)
/datum/shuttle/autodock/web_shuttle/proc/rename_shuttle(mob/user)
if(!can_rename)
to_chat(user, "<span class='warning'>You can't rename this vessel.</span>")
return
@@ -162,6 +163,8 @@
var/list/my_doors //Should be list("id_tag" = "Pretty Door Name", ...)
var/list/my_sensors //Should be list("id_tag" = "Pretty Sensor Name", ...)
// Note - Searching own area for doors/sensors is fine for legacy web shuttles as they are single-area.
// However if this code is copied to future multi-area shuttles, should search in all shuttle areas
/obj/machinery/computer/shuttle_control/web/Initialize()
. = ..()
var/area/my_area = get_area(src)
@@ -186,7 +189,7 @@
log_debug("[my_area] shuttle computer couldn't find [lost] sensor!")
/obj/machinery/computer/shuttle_control/web/attackby(obj/I, mob/user)
var/datum/shuttle/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag]
if(shuttle && istype(I,/obj/item/clothing/head/pilot))
var/obj/item/clothing/head/pilot/H = I
H.shuttle_comp = src
@@ -208,7 +211,7 @@
/*
// If nanoUI falls over and you want a non-nanoUI UI, feel free to uncomment this section.
var/datum/shuttle/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag]
if(!istype(WS))
message_admins("ERROR: Shuttle computer ([src]) ([shuttle_tag]) could not find their shuttle in the shuttles list.")
return
@@ -272,8 +275,9 @@
/obj/machinery/computer/shuttle_control/web/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/list/routes[0]
var/datum/shuttle/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/web_shuttle/shuttle = SSshuttles.shuttles[shuttle_tag]
if(!istype(shuttle))
to_chat(user, "<span class='warning'>Unable to establish link with the shuttle.</span>")
return
var/list/R = shuttle.web_master.get_available_routes()
@@ -333,11 +337,11 @@
"future_location" = future_location,
"shuttle_state" = shuttle_state,
"routes" = routes,
"has_docking" = shuttle.docking_controller? 1 : 0,
"has_docking" = shuttle.shuttle_docking_controller? 1 : 0,
"skip_docking" = shuttle.skip_docking_checks(),
"is_moving" = shuttle.moving_status != SHUTTLE_IDLE,
"docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null,
"docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null,
"docking_status" = shuttle.shuttle_docking_controller? shuttle.shuttle_docking_controller.get_docking_status() : null,
"docking_override" = shuttle.shuttle_docking_controller? shuttle.shuttle_docking_controller.override_enabled : null,
"is_in_transit" = shuttle.has_arrive_time(),
"travel_progress" = between(0, percent_finished, 100),
"time_left" = round( (total_time - elapsed_time) / 10),
@@ -360,13 +364,13 @@
/obj/machinery/computer/shuttle_control/web/Topic(href, href_list)
if(..())
return 1
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/datum/shuttle/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/web_shuttle/WS = SSshuttles.shuttles[shuttle_tag]
if(!istype(WS))
message_admins("ERROR: Shuttle computer ([src]) ([shuttle_tag]) could not find their shuttle in the shuttles list.")
return
@@ -436,19 +440,38 @@
message_admins("ERROR: Shuttle computer was asked to travel to a nonexistant destination.")
return
WS.next_location = target_destination.my_landmark
if(!can_move(WS, usr))
return
WS.web_master.future_destination = target_destination
to_chat(usr, "<span class='notice'>[WS.visible_name] flight computer received command.</span>")
WS.web_master.reset_autopath() // Deviating from the path will almost certainly confuse the autopilot, so lets just reset its memory.
var/travel_time = new_route.travel_time * WS.flight_time_modifier
// TODO - Leshana - Change this to use proccess stuff of autodock!
if(new_route.interim && new_route.travel_time)
WS.long_jump(WS.current_area, target_destination.my_area, new_route.interim, travel_time / 10)
WS.long_jump(target_destination.my_landmark, new_route.interim, travel_time / 10)
else
WS.short_jump(WS.current_area, target_destination.my_area)
WS.short_jump(target_destination.my_landmark)
ui_interact(usr)
//check if we're undocked, give option to force launch
/obj/machinery/computer/shuttle_control/web/proc/check_docking(datum/shuttle/autodock/MS)
if(MS.skip_docking_checks() || MS.check_undocked())
return 1
var/choice = alert("The shuttle is currently docked! Please undock before continuing.","Error","Cancel","Force Launch")
if(choice == "Cancel")
return 0
choice = alert("Forcing a shuttle launch while docked may result in severe injury, death and/or damage to property. Are you sure you wish to continue?", "Force Launch", "Force Launch", "Cancel")
if(choice == "Cancel")
return 0
return 1
// Props, for now.
/obj/structure/flight_left
name = "flight computer meters"
@@ -474,11 +497,18 @@
/obj/shuttle_connector/Initialize()
. = ..()
SSshuttles.OnDocksInitialized(CALLBACK(src, .proc/setup_routes))
GLOB.shuttle_added.register_global(src, .proc/setup_routes)
/obj/shuttle_connector/proc/setup_routes()
if(destinations && shuttle_name)
var/datum/shuttle/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name]
/obj/shuttle_connector/Destroy()
GLOB.shuttle_added.unregister_global(src, .proc/setup_routes)
. = ..()
// This is called whenever a shuttle is initialized. If its our shuttle, do our thing!
/obj/shuttle_connector/proc/setup_routes(var/new_shuttle)
var/datum/shuttle/autodock/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name]
if(ES != new_shuttle)
return // Its not our shuttle! Ignore!
if(destinations && istype(ES))
var/datum/shuttle_web_master/WM = ES.web_master
for(var/new_dest in destinations)
@@ -487,7 +517,9 @@
for(var/type_to_link in D.routes_to_make)
var/travel_delay = D.routes_to_make[type_to_link]
D.link_destinations(WM.get_destination_by_type(type_to_link), D.preferred_interim_area, travel_delay)
D.link_destinations(WM.get_destination_by_type(type_to_link), D.preferred_interim_tag, travel_delay)
else
warning("[log_info_line()]'s shuttle [global.log_info_line(ES)] initialized but destinations:[destinations]")
qdel(src)

View File

@@ -8,7 +8,7 @@
/datum/shuttle_route
var/datum/shuttle_destination/start = null // One of the two sides of this route. Start just means it was the creator of this route.
var/datum/shuttle_destination/end = null // The second side.
var/area/interim = null // Where the shuttle sits during the movement. Make sure no other shuttle shares this or Very Bad Things will happen.
var/var/obj/effect/shuttle_landmark/interim // Where the shuttle sits during the movement. Make sure no other shuttle shares this or Very Bad Things will happen.
var/travel_time = 0 // How long it takes to move from start to end, or end to start. Set to 0 for instant travel.
var/one_way = FALSE // If true, you can't travel from end to start.
@@ -16,7 +16,7 @@
start = _start
end = _end
if(_interim)
interim = locate(_interim)
interim = SSshuttles.get_landmark(_interim)
travel_time = _time
one_way = _oneway
@@ -50,14 +50,12 @@
// This is the second datum, and contains information on all the potential destinations for a specific shuttle.
/datum/shuttle_destination
var/name = "a place" // Name of the destination, used for the flight computer.
var/area/my_area = null // Where the shuttle will move to when it actually arrives.
var/obj/effect/shuttle_landmark/my_landmark = null // Where the shuttle will move to when it actually arrives.
var/datum/shuttle_web_master/master = null // The datum that does the coordination with the actual shuttle datum.
var/list/routes = list() // Routes that are connected to this destination.
var/preferred_interim_area = null // When building a new route, use this interim area.
var/preferred_interim_tag = null // When building a new route, use interim landmark with this tag.
var/skip_me = FALSE // We will not autocreate this one. Some map must be doing it.
var/dock_target = null // The tag_id that the shuttle will use to try to dock to the destination, if able.
var/radio_announce = 0 // Whether it will make a station announcement (0) or a radio announcement (1).
var/announcer = null // The name of the 'announcer' that will say the arrival/departure messages. Defaults to the map's boss name if blank.
// var/arrival_message = null // Message said if the ship enters this destination. Not announced if the ship is cloaked.
@@ -72,7 +70,9 @@
var/list/routes_to_make = list()
/datum/shuttle_destination/New(var/new_master)
my_area = locate(my_area)
my_landmark = SSshuttles.get_landmark(my_landmark)
if(!my_landmark)
log_debug("Web shuttle destination '[name]' could not find its landmark '[my_landmark]'.")
master = new_master
/datum/shuttle_destination/Destroy()
@@ -99,7 +99,7 @@
// Now link our new destination to us.
var/travel_delay = destinations_to_create[type_to_make]
link_destinations(new_dest, preferred_interim_area, travel_delay)
link_destinations(new_dest, preferred_interim_tag, travel_delay)
to_world("SHUTTLES: [name] has linked themselves to [new_dest.name]")
to_world("SHUTTLES: [name] has finished building destinations. already_made list is \[[english_list(already_made)]\].")
@@ -135,14 +135,14 @@
else
global_announcer.autosay(get_arrival_message(),(announcer ? announcer : "[using_map.boss_name]"))
/datum/shuttle_destination/proc/link_destinations(var/datum/shuttle_destination/other_place, var/area/interim_area, var/travel_time = 0)
/datum/shuttle_destination/proc/link_destinations(var/datum/shuttle_destination/other_place, var/interim_tag, var/travel_time = 0)
// First, check to make sure this doesn't cause a duplicate route.
for(var/datum/shuttle_route/R in routes)
if(R.start == other_place || R.end == other_place)
return
// Now we can connect them.
var/datum/shuttle_route/new_route = new(src, other_place, interim_area, travel_time)
var/datum/shuttle_route/new_route = new(src, other_place, interim_tag, travel_time)
routes += new_route
other_place.routes += new_route
@@ -166,7 +166,7 @@
// This is the third and final datum, which coordinates with the shuttle datum to tell it where it is, where it can go, and how long it will take.
// It is also responsible for instancing all the destinations it has control over, and linking them together.
/datum/shuttle_web_master
var/datum/shuttle/web_shuttle/my_shuttle = null // Ref to the shuttle this datum is coordinating with.
var/datum/shuttle/autodock/web_shuttle/my_shuttle = null // Ref to the shuttle this datum is coordinating with.
var/datum/shuttle_destination/current_destination = null // Where the shuttle currently is. Bit of a misnomer.
var/datum/shuttle_destination/future_destination = null // Where it will be in the near future.
var/datum/shuttle_destination/starting_destination = null // Where the shuttle will start at, generally at the home base.
@@ -204,7 +204,7 @@
for(var/datum/shuttle_destination/D in destinations)
for(var/type_to_link in D.routes_to_make)
var/travel_delay = D.routes_to_make[type_to_link]
D.link_destinations(get_destination_by_type(type_to_link), D.preferred_interim_area, travel_delay)
D.link_destinations(get_destination_by_type(type_to_link), D.preferred_interim_tag, travel_delay)
/datum/shuttle_web_master/proc/on_shuttle_departure()
current_destination.exit()
@@ -214,11 +214,6 @@
future_destination.enter()
current_destination = future_destination
future_destination = null
my_shuttle.current_area = current_destination.my_area
/datum/shuttle_web_master/proc/current_dock_target()
if(current_destination)
return current_destination.dock_target
/datum/shuttle_web_master/proc/get_available_routes()
if(current_destination)
@@ -254,10 +249,11 @@
future_destination = R.get_other_side(current_destination)
var/travel_time = R.travel_time * my_shuttle.flight_time_modifier * 2 // Autopilot is less efficent than having someone flying manually.
// TODO - Leshana - Change this to use proccess stuff of autodock!
if(R.interim && R.travel_time > 0)
my_shuttle.long_jump(my_shuttle.current_area, future_destination.my_area, R.interim, travel_time / 10)
my_shuttle.long_jump(future_destination.my_landmark, R.interim, travel_time / 10)
else
my_shuttle.short_jump(my_shuttle.current_area, future_destination.my_area)
my_shuttle.short_jump(future_destination.my_landmark)
return TRUE // Note this will return before the shuttle actually arrives.
/datum/shuttle_web_master/proc/process_autopath()

View File

@@ -78,12 +78,12 @@
return 1
/datum/unit_test/zas_area_test/supply_centcomm
name = "ZAS: Supply Shuttle (CentCom)"
area_path = /area/supply/dock
name = "ZAS: Supply Shuttle"
area_path = /area/shuttle/supply
/datum/unit_test/zas_area_test/emergency_shuttle
name = "ZAS: Emergency Shuttle"
area_path = /area/shuttle/escape/centcom
area_path = /area/shuttle/escape
/datum/unit_test/zas_area_test/ai_chamber
name = "ZAS: AI Chamber"

BIN
icons/effects/numbers.dmi Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

BIN
icons/obj/overmap.dmi Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -99,6 +99,11 @@ var/list/all_maps = list()
var/datum/spawnpoint/spawnpoint_stayed = /datum/spawnpoint/cryo // Used if you end the round on the station.
// VOREStation Edit End
var/use_overmap = 0 // If overmap should be used (including overmap space travel override)
var/overmap_size = 20 // Dimensions of overmap zlevel if overmap is used.
var/overmap_z = 0 // If 0 will generate overmap zlevel on init. Otherwise will populate the zlevel provided.
var/overmap_event_areas = 0 // How many event "clouds" will be generated
var/lobby_icon = 'icons/misc/title.dmi' // The icon which contains the lobby image(s)
var/list/lobby_screens = list("mockingjay00") // The list of lobby screen to pick() from. If left unset the first icon state is always selected.

View File

@@ -0,0 +1,84 @@
<h3>Shuttle Status</h3>
<div class="item" style="padding-top: 10px">
<div class="item">
{{:data.shuttle_status}}
</div>
{{if data.can_cloak}}
<div class="item">
{{:data.legit ? "ATC Inhibitor" : "Cloaking Field"}} is {{:data.cloaked ? "enabled" : "disabled"}}. {{:helper.link('Toggle', 'arrowreturn-1-s', {'toggle_cloaked' : '1'}) }}
</div>
{{/if}}
</div>
<div class="item" style="padding-top: 10px">
<div class="item">
<div class="itemLabel">
Bluespace Drive:
</div>
<div class="itemContent">
{{if data.shuttle_state == "idle"}}
<span class="idle">IDLE</span>
{{else data.shuttle_state == "warmup"}}
<span style="font-weight: bold;color: #336699">SPINNING UP</span>
{{else data.shuttle_state == "in_transit"}}
<span style="font-weight: bold;color: #336699">ENGAGED</span>
{{else}}
<span class="bad">ERROR</span>
{{/if}}
</div>
</div>
</div>
{{if data.has_docking}}
<div class="item" style="padding-top: 10px">
<div class="item">
<div class="itemLabel">
Docking Status:
</div>
<div class="itemContent">
{{if data.docking_status == "docked"}}
<span class="good">DOCKED</span>
{{else data.docking_status == "docking"}}
{{if !data.docking_override}}
<span class="average">DOCKING</span>
{{else}}
<span class="average">DOCKING-MANUAL</span>
{{/if}}
{{else data.docking_status == "undocking"}}
{{if !data.docking_override}}
<span class="average">UNDOCKING</span>
{{else}}
<span class="average">UNDOCKING-MANUAL</span>
{{/if}}
{{else data.docking_status == "undocked"}}
<span class="idle">UNDOCKED</span>
{{else}}
<span class="bad">ERROR</span>
{{/if}}
</div>
<div class="itemLabel">
Docking Codes:
</div>
<div class="itemContent">
{{:helper.link(data.docking_codes ? data.docking_codes : 'Not set', null, {'set_codes' : '1'}, null , null)}}
</div>
</div>
</div>
{{/if}}
<div class="item" style="padding-top: 10px">
<div class="itemLabel">
Current Destination:
</div>
<span class="average">{{:data.destination_name}}</span>
<div class="item">
{{:helper.link('Choose Destination', 'arrowreturn-1-s', {'pick' : '1'}, data.can_pick ? null : 'disabled' , null)}}
</div>
</div>
<h3>Shuttle Control</h3>
<div class="item" style="padding-top: 10px">
<div class="item">
<div class="itemContent" style="padding-top: 2px; width: 100%">
{{:helper.link('Launch Shuttle', 'arrowthickstop-1-e', {'move' : '1'}, data.can_launch ? null : 'disabled' , null)}}
{{:helper.link('Cancel Launch', 'cancel', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}}
{{:helper.link('Force Launch', 'alert', {'force' : '1'}, data.can_force ? null : 'disabled' , data.can_force ? 'redButton' : null)}}
</div>
</div>
</div>

Binary file not shown.

Binary file not shown.

View File

@@ -337,6 +337,8 @@
#include "code\datums\observation\logged_in.dm"
#include "code\datums\observation\moved.dm"
#include "code\datums\observation\observation.dm"
#include "code\datums\observation\shuttle_added.dm"
#include "code\datums\observation\shuttle_moved.dm"
#include "code\datums\observation\turf_changed.dm"
#include "code\datums\observation\unequipped.dm"
#include "code\datums\observation\z_moved.dm"
@@ -509,6 +511,7 @@
#include "code\game\area\Away Mission areas.dm"
#include "code\game\area\Space Station 13 areas.dm"
#include "code\game\area\Space Station 13 areas_vr.dm"
#include "code\game\area\ss13_deprecated_areas.dm"
#include "code\game\dna\dna2.dm"
#include "code\game\dna\dna2_domutcheck.dm"
#include "code\game\dna\dna2_helpers.dm"
@@ -2833,13 +2836,9 @@
#include "code\modules\organs\subtypes\vox_vr.dm"
#include "code\modules\organs\subtypes\xenos.dm"
#include "code\modules\overmap\_defines.dm"
#include "code\modules\overmap\overmap_object.dm"
#include "code\modules\overmap\sectors.dm"
#include "code\modules\overmap\ships\ship.dm"
#include "code\modules\overmap\ships\computers\engine_control.dm"
#include "code\modules\overmap\ships\computers\helm.dm"
#include "code\modules\overmap\ships\computers\shuttle.dm"
#include "code\modules\overmap\ships\engines\engine.dm"
#include "code\modules\overmap\ships\engines\thermal.dm"
#include "code\modules\overmap\spacetravel.dm"
#include "code\modules\paperwork\adminpaper.dm"
#include "code\modules\paperwork\carbonpaper.dm"
#include "code\modules\paperwork\clipboard.dm"
@@ -3177,8 +3176,11 @@
#include "code\modules\shuttles\crashes.dm"
#include "code\modules\shuttles\departmental.dm"
#include "code\modules\shuttles\escape_pods.dm"
#include "code\modules\shuttles\landmarks.dm"
#include "code\modules\shuttles\shuttle.dm"
#include "code\modules\shuttles\shuttle_autodock.dm"
#include "code\modules\shuttles\shuttle_console.dm"
#include "code\modules\shuttles\shuttle_console_multi.dm"
#include "code\modules\shuttles\shuttle_emergency.dm"
#include "code\modules\shuttles\shuttle_ferry.dm"
#include "code\modules\shuttles\shuttle_specops.dm"