mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-20 15:21:29 +00:00
904 lines
24 KiB
Plaintext
904 lines
24 KiB
Plaintext
//use this define to highlight docking port bounding boxes (ONLY FOR DEBUG USE)
|
|
// also uncomment the #undef at the bottom of the file
|
|
//#define DOCKING_PORT_HIGHLIGHT
|
|
|
|
//NORTH default dir
|
|
/obj/docking_port
|
|
invisibility = 101
|
|
icon = 'icons/obj/device.dmi'
|
|
//icon = 'icons/dirsquare.dmi'
|
|
icon_state = "pinonfar"
|
|
|
|
unacidable = 1
|
|
anchored = 1
|
|
|
|
var/id
|
|
dir = NORTH //this should point -away- from the dockingport door, ie towards the ship
|
|
var/width = 0 //size of covered area, perpendicular to dir
|
|
var/height = 0 //size of covered area, paralell to dir
|
|
var/dwidth = 0 //position relative to covered area, perpendicular to dir
|
|
var/dheight = 0 //position relative to covered area, parallel to dir
|
|
|
|
// A timid shuttle will not register itself with the shuttle subsystem
|
|
// All shuttle templates are timid
|
|
var/timid = FALSE
|
|
|
|
var/list/ripples = list()
|
|
var/hidden = FALSE //are we invisible to shuttle navigation computers?
|
|
|
|
//these objects are indestructable
|
|
/obj/docking_port/Destroy(force)
|
|
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)
|
|
_dir = dir
|
|
if(!_x)
|
|
_x = x
|
|
if(!_y)
|
|
_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, area/A)
|
|
if(!_dir)
|
|
_dir = dir
|
|
if(!_x)
|
|
_x = x
|
|
if(!_y)
|
|
_y = y
|
|
if(!_z)
|
|
_z = z
|
|
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()
|
|
|
|
var/xi
|
|
var/yi
|
|
for(var/dx=0, dx<width, ++dx)
|
|
for(var/dy=0, dy<height, ++dy)
|
|
xi = _x + (dx-dwidth)*cos - (dy-dheight)*sin
|
|
yi = _y + (dy-dheight)*cos + (dx-dwidth)*sin
|
|
var/turf/T = locate(xi, yi, _z)
|
|
if(A)
|
|
if(get_area(T) == A)
|
|
. += T
|
|
else
|
|
. += null
|
|
else
|
|
. += T
|
|
|
|
#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
|
|
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/register()
|
|
return 0
|
|
|
|
/obj/docking_port/stationary
|
|
name = "dock"
|
|
|
|
var/turf_type = /turf/space
|
|
var/area_type = /area/space
|
|
|
|
var/lock_shuttle_doors = 0
|
|
|
|
/obj/docking_port/stationary/register()
|
|
if(!SSshuttle)
|
|
throw EXCEPTION("docking port [src] could not initialize.")
|
|
return 0
|
|
|
|
SSshuttle.stationary += src
|
|
if(!id)
|
|
id = "[SSshuttle.stationary.len]"
|
|
if(name == "dock")
|
|
name = "dock[SSshuttle.stationary.len]"
|
|
|
|
#ifdef DOCKING_PORT_HIGHLIGHT
|
|
highlight("#f00")
|
|
#endif
|
|
return 1
|
|
|
|
//returns first-found touching shuttleport
|
|
/obj/docking_port/stationary/get_docked()
|
|
return locate(/obj/docking_port/mobile) in loc
|
|
/*
|
|
for(var/turf/T in return_ordered_turfs())
|
|
. = locate(/obj/docking_port/mobile) in loc
|
|
if(.)
|
|
return .
|
|
*/
|
|
|
|
/obj/docking_port/stationary/transit
|
|
name = "In transit"
|
|
turf_type = /turf/space/transit
|
|
|
|
lock_shuttle_doors = 1
|
|
|
|
/obj/docking_port/stationary/transit/register()
|
|
if(!..())
|
|
return 0
|
|
|
|
name = "In transit" //This looks weird, but- it means that the on-map instances can be named something actually usable to search for, but still appear correctly in terminals.
|
|
|
|
SSshuttle.transit += src
|
|
return 1
|
|
|
|
/obj/docking_port/mobile
|
|
icon_state = "mobile"
|
|
name = "shuttle"
|
|
icon_state = "pinonclose"
|
|
|
|
var/area/shuttle/areaInstance
|
|
var/list/shuttle_areas
|
|
|
|
var/timer //used as a timer (if you want time left to complete move, use timeLeft proc)
|
|
var/mode = SHUTTLE_IDLE //current shuttle mode (see global defines)
|
|
var/callTime = 50 //time spent in transit (deciseconds)
|
|
var/roundstart_move //id of port to send shuttle to at roundstart
|
|
var/travelDir = 0 //direction the shuttle would travel in
|
|
var/rebuildable = 0 //can build new shuttle consoles for this one
|
|
|
|
var/obj/docking_port/stationary/destination
|
|
var/obj/docking_port/stationary/previous
|
|
|
|
/obj/docking_port/mobile/New()
|
|
..()
|
|
|
|
var/area/A = get_area(src)
|
|
if(istype(A, /area/shuttle))
|
|
areaInstance = A
|
|
|
|
if(!areaInstance)
|
|
areaInstance = new()
|
|
areaInstance.name = name
|
|
areaInstance.contents += return_ordered_turfs()
|
|
|
|
#ifdef DOCKING_PORT_HIGHLIGHT
|
|
highlight("#0f0")
|
|
#endif
|
|
|
|
/obj/docking_port/mobile/Initialize()
|
|
if(!timid)
|
|
register()
|
|
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, areaInstance))
|
|
shuttle_areas[cur_area] = TRUE
|
|
..()
|
|
|
|
/obj/docking_port/mobile/register()
|
|
if(!SSshuttle)
|
|
throw EXCEPTION("docking port [src] could not initialize.")
|
|
return 0
|
|
|
|
SSshuttle.mobile += src
|
|
|
|
if(!id)
|
|
id = "[SSshuttle.mobile.len]"
|
|
if(name == "shuttle")
|
|
name = "shuttle[SSshuttle.mobile.len]"
|
|
|
|
return 1
|
|
|
|
/obj/docking_port/mobile/Destroy(force)
|
|
if(force)
|
|
SSshuttle.mobile -= src
|
|
areaInstance = null
|
|
destination = null
|
|
previous = null
|
|
shuttle_areas = null
|
|
return ..()
|
|
|
|
//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 0 //0 means we can move
|
|
|
|
//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
|
|
//check dock is big enough to contain us
|
|
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 = "check_dock(): shuttle [src] cannot dock at [S], error: [status]"
|
|
message_admins(msg)
|
|
throw EXCEPTION(msg)
|
|
return FALSE
|
|
|
|
|
|
//call the shuttle to destination S
|
|
/obj/docking_port/mobile/proc/request(obj/docking_port/stationary/S)
|
|
|
|
if(!check_dock(S))
|
|
return
|
|
|
|
switch(mode)
|
|
if(SHUTTLE_CALL)
|
|
if(S == destination)
|
|
if(world.time <= timer)
|
|
timer = world.time
|
|
else
|
|
destination = S
|
|
timer = world.time
|
|
if(SHUTTLE_RECALL)
|
|
if(S == destination)
|
|
timer = world.time - timeLeft(1)
|
|
else
|
|
destination = S
|
|
timer = world.time
|
|
mode = SHUTTLE_CALL
|
|
else
|
|
destination = S
|
|
mode = SHUTTLE_CALL
|
|
timer = world.time
|
|
enterTransit() //hyperspace
|
|
|
|
//recall the shuttle to where it was previously
|
|
/obj/docking_port/mobile/proc/cancel()
|
|
if(mode != SHUTTLE_CALL)
|
|
return
|
|
|
|
timer = world.time - timeLeft(1)
|
|
mode = SHUTTLE_RECALL
|
|
|
|
/obj/docking_port/mobile/proc/enterTransit()
|
|
previous = null
|
|
// if(!destination)
|
|
// return
|
|
var/obj/docking_port/stationary/S0 = get_docked()
|
|
var/obj/docking_port/stationary/S1 = findTransitDock()
|
|
if(S1)
|
|
if(dock(S1))
|
|
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/S0 = get_docked()
|
|
var/turf_type = /turf/space
|
|
var/area_type = /area/space
|
|
if(S0)
|
|
if(S0.turf_type)
|
|
turf_type = S0.turf_type
|
|
if(S0.area_type)
|
|
area_type = S0.area_type
|
|
|
|
var/list/L0 = return_ordered_turfs(x, y, z, dir, areaInstance)
|
|
|
|
//remove area surrounding docking port
|
|
if(areaInstance.contents.len)
|
|
var/area/A0 = locate("[area_type]")
|
|
if(!A0)
|
|
A0 = new area_type(null)
|
|
for(var/turf/T0 in L0)
|
|
A0.contents += T0
|
|
|
|
for(var/i in L0)
|
|
var/turf/T0 = i
|
|
if(!T0)
|
|
continue
|
|
T0.empty(turf_type)
|
|
|
|
qdel(src, force=TRUE)
|
|
|
|
/obj/docking_port/mobile/proc/create_ripples(obj/docking_port/stationary/S1)
|
|
var/list/turfs = ripple_area(S1)
|
|
for(var/i in turfs)
|
|
ripples += new /obj/effect/temp_visual/ripple(i)
|
|
|
|
/obj/docking_port/mobile/proc/remove_ripples()
|
|
if(ripples.len)
|
|
for(var/i in ripples)
|
|
qdel(i)
|
|
ripples.Cut()
|
|
|
|
|
|
/obj/docking_port/mobile/proc/ripple_area(obj/docking_port/stationary/S1)
|
|
var/list/L0 = return_ordered_turfs(x, y, z, dir, areaInstance)
|
|
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)
|
|
continue
|
|
var/turf/T1 = L1[i]
|
|
if(!T1)
|
|
continue
|
|
if(T0.type != T0.baseturf)
|
|
ripple_turfs += T1
|
|
|
|
return ripple_turfs
|
|
|
|
//this is the main proc. It instantly moves our mobile port to stationary port S1
|
|
//it handles all the generic behaviour, such as sanity checks, closing doors on the shuttle, stunning mobs, etc
|
|
/obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/S1, force=FALSE)
|
|
// Crashing this ship with NO SURVIVORS
|
|
if(S1.get_docked() == src)
|
|
remove_ripples()
|
|
return
|
|
|
|
if(!force)
|
|
if(!check_dock(S1))
|
|
return -1
|
|
|
|
if(canMove())
|
|
return -1
|
|
|
|
|
|
// //rotate transit docking ports, so we don't need zillions of variants
|
|
// if(istype(S1, /obj/docking_port/stationary/transit))
|
|
// S1.dir = turn(NORTH, -travelDir)
|
|
|
|
var/obj/docking_port/stationary/S0 = get_docked()
|
|
var/turf_type = /turf/space
|
|
var/area_type = /area/space
|
|
if(S0)
|
|
if(S0.turf_type)
|
|
turf_type = S0.turf_type
|
|
if(S0.area_type)
|
|
area_type = S0.area_type
|
|
|
|
//close and lock the dock's airlocks
|
|
closePortDoors(S0)
|
|
|
|
var/list/L0 = return_ordered_turfs(x, y, z, dir, areaInstance)
|
|
var/list/L1 = return_ordered_turfs(S1.x, S1.y, S1.z, S1.dir)
|
|
|
|
var/rotation = dir2angle(S1.dir)-dir2angle(dir)
|
|
if((rotation % 90) != 0)
|
|
rotation += (rotation % 90) //diagonal rotations not allowed, round up
|
|
rotation = SimplifyDegrees(rotation)
|
|
|
|
|
|
|
|
//remove area surrounding docking port
|
|
if(areaInstance.contents.len)
|
|
var/area/A0 = locate("[area_type]")
|
|
if(!A0)
|
|
A0 = new area_type(null)
|
|
for(var/turf/T0 in L0)
|
|
A0.contents += T0
|
|
|
|
// Removes ripples
|
|
remove_ripples()
|
|
|
|
//move or squish anything in the way ship at destination
|
|
roadkill(L0, L1, S1.dir)
|
|
|
|
for(var/i in 1 to L0.len)
|
|
var/turf/T0 = L0[i]
|
|
if(!T0)
|
|
continue
|
|
var/turf/T1 = L1[i]
|
|
if(!T1)
|
|
continue
|
|
|
|
T0.copyTurf(T1)
|
|
areaInstance.contents += T1
|
|
|
|
//copy over air
|
|
if(istype(T1, /turf/simulated))
|
|
var/turf/simulated/Ts1 = T1
|
|
Ts1.copy_air_with_tile(T0)
|
|
|
|
//move mobile to new location
|
|
for(var/atom/movable/AM in T0)
|
|
AM.onShuttleMove(T1, rotation)
|
|
|
|
if(rotation)
|
|
T1.shuttleRotate(rotation)
|
|
|
|
//lighting stuff
|
|
SSair.remove_from_active(T1)
|
|
T1.CalculateAdjacentTurfs()
|
|
SSair.add_to_active(T1,1)
|
|
|
|
T0.ChangeTurf(turf_type)
|
|
|
|
SSair.remove_from_active(T0)
|
|
T0.CalculateAdjacentTurfs()
|
|
SSair.add_to_active(T0,1)
|
|
|
|
for(var/A1 in L1)
|
|
var/turf/T1 = A1
|
|
T1.postDock(S1)
|
|
for(var/atom/movable/M in T1)
|
|
M.postDock(S1)
|
|
|
|
loc = S1.loc
|
|
dir = S1.dir
|
|
|
|
unlockPortDoors(S1)
|
|
|
|
|
|
/obj/docking_port/mobile/proc/findTransitDock()
|
|
var/obj/docking_port/stationary/transit/T = SSshuttle.getDock("[id]_transit")
|
|
if(T && check_dock(T))
|
|
return T
|
|
|
|
|
|
/obj/docking_port/mobile/proc/findRoundstartDock()
|
|
var/obj/docking_port/stationary/D
|
|
D = SSshuttle.getDock(roundstart_move)
|
|
|
|
if(D)
|
|
return D
|
|
|
|
/obj/docking_port/mobile/proc/dockRoundstart()
|
|
// Instead of spending a lot of time trying to work out where to place
|
|
// our shuttle, just create it somewhere empty and send it to where
|
|
// it should go
|
|
. = 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"
|
|
|
|
|
|
|
|
//shuttle-door closing is handled in the dock() proc whilst looping through turfs
|
|
//this one closes the door where we are docked at, if there is one there.
|
|
/obj/docking_port/mobile/proc/closePortDoors(obj/docking_port/stationary/S0)
|
|
if(!istype(S0))
|
|
return 1
|
|
|
|
for(var/obj/machinery/door/airlock/A in GLOB.airlocks)
|
|
if(A.id_tag == S0.id)
|
|
spawn(-1)
|
|
A.close()
|
|
A.lock()
|
|
|
|
/obj/docking_port/mobile/proc/unlockPortDoors(obj/docking_port/stationary/S1)
|
|
if(!istype(S1))
|
|
return 0
|
|
|
|
for(var/obj/machinery/door/airlock/A in GLOB.airlocks)
|
|
if(A.id_tag == S1.id)
|
|
spawn(-1)
|
|
if(A.locked)
|
|
A.unlock()
|
|
|
|
/obj/docking_port/mobile/proc/roadkill(list/L0, list/L1, dir)
|
|
var/list/hurt_mobs = list()
|
|
for(var/i in 1 to L0.len)
|
|
var/turf/T0 = L0[i]
|
|
var/turf/T1 = L1[i]
|
|
if(!T0 || !T1)
|
|
continue
|
|
|
|
for(var/atom/movable/AM in T1)
|
|
if(AM.pulledby)
|
|
AM.pulledby.stop_pulling()
|
|
if(ismob(AM))
|
|
var/mob/M = AM
|
|
if(M.buckled)
|
|
M.buckled.unbuckle_mob(M, 1)
|
|
if(isliving(AM))
|
|
var/mob/living/L = AM
|
|
L.stop_pulling()
|
|
if(L.anchored)
|
|
L.gib()
|
|
else
|
|
if(!(L in hurt_mobs))
|
|
hurt_mobs |= L
|
|
L.visible_message("<span class='warning'>[L] is hit by \
|
|
a hyperspace ripple[L.anchored ? "":" and is thrown clear"]!</span>",
|
|
"<span class='userdanger'>You feel an immense \
|
|
crushing pressure as the space around you ripples.</span>")
|
|
L.Paralyse(10)
|
|
L.ex_act(2)
|
|
|
|
// Move unanchored atoms
|
|
if(!AM.anchored)
|
|
step(AM, dir)
|
|
else
|
|
if(AM.simulated) // Don't qdel lighting overlays, they are static
|
|
qdel(AM)
|
|
|
|
//used by shuttle subsystem to check timers
|
|
/obj/docking_port/mobile/proc/check()
|
|
check_effects()
|
|
|
|
var/timeLeft = timeLeft(1)
|
|
|
|
if(timeLeft <= 0)
|
|
switch(mode)
|
|
if(SHUTTLE_CALL)
|
|
if(dock(destination))
|
|
setTimer(20) //can't dock for some reason, try again in 2 seconds
|
|
return
|
|
if(SHUTTLE_RECALL)
|
|
if(dock(previous))
|
|
setTimer(20) //can't dock for some reason, try again in 2 seconds
|
|
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)
|
|
|
|
/obj/docking_port/mobile/proc/setTimer(wait)
|
|
if(timer <= 0)
|
|
timer = world.time
|
|
timer += wait - timeLeft(1)
|
|
|
|
//returns timeLeft
|
|
/obj/docking_port/mobile/proc/timeLeft(divisor)
|
|
if(divisor <= 0)
|
|
divisor = 10
|
|
if(!timer)
|
|
return round(callTime/divisor, 1)
|
|
return max( round((timer+callTime-world.time)/divisor,1), 0 )
|
|
|
|
// returns 3-letter mode string, used by status screens and mob status panel
|
|
/obj/docking_port/mobile/proc/getModeStr()
|
|
switch(mode)
|
|
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)]mins)"
|
|
|
|
/obj/machinery/computer/shuttle
|
|
name = "Shuttle Console"
|
|
icon_screen = "shuttle"
|
|
icon_keyboard = "tech_key"
|
|
req_access = list( )
|
|
circuit = /obj/item/circuitboard/shuttle
|
|
var/shuttleId
|
|
var/possible_destinations = ""
|
|
var/admin_controlled
|
|
var/max_connect_range = 7
|
|
var/docking_request = 0
|
|
|
|
/obj/machinery/computer/shuttle/New(location, obj/item/circuitboard/shuttle/C)
|
|
..()
|
|
if(istype(C))
|
|
possible_destinations = C.possible_destinations
|
|
shuttleId = C.shuttleId
|
|
|
|
connect()
|
|
|
|
/obj/machinery/computer/shuttle/proc/connect()
|
|
var/obj/docking_port/mobile/M
|
|
if(!shuttleId)
|
|
// find close shuttle that is ok to mess with
|
|
if(!SSshuttle) //intentionally mapping shuttle consoles without actual shuttles IS POSSIBLE OH MY GOD WHO KNEW *glare*
|
|
return
|
|
for(var/obj/docking_port/mobile/D in SSshuttle.mobile)
|
|
if(get_dist(src, D) <= max_connect_range && D.rebuildable)
|
|
M = D
|
|
shuttleId = M.id
|
|
break
|
|
else if(!possible_destinations && SSshuttle) //possible destinations should **not** always exist; so, if it's specifically set to null, don't make it exist
|
|
M = SSshuttle.getShuttle(shuttleId)
|
|
|
|
if(M && !possible_destinations)
|
|
// find perfect fits
|
|
possible_destinations = ""
|
|
for(var/obj/docking_port/stationary/S in SSshuttle.stationary)
|
|
if(!istype(S, /obj/docking_port/stationary/transit) && S.width == M.width && S.height == M.height && S.dwidth == M.dwidth && S.dheight == M.dheight && findtext(S.id, M.id))
|
|
possible_destinations += "[possible_destinations ? ";" : ""][S.id]"
|
|
|
|
/obj/machinery/computer/shuttle/attack_hand(mob/user)
|
|
if(..(user))
|
|
return
|
|
if(!shuttleId)
|
|
return
|
|
connect()
|
|
add_fingerprint(user)
|
|
ui_interact(user)
|
|
|
|
/obj/machinery/computer/shuttle/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
|
var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId)
|
|
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
|
if(!ui)
|
|
ui = new(user, src, ui_key, "shuttle_console.tmpl", M ? M.name : "shuttle", 300, 200)
|
|
ui.open()
|
|
|
|
/obj/machinery/computer/shuttle/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
|
|
var/data[0]
|
|
var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId)
|
|
data["status"] = M ? M.getStatusText() : null
|
|
if(M)
|
|
data["shuttle"] = 1
|
|
var/list/docking_ports = list()
|
|
data["docking_ports"] = docking_ports
|
|
var/list/options = params2list(possible_destinations)
|
|
for(var/obj/docking_port/stationary/S in SSshuttle.stationary)
|
|
if(!options.Find(S.id))
|
|
continue
|
|
if(!M.check_dock(S))
|
|
continue
|
|
docking_ports[++docking_ports.len] = list("name" = S.name, "id" = S.id)
|
|
data["docking_ports_len"] = docking_ports.len
|
|
data["admin_controlled"] = admin_controlled
|
|
data["docking_request"] = docking_request
|
|
|
|
return data
|
|
|
|
/obj/machinery/computer/shuttle/Topic(href, href_list)
|
|
if(..())
|
|
return 1
|
|
|
|
if(!allowed(usr))
|
|
to_chat(usr, "<span class='danger'>Access denied.</span>")
|
|
return
|
|
|
|
var/list/options = params2list(possible_destinations)
|
|
if(href_list["move"])
|
|
if(!options.Find(href_list["move"])) //I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING.
|
|
// Seriously, though, NEVER trust a Topic with something like this. Ever.
|
|
message_admins("move HREF ([src] attempted to move to: [href_list["move"]]) exploit attempted by [key_name_admin(usr)] on [src] (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
|
|
return
|
|
switch(SSshuttle.moveShuttle(shuttleId, href_list["move"], 1))
|
|
if(0)
|
|
to_chat(usr, "<span class='notice'>Shuttle received message and will be sent shortly.</span>")
|
|
if(1)
|
|
to_chat(usr, "<span class='warning'>Invalid shuttle requested.</span>")
|
|
else
|
|
to_chat(usr, "<span class='notice'>Unable to comply.</span>")
|
|
return 1
|
|
|
|
/obj/machinery/computer/shuttle/emag_act(mob/user)
|
|
if(!emagged)
|
|
src.req_access = list()
|
|
emagged = 1
|
|
to_chat(user, "<span class='notice'>You fried the consoles ID checking system.</span>")
|
|
|
|
/obj/machinery/computer/shuttle/ferry
|
|
name = "transport ferry console"
|
|
circuit = /obj/item/circuitboard/ferry
|
|
shuttleId = "ferry"
|
|
possible_destinations = "ferry_home;ferry_away"
|
|
|
|
|
|
/obj/machinery/computer/shuttle/ferry/request
|
|
name = "ferry console"
|
|
circuit = /obj/item/circuitboard/ferry/request
|
|
var/cooldown //prevents spamming admins
|
|
possible_destinations = "ferry_home"
|
|
admin_controlled = 1
|
|
|
|
/obj/machinery/computer/shuttle/ferry/request/Topic(href, href_list)
|
|
if(..())
|
|
return 1
|
|
if(href_list["request"])
|
|
if(cooldown)
|
|
return
|
|
cooldown = 1
|
|
to_chat(usr, "<span class='notice'>Your request has been recieved by Centcom.</span>")
|
|
log_admin("[key_name(usr)] requested to move the transport ferry to Centcom.")
|
|
message_admins("<b>FERRY: <font color='blue'>[key_name_admin(usr)] (<A HREF='?_src_=holder;secretsfun=moveferry'>Move Ferry</a>)</b> is requesting to move the transport ferry to Centcom.</font>")
|
|
. = 1
|
|
SSnanoui.update_uis(src)
|
|
spawn(600) //One minute cooldown
|
|
cooldown = 0
|
|
|
|
/obj/machinery/computer/shuttle/white_ship
|
|
name = "White Ship Console"
|
|
desc = "Used to control the White Ship."
|
|
circuit = /obj/item/circuitboard/white_ship
|
|
shuttleId = "whiteship"
|
|
possible_destinations = "whiteship_away;whiteship_home;whiteship_z4"
|
|
|
|
/obj/machinery/computer/shuttle/golem_ship
|
|
name = "Golem Ship Console"
|
|
desc = "Used to control the Golem Ship."
|
|
circuit = /obj/item/circuitboard/golem_ship
|
|
shuttleId = "freegolem"
|
|
possible_destinations = "freegolem_z3;freegolem_z5;freegolem_z1;freegolem_z6"
|
|
resistance_flags = INDESTRUCTIBLE
|
|
|
|
/obj/machinery/computer/shuttle/golem_ship/attack_hand(mob/user)
|
|
if(!isgolem(user))
|
|
to_chat(user, "<span class='notice'>The console is unresponsive. Seems only golems can use it.</span>")
|
|
return
|
|
..()
|
|
|
|
/obj/machinery/computer/shuttle/engineering
|
|
name = "Engineering Shuttle Console"
|
|
desc = "Used to call and send the engineering shuttle."
|
|
shuttleId = "engineering"
|
|
possible_destinations = "engineering_home;engineering_away"
|
|
|
|
/obj/machinery/computer/shuttle/science
|
|
name = "Science Shuttle Console"
|
|
desc = "Used to call and send the science shuttle."
|
|
shuttleId = "science"
|
|
possible_destinations = "science_home;science_away"
|
|
|
|
/obj/machinery/computer/shuttle/admin
|
|
name = "Administration Shuttle Console"
|
|
desc = "Used to call and send the administration shuttle."
|
|
shuttleId = "admin"
|
|
possible_destinations = "admin_home;admin_away"
|
|
resistance_flags = INDESTRUCTIBLE
|
|
|
|
/obj/machinery/computer/shuttle/trade
|
|
name = "Freighter Console"
|
|
resistance_flags = INDESTRUCTIBLE
|
|
|
|
/obj/machinery/computer/shuttle/trade/sol
|
|
req_access = list(access_trade_sol)
|
|
possible_destinations = "trade_sol_base;trade_dock"
|
|
shuttleId = "trade_sol"
|
|
|
|
//#undef DOCKING_PORT_HIGHLIGHT
|
|
|
|
/turf/proc/copyTurf(turf/T)
|
|
if(T.type != type)
|
|
var/obj/O
|
|
if(underlays.len) //we have underlays, which implies some sort of transparency, so we want to a snapshot of the previous turf as an underlay
|
|
O = new()
|
|
O.underlays.Add(T)
|
|
T.ChangeTurf(type)
|
|
if(underlays.len)
|
|
T.underlays = O.underlays
|
|
if(T.icon_state != icon_state)
|
|
T.icon_state = icon_state
|
|
if(T.icon != icon)
|
|
T.icon = icon
|
|
if(T.color != color)
|
|
T.color = color
|
|
if(T.dir != dir)
|
|
T.dir = dir
|
|
return T
|