//use this define to highlight docking port bounding boxes (ONLY FOR DEBUG USE) #ifdef TESTING #define DOCKING_PORT_HIGHLIGHT #endif //NORTH default dir /obj/docking_port invisibility = INVISIBILITY_ABSTRACT icon = 'icons/obj/device.dmi' //icon = 'icons/dirsquare.dmi' icon_state = "pinonfar" resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF anchored = TRUE // var/id // this should point -away- from the dockingport door, ie towards the ship dir = NORTH var/width = 0 //size of covered area, perpendicular to dir var/height = 0 //size of covered area, parallel to dir var/dwidth = 0 //position relative to covered area, perpendicular to dir var/dheight = 0 //position relative to covered area, parallel to dir var/area_type var/turf_type var/baseturf_type var/hidden = FALSE //are we invisible to shuttle navigation computers? //these objects are indestructible /obj/docking_port/Destroy(force) // unless you assert that you know what you're doing. Horrible things // may result. if(force) ..() . = QDEL_HINT_HARDDEL_NOW else return QDEL_HINT_LETMELIVE /obj/docking_port/take_damage() return /obj/docking_port/singularity_pull() return /obj/docking_port/singularity_act() return 0 /obj/docking_port/shuttleRotate() return //we don't rotate with shuttles via this code. //returns a list(x0,y0, x1,y1) where points 0 and 1 are bounding corners of the projected rectangle /obj/docking_port/proc/return_coords(_x, _y, _dir) if(_dir == null) _dir = dir if(_x == null) _x = x if(_y == null) _y = y //byond's sin and cos functions are inaccurate. This is faster and perfectly accurate var/cos = 1 var/sin = 0 switch(_dir) if(WEST) cos = 0 sin = 1 if(SOUTH) cos = -1 sin = 0 if(EAST) cos = 0 sin = -1 return list( _x + (-dwidth*cos) - (-dheight*sin), _y + (-dwidth*sin) + (-dheight*cos), _x + (-dwidth+width-1)*cos - (-dheight+height-1)*sin, _y + (-dwidth+width-1)*sin + (-dheight+height-1)*cos ) //returns turfs within our projected rectangle in no particular order /obj/docking_port/proc/return_turfs() var/list/L = return_coords() var/turf/T0 = locate(L[1],L[2],z) var/turf/T1 = locate(L[3],L[4],z) return block(T0,T1) //returns turfs within our projected rectangle in a specific order. //this ensures that turfs are copied over in the same order, regardless of any rotation /obj/docking_port/proc/return_ordered_turfs(_x, _y, _z, _dir) var/cos = 1 var/sin = 0 switch(_dir) if(WEST) cos = 0 sin = 1 if(SOUTH) cos = -1 sin = 0 if(EAST) cos = 0 sin = -1 . = list() for(var/dx in 0 to width-1) var/compX = dx-dwidth for(var/dy in 0 to height-1) var/compY = dy-dheight // realX = _x + compX*cos - compY*sin // realY = _y + compY*cos - compX*sin // locate(realX, realY, _z) var/turf/T = locate(_x + compX*cos - compY*sin, _y + compY*cos + compX*sin, _z) .[T] = NONE #ifdef DOCKING_PORT_HIGHLIGHT //Debug proc used to highlight bounding area /obj/docking_port/proc/highlight(_color) var/list/L = return_coords() var/turf/T0 = locate(L[1],L[2],z) var/turf/T1 = locate(L[3],L[4],z) for(var/turf/T in block(T0,T1)) T.color = _color LAZYINITLIST(T.atom_colours) T.maptext = null if(_color) var/turf/T = locate(L[1], L[2], z) T.color = "#0f0" T = locate(L[3], L[4], z) T.color = "#00f" #endif //return first-found touching dockingport /obj/docking_port/proc/get_docked() return locate(/obj/docking_port/stationary) in loc /obj/docking_port/proc/getDockedId() var/obj/docking_port/P = get_docked() if(P) return P.id /obj/docking_port/proc/is_in_shuttle_bounds(atom/A) var/turf/T = get_turf(A) if(T.z != z) return FALSE var/list/bounds = return_coords() var/x0 = bounds[1] var/y0 = bounds[2] var/x1 = bounds[3] var/y1 = bounds[4] if(x0 <= x1 && !ISINRANGE(T.x, x0, x1)) return FALSE else if(!ISINRANGE(T.x, x1, x0)) return FALSE if(y0 <= y1 && !ISINRANGE(T.y, y0, y1)) return FALSE else if(!ISINRANGE(T.y, y1, y0)) return FALSE return TRUE /obj/docking_port/stationary name = "dock" turf_type = /turf/open/space baseturf_type = /turf/open/space area_type = /area/space var/list/baseturf_cache var/last_dock_time /obj/docking_port/stationary/Initialize(mapload) . = ..() SSshuttle.stationary += src if(!id) id = "[SSshuttle.stationary.len]" if(name == "dock") name = "dock[SSshuttle.stationary.len]" baseturf_cache = typecacheof(baseturf_type) if(mapload) for(var/turf/T in return_turfs()) T.flags_1 |= NO_RUINS_1 #ifdef DOCKING_PORT_HIGHLIGHT highlight("#f00") #endif //returns first-found touching shuttleport /obj/docking_port/stationary/get_docked() . = locate(/obj/docking_port/mobile) in loc /obj/docking_port/stationary/transit name = "In Transit" turf_type = /turf/open/space/transit var/list/turf/assigned_turfs = list() var/area/shuttle/transit/assigned_area var/obj/docking_port/mobile/owner /obj/docking_port/stationary/transit/Initialize() . = ..() SSshuttle.transit += src /obj/docking_port/stationary/transit/proc/dezone() for(var/i in 1 to assigned_turfs.len) var/turf/T = assigned_turfs[i] if(T.type == turf_type) T.ChangeTurf(/turf/open/space,/turf/open/space) T.flags_1 |= UNUSED_TRANSIT_TURF_1 /obj/docking_port/stationary/transit/Destroy(force=FALSE) if(force) if(get_docked()) log_world("A transit dock was destroyed while something was docked to it.") SSshuttle.transit -= src if(owner) owner = null if(assigned_turfs) dezone() assigned_turfs.Cut() assigned_turfs = null . = ..() /obj/docking_port/mobile name = "shuttle" icon_state = "pinonclose" area_type = /area/shuttle var/list/area/shuttle/shuttle_areas var/timer //used as a timer (if you want time left to complete move, use timeLeft proc) var/last_timer_length var/mode = SHUTTLE_IDLE //current shuttle mode var/callTime = 100 //time spent in transit (deciseconds). Should not be lower then 10 seconds without editing the animation of the hyperspace ripples. var/ignitionTime = 55 // time spent "starting the engines". Also rate limits how often we try to reserve transit space if its ever full of transiting shuttles. var/roundstart_move //id of port to send shuttle to at roundstart // The direction the shuttle prefers to travel in var/preferred_direction = NORTH // And the angle from the front of the shuttle to the port var/port_direction = NORTH var/obj/docking_port/stationary/destination var/obj/docking_port/stationary/previous var/obj/docking_port/stationary/transit/assigned_transit var/launch_status = NOLAUNCH var/list/movement_force = list("KNOCKDOWN" = 3, "THROW" = 0) // A timid shuttle will not register itself with the shuttle subsystem // All shuttle templates are timid var/timid = FALSE var/list/ripples = list() var/engine_coeff = 1 //current engine coeff var/current_engines = 0 //current engine power var/initial_engines = 0 //initial engine power var/can_move_docking_ports = FALSE //if this shuttle can move docking ports other than the one it is docked at var/list/hidden_turfs = list() /obj/docking_port/mobile/proc/register() SSshuttle.mobile += src /obj/docking_port/mobile/Destroy(force) if(force) SSshuttle.mobile -= src destination = null previous = null assigned_transit = null shuttle_areas = null . = ..() /obj/docking_port/mobile/Initialize(mapload) . = ..() if(!timid) register() if(!id) id = "[SSshuttle.mobile.len]" if(name == "shuttle") name = "shuttle[SSshuttle.mobile.len]" shuttle_areas = list() var/list/all_turfs = return_ordered_turfs(x, y, z, dir) for(var/i in 1 to all_turfs.len) var/turf/curT = all_turfs[i] var/area/cur_area = curT.loc if(istype(cur_area, area_type)) shuttle_areas[cur_area] = TRUE initial_engines = count_engines() current_engines = initial_engines #ifdef DOCKING_PORT_HIGHLIGHT highlight("#0f0") #endif //this is a hook for custom behaviour. Maybe at some point we could add checks to see if engines are intact /obj/docking_port/mobile/proc/canMove() return TRUE //this is to check if this shuttle can physically dock at dock S /obj/docking_port/mobile/proc/canDock(obj/docking_port/stationary/S) if(!istype(S)) return SHUTTLE_NOT_A_DOCKING_PORT if(istype(S, /obj/docking_port/stationary/transit)) return SHUTTLE_CAN_DOCK if(dwidth > S.dwidth) return SHUTTLE_DWIDTH_TOO_LARGE if(width-dwidth > S.width-S.dwidth) return SHUTTLE_WIDTH_TOO_LARGE if(dheight > S.dheight) return SHUTTLE_DHEIGHT_TOO_LARGE if(height-dheight > S.height-S.dheight) return SHUTTLE_HEIGHT_TOO_LARGE //check the dock isn't occupied var/currently_docked = S.get_docked() if(currently_docked) // by someone other than us if(currently_docked != src) return SHUTTLE_SOMEONE_ELSE_DOCKED else // This isn't an error, per se, but we can't let the shuttle code // attempt to move us where we currently are, it will get weird. return SHUTTLE_ALREADY_DOCKED return SHUTTLE_CAN_DOCK /obj/docking_port/mobile/proc/check_dock(obj/docking_port/stationary/S) var/status = canDock(S) if(status == SHUTTLE_CAN_DOCK) return TRUE else if(status == SHUTTLE_ALREADY_DOCKED) // We're already docked there, don't need to do anything. // Triggering shuttle movement code in place is weird return FALSE else var/msg = "Shuttle [src] cannot dock at [S], error: [status]" message_admins(msg) return FALSE /obj/docking_port/mobile/proc/transit_failure() message_admins("Shuttle [src] repeatedly failed to create transit zone.") //call the shuttle to destination S /obj/docking_port/mobile/proc/request(obj/docking_port/stationary/S) if(!check_dock(S)) testing("check_dock failed on request for [src]") return if(mode == SHUTTLE_IGNITING && destination == S) return switch(mode) if(SHUTTLE_CALL) if(S == destination) if(timeLeft(1) < callTime * engine_coeff) setTimer(callTime * engine_coeff) else destination = S setTimer(callTime * engine_coeff) if(SHUTTLE_RECALL) if(S == destination) setTimer(callTime * engine_coeff - timeLeft(1)) else destination = S setTimer(callTime * engine_coeff) mode = SHUTTLE_CALL if(SHUTTLE_IDLE, SHUTTLE_IGNITING) destination = S mode = SHUTTLE_IGNITING setTimer(ignitionTime) //recall the shuttle to where it was previously /obj/docking_port/mobile/proc/cancel() if(mode != SHUTTLE_CALL) return remove_ripples() invertTimer() mode = SHUTTLE_RECALL /obj/docking_port/mobile/proc/enterTransit() if((SSshuttle.lockdown && (z in GLOB.station_z_levels)) || !canMove()) //emp went off, no escape mode = SHUTTLE_IDLE return previous = null // if(!destination) // return var/obj/docking_port/stationary/S0 = get_docked() var/obj/docking_port/stationary/S1 = assigned_transit if(S1) if(dock(S1) != DOCKING_SUCCESS) WARNING("shuttle \"[id]\" could not enter transit space. Docked at [S0 ? S0.id : "null"]. Transit dock [S1 ? S1.id : "null"].") else previous = S0 else WARNING("shuttle \"[id]\" could not enter transit space. S0=[S0 ? S0.id : "null"] S1=[S1 ? S1.id : "null"]") /obj/docking_port/mobile/proc/jumpToNullSpace() // Destroys the docking port and the shuttle contents. // Not in a fancy way, it just ceases. var/obj/docking_port/stationary/current_dock = get_docked() var/turf_type = /turf/open/space var/baseturf_type = /turf/open/space var/underlying_area_type = /area/space // If the shuttle is docked to a stationary port, restore its normal // "empty" area and turf if(current_dock) if(current_dock.turf_type) turf_type = current_dock.turf_type if(current_dock.baseturf_type) baseturf_type = current_dock.baseturf_type if(current_dock.area_type) underlying_area_type = current_dock.area_type var/list/old_turfs = return_ordered_turfs(x, y, z, dir) var/area/underlying_area = locate(underlying_area_type) in GLOB.sortedAreas if(!underlying_area) underlying_area = new underlying_area_type(null) for(var/i in 1 to old_turfs.len) var/turf/oldT = old_turfs[i] if(!oldT || !istype(oldT.loc, area_type)) continue var/area/old_area = oldT.loc underlying_area.contents += oldT oldT.change_area(old_area, underlying_area) oldT.empty(turf_type, baseturf_type) qdel(src, force=TRUE) /obj/docking_port/mobile/proc/create_ripples(obj/docking_port/stationary/S1, animate_time) var/list/turfs = ripple_area(S1) for(var/t in turfs) ripples += new /obj/effect/temp_visual/ripple(t, animate_time) /obj/docking_port/mobile/proc/remove_ripples() for(var/R in ripples) qdel(R) ripples.Cut() /obj/docking_port/mobile/proc/ripple_area(obj/docking_port/stationary/S1) var/list/L0 = return_ordered_turfs(x, y, z, dir) var/list/L1 = return_ordered_turfs(S1.x, S1.y, S1.z, S1.dir) var/list/ripple_turfs = list() for(var/i in 1 to L0.len) var/turf/T0 = L0[i] if(!T0 || !istype(T0.loc, area_type)) continue var/turf/T1 = L1[i] if(!T1) continue if(T0.type != T0.baseturf) ripple_turfs += T1 return ripple_turfs /obj/docking_port/mobile/proc/check_poddoors() for(var/obj/machinery/door/poddoor/shuttledock/pod in GLOB.airlocks) pod.check() //this is the main proc. It instantly moves our mobile port to stationary port new_dock /obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/new_dock, movement_direction, force=FALSE) // Crashing this ship with NO SURVIVORS if(new_dock.get_docked() == src) remove_ripples() return DOCKING_SUCCESS if(!force) if(!check_dock(new_dock)) return DOCKING_BLOCKED if(!canMove()) return DOCKING_IMMOBILIZED var/obj/docking_port/stationary/old_dock = get_docked() // The turf that gets placed under where the shuttle moved from var/underlying_turf_type = /turf/open/space // The baseturf that the gets assigned to the turf_type above var/underlying_baseturf_type = /turf/open/space // The area that gets placed under where the shuttle moved from var/underlying_area_type = /area/space // The baseturf cache is a typecache of what counts as a baseturf to be left behind var/list/baseturf_cache if(old_dock) //Dock overwrites if(old_dock.turf_type) underlying_turf_type = old_dock.turf_type if(old_dock.baseturf_type) underlying_baseturf_type = old_dock.baseturf_type if(old_dock.area_type) underlying_area_type = old_dock.area_type if(old_dock.baseturf_cache) baseturf_cache = old_dock.baseturf_cache if(!baseturf_cache) baseturf_cache = typecacheof(underlying_baseturf_type) /************************************************************************************************************** old_turfs is an associative list with a turf:bitflag structure new_turfs is a standard list composed of turf instances The bitflag contains the data for what inhabitants of that coordinate should be moved to the new location The bitflags can be found in __DEFINES/shuttles.dm */ var/list/old_turfs = return_ordered_turfs(x, y, z, dir) var/list/new_turfs = return_ordered_turfs(new_dock.x, new_dock.y, new_dock.z, new_dock.dir) /**************************************************************************************************************/ // The underlying old area is the area assumed to be under the shuttle's starting location // If it no longer/has never existed it will be created var/area/underlying_old_area = locate(underlying_area_type) in GLOB.sortedAreas if(!underlying_old_area) underlying_old_area = new underlying_area_type(null) var/rotation = 0 if(new_dock.dir != dir) //Even when the dirs are the same rotation is coming out as not 0 for some reason rotation = dir2angle(new_dock.dir)-dir2angle(dir) if ((rotation % 90) != 0) rotation += (rotation % 90) //diagonal rotations not allowed, round up rotation = SIMPLIFY_DEGREES(rotation) if(!movement_direction) movement_direction = turn(preferred_direction, 180) remove_ripples() var/list/moved_atoms = list() //Everything not a turf that gets moved in the shuttle var/list/areas_to_move = list() //unique assoc list of areas on turfs being moved CHECK_TICK /****************************************All beforeShuttleMove procs*****************************************/ for(var/i in 1 to old_turfs.len) CHECK_TICK var/turf/oldT = old_turfs[i] var/turf/newT = new_turfs[i] if(!newT) return DOCKING_NULL_DESTINATION if(!oldT) return DOCKING_NULL_SOURCE var/area/old_area = oldT.loc var/move_mode = old_area.beforeShuttleMove(shuttle_areas) //areas var/list/old_contents = oldT.contents for(var/k in 1 to old_contents.len) CHECK_TICK var/atom/movable/moving_atom = old_contents[k] if(moving_atom.loc != oldT) //fix for multi-tile objects continue move_mode = moving_atom.beforeShuttleMove(newT, rotation, move_mode) //atoms move_mode = oldT.fromShuttleMove(newT, underlying_turf_type, baseturf_cache, move_mode) //turfs move_mode = newT.toShuttleMove(oldT, move_mode , src) //turfs if(move_mode & MOVE_AREA) areas_to_move[old_area] = TRUE old_turfs[oldT] = move_mode /*******************************************Hiding turfs if necessary*******************************************/ var/list/new_hidden_turfs if(hidden) new_hidden_turfs = list() for(var/i in 1 to old_turfs.len) CHECK_TICK var/turf/oldT = old_turfs[i] if(old_turfs[oldT] & MOVE_TURF) new_hidden_turfs += new_turfs[i] SSshuttle.update_hidden_docking_ports(null, new_hidden_turfs) /*******************************************All onShuttleMove procs******************************************/ CHECK_TICK for(var/i in 1 to old_turfs.len) var/turf/oldT = old_turfs[i] var/turf/newT = new_turfs[i] var/move_mode = old_turfs[oldT] if(move_mode & MOVE_CONTENTS) for(var/k in oldT) var/atom/movable/moving_atom = k if(moving_atom.loc != oldT) //fix for multi-tile objects continue moving_atom.onShuttleMove(newT, oldT, movement_force, movement_direction, old_dock, src) //atoms moved_atoms[moving_atom] = oldT if(move_mode & MOVE_TURF) oldT.onShuttleMove(newT, movement_force, movement_direction) //turfs if(move_mode & MOVE_AREA) var/area/shuttle_area = oldT.loc shuttle_area.onShuttleMove(oldT, newT, underlying_old_area) //areas /******************************************All afterShuttleMove procs****************************************/ underlying_old_area.afterShuttleMove() // Parallax handling // This needs to be done before the atom after move var/new_parallax_dir = FALSE if(istype(new_dock, /obj/docking_port/stationary/transit)) new_parallax_dir = preferred_direction for(var/i in 1 to areas_to_move.len) CHECK_TICK var/area/internal_area = areas_to_move[i] internal_area.afterShuttleMove(new_parallax_dir) //areas for(var/i in 1 to old_turfs.len) CHECK_TICK if(!(old_turfs[old_turfs[i]] & MOVE_TURF)) continue var/turf/oldT = old_turfs[i] var/turf/newT = new_turfs[i] newT.afterShuttleMove(oldT, underlying_turf_type, underlying_baseturf_type, rotation) //turfs for(var/i in 1 to moved_atoms.len) CHECK_TICK var/atom/movable/moved_object = moved_atoms[i] if(QDELETED(moved_object)) continue var/turf/oldT = moved_atoms[moved_object] moved_object.afterShuttleMove(oldT, movement_force, dir, preferred_direction, movement_direction, rotation)//atoms for(var/i in 1 to old_turfs.len) CHECK_TICK // Objects can block air so either turf or content changes means an air update is needed if(!(old_turfs[old_turfs[i]] & MOVE_CONTENTS | MOVE_TURF)) continue var/turf/oldT = old_turfs[i] var/turf/newT = new_turfs[i] oldT.blocks_air = initial(oldT.blocks_air) oldT.air_update_turf(TRUE) newT.blocks_air = initial(newT.blocks_air) newT.air_update_turf(TRUE) /*******************************************Unhiding turfs if necessary******************************************/ if(new_hidden_turfs) SSshuttle.update_hidden_docking_ports(hidden_turfs, null) hidden_turfs = new_hidden_turfs check_poddoors() new_dock.last_dock_time = world.time setDir(new_dock.dir) return DOCKING_SUCCESS /obj/docking_port/mobile/proc/findRoundstartDock() return SSshuttle.getDock(roundstart_move) /obj/docking_port/mobile/proc/dockRoundstart() . = dock_id(roundstart_move) /obj/docking_port/mobile/proc/dock_id(id) var/port = SSshuttle.getDock(id) if(port) . = dock(port) else . = null /obj/effect/landmark/shuttle_import name = "Shuttle Import" //used by shuttle subsystem to check timers /obj/docking_port/mobile/proc/check() check_effects() if(mode == SHUTTLE_IGNITING) check_transit_zone() if(timeLeft(1) > 0) return // If we can't dock or we don't have a transit slot, wait for 20 ds, // then try again switch(mode) if(SHUTTLE_CALL) var/error = dock(destination, preferred_direction) if(error && error & (DOCKING_NULL_DESTINATION | DOCKING_NULL_SOURCE)) var/msg = "A mobile dock in transit exited dock() with an error. This is most likely a mapping problem: Error: [error], ([src]) ([previous])" WARNING(msg) message_admins(msg) mode = SHUTTLE_IDLE return else if(error) setTimer(20) return if(SHUTTLE_RECALL) if(dock(previous) != DOCKING_SUCCESS) setTimer(20) return if(SHUTTLE_IGNITING) if(check_transit_zone() != TRANSIT_READY) setTimer(20) return else mode = SHUTTLE_CALL setTimer(callTime * engine_coeff) enterTransit() return mode = SHUTTLE_IDLE timer = 0 destination = null /obj/docking_port/mobile/proc/check_effects() if(!ripples.len) if((mode == SHUTTLE_CALL) || (mode == SHUTTLE_RECALL)) var/tl = timeLeft(1) if(tl <= SHUTTLE_RIPPLE_TIME) create_ripples(destination, tl) var/obj/docking_port/stationary/S0 = get_docked() if(istype(S0, /obj/docking_port/stationary/transit) && timeLeft(1) <= PARALLAX_LOOP_TIME) for(var/place in shuttle_areas) var/area/shuttle/shuttle_area = place if(shuttle_area.parallax_movedir) parallax_slowdown() /obj/docking_port/mobile/proc/parallax_slowdown() for(var/place in shuttle_areas) var/area/shuttle/shuttle_area = place shuttle_area.parallax_movedir = FALSE if(assigned_transit && assigned_transit.assigned_area) assigned_transit.assigned_area.parallax_movedir = FALSE var/list/L0 = return_ordered_turfs(x, y, z, dir) for (var/thing in L0) var/turf/T = thing if(!T || !istype(T.loc, area_type)) continue for (var/thing2 in T) var/atom/movable/AM = thing2 if (length(AM.client_mobs_in_contents)) AM.update_parallax_contents() /obj/docking_port/mobile/proc/check_transit_zone() if(assigned_transit) return TRANSIT_READY else SSshuttle.request_transit_dock(src) /obj/docking_port/mobile/proc/setTimer(wait) timer = world.time + wait last_timer_length = wait /obj/docking_port/mobile/proc/modTimer(multiple) var/time_remaining = timer - world.time if(time_remaining < 0 || !last_timer_length) return time_remaining *= multiple last_timer_length *= multiple setTimer(time_remaining) /obj/docking_port/mobile/proc/invertTimer() if(!last_timer_length) return var/time_remaining = timer - world.time if(time_remaining > 0) var/time_passed = last_timer_length - time_remaining setTimer(time_passed) //returns timeLeft /obj/docking_port/mobile/proc/timeLeft(divisor) if(divisor <= 0) divisor = 10 var/ds_remaining if(!timer) ds_remaining = callTime * engine_coeff else ds_remaining = max(0, timer - world.time) . = round(ds_remaining / divisor, 1) // returns 3-letter mode string, used by status screens and mob status panel /obj/docking_port/mobile/proc/getModeStr() switch(mode) if(SHUTTLE_IGNITING) return "IGN" if(SHUTTLE_RECALL) return "RCL" if(SHUTTLE_CALL) return "ETA" if(SHUTTLE_DOCKED) return "ETD" if(SHUTTLE_ESCAPE) return "ESC" if(SHUTTLE_STRANDED) return "ERR" return "" // returns 5-letter timer string, used by status screens and mob status panel /obj/docking_port/mobile/proc/getTimerStr() if(mode == SHUTTLE_STRANDED) return "--:--" var/timeleft = timeLeft() if(timeleft > 0) return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" else return "00:00" /obj/docking_port/mobile/proc/getStatusText() var/obj/docking_port/stationary/dockedAt = get_docked() . = (dockedAt && dockedAt.name) ? dockedAt.name : "unknown" if(istype(dockedAt, /obj/docking_port/stationary/transit)) var/obj/docking_port/stationary/dst if(mode == SHUTTLE_RECALL) dst = previous else dst = destination . += " towards [dst ? dst.name : "unknown location"] ([timeLeft(600)] minutes)" // attempts to locate /obj/machinery/computer/shuttle with matching ID inside the shuttle /obj/docking_port/mobile/proc/getControlConsole() for(var/place in shuttle_areas) var/area/shuttle/shuttle_area = place for(var/obj/machinery/computer/shuttle/S in shuttle_area) if(S.shuttleId == id) return S return null /obj/docking_port/mobile/proc/hyperspace_sound(phase, list/areas) var/s switch(phase) if(HYPERSPACE_WARMUP) s = 'sound/effects/hyperspace_begin.ogg' if(HYPERSPACE_LAUNCH) s = 'sound/effects/hyperspace_progress.ogg' if(HYPERSPACE_END) s = 'sound/effects/hyperspace_end.ogg' else CRASH("Invalid hyperspace sound phase: [phase]") for(var/A in areas) for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice playsound(E, s, 100, FALSE, max(width, height) - world.view) // Losing all initial engines should get you 2 // Adding another set of engines at 0.5 time /obj/docking_port/mobile/proc/alter_engines(mod) if(mod == 0) return var/old_coeff = engine_coeff engine_coeff = get_engine_coeff(current_engines,mod) current_engines = max(0,current_engines + mod) if(in_flight()) var/delta_coeff = engine_coeff / old_coeff modTimer(delta_coeff) /obj/docking_port/mobile/proc/count_engines() . = 0 for(var/thing in shuttle_areas) var/area/shuttle/areaInstance = thing for(var/obj/structure/shuttle/engine/E in areaInstance.contents) if(!QDELETED(E)) . += E.engine_power // Double initial engines to get to 0.5 minimum // Lose all initial engines to get to 2 //For 0 engine shuttles like BYOS 5 engines to get to doublespeed /obj/docking_port/mobile/proc/get_engine_coeff(current,engine_mod) var/new_value = max(0,current + engine_mod) if(new_value == initial_engines) return 1 if(new_value > initial_engines) var/delta = new_value - initial_engines var/change_per_engine = (1 - ENGINE_COEFF_MIN) / ENGINE_DEFAULT_MAXSPEED_ENGINES // 5 by default if(initial_engines > 0) change_per_engine = (1 - ENGINE_COEFF_MIN) / initial_engines // or however many it had return CLAMP(1 - delta * change_per_engine,ENGINE_COEFF_MIN,ENGINE_COEFF_MAX) if(new_value < initial_engines) var/delta = initial_engines - new_value var/change_per_engine = 1 //doesn't really matter should not be happening for 0 engine shuttles if(initial_engines > 0) change_per_engine = (ENGINE_COEFF_MAX - 1) / initial_engines //just linear drop to max delay return CLAMP(1 + delta * change_per_engine,ENGINE_COEFF_MIN,ENGINE_COEFF_MAX) /obj/docking_port/mobile/proc/in_flight() switch(mode) if(SHUTTLE_CALL,SHUTTLE_RECALL) return TRUE if(SHUTTLE_IDLE,SHUTTLE_IGNITING) return FALSE else return FALSE // hmm /obj/docking_port/mobile/emergency/in_flight() switch(mode) if(SHUTTLE_ESCAPE) return TRUE if(SHUTTLE_STRANDED,SHUTTLE_ENDGAME) return FALSE else return ..() //Called when emergency shuttle leaves the station /obj/docking_port/mobile/proc/on_emergency_launch() if(launch_status == UNLAUNCHED) //Pods will not launch from the mine/planet, and other ships won't launch unless we tell them to. launch_status = ENDGAME_LAUNCHED enterTransit() /obj/docking_port/mobile/emergency/on_emergency_launch() return //Called when emergency shuttle docks at centcom /obj/docking_port/mobile/proc/on_emergency_dock() //Mapping a new docking point for each ship mappers could potentially want docking with centcom would take up lots of space, just let them keep flying off into the sunset for their greentext if(launch_status == ENDGAME_LAUNCHED) launch_status = ENDGAME_TRANSIT /obj/docking_port/mobile/pod/on_emergency_dock() if(launch_status == ENDGAME_LAUNCHED) dock(SSshuttle.getDock("[id]_away")) //Escape pods dock at centcom mode = SHUTTLE_ENDGAME /obj/docking_port/mobile/emergency/on_emergency_dock() return #undef DOCKING_PORT_HIGHLIGHT