diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index e32405cad5..9505248753 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -32,3 +32,5 @@ GLOBAL_LIST_EMPTY(all_languages)
GLOBAL_LIST_EMPTY(latejoiners) //CIT CHANGE - All latejoining people, for traitor-target purposes.
GLOBAL_LIST_EMPTY(sentient_disease_instances)
+
+GLOBAL_LIST_EMPTY(latejoin_ai_cores)
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 8e29207fe1..9fae8ac8a8 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -66,18 +66,18 @@ SUBSYSTEM_DEF(job)
SetupOccupations()
return type_occupations[jobtype]
-/datum/controller/subsystem/job/proc/AssignRole(mob/dead/new_player/player, rank, latejoin=0)
+/datum/controller/subsystem/job/proc/AssignRole(mob/dead/new_player/player, rank, latejoin = FALSE)
Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]")
if(player && player.mind && rank)
var/datum/job/job = GetJob(rank)
if(!job)
- return 0
+ return FALSE
if(jobban_isbanned(player, rank))
- return 0
+ return FALSE
if(!job.player_old_enough(player.client))
- return 0
+ return FALSE
if(job.required_playtime_remaining(player.client))
- return 0
+ return FALSE
var/position_limit = job.total_positions
if(!latejoin)
position_limit = job.spawn_positions
@@ -85,9 +85,9 @@ SUBSYSTEM_DEF(job)
player.mind.assigned_role = rank
unassigned -= player
job.current_positions++
- return 1
+ return TRUE
Debug("AR has failed, Player: [player], Rank: [rank]")
- return 0
+ return FALSE
/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level, flag)
@@ -224,6 +224,8 @@ SUBSYSTEM_DEF(job)
if(SSticker.triai)
for(var/datum/job/ai/A in occupations)
A.spawn_positions = 3
+ for(var/obj/effect/landmark/start/ai/secondary/S in GLOB.start_landmarks_list)
+ S.latejoin_active = TRUE
//Get the players who are ready
for(var/mob/dead/new_player/player in GLOB.player_list)
@@ -359,7 +361,7 @@ SUBSYSTEM_DEF(job)
return 1
//Gives the player the stuff he should have with his rank
-/datum/controller/subsystem/job/proc/EquipRank(mob/M, rank, joined_late=0)
+/datum/controller/subsystem/job/proc/EquipRank(mob/M, rank, joined_late = FALSE)
var/mob/dead/new_player/N
var/mob/living/H
if(!joined_late)
@@ -382,6 +384,7 @@ SUBSYSTEM_DEF(job)
if(locate(/mob/living) in sloc.loc)
continue
S = sloc
+ sloc.used = TRUE
break
if(length(GLOB.jobspawn_overrides[rank]))
S = pick(GLOB.jobspawn_overrides[rank])
@@ -396,9 +399,13 @@ SUBSYSTEM_DEF(job)
H.mind.assigned_role = rank
if(job)
+<<<<<<< HEAD
if(!job.dresscodecompliant)// CIT CHANGE - dress code compliance
equip_loadout(N, H) // CIT CHANGE - allows players to spawn with loadout items
var/new_mob = job.equip(H)
+=======
+ var/new_mob = job.equip(H, null, null, joined_late)
+>>>>>>> 0c27e22... Latejoin Silicons (#36560)
if(ismob(new_mob))
H = new_mob
if(!joined_late)
@@ -406,23 +413,28 @@ SUBSYSTEM_DEF(job)
else
M = H
- SSpersistence.antag_rep_change[M.client.ckey] += job.antag_rep
+ SSpersistence.antag_rep_change[M.client.ckey] += job.antag_rep
to_chat(M, "You are the [rank].")
- to_chat(M, "As the [rank] you answer directly to [job.supervisors]. Special circumstances may change this.")
- to_chat(M, "To speak on your departments radio, use the :h button. To see others, look closely at your headset.")
- if(job.req_admin_notify)
- to_chat(M, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.")
- if(CONFIG_GET(number/minimal_access_threshold))
- to_chat(M, "As this station was initially staffed with a [CONFIG_GET(flag/jobs_have_minimal_access) ? "full crew, only your job's necessities" : "skeleton crew, additional access may"] have been added to your ID card.")
+ if(job)
+ to_chat(M, "As the [rank] you answer directly to [job.supervisors]. Special circumstances may change this.")
+ to_chat(M, "To speak on your departments radio, use the :h button. To see others, look closely at your headset.")
+ if(job.req_admin_notify)
+ to_chat(M, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.")
+ if(CONFIG_GET(number/minimal_access_threshold))
+ to_chat(M, "As this station was initially staffed with a [CONFIG_GET(flag/jobs_have_minimal_access) ? "full crew, only your job's necessities" : "skeleton crew, additional access may"] have been added to your ID card.")
if(job && H)
+<<<<<<< HEAD
if(job.dresscodecompliant)// CIT CHANGE - dress code compliance
equip_loadout(N, H) // CIT CHANGE - allows players to spawn with loadout items
job.after_spawn(H, M)
equip_loadout(N, H, TRUE)//CIT CHANGE - makes players spawn with in-backpack loadout items properly. A little hacky but it works
//handle_roundstart_items(H, M.ckey, H.mind.assigned_role, H.mind.special_role) //CIT CHANGE - makes donators spawn with their items. This can safely be commented out when all of the donator items are migrated to the loadout system
+=======
+ job.after_spawn(H, M, joined_late)
+>>>>>>> 0c27e22... Latejoin Silicons (#36560)
return H
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 2db34bb897..c734699002 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -323,11 +323,6 @@ SUBSYSTEM_DEF(ticker)
mode.post_setup()
GLOB.start_state = new /datum/station_state()
GLOB.start_state.count()
- //Cleanup some stuff
- for(var/obj/effect/landmark/start/S in GLOB.landmarks_list)
- //Deleting Startpoints but we need the ai point to AI-ize people later
- if(S.delete_after_roundstart)
- qdel(S)
//assign crew objectives and generate miscreants
if(CONFIG_GET(flag/allow_extended_miscreants) && GLOB.master_mode == "extended")
@@ -341,6 +336,14 @@ SUBSYSTEM_DEF(ticker)
send2irc("Server", "Round [GLOB.round_id ? "#[GLOB.round_id]:" : "of"] [hide_mode ? "secret":"[mode.name]"] has started[allmins.len ? ".":" with no active admins online!"]")
setup_done = TRUE
+ for(var/i in GLOB.start_landmarks_list)
+ var/obj/effect/landmark/start/S = i
+ if(istype(S)) //we can not runtime here. not in this important of a proc.
+ S.after_round_start()
+ else
+ stack_trace("[S] [S.type] found in start landmarks list, which isn't a start landmark!")
+
+
//These callbacks will fire after roundstart key transfer
/datum/controller/subsystem/ticker/proc/OnRoundstart(datum/callback/cb)
if(!HasRoundStarted())
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index cb215ab7e4..0b502d4fe6 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -111,7 +111,7 @@
if(current)
current.transfer_observers_to(new_character) //transfer anyone observing the old character to the new one
current = new_character //associate ourself with our new body
- new_character.mind = src //and associate our new body with ourself
+ new_character.mind = src //and associate our new body with ourself
for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body
var/datum/antagonist/A = a
A.on_body_transfer(old_current, current)
@@ -311,7 +311,7 @@
else
uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
var/unlock_note
-
+
if(uplink_loc == R)
R.traitor_frequency = sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
@@ -331,7 +331,7 @@
if(!silent)
to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [P.traitor_unlock_degrees] from its starting position to unlock its hidden features.")
unlock_note = "Uplink Degrees: [P.traitor_unlock_degrees] ([P.name])."
-
+
if(uplink_owner)
uplink_owner.antag_memory += unlock_note + "
"
else
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 8f0bbae4b6..ed88d51c2d 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -35,6 +35,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark)
layer = MOB_LAYER
var/jobspawn_override = FALSE
var/delete_after_roundstart = TRUE
+ var/used = FALSE
+
+/obj/effect/landmark/start/proc/after_round_start()
+ if(delete_after_roundstart)
+ qdel(src)
/obj/effect/landmark/start/New()
GLOB.start_landmarks_list += src
@@ -187,12 +192,18 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark)
icon_state = "AI"
delete_after_roundstart = FALSE
var/primary_ai = TRUE
+ var/latejoin_active = TRUE
+
+/obj/effect/landmark/start/ai/after_round_start()
+ if(latejoin_active && !used)
+ new /obj/structure/AIcore/latejoin_inactive(loc)
+ return ..()
/obj/effect/landmark/start/ai/secondary
icon = 'icons/effects/landmarks_static.dmi'
icon_state = "ai_spawn"
primary_ai = FALSE
-
+ latejoin_active = FALSE
//Department Security spawns
diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index 4425dcff6c..a8061f6c2a 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -7,12 +7,14 @@
desc = "The framework for an artificial intelligence core."
max_integrity = 500
var/state = 0
- var/datum/ai_laws/laws = new()
+ var/datum/ai_laws/laws
var/obj/item/circuitboard/circuit = null
var/obj/item/device/mmi/brain = null
+ var/can_deconstruct = TRUE
-/obj/structure/AIcore/New()
- ..()
+/obj/structure/AIcore/Initialize()
+ . = ..()
+ laws = new
laws.set_laws_config()
/obj/structure/AIcore/Destroy()
@@ -24,11 +26,60 @@
brain = null
return ..()
+/obj/structure/AIcore/latejoin_inactive
+ name = "Networked AI core"
+ desc = "This AI core is connected by bluespace transmitters to NTNet, allowing for an AI personality to be downloaded to it on the fly mid-shift."
+ can_deconstruct = FALSE
+ icon_state = "ai-empty"
+ anchored = TRUE
+ state = AI_READY_CORE
+ var/available = TRUE
+ var/safety_checks = TRUE
+ var/active = TRUE
+
+/obj/structure/AIcore/latejoin_inactive/examine(mob/user)
+ . = ..()
+ to_chat(user, "Its transmitter seems to be [active? "on" : "off"].")
+
+/obj/structure/AIcore/latejoin_inactive/proc/is_available() //If people still manage to use this feature to spawn-kill AI latejoins ahelp them.
+ if(!available)
+ return FALSE
+ if(!safety_checks)
+ return TRUE
+ if(!active)
+ return FALSE
+ var/turf/T = get_turf(src)
+ var/area/A = get_area(src)
+ if(!A.blob_allowed)
+ return FALSE
+ if(!A.power_equip)
+ return FALSE
+ if(!SSmapping.level_trait(T.z,ZTRAIT_STATION))
+ return FALSE
+ if(!istype(T, /turf/open/floor))
+ return FALSE
+ return TRUE
+
+/obj/structure/AIcore/latejoin_inactive/attackby(obj/item/P, mob/user, params)
+ if(istype(P, /obj/item/device/multitool))
+ active = !active
+ to_chat(user, "You [active? "activate" : "deactivate"] [src]'s transimtters.")
+ return
+ return ..()
+
+/obj/structure/AIcore/latejoin_inactive/Initialize()
+ . = ..()
+ GLOB.latejoin_ai_cores += src
+
+/obj/structure/AIcore/latejoin_inactive/Destroy()
+ GLOB.latejoin_ai_cores -= src
+ return ..()
+
/obj/structure/AIcore/attackby(obj/item/P, mob/user, params)
if(istype(P, /obj/item/wrench))
return default_unfasten_wrench(user, P, 20)
if(!anchored)
- if(istype(P, /obj/item/weldingtool))
+ if(istype(P, /obj/item/weldingtool) && can_deconstruct)
if(state != EMPTY_CORE)
to_chat(user, "The core must be empty to deconstruct it!")
return
@@ -259,7 +310,6 @@ That prevents a few funky behaviors.
return 0
return 1
-
/obj/structure/AIcore/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/device/aicard/card)
if(state != AI_READY_CORE || !..())
return
@@ -275,6 +325,5 @@ That prevents a few funky behaviors.
else //If for some reason you use an empty card on an empty AI terminal.
to_chat(user, "There is no AI loaded on this terminal!")
-
/obj/item/circuitboard/aicore
name = "AI core (AI Core Board)" //Well, duh, but best to be consistent
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index d1f58dfe7f..3d22385513 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -769,11 +769,7 @@
var/J_totPos = html_encode(job.total_positions)
dat += "