//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 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.id != "[id]_transit") 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 /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 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 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*"]

" 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 += "Send to [S.name]
" if(!destination_found) dat += "Shuttle Locked
" if(admin_controlled) dat += "Authorized personnel only
" dat += "Request Authorization
" dat += "Close" var/datum/browser/popup = new(user, "computer", M ? M.name : "shuttle", 300, 200) popup.set_content("
[dat]
") 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 usr.set_machine(src) src.add_fingerprint(usr) if(!allowed(usr)) to_chat(usr, "Access denied.") return if(href_list["move"]) switch(shuttle_master.moveShuttle(shuttleId, href_list["move"], 1)) if(0) to_chat(usr, "Shuttle received message and will be sent shortly.") if(1) to_chat(usr, "Invalid shuttle requested.") else to_chat(usr, "Unable to comply.") updateUsrDialog() /obj/machinery/computer/shuttle/emag_act(mob/user) if(!emagged) src.req_access = list() emagged = 1 to_chat(user, "You fried the consoles ID checking system.") /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(href_list["request"]) if(cooldown) return cooldown = 1 to_chat(usr, "Your request has been recieved by Centcom.") to_chat(admins, "FERRY: [key_name_admin(usr)] (?) (FLW) (Move Ferry) is requesting to move the transport ferry to Centcom.") 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" #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