From a3fa541e2e50e70ccdf503183c8209a162d0bf87 Mon Sep 17 00:00:00 2001 From: Fikou <23585223+Fikou@users.noreply.github.com> Date: Sun, 17 Dec 2023 02:02:45 +0100 Subject: [PATCH] Bridge Assistant Station Trait (#80279) ## About The Pull Request adds a station trait which adds a new role, the bridge assistant he is designed to help commandeer the bridge and help out other heads when needed. he is armed with the mini energy gun (the one heads used to have on kilostation), a flash, a toolbelt (with an inducer), some cool shades and a swanky scarf. as he is a nerd he is weak and unable to twohand weapons, preventing him from wielding the fire axe. currently he does not have a mindshield but he cannot roll antag he currently has access to the bridge, announcement console, eva, teleporter, gateway, maint, and a weapon permit (somewhat (not really other than for nerds) interestingly this is the first job that isnt assistant that doesnt have access to any lathes, so he doesnt have orm access unlike all the other jobs (except assistant)) the trait also makes a coffee machine spawn on the bridge here is some useful art of your role ![image](https://github.com/tgstation/tgstation/assets/23585223/905e5527-9069-4226-b160-8dedd1ea7b74) and ingame screenshots ![image](https://github.com/tgstation/tgstation/assets/23585223/0aa537ac-a791-4249-a702-490584919fd9) ![image](https://github.com/tgstation/tgstation/assets/23585223/eb93e2d1-0291-4ade-9208-b1c0b68648c7) ![image](https://github.com/tgstation/tgstation/assets/23585223/1d3c2f11-8ac0-4ee9-91a5-176f81a08dcb) ## Why It's Good For The Game Adds upon the station trait job system with a straight forward role that IS just a human (unlike the cargorilla), and is pretty basic with no custom assets or whatever other than hud icons Having the bridge assistant in some rounds seems like a neat way to protect it since it gets fucked up in like half the time, while also not having enough mechanical depth or gameplay as to warrant it as a permanent role ## Changelog :cl: add: Bridge Assistant job accessible from a station trait. /:cl: --------- Co-authored-by: san7890 --- code/__DEFINES/atom_hud.dm | 1 + code/__DEFINES/jobs.dm | 74 ++++++++------- code/__DEFINES/traits/declarations.dm | 6 ++ code/_globalvars/traits/_traits.dm | 4 + code/_onclick/hud/new_player.dm | 2 +- code/controllers/subsystem/dynamic/dynamic.dm | 3 + .../subsystem/processing/station.dm | 4 + code/datums/components/twohanded.dm | 16 +++- code/datums/components/wall_mounted.dm | 2 + code/datums/id_trim/jobs.dm | 22 +++++ code/datums/records/manifest.dm | 33 +++++++ code/datums/station_traits/_station_trait.dm | 2 +- code/datums/station_traits/job_traits.dm | 87 ++++++++++++++++-- code/datums/station_traits/neutral_traits.dm | 6 +- code/game/objects/items/storage/belt.dm | 20 ++++ code/modules/admin/verbs/list_exposer.dm | 7 +- code/modules/bitrunning/job.dm | 2 +- code/modules/clothing/outfits/plasmaman.dm | 7 ++ code/modules/jobs/job_types/assistant.dm | 9 +- code/modules/jobs/job_types/prisoner.dm | 2 +- .../station_trait/bridge_assistant.dm | 79 ++++++++++++++++ .../{ => station_trait}/cargo_gorilla.dm | 2 +- code/modules/mob/dead/crew_manifest.dm | 38 -------- .../modules/mob/dead/new_player/new_player.dm | 5 +- code/modules/mob/dead/observer/observer.dm | 5 +- code/modules/mob/living/silicon/silicon.dm | 5 +- .../computers/item/role_tablet_presets.dm | 12 ++- .../modules/unit_tests/station_trait_tests.dm | 2 + icons/hud/lobby/signup_button.dmi | Bin 676 -> 668 bytes icons/mob/huds/hud.dmi | Bin 10550 -> 10588 bytes icons/obj/card.dmi | Bin 24223 -> 24234 bytes tgstation.dme | 4 +- .../tgui/interfaces/common/JobToIcon.ts | 1 + 33 files changed, 347 insertions(+), 115 deletions(-) create mode 100644 code/modules/jobs/job_types/station_trait/bridge_assistant.dm rename code/modules/jobs/job_types/{ => station_trait}/cargo_gorilla.dm (93%) delete mode 100644 code/modules/mob/dead/crew_manifest.dm diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm index 95186f49c27f65..df837c86d9a310 100644 --- a/code/__DEFINES/atom_hud.dm +++ b/code/__DEFINES/atom_hud.dm @@ -83,6 +83,7 @@ #define SECHUD_BARTENDER "hudbartender" #define SECHUD_BITRUNNER "hudbitrunner" #define SECHUD_BOTANIST "hudbotanist" +#define SECHUD_BRIDGE_ASSISTANT "hudbridgeassistant" #define SECHUD_CAPTAIN "hudcaptain" #define SECHUD_CARGO_TECHNICIAN "hudcargotechnician" #define SECHUD_CHAPLAIN "hudchaplain" diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index ba1f3e9ab82fbf..ea5a7596759c5b 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -48,6 +48,7 @@ #define JOB_RESEARCH_DIRECTOR "Research Director" #define JOB_CHIEF_ENGINEER "Chief Engineer" #define JOB_CHIEF_MEDICAL_OFFICER "Chief Medical Officer" +#define JOB_BRIDGE_ASSISTANT "Bridge Assistant" //Silicon #define JOB_AI "AI" #define JOB_CYBORG "Cyborg" @@ -122,41 +123,42 @@ #define JOB_DISPLAY_ORDER_ASSISTANT 1 #define JOB_DISPLAY_ORDER_CAPTAIN 2 #define JOB_DISPLAY_ORDER_HEAD_OF_PERSONNEL 3 -#define JOB_DISPLAY_ORDER_BARTENDER 4 -#define JOB_DISPLAY_ORDER_BOTANIST 5 -#define JOB_DISPLAY_ORDER_COOK 6 -#define JOB_DISPLAY_ORDER_JANITOR 7 -#define JOB_DISPLAY_ORDER_CLOWN 8 -#define JOB_DISPLAY_ORDER_MIME 9 -#define JOB_DISPLAY_ORDER_CURATOR 10 -#define JOB_DISPLAY_ORDER_LAWYER 11 -#define JOB_DISPLAY_ORDER_CHAPLAIN 12 -#define JOB_DISPLAY_ORDER_PSYCHOLOGIST 13 -#define JOB_DISPLAY_ORDER_AI 14 -#define JOB_DISPLAY_ORDER_CYBORG 15 -#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 16 -#define JOB_DISPLAY_ORDER_STATION_ENGINEER 17 -#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 18 -#define JOB_DISPLAY_ORDER_QUARTERMASTER 19 -#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 20 -#define JOB_DISPLAY_ORDER_SHAFT_MINER 21 -#define JOB_DISPLAY_ORDER_BITRUNNER 22 -#define JOB_DISPLAY_ORDER_CARGO_GORILLA 23 -#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 24 -#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 25 -#define JOB_DISPLAY_ORDER_PARAMEDIC 26 -#define JOB_DISPLAY_ORDER_CHEMIST 27 -#define JOB_DISPLAY_ORDER_VIROLOGIST 28 -#define JOB_DISPLAY_ORDER_CORONER 29 -#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 30 -#define JOB_DISPLAY_ORDER_SCIENTIST 31 -#define JOB_DISPLAY_ORDER_ROBOTICIST 32 -#define JOB_DISPLAY_ORDER_GENETICIST 33 -#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 34 -#define JOB_DISPLAY_ORDER_WARDEN 35 -#define JOB_DISPLAY_ORDER_DETECTIVE 36 -#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 37 -#define JOB_DISPLAY_ORDER_PRISONER 38 +#define JOB_DISPLAY_ORDER_BRIDGE_ASSISTANT 4 +#define JOB_DISPLAY_ORDER_BARTENDER 5 +#define JOB_DISPLAY_ORDER_BOTANIST 6 +#define JOB_DISPLAY_ORDER_COOK 7 +#define JOB_DISPLAY_ORDER_JANITOR 8 +#define JOB_DISPLAY_ORDER_CLOWN 9 +#define JOB_DISPLAY_ORDER_MIME 10 +#define JOB_DISPLAY_ORDER_CURATOR 11 +#define JOB_DISPLAY_ORDER_LAWYER 12 +#define JOB_DISPLAY_ORDER_CHAPLAIN 13 +#define JOB_DISPLAY_ORDER_PSYCHOLOGIST 14 +#define JOB_DISPLAY_ORDER_AI 15 +#define JOB_DISPLAY_ORDER_CYBORG 16 +#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 17 +#define JOB_DISPLAY_ORDER_STATION_ENGINEER 18 +#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 19 +#define JOB_DISPLAY_ORDER_QUARTERMASTER 20 +#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 21 +#define JOB_DISPLAY_ORDER_SHAFT_MINER 22 +#define JOB_DISPLAY_ORDER_BITRUNNER 23 +#define JOB_DISPLAY_ORDER_CARGO_GORILLA 24 +#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 25 +#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 26 +#define JOB_DISPLAY_ORDER_PARAMEDIC 27 +#define JOB_DISPLAY_ORDER_CHEMIST 28 +#define JOB_DISPLAY_ORDER_VIROLOGIST 29 +#define JOB_DISPLAY_ORDER_CORONER 30 +#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 31 +#define JOB_DISPLAY_ORDER_SCIENTIST 32 +#define JOB_DISPLAY_ORDER_ROBOTICIST 33 +#define JOB_DISPLAY_ORDER_GENETICIST 34 +#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 35 +#define JOB_DISPLAY_ORDER_WARDEN 36 +#define JOB_DISPLAY_ORDER_DETECTIVE 37 +#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 38 +#define JOB_DISPLAY_ORDER_PRISONER 39 #define DEPARTMENT_UNASSIGNED "No Department" @@ -209,6 +211,8 @@ /// Combination flag for jobs which are considered regular crew members of the station. #define STATION_JOB_FLAGS (JOB_ANNOUNCE_ARRIVAL|JOB_CREW_MANIFEST|JOB_EQUIP_RANK|JOB_CREW_MEMBER|JOB_NEW_PLAYER_JOINABLE|JOB_REOPEN_ON_ROUNDSTART_LOSS|JOB_ASSIGN_QUIRKS|JOB_CAN_BE_INTERN) +/// Combination flag for jobs which should be there for every station trait job. +#define STATION_TRAIT_JOB_FLAGS (JOB_CANNOT_OPEN_SLOTS|JOB_HIDE_WHEN_EMPTY|JOB_LATEJOIN_ONLY&~JOB_REOPEN_ON_ROUNDSTART_LOSS) #define FACTION_NONE "None" #define FACTION_STATION "Station" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 60366b69a125ed..b0ca10a8d88339 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -489,6 +489,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Is the mob standing on an elevated surface? This prevents them from dropping down if not elevated first. #define TRAIT_ON_ELEVATED_SURFACE "on_elevated_surface" +// Prevents you from twohanding weapons. +#define TRAIT_NO_TWOHANDING "no_twohanding" + // METABOLISMS // Various jobs on the station have historically had better reactions // to various drinks and foodstuffs. Security liking donuts is a classic @@ -817,6 +820,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Similar trait given to temporary bodies inhabited by players #define TRAIT_TEMPORARY_BODY "temporary_body" +/// Trait given to objects with the wallmounted component +#define TRAIT_WALLMOUNTED "wallmounted" + /// Trait given to mechs that can have orebox functionality on movement #define TRAIT_OREBOX_FUNCTIONAL "orebox_functional" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 5fd5f03fd50673..77bec032d86279 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -98,6 +98,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( /datum/wound = list( "TRAIT_WOUND_SCANNED" = TRAIT_WOUND_SCANNED, ), + /obj = list( + "TRAIT_WALLMOUNTED" = TRAIT_WALLMOUNTED, + ), /mob = list( "TRAIT_ABDUCTOR_SCIENTIST_TRAINING" = TRAIT_ABDUCTOR_SCIENTIST_TRAINING, "TRAIT_ABDUCTOR_TRAINING" = TRAIT_ABDUCTOR_TRAINING, @@ -304,6 +307,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NO_SOUL" = TRAIT_NO_SOUL, "TRAIT_NO_STRIP" = TRAIT_NO_STRIP, "TRAIT_NO_TRANSFORM" = TRAIT_NO_TRANSFORM, + "TRAIT_NO_TWOHANDING" = TRAIT_NO_TWOHANDING, "TRAIT_NOCRITDAMAGE" = TRAIT_NOCRITDAMAGE, "TRAIT_NO_UNDERWEAR" = TRAIT_NO_UNDERWEAR, "TRAIT_NO_ZOMBIFY" = TRAIT_NO_ZOMBIFY, diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm index 9719a8a3859cc8..5fa44b7f0542bd 100644 --- a/code/_onclick/hud/new_player.dm +++ b/code/_onclick/hud/new_player.dm @@ -39,7 +39,7 @@ var/y_offset = 397 var/y_button_offset = 27 for (var/datum/station_trait/trait as anything in GLOB.lobby_station_traits) - if (!trait.can_display_lobby_button()) + if (!trait.can_display_lobby_button(mymob.client)) continue var/atom/movable/screen/lobby/button/sign_up/sign_up_button = new(our_hud = src) sign_up_button.SlowInit() diff --git a/code/controllers/subsystem/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm index 8741f75ee67cac..e8898554309a0b 100644 --- a/code/controllers/subsystem/dynamic/dynamic.dm +++ b/code/controllers/subsystem/dynamic/dynamic.dm @@ -931,6 +931,9 @@ SUBSYSTEM_DEF(dynamic) stack_trace("Invalid dynamic configuration variable [variable] in [ruleset.ruletype] [ruleset.name].") continue ruleset.vars[variable] = rule_conf[variable] + ruleset.restricted_roles |= SSstation.antag_restricted_roles + if(length(ruleset.protected_roles)) //if we care to protect any role, we should protect station trait roles too + ruleset.protected_roles |= SSstation.antag_protected_roles if(CONFIG_GET(flag/protect_roles_from_antagonist)) ruleset.restricted_roles |= ruleset.protected_roles if(CONFIG_GET(flag/protect_assistant_from_antagonist)) diff --git a/code/controllers/subsystem/processing/station.dm b/code/controllers/subsystem/processing/station.dm index 95a47a8de5cc80..00c9d14a845f5c 100644 --- a/code/controllers/subsystem/processing/station.dm +++ b/code/controllers/subsystem/processing/station.dm @@ -11,6 +11,10 @@ PROCESSING_SUBSYSTEM_DEF(station) var/list/selectable_traits_by_types = list(STATION_TRAIT_POSITIVE = list(), STATION_TRAIT_NEUTRAL = list(), STATION_TRAIT_NEGATIVE = list()) ///Currently active announcer. Starts as a type but gets initialized after traits are selected var/datum/centcom_announcer/announcer = /datum/centcom_announcer/default + ///A list of trait roles that should be protected from antag + var/list/antag_protected_roles = list() + ///A list of trait roles that should never be able to roll antag + var/list/antag_restricted_roles = list() /datum/controller/subsystem/processing/station/Initialize() diff --git a/code/datums/components/twohanded.dm b/code/datums/components/twohanded.dm index 37df730821762e..89db2d27c83492 100644 --- a/code/datums/components/twohanded.dm +++ b/code/datums/components/twohanded.dm @@ -173,17 +173,25 @@ /datum/component/two_handed/proc/wield(mob/living/carbon/user) if(wielded) return + var/atom/atom_parent = parent + if(HAS_TRAIT(user, TRAIT_NO_TWOHANDING)) + if(require_twohands) + atom_parent.balloon_alert(user, "too weak to wield!") + user.dropItemToGround(parent, force = TRUE) + else + atom_parent.balloon_alert(user, "too weak to wield with both hands!") + return if(user.get_inactive_held_item()) if(require_twohands) - to_chat(user, span_notice("[parent] is too cumbersome to carry in one hand!")) - user.dropItemToGround(parent, force=TRUE) + atom_parent.balloon_alert(user, "can't carry in one hand!") + user.dropItemToGround(parent, force = TRUE) else - to_chat(user, span_warning("You need your other hand to be empty!")) + atom_parent.balloon_alert(user, "holding something in other hand!") return if(user.usable_hands < 2) if(require_twohands) user.dropItemToGround(parent, force=TRUE) - to_chat(user, span_warning("You don't have enough intact hands.")) + atom_parent.balloon_alert(user, "not enough hands!") return // wield update status diff --git a/code/datums/components/wall_mounted.dm b/code/datums/components/wall_mounted.dm index 8d1722f89feb80..00482592f2b3f2 100644 --- a/code/datums/components/wall_mounted.dm +++ b/code/datums/components/wall_mounted.dm @@ -16,12 +16,14 @@ on_drop = on_drop_callback /datum/component/wall_mounted/RegisterWithParent() + ADD_TRAIT(parent, TRAIT_WALLMOUNTED, REF(src)) RegisterSignal(hanging_wall_turf, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) RegisterSignal(hanging_wall_turf, COMSIG_TURF_CHANGE, PROC_REF(on_turf_changing)) RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(drop_wallmount)) RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_linked_destroyed)) /datum/component/wall_mounted/UnregisterFromParent() + REMOVE_TRAIT(parent, TRAIT_WALLMOUNTED, REF(src)) UnregisterSignal(hanging_wall_turf, list(COMSIG_ATOM_EXAMINE, COMSIG_TURF_CHANGE)) UnregisterSignal(parent, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED)) hanging_wall_turf = null diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm index 986f0b188f914d..07d7d3b580a501 100644 --- a/code/datums/id_trim/jobs.dm +++ b/code/datums/id_trim/jobs.dm @@ -202,6 +202,28 @@ ) job = /datum/job/botanist +/datum/id_trim/job/bridge_assistant + assignment = "Bridge Assistant" + trim_state = "trim_assistant" + department_color = COLOR_COMMAND_BLUE + subdepartment_color = COLOR_COMMAND_BLUE + sechud_icon_state = SECHUD_BRIDGE_ASSISTANT + minimal_access = list( + ACCESS_COMMAND, + ACCESS_EVA, + ACCESS_GATEWAY, + ACCESS_MAINT_TUNNELS, + ACCESS_RC_ANNOUNCE, + ACCESS_TELEPORTER, + ACCESS_WEAPONS, + ) + extra_access = list() + template_access = list( + ACCESS_CAPTAIN, + ACCESS_CHANGE_IDS, + ) + job = /datum/job/bridge_assistant + /datum/id_trim/job/captain assignment = "Captain" intern_alt_name = "Captain-in-Training" diff --git a/code/datums/records/manifest.dm b/code/datums/records/manifest.dm index 225da0e0c04450..7d3b7978af9045 100644 --- a/code/datums/records/manifest.dm +++ b/code/datums/records/manifest.dm @@ -160,3 +160,36 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new) target.rank = assignment target.trim = trim + +/datum/manifest/ui_state(mob/user) + return GLOB.always_state + +/datum/manifest/ui_status(mob/user, datum/ui_state/state) + return (isnewplayer(user) || isobserver(user) || isAI(user) || ispAI(user) || user.client?.holder) ? UI_INTERACTIVE : UI_CLOSE + +/datum/manifest/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "CrewManifest") + ui.open() + +/datum/manifest/ui_data(mob/user) + var/list/positions = list() + for(var/datum/job_department/department as anything in SSjob.joinable_departments) + var/open = 0 + var/list/exceptions = list() + for(var/datum/job/job as anything in department.department_jobs) + if(job.total_positions == -1) + exceptions += job.title + continue + var/open_slots = job.total_positions - job.current_positions + if(open_slots < 1) + continue + open += open_slots + positions[department.department_name] = list("exceptions" = exceptions, "open" = open) + + return list( + "manifest" = get_manifest(), + "positions" = positions + ) + diff --git a/code/datums/station_traits/_station_trait.dm b/code/datums/station_traits/_station_trait.dm index 8b2f7158b2ee39..5d9c0b4d01f1fb 100644 --- a/code/datums/station_traits/_station_trait.dm +++ b/code/datums/station_traits/_station_trait.dm @@ -83,7 +83,7 @@ GLOBAL_LIST_EMPTY(lobby_station_traits) return /// Return TRUE if we want to show a lobby button, by default we assume we don't want it after the round begins -/datum/station_trait/proc/can_display_lobby_button() +/datum/station_trait/proc/can_display_lobby_button(client/player) return sign_up_button && !SSticker.HasRoundStarted() /// Apply any additional handling we need to our lobby button diff --git a/code/datums/station_traits/job_traits.dm b/code/datums/station_traits/job_traits.dm index 300d6da8fa21c2..7d04127c7b1452 100644 --- a/code/datums/station_traits/job_traits.dm +++ b/code/datums/station_traits/job_traits.dm @@ -1,3 +1,7 @@ +#define CAN_ROLL_ALWAYS 1 //always can roll for antag +#define CAN_ROLL_PROTECTED 2 //can roll if config lets protected roles roll +#define CAN_ROLL_NEVER 3 //never roll antag + /** * A station trait which enables a temporary job * Generally speaking these should always all be mutually exclusive, don't have too many at once @@ -7,13 +11,22 @@ abstract_type = /datum/station_trait/job /// What tooltip to show on the button var/button_desc = "Sign up to gain some kind of unusual job, not available in most rounds." + /// Can this job roll antag? + var/can_roll_antag = CAN_ROLL_ALWAYS + /// How many positions to spawn? + var/position_amount = 1 /// Type of job to enable - var/job_to_add = /datum/job/clown + var/datum/job/job_to_add = /datum/job/clown /// Who signed up to this in the lobby var/list/lobby_candidates /datum/station_trait/job/New() . = ..() + switch(can_roll_antag) + if(CAN_ROLL_PROTECTED) + SSstation.antag_protected_roles += job_to_add::title + if(CAN_ROLL_NEVER) + SSstation.antag_restricted_roles += job_to_add::title blacklist += subtypesof(/datum/station_trait/job) - type // All but ourselves RegisterSignal(SSdcs, COMSIG_GLOB_PRE_JOBS_ASSIGNED, PROC_REF(pre_jobs_assigned)) @@ -50,16 +63,19 @@ if (!LAZYLEN(lobby_candidates)) on_failed_assignment() return // Nobody signed up :( - var/mob/dead/new_player/picked_player = pick(lobby_candidates) - picked_player.mind.assigned_role = new job_to_add() + for(var/_ in 1 to position_amount) + var/mob/dead/new_player/picked_player = pick_n_take(lobby_candidates) + picked_player.mind.assigned_role = new job_to_add() lobby_candidates = null /// Called if we didn't assign a role before the round began, we add it to the latejoin menu instead /datum/station_trait/job/proc/on_failed_assignment() - var/datum/job/our_job = job_to_add - our_job = SSjob.GetJob(our_job::title) - our_job.total_positions++ + var/datum/job/our_job = SSjob.GetJob(job_to_add::title) + our_job.total_positions = position_amount +/datum/station_trait/job/can_display_lobby_button(client/player) + var/datum/job/our_job = SSjob.GetJob(job_to_add::title) + return our_job.player_old_enough(player) && ..() /// Adds a gorilla to the cargo department, replacing the sloth and the mech /datum/station_trait/job/cargorilla @@ -67,6 +83,7 @@ button_desc = "Sign up to become the Cargo Gorilla, a peaceful shepherd of boxes." weight = 1 show_in_report = FALSE // Selective attention test. Did you spot the gorilla? + can_roll_antag = CAN_ROLL_NEVER job_to_add = /datum/job/cargo_gorilla /datum/station_trait/job/cargorilla/New() @@ -92,3 +109,61 @@ // monkey carries the crates, the age of robot is over if(GLOB.cargo_ripley) qdel(GLOB.cargo_ripley) + +/datum/station_trait/job/bridge_assistant + name = "Bridge Assistant" + button_desc = "Sign up to become the Bridge Assistant and watch over the Bridge." + weight = 2 + report_message = "We have installed a Bridge Assistant on your station." + show_in_report = TRUE + can_roll_antag = CAN_ROLL_PROTECTED + job_to_add = /datum/job/bridge_assistant + +/datum/station_trait/job/bridge_assistant/New() + . = ..() + RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(add_coffeemaker)) + +/datum/station_trait/job/bridge_assistant/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays) + . = ..() + overlays += "bridge_assistant" + +/// Creates a coffeemaker in the bridge, if we don't have one yet. +/datum/station_trait/job/bridge_assistant/proc/add_coffeemaker(datum/source) + SIGNAL_HANDLER + var/area/bridge = GLOB.areas_by_type[/area/station/command/bridge] + if(isnull(bridge)) //no bridge, what will he assist? + return + var/list/possible_coffeemaker_positions = list(/area/station/command/bridge, /area/station/command/meeting_room) + var/list/coffeemakers = SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/coffeemaker) + for(var/obj/machinery/coffeemaker as anything in coffeemakers) //don't spawn a coffeemaker if there is already one on the bridge + if(is_type_in_list(get_area(coffeemaker), possible_coffeemaker_positions)) + return + var/list/tables = list() + for(var/turf/area_turf as anything in bridge.get_contained_turfs()) + var/obj/structure/table/table = locate() in area_turf + if(isnull(table)) + continue + if(area_turf.is_blocked_turf(ignore_atoms = list(table))) //don't spawn a coffeemaker on a fax machine or smth + continue + tables += table + if(!length(tables)) + return + var/picked_table = pick_n_take(tables) + var/picked_turf = get_turf(picked_table) + if(length(tables)) + var/another_table = pick(tables) + for(var/obj/thing_on_table in picked_turf) //if there's paper bins or other shit on the table, get that off + if(thing_on_table == picked_table) + continue + if(HAS_TRAIT(thing_on_table, TRAIT_WALLMOUNTED) || (thing_on_table.flags_1 & ON_BORDER_1) || thing_on_table.layer < TABLE_LAYER) + continue + if(thing_on_table.invisibility || !thing_on_table.alpha || !thing_on_table.mouse_opacity) + continue + thing_on_table.forceMove(get_turf(another_table)) + new /obj/machinery/coffeemaker/impressa(picked_turf) + new /obj/item/reagent_containers/cup/coffeepot(picked_turf) + new /obj/item/storage/box/coffeepack(picked_turf) + +#undef CAN_ROLL_ALWAYS +#undef CAN_ROLL_PROTECTED +#undef CAN_ROLL_NEVER diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm index 4f47cc2ba2d860..96c4099c31d9d1 100644 --- a/code/datums/station_traits/neutral_traits.dm +++ b/code/datums/station_traits/neutral_traits.dm @@ -2,7 +2,7 @@ name = "Bananium Shipment" trait_type = STATION_TRAIT_NEUTRAL weight = 5 - report_message = "Rumors has it that the clown planet has been sending support packages to clowns in this system" + report_message = "Rumors has it that the clown planet has been sending support packages to clowns in this system." trait_to_give = STATION_TRAIT_BANANIUM_SHIPMENTS /datum/station_trait/unnatural_atmosphere @@ -10,7 +10,7 @@ trait_type = STATION_TRAIT_NEUTRAL weight = 5 show_in_report = TRUE - report_message = "System's local planet has irregular atmospherical properties" + report_message = "System's local planet has irregular atmospherical properties." trait_to_give = STATION_TRAIT_UNNATURAL_ATMOSPHERE // This station trait modifies the atmosphere, which is too far past the time admins are able to revert it @@ -197,7 +197,7 @@ /datum/station_trait/birthday/proc/announce_birthday() - report_message = "We here at Nanotrasen would all like to wish [birthday_person ? birthday_person_name : "Employee Name"] a very happy birthday" + report_message = "We here at Nanotrasen would all like to wish [birthday_person ? birthday_person_name : "Employee Name"] a very happy birthday." priority_announce("Happy birthday to [birthday_person ? birthday_person_name : "Employee Name"]! Nanotrasen wishes you a very happy [birthday_person ? thtotext(birthday_person.age + 1) : "255th"] birthday.") if(birthday_person) playsound(birthday_person, 'sound/items/party_horn.ogg', 50) diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 1644a7da981b97..19f6c35ca772ef 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -190,6 +190,26 @@ to_preload += /obj/item/extinguisher/mini return to_preload +/obj/item/storage/belt/utility/full/inducer/PopulateContents() + SSwardrobe.provide_type(/obj/item/screwdriver, src) + SSwardrobe.provide_type(/obj/item/wrench, src) + SSwardrobe.provide_type(/obj/item/weldingtool, src) + SSwardrobe.provide_type(/obj/item/crowbar/red, src) + SSwardrobe.provide_type(/obj/item/wirecutters, src) + SSwardrobe.provide_type(/obj/item/multitool, src) + SSwardrobe.provide_type(/obj/item/inducer, src) + +/obj/item/storage/belt/utility/full/inducer/get_types_to_preload() + var/list/to_preload = list() //Yes this is a pain. Yes this is the point + to_preload += /obj/item/screwdriver + to_preload += /obj/item/wrench + to_preload += /obj/item/weldingtool + to_preload += /obj/item/crowbar + to_preload += /obj/item/wirecutters + to_preload += /obj/item/multitool + to_preload += /obj/item/inducer + return to_preload + /obj/item/storage/belt/utility/syndicate preload = FALSE diff --git a/code/modules/admin/verbs/list_exposer.dm b/code/modules/admin/verbs/list_exposer.dm index 445097e8bef3fa..5ea2e2277501ad 100644 --- a/code/modules/admin/verbs/list_exposer.dm +++ b/code/modules/admin/verbs/list_exposer.dm @@ -51,12 +51,7 @@ if(!SSticker.HasRoundStarted()) tgui_alert(usr, "The game hasn't started yet!") return - var/data = "Showing Crew Manifest.
" - data += "" - for(var/datum/record/crew/entry in GLOB.manifest.general) - data += "" - data += "
NamePosition
[entry.name][entry.rank][entry.rank != entry.trim ? " ([entry.trim])" : ""]
" - usr << browse(data, "window=manifest;size=440x410") + GLOB.manifest.ui_interact(usr) /datum/admins/proc/output_ai_laws() var/law_bound_entities = 0 diff --git a/code/modules/bitrunning/job.dm b/code/modules/bitrunning/job.dm index 57581753c0fb6f..1e749a3c7a58e5 100644 --- a/code/modules/bitrunning/job.dm +++ b/code/modules/bitrunning/job.dm @@ -29,7 +29,7 @@ /obj/item/food/cornchips/blue = 1, ) rpg_title = "Recluse" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + job_flags = STATION_JOB_FLAGS /datum/outfit/job/bitrunner name = "Bitrunner" diff --git a/code/modules/clothing/outfits/plasmaman.dm b/code/modules/clothing/outfits/plasmaman.dm index a422d2d736e92b..40d7cfa888e058 100644 --- a/code/modules/clothing/outfits/plasmaman.dm +++ b/code/modules/clothing/outfits/plasmaman.dm @@ -288,3 +288,10 @@ uniform = /obj/item/clothing/under/plasmaman/bitrunner gloves = /obj/item/clothing/gloves/color/plasmaman/black head = /obj/item/clothing/head/helmet/space/plasmaman/bitrunner + +/datum/outfit/plasmaman/bridge_assistant + name = "Bridge Assistant Plasmaman" + + uniform = /obj/item/clothing/under/plasmaman //call me when this is gags and not 10 million new assets + gloves = /obj/item/clothing/gloves/color/plasmaman/black + head = /obj/item/clothing/head/helmet/space/plasmaman diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm index 605a77ca83ac12..674211c40eac09 100644 --- a/code/modules/jobs/job_types/assistant.dm +++ b/code/modules/jobs/job_types/assistant.dm @@ -45,14 +45,16 @@ Assistant /datum/outfit/job/assistant/pre_equip(mob/living/carbon/human/target) ..() + give_hat() + give_jumpsuit(target) + +/datum/outfit/job/assistant/proc/give_hat() for(var/holidayname in GLOB.holidays) var/datum/holiday/holiday_today = GLOB.holidays[holidayname] var/obj/item/special_hat = holiday_today.holiday_hat if(prob(HOLIDAY_HAT_CHANCE) && !isnull(special_hat) && isnull(head)) head = special_hat - give_jumpsuit(target) - /datum/outfit/job/assistant/proc/give_jumpsuit(mob/living/carbon/human/target) var/static/jumpsuit_number = 0 jumpsuit_number += 1 @@ -73,6 +75,9 @@ Assistant /datum/outfit/job/assistant/consistent name = "Assistant - Consistent" +/datum/outfit/job/assistant/consistent/give_hat() + return + /datum/outfit/job/assistant/consistent/give_jumpsuit(mob/living/carbon/human/target) uniform = /obj/item/clothing/under/color/grey diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm index c18b92decc2631..28b697a794fdf3 100644 --- a/code/modules/jobs/job_types/prisoner.dm +++ b/code/modules/jobs/job_types/prisoner.dm @@ -23,7 +23,7 @@ family_heirlooms = list(/obj/item/pen/blue) rpg_title = "Defeated Miniboss" - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS + job_flags = STATION_JOB_FLAGS | JOB_CANNOT_OPEN_SLOTS & ~JOB_REOPEN_ON_ROUNDSTART_LOSS /datum/job/prisoner/New() . = ..() diff --git a/code/modules/jobs/job_types/station_trait/bridge_assistant.dm b/code/modules/jobs/job_types/station_trait/bridge_assistant.dm new file mode 100644 index 00000000000000..ebc1af0ee1a368 --- /dev/null +++ b/code/modules/jobs/job_types/station_trait/bridge_assistant.dm @@ -0,0 +1,79 @@ +/datum/job/bridge_assistant + title = JOB_BRIDGE_ASSISTANT + description = "Watch over the Bridge, command its consoles, and spend your days brewing coffee for higher-ups." + auto_deadmin_role_flags = DEADMIN_POSITION_HEAD //not really a head but close enough + department_head = list(JOB_CAPTAIN) + faction = FACTION_STATION + total_positions = 0 + spawn_positions = 0 + supervisors = "the Captain, and in non-Bridge related situations the other heads" + minimal_player_age = 7 + exp_requirements = 300 + exp_required_type = EXP_TYPE_CREW + exp_granted_type = EXP_TYPE_CREW + config_tag = "BRIDGE_ASSISTANT" + + outfit = /datum/outfit/job/bridge_assistant + plasmaman_outfit = /datum/outfit/plasmaman/bridge_assistant + + paycheck = PAYCHECK_CREW + paycheck_department = ACCOUNT_CIV + + liver_traits = list(TRAIT_PRETENDER_ROYAL_METABOLISM) + + display_order = JOB_DISPLAY_ORDER_BRIDGE_ASSISTANT + departments_list = list(/datum/job_department/command) + + family_heirlooms = list(/obj/item/banner/command/mundane) + + mail_goodies = list( + /obj/item/storage/fancy/cigarettes = 1, + /obj/item/pen/fountain = 1, + ) + rpg_title = "Royal Guard" + allow_bureaucratic_error = FALSE + job_flags = STATION_JOB_FLAGS | STATION_TRAIT_JOB_FLAGS + ignore_human_authority = TRUE + +/datum/job/bridge_assistant/after_spawn(mob/living/spawned, client/player_client) + . = ..() + ADD_TRAIT(spawned, TRAIT_NO_TWOHANDING, JOB_TRAIT) + +/datum/job/bridge_assistant/get_roundstart_spawn_point() + var/list/chair_turfs = list() + var/list/possible_turfs = list() + var/area/bridge = GLOB.areas_by_type[/area/station/command/bridge] + if(isnull(bridge)) + return ..() //if no bridge, spawn on the arrivals shuttle (but also what the fuck) + for(var/turf/possible_turf as anything in bridge.get_contained_turfs()) + if(possible_turf.is_blocked_turf()) + continue + if(locate(/obj/structure/chair) in possible_turf) + chair_turfs += possible_turf + continue + possible_turfs += possible_turf + if(length(chair_turfs)) + return pick(chair_turfs) //prioritize turfs with a chair + if(length(possible_turfs)) + return pick(possible_turfs) //if none, just pick a random turf in the bridge + return ..() //if the bridge has no turfs, spawn on the arrivals shuttle + +/datum/outfit/job/bridge_assistant + name = "Bridge Assistant" + jobtype = /datum/job/bridge_assistant + + id_trim = /datum/id_trim/job/bridge_assistant + backpack_contents = list( + /obj/item/modular_computer/pda/bridge_assistant = 1, + ) + + uniform = /obj/item/clothing/under/trek/command/next + neck = /obj/item/clothing/neck/large_scarf/blue + belt = /obj/item/storage/belt/utility/full/inducer + ears = /obj/item/radio/headset/headset_com + glasses = /obj/item/clothing/glasses/sunglasses + gloves = /obj/item/clothing/gloves/fingerless + head = /obj/item/clothing/head/soft/black + shoes = /obj/item/clothing/shoes/laceup + l_pocket = /obj/item/gun/energy/e_gun/mini + r_pocket = /obj/item/assembly/flash/handheld diff --git a/code/modules/jobs/job_types/cargo_gorilla.dm b/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm similarity index 93% rename from code/modules/jobs/job_types/cargo_gorilla.dm rename to code/modules/jobs/job_types/station_trait/cargo_gorilla.dm index 96a79e3a4d36f7..87363bf9b7bd5d 100644 --- a/code/modules/jobs/job_types/cargo_gorilla.dm +++ b/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm @@ -16,7 +16,7 @@ ) rpg_title = "Beast of Burden" allow_bureaucratic_error = FALSE - job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_CANNOT_OPEN_SLOTS | JOB_HIDE_WHEN_EMPTY | JOB_LATEJOIN_ONLY + job_flags = STATION_TRAIT_JOB_FLAGS | JOB_ANNOUNCE_ARRIVAL | JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK /datum/job/cargo_gorilla/get_roundstart_spawn_point() if (length(GLOB.gorilla_start)) diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm deleted file mode 100644 index 6a2c84622a2e34..00000000000000 --- a/code/modules/mob/dead/crew_manifest.dm +++ /dev/null @@ -1,38 +0,0 @@ -/datum/crew_manifest - -/datum/crew_manifest/ui_state(mob/user) - return GLOB.always_state - -/datum/crew_manifest/ui_status(mob/user, datum/ui_state/state) - return (isnewplayer(user) || isobserver(user) || isAI(user) || ispAI(user)) ? UI_INTERACTIVE : UI_CLOSE - -/datum/crew_manifest/ui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if (!ui) - ui = new(user, src, "CrewManifest") - ui.open() - -/datum/crew_manifest/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) - . = ..() - if(.) - return - -/datum/crew_manifest/ui_data(mob/user) - var/list/positions = list() - for(var/datum/job_department/department as anything in SSjob.joinable_departments) - var/open = 0 - var/list/exceptions = list() - for(var/datum/job/job as anything in department.department_jobs) - if(job.total_positions == -1) - exceptions += job.title - continue - var/open_slots = job.total_positions - job.current_positions - if(open_slots < 1) - continue - open += open_slots - positions[department.department_name] = list("exceptions" = exceptions, "open" = open) - - return list( - "manifest" = GLOB.manifest.get_manifest(), - "positions" = positions - ) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 4324b81c14c0d9..47ca4501f0c4e9 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -296,10 +296,7 @@ return client.crew_manifest_delay = world.time + (1 SECONDS) - if(!GLOB.crew_manifest_tgui) - GLOB.crew_manifest_tgui = new /datum/crew_manifest(src) - - GLOB.crew_manifest_tgui.ui_interact(src) + GLOB.manifest.ui_interact(src) /mob/dead/new_player/Move() return 0 diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 2f0d24e7b2f6ed..533e35eeebab6c 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -711,10 +711,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return client.crew_manifest_delay = world.time + (1 SECONDS) - if(!GLOB.crew_manifest_tgui) - GLOB.crew_manifest_tgui = new /datum/crew_manifest(src) - - GLOB.crew_manifest_tgui.ui_interact(src) + GLOB.manifest.ui_interact(src) //this is called when a ghost is drag clicked to something. /mob/dead/observer/MouseDrop(atom/over) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index fbe9986c3ca3c6..8a0c793103f5fb 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -349,10 +349,7 @@ return client.crew_manifest_delay = world.time + (1 SECONDS) - if(!GLOB.crew_manifest_tgui) - GLOB.crew_manifest_tgui = new /datum/crew_manifest(src) - - GLOB.crew_manifest_tgui.ui_interact(src) + GLOB.manifest.ui_interact(src) /mob/living/silicon/proc/set_autosay() //For allowing the AI and borgs to set the radio behavior of auto announcements (state laws, arrivals). if(!radio) diff --git a/code/modules/modular_computers/computers/item/role_tablet_presets.dm b/code/modules/modular_computers/computers/item/role_tablet_presets.dm index 500077760a88aa..904b9fc9837cdb 100644 --- a/code/modules/modular_computers/computers/item/role_tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/role_tablet_presets.dm @@ -103,7 +103,7 @@ /obj/item/modular_computer/pda/heads/quartermaster name = "quartermaster PDA" - greyscale_config = /datum/greyscale_config/tablet/stripe_thick + greyscale_config = /datum/greyscale_config/tablet/stripe_thick/head greyscale_colors = "#c4b787#18191e#8b4c31" inserted_item = /obj/item/pen/survival stored_paper = 20 @@ -412,7 +412,7 @@ ) /** - * No Department + * No Department/Station Trait */ /obj/item/modular_computer/pda/assistant @@ -421,6 +421,14 @@ /datum/computer_file/program/bounty_board, ) +/obj/item/modular_computer/pda/bridge_assistant + name = "bridge assistant PDA" + greyscale_colors = "#374f7e#a92323" + starting_programs = list( + /datum/computer_file/program/crew_manifest, + /datum/computer_file/program/status, + ) + /** * Non-roles */ diff --git a/code/modules/unit_tests/station_trait_tests.dm b/code/modules/unit_tests/station_trait_tests.dm index 430e83b9af7bea..21173e152d7574 100644 --- a/code/modules/unit_tests/station_trait_tests.dm +++ b/code/modules/unit_tests/station_trait_tests.dm @@ -6,5 +6,7 @@ for(var/datum/job/job as anything in subtypesof(/datum/job)) if(!(initial(job.job_flags) & JOB_CREW_MEMBER)) continue + if((initial(job.job_flags) & STATION_TRAIT_JOB_FLAGS) == STATION_TRAIT_JOB_FLAGS) + continue if(!(job in cyber_trait.job_to_cybernetic)) TEST_FAIL("Job [job] does not have an assigned cybernetic for [cyber_trait.type] station trait.") diff --git a/icons/hud/lobby/signup_button.dmi b/icons/hud/lobby/signup_button.dmi index 4ff31a316c2ac3b3cb47c4130f1b79b1cb872547..a67cc5584424e0a78c9069417a0e9ff16e923c65 100644 GIT binary patch delta 573 zcmV-D0>b^I1)K$tYJbCeR9JLGWpiV4X>fFDZ*Bkpc$}S*O$x#=5QW#xDMGsy(bBCO zX^}4M6-sQV}r*nmmL`pl4nLnIcGYA5xNVa2Sj|NqWa_V$wfYt3n4oc7G9kX=HE$aSqReD>QqJ za`T_HX6Y1b*6bEitJag&f!3_GeIFh5W#hM89B?UGh6uqkJ{stpMT}A&y6YU2(W)sw zkMjmZf~#In5daMU00B-(L_t(oh3%CwPQ)+}MaPBv$u4t{h!Y@j0J|_kh!$}JH~|GW zfCJEk8_-j71An*+lZ8xvur0C;VkN&cZzuD2oMhuA*BOjXz@ol6HhOey!a8kw^M2v@ zMNqF!sD222|6}mo#Omv2|6}mol-ixzEy6nl5&3k zs65=IgiTndi{W|L@iIKEab1$=(M3IEjgwDCSmV_8rGFM^0SIi(4|rd?eEU$|Uk`U* z`mf;5IQ!R4g|=W<5aC*8-z6Oc{3I?#Dmae=Fkll{*wz8g95`YCC;5W2IPXdU2TMg~ z9nqBn>{*C&kNO{>pK^kpppZL@NO3|KZ)M1xyBLcX9rafFDZ*Bkpc$}S*!3u*g42I9`Qxv_9v5D7R zhB7?NSCC?(4eMHIx{ba4>bz};;_XlP{^SoN73cC&)uOmo{0W}WS#c8i{=nG_RnW1> za?UhhuftSOkO87h2z&b&63!HQij5;@otJl*^bgkPIKhllK!2^Z3?7K)@GN%0$oD9B z|6XU;ZgF-?w&>av&pQK)J9_8Q7}>b4ThA^Z+d-cz-V4e*Lh%KhJkAts=eH;Lw9p zGM|{_;tI5;z*m%qb@_aaTJCI3k5urIfQ%I!7BJSBDb9@4FTzRHq4LQMIT}>_{N-Hz zHpTvoHkM)^$Rk5qgX2Gvv;oK`8uPMa$P-nLH_u;|ib-BKR!tcWxv{5)f>UP9MT6!9 zOJg1xbUrHWY3KquQ{ZEio^ap8o0`c2)StSQPEyrB(9&_{mi{;v&&#T+sulDL^Q-!K Th`d__00000NkvXXu0mjf>HQmH diff --git a/icons/mob/huds/hud.dmi b/icons/mob/huds/hud.dmi index 1ade65044e6576921d4dc62f350739e015f8c2dd..eb7007c9c454c20791ef8c2f8a9f9178af98a043 100644 GIT binary patch literal 10588 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 tw0U`oK~;w;dGT+Nb@4w_