#define NOT_BEGUN 0 #define STAGE_1 1 #define STAGE_2 2 #define STAGE_3 3 #define STAGE_4 4 #define HIJACKED 5 /obj/machinery/computer/emergency_shuttle name = "emergency shuttle console" desc = "For shuttle control." icon_screen = "shuttle" icon_keyboard = "tech_key" resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF var/auth_need = 3 var/list/authorized = list() var/hijack_last_stage_increase = 0 SECONDS var/hijack_stage_time = 5 SECONDS var/hijack_stage_cooldown = 5 SECONDS var/hijack_flight_time_increase = 30 SECONDS var/hijack_completion_flight_time_set = 10 SECONDS var/hijack_hacking = FALSE var/hijack_announce = TRUE /obj/machinery/computer/emergency_shuttle/examine(mob/user) . = ..() if(hijack_announce) . += "Security systems present on console. Any unauthorized tampering will result in an emergency announcement, and a fee of 20000 credits." if(user?.mind?.get_hijack_speed()) . += "Alt click on this to attempt to hijack the shuttle. This will take multiple tries (current: stage [SSshuttle.emergency.hijack_status]/[HIJACKED])." . += "It will take you [user.mind.get_hijack_speed() / 10] seconds to reprogram a stage of the shuttle's navigational firmware, and the console will undergo automated timed lockout for [hijack_stage_cooldown / 10] seconds after each stage." if(hijack_announce) . += "It is probably best to fortify your position as to be uninterrupted during the attempt, given the automatic announcements..." /obj/machinery/computer/emergency_shuttle/attackby(obj/item/card/W, mob/user, params) if(stat & (BROKEN|NOPOWER)) return if(!istype(W, /obj/item/card)) return if(SSshuttle.emergency.mode != SHUTTLE_DOCKED) return if(!user) return if(SSshuttle.emergency.timeLeft() < 11) return if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) if(istype(W, /obj/item/pda)) var/obj/item/pda/pda = W W = pda.id if(!W:access) //no access to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") return var/list/cardaccess = W:access if(!istype(cardaccess, /list) || !cardaccess.len) //no access to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") return if(!(ACCESS_HEADS in W:access)) //doesn't have this access to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") return 0 var/choice = alert(user, "Would you like to (un)authorize a shortened launch time? [auth_need - length(authorized)] authorization\s are still needed. Use abort to cancel all authorizations.", "Shuttle Launch", "Authorize", "Repeal", "Abort") if(SSshuttle.emergency.mode != SHUTTLE_DOCKED || user.get_active_hand() != W) return 0 var/seconds = SSshuttle.emergency.timeLeft() if(seconds <= 10) return 0 switch(choice) if("Authorize") if(!authorized.Find(W:registered_name)) authorized += W:registered_name if(auth_need - authorized.len > 0) message_admins("[key_name_admin(user)] has authorized early shuttle launch.") log_game("[key_name(user)] has authorized early shuttle launch in ([x], [y], [z]).") GLOB.minor_announcement.Announce("[auth_need - authorized.len] more authorization(s) needed until shuttle is launched early") else message_admins("[key_name_admin(user)] has launched the emergency shuttle [seconds] seconds before launch.") log_game("[key_name(user)] has launched the emergency shuttle in ([x], [y], [z]) [seconds] seconds before launch.") GLOB.minor_announcement.Announce("The emergency shuttle will launch in 10 seconds") SSshuttle.emergency.setTimer(100) if("Repeal") if(authorized.Remove(W:registered_name)) GLOB.minor_announcement.Announce("[auth_need - authorized.len] authorizations needed until shuttle is launched early") if("Abort") if(authorized.len) GLOB.minor_announcement.Announce("All authorizations to launch the shuttle early have been revoked.") authorized.Cut() /obj/machinery/computer/emergency_shuttle/emag_act(mob/user) if(!emagged && SSshuttle.emergency.mode == SHUTTLE_DOCKED) var/time = SSshuttle.emergency.timeLeft() message_admins("[key_name_admin(user)] has emagged the emergency shuttle: [time] seconds before launch.") log_game("[key_name(user)] has emagged the emergency shuttle in ([x], [y], [z]): [time] seconds before launch.") GLOB.minor_announcement.Announce("The emergency shuttle will launch in 10 seconds", "SYSTEM ERROR:") SSshuttle.emergency.setTimer(100) emagged = TRUE return TRUE /obj/machinery/computer/emergency_shuttle/proc/increase_hijack_stage() var/obj/docking_port/mobile/emergency/shuttle = SSshuttle.emergency shuttle.hijack_status++ if(hijack_announce) announce_hijack_stage() hijack_last_stage_increase = world.time atom_say("Navigational protocol error! Rebooting systems.") if(shuttle.mode == SHUTTLE_ESCAPE) if(shuttle.hijack_status == HIJACKED) shuttle.setTimer(hijack_completion_flight_time_set) else shuttle.setTimer(shuttle.timeLeft(1) + hijack_flight_time_increase) //give the guy more time to hijack if it's already in flight. return shuttle.hijack_status /obj/machinery/computer/emergency_shuttle/AltClick(user) if(isliving(user)) attempt_hijack_stage(user) /obj/machinery/computer/emergency_shuttle/proc/attempt_hijack_stage(mob/living/user) var/is_ai = isAI(user) if(!Adjacent(user) && !is_ai) return if(!ishuman(user) && !is_ai) //No, xenomorphs, constructs and traitors in cyborgs can not hack it. return if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) to_chat(user, "You need your hands free before you can manipulate [src].") return var/speed = user.mind?.get_hijack_speed() if(!speed) to_chat(user, "You manage to open a user-mode shell on [src], and hundreds of lines of debugging output fly through your vision. It is probably best to leave this alone.") return if(hijack_hacking) return if(SSshuttle.emergency.hijack_status >= HIJACKED) to_chat(user, "The emergency shuttle is already loaded with a corrupt navigational payload. What more do you want from it?") return if(hijack_last_stage_increase >= world.time - hijack_stage_cooldown) atom_say("ACCESS DENIED: Console is temporarily on security lockdown. Please try again.") return hijack_hacking = TRUE to_chat(user, "You [SSshuttle.emergency.hijack_status == NOT_BEGUN ? "begin" : "continue"] to override [src]'s navigational protocols.") atom_say("Software override initiated.") playsound(src, 'sound/machines/terminal_on.ogg', 100, FALSE) var/turf/console_hijack_turf = get_turf(src) message_admins("[src] is being overridden for hijack by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(console_hijack_turf)]") . = FALSE if(do_after(user, speed, target = src)) increase_hijack_stage() console_hijack_turf = get_turf(src) message_admins("[ADMIN_LOOKUPFLW(user)] has hijacked [src] in [ADMIN_VERBOSEJMP(console_hijack_turf)]. Hijack stage increased to stage [SSshuttle.emergency.hijack_status] out of [HIJACKED].") log_game("[key_name(usr)] has hijacked [src]. Hijack stage increased to stage [SSshuttle.emergency.hijack_status] out of [HIJACKED].") . = TRUE to_chat(user, "You fiddle with [src]'s programming and manage to get a foothold, looks like it'll take [hijack_stage_cooldown / 10] seconds before you can try again!") visible_message("[user.name] appears to be tampering with [src].") hijack_hacking = FALSE /obj/machinery/computer/emergency_shuttle/proc/announce_hijack_stage() var/msg switch(SSshuttle.emergency.hijack_status) if(NOT_BEGUN) return if(STAGE_1) if(ishuman(usr)) var/mob/living/carbon/human/H = usr msg = "AUTHENTICATING - FAIL. AUTHENTICATING - FAIL. AUTHENTICATING - FAI###### Welcome, technician: [random_name(pick(MALE, FEMALE), H.dna.species.name)]. Debug mode: Enabled." else msg = "AUTHENTICATING - FAIL. AUTHENTICATING - FAIL. AUTHENTICATING - FAI###### Welcome, technician: admin_[rand(0,25)]. Debug mode: Enabled." if(STAGE_2) msg = "Warning: Navigational route fails \"IS_AUTHORIZED\". Please try againNN[Gibberish("againagainerroragainagain", 70, 50)]." if(STAGE_3) msg = "CRC mismatch at ~h~ in calculated route buffer. Full reset initiated of FTL_NAVIGATION_SERVICES. Memory decrypted for automatic repair." if(STAGE_4) msg = "~ACS_directive module_load(cyberdyne.exploit.nanotrasen.shuttlenav)... NT key mismatch. Confirm load? Y...###Reboot complete. CALL transponder.disable(1) ON /transmitter IN world; System link initiated with connected engines..." if(HIJACKED) msg = "SYSTEM OVERRIDE - Resetting course to \[[Gibberish("###########", 100, 90)]\] \ ([Gibberish("#######", 100, 90)]/[Gibberish("#######", 100, 90)]/[Gibberish("#######", 100, 90)]) \ {AUTH - ROOT (uid: 0)}.
\ [SSshuttle.emergency.mode == SHUTTLE_ESCAPE ? "Diverting from existing route - Bluespace exit in
\ [hijack_completion_flight_time_set >= INFINITY ? "[Gibberish("\[ERROR\]", 50, 50)]" : hijack_completion_flight_time_set / 10] seconds." : ""]" announce_here("SYSTEM ERROR", Gibberish(msg, 70, rand(3,6))) /obj/machinery/computer/emergency_shuttle/proc/announce_here(a_header = "Emergency Shuttle", a_text = "") var/msg_text = "[a_header]
[a_text]
" for(var/mob/R in range(35, src)) //Normal escape shutttle is 30 tiles from console to bottom. Extra range for if we ever get a bigger shuttle. Would do in shuttle area, doesn't account for mechs and such, to_chat(R, msg_text) SEND_SOUND(R, sound('sound/misc/notice1.ogg')) /obj/docking_port/mobile/emergency name = "emergency shuttle" id = "emergency" dwidth = 9 width = 22 height = 11 dir = 4 travelDir = 0 var/sound_played = 0 //If the launch sound has been sent to all players on the shuttle itself var/canRecall = TRUE //no bad condom, do not recall the crew transfer shuttle! ///State of the emergency shuttles hijack status. var/hijack_status = NOT_BEGUN /obj/docking_port/mobile/emergency/register() if(!..()) return 0 //shuttle master not initialized SSshuttle.emergency = src return 1 /obj/docking_port/mobile/emergency/Destroy(force) if(force) // This'll make the shuttle subsystem use the backup shuttle. if(SSshuttle.emergency == src) // If we're the selected emergency shuttle SSshuttle.emergencyDeregister() return ..() /obj/docking_port/mobile/emergency/timeLeft(divisor) if(divisor <= 0) divisor = 10 if(!timer) return round(SSshuttle.emergencyCallTime/divisor, 1) var/dtime = world.time - timer switch(mode) if(SHUTTLE_ESCAPE) dtime = max(SSshuttle.emergencyEscapeTime - dtime, 0) if(SHUTTLE_DOCKED) dtime = max(SSshuttle.emergencyDockTime - dtime, 0) else dtime = max(SSshuttle.emergencyCallTime - dtime, 0) return round(dtime/divisor, 1) /obj/docking_port/mobile/emergency/request(obj/docking_port/stationary/S, coefficient=1, area/signalOrigin, reason, redAlert) SSshuttle.emergencyCallTime = initial(SSshuttle.emergencyCallTime) * coefficient switch(mode) if(SHUTTLE_RECALL) mode = SHUTTLE_CALL timer = world.time - timeLeft(1) if(SHUTTLE_IDLE) mode = SHUTTLE_CALL timer = world.time if(SHUTTLE_CALL) if(world.time < timer) //this is just failsafe timer = world.time else return if(prob(70)) SSshuttle.emergencyLastCallLoc = signalOrigin else SSshuttle.emergencyLastCallLoc = null if(canRecall) GLOB.major_announcement.Announce( "The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]", new_title = "Priority Announcement", new_sound = sound('sound/AI/eshuttle_call.ogg') ) else GLOB.major_announcement.Announce( "The crew transfer shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason]", new_title = "Priority Announcement", new_sound = sound('sound/AI/cshuttle.ogg') ) /obj/docking_port/mobile/emergency/cancel(area/signalOrigin) if(!canRecall) return if(mode != SHUTTLE_CALL) return timer = world.time - timeLeft(1) mode = SHUTTLE_RECALL if(prob(70)) SSshuttle.emergencyLastCallLoc = signalOrigin else SSshuttle.emergencyLastCallLoc = null GLOB.major_announcement.Announce( "The emergency shuttle has been recalled.[SSshuttle.emergencyLastCallLoc ? " Recall signal traced. Results can be viewed on any communications console." : "" ]", new_title = "Priority Announcement", new_sound = sound('sound/AI/eshuttle_recall.ogg') ) /obj/docking_port/mobile/emergency/proc/is_hijacked(fullcheck = FALSE) if(hijack_status == HIJACKED && !fullcheck) //Don't even bother if it was done via computer. return TRUE for(var/mob/living/player in GLOB.player_list) if(!player.mind) continue if(player.stat == DEAD) // Corpses continue if(issilicon(player)) //Borgs are technically dead anyways continue if(isanimal(player)) //Poly does not own the shuttle continue if(ishuman(player)) //hostages allowed on the shuttle, check for restraints/them being golems var/mob/living/carbon/human/H = player if(!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD) //new crit users who are in hard crit are considered dead continue if(H.handcuffed) //cuffs continue if(H.wear_suit && H.wear_suit.breakouttime) //straight jacket continue if(istype(H.loc, /obj/structure/closet)) //locked/welded locker, all aboard the clown train honk honk var/obj/structure/closet/C = H.loc if(C.welded || C.locked) continue if(isgolem(H)) // golems are often used in hijacks, so they really shouldn't all be forced to space themselves before the shuttle docks continue var/special_role = player.mind.special_role if(special_role) // There's a long list of special roles, but almost all of them are antags anyway. // If you manage to escape with a pet slaughter demon - go for it! Greentext well earned! if(special_role != SPECIAL_ROLE_EVENTMISC && special_role != SPECIAL_ROLE_ERT && special_role != SPECIAL_ROLE_DEATHSQUAD) continue if(get_area(player) == areaInstance) return FALSE return TRUE /obj/docking_port/mobile/emergency/check() if(!timer) return var/time_left = timeLeft(1) // The emergency shuttle doesn't work like others so this // ripple check is slightly different if(!ripples.len && (time_left <= SHUTTLE_RIPPLE_TIME) && ((mode == SHUTTLE_CALL) || (mode == SHUTTLE_ESCAPE))) var/destination if(mode == SHUTTLE_CALL) destination = SSshuttle.getDock("emergency_home") else if(mode == SHUTTLE_ESCAPE) destination = SSshuttle.getDock("emergency_away") create_ripples(destination) switch(mode) if(SHUTTLE_RECALL) if(time_left <= 0) mode = SHUTTLE_IDLE timer = 0 if(SHUTTLE_CALL) if(time_left <= 0) //move emergency shuttle to station if(dock(SSshuttle.getDock("emergency_home"))) setTimer(20) return mode = SHUTTLE_DOCKED timer = world.time if(canRecall) GLOB.major_announcement.Announce( "The emergency shuttle has docked with the station. You have [timeLeft(600)] minutes to board the emergency shuttle.", new_title = "Priority Announcement", new_sound = sound('sound/AI/eshuttle_dock.ogg') ) else GLOB.major_announcement.Announce( "The crew transfer shuttle has docked with the station. You have [timeLeft(600)] minutes to board the crew transfer shuttle.", new_title = "Priority Announcement", new_sound = sound('sound/AI/cshuttle_dock.ogg') ) /* //Gangs only have one attempt left if the shuttle has docked with the station to prevent suffering from dominator delays for(var/datum/gang/G in ticker.mode.gangs) if(isnum(G.dom_timer)) G.dom_attempts = 0 else G.dom_attempts = min(1,G.dom_attempts) */ if(SHUTTLE_DOCKED) if(time_left <= 0 && length(SSshuttle.hostile_environments)) GLOB.major_announcement.Announce( "Hostile environment detected. Departure has been postponed indefinitely pending conflict resolution.", new_title = "Priority Announcement" ) sound_played = 0 mode = SHUTTLE_STRANDED if(time_left <= 50 && !sound_played) //4 seconds left - should sync up with the launch sound_played = 1 var/hyperspace_sound = sound('sound/effects/hyperspace_begin.ogg') for(var/area/shuttle/escape/E in world) SEND_SOUND(E, hyperspace_sound) if(time_left <= 0 && !length(SSshuttle.hostile_environments)) //move each escape pod to its corresponding transit dock for(var/obj/docking_port/mobile/pod/M in SSshuttle.mobile) if(is_station_level(M.z)) //Will not launch from the mine/planet M.enterTransit() //now move the actual emergency shuttle to its transit dock var/hyperspace_progress_sound = sound('sound/effects/hyperspace_progress.ogg') for(var/area/shuttle/escape/E in world) SEND_SOUND(E, hyperspace_progress_sound) enterTransit() mode = SHUTTLE_ESCAPE timer = world.time GLOB.major_announcement.Announce( "The Emergency Shuttle has left the station. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.", new_title = "Priority Announcement" ) if(SHUTTLE_ESCAPE) if(time_left <= 0) //move each escape pod to its corresponding escape dock for(var/obj/docking_port/mobile/pod/M in SSshuttle.mobile) M.dock(SSshuttle.getDock("[M.id]_away")) var/hyperspace_end_sound = sound('sound/effects/hyperspace_end.ogg') for(var/area/shuttle/escape/E in world) SEND_SOUND(E, hyperspace_end_sound) // now move the actual emergency shuttle to centcomm // unless the shuttle is "hijacked" var/destination_dock = "emergency_away" if(is_hijacked()) destination_dock = "emergency_syndicate" GLOB.major_announcement.Announce( "Corruption detected in shuttle navigation protocols. Please contact your supervisor.", new_title = "Priority Announcement" ) dock_id(destination_dock) mode = SHUTTLE_ENDGAME timer = 0 open_dock() /obj/docking_port/mobile/emergency/proc/open_dock() pass() /* for(var/obj/machinery/door/poddoor/shuttledock/D in airlocks) var/turf/T = get_step(D, D.checkdir) if(!istype(T,/turf/space)) spawn(0) D.open() */ //Leaving this here incase someone decides to port -tg-'s escape shuttle stuff: // This basically opens a big-ass row of blast doors when the shuttle arrives at centcom /obj/docking_port/mobile/pod name = "escape pod" id = "pod" dwidth = 1 width = 3 height = 4 /obj/docking_port/mobile/pod/Initialize(mapload) . = ..() if(id == "pod") WARNING("[type] id has not been changed from the default. Use the id convention \"pod1\" \"pod2\" etc.") /obj/docking_port/mobile/pod/cancel() return /obj/docking_port/stationary/random name = "escape pod" id = "pod" dwidth = 1 width = 3 height = 4 var/target_area = /area/mine/unexplored /obj/docking_port/stationary/random/Initialize() ..() var/list/turfs = get_area_turfs(target_area) var/turf/T = pick(turfs) src.loc = T /obj/docking_port/mobile/emergency/backup name = "backup shuttle" id = "backup" dwidth = 2 width = 8 height = 8 dir = 4 /obj/docking_port/mobile/emergency/backup/register() var/current_emergency = SSshuttle.emergency ..() SSshuttle.emergency = current_emergency SSshuttle.backup_shuttle = src #undef NOT_BEGUN #undef STAGE_1 #undef STAGE_2 #undef STAGE_3 #undef STAGE_4 #undef HIJACKED