Sign up for Cargorilla from the lobby (#79776)

## About The Pull Request

If the station rolls the "Cargo Gorilla" trait, a button will now be
visible on the lobby.
Clicking on this button before the round has started will add you to a
list of participants, one of whom will be selected to become a gorilla
when the round begins.
If nobody signs up (because they're really boring I guess) the job will
instead appear on the latejoin menu.
Once someone has become the gorilla the button will disappear.


![dreamseeker_ntP3OayAuV](https://github.com/tgstation/tgstation/assets/7483112/a26087ea-1ee7-4e9f-b37c-195cb1b1744f)

While implementing this I noticed that an inverted check means we were
never populating the "GLOB.cargo_sloth" field which means the station
trait wasn't even working.

BEHIND THE SCENES
This also adds a generic "job station trait" which can be expanded in
the future.
Future developers can extend this to add other "rare jobs" with relative
ease.
By default I have made it so all subtypes of this trait are mutually
exclusive, only one can roll at a time.

This also means that I have converted "cargo gorilla" into a job, which
applies most of the code previously located in the mob's typepath or in
the station trait.
The fact that it is a job means that **admins** can enable any number of
gorillas to be present on the latejoin menu (but not the roundstart one,
as it is not possible to add Cargo Gorilla to your occupation
preferences) if they so desire.
The random beurocratic station trait, event, and traitor item (and the
job console) are not able to add gorilla slots.

Because I changed "Cargo Gorilla" to a job it now no longer exists on
the map until a player gains the role, and there wasn't a non-hacky way
to copy the name of this round's cargo sloth. Instead I just added a
small cargo gorilla name list.

## Why It's Good For The Game

Makes the presence of a fun trait more visible to players.
Means that people who aren't observing get a chance to be a monkey.
This is a framework several other people have wanted to exist for their
own features.

## Changelog

🆑 Jacquerel and Fikou
qol: If the station rolls the "Cargo Gorilla" station trait. you will be
able to sign up for the role from the game lobby.
qol: If nobody signs up to be the Cargo Gorilla then you can select it
from the Late Join menu and arrive on the arrival shuttle.
fix: The Cargo Gorilla will actually spawn.
/🆑
This commit is contained in:
Jacquerel
2023-12-12 08:48:49 -08:00
committed by GitHub
parent d1ce866c4d
commit 90b974071d
25 changed files with 357 additions and 139 deletions
@@ -84,6 +84,9 @@
/// (new_name, old_name)
#define COMSIG_GLOB_STATION_NAME_CHANGED "!station_name_changed"
/// Global signal sent before we decide what job everyone has
#define COMSIG_GLOB_PRE_JOBS_ASSIGNED "!pre_roles_assigned"
/// global signal when a global nullrod type is picked
#define COMSIG_GLOB_NULLROD_PICKED "!nullrod_picked"
+21 -15
View File
@@ -76,6 +76,7 @@
//Supply
#define JOB_QUARTERMASTER "Quartermaster"
#define JOB_CARGO_TECHNICIAN "Cargo Technician"
#define JOB_CARGO_GORILLA "Cargo Gorilla"
#define JOB_SHAFT_MINER "Shaft Miner"
#define JOB_BITRUNNER "Bitrunner"
//Service
@@ -140,21 +141,22 @@
#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 20
#define JOB_DISPLAY_ORDER_SHAFT_MINER 21
#define JOB_DISPLAY_ORDER_BITRUNNER 22
#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 23
#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 24
#define JOB_DISPLAY_ORDER_PARAMEDIC 25
#define JOB_DISPLAY_ORDER_CHEMIST 26
#define JOB_DISPLAY_ORDER_VIROLOGIST 27
#define JOB_DISPLAY_ORDER_CORONER 28
#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 29
#define JOB_DISPLAY_ORDER_SCIENTIST 30
#define JOB_DISPLAY_ORDER_ROBOTICIST 31
#define JOB_DISPLAY_ORDER_GENETICIST 32
#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 33
#define JOB_DISPLAY_ORDER_WARDEN 34
#define JOB_DISPLAY_ORDER_DETECTIVE 35
#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 36
#define JOB_DISPLAY_ORDER_PRISONER 37
#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 DEPARTMENT_UNASSIGNED "No Department"
@@ -200,6 +202,10 @@
#define JOB_CAN_BE_INTERN (1<<8)
/// This job cannot have more slots opened by the Head of Personnel (but admins or other random events can still do this).
#define JOB_CANNOT_OPEN_SLOTS (1<<9)
/// This job will not display on the job menu when there are no slots available, instead of appearing greyed out
#define JOB_HIDE_WHEN_EMPTY (1<<10)
/// This job cannot be signed up for at round start or recorded in your preferences
#define JOB_LATEJOIN_ONLY (1<<11)
/// 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)
+1
View File
@@ -107,6 +107,7 @@ GLOBAL_LIST_EMPTY(generic_event_spawns)
/// essentially allowing a user to override generic job spawnpoints with a specific one
GLOBAL_LIST_EMPTY(jobspawn_overrides)
GLOBAL_LIST_EMPTY(gorilla_start)
GLOBAL_LIST_EMPTY(wizardstart)
GLOBAL_LIST_EMPTY(nukeop_start)
GLOBAL_LIST_EMPTY(nukeop_leader_start)
+1
View File
@@ -25,6 +25,7 @@ GLOBAL_LIST_INIT(megacarp_first_names, world.file2list("strings/names/megacarp1.
GLOBAL_LIST_INIT(megacarp_last_names, world.file2list("strings/names/megacarp2.txt"))
GLOBAL_LIST_INIT(cyberauth_names, world.file2list("strings/names/cyberauth.txt"))
GLOBAL_LIST_INIT(syndicate_monkey_names, world.file2list("strings/names/syndicate_monkey.txt"))
GLOBAL_LIST_INIT(cargorilla_names, world.file2list("strings/names/cargorilla.txt"))
GLOBAL_LIST_INIT(guardian_first_names, world.file2list("strings/names/guardian_descriptions.txt"))
GLOBAL_LIST_INIT(guardian_tech_surnames, world.file2list("strings/names/guardian_gamepieces.txt"))
GLOBAL_LIST_INIT(guardian_fantasy_surnames, world.file2list("strings/names/guardian_tarot.txt"))
+54 -8
View File
@@ -1,37 +1,65 @@
#define SHUTTER_MOVEMENT_DURATION 0.4 SECONDS
#define SHUTTER_WAIT_DURATION 0.2 SECONDS
/// Maximum number of station trait buttons we will display, please think hard before creating scenarios where there are more than this
#define MAX_STATION_TRAIT_BUTTONS_VERTICAL 3
/datum/hud/new_player
///Whether the menu is currently on the client's screen or not
var/menu_hud_status = TRUE
/datum/hud/new_player/New(mob/owner)
..()
. = ..()
if(!owner || !owner.client)
if (!owner || !owner.client)
return
if(owner.client.interviewee)
if (owner.client.interviewee)
return
var/list/buttons = subtypesof(/atom/movable/screen/lobby)
for(var/button_type in buttons)
var/atom/movable/screen/lobby/lobbyscreen = new button_type(our_hud = src)
for (var/atom/movable/screen/lobby/lobbyscreen as anything in buttons)
if (!initial(lobbyscreen.always_available))
continue
lobbyscreen = new lobbyscreen(our_hud = src)
lobbyscreen.SlowInit()
static_inventory += lobbyscreen
if(!lobbyscreen.always_shown)
if (!lobbyscreen.always_shown)
lobbyscreen.RegisterSignal(src, COMSIG_HUD_LOBBY_COLLAPSED, TYPE_PROC_REF(/atom/movable/screen/lobby, collapse_button))
lobbyscreen.RegisterSignal(src, COMSIG_HUD_LOBBY_EXPANDED, TYPE_PROC_REF(/atom/movable/screen/lobby, expand_button))
if(istype(lobbyscreen, /atom/movable/screen/lobby/button))
if (istype(lobbyscreen, /atom/movable/screen/lobby/button))
var/atom/movable/screen/lobby/button/lobby_button = lobbyscreen
lobby_button.owner = REF(owner)
add_station_trait_buttons()
/// Display buttons for relevant station traits
/datum/hud/new_player/proc/add_station_trait_buttons()
if (!mymob?.client || mymob.client.interviewee || !length(GLOB.lobby_station_traits))
return
var/buttons_created = 0
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())
continue
var/atom/movable/screen/lobby/button/sign_up/sign_up_button = new(our_hud = src)
sign_up_button.SlowInit()
sign_up_button.owner = REF(mymob)
sign_up_button.screen_loc = offset_to_screen_loc(233, y_offset, mymob.client.view)
y_offset += y_button_offset
static_inventory += sign_up_button
trait.setup_lobby_button(sign_up_button)
buttons_created++
if (buttons_created >= MAX_STATION_TRAIT_BUTTONS_VERTICAL)
return
/atom/movable/screen/lobby
plane = SPLASHSCREEN_PLANE
layer = LOBBY_MENU_LAYER
screen_loc = "TOP,CENTER"
///Whether this HUD element can be hidden from the client's "screen" (moved off-screen) or not
/// Whether this HUD element can be hidden from the client's "screen" (moved off-screen) or not
var/always_shown = FALSE
/// If true we will create this button every time the HUD is generated
var/always_available = TRUE
///Set the HUD in New, as lobby screens are made before Atoms are Initialized.
/atom/movable/screen/lobby/New(loc, datum/hud/our_hud, ...)
@@ -399,6 +427,23 @@
var/mob/dead/new_player/new_player = hud.mymob
new_player.handle_player_polling()
/// A generic "sign up" button used by station traits
/atom/movable/screen/lobby/button/sign_up
icon = 'icons/hud/lobby/signup_button.dmi'
icon_state = "signup"
base_icon_state = "signup"
always_available = FALSE
/atom/movable/screen/lobby/button/sign_up/MouseEntered(location, control, params)
. = ..()
if(QDELETED(src) || !desc)
return
openToolTip(usr, tip_src = src, params = params, title = name, content = desc,)
/atom/movable/screen/lobby/button/sign_up/MouseExited()
. = ..()
closeToolTip(usr)
/atom/movable/screen/lobby/button/collapse
name = "Collapse Lobby Menu"
icon = 'icons/hud/lobby/collapse_expand.dmi'
@@ -526,3 +571,4 @@
#undef SHUTTER_MOVEMENT_DURATION
#undef SHUTTER_WAIT_DURATION
#undef MAX_STATION_TRAIT_BUTTONS_VERTICAL
+4
View File
@@ -55,6 +55,10 @@
/atom/movable/screen/proc/component_click(atom/movable/screen/component_button/component, params)
return
/// Returns the mob this is being displayed to, if any
/atom/movable/screen/proc/get_mob()
return hud?.mymob
/atom/movable/screen/text
icon = null
icon_state = null
+12
View File
@@ -94,6 +94,18 @@ SUBSYSTEM_DEF(job)
set_overflow_role(CONFIG_GET(string/overflow_job)) // this must always go after load_jobs_from_config() due to how the legacy systems operate, this always takes precedent.
return SS_INIT_SUCCESS
/// Returns a list of jobs that we are allowed to fuck with during random events
/datum/controller/subsystem/job/proc/get_valid_overflow_jobs()
var/static/list/overflow_jobs
if (!isnull(overflow_jobs))
return overflow_jobs
overflow_jobs = list()
for (var/datum/job/check_job in joinable_occupations)
if (!check_job.allow_bureaucratic_error)
continue
overflow_jobs += check_job
return overflow_jobs
/datum/controller/subsystem/job/proc/set_overflow_role(new_overflow_role)
var/datum/job/new_overflow = ispath(new_overflow_role) ? GetJobType(new_overflow_role) : GetJob(new_overflow_role)
@@ -18,6 +18,7 @@ PROCESSING_SUBSYSTEM_DEF(station)
// Autowiki also wants consistent outputs, for example making sure the vending machine page always reports the normal products
#if !defined(UNIT_TESTS) && !defined(AUTOWIKI)
SetupTraits()
display_lobby_traits()
#endif
announcer = new announcer() //Initialize the station's announcer datum
@@ -96,3 +97,12 @@ PROCESSING_SUBSYSTEM_DEF(station)
for(var/i in trait_instance.blacklist)
var/datum/station_trait/trait_to_remove = i
selectable_traits_by_types[initial(trait_to_remove.trait_type)] -= trait_to_remove
/// Update station trait lobby buttons for clients who joined before we initialised this subsystem
/datum/controller/subsystem/processing/station/proc/display_lobby_traits()
for (var/mob/dead/new_player/player as anything in GLOB.new_player_list)
var/datum/hud/new_player/observer_hud = player.hud_used
if (!istype(observer_hud))
continue
observer_hud.add_station_trait_buttons()
observer_hud.show_hud(observer_hud.hud_version)
+1
View File
@@ -236,6 +236,7 @@ SUBSYSTEM_DEF(ticker)
var/can_continue = FALSE
can_continue = SSdynamic.pre_setup() //Choose antagonists
CHECK_TICK
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_JOBS_ASSIGNED, src)
can_continue = can_continue && SSjob.DivideOccupations() //Distribute jobs
CHECK_TICK
@@ -30,7 +30,7 @@
src.ban_type = ban_type
src.assumed_control_message = assumed_control_message || "You are [parent]!"
src.extra_control_checks = extra_control_checks
src.after_assumed_control= after_assumed_control
src.after_assumed_control = after_assumed_control
var/mob/mob_parent = parent
LAZYADD(GLOB.joinable_mobs[format_text("[initial(mob_parent.name)]")], mob_parent)
@@ -42,9 +42,10 @@
. = ..()
RegisterSignal(parent, COMSIG_ATOM_ATTACK_GHOST, PROC_REF(on_ghost_clicked))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examined))
RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(on_login))
/datum/component/ghost_direct_control/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_ATOM_EXAMINE))
UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_ATOM_EXAMINE, COMSIG_MOB_LOGIN))
return ..()
/datum/component/ghost_direct_control/Destroy(force, silent)
@@ -138,3 +139,8 @@
to_chat(new_body, span_boldnotice(assumed_control_message))
after_assumed_control?.Invoke(harbinger)
qdel(src)
/// When someone else assumes control via some other means, get rid of our component
/datum/component/ghost_direct_control/proc/on_login()
SIGNAL_HANDLER
qdel(src)
+57 -9
View File
@@ -1,3 +1,6 @@
/// Station traits displayed in the lobby
GLOBAL_LIST_EMPTY(lobby_station_traits)
///Base class of station traits. These are used to influence rounds in one way or the other by influencing the levers of the station.
/datum/station_trait
///Name of the trait
@@ -22,6 +25,10 @@
var/trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
/// Whether or not this trait can be reverted by an admin
var/can_revert = TRUE
/// If set to true we'll show a button on the lobby to notify people about this trait
var/sign_up_button = FALSE
/// Lobby buttons controlled by this trait
var/list/lobby_buttons = list()
/// The ID that we look for in dynamic.json. Not synced with 'name' because I can already see this go wrong
var/dynamic_threat_id
/// If ran during dynamic, do we reduce the total threat? Will be overriden by config if set
@@ -34,7 +41,8 @@
if(threat_reduction)
GLOB.dynamic_station_traits[src] = threat_reduction
if(sign_up_button)
GLOB.lobby_station_traits += src
if(trait_processes)
START_PROCESSING(SSstation, src)
if(trait_to_give)
@@ -43,14 +51,10 @@
/datum/station_trait/Destroy()
SSstation.station_traits -= src
GLOB.dynamic_station_traits.Remove(src)
destroy_lobby_buttons()
return ..()
/// Proc ran when round starts. Use this for roundstart effects.
/datum/station_trait/proc/on_round_start()
SIGNAL_HANDLER
return
///type of info the centcom report has on this trait, if any.
/// Returns the type of info the centcom report has on this trait, if any.
/datum/station_trait/proc/get_report()
return "<i>[name]</i> - [report_message]"
@@ -64,7 +68,7 @@
qdel(src)
///Called by decals if they can be colored, to see if we got some cool colors for them. Only takes the first station trait
/// Called by decals if they can be colored, to see if we got some cool colors for them. Only takes the first station trait
/proc/request_station_colors(atom/thing_to_color, pattern)
for(var/datum/station_trait/trait in SSstation.station_traits)
var/decal_color = trait.get_decal_color(thing_to_color, pattern || PATTERN_DEFAULT)
@@ -72,6 +76,50 @@
return decal_color
return null
///Return a color for the decals, if any
/// Return a color for the decals, if any
/datum/station_trait/proc/get_decal_color(thing_to_color, pattern)
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()
return sign_up_button && !SSticker.HasRoundStarted()
/// Apply any additional handling we need to our lobby button
/datum/station_trait/proc/setup_lobby_button(atom/movable/screen/lobby/button/sign_up/lobby_button)
SHOULD_CALL_PARENT(TRUE)
lobby_buttons |= lobby_button
RegisterSignal(lobby_button, COMSIG_ATOM_UPDATE_ICON, PROC_REF(on_lobby_button_update_icon))
RegisterSignal(lobby_button, COMSIG_CLICK, PROC_REF(on_lobby_button_click))
RegisterSignal(lobby_button, COMSIG_QDELETING, PROC_REF(on_lobby_button_destroyed))
lobby_button.update_appearance(UPDATE_ICON)
/// Called when our lobby button is clicked on
/datum/station_trait/proc/on_lobby_button_click(atom/movable/screen/lobby/button/sign_up/lobby_button, location, control, params, mob/dead/new_player/user)
SIGNAL_HANDLER
return
/// Called when our lobby button tries to update its appearance
/datum/station_trait/proc/on_lobby_button_update_icon(atom/movable/screen/lobby/button/sign_up/lobby_button, updates)
SIGNAL_HANDLER
return
/// Don't hold references to deleted buttons
/datum/station_trait/proc/on_lobby_button_destroyed(atom/movable/screen/lobby/button/sign_up/lobby_button)
SIGNAL_HANDLER
lobby_buttons -= lobby_button
/// Proc ran when round starts. Use this for roundstart effects. By default we clean up our buttons here.
/datum/station_trait/proc/on_round_start()
SIGNAL_HANDLER
destroy_lobby_buttons()
/// Remove all of our active lobby buttons
/datum/station_trait/proc/destroy_lobby_buttons()
for (var/atom/movable/screen/button as anything in lobby_buttons)
var/mob/hud_owner = button.get_mob()
qdel(button)
if (QDELETED(hud_owner))
continue
var/datum/hud/using_hud = hud_owner.hud_used
using_hud?.show_hud(using_hud?.hud_version)
lobby_buttons = list()
+95
View File
@@ -0,0 +1,95 @@
/**
* A station trait which enables a temporary job
* Generally speaking these should always all be mutually exclusive, don't have too many at once
*/
/datum/station_trait/job
sign_up_button = TRUE
trait_flags = STATION_TRAIT_ABSTRACT
/// What tooltip to show on the button
var/button_desc = "Sign up to gain some kind of unusual job, not available in most rounds."
/// Type of job to enable
var/job_to_add = /datum/job/clown
/// Who signed up to this in the lobby
var/list/lobby_candidates
/datum/station_trait/job/New()
. = ..()
blacklist += subtypesof(/datum/station_trait/job) - type // All but ourselves
RegisterSignal(SSdcs, COMSIG_GLOB_PRE_JOBS_ASSIGNED, PROC_REF(pre_jobs_assigned))
/datum/station_trait/job/setup_lobby_button(atom/movable/screen/lobby/button/sign_up/lobby_button)
RegisterSignal(lobby_button, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_lobby_button_update_overlays))
lobby_button.desc = button_desc
return ..()
/datum/station_trait/job/on_lobby_button_click(atom/movable/screen/lobby/button/sign_up/lobby_button, location, control, params, mob/dead/new_player/user)
if (LAZYFIND(lobby_candidates, user))
LAZYREMOVE(lobby_candidates, user)
else
LAZYADD(lobby_candidates, user)
/datum/station_trait/job/on_lobby_button_update_icon(atom/movable/screen/lobby/button/sign_up/lobby_button, updates)
if (LAZYFIND(lobby_candidates, lobby_button.get_mob()))
lobby_button.base_icon_state = "signup_on"
else
lobby_button.base_icon_state = "signup"
/// Add an overlay based on whether you are actively signed up for this role
/datum/station_trait/job/proc/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays)
SIGNAL_HANDLER
overlays += LAZYFIND(lobby_candidates, lobby_button.get_mob()) ? "tick" : "cross"
/// Called before we start assigning roles, assign ours first
/datum/station_trait/job/proc/pre_jobs_assigned()
SIGNAL_HANDLER
sign_up_button = FALSE
destroy_lobby_buttons()
for (var/mob/dead/new_player/signee as anything in lobby_candidates)
if (isnull(signee) || !signee.client || !signee.mind || signee.ready != PLAYER_READY_TO_PLAY)
LAZYREMOVE(lobby_candidates, signee)
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()
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++
/// Adds a gorilla to the cargo department, replacing the sloth and the mech
/datum/station_trait/job/cargorilla
name = "Cargo Gorilla"
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?
job_to_add = /datum/job/cargo_gorilla
trait_flags = STATION_TRAIT_MAP_UNRESTRICTED
/datum/station_trait/job/cargorilla/New()
. = ..()
RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(replace_cargo))
/datum/station_trait/job/cargorilla/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays)
. = ..()
overlays += LAZYFIND(lobby_candidates, lobby_button.get_mob()) ? "gorilla_on" : "gorilla_off"
/// Remove the cargo equipment and personnel that are being replaced by a gorilla.
/datum/station_trait/job/cargorilla/proc/replace_cargo(datum/source)
SIGNAL_HANDLER
var/mob/living/basic/sloth/cargo_sloth = GLOB.cargo_sloth
if(isnull(cargo_sloth))
lobby_candidates = list()
destroy_lobby_buttons() // Sorry folks
sign_up_button = FALSE
return
// hmm our sloth looks funny today
qdel(cargo_sloth)
// monkey carries the crates, the age of robot is over
if(GLOB.cargo_ripley)
qdel(GLOB.cargo_ripley)
@@ -123,7 +123,7 @@
/datum/station_trait/overflow_job_bureaucracy/proc/set_overflow_job_override(datum/source)
SIGNAL_HANDLER
var/datum/job/picked_job = pick(SSjob.joinable_occupations)
var/datum/job/picked_job = pick(SSjob.get_valid_overflow_jobs())
chosen_job_name = lowertext(picked_job.title) // like Chief Engineers vs like chief engineers
SSjob.set_overflow_role(picked_job.type)
@@ -141,60 +141,6 @@
var/new_colored_assistant_type = pick(subtypesof(/datum/colored_assistant) - get_configured_colored_assistant_type())
GLOB.colored_assistant = new new_colored_assistant_type
/datum/station_trait/cargorilla
name = "Cargo Gorilla"
trait_type = STATION_TRAIT_NEUTRAL
weight = 1
show_in_report = FALSE // Selective attention test. Did you spot the gorilla?
/// The gorilla we created, we only hold this ref until the round starts.
var/mob/living/basic/gorilla/cargorilla/cargorilla
/datum/station_trait/cargorilla/New()
. = ..()
RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(replace_cargo))
/// Replace some cargo equipment and 'personnel' with a gorilla.
/datum/station_trait/cargorilla/proc/replace_cargo(datum/source)
SIGNAL_HANDLER
var/mob/living/basic/sloth/cargo_sloth = GLOB.cargo_sloth
if(isnull(cargo_sloth))
return
cargorilla = new(cargo_sloth.loc)
cargorilla.name = cargo_sloth.name
// We do a poll on roundstart, don't let ghosts in early
INVOKE_ASYNC(src, PROC_REF(make_id_for_gorilla))
// hm our sloth looks funny today
qdel(cargo_sloth)
// monkey carries the crates, the age of robot is over
if(GLOB.cargo_ripley)
qdel(GLOB.cargo_ripley)
/// Makes an ID card for the gorilla
/datum/station_trait/cargorilla/proc/make_id_for_gorilla()
var/obj/item/card/id/advanced/cargo_gorilla/gorilla_id = new(cargorilla.loc)
gorilla_id.registered_name = cargorilla.name
gorilla_id.update_label()
cargorilla.put_in_hands(gorilla_id, del_on_fail = TRUE)
/datum/station_trait/cargorilla/on_round_start()
if(!cargorilla)
return
addtimer(CALLBACK(src, PROC_REF(get_ghost_for_gorilla), cargorilla), 12 SECONDS) // give ghosts a bit of time to funnel in
cargorilla = null
/// Get us a ghost for the gorilla.
/datum/station_trait/cargorilla/proc/get_ghost_for_gorilla(mob/living/basic/gorilla/cargorilla/gorilla)
if(QDELETED(gorilla))
return
gorilla.poll_for_gorilla()
/datum/station_trait/birthday
name = "Employee Birthday"
trait_type = STATION_TRAIT_NEUTRAL
@@ -32,6 +32,8 @@
var/list/jobs = list()
for (var/datum/job/job as anything in SSjob.joinable_occupations)
if (job.job_flags & JOB_LATEJOIN_ONLY)
continue
var/datum/job_department/department_type = job.department_for_prefs || job.departments_list?[1]
if (isnull(department_type))
stack_trace("[job] does not have a department set, yet is a joinable occupation!")
@@ -86,6 +88,8 @@
var/list/job_required_experience = list()
for (var/datum/job/job as anything in SSjob.all_occupations)
if (job.job_flags & JOB_LATEJOIN_ONLY)
continue
var/required_playtime_remaining = job.required_playtime_remaining(user.client)
if (required_playtime_remaining)
job_required_experience[job.title] = list(
+6 -10
View File
@@ -13,19 +13,15 @@
priority_announce("A recent bureaucratic error in the Organic Resources Department may result in personnel shortages in some departments and redundant staffing in others.", "Paperwork Mishap Alert")
/datum/round_event/bureaucratic_error/start()
var/list/jobs = SSjob.joinable_occupations.Copy()
if(prob(33)) // Only allows latejoining as a single role. Add latejoin AI bluespace pods for fun later.
var/list/jobs = SSjob.get_valid_overflow_jobs()
if(prob(33)) // Only allows latejoining as a single role.
var/datum/job/overflow = pick_n_take(jobs)
overflow.spawn_positions = -1
overflow.total_positions = -1 // Ensures infinite slots as this role. Assistant will still be open for those that cant play it.
for(var/job in jobs)
var/datum/job/current = job
if(!current.allow_bureaucratic_error)
continue
current.total_positions = 0
else // Adds/removes a random amount of job slots from all jobs.
for(var/datum/job/current as anything in jobs)
if(!current.allow_bureaucratic_error)
continue
var/ran = rand(-2,4)
current.total_positions = max(current.total_positions + ran, 0)
return
// Adds/removes a random amount of job slots from all jobs.
for(var/datum/job/current as anything in jobs)
current.total_positions = max(current.total_positions + rand(-2,4), 0)
@@ -0,0 +1,50 @@
/datum/job/cargo_gorilla
title = JOB_CARGO_GORILLA
description = "Assist the supply department by moving freight and disposing of unwanted fruits."
department_head = list(JOB_QUARTERMASTER)
faction = FACTION_STATION
total_positions = 0
spawn_positions = 0
supervisors = SUPERVISOR_QM
spawn_type = /mob/living/basic/gorilla/cargorilla
config_tag = "CARGO_GORILLA"
random_spawns_possible = FALSE
display_order = JOB_DISPLAY_ORDER_CARGO_GORILLA
departments_list = list(/datum/job_department/cargo)
mail_goodies = list(
/obj/item/food/grown/banana = 1,
)
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
/datum/job/cargo_gorilla/get_roundstart_spawn_point()
if (length(GLOB.gorilla_start))
return pick(GLOB.gorilla_start)
return ..()
/datum/job/cargo_gorilla/get_spawn_mob(client/player_client, atom/spawn_point)
if (!player_client)
return
var/mob/living/the_big_man = new spawn_type(get_turf(spawn_point))
the_big_man.fully_replace_character_name(the_big_man.real_name, pick(GLOB.cargorilla_names))
return the_big_man
/datum/job/cargo_gorilla/after_spawn(mob/living/spawned, client/player_client)
. = ..()
// Gorilla with a wage, what's he buyin?
var/datum/bank_account/bank_account = new(spawned.real_name, src)
bank_account.payday(STARTING_PAYCHECKS, TRUE)
bank_account.replaceable = FALSE
spawned.add_mob_memory(/datum/memory/key/account, remembered_id = bank_account.account_id)
var/obj/item/card/id/advanced/cargo_gorilla/gorilla_id = new(spawned.loc)
gorilla_id.registered_name = spawned.name
gorilla_id.update_label()
gorilla_id.registered_account = bank_account
bank_account.bank_cards += gorilla_id
spawned.put_in_hands(gorilla_id, del_on_fail = TRUE)
to_chat(spawned, span_boldnotice("You are Cargorilla, a pacifist friend of the station and carrier of freight."))
to_chat(spawned, span_notice("You can pick up crates by clicking on them, and drop them by clicking on the ground."))
spawned.mind.special_role = "Cargorilla"
@@ -80,6 +80,8 @@ GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new)
)
if(job_availability != JOB_AVAILABLE)
if (job_datum.job_flags & JOB_HIDE_WHEN_EMPTY)
continue
job_data["unavailable_reason"] = get_job_unavailable_error_message(job_availability, job_datum.title)
if(job_datum.total_positions < 0)
@@ -95,6 +97,7 @@ GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new)
/datum/latejoin_menu/ui_static_data(mob/user)
var/list/departments = list()
var/mob/dead/new_player/owner = user
for(var/datum/job_department/department as anything in SSjob.joinable_departments)
var/list/department_jobs = list()
@@ -108,6 +111,8 @@ GLOBAL_DATUM_INIT(latejoin_menu, /datum/latejoin_menu, new)
//Jobs under multiple departments should only be displayed if this is their first department or the command department
if(LAZYLEN(job_datum.departments_list) > 1 && job_datum.departments_list[1] != department.type && !(job_datum.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
continue
if((job_datum.job_flags & JOB_HIDE_WHEN_EMPTY) && owner.IsJobUnavailable(job_datum.title, latejoin = TRUE) != JOB_AVAILABLE)
continue
var/list/job_data = list(
"command" = !!(job_datum.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND),
@@ -158,31 +158,4 @@
ADD_TRAIT(src, TRAIT_PACIFISM, INNATE_TRAIT)
AddComponent(/datum/component/crate_carrier)
/mob/living/basic/gorilla/cargorilla/death(gibbed)
var/datum/component/potential_component = GetComponent(/datum/component/ghost_direct_control)
if(!QDELETED(potential_component))
qdel(potential_component)
return ..()
/**
* Poll ghosts for control of the gorilla. Not added in init because we only want to poll when the round starts.
* Preferably in future we can replace this with a popup on the lobby to queue to become a gorilla.
*/
/mob/living/basic/gorilla/cargorilla/proc/poll_for_gorilla()
AddComponent(\
/datum/component/ghost_direct_control,\
poll_candidates = TRUE,\
poll_length = 30 SECONDS,\
role_name = "Cargorilla",\
assumed_control_message = "You are Cargorilla, a pacifist friend of the station and carrier of freight.",\
poll_ignore_key = POLL_IGNORE_CARGORILLA,\
after_assumed_control = CALLBACK(src, PROC_REF(became_player_controlled)),\
)
/// Called once a ghost assumes control
/mob/living/basic/gorilla/cargorilla/proc/became_player_controlled()
mind.set_assigned_role(SSjob.GetJobType(/datum/job/cargo_technician))
mind.special_role = "Cargorilla"
to_chat(src, span_notice("You can pick up crates by clicking on them, and drop them by clicking on the ground."))
#undef GORILLA_HANDS_LAYER
+2 -1
View File
@@ -44,11 +44,12 @@ GLOBAL_DATUM(cargo_sloth, /mob/living/basic/sloth)
AddElement(/datum/element/ai_retaliate)
AddComponent(/datum/component/tree_climber)
if(!mapload || isnull(GLOB.cargo_sloth) || !is_station_level(z))
if(!mapload || !isnull(GLOB.cargo_sloth) || !is_station_level(z))
return
// If someone adds non-cargo sloths to maps we'll have a problem but we're fine for now
GLOB.cargo_sloth = src
GLOB.gorilla_start += get_turf(src)
/mob/living/basic/sloth/Destroy()
if(GLOB.cargo_sloth == src)
+12 -12
View File
@@ -107,22 +107,22 @@ Notes:
//Open a tooltip for user, at a location based on params
//Theme is a CSS class in tooltip.html, by default this wrapper chooses a CSS class based on the user's UI_style (Midnight, Plasmafire, Retro, etc)
//Includes sanity.checks
/proc/openToolTip(mob/user = null, atom/movable/tip_src = null, params = null,title = "",content = "",theme = "")
if(istype(user))
if(user.client && user.client.tooltips)
var/ui_style = user.client?.prefs?.read_preference(/datum/preference/choiced/ui_style)
if(!theme && ui_style)
theme = lowertext(ui_style)
if(!theme)
theme = "default"
user.client.tooltips.show(tip_src, params,title,content,theme)
/proc/openToolTip(mob/user = null, atom/movable/tip_src = null, params = null, title = "", content = "", theme = "")
if(!istype(user) || !user.client?.tooltips)
return
var/ui_style = user.client?.prefs?.read_preference(/datum/preference/choiced/ui_style)
if(!theme && ui_style)
theme = lowertext(ui_style)
if(!theme)
theme = "default"
user.client.tooltips.show(tip_src, params, title, content, theme)
//Arbitrarily close a user's tooltip
//Includes sanity checks.
/proc/closeToolTip(mob/user)
if(istype(user))
if(user.client && user.client.tooltips)
user.client.tooltips.hide()
if(!istype(user) || !user.client?.tooltips)
return
user.client.tooltips.hide()
Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

+7
View File
@@ -0,0 +1,7 @@
Cala
Cerchak
Citrus
Coco
Grodd
Paperwork
Winston
+2
View File
@@ -1729,6 +1729,7 @@
#include "code\datums\skills\mining.dm"
#include "code\datums\station_traits\_station_trait.dm"
#include "code\datums\station_traits\admin_panel.dm"
#include "code\datums\station_traits\job_traits.dm"
#include "code\datums\station_traits\negative_traits.dm"
#include "code\datums\station_traits\neutral_traits.dm"
#include "code\datums\station_traits\positive_traits.dm"
@@ -4116,6 +4117,7 @@
#include "code\modules\jobs\job_types\bartender.dm"
#include "code\modules\jobs\job_types\botanist.dm"
#include "code\modules\jobs\job_types\captain.dm"
#include "code\modules\jobs\job_types\cargo_gorilla.dm"
#include "code\modules\jobs\job_types\cargo_technician.dm"
#include "code\modules\jobs\job_types\chemist.dm"
#include "code\modules\jobs\job_types\chief_engineer.dm"
@@ -10,6 +10,7 @@ export const JOB2ICON = {
Bitrunner: 'gamepad',
Botanist: 'seedling',
Captain: 'crown',
'Cargo Gorilla': 'paw',
'Cargo Technician': 'box',
'CentCom Commander': 'star',
'CentCom Head Intern': 'pen-fancy',