mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-12 16:37:19 +01:00
441ef5805d
These were some of the first given feedback for skills, so I may as well implement it. Piloting is now split into three levels like other skills which note what pilot class of vessel you may comfortably pilot. (Familiar) **Shuttle Pilot** is for all landables: Spark, Canary, Intrepid, the Quark, and the like are all equal under it. (Trained) **Class II Pilot** covers most ships, which are "overmap-only" and unable to land or dock. (Professional) **Class IV Pilot** covers only the Horizon normally, but design-wise extends to whatever is the main player ship, or for anything massive **Antags get Professional Pilot, so they can always do ship hijack gimmicks without worry.** As a convenience to make Bridge personnel stand out, Professional/Class IV Pilots can deduce their current overmap coordinate from examining space on help intent for 3s. There's no way to directly link to the overmap, so on the backend, this ability briefly references an existing ship console. ### Design Class II and IV are pre-existing terms in-lore, used on the Interstellar Travel page to refer to civilian size classes. With how important each level of piloting is, they cost _+4 points_ each instead of going 2 -> 4 -> 8. This lets there exist the occasional pilot character from any background, but it's _always_ an investment and piloting the Horizon stays hard without training. As Familiar currently costs 6 points, this is effectively a 2-point buff for most cases as you'll rarely have to pilot non-shuttles. (**EDIT**: Piloting levels are now 4 -> 8 -> 10 so there's a bit of flexibility to have an extra skill for Professional Pilots from Expeditionary Trade) #### The _**real meat**_ of this PR... is that you can now attempt to pilot vessels a level higher than you with penalties, so there's now a failure spectrum should you manage to access an adjacent-class ship rather than always being hard-locked. The penalties affect acceleration, slowing, turning, and rolling. They're most extreme for Shuttle and Class IV to represent how clueless an unlicensed pilot would be and how complex the Horizon realistically is to operate. With smart usage of autopilot, you can actually find a way to travel normal-ish. I think this is a fun interaction to have for underskilled characters, and makes autopilot actually have a use as it otherwise kills you. Maybe Bridge personnel can give lessons to Class II pilots with it. ### Piloting Education Backgrounds Coming are THREE piloting education backgrounds: Flight Academy, Fleet Training, and Expeditionary Trade. #### ALL GET _Familiar Electrical Engineering,_ _Familiar Atmospheric_ (EDIT: Flight Academy is now the only background that gets Familiar Mechanical Engineering, this makes them stand out as being notably handy/utility-ready for emergencies, but other backgrounds can still easily match them with points. Fleet Training has Tenacity down to Trained for character variety, though tenacity is very slight buffs and hard to notice. Expeditionary Trade now uses Familiar Firearms over Familiar Armed Combat as it's more immediately important.) Electrical Engineering now lets you use a power cell on an unpowered ship console to give it emergency battery life based on the charge. This helps with cases of being stranded from an APC broken by a meteor, a high-capacity cell can give 10m or more based on Electrical level. I may expand this bypass mechanic to all consoles in the future. Atmospherics skill has no implementation currently, but dealing with breaches or cycling on shuttles is too important. Flight Academy - BC, XO, Captain *Professional Piloting, Trained Leadership, Familiar Mechanical Engineering* > "You are at least 25 years of age, with the best navigation training the Spur can offer, vessel handiness in a pinch, and the know-how for reining in or assuring passengers for the voyage." Flight Academy has Leadership honestly just because I didn't want Fleet Training to look too good, and because I think they can rock the "this is your pilot speaking" in a cool way. Bridge personnel are usually the ones that have to wraggle people on away sites, especially during events. Mechanical Engineering allows them to make machine frames of ship consoles in emergencies. Fleet Training - BC, XO, Captain *Professional Piloting, Trained Firearms, Trained Tenacity* > "You are at least 25 years of age, with the most extensive navigation training possible and military discipline to back it up. You're a steady shot in emergencies and, damn it-- You. Go. Down. With. The. Ship." This was made because a player informed me many BC and XOs choose military backgrounds, and the +4 points design would prevent that. Fleet Training isn't the same as Military Training, but having all of Piloting gives you 6 points free to match MT if wanted. The high Tenacity is primarily for surviving in ship combat, where you'll probably bleed/suffocate to death. Expeditionary Trade - Miners, Xenology roles, OM, RD *Familiar Piloting, Familiar Firearms, Familiar Medicine* > "You've developed trade skills for a career of expeditions. You are at home in a shuttle piloting to and from for an average day's work. You can even hit a shot or tend to wounds when things eventually get unsafe." This is for Miner and Xenoarch primarily. An expedition is just any journey with an objective, all roles revolving around piloting shuttles to an objective needs to be able to hold their own if they ever find carp or have an accident, especially since they'll likely be alone. #### MISC stuff corrected shaper gear comments I left wrong Hotwiring piloting consoles now checks for Familiar in Electrical Engineering. Its visible emote also now looks identical to attempting an emergency bypass at first glance, so you can sneakily attempt one with an unpowered console. The Doctor of Psychology education background now gives Trained in Leadership. This was intended to be uncommented, but the person forgot to do it. Research Directors now need to have Familiar/Shuttle Pilot. According to the Guide to Piloting wikipage, they're supposed to be able to fly the Intrepid and Quark for expeditions, and likely mirroring how the OM can fly the Spark, but an RD can spawn completely incapable of piloting currently. --------- Signed-off-by: runecap <43975590+runecap@users.noreply.github.com> Co-authored-by: VMSolidus <evilexecutive@gmail.com>
314 lines
14 KiB
Plaintext
314 lines
14 KiB
Plaintext
/*
|
|
While these computers can be placed anywhere, they will only function if placed on either a non-space, non-shuttle turf
|
|
with an /obj/effect/overmap/visitable/ship present elsewhere on that z level, or else placed in a shuttle area with an /obj/effect/overmap/visitable/ship
|
|
somewhere on that shuttle. Subtypes of these can be then used to perform ship overmap movement functions.
|
|
*/
|
|
/obj/structure/machinery/computer/ship
|
|
/// Weakrefs to mobs in direct-view mode.
|
|
var/list/viewers
|
|
/// How much the view is increased by when the mob is in overmap mode.
|
|
var/extra_view = 0
|
|
/// The ship we're attached to. This is a typecheck for linked, to ensure we're linked to a ship and not a sector
|
|
var/obj/effect/overmap/visitable/ship/connected
|
|
/// Are we targeting anything right now?
|
|
var/targeting = FALSE
|
|
var/linked_type = /obj/effect/overmap/visitable/ship
|
|
|
|
/// For hotwiring, how many cycles are needed. This decreases by 1 each cycle and triggers at 0
|
|
var/hotwire_progress = 8
|
|
// Time window for emergency power from Pilot: Spacecraft + Electrical Engineering skills.
|
|
var/emergency_power_window
|
|
|
|
/obj/structure/machinery/computer/antagonist_hints(mob/user, distance, is_adjacent)
|
|
. += ..()
|
|
. += "Consoles like these are typically access-locked."
|
|
. += "You can remove this lock with <b>wirecutters</b>, but it would take awhile! Alternatively, you can also use a cryptographic sequencer (emag) for instant removal."
|
|
. += "Hotwiring with wirecutters looks identical to attempting an emergency power bypass at first glance. You can avoid early suspicion doing it with equipment power out."
|
|
|
|
/obj/structure/machinery/computer/mechanics_hints(mob/user, distance, is_adjacent)
|
|
. += ..()
|
|
if(user.GetComponent(ELECTRICAL_ENGINEERING_SKILL_COMPONENT)?.skill_level >= SKILL_LEVEL_FAMILIAR)
|
|
. += "Ship consoles like these can be directly given batterylife in emergencies. Simply use a power cell on it when unpowered to attempt a bypass with some of the charge." \
|
|
+ " Batterylife efficiency scales with Electrical Engineering and bypass completion is faster when Familiar with Piloting Spacecraft."
|
|
|
|
/obj/structure/machinery/computer/ship/attackby(obj/item/attacking_item, mob/user)
|
|
var/electrical_level = user.GetComponent(ELECTRICAL_ENGINEERING_SKILL_COMPONENT)?.skill_level
|
|
|
|
if(electrical_level > SKILL_LEVEL_UNFAMILIAR && (emergency_power_window || stat & NOPOWER) && istype(attacking_item, /obj/item/cell))
|
|
var/obj/item/cell/battery = attacking_item
|
|
var/pilot_level = user.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level
|
|
user.visible_message("<b>[user]<b> opens a panel underneath \the [src]...", SPAN_NOTICE("You start searching for the power port to attempt an emergency power bypass..."))
|
|
emergency_power_window = world.time
|
|
if(pilot_level <= SKILL_LEVEL_UNFAMILIAR && Adjacent(user) && user.get_active_hand() == battery)
|
|
if(do_after(user, 3 SECONDS))
|
|
to_chat(user, SPAN_NOTICE("Although unfamiliar with spacecraft to find it fast, your electrical intuition knows there's a port somewhere..."))
|
|
else
|
|
user.visible_message("<b>[user]</b> closes the panel underneath \the [src].", SPAN_WARNING("You need to stay and look longer if you want to perform a bypass."))
|
|
return
|
|
while(battery.percent() > 0 && Adjacent(user) && user.get_active_hand() == battery)
|
|
if(do_after(user, 3 SECONDS))
|
|
if((pilot_level <= SKILL_LEVEL_UNFAMILIAR && do_after(user, 2 SECONDS)) || pilot_level >= SKILL_LEVEL_FAMILIAR)
|
|
var/bypass_value = battery.maxcharge/10
|
|
emergency_power_window += bypass_value/((100 - pilot_level * 3)/electrical_level) SECONDS // should be 10m for high-caps
|
|
playsound(src.loc, 'sound/machines/click.ogg', 30)
|
|
battery.charge -= bypass_value
|
|
user.visible_message("<b>[user]</b> presses into some port with a click.<BR><i>\The [src]'s power bypass timer notifies there are <b>[round((emergency_power_window - world.time)/10)]</b> seconds of emergency power remaining.</i>", SPAN_NOTICE("You press into the port with \the [battery] transferring 10% into \the [src] and leaving \the [battery] with [battery.percent()]%<BR>\The [src]'s power bypass timer notifies there are <b>[round((emergency_power_window - world.time)/10)]</b> seconds of emergency power remaining."))
|
|
stat &= ~NOPOWER
|
|
update_icon()
|
|
return
|
|
if(attacking_item.tool_behaviour == TOOL_CABLECOIL && electrical_level > SKILL_LEVEL_UNFAMILIAR) // Repair from hotwire
|
|
var/obj/item/stack/cable_coil/C = attacking_item
|
|
if(hotwire_progress >= initial(hotwire_progress))
|
|
to_chat(usr, SPAN_BOLD("\The [src] does not require repairs."))
|
|
else
|
|
to_chat(usr, SPAN_BOLD("You attempt to replace some cabling for \the [src]..."))
|
|
while(C.can_use(2, user))
|
|
if(do_after(user, 15 SECONDS, src, DO_UNIQUE))
|
|
if(hotwire_progress < initial(hotwire_progress))
|
|
C.use(2)
|
|
hotwire_progress++
|
|
if(hotwire_progress >= initial(hotwire_progress))
|
|
restore_access(user)
|
|
return
|
|
to_chat(usr, SPAN_BOLD("You replace some broken cabling of \the [src] <b>([(hotwire_progress / initial(hotwire_progress)) * 100]%)</b>."))
|
|
playsound(src.loc, 'sound/items/Deconstruct.ogg', 30, TRUE)
|
|
return
|
|
|
|
if(attacking_item.tool_behaviour == TOOL_WIRECUTTER && electrical_level > SKILL_LEVEL_UNFAMILIAR) // Hotwiring
|
|
if(!req_access && !req_one_access && !emagged) // Already hacked/no need to hack
|
|
to_chat(user, SPAN_BOLD("[src] is not access-locked."))
|
|
return
|
|
// Begin hotwire
|
|
user.visible_message("<b>[user]</b> opens a panel underneath \the [src]...", SPAN_BOLD("You open the maintenance panel and attempt to hotwire \the [src]..."))
|
|
while(hotwire_progress > 0)
|
|
if(do_after(user, 15 SECONDS, src, DO_UNIQUE))
|
|
hotwire_progress--
|
|
if(hotwire_progress <= 0)
|
|
emag_act(user=user, hotwired=TRUE)
|
|
return
|
|
to_chat(user, SPAN_BOLD("You snip some cabling from \the [src] <b>([((initial(hotwire_progress)-hotwire_progress) / initial(hotwire_progress)) * 100]%)</b>."))
|
|
playsound(src.loc, 'sound/items/Wirecutter.ogg', 30, TRUE)
|
|
else
|
|
return
|
|
return ..()
|
|
|
|
/obj/structure/machinery/computer/ship/attack_hand(mob/user)
|
|
var/piloting_difference = user.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level - connected.pilot_class
|
|
var/pilot_level = user.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level
|
|
if(stat & (NOPOWER|BROKEN))
|
|
return FALSE
|
|
if(emergency_power_window && world.time >= emergency_power_window)
|
|
emergency_power_window = null
|
|
if(!powered())
|
|
stat |= NOPOWER
|
|
visible_message(SPAN_DANGER("\The [src] flickers, its power bypass depleted."))
|
|
else
|
|
visible_message(SPAN_NOTICE("\The [src] hums as it transitions from emergency power to main."))
|
|
update_icon()
|
|
// Snowflake case for checking player characters for a Pilot Spacecraft Skill.
|
|
// Only player characters will have the component. Which will both always be present on them, and will only enable its own return logic if it exists.
|
|
// NPCs, Ghostroles, and Offship Antags that don't generate skills are unaffected by this check by intentional design so that we don't have to account for them.
|
|
if (!connected.pilot_class && pilot_level <= SKILL_LEVEL_UNFAMILIAR)
|
|
// No pilot_class means it's probably a station, so only Unfamiliar is checked for
|
|
to_chat(user, SPAN_WARNING("There's just so many buttons... You have no idea where to even begin."))
|
|
return FALSE
|
|
// A lack of a difference means skill level (1-4) is less than pilot_class (1-3)
|
|
if(piloting_difference <= 0)
|
|
if(connected.pilot_class == PILOTING_CLASS_SHUTTLE)
|
|
user.visible_message("<b>[user]</b> starts indecisively messing with \the [src].", SPAN_WARNING("There's just so many buttons... You have no idea where to even begin, but..."))
|
|
if(do_after(user, 10 SECONDS) && Adjacent(user))
|
|
user.visible_message("<b>[user]</b> peers at \the [src].", SPAN_NOTICE("...maybe, if you take it slow? It <i>is</i> only a shuttle."))
|
|
if(do_after(user, 3 SECONDS) && Adjacent(user))
|
|
if(prob(30))
|
|
to_chat(user, SPAN_WARNING("You lose track and can't figure out the controls."))
|
|
return
|
|
else
|
|
return
|
|
else
|
|
to_chat(user, SPAN_WARNING("You can't keep track of controls while moving."))
|
|
return
|
|
else
|
|
to_chat(user, SPAN_WARNING("This ship's class is notably more complex than you're used to, but you can still grasp the core controls."))
|
|
// Conversely, Non-Unfamiliar skill level (2-4) will always be higher than pilot class (1-3)
|
|
if(pilot_level < connected.pilot_class)
|
|
to_chat(user, SPAN_WARNING("There's just so many buttons... You have no idea where to even begin, this is far too complex for you."))
|
|
return
|
|
if(use_check_and_message(user))
|
|
return
|
|
if(!emagged && !allowed(user))
|
|
to_chat(user, SPAN_WARNING("Access denied."))
|
|
return FALSE
|
|
user.set_machine(src)
|
|
ui_interact(user)
|
|
|
|
/obj/structure/machinery/computer/ship/attack_ai(mob/user)
|
|
if(!ai_can_interact(user))
|
|
return
|
|
src.add_hiddenprint(user)
|
|
user.set_machine(src)
|
|
ui_interact(user)
|
|
|
|
/obj/structure/machinery/computer/ship/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = ..()
|
|
if(emergency_power_window)
|
|
var/power_window_rounded = round((emergency_power_window - world.time)/10)
|
|
. += SPAN_ITALIC("The emergency power bypass is enabled, the timer shows <b>[power_window_rounded > 0 ? power_window_rounded : "no"]</b> seconds remain.")
|
|
if(initial(hotwire_progress) != hotwire_progress)
|
|
if(hotwire_progress != 0)
|
|
. += SPAN_ITALIC("The bottom panel appears open with wires hanging out. It can be repaired with additional cabling. <i>Current progress: [(hotwire_progress / initial(hotwire_progress)) * 100]%</i>")
|
|
else
|
|
. += SPAN_ITALIC("The bottom panel appears open with wires hanging out. It can be repaired with additional cabling.")
|
|
|
|
/obj/structure/machinery/computer/ship/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/hotwired = FALSE)
|
|
if(emagged)
|
|
to_chat(user, SPAN_WARNING("\The [src] has already been subverted."))
|
|
return FALSE
|
|
emagged = TRUE
|
|
if(hotwired)
|
|
user.visible_message(SPAN_WARNING("\The [src] sparks as a panel suddenly opens and burnt cabling spills out!"),SPAN_BOLD("You short out the console's ID checking system. It's now available to everyone!"))
|
|
else
|
|
user.visible_message(SPAN_WARNING("\The [src] sparks!"),SPAN_BOLD("You short out the console's ID checking system. It's now available to everyone!"))
|
|
spark(src, 2, 0)
|
|
hotwire_progress = 0
|
|
return TRUE
|
|
|
|
/// Used to restore access removed from emag_act() by setting access from req_access_old and req_one_access_old
|
|
/obj/structure/machinery/computer/ship/proc/restore_access(var/mob/user)
|
|
if(!emagged)
|
|
to_chat(user, SPAN_WARNING("There is no access to restore for \the [src]!"))
|
|
return FALSE
|
|
emagged = FALSE
|
|
to_chat(user, "You repair out the console's ID checking system. It's access restrictions have been restored.")
|
|
playsound(loc, 'sound/machines/ping.ogg', 50, FALSE)
|
|
hotwire_progress = initial(hotwire_progress)
|
|
return TRUE
|
|
|
|
/obj/structure/machinery/computer/ship/Topic(href, href_list)
|
|
if(..())
|
|
return TOPIC_HANDLED
|
|
if(href_list["sync"])
|
|
sync_linked()
|
|
return TOPIC_REFRESH
|
|
if(href_list["close"])
|
|
unlook(usr)
|
|
usr.unset_machine()
|
|
return TOPIC_HANDLED
|
|
return TOPIC_NOACTION
|
|
|
|
/obj/structure/machinery/computer/ship/sync_linked()
|
|
. = ..()
|
|
if(istype(linked, linked_type))
|
|
connected = linked
|
|
|
|
// Management of mob view displacement. look to shift view to the ship on the overmap; unlook to shift back.
|
|
|
|
/obj/structure/machinery/computer/ship/proc/look(var/mob/user)
|
|
if(linked)
|
|
user.reset_view(linked)
|
|
if(user.client)
|
|
user.client.view = world.view + extra_view
|
|
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(unlook))
|
|
if(user.eyeobj)
|
|
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(unlook))
|
|
LAZYDISTINCTADD(viewers, WEAKREF(user))
|
|
if(linked)
|
|
LAZYDISTINCTADD(linked.navigation_viewers, WEAKREF(user))
|
|
ADD_TRAIT(user, TRAIT_COMPUTER_VIEW, REF(src))
|
|
|
|
/// Handles disabling the user's overmap view when a signal comes in, primarily used when the TGUI is closed, see helm.dm and sensors.dm
|
|
/obj/structure/machinery/computer/ship/proc/handle_unlook_signal(var/datum/source, var/mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
unlook(user)
|
|
|
|
/obj/structure/machinery/computer/ship/proc/unlook(var/mob/user)
|
|
user.reset_view()
|
|
var/client/c = user.client
|
|
|
|
if(isEye(user))
|
|
var/mob/abstract/eye/E = user
|
|
E.reset_view()
|
|
c = E.owner.client
|
|
|
|
if(c)
|
|
c.view = world.view
|
|
c.pixel_x = 0
|
|
c.pixel_y = 0
|
|
|
|
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
|
|
|
|
if(isEye(user)) // If we're an AI eye, the computer has our AI mob in its viewers list not the eye mob
|
|
var/mob/abstract/eye/E = user
|
|
UnregisterSignal(E.owner, COMSIG_MOVABLE_MOVED)
|
|
LAZYREMOVE(viewers, WEAKREF(E.owner))
|
|
LAZYREMOVE(viewers, WEAKREF(user))
|
|
if(linked)
|
|
LAZYREMOVE(linked.navigation_viewers, WEAKREF(user))
|
|
|
|
if(linked)
|
|
for(var/obj/structure/machinery/computer/ship/sensors/sensor in linked.consoles)
|
|
sensor.hide_contacts(user)
|
|
|
|
REMOVE_TRAIT(user, TRAIT_COMPUTER_VIEW, REF(src))
|
|
|
|
/obj/structure/machinery/computer/ship/proc/viewing_overmap(mob/user)
|
|
return (WEAKREF(user) in viewers) || (linked && (WEAKREF(user) in linked.navigation_viewers))
|
|
|
|
/obj/structure/machinery/computer/ship/CouldNotUseTopic(mob/user)
|
|
. = ..()
|
|
unlook(user)
|
|
|
|
/obj/structure/machinery/computer/ship/CouldUseTopic(mob/user)
|
|
. = ..()
|
|
if(viewing_overmap(user))
|
|
look(user)
|
|
|
|
/obj/structure/machinery/computer/ship/check_eye(var/mob/user)
|
|
if(!viewing_overmap(user))
|
|
return FALSE
|
|
|
|
var/flags = issilicon(user) ? USE_ALLOW_NON_ADJACENT : 0
|
|
if (use_check_and_message(user, flags) || user.blinded || !operable() || !linked)
|
|
return -1
|
|
else
|
|
return SEE_THRU
|
|
|
|
/obj/structure/machinery/computer/ship/Destroy()
|
|
if(linked)
|
|
linked = null
|
|
if(connected)
|
|
LAZYREMOVE(connected.consoles, src)
|
|
. = ..()
|
|
|
|
/obj/structure/machinery/computer/ship/sensors/Destroy()
|
|
sensor_ref = null
|
|
identification = null
|
|
QDEL_NULL(sound_token)
|
|
if(LAZYLEN(viewers))
|
|
for(var/datum/weakref/W in viewers)
|
|
var/M = W.resolve()
|
|
if(M)
|
|
unlook(M)
|
|
if(linked)
|
|
LAZYREMOVE(linked.navigation_viewers, W)
|
|
. = ..()
|
|
|
|
/obj/structure/machinery/computer/ship/on_user_login(mob/M)
|
|
unlook(M)
|
|
|
|
/obj/structure/machinery/computer/ship/attempt_hook_up(var/obj/effect/overmap/visitable/sector)
|
|
. = ..()
|
|
|
|
if(.)
|
|
if(istype(linked, linked_type))
|
|
connected = linked
|
|
if(istype(connected)) // we do a little type abuse
|
|
LAZYSET(connected.consoles, src, TRUE)
|
|
|
|
/obj/structure/machinery/computer/ship/Initialize()
|
|
. = ..()
|
|
if(SSatlas.current_map.use_overmap && !linked)
|
|
var/my_sector = GLOB.map_sectors["[z]"]
|
|
if(istype(my_sector, linked_type))
|
|
attempt_hook_up(my_sector)
|