diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 4955a8f6ee6a..888e3c2ce657 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -61,3 +61,8 @@ var/atom/cmp_dist_origin=null
/// Compares mobs based on their timeofdeath value in ascending order
/proc/cmp_mob_deathtime_asc(mob/A, mob/B)
return A.timeofdeath - B.timeofdeath
+
+/// Compares observers based on their larva_queue_time value in ascending order
+/// Assumes the client on the observer is not null
+/proc/cmp_obs_larvaqueuetime_asc(mob/dead/observer/A, mob/dead/observer/B)
+ return A.client.larva_queue_time - B.client.larva_queue_time
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 4d6ef5fa75ab..e3191628f688 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -241,12 +241,11 @@
else
return get_step(start, EAST)
-/// Get a list of observers that can be alien candidates, optionally sorted by timeofdeath
+/// Get a list of observers that can be alien candidates, optionally sorted by larva_queue_time
/proc/get_alien_candidates(sorted = TRUE)
var/list/candidates = list()
- for(var/i in GLOB.observer_list)
- var/mob/dead/observer/cur_obs = i
+ for(var/mob/dead/observer/cur_obs as anything in GLOB.observer_list)
// Preference check
if(!cur_obs.client || !cur_obs.client.prefs || !(cur_obs.client.prefs.be_special & BE_ALIEN_AFTER_DEATH))
continue
@@ -263,22 +262,22 @@
// copied from join as xeno
var/deathtime = world.time - cur_obs.timeofdeath
- if(deathtime < 3000 && ( !cur_obs.client.admin_holder || !(cur_obs.client.admin_holder.rights & R_ADMIN)) )
+ if(deathtime < (5 MINUTES) && ( !cur_obs.client.admin_holder || !(cur_obs.client.admin_holder.rights & R_ADMIN)) )
continue
// AFK players cannot be drafted
- if(cur_obs.client.inactivity / 600 > ALIEN_SELECT_AFK_BUFFER + 5)
+ if(cur_obs.client.inactivity / (1 MINUTES) > ALIEN_SELECT_AFK_BUFFER + 5)
continue
// Mods with larva protection cannot be drafted
- if((cur_obs.client.admin_holder && (cur_obs.client.admin_holder.rights & R_MOD)) && cur_obs.adminlarva == 0)
+ if((cur_obs.client.admin_holder && (cur_obs.client.admin_holder.rights & R_MOD)) && !cur_obs.adminlarva)
continue
candidates += cur_obs
- // Optionally sort by timeofdeath
+ // Optionally sort by larva_queue_time
if(sorted && length(candidates))
- candidates = sort_list(candidates, GLOBAL_PROC_REF(cmp_mob_deathtime_asc))
+ candidates = sort_list(candidates, GLOBAL_PROC_REF(cmp_obs_larvaqueuetime_asc))
return candidates
@@ -288,13 +287,22 @@
* Arguments:
* * candidates - The list of observers from get_alien_candidates() with atleast one
* * dequeued - How many candidates to skip messaging because they were dequeued
+ * * cache_only - Whether to not actually send a to_chat message and instead only update larva_queue_cached_message
*/
-/proc/message_alien_candidates(list/candidates, dequeued)
+/proc/message_alien_candidates(list/candidates, dequeued, cache_only = FALSE)
var/new_players = 0
for(var/i in (1 + dequeued) to candidates.len)
- to_chat(candidates[i], SPAN_XENONOTICE("You are [dequeued ? "now" : "currently"] [i-dequeued]\th in the larva queue. There are [new_players] ahead of you that have yet to play this round."))
var/mob/dead/observer/cur_obs = candidates[i]
- if(!cur_obs.timeofdeath)
+
+ // Generate the messages
+ var/cached_message = SPAN_XENONOTICE("You are currently [i-dequeued]\th in the larva queue. There are [new_players] ahead of you that have yet to play this round.")
+ cur_obs.larva_queue_cached_message = cached_message
+ if(!cache_only)
+ var/chat_message = dequeued ? replacetext(cached_message, "currently", "now") : cached_message
+ to_chat(candidates[i], chat_message)
+
+ // Count how many are prioritized
+ if(cur_obs.client.larva_queue_time < 2) // 0 and 1 because facehuggers/t-domers are slightly deprioritized
new_players++
/proc/convert_k2c(temp)
diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm
index 8237fc63b7e2..bc588c82c12f 100644
--- a/code/game/gamemodes/cm_initialize.dm
+++ b/code/game/gamemodes/cm_initialize.dm
@@ -364,7 +364,27 @@ Additional game mode variables.
available_xenos[larva_option] = list(hive)
if(!available_xenos.len || (instant_join && !available_xenos_non_ssd.len))
- to_chat(xeno_candidate, SPAN_WARNING("There aren't any available xenomorphs or burrowed larvae. You can try getting spawned as a chestburster larva by toggling your Xenomorph candidacy in Preferences -> Toggle SpecialRole Candidacy."))
+ if(!xeno_candidate.client || !xeno_candidate.client.prefs || !(xeno_candidate.client.prefs.be_special & BE_ALIEN_AFTER_DEATH))
+ to_chat(xeno_candidate, SPAN_WARNING("There aren't any available xenomorphs or burrowed larvae. You can try getting spawned as a chestburster larva by toggling your Xenomorph candidacy in Preferences -> Toggle SpecialRole Candidacy."))
+ return FALSE
+ to_chat(xeno_candidate, SPAN_WARNING("There aren't any available xenomorphs or burrowed larvae."))
+
+ // Give the player a cached message of their queue status if they are an observer
+ var/mob/dead/observer/candidate_observer = xeno_candidate
+ if(istype(candidate_observer))
+ if(candidate_observer.larva_queue_cached_message)
+ to_chat(xeno_candidate, candidate_observer.larva_queue_cached_message)
+ return FALSE
+
+ // No cache, lets check now then
+ message_alien_candidates(get_alien_candidates(), dequeued = 0, cache_only = TRUE)
+ if(candidate_observer.larva_queue_cached_message)
+ to_chat(xeno_candidate, candidate_observer.larva_queue_cached_message)
+ return FALSE
+
+ // We aren't in queue yet, lets teach them about the queue then
+ candidate_observer.larva_queue_cached_message = SPAN_XENONOTICE("You are currently still awaiting assignment in the larva queue. Priority is given to players who have yet to play in the round, but otherwise the ordering is based on your time of death. When you have been dead long enough and are not inactive, you will periodically receive messages where you are in the queue relative to other currently valid xeno candidates. Note: Playing as a facehugger or in the thunderdome will not alter your time of death. This means you won't lose your relative place in queue if you step away, disconnect, play as a facehugger, or play in the thunderdome.")
+ to_chat(xeno_candidate, candidate_observer.larva_queue_cached_message)
return FALSE
var/mob/living/carbon/xenomorph/new_xeno
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index 8f0939474427..fbec4aa13f36 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -26,6 +26,8 @@
var/adminobs = null
var/area = null
var/time_died_as_mouse = null //when the client last died as a mouse
+ /// The descriminator for larva queue ordering: Generally set to timeofdeath except for facehuggers/admin z-level play
+ var/larva_queue_time
var/donator = 0
var/adminhelped = 0
diff --git a/code/modules/cm_aliens/structures/special/pylon_core.dm b/code/modules/cm_aliens/structures/special/pylon_core.dm
index 0b57c4ff3bd2..f3f271ea2ca5 100644
--- a/code/modules/cm_aliens/structures/special/pylon_core.dm
+++ b/code/modules/cm_aliens/structures/special/pylon_core.dm
@@ -151,9 +151,9 @@
var/list/players_with_xeno_pref = get_alien_candidates()
if(players_with_xeno_pref && players_with_xeno_pref.len)
if(spawning_larva && spawn_burrowed_larva(players_with_xeno_pref[1]))
- message_alien_candidates(players_with_xeno_pref, 1)
+ message_alien_candidates(players_with_xeno_pref, dequeued = 1)
else
- message_alien_candidates(players_with_xeno_pref, 0)
+ message_alien_candidates(players_with_xeno_pref, dequeued = 0)
if(linked_hive.hijack_burrowed_surge && (last_surge_time + surge_cooldown) < world.time)
last_surge_time = world.time
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index f48efa96578d..f59ba19d6c74 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -33,8 +33,8 @@
plane = GHOST_PLANE
layer = ABOVE_FLY_LAYER
stat = DEAD
- var/adminlarva = 0
- var/ghostvision = 1
+ var/adminlarva = FALSE
+ var/ghostvision = TRUE
var/can_reenter_corpse
var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
//If you died in the game and are a ghost - this will remain as null.
@@ -45,7 +45,7 @@
"Squad HUD" = FALSE,
"Xeno Status HUD" = FALSE
)
- universal_speak = 1
+ universal_speak = TRUE
var/updatedir = TRUE //Do we have to update our dir as the ghost moves around?
var/atom/movable/following = null
var/datum/orbit_menu/orbit_menu
@@ -55,6 +55,8 @@
var/own_orbit_size = 0
var/observer_actions = list(/datum/action/observer_action/join_xeno)
var/datum/action/minimap/observer/minimap
+ var/larva_queue_cached_message
+
alpha = 127
/mob/dead/observer/verb/toggle_ghostsee()
@@ -363,6 +365,12 @@ Works together with spawning an observer, noted above.
if(ghost.client.player_data)
ghost.client.player_data.load_timestat_data()
+ // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
+ // We don't change facehugger timeofdeath because they are still on cooldown if they died as a hugger
+ // Facehuggers are atleast 1 because they did get some action compared to those at 0 timeofdeath
+ var/new_tod = isfacehugger(src) ? 1 : ghost.timeofdeath
+ ghost.client.larva_queue_time = max(ghost.client.larva_queue_time, new_tod)
+
ghost.set_huds_from_prefs()
return ghost
@@ -405,6 +413,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
var/mob/dead/observer/ghost = ghostize((is_nested && nest && !QDELETED(nest))) //FALSE parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
if(ghost && !is_admin_level(z))
ghost.timeofdeath = world.time
+ ghost.client?.larva_queue_time = world.time
if(is_nested && nest && !QDELETED(nest))
ghost.can_reenter_corpse = FALSE
diff --git a/code/modules/mob/living/carbon/xenomorph/Embryo.dm b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
index ee556b2b6fe4..a74c82026dbe 100644
--- a/code/modules/mob/living/carbon/xenomorph/Embryo.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
@@ -158,7 +158,7 @@
if(candidates && candidates.len)
picked = candidates[1]
- message_alien_candidates(candidates, 1)
+ message_alien_candidates(candidates, dequeued = 1)
// Spawn the larva
var/mob/living/carbon/xenomorph/larva/new_xeno
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
index d9ab8a1e849d..c0b57af9d3db 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
@@ -155,6 +155,7 @@
for(var/mob/dead/observer/observer as anything in GLOB.observer_list)
to_chat(observer, SPAN_DEADSAY("[human] has been facehugged by [src]" + " [OBSERVER_JMP(observer, human)]"))
to_chat(src, SPAN_DEADSAY("[human] has been facehugged by [src]"))
+ timeofdeath = 1 // Ever so slightly deprioritized for larva queue
qdel(src)
if(hug_area)
xeno_message(SPAN_XENOMINORWARNING("You sense that [src] has facehugged a host at \the [hug_area]!"), 1, src.hivenumber)
diff --git a/code/modules/mob/living/carbon/xenomorph/death.dm b/code/modules/mob/living/carbon/xenomorph/death.dm
index 9870c485e15a..f3534318f673 100644
--- a/code/modules/mob/living/carbon/xenomorph/death.dm
+++ b/code/modules/mob/living/carbon/xenomorph/death.dm
@@ -58,7 +58,7 @@
GLOB.hive_datum[hivenumber].stored_larva--
GLOB.hive_datum[hivenumber].hive_ui.update_burrowed_larva()
if(count)
- message_alien_candidates(players_with_xeno_pref, count)
+ message_alien_candidates(players_with_xeno_pref, dequeued = count)
if(hive && hive.living_xeno_queen == src)
xeno_message(SPAN_XENOANNOUNCE("A sudden tremor ripples through the hive... the Queen has been slain! Vengeance!"),3, hivenumber)
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 77ba9681ebb6..7e181ed470cb 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -628,15 +628,15 @@
/obj/docking_port/mobile/proc/intoTheSunset()
// Loop over mobs
- for(var/t in return_turfs())
- var/turf/T = t
- for(var/mob/living/L in T.GetAllContents())
+ for(var/turf/turf as anything in return_turfs())
+ for(var/mob/living/mob in turf.GetAllContents())
// Ghostize them and put them in nullspace stasis (for stat & possession checks)
- //L.notransform = TRUE
- var/mob/dead/observer/O = L.ghostize(FALSE)
- if(O)
- O.timeofdeath = world.time
- L.moveToNullspace()
+ //mob.notransform = TRUE
+ var/mob/dead/observer/obs = mob.ghostize(FALSE)
+ if(obs)
+ obs.timeofdeath = world.time
+ obs.client?.larva_queue_time = world.time
+ mob.moveToNullspace()
// Now that mobs are stowed, delete the shuttle
jumpToNullSpace()