mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-30 20:22:32 +00:00
* Adds Syndicate Infiltration Team
Goals:
- Provide a way to introduce a group of co-operating traitors, e.g: for
events.
- Provide a way to draw several ghosts into the round at once.
- Provide a way to enable additional objectives (e.g: kidnap) traitors
don't currently have a way to complete.
- Provide an evil counterpart to the Emergency Response Team.
Main:
- Creates a new "Send Infiltration Team" verb under Secrets -> IC for
admins. This verb creates a team of syndicate infiltrators, drawn from
ghosts.
- The infiltrators themselves are prespawned with stealth items
(including 20 TC uplink implants, dust implants, chameleon jumpsuit,
etc).
- The admin who invokes the SIT chooses their mission. It can be
anything.
- This team has their own shuttle, with 3 docking points on the
Cyberiad: arrivals maint, eng maint, and sci maint.
- Their shuttle has blast doors, and a small shuttle brig for abducting
Cyberiad crew.
Misc:
- Creates an 'emag_immune' attribute for doors (and pod doors). Applies
it to SST, SIT and nuke ops main doors. SST application prevents an
exploit. SIT/nukeops application prevents failzerg.
* check_rights, color codes
* Improved Options
- Added PDA painter to SIT spawn room, to prevent SIT members from
metagaming the SIT by identifying them from their PDA color.
- Added food, drinks, and laundry machine/crayons (to recolor gloves) to
the SIT spawn room.
- Added team size prompt for issuing admin, to allow them to send larger
or smaller teams.
- Removed storage implant from the list of things SIT team members spawn
with. In most cases, they will have plenty of TC to buy one if they want
it.
- Fixed action button sprite on dust implant. No longer displays as
blank.
- Added the number of TC each team member gets on spawn as an
admin-configurable option when using the verb. 20-30 is suggested.
- Updated memories ("Notes") given to each SIT member upon creation.
- Fixed a bug with SIT team size which was sometimes causing one less
person to spawn than was intended.
* Minor Improvements
- Made SIT members show up on antaghud, and to each other. Rather like
nuke ops.
- Made SIT members start out with a flashlight in their backpack, so
they can more easily find their way into the station from maint, without
bumbling around in the dark.
- Removed manhack grenade from SIT shuttle, replaced it with a welding
tool. Subtle hint to break the wall into the station.
- Added option for admins to spawn a Syndicate Officer to brief the team
before they go. The officer does not go with the team. They're rather
like ERT Directors briefing ERTs before they are sent out. Optional
fluff.
* Map Area Tweak
* Fixes potential SST exploit with c4-ing mech door
* Tiger
* World's smallest merge/Travis fix
* Renames Officer to Management Consultant
* Removes protected var blocking C4 on poddoors
* Removes emag_immune property for doors
855 lines
23 KiB
Plaintext
855 lines
23 KiB
Plaintext
//use this define to highlight docking port bounding boxes (ONLY FOR DEBUG USE)
|
|
//#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
|
|
|
|
//these objects are indestructable
|
|
/obj/docking_port/Destroy()
|
|
return QDEL_HINT_LETMELIVE
|
|
|
|
/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 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(!shuttle_master)
|
|
throw EXCEPTION("docking port [src] could not initialize.")
|
|
return 0
|
|
|
|
shuttle_master.stationary += src
|
|
if(!id)
|
|
id = "[shuttle_master.stationary.len]"
|
|
if(name == "dock")
|
|
name = "dock[shuttle_master.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.
|
|
|
|
shuttle_master.transit += src
|
|
return 1
|
|
|
|
/obj/docking_port/mobile
|
|
icon_state = "mobile"
|
|
name = "shuttle"
|
|
icon_state = "pinonclose"
|
|
|
|
var/area/shuttle/areaInstance
|
|
|
|
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/register()
|
|
if(!shuttle_master)
|
|
throw EXCEPTION("docking port [src] could not initialize.")
|
|
return 0
|
|
|
|
shuttle_master.mobile += src
|
|
|
|
if(!id)
|
|
id = "[shuttle_master.mobile.len]"
|
|
if(name == "shuttle")
|
|
name = "shuttle[shuttle_master.mobile.len]"
|
|
|
|
return 1
|
|
|
|
|
|
//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 1
|
|
if(istype(S, /obj/docking_port/stationary/transit))
|
|
return 0
|
|
//check dock is big enough to contain us
|
|
if(dwidth > S.dwidth)
|
|
return 2
|
|
if(width-dwidth > S.width-S.dwidth)
|
|
return 3
|
|
if(dheight > S.dheight)
|
|
return 4
|
|
if(height-dheight > S.height-S.dheight)
|
|
return 5
|
|
//check the dock isn't occupied
|
|
if(S.get_docked())
|
|
return 6
|
|
return 0 //0 means we can dock
|
|
|
|
//call the shuttle to destination S
|
|
/obj/docking_port/mobile/proc/request(obj/docking_port/stationary/S)
|
|
if(canDock(S))
|
|
. = 1
|
|
throw EXCEPTION("request(): shuttle cannot dock")
|
|
return 1 //we can't dock at S
|
|
|
|
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"]")
|
|
|
|
//default shuttleRotate
|
|
/atom/proc/shuttleRotate(rotation)
|
|
//rotate our direction
|
|
dir = angle2dir(rotation+dir2angle(dir))
|
|
|
|
//rotate the pixel offsets too.
|
|
if(pixel_x || pixel_y)
|
|
if(rotation < 0)
|
|
rotation += 360
|
|
for(var/turntimes=rotation/90;turntimes>0;turntimes--)
|
|
var/oldPX = pixel_x
|
|
var/oldPY = pixel_y
|
|
pixel_x = oldPY
|
|
pixel_y = (oldPX*(-1))
|
|
|
|
/atom/proc/postDock()
|
|
if(smooth)
|
|
smooth_icon(src)
|
|
|
|
|
|
//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)
|
|
. = canDock(S1)
|
|
if(.)
|
|
throw EXCEPTION("dock(): shuttle cannot dock")
|
|
return .
|
|
|
|
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
|
|
|
|
//move or squish anything in the way ship at destination
|
|
roadkill(L1, S1.dir)
|
|
|
|
var/list/door_unlock_list = 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
|
|
|
|
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)
|
|
if(rotation)
|
|
AM.shuttleRotate(rotation)
|
|
|
|
if(istype(AM,/obj))
|
|
var/obj/O = AM
|
|
if(istype(O, /obj/docking_port/stationary))
|
|
continue
|
|
O.forceMove(T1)
|
|
|
|
//close open doors
|
|
if(istype(O, /obj/machinery/door))
|
|
var/obj/machinery/door/Door = O
|
|
spawn(-1)
|
|
if(Door)
|
|
if(istype(Door, /obj/machinery/door/airlock))
|
|
var/obj/machinery/door/airlock/A = Door
|
|
A.close(0,1)
|
|
if(A.id_tag == "s_docking_airlock")
|
|
A.lock()
|
|
door_unlock_list += A
|
|
else
|
|
Door.close()
|
|
else if(istype(AM,/mob))
|
|
var/mob/M = AM
|
|
if(!M.move_on_shuttle)
|
|
continue
|
|
M.forceMove(T1)
|
|
|
|
//docking turbulence
|
|
if(M.client)
|
|
spawn(0)
|
|
if(M.buckled)
|
|
shake_camera(M, 2, 1) // turn it down a bit come on
|
|
else
|
|
shake_camera(M, 7, 1)
|
|
if(istype(M, /mob/living/carbon))
|
|
if(!M.buckled)
|
|
M.Weaken(3)
|
|
|
|
|
|
if(rotation)
|
|
T1.shuttleRotate(rotation)
|
|
|
|
//lighting stuff
|
|
air_master.remove_from_active(T1)
|
|
T1.CalculateAdjacentTurfs()
|
|
air_master.add_to_active(T1,1)
|
|
|
|
T0.ChangeTurf(turf_type)
|
|
|
|
air_master.remove_from_active(T0)
|
|
T0.CalculateAdjacentTurfs()
|
|
air_master.add_to_active(T0,1)
|
|
|
|
for(var/A1 in L1)
|
|
var/turf/T1 = A1
|
|
T1.postDock()
|
|
for(var/atom/movable/M in T1)
|
|
M.postDock()
|
|
|
|
loc = S1.loc
|
|
dir = S1.dir
|
|
|
|
unlockPortDoors(S1)
|
|
if(S1 && !S1.lock_shuttle_doors)
|
|
for(var/obj/machinery/door/airlock/A in door_unlock_list)
|
|
spawn(-1)
|
|
A.unlock()
|
|
|
|
/*
|
|
if(istype(S1, /obj/docking_port/stationary/transit))
|
|
var/d = turn(dir, 180 + travelDir)
|
|
for(var/turf/space/transit/T in S1.return_ordered_turfs())
|
|
T.pushdirection = d
|
|
T.update_icon()
|
|
*/
|
|
|
|
|
|
|
|
/obj/docking_port/mobile/proc/findTransitDock()
|
|
var/obj/docking_port/stationary/transit/T = shuttle_master.getDock("[id]_transit")
|
|
if(T && !canDock(T))
|
|
return T
|
|
/* commented out due to issues with rotation
|
|
for(var/obj/docking_port/stationary/transit/S in shuttle_master.transit)
|
|
if(S.id)
|
|
continue
|
|
if(!canDock(S))
|
|
return S
|
|
*/
|
|
|
|
|
|
//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 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 airlocks)
|
|
if(A.id_tag == S1.id)
|
|
spawn(-1)
|
|
if(A.locked)
|
|
A.unlock()
|
|
|
|
/obj/docking_port/mobile/proc/roadkill(list/L, dir, x, y)
|
|
for(var/turf/T in L)
|
|
for(var/atom/movable/AM in T)
|
|
if(ismob(AM))
|
|
if(istype(AM, /mob/living))
|
|
var/mob/living/M = AM
|
|
M.Paralyse(10)
|
|
M.take_organ_damage(80)
|
|
M.anchored = 0
|
|
else
|
|
continue
|
|
|
|
if(!AM.anchored)
|
|
step(AM, dir)
|
|
else
|
|
if(AM.simulated) //lighting overlays are static
|
|
qdel(AM)
|
|
/*
|
|
//used to check if atom/A is within the shuttle's bounding box
|
|
/obj/docking_port/mobile/proc/onShuttleCheck(atom/A)
|
|
var/turf/T = get_turf(A)
|
|
if(!T)
|
|
return 0
|
|
|
|
var/list/L = return_coords()
|
|
if(L[1] > L[3])
|
|
L.Swap(1,3)
|
|
if(L[2] > L[4])
|
|
L.Swap(2,4)
|
|
|
|
if(L[1] <= T.x && T.x <= L[3])
|
|
if(L[2] <= T.y && T.y <= L[4])
|
|
return 1
|
|
return 0
|
|
*/
|
|
//used by shuttle subsystem to check timers
|
|
/obj/docking_port/mobile/proc/check()
|
|
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/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/weapon/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/weapon/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(!shuttle_master) //intentionally mapping shuttle consoles without actual shuttles IS POSSIBLE OH MY GOD WHO KNEW *glare*
|
|
return
|
|
for(var/obj/docking_port/mobile/D in shuttle_master.mobile)
|
|
if(get_dist(src, D) <= max_connect_range && D.rebuildable)
|
|
M = D
|
|
shuttleId = M.id
|
|
break
|
|
else if(!possible_destinations && shuttle_master) //possible destinations should **not** always exist; so, if it's specifically set to null, don't make it exist
|
|
M = shuttle_master.getShuttle(shuttleId)
|
|
|
|
if(M && !possible_destinations)
|
|
// find perfect fits
|
|
possible_destinations = ""
|
|
for(var/obj/docking_port/stationary/S in shuttle_master.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
|
|
src.add_fingerprint(usr)
|
|
|
|
connect()
|
|
|
|
var/list/options = params2list(possible_destinations)
|
|
var/obj/docking_port/mobile/M = shuttle_master.getShuttle(shuttleId)
|
|
var/dat = "Status: [M ? M.getStatusText() : "*Missing*"]<br><br>"
|
|
if(M)
|
|
var/destination_found
|
|
for(var/obj/docking_port/stationary/S in shuttle_master.stationary)
|
|
if(!options.Find(S.id))
|
|
continue
|
|
if(M.canDock(S))
|
|
continue
|
|
destination_found = 1
|
|
dat += "<A href='?src=\ref[src];move=[S.id]'>Send to [S.name]</A><br>"
|
|
if(!destination_found)
|
|
dat += "<B>Shuttle Locked</B><br>"
|
|
if(admin_controlled)
|
|
dat += "Authorized personnel only<br>"
|
|
dat += "<A href='?src=\ref[src];request=1]'>Request Authorization</A><br>"
|
|
if(docking_request)
|
|
dat += "<A href='?src=\ref[src];request=1]'>Request docking at NSS Cyberiad</A><br>"
|
|
dat += "<a href='?src=\ref[user];mach_close=computer'>Close</a>"
|
|
|
|
var/datum/browser/popup = new(user, "computer", M ? M.name : "shuttle", 300, 200)
|
|
popup.set_content("<center>[dat]</center>")
|
|
popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
|
|
popup.open()
|
|
|
|
/obj/machinery/computer/shuttle/Topic(href, href_list)
|
|
if(..())
|
|
return 1
|
|
usr.set_machine(src)
|
|
src.add_fingerprint(usr)
|
|
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(shuttle_master.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>")
|
|
updateUsrDialog()
|
|
|
|
/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/weapon/circuitboard/ferry
|
|
shuttleId = "ferry"
|
|
possible_destinations = "ferry_home;ferry_away"
|
|
|
|
|
|
/obj/machinery/computer/shuttle/ferry/request
|
|
name = "ferry console"
|
|
circuit = /obj/item/weapon/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>")
|
|
spawn(600) //One minute cooldown
|
|
cooldown = 0
|
|
|
|
|
|
/obj/machinery/computer/shuttle/ert
|
|
name = "specops shuttle console"
|
|
//circuit = /obj/item/weapon/circuitboard/ert
|
|
shuttleId = "specops"
|
|
possible_destinations = "specops_home;specops_away"
|
|
|
|
|
|
/obj/machinery/computer/shuttle/white_ship
|
|
name = "White Ship Console"
|
|
desc = "Used to control the White Ship."
|
|
circuit = /obj/item/weapon/circuitboard/white_ship
|
|
shuttleId = "whiteship"
|
|
possible_destinations = "whiteship_away;whiteship_home;whiteship_z4"
|
|
|
|
/obj/machinery/computer/shuttle/vox
|
|
name = "skipjack control console"
|
|
req_access = list(access_vox)
|
|
shuttleId = "skipjack"
|
|
possible_destinations = "skipjack_away;skipjack_ne;skipjack_nw;skipjack_se;skipjack_sw;skipjack_z5"
|
|
|
|
/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"
|
|
|
|
/obj/machinery/computer/shuttle/sst
|
|
name = "Syndicate Strike Time Shuttle Console"
|
|
desc = "Used to call and send the SST shuttle."
|
|
shuttleId = "sst"
|
|
possible_destinations = "sst_home;sst_away"
|
|
|
|
/obj/machinery/computer/shuttle/sit
|
|
req_access = list(access_syndicate)
|
|
name = "Syndicate Infiltration Team Shuttle Console"
|
|
desc = "Used to call and send the SIT shuttle."
|
|
shuttleId = "sit"
|
|
possible_destinations = "sit_arrivals;sit_scimaint;sit_engshuttle;sit_away"
|
|
|
|
|
|
var/global/trade_dock_timelimit = 0
|
|
var/global/trade_dockrequest_timelimit = 0
|
|
|
|
/obj/machinery/computer/shuttle/trade
|
|
name = "Freighter Console"
|
|
docking_request = 1
|
|
var/possible_destinations_dock
|
|
var/possible_destinations_nodock
|
|
var/docking_request_message = "A trading ship has requested docking aboard the NSS Cyberiad for trading. This request can be accepted or denied using a communications console."
|
|
|
|
/obj/machinery/computer/shuttle/trade/attack_hand(mob/user)
|
|
if(world.time < trade_dock_timelimit)
|
|
possible_destinations = possible_destinations_dock
|
|
else
|
|
possible_destinations = possible_destinations_nodock
|
|
|
|
docking_request = (world.time > trade_dockrequest_timelimit && world.time > trade_dock_timelimit)
|
|
..(user)
|
|
|
|
/obj/machinery/computer/shuttle/trade/Topic(href, href_list)
|
|
if(..())
|
|
return 1
|
|
if(href_list["request"])
|
|
if(world.time < trade_dockrequest_timelimit || world.time < trade_dock_timelimit)
|
|
return
|
|
to_chat(usr, "<span class='notice'>Request sent.</span>")
|
|
command_announcement.Announce(docking_request_message, "Docking Request")
|
|
trade_dockrequest_timelimit = world.time + 1200 // They have 2 minutes to approve the request.
|
|
|
|
/obj/machinery/computer/shuttle/trade/sol
|
|
req_access = list(access_trade_sol)
|
|
possible_destinations_dock = "trade_sol_base;trade_sol_offstation;trade_dock"
|
|
possible_destinations_nodock = "trade_sol_base;trade_sol_offstation"
|
|
shuttleId = "trade_sol"
|
|
docking_request_message = "A trading ship of Sol origin has requested docking aboard the NSS Cyberiad for trading. This request can be accepted or denied using a communications console."
|
|
|
|
#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
|