diff --git a/code/__DEFINES/skills.dm b/code/__DEFINES/skills.dm index 989e7f178a9..95f2a7fa028 100644 --- a/code/__DEFINES/skills.dm +++ b/code/__DEFINES/skills.dm @@ -20,6 +20,10 @@ #define SKILL_SUBCATEGORY_SCIENCE "Science" #define SKILL_SUBCATEGORY_OPERATIONS "Operations" + #define PILOTING_CLASS_SHUTTLE 1 + #define PILOTING_CLASS_TWO 2 //Most ships + #define PILOTING_CLASS_MAX 3 //Only the SCCV Horizon normally + ///A combat skill is a skill that has a direct effect in combat. These have an increased cost. #define SKILL_CATEGORY_COMBAT "Combat" #define SKILL_SUBCATEGORY_RANGED "Ranged" diff --git a/code/datums/skills/occupational/engineering.dm b/code/datums/skills/occupational/engineering.dm index 5dca3d8b8d1..5b71002fa55 100644 --- a/code/datums/skills/occupational/engineering.dm +++ b/code/datums/skills/occupational/engineering.dm @@ -9,7 +9,8 @@ component_type = ELECTRICAL_ENGINEERING_SKILL_COMPONENT skill_level_descriptions = alist( SKILL_LEVEL_UNFAMILIAR = "You have little to no experience working with electrical systems.", - SKILL_LEVEL_FAMILIAR = "You have some experience working with electrical systes, though are not formally trained.", + SKILL_LEVEL_FAMILIAR = "You have some experience working with electrical systems, though are not formally trained.
" \ + + " - You can use a power cell to attempt emergency power bypasses on piloting consoles when they're unpowered.", SKILL_LEVEL_TRAINED = "You have formal training in electrical engineering concepts, equivalent to a Bachelor's Degree.", SKILL_LEVEL_PROFESSIONAL = "You have many years of training in electrical related Engineering concepts, equivalent to a Master's Degree or better.
" \ + " - You can tell what most machinery and airlock wires do just by looking at them." \ diff --git a/code/datums/skills/occupational/operations.dm b/code/datums/skills/occupational/operations.dm index 3132761fca3..41bf82e8073 100644 --- a/code/datums/skills/occupational/operations.dm +++ b/code/datums/skills/occupational/operations.dm @@ -51,23 +51,51 @@ /singleton/skill/pilot_spacecraft name = "Pilot: Spacecraft" description = "Governs the user's ability to pilot spacecraft of any size, and is required to do so in the first place." - maximum_level = SKILL_LEVEL_FAMILIAR - uneducated_skill_cap = SKILL_LEVEL_FAMILIAR + maximum_level = SKILL_LEVEL_PROFESSIONAL + uneducated_skill_cap = SKILL_LEVEL_TRAINED category = /singleton/skill_category/occupational subcategory = SKILL_SUBCATEGORY_OPERATIONS skill_level_map = list( "Unlicensed", - "Licensed Pilot" + "Shuttle Pilot", + "Class II Pilot", + "Class IV Pilot" ) skill_level_descriptions = alist( - SKILL_LEVEL_UNFAMILIAR = "You are incapable of piloting spacecraft.", - SKILL_LEVEL_FAMILIAR = "You are capable of piloting spacecraft." + SKILL_LEVEL_UNFAMILIAR = "You are inexperienced and mostly incapable of piloting spacecraft, but may attempt piloting shuttles in emergencies.
" \ + + "- There is only a 70% chance you will be able to view the consoles after several seconds waiting.
" \ + + "- Accelerations and Slows take 2 seconds to peform.
" \ + + " - Additional 65% chance to accelerate further.
" \ + + " - Additional 60% and 70% chances to slow further.
" \ + + "- Turns take 3 seconds to perform and have a 60% chance to continue in the given direction.
" \ + + "- Rolls have a 60% chance to move north/south, and a 70% chance afterwards to go further in it.", + SKILL_LEVEL_FAMILIAR = "You are capable of piloting shuttlecraft.
" \ + + "- You may attempt piloting ships a level higher with mild penalties:
" \ + + "- Accelerations and Slows take 1 second to perform.
" \ + + " - Additional 65% chance to accelerate further.
" \ + + " - Additional 60% chance to slow further.
" \ + + "- Turns take 1 second to perform.
" \ + + "- Rolls have a 60% chance to move north/south.", + SKILL_LEVEL_TRAINED = "You are capable of piloting up to Class II ships, which are often unable to land or dock.
" \ + + "- Class II includes all other ships movable on the overmap...with one major exception.
" \ + + "- You may attempt piloting a level higher with notable penalties similar to Unfamiliar:
" \ + + "- Accelerations and Slows take 2 seconds to perform.
" \ + + " - Additional 65% chance to accelerate more AND a 70% chance to accelerate even further.
" \ + + " - Additional 60% chance to slow further.
" \ + + "- Turns take 3 seconds to perform and a have a 60% chance to continue in the given direction, PLUS another 70% chance to continue again.
" \ + + "- Rolls have a 60% chance to move north/south, and a 70% chance afterwards to go further in it.", + SKILL_LEVEL_PROFESSIONAL = "You are capable of piloting up to Class IV ships, the highest, rarest, and most complex category, where the SCCV Horizon falls under.
" \ + + "- The SCCV Horizon is the only Class IV ship normally.
" \ + + "- Your expert navigational training lets you deduce the current overmap coordinate from examining space with help intent." ) skill_cost_map = alist( SKILL_LEVEL_UNFAMILIAR = 0, - SKILL_LEVEL_FAMILIAR = 6 + SKILL_LEVEL_FAMILIAR = 4, + SKILL_LEVEL_TRAINED = 8, + SKILL_LEVEL_PROFESSIONAL = 10 ) required = TRUE + antag_level = SKILL_LEVEL_PROFESSIONAL //So antags are always able to do Horizon/ship hijack gimmicks component_type = PILOT_SPACECRAFT_SKILL_COMPONENT /singleton/skill/pilot_mechs diff --git a/code/game/jobs/job/captain.dm b/code/game/jobs/job/captain.dm index cf78b9ab7f7..83ab0b1e739 100644 --- a/code/game/jobs/job/captain.dm +++ b/code/game/jobs/job/captain.dm @@ -26,7 +26,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca blacklisted_species = list(SPECIES_TAJARA, SPECIES_TAJARA_MSAI, SPECIES_TAJARA_ZHAN, SPECIES_UNATHI, SPECIES_DIONA, SPECIES_IPC, SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_ZENGHU, SPECIES_IPC_BISHOP, SPECIES_IPC_SHELL, SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR, SPECIES_VAURCA_ATTENDANT, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER, SPECIES_DIONA, SPECIES_DIONA_COEUS) skill_requirements = alist( - /singleton/skill/pilot_spacecraft = SKILL_LEVEL_FAMILIAR + /singleton/skill/pilot_spacecraft = SKILL_LEVEL_PROFESSIONAL ) /obj/outfit/job/captain @@ -116,7 +116,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca blacklisted_species = list(SPECIES_TAJARA_ZHAN, SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR, SPECIES_VAURCA_ATTENDANT, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER) skill_requirements = alist( - /singleton/skill/pilot_spacecraft = SKILL_LEVEL_FAMILIAR + /singleton/skill/pilot_spacecraft = SKILL_LEVEL_PROFESSIONAL ) /obj/outfit/job/xo @@ -174,7 +174,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca blacklisted_species = list(SPECIES_TAJARA_ZHAN, SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR, SPECIES_VAURCA_ATTENDANT, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER) skill_requirements = alist( - /singleton/skill/pilot_spacecraft = SKILL_LEVEL_FAMILIAR + /singleton/skill/pilot_spacecraft = SKILL_LEVEL_PROFESSIONAL ) /obj/outfit/job/bridge_crew diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index 38c8fea5475..c8017819508 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -28,6 +28,10 @@ blacklisted_species = list(SPECIES_TAJARA_MSAI, SPECIES_TAJARA_ZHAN, SPECIES_VAURCA_BREEDER, SPECIES_VAURCA_WORKER, SPECIES_VAURCA_WARRIOR, SPECIES_VAURCA_ATTENDANT, SPECIES_VAURCA_BULWARK) + skill_requirements = alist( + /singleton/skill/pilot_spacecraft = SKILL_LEVEL_FAMILIAR + ) + /obj/outfit/job/rd name = "Research Director" jobtype = /datum/job/rd diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index e229f4af991..57152d3679d 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -19,6 +19,17 @@ var/use_space_appearance = TRUE +/turf/space/feedback_hints(mob/user, distance, is_adjacent) + . = ..() + if(SSatlas.current_map.use_overmap && user.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level == SKILL_LEVEL_PROFESSIONAL && user.a_intent == I_HELP) + if(!SSatlas.current_sector.starlight_range) + to_chat(user, SPAN_WARNING("There's not a speck of starlight to work with.")) + return + to_chat(user, SPAN_NOTICE("You try deducing the angles and positioning of local stars...")) + if(do_after(user, 3 SECONDS)) + var/obj/effect/overmap/visitable/location = GLOB.map_sectors["[z]"] + . += SPAN_NOTICE("Through your sense of navigation, you realize you must be around Sector [location.x] - [location.y]") + /turf/space/dynamic //For use in edge cases where you want the turf to not be completely lit, like in places where you have placed lattice. //todomatt: this is useless now diff --git a/code/modules/background/education/medical.dm b/code/modules/background/education/medical.dm index 397a176e875..e81cb56f5ba 100644 --- a/code/modules/background/education/medical.dm +++ b/code/modules/background/education/medical.dm @@ -71,9 +71,7 @@ SPECIES_SKRELL_AXIORI = 60 ) skills = list( - /* Uncomment this block after finishing the Leadership skill. Psychologists should be able to give people morale bonuses as a mechanic. /singleton/skill/leadership = SKILL_LEVEL_TRAINED, - */ /singleton/skill/pharmacology = SKILL_LEVEL_FAMILIAR, /singleton/skill/medicine = SKILL_LEVEL_FAMILIAR, /singleton/skill/anatomy = SKILL_LEVEL_FAMILIAR, diff --git a/code/modules/background/education/misc.dm b/code/modules/background/education/misc.dm index e6a9c81eaba..95642d10e20 100644 --- a/code/modules/background/education/misc.dm +++ b/code/modules/background/education/misc.dm @@ -25,3 +25,56 @@ SPECIES_SKRELL = 55, SPECIES_SKRELL_AXIORI = 55 ) + +/singleton/education/flight_academy + name = "Flight Academy" + description = "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." + jobs = list("Bridge Crew", "Executive Officer", "Captain") + minimum_character_age = list( + SPECIES_HUMAN = 25, + SPECIES_SKRELL = 60, + SPECIES_SKRELL_AXIORI = 60 + ) + skills = list( + /singleton/skill/pilot_spacecraft = SKILL_LEVEL_PROFESSIONAL, + /singleton/skill/electrical_engineering = SKILL_LEVEL_FAMILIAR, + /singleton/skill/atmospherics_systems = SKILL_LEVEL_FAMILIAR, + /singleton/skill/mechanical_engineering = SKILL_LEVEL_FAMILIAR, //To make a machine frame for ship consoles in emergencies + /singleton/skill/leadership = SKILL_LEVEL_TRAINED + ) + +/singleton/education/fleet_training + name = "Fleet Training" + description = "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." + jobs = list("Bridge Crew", "Executive Officer", "Captain") + minimum_character_age = list( + SPECIES_HUMAN = 25, + SPECIES_SKRELL = 60, + SPECIES_SKRELL_AXIORI = 60 + ) + skills = list( + /singleton/skill/pilot_spacecraft = SKILL_LEVEL_PROFESSIONAL, + /singleton/skill/electrical_engineering = SKILL_LEVEL_FAMILIAR, + /singleton/skill/atmospherics_systems = SKILL_LEVEL_FAMILIAR, + /singleton/skill/firearms = SKILL_LEVEL_TRAINED, + /singleton/skill/tenacity = SKILL_LEVEL_TRAINED + ) + +/singleton/education/expeditionary_trade + name = "Expeditionary Trade" + description = "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." + jobs = list("Shaft Miner", "Xenoarchaeologist", "Xenobiologist", "Xenobotanist", "Operations Manager", "Research Director") + minimum_character_age = list( + SPECIES_HUMAN = 18, + SPECIES_SKRELL = 60, + SPECIES_SKRELL_AXIORI = 60 + ) + skills = list( + /singleton/skill/pilot_spacecraft = SKILL_LEVEL_FAMILIAR, + /singleton/skill/electrical_engineering = SKILL_LEVEL_FAMILIAR, + /singleton/skill/atmospherics_systems = SKILL_LEVEL_FAMILIAR, + /singleton/skill/firearms = SKILL_LEVEL_FAMILIAR, + /singleton/skill/medicine = SKILL_LEVEL_FAMILIAR + ) diff --git a/code/modules/mob/language/station.dm b/code/modules/mob/language/station.dm index 4a25ac54bfb..c14eae7f262 100644 --- a/code/modules/mob/language/station.dm +++ b/code/modules/mob/language/station.dm @@ -234,11 +234,12 @@ /datum/language/bug/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) log_say("[key_name(speaker)] : ([name]) [message]") + if(is_lemurian_sea_sector()) to_chat(speaker, SPAN_WARNING("You attempt to reach the Hivenet, but find nothing!")) return - var/mob/living/carbon/human/H = speaker //Check for Preimminent Shaper robes, which obscure Hive affiliation + var/mob/living/carbon/human/H = speaker //Check for Preimminent Shaper helmet, which obscure Hive affiliation var/obj/item/clothing/head/shaper/helmet = H.get_equipped_item(slot_head) if(!speaker_mask) speaker_mask = speaker.real_name diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 2f8429fa2f7..3bdc8eafb4a 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -483,7 +483,7 @@ return get_id_name("Unknown") if( head && (head.flags_inv&HIDEFACE) ) return get_id_name("Unknown") //Likewise for hats - if(istype(wear_suit, /obj/item/clothing/suit/vaurca/shaper)) //Check for Preimminent Shaper helmet which obscures Hive affiliation + if(istype(wear_suit, /obj/item/clothing/suit/vaurca/shaper)) //Check for Preimminent robes which obscures Hive affiliation var/list/hiveless_name = splittext(real_name, " ") //then remove Hive surname, ignore ID for obvious reason. return hiveless_name[1] var/face_name = get_face_name() diff --git a/code/modules/overmap/ships/computers/helm.dm b/code/modules/overmap/ships/computers/helm.dm index 3d75449f8ff..a2ee0f60ff1 100644 --- a/code/modules/overmap/ships/computers/helm.dm +++ b/code/modules/overmap/ships/computers/helm.dm @@ -254,6 +254,8 @@ var/ndir = text2num(params["roll"]) if(ishuman(usr)) var/mob/living/carbon/human/H = usr + var/piloting_difference = H.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level - connected.pilot_class + var/dir_to_move = turn(connected.dir, ndir == WEST ? 90 : -90) var/turf/new_turf = get_step(connected, dir_to_move) if(new_turf.x > SSatlas.current_map.overmap_size || new_turf.y > SSatlas.current_map.overmap_size) @@ -262,6 +264,13 @@ if(do_after(H, 1 SECOND) && connected.can_combat_roll()) visible_message(SPAN_DANGER("[H] tilts the yoke all the way to the [ndir == WEST ? "left" : "right"]!")) connected.combat_roll(ndir) + if(piloting_difference <= 0 && prob(60)) //A lack of difference means skill level (1-4) is less than pilot_class (1-3) + ndir = pick(NORTH, SOUTH) + connected.forceMove(get_step(connected, ndir)) + if(connected.pilot_class != PILOTING_CLASS_TWO && prob(70)) + connected.forceMove(get_step(connected, ndir)) + to_chat(H, SPAN_WARNING("You don't need to be an expert to realize you fumbled that.")) + H.visible_message(SPAN_DANGER("[H]'s grip slips!"), SPAN_DANGER("Your handling slips and the vessel teeters off trajectory!")) if(issilicon(usr)) var/mob/living/silicon/H = usr var/dir_to_move = turn(connected.dir, ndir == WEST ? 90 : -90) @@ -288,7 +297,24 @@ if(isliving(usr))// AI and robots are allowed to pilot now! if (action == "move") - if(prob(usr.confused * 5)) + var/mob/living/H = usr + var/piloting_difference = H.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level - connected.pilot_class + + if(piloting_difference <= 0) + to_chat(H, SPAN_NOTICE("You begin burning up the vessel's speed...")) + if((connected.pilot_class != PILOTING_CLASS_TWO && do_after(H, 2 SECONDS)) || (connected.pilot_class == PILOTING_CLASS_TWO && do_after(H, 1 SECOND))) + connected.relaymove(H, connected.dir, accellimit) + if(prob(65)) //Can't ignore the 1s burn_delay, so manually do the proc to adjust speed instead + var/acceleration = min(connected.get_burn_acceleration(), accellimit) + var/theta = dir2degree(connected.dir) + if(connected.pilot_class == PILOTING_CLASS_MAX && prob(70)) + acceleration *= 2 + to_chat(H, SPAN_DANGER("Too fast!")) + connected.adjust_speed(acceleration * cos(theta), acceleration * sin(theta)) + H.visible_message(SPAN_WARNING("[H] motions strongly at \the [src]"), SPAN_WARNING("The speed picks up faster than anticipated.")) + else + return + if(prob(H.confused * 5)) params["turn"] = pick("45", "-45") else connected.relaymove(usr, connected.dir, accellimit) @@ -296,7 +322,35 @@ if (action == "turn") var/ndir = text2num(params["turn"]) - if(connected.can_turn()) + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + var/piloting_difference = H.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level - connected.pilot_class + + if(connected.can_turn() && piloting_difference <= 0) + to_chat(H, SPAN_NOTICE("You feel you can work a turn [ndir == WEST ? "left" : "right"] here...")) + if((connected.pilot_class != PILOTING_CLASS_TWO && do_after(H, 3 SECONDS)) || (connected.pilot_class == PILOTING_CLASS_TWO && do_after(H, 1 SECOND))) + connected.turn_ship(ndir) + if(prob(60)) + connected.turn_ship(ndir) + if(connected.pilot_class == PILOTING_CLASS_MAX && prob(70)) + connected.turn_ship(ndir) + to_chat(H, SPAN_DANGER("Too far!")) + H.visible_message(SPAN_WARNING("[H] swerves inaccurately on \the [src]"), SPAN_WARNING("You're imprecise and make a wider turn.")) + addtimer(CALLBACK(src, PROC_REF(refresh_ui)), min(connected.vessel_mass / 10, 1) SECONDS + 1) + + else // When moving/interrupted mid-action, always get worse result + connected.turn_ship(ndir) + connected.turn_ship(ndir) + if(prob(70)) + connected.turn_ship(ndir) + to_chat(H, SPAN_DANGER("Damn it!")) + H.visible_message(SPAN_WARNING("[H] swerves loosely on \the [src]."), SPAN_WARNING("Your negligence overshoots the turning.")) + addtimer(CALLBACK(src, PROC_REF(refresh_ui)), min(connected.vessel_mass / 10, 1) SECONDS + 1) + + else if(connected.can_turn()) // Normal, w/o penalty + connected.turn_ship(ndir) + addtimer(CALLBACK(src, PROC_REF(refresh_ui)), min(connected.vessel_mass / 10, 1) SECONDS + 1) + else if(connected.can_turn()) // For AI and robots connected.turn_ship(ndir) addtimer(CALLBACK(src, PROC_REF(refresh_ui)), min(connected.vessel_mass / 10, 1) SECONDS + 1) @@ -314,8 +368,32 @@ connected.combat_turn(ndir) if (action == "brake") - connected.decelerate() - addtimer(CALLBACK(src, PROC_REF(refresh_ui)), connected.burn_delay + 1) + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + var/piloting_difference = H.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level - connected.pilot_class + + to_chat(H, SPAN_NOTICE("You begin clamping down the vessel's speed...")) + if(piloting_difference <= 0) + if((connected.pilot_class != PILOTING_CLASS_TWO && do_after(H, 2 SECONDS)) || (connected.pilot_class == PILOTING_CLASS_TWO && do_after(H, 1 SECOND))) + connected.decelerate() + if(prob(60)) // Can't ignore the 1s burn_delay, so manually do decelerate()'s effects instead + var/magnitude_velocity = ((connected.speed[1] ** 2) + (connected.speed[2] **2)) ** (1/2) + var/alpha = min(connected.get_burn_acceleration(), magnitude_velocity) + var/delta_x = -(connected.speed[1] / magnitude_velocity) * alpha + var/delta_y = -(connected.speed[2] / magnitude_velocity) * alpha + if(connected.pilot_class != PILOTING_CLASS_TWO && prob(70)) + delta_x *= 2 + delta_y *= 2 + connected.adjust_speed(delta_x, delta_y) + to_chat(H, SPAN_WARNING("That clamp was stronger than intended.")) + else + return + else // Normal, w/o penalty + connected.decelerate() + addtimer(CALLBACK(src, PROC_REF(refresh_ui)), connected.burn_delay + 1) + else + connected.decelerate() + addtimer(CALLBACK(src, PROC_REF(refresh_ui)), connected.burn_delay + 1) if (action == "apilot") autopilot = !autopilot diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm index 229470d1ada..e9e1d24b8b4 100644 --- a/code/modules/overmap/ships/computers/ship.dm +++ b/code/modules/overmap/ships/computers/ship.dm @@ -16,14 +16,47 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov /// 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 wirecutters, 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) - if(attacking_item.tool_behaviour == TOOL_CABLECOIL) // Repair from hotwire + 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("[user] 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("[user] 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("[user] presses into some port with a click.
\The [src]'s power bypass timer notifies there are [round((emergency_power_window - world.time)/10)] seconds of emergency power remaining.", SPAN_NOTICE("You press into the port with \the [battery] transferring 10% into \the [src] and leaving \the [battery] with [battery.percent()]%
\The [src]'s power bypass timer notifies there are [round((emergency_power_window - world.time)/10)] 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.")) @@ -41,12 +74,12 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov playsound(src.loc, 'sound/items/Deconstruct.ogg', 30, TRUE) return - if(attacking_item.tool_behaviour == TOOL_WIRECUTTER) // Hotwiring + 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("[user] opens a panel underneath \the [src] and starts snipping wires...", SPAN_BOLD("You open the maintenance panel and attempt to hotwire \the [src]...")) + user.visible_message("[user] 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-- @@ -60,15 +93,46 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov 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 (user.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level == SKILL_LEVEL_UNFAMILIAR) + 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("[user] 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("[user] peers at \the [src].", SPAN_NOTICE("...maybe, if you take it slow? It is 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)) @@ -86,6 +150,9 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov /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 [power_window_rounded > 0 ? power_window_rounded : "no"] 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. Current progress: [(hotwire_progress / initial(hotwire_progress)) * 100]%") diff --git a/code/modules/overmap/ships/computers/shuttle.dm b/code/modules/overmap/ships/computers/shuttle.dm index 8bf4ea33d47..ce91dda4b6b 100644 --- a/code/modules/overmap/ships/computers/shuttle.dm +++ b/code/modules/overmap/ships/computers/shuttle.dm @@ -3,6 +3,33 @@ name = "general shuttle control console" ui_template = "ShuttleControlConsoleMultiExplore" var/obj/effect/overmap/visitable/ship/connected //Ship we're connected to + var/emergency_power_window //Time window for emergency power from Pilot: Spacecraft + Electrical Engineering skills + +/obj/structure/machinery/computer/shuttle_control/explore/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("[user] 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("[user] 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("[user] presses into some port with a click.
\The [src]'s power bypass timer notifies there are [round((emergency_power_window - world.time)/10)] seconds of emergency power remaining.", SPAN_NOTICE("You press into the port with \the [battery] transferring 10% into \the [src] and leaving \the [battery] with [battery.percent()]%
\The [src]'s power bypass timer notifies there are [round((emergency_power_window - world.time)/10)] seconds of emergency power remaining.")) + stat &= ~NOPOWER + update_icon() + return /obj/structure/machinery/computer/shuttle_control/explore/Initialize() . = ..() diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm index aad4ea59e92..007e7c766d5 100644 --- a/code/modules/overmap/ships/landable.dm +++ b/code/modules/overmap/ships/landable.dm @@ -11,6 +11,7 @@ var/use_mapped_z_levels = FALSE //If you use this, use /obj/effect/shuttle_landmark/ship as the landmark (set the landmark_tag to match on the shuttle, no other setup needed) icon_state = "shuttle" moving_state = "shuttle_moving" + pilot_class = PILOTING_CLASS_SHUTTLE layer = OVERMAP_SHUTTLE_LAYER /obj/effect/overmap/visitable/ship/landable/Destroy() diff --git a/code/modules/overmap/ships/ship.dm b/code/modules/overmap/ships/ship.dm index 6ded372a649..25811ec7f20 100644 --- a/code/modules/overmap/ships/ship.dm +++ b/code/modules/overmap/ships/ship.dm @@ -22,6 +22,9 @@ generic_object = FALSE var/moving_state = "ship_moving" + /*For Pilot: Spacecraft skill. Denotes SHUTTLE for landables, CLASS_TWO for standard "overmap-only" ships, or CLASS_MAX for massive + ones which is just the Horizon/main player ship. When null, the skill only cares for being Unfamiliar; stations are null.*/ + var/pilot_class = PILOTING_CLASS_TWO layer = OVERMAP_SHIP_LAYER //RP fluff details to appear on scan readouts for mobile objects. @@ -81,6 +84,7 @@ obfuscated_name = "unidentified stationary object" unknown_id = "Unknown artificial structure" static_vessel = TRUE + pilot_class = null propulsion = "None equipped, flight incapable" halted = TRUE // Cannot fly under any circumstances diff --git a/html/changelogs/diggiez - PilotingSplit.yml b/html/changelogs/diggiez - PilotingSplit.yml new file mode 100644 index 00000000000..264e51b1160 --- /dev/null +++ b/html/changelogs/diggiez - PilotingSplit.yml @@ -0,0 +1,65 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# - (fixes bugs) +# wip +# - (work in progress) +# qol +# - (quality of life) +# soundadd +# - (adds a sound) +# sounddel +# - (removes a sound) +# rscadd +# - (adds a feature) +# rscdel +# - (removes a feature) +# imageadd +# - (adds an image or sprite) +# imagedel +# - (removes an image or sprite) +# spellcheck +# - (fixes spelling or grammar) +# experiment +# - (experimental change) +# balance +# - (balance changes) +# code_imp +# - (misc internal code change) +# refactor +# - (refactors code) +# config +# - (makes a change to the config files) +# admin +# - (makes changes to administrator tools) +# server +# - (miscellaneous changes to server) +################################# + +# Your name. +author: diggiez + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Pilot: Spacecraft is now split between tiers based on a ship's pilot class. Familiar for shuttles, Trained for most ships, Professional for the Horizon." + - rscadd: "You may attempt to pilot ships a level higher with penalties, including at Familiar. Only those 2+ tiers away are hard-denied." + - rscadd: "Three piloting education backgrounds: Flight Academy, Fleet Training, and Expeditionary Trade. First two are for Bridge personnel, last is for any role focused on away missions." + - rscadd: "Professional Piloting gives the convenient ability to deduce the current overmap coordinate from examining space for 3s." + - rscadd: "With Familiar in Electrical Engineering, ship consoles may be given emergency battery life. Useful when you'd otherwise be stranded from no power." + - rscadd: "Repairing and hotwiring ship consoles now requires Familiar in Electrical Engineering." + - rscadd: "Research Directors need Shuttle Piloting as they're expected to be able to pilot the Intrepid or Quark when necessary." + - rscadd: "Doctor of Psychology education gives Trained in Leadership, as was intended to happen with the addition of Leadership." diff --git a/maps/sccv_horizon/code/sccv_horizon_overmap.dm b/maps/sccv_horizon/code/sccv_horizon_overmap.dm index d52bbd3ab74..ce0c004001e 100644 --- a/maps/sccv_horizon/code/sccv_horizon_overmap.dm +++ b/maps/sccv_horizon/code/sccv_horizon_overmap.dm @@ -9,6 +9,7 @@ desc = "A line without compare, the Venator-series consists of one vessel so far: the SCCV Horizon, the lead ship of its class. Designed to be an entirely self-sufficient general-purpose surveying ship and to carry multiple replacement crews simultaneously, the Venator is equipped with both a bluespace and a warp drive and two different engines. Defying typical cruiser dimensions, the Venator is home to a sizable residential deck below the operations deck of the ship, where the crew is housed. It also features weapon hardpoints in its prominent wing nacelles. This one's transponder identifies it, obviously, as the SCCV Horizon." icon_state = "venator" moving_state = "venator_moving" + pilot_class = PILOTING_CLASS_MAX colors = list("#cfd4ff", "#78adf8") fore_dir = SOUTH vessel_mass = 70000