From 4747747dc2b89e6f07e392f64180328870f46576 Mon Sep 17 00:00:00 2001 From: Zephyr <12817816+ZephyrTFA@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:27:14 -0400 Subject: [PATCH] Players can no longer open additional job slots (#2824) ## Changelog :cl: balance: Only admins can open job slots fix: Cryoing correctly opens a job slot code: Refactored ShipOwner into tsx /:cl: --- code/game/machinery/cryopod.dm | 31 ++++++--- .../overmap/ships/controlled_ship_datum.dm | 11 ++- code/modules/overmap/ships/owner_action.dm | 52 +++++++++++--- .../{ShipOwner.js => ShipOwner.tsx} | 67 ++++++++++++++++--- 4 files changed, 132 insertions(+), 29 deletions(-) rename tgui/packages/tgui/interfaces/{ShipOwner.js => ShipOwner.tsx} (81%) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 22b15f162b25..6b63a3ae83b8 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -303,15 +303,28 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/cryopod/retro, 17) /obj/machinery/cryopod/proc/despawn_occupant() var/mob/living/mob_occupant = occupant - if(linked_ship) - if(mob_occupant.job in linked_ship.current_ship.job_slots) - linked_ship.current_ship.job_slots[mob_occupant.job]++ - - if(mob_occupant.mind && mob_occupant.mind.assigned_role) - //Handle job slot/tater cleanup. - if(LAZYLEN(mob_occupant.mind.objectives)) - mob_occupant.mind.objectives.Cut() - mob_occupant.mind.special_role = null + if(!isnull(mob_occupant.mind.original_ship)) + var/datum/overmap/ship/controlled/original_ship_instance = mob_occupant.mind.original_ship.resolve() + + var/job_identifier = mob_occupant.job + + var/datum/job/crew_job + for(var/datum/job/job as anything in original_ship_instance.job_slots) + if(job.name == job_identifier) + crew_job = job + break + + if(isnull(crew_job)) + message_admins(span_warning("Failed to identify the job of [key_name_admin(mob_occupant)] belonging to [original_ship_instance.name] at [loc_name(src)].")) + else + original_ship_instance.job_slots[crew_job]++ + original_ship_instance.job_holder_refs[crew_job] -= WEAKREF(mob_occupant) + + if(mob_occupant.mind && mob_occupant.mind.assigned_role) + //Handle job slot/tater cleanup. + if(LAZYLEN(mob_occupant.mind.objectives)) + mob_occupant.mind.objectives.Cut() + mob_occupant.mind.special_role = null // Delete them from datacore. var/announce_rank = null diff --git a/code/modules/overmap/ships/controlled_ship_datum.dm b/code/modules/overmap/ships/controlled_ship_datum.dm index 4d3152bc7afd..bee32994e992 100644 --- a/code/modules/overmap/ships/controlled_ship_datum.dm +++ b/code/modules/overmap/ships/controlled_ship_datum.dm @@ -36,9 +36,13 @@ var/list/datum/mission/missions /// The maximum number of currently active missions that a ship may take on. var/max_missions = 2 - /// Manifest list of people on the ship + + /// Manifest list of people on the ship. Indexed by mob REAL NAME. value is JOB INSTANCE var/list/manifest = list() + /// List of mob refs indexed by their job instance + var/list/datum/weakref/job_holder_refs = list() + var/list/datum/mind/owner_candidates /// The mob of the current ship owner. Tracking mostly uses this; that lets us pick up on logouts, which let us @@ -130,6 +134,7 @@ if(!QDELETED(shipkey)) QDEL_NULL(shipkey) QDEL_LIST(manifest) + job_holder_refs.Cut() job_slots.Cut() blacklisted.Cut() for(var/a_key in applications) @@ -298,6 +303,10 @@ if(!owner_mob) set_owner_mob(H) + if(!(human_job in job_holder_refs)) + job_holder_refs[human_job] = list() + job_holder_refs[human_job] += WEAKREF(H) + /datum/overmap/ship/controlled/proc/set_owner_mob(mob/new_owner) if(owner_mob) // we (hopefully) don't have to hook qdeletion, diff --git a/code/modules/overmap/ships/owner_action.dm b/code/modules/overmap/ships/owner_action.dm index 04169054b2e6..96c97bdf5423 100644 --- a/code/modules/overmap/ships/owner_action.dm +++ b/code/modules/overmap/ships/owner_action.dm @@ -65,6 +65,34 @@ ui = new(user, src, "ShipOwner", name) ui.open() +/datum/action/ship_owner/proc/allow_job_slot_increase(datum/job/job_target) + var/default_slots = parent_ship.source_template.job_slots[job_target] + var/current_slots = parent_ship.job_slots[job_target] + + var/used_slots = 0 + var/job_holders = parent_ship.job_holder_refs[job_target] + + for(var/datum/weakref/job_holder_ref as anything in job_holders) + var/mob/living/job_holder = job_holder_ref.resolve() + if(isnull(job_holder)) + continue + + if(job_holder.client) + used_slots += 1 + continue + + var/mob/dead/observer/job_holder_ghost + for(var/mob/dead/observer/ghost in GLOB.dead_mob_list) + if(ghost.mind == job_holder.mind) + job_holder_ghost = ghost + break + if(!isnull(job_holder_ghost)) + used_slots += 1 + continue + + var/actual_slots = current_slots + used_slots + return actual_slots < default_slots + /datum/action/ship_owner/ui_data(mob/user) . = list() .["memo"] = parent_ship.memo @@ -72,6 +100,7 @@ .["pending"] = FALSE .["joinMode"] = parent_ship.join_mode .["cooldown"] = COOLDOWN_TIMELEFT(parent_ship, job_slot_adjustment_cooldown) + .["isAdmin"] = !!user.client?.holder .["applications"] = list() for(var/a_key as anything in parent_ship.applications) var/datum/ship_application/app = parent_ship.applications[a_key] @@ -85,6 +114,10 @@ text = app.app_msg, status = app.status )) + var/list/job_increase_allowed = list() + for(var/datum/job/job as anything in parent_ship.job_slots) + job_increase_allowed[job.name] = allow_job_slot_increase(job) + .["jobIncreaseAllowed"] = job_increase_allowed /datum/action/ship_owner/ui_static_data(mob/user) . = list() @@ -197,18 +230,21 @@ if(!target_job || target_job.officer || !COOLDOWN_FINISHED(parent_ship, job_slot_adjustment_cooldown)) return TRUE + var/change_amount = params["delta"] + if(change_amount > 0 && !allow_job_slot_increase(target_job)) + if(!user.client.holder) + to_chat(user, span_warning("You cannot increase the number of slots for this job.")) + return TRUE + message_admins("[key_name_admin(user)] has increased the number of slots for [target_job.name] on [parent_ship.name] by [change_amount].") + + var/new_amount = parent_ship.job_slots[target_job] + change_amount var/job_default_slots = parent_ship.source_template.job_slots[target_job] var/job_max_slots = min(job_default_slots * 2, job_default_slots + 3) - var/new_slots = parent_ship.job_slots[target_job] + params["delta"] - if(new_slots < 0 || new_slots > job_max_slots) + if(new_amount < 0 || new_amount > job_max_slots) return TRUE - var/cooldown_time = 5 SECONDS - if(params["delta"] > 0 && new_slots > job_default_slots) - cooldown_time = 2 MINUTES - COOLDOWN_START(parent_ship, job_slot_adjustment_cooldown, cooldown_time * cooldown_coeff) - - parent_ship.job_slots[target_job] = new_slots + COOLDOWN_START(parent_ship, job_slot_adjustment_cooldown, (5 SECONDS) * cooldown_coeff) + parent_ship.job_slots[target_job] = new_amount update_static_data(user) return TRUE diff --git a/tgui/packages/tgui/interfaces/ShipOwner.js b/tgui/packages/tgui/interfaces/ShipOwner.tsx similarity index 81% rename from tgui/packages/tgui/interfaces/ShipOwner.js rename to tgui/packages/tgui/interfaces/ShipOwner.tsx index b8fa0f6e97fc..3beab28f48b0 100644 --- a/tgui/packages/tgui/interfaces/ShipOwner.js +++ b/tgui/packages/tgui/interfaces/ShipOwner.tsx @@ -9,6 +9,40 @@ import { } from '../components'; import { Window } from '../layouts'; +type ShipOwnerData = { + crew: [CrewData]; + jobs: [JobData]; + jobIncreaseAllowed: [string]; + memo: string; + pending: boolean; + joinMode: string; + cooldown: number; + applications: [ApplicationData]; + isAdmin: boolean; +}; + +type ApplicationData = { + ref: string; + key: string; + name: string; + text: string; + status: string; +}; + +type CrewData = { + ref: string; + name: string; + allowed: boolean; +}; + +type JobData = { + ref: string; + name: string; + slots: number; + max: number; + def: number; +}; + export const ShipOwner = (props, context) => { return ( @@ -19,8 +53,8 @@ export const ShipOwner = (props, context) => { ); }; -const ShipOwnerContent = (props, context) => { - const { act, data } = useBackend(context); +const ShipOwnerContent = (_, context: any) => { + const { act, data } = useBackend(context); const [tab, setTab] = useLocalState(context, 'tab', 1); const { crew = [], @@ -30,6 +64,8 @@ const ShipOwnerContent = (props, context) => { joinMode, cooldown = 1, applications = [], + isAdmin, + jobIncreaseAllowed = [], } = data; return (
{ Message Status - {applications.map((app) => ( + {applications.map((app: ApplicationData) => ( {app.key} {app.name} @@ -144,16 +180,16 @@ const ShipOwnerContent = (props, context) => { Can be owner Transfer Ownership - {crew.map((mind) => ( - - {mind.name} + {crew.map((crew_data: CrewData) => ( + + {crew_data.name} act('toggleCandidate', { - ref: mind.ref, + ref: crew_data.ref, }) } /> @@ -163,7 +199,7 @@ const ShipOwnerContent = (props, context) => { content="Transfer Owner" onClick={() => act('transferOwner', { - ref: mind.ref, + ref: crew_data.ref, }) } /> @@ -184,13 +220,22 @@ const ShipOwnerContent = (props, context) => { Job Name Slots - {jobs.map((job) => ( + {jobs.map((job: JobData) => ( {job.name}