From 85e0a614b4115b2715637310791b24b1a00b0638 Mon Sep 17 00:00:00 2001 From: Programs-The-Station <100493881+Programs-The-Station@users.noreply.github.com> Date: Tue, 3 Sep 2024 21:00:21 -0500 Subject: [PATCH] Adds Positronic Brains to Latejoin Menu (#11162) * Fixes Posi Created in null bug And Dehardcode random PAI * Adds Posibrain to Latejoin Menu * Posi Job now subtype of cyborg. Failing to Spawn as Posi Kicks player back to lobby. * No more Latejoin Cyborg * Adds after_spawn_silicon per request * Forgot Debug Code Comment * Requested Changes * Remove from Prefs * Procify * Remove Src * Missed a Job * Fix stupid problem that slipped testing * Requested Changes. DM is weird. * Update code/modules/mob/living/brain/posibrain.dm Co-authored-by: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> * Update code/modules/mob/living/brain/posibrain.dm Co-authored-by: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> * Update code/modules/mob/living/brain/posibrain.dm Co-authored-by: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> * Revert "Fix stupid problem that slipped testing" This reverts commit f3f1dd1b7fbec3c8fa265bd70ac3c7d3251877ea. * Revert "Fixes Posi Created in null bug" This reverts commit 39d0309caae0a831ff7585eb00c6a6121048f8eb. * Changes as requested by Bacon, refactored unused code. * Whoops Forgot a Path * Refactors Posibrain job back into own type and not subtype of cyborg * Update Return * Whoopsies forgot a debug comment out * Requested changes * Removed report to coders line, since posi's have known "fail" states * Fix Spawning Logic --------- Co-authored-by: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Co-authored-by: Rukofamicom --- beestation.dme | 1 + code/__DEFINES/jobs.dm | 1 + code/controllers/subsystem/job.dm | 10 +++- code/modules/jobs/job_types/_job.dm | 1 + code/modules/jobs/job_types/cyborg.dm | 2 +- code/modules/jobs/job_types/posibrain.dm | 57 ++++++++++++++++++++++ code/modules/jobs/jobs.dm | 1 + code/modules/mob/living/brain/posibrain.dm | 56 ++++++++++++++++++--- 8 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 code/modules/jobs/job_types/posibrain.dm diff --git a/beestation.dme b/beestation.dme index 598b90abcda73..a23ea0463ef16 100644 --- a/beestation.dme +++ b/beestation.dme @@ -2849,6 +2849,7 @@ #include "code\modules\jobs\job_types\medical_doctor.dm" #include "code\modules\jobs\job_types\mime.dm" #include "code\modules\jobs\job_types\paramedic.dm" +#include "code\modules\jobs\job_types\posibrain.dm" #include "code\modules\jobs\job_types\quartermaster.dm" #include "code\modules\jobs\job_types\research_director.dm" #include "code\modules\jobs\job_types\roboticist.dm" diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 7188b23913772..21bd824b82320 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -128,6 +128,7 @@ // Silicon #define JOB_NAME_AI "AI" #define JOB_NAME_CYBORG "Cyborg" +#define JOB_NAME_POSIBRAIN "Positronic Brain" #define JOB_NAME_PAI "Personal AI" // ERTs diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 5f66bd10bb517..992c7e6d37169 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -35,6 +35,7 @@ SUBSYSTEM_DEF(job) JOB_NAME_AI, JOB_NAME_ASSISTANT, JOB_NAME_CYBORG, + JOB_NAME_POSIBRAIN, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, @@ -553,7 +554,14 @@ SUBSYSTEM_DEF(job) newplayer.new_character = living_mob else M = living_mob - + else + if(!isnull(new_mob)) //Detect fail condition on equip + //if equip() is somehow able to fail, send them back to lobby + var/mob/dead/new_player/NP = new() + NP.ckey = M.client.ckey + qdel(M) + to_chat(M, "Error equipping [rank]. Returning to lobby.") + return null SSpersistence.antag_rep_change[M.client.ckey] += job.GetAntagRep() if(M.client.holder) diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 522502a2d25fc..c988a8c2d53c6 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -252,6 +252,7 @@ return antag_rep //Don't override this unless the job transforms into a non-human (Silicons do this for example) +//Returning FALSE is considered a failure. A null or mob return is a successful equip. /datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source) if(!H) return FALSE diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm index 44f340c3bd80d..9a36457dd2170 100644 --- a/code/modules/jobs/job_types/cyborg.dm +++ b/code/modules/jobs/job_types/cyborg.dm @@ -5,7 +5,7 @@ department_head_for_prefs = JOB_NAME_AI auto_deadmin_role_flags = DEADMIN_POSITION_SILICON faction = "Station" - total_positions = 1 + total_positions = 0 spawn_positions = 1 supervisors = "your laws and the AI" //Nodrak selection_color = "#ddffdd" diff --git a/code/modules/jobs/job_types/posibrain.dm b/code/modules/jobs/job_types/posibrain.dm new file mode 100644 index 0000000000000..e4c5b396803ce --- /dev/null +++ b/code/modules/jobs/job_types/posibrain.dm @@ -0,0 +1,57 @@ +GLOBAL_LIST_EMPTY(on_station_posis) + +/datum/job/posibrain + title = JOB_NAME_POSIBRAIN + description = "Follow your AI's interpretation of your laws above all else, or your own interpretation if not connected to an AI. Choose one of many modules with different tools, ask robotics for maintenance and upgrades." + department_for_prefs = DEPT_BITFLAG_SILICON + department_head_for_prefs = JOB_NAME_AI + auto_deadmin_role_flags = DEADMIN_POSITION_SILICON + faction = "Station" + total_positions = 0 + spawn_positions = 0 + supervisors = "your laws" //No AI yet as you are just a cube + selection_color = "#ddffdd" + minimal_player_age = 21 + exp_requirements = 120 + exp_type = EXP_TYPE_CREW + random_spawns_possible = FALSE + + display_order = JOB_DISPLAY_ORDER_CYBORG + departments = DEPT_BITFLAG_SILICON + + show_in_prefs = FALSE //No reason to show in preferences + +/datum/job/posibrain/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source = null) + + var/obj/item/mmi/posibrain/P = pick(GLOB.on_station_posis) + + //Never show number of current posis + current_positions = 0 + + if(!P.activate(H)) //If we failed to activate a posi, kick them back to the lobby. + to_chat(H, "Failed to Late Join as a Posibrain. Look higher in chat for the reason.") + return FALSE //Returning False is considered a failure, rather than null or a mob, which is a success. + + qdel(H) + return P + +/datum/job/posibrain/radio_help_message(mob/M) + to_chat(M, "Prefix your message with :b to speak with other cyborgs and AI.") + +/datum/job/posibrain/proc/check_add_posi_slot(obj/item/mmi/posibrain/pb) + var/turf/currentturf = get_turf(pb) + if( is_station_level(currentturf.z) ) + GLOB.on_station_posis |= pb + + //Update Job Quantities + //We should never show a posibrain as a filled job, so just make number of current positions zero + current_positions = 0 + total_positions = length(GLOB.on_station_posis) + +/datum/job/posibrain/proc/remove_posi_slot(obj/item/mmi/posibrain/pb) + GLOB.on_station_posis -= pb + + //Update Job Quantities + //We should never show a posibrain as a filled job, so just make number of current positions zero + current_positions = 0 + total_positions = length(GLOB.on_station_posis) diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm index 0c3764e702b3d..535880c30a30c 100644 --- a/code/modules/jobs/jobs.dm +++ b/code/modules/jobs/jobs.dm @@ -151,6 +151,7 @@ GLOBAL_LIST_INIT(security_lightup_areas, \ GLOBAL_LIST_INIT(nonhuman_positions, list( JOB_NAME_AI, JOB_NAME_CYBORG, + JOB_NAME_POSIBRAIN, ROLE_PAI )) diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm index a8e4655e2276e..826fade383784 100644 --- a/code/modules/mob/living/brain/posibrain.dm +++ b/code/modules/mob/living/brain/posibrain.dm @@ -85,33 +85,38 @@ GLOBAL_VAR(posibrain_notify_cooldown) //Two ways to activate a positronic brain. A clickable link in the ghost notif, or simply clicking the object itself. /obj/item/mmi/posibrain/proc/activate(mob/user) if(QDELETED(brainmob)) - return + return FALSE if(is_banned_from(user.ckey, ROLE_POSIBRAIN)) to_chat(user, "You are restricted from taking positronic brain spawns at this time.") - return + return FALSE if(user.client.get_exp_living(TRUE) <= MINUTES_REQUIRED_BASIC) to_chat(user, "You aren't allowed to take positronic brain spawns yet.") - return + return FALSE if(is_occupied() || QDELETED(brainmob) || QDELETED(src) || QDELETED(user)) - return + return FALSE if(user.ckey in GLOB.posi_key_list) to_chat(user, "Positronic brain spawns limited to 1 per round.") - return + return FALSE if(!(GLOB.ghost_role_flags & GHOSTROLE_SILICONS)) to_chat(user, "Central Command has temporarily outlawed posibrain sentience in this sector...") - return + return FALSE if(user.suiciding) //if they suicided, they're out forever. to_chat(user, "[src] fizzles slightly. Sadly it doesn't take those who suicided!") - return + return FALSE var/posi_ask = alert("Become a [name]? (Warning, You can no longer be cloned, and all past lives will be forgotten!)","Are you positive?","Yes","No") if(posi_ask != "Yes" || QDELETED(src)) - return + return FALSE if(brainmob.suiciding) //clear suicide status if the old occupant suicided. brainmob.set_suicide(FALSE) var/ckey = user.ckey if(transfer_personality(user)) GLOB.posi_key_list += ckey + var/datum/job/posibrain/pj = SSjob.GetJob(JOB_NAME_POSIBRAIN) + pj.remove_posi_slot(src) + + return TRUE + /obj/item/mmi/posibrain/transfer_identity(mob/living/carbon/C) name = "[initial(name)] ([C])" brainmob.name = C.real_name @@ -179,6 +184,11 @@ GLOBAL_VAR(posibrain_notify_cooldown) brainmob.real_name = brainmob.name brainmob.forceMove(src) brainmob.container = src + + //If we are on the station level, add it to the list of available posibrains. + var/datum/job/posibrain/pj = SSjob.GetJob(JOB_NAME_POSIBRAIN) + pj.check_add_posi_slot(src) + if(autoping) ping_ghosts("created", TRUE) @@ -195,3 +205,33 @@ GLOBAL_VAR(posibrain_notify_cooldown) icon_state = "[initial(icon_state)]-occupied" else icon_state = initial(icon_state) + +//This Proc triggers when the Z level changes. If the Posi enters the station level, add it to the Job list. +//If it leaves, remove it. +/obj/item/mmi/posibrain/onTransitZ(old_z, new_z) + . = ..() + + if(is_station_level(old_z) == is_station_level(new_z)) + //Early Return if we aren't entering or leaving station Z level. + return + + if(is_occupied()) + //No need to track occupied Posis + return + + var/datum/job/posibrain/pj = SSjob.GetJob(JOB_NAME_POSIBRAIN) + + //Posi was on station, now is not on station + if(is_station_level(new_z)) + pj.check_add_posi_slot(src) + else + pj.remove_posi_slot(src) + +/obj/item/mmi/posibrain/Destroy() + if(is_occupied()) + //No need to track occupied Posis + return ..() + + var/datum/job/posibrain/pj = SSjob.GetJob(JOB_NAME_POSIBRAIN) + pj.remove_posi_slot(src) + return ..()