mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-06-18 20:03:21 +01:00
93ed0f096d
* Refactor/deduplicate camera eye code Camera Eyes previously had duplicated logic across several files. This change uncooks the spaghetti. Additionally, half-baked support for TG's multicam feature has been removed, as it was not functional or in use. * lets ff now * Camera Eye refactor fixes and finishing touches This change completes a refactor of AI eyes, which were previously used by xenobio consoles, syndicate and abductor camera consoles, shuttle docking computers, holograms, and, of course, the AI. Duplicated logic has been extracted to an abstract base mob, /mob/camera/eye, from which new types for each of the above now derive. Functionality is largely the same, with only a few minor cosmetic differences (i.e. camera eyes are now appropriately named given their type and user), as well as a quality-of-life enhancement for holograms, slowing their movement speed to base run speed to prevent users from accidentally zooming out of calls. * Camera eye refactor: Fix AI acceleration toggle The acceleration toggle was broken in the camera eye refactor, as previously the boolean was stored on the AI rather than its eye. This change fixes that. * Camera eye refactor: Fix syndicate cam visibility With the camera eye refactor, the syndicate advanced camera consoles lost the ability to view maintenance tunnels and other areas without active cameras, seeing static in their place instead (as all other cameras do). This change reinstates the original behavior. * Camera eye refactor: Convert spaces to tabs * Camera eye refactor: Fix CRLF * Apply suggestions from code review General minor code quality improvements suggested by GDNgit Co-authored-by: GDN <96800819+GDNgit@users.noreply.github.com> * Apply suggestions from code review Rename parameter names to avoid src accesses, remove an ambiguous and unused mob_define and holopad range variable from a previous WIP, change the for loop in /mob/camera/eye/relaymove to a for-to loop, and change the chat message warning, sent when an AI Eye is created on an AI that already has one, to a stack trace * Adds toggle to AI commands for fast holograms * Refactor ripped Hologram Eye relaymove Previously, the relaymove proc for hologram eyes was redundant and nearly impossible to read. It has been separated out into a few different named procs, and has had its use of `spawn` removed. * Remove unnecessary src access * Fix bug involving shuttle placement outlines The camera eye refactor that this commit is a part of introduced a bug that prevented shuttle placement outlines from showing up on first use of the shuttle console. This change fixes that bug. * Unrevert some changes from #26306 lost in merge * Remove erroneous free xray vision on advanced cams * Autodoc camera acceleration vars * Remove redundant null var initialization per code review Co-authored-by: Drsmail <60036448+Drsmail@users.noreply.github.com> Signed-off-by: asciodev <81930475+asciodev@users.noreply.github.com> * Changed variables to camel_case, autodocs, cleanup Changed a number of camera eye-related variables to camel_case style, added appropriate autodoc comments, as per code review. Also removed an unused cameranet function, modified the call signature of a cameranet function to be more semantic, and changed a qdel-on-initialize in camera eyes to return INITIALIZE_HINT_QDEL instead. Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> * Remove stray qdel(src) per code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: asciodev <81930475+asciodev@users.noreply.github.com> --------- Signed-off-by: asciodev <81930475+asciodev@users.noreply.github.com> Co-authored-by: GDN <96800819+GDNgit@users.noreply.github.com> Co-authored-by: Drsmail <60036448+Drsmail@users.noreply.github.com> Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
429 lines
15 KiB
Plaintext
429 lines
15 KiB
Plaintext
#define CALL_SHUTTLE_REASON_LENGTH 12
|
|
SUBSYSTEM_DEF(shuttle)
|
|
name = "Shuttle"
|
|
wait = 10
|
|
init_order = INIT_ORDER_SHUTTLE
|
|
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
|
|
runlevels = RUNLEVEL_SETUP | RUNLEVEL_GAME
|
|
offline_implications = "Shuttles will no longer function. Immediate server restart recommended."
|
|
cpu_display = SS_CPUDISPLAY_LOW
|
|
var/list/mobile = list()
|
|
var/list/stationary = list()
|
|
var/list/transit = list()
|
|
|
|
//emergency shuttle stuff
|
|
var/obj/docking_port/mobile/emergency/emergency
|
|
var/obj/docking_port/mobile/emergency/backup/backup_shuttle
|
|
var/emergencyCallTime = SHUTTLE_CALLTIME //time taken for emergency shuttle to reach the station when called (in deciseconds)
|
|
var/emergencyDockTime = SHUTTLE_DOCKTIME //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
|
|
var/emergencyEscapeTime = SHUTTLE_ESCAPETIME //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
|
|
var/emergency_sec_level_time = 0 // time sec level was last raised to red or higher
|
|
var/area/emergencyLastCallLoc
|
|
/// Things blocking escape shuttle from leaving.
|
|
var/list/hostile_environments = list()
|
|
|
|
//supply shuttle stuff
|
|
var/obj/docking_port/mobile/supply/supply
|
|
/// Supply shuttle turfs to make mail be put down faster
|
|
var/static/list/supply_shuttle_turfs = list()
|
|
|
|
var/list/hidden_shuttle_turfs = list() //all turfs hidden from navigation computers associated with a list containing the image hiding them and the type of the turf they are pretending to be
|
|
var/list/hidden_shuttle_turf_images = list() //only the images from the above list
|
|
/// Default refuel delay
|
|
var/refuel_delay = 20 MINUTES
|
|
/// Whether or not a custom shuttle has been ordered.
|
|
var/custom_shuttle_ordered = FALSE
|
|
// These vars are necessary to prevent multiple loads on the same turfs at the same times causing massive server issues
|
|
/// Whether or not a custom shuttle is currently loading at centcomm.
|
|
var/custom_escape_shuttle_loading = FALSE
|
|
/// Whether or not a shuttle is currently being loaded at the template landmark, if it exists.
|
|
var/loading_shuttle_at_preview_template = FALSE
|
|
/// Have we locked in the emergency shuttle, to prevent people from breaking things / wasting player money?
|
|
var/emergency_locked_in = FALSE
|
|
|
|
/datum/controller/subsystem/shuttle/Initialize()
|
|
if(!emergency)
|
|
WARNING("No /obj/docking_port/mobile/emergency placed on the map!")
|
|
if(!backup_shuttle)
|
|
WARNING("No /obj/docking_port/mobile/emergency/backup placed on the map!")
|
|
if(!supply)
|
|
WARNING("No /obj/docking_port/mobile/supply placed on the map!")
|
|
|
|
initial_load()
|
|
initial_move()
|
|
|
|
/datum/controller/subsystem/shuttle/get_stat_details()
|
|
return "M:[length(mobile)] S:[length(stationary)] T:[length(transit)]"
|
|
|
|
/datum/controller/subsystem/shuttle/proc/initial_load()
|
|
for(var/obj/docking_port/D in world)
|
|
D.register()
|
|
CHECK_TICK
|
|
|
|
/datum/controller/subsystem/shuttle/fire(resumed = FALSE)
|
|
for(var/thing in mobile)
|
|
if(thing)
|
|
var/obj/docking_port/mobile/P = thing
|
|
P.check()
|
|
continue
|
|
CHECK_TICK
|
|
mobile.Remove(thing)
|
|
|
|
/datum/controller/subsystem/shuttle/proc/getShuttle(id)
|
|
for(var/obj/docking_port/mobile/M in mobile)
|
|
if(M.id == id)
|
|
return M
|
|
WARNING("couldn't find shuttle with id: [id]")
|
|
|
|
/datum/controller/subsystem/shuttle/proc/getDock(id)
|
|
for(var/obj/docking_port/stationary/S in stationary)
|
|
if(S.id == id)
|
|
return S
|
|
WARNING("couldn't find dock with id: [id]")
|
|
|
|
/datum/controller/subsystem/shuttle/proc/secondsToRefuel()
|
|
var/elapsed = world.time - SSticker.round_start_time
|
|
var/remaining = round((refuel_delay - elapsed) / 10)
|
|
return remaining > 0 ? remaining : 0
|
|
|
|
/datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason)
|
|
if(!emergency)
|
|
WARNING("requestEvac(): There is no emergency shuttle, but the shuttle was called. Using the backup shuttle instead.")
|
|
if(!backup_shuttle)
|
|
WARNING("requestEvac(): There is no emergency shuttle, or backup shuttle!\
|
|
The game will be unresolvable.This is possibly a mapping error, \
|
|
more likely a bug with the shuttle \
|
|
manipulation system, or badminry. It is possible to manually \
|
|
resolve this problem by loading an emergency shuttle template \
|
|
manually, and then calling register() on the mobile docking port. \
|
|
Good luck.")
|
|
return
|
|
emergency = backup_shuttle
|
|
|
|
if(secondsToRefuel())
|
|
to_chat(user, "The emergency shuttle is refueling. Please wait another [abs(round(((world.time - SSticker.round_start_time) - refuel_delay)/600))] minutes before trying again.")
|
|
return
|
|
|
|
switch(emergency.mode)
|
|
if(SHUTTLE_RECALL)
|
|
to_chat(user, "The emergency shuttle may not be called while returning to Centcom.")
|
|
return
|
|
if(SHUTTLE_CALL)
|
|
to_chat(user, "The emergency shuttle is already on its way.")
|
|
return
|
|
if(SHUTTLE_DOCKED)
|
|
to_chat(user, "The emergency shuttle is already here.")
|
|
return
|
|
if(SHUTTLE_ESCAPE)
|
|
to_chat(user, "The emergency shuttle is moving away to a safe distance.")
|
|
return
|
|
if(SHUTTLE_STRANDED)
|
|
to_chat(user, "The emergency shuttle has been disabled by Centcom.")
|
|
return
|
|
|
|
if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH)
|
|
to_chat(user, "Reason is too short. [CALL_SHUTTLE_REASON_LENGTH] character minimum.")
|
|
return
|
|
|
|
var/area/signal_origin = get_area(user)
|
|
var/emergency_reason = "\nNature of emergency:\n\n[call_reason]"
|
|
if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
|
|
var/extra_minutes = 0
|
|
var/priority_time = emergencyCallTime * 0.5
|
|
if(world.time - emergency_sec_level_time < priority_time)
|
|
extra_minutes = 5
|
|
emergency.request(null, 0.5 + extra_minutes / (emergencyCallTime / 600), signal_origin, html_decode(emergency_reason), 1)
|
|
else
|
|
emergency.request(null, 1, signal_origin, html_decode(emergency_reason), 0)
|
|
|
|
log_game("[key_name(user)] has called the shuttle.")
|
|
message_admins("[key_name_admin(user)] has called the shuttle.")
|
|
|
|
return
|
|
|
|
|
|
// Called when an emergency shuttle mobile docking port is
|
|
// destroyed, which will only happen with admin intervention
|
|
/datum/controller/subsystem/shuttle/proc/emergencyDeregister()
|
|
// When a new emergency shuttle is created, it will override the
|
|
// backup shuttle.
|
|
emergency = backup_shuttle
|
|
|
|
/datum/controller/subsystem/shuttle/proc/cancelEvac(mob/user)
|
|
if(canRecall())
|
|
emergency.cancel(get_area(user))
|
|
log_game("[key_name(user)] has recalled the shuttle.")
|
|
message_admins("[key_name_admin(user)] has recalled the shuttle.")
|
|
return 1
|
|
|
|
/datum/controller/subsystem/shuttle/proc/canRecall()
|
|
if(emergency.mode != SHUTTLE_CALL)
|
|
return
|
|
if(!emergency.canRecall)
|
|
return
|
|
if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
|
|
if(emergency.timeLeft(1) < emergencyCallTime * 0.25)
|
|
return
|
|
else
|
|
if(emergency.timeLeft(1) < emergencyCallTime * 0.5)
|
|
return
|
|
return 1
|
|
|
|
/datum/controller/subsystem/shuttle/proc/autoEvac()
|
|
var/callShuttle = 1
|
|
|
|
for(var/thing in GLOB.shuttle_caller_list)
|
|
if(is_ai(thing))
|
|
var/mob/living/silicon/ai/AI = thing
|
|
if(AI.stat || !AI.client)
|
|
continue
|
|
else if(istype(thing, /obj/machinery/computer/communications))
|
|
var/obj/machinery/computer/communications/C = thing
|
|
if(C.stat & BROKEN)
|
|
continue
|
|
else if(istype(thing, /obj/item/circuitboard/communications))
|
|
continue
|
|
|
|
var/turf/T = get_turf(thing)
|
|
if(T && is_station_level(T.z))
|
|
callShuttle = 0
|
|
break
|
|
|
|
if(callShuttle)
|
|
if(emergency.mode < SHUTTLE_CALL)
|
|
emergency.request(null, 2.5)
|
|
log_game("There is no means of calling the shuttle anymore. Shuttle automatically called.")
|
|
message_admins("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
|
|
|
|
/datum/controller/subsystem/shuttle/proc/registerHostileEnvironment(datum/bad)
|
|
hostile_environments |= bad
|
|
|
|
/datum/controller/subsystem/shuttle/proc/clearHostileEnvironment(datum/bad)
|
|
hostile_environments -= bad
|
|
|
|
//try to move/request to dockHome if possible, otherwise dockAway. Mainly used for admin buttons
|
|
/datum/controller/subsystem/shuttle/proc/toggleShuttle(shuttleId, dockHome, dockAway, timed)
|
|
var/obj/docking_port/mobile/M = getShuttle(shuttleId)
|
|
if(!M)
|
|
return 1
|
|
var/obj/docking_port/stationary/dockedAt = M.get_docked()
|
|
var/destination = dockHome
|
|
if(dockedAt && dockedAt.id == dockHome)
|
|
destination = dockAway
|
|
if(timed)
|
|
if(M.request(getDock(destination)))
|
|
return 2
|
|
else
|
|
if(M.dock(getDock(destination)))
|
|
return 2
|
|
return 0 //dock successful
|
|
|
|
|
|
/datum/controller/subsystem/shuttle/proc/moveShuttle(shuttleId, dockId, timed, mob/user)
|
|
var/obj/docking_port/mobile/M = getShuttle(shuttleId)
|
|
var/obj/docking_port/stationary/D = getDock(dockId)
|
|
//check if the shuttle is on lockdown
|
|
if(M.uses_lockdown)
|
|
if(M.mode == SHUTTLE_IGNITING)
|
|
return 5
|
|
if(M.mode != SHUTTLE_IDLE)
|
|
return 4
|
|
if(M.lockeddown)
|
|
return 3
|
|
M.lockeddown = TRUE
|
|
addtimer(VARSET_CALLBACK(M, lockeddown, FALSE), 15 SECONDS)
|
|
if(!M)
|
|
return 1
|
|
M.last_caller = user // Save the caller of the shuttle for later logging
|
|
if(timed)
|
|
if(M.request(D))
|
|
return 2
|
|
else
|
|
if(M.dock(D))
|
|
return 2
|
|
return 0 //dock successful
|
|
|
|
/datum/controller/subsystem/shuttle/proc/initial_move()
|
|
for(var/obj/docking_port/mobile/M in mobile)
|
|
if(!M.roundstart_move)
|
|
continue
|
|
M.dockRoundstart()
|
|
|
|
/datum/controller/subsystem/shuttle/proc/get_dock_overlap(x0, y0, x1, y1, z)
|
|
. = list()
|
|
var/list/stationary_cache = stationary
|
|
for(var/i in 1 to length(stationary_cache))
|
|
var/obj/docking_port/port = stationary_cache[i]
|
|
if(!port || port.z != z)
|
|
continue
|
|
var/list/bounds = port.return_coords()
|
|
var/list/overlap = get_overlap(x0, y0, x1, y1, bounds[1], bounds[2], bounds[3], bounds[4])
|
|
var/list/xs = overlap[1]
|
|
var/list/ys = overlap[2]
|
|
if(length(xs) && length(ys))
|
|
.[port] = overlap
|
|
|
|
/datum/controller/subsystem/shuttle/proc/update_hidden_docking_ports(list/remove_turfs, list/add_turfs)
|
|
var/list/remove_images = list()
|
|
var/list/add_images = list()
|
|
|
|
if(remove_turfs)
|
|
for(var/T in remove_turfs)
|
|
var/list/L = hidden_shuttle_turfs[T]
|
|
if(L)
|
|
remove_images += L[1]
|
|
hidden_shuttle_turfs -= remove_turfs
|
|
|
|
if(add_turfs)
|
|
for(var/V in add_turfs)
|
|
var/turf/T = V
|
|
var/image/I
|
|
if(length(remove_images))
|
|
//we can just reuse any images we are about to delete instead of making new ones
|
|
I = remove_images[1]
|
|
remove_images.Cut(1, 2)
|
|
I.loc = T
|
|
else
|
|
I = image(loc = T)
|
|
add_images += I
|
|
I.appearance = T.appearance
|
|
I.override = TRUE
|
|
hidden_shuttle_turfs[T] = list(I, T.type)
|
|
|
|
hidden_shuttle_turf_images -= remove_images
|
|
hidden_shuttle_turf_images += add_images
|
|
|
|
for(var/V in GLOB.navigation_computers)
|
|
var/obj/machinery/computer/camera_advanced/shuttle_docker/C = V
|
|
C.update_hidden_docking_ports(remove_images, add_images)
|
|
|
|
QDEL_LIST_CONTENTS(remove_images)
|
|
|
|
/datum/controller/subsystem/shuttle/proc/mail_delivery()
|
|
for(var/obj/machinery/requests_console/console in GLOB.allRequestConsoles)
|
|
if(console.department != "Cargo Bay")
|
|
continue
|
|
console.createMessage("Messaging and Intergalactic Letters", "New Mail Crates ready to be ordered!", "A new mail crate is able to be shipped alongside your next orders!", RQ_NORMALPRIORITY)
|
|
|
|
if(!length(supply_shuttle_turfs))
|
|
for(var/turf/simulated/T in supply.areaInstance)
|
|
if(is_blocked_turf(T))
|
|
continue
|
|
supply_shuttle_turfs += T
|
|
if(!length(supply_shuttle_turfs)) // In case some nutjob walled the supply shuttle 10 minutes into the round
|
|
stack_trace("There were no available turfs on the Supply Shuttle to spawn a mail crate in!")
|
|
return
|
|
var/turf/spawn_location = pick(supply_shuttle_turfs)
|
|
new /obj/structure/closet/crate/mail(spawn_location)
|
|
|
|
// load an alternative shuttle in at the appropriate landmark.
|
|
/datum/controller/subsystem/shuttle/proc/load_template(datum/map_template/shuttle/S)
|
|
// load shuttle template, centred at shuttle import landmark,
|
|
if(loading_shuttle_at_preview_template)
|
|
CRASH("A shuttle was already loading at the preview template when another was loaded")
|
|
|
|
S.preload()
|
|
|
|
loading_shuttle_at_preview_template = TRUE
|
|
var/turf/landmark_turf = get_turf(locate("landmark*Shuttle Import"))
|
|
S.load(landmark_turf, centered = TRUE)
|
|
|
|
var/affected = S.get_affected_turfs(landmark_turf, centered = TRUE)
|
|
|
|
var/mobile_docking_ports = 0
|
|
var/obj/docking_port/mobile/port
|
|
// Search the turfs for docking ports
|
|
// - We need to find the mobile docking port because that is the heart of
|
|
// the shuttle.
|
|
// - We need to check that no additional ports have slipped in from the
|
|
// template, because that causes unintended behaviour.
|
|
for(var/T in affected)
|
|
for(var/obj/docking_port/P in T)
|
|
if(istype(P, /obj/docking_port/mobile))
|
|
port = P
|
|
mobile_docking_ports++
|
|
if(mobile_docking_ports > 1)
|
|
qdel(P, force = TRUE)
|
|
log_world("Map warning: Shuttle Template [S.mappath] has multiple mobile docking ports.")
|
|
else if(!port.timid)
|
|
// The shuttle template we loaded isn't "timid" which means
|
|
// it's already registered with the shuttles subsystem.
|
|
// This is a bad thing.
|
|
WARNING("Template [S] is non-timid! Unloading.")
|
|
port.jumpToNullSpace()
|
|
loading_shuttle_at_preview_template = FALSE
|
|
return
|
|
|
|
if(istype(P, /obj/docking_port/stationary))
|
|
log_world("Map warning: Shuttle Template [S.mappath] has a stationary docking port.")
|
|
|
|
if(port)
|
|
loading_shuttle_at_preview_template = FALSE
|
|
return port
|
|
|
|
for(var/T in affected)
|
|
var/turf/T0 = T
|
|
T0.contents = null
|
|
|
|
var/msg = "load_template(): Shuttle Template [S.mappath] has no mobile docking port. Aborting import."
|
|
message_admins(msg)
|
|
WARNING(msg)
|
|
loading_shuttle_at_preview_template = FALSE
|
|
|
|
/// Create a new shuttle and replace the emergency shuttle with it.
|
|
/// if loaded shuttle is passed in, a new one will not be loaded.
|
|
/datum/controller/subsystem/shuttle/proc/replace_shuttle(obj/docking_port/mobile/loaded_shuttle)
|
|
if(custom_escape_shuttle_loading)
|
|
CRASH("A custom escape shuttle was already being loaded at centcomm when another shuttle attempted to load.")
|
|
custom_escape_shuttle_loading = TRUE
|
|
// get the existing shuttle information, if any
|
|
var/timer = 0
|
|
var/mode = SHUTTLE_IDLE
|
|
var/obj/docking_port/stationary/dock
|
|
|
|
if(emergency)
|
|
timer = emergency.timer
|
|
mode = emergency.mode
|
|
dock = emergency.get_docked()
|
|
if(!dock) //lance moment
|
|
dock = getDock("emergency_away")
|
|
else
|
|
dock = loaded_shuttle.findRoundstartDock()
|
|
|
|
if(!dock)
|
|
var/m = "No dock found for preview shuttle, aborting."
|
|
WARNING(m)
|
|
custom_escape_shuttle_loading = FALSE
|
|
throw EXCEPTION(m)
|
|
|
|
var/result = loaded_shuttle.canDock(dock)
|
|
// truthy value means that it cannot dock for some reason
|
|
// but we can ignore the someone else docked error because we'll
|
|
// be moving into their place shortly
|
|
if((result != SHUTTLE_CAN_DOCK) && (result != SHUTTLE_SOMEONE_ELSE_DOCKED))
|
|
|
|
var/m = "Unsuccessful dock of [loaded_shuttle] ([result])."
|
|
message_admins(m)
|
|
WARNING(m)
|
|
custom_escape_shuttle_loading = FALSE
|
|
return
|
|
|
|
emergency.jumpToNullSpace()
|
|
|
|
loaded_shuttle.dock(dock)
|
|
|
|
// Shuttle state involves a mode and a timer based on world.time, so
|
|
// plugging the existing shuttles old values in works fine.
|
|
loaded_shuttle.timer = timer
|
|
loaded_shuttle.mode = mode
|
|
|
|
loaded_shuttle.register()
|
|
|
|
// TODO indicate to the user that success happened, rather than just
|
|
// blanking the modification tab
|
|
|
|
custom_escape_shuttle_loading = FALSE
|
|
return loaded_shuttle
|
|
|
|
#undef CALL_SHUTTLE_REASON_LENGTH
|