diff --git a/code/controllers/subsystem/communications.dm b/code/controllers/subsystem/communications.dm
index 992835072b3..37f1fd582e9 100644
--- a/code/controllers/subsystem/communications.dm
+++ b/code/controllers/subsystem/communications.dm
@@ -17,6 +17,10 @@ SUBSYSTEM_DEF(communications)
var/list/command_report_footnotes = list()
/// A counter of conditions that are blocking the command report from printing. Counter incremements up for every blocking condition, and de-incrememnts when it is complete.
var/block_command_report = 0
+ /// Has a special xenomorph egg been delivered?
+ var/xenomorph_egg_delivered = FALSE
+ /// The location where the special xenomorph egg was planted
+ var/area/captivity_area
/datum/controller/subsystem/communications/proc/can_announce(mob/living/user, is_silicon)
if(is_silicon && COOLDOWN_FINISHED(src, silicon_message_cooldown))
diff --git a/code/game/objects/effects/spawners/xeno_egg_delivery.dm b/code/game/objects/effects/spawners/xeno_egg_delivery.dm
index 2ba20bcb21b..b09a37f14b1 100644
--- a/code/game/objects/effects/spawners/xeno_egg_delivery.dm
+++ b/code/game/objects/effects/spawners/xeno_egg_delivery.dm
@@ -2,17 +2,28 @@
name = "xeno egg delivery"
icon = 'icons/mob/nonhuman-player/alien.dmi'
icon_state = "egg_growing"
- var/announcement_time = 1200
+ var/announcement_time = 120 SECONDS
/obj/effect/spawner/xeno_egg_delivery/Initialize(mapload)
. = ..()
- var/turf/T = get_turf(src)
+ var/turf/spawn_turf = get_turf(src)
- new /obj/structure/alien/egg(T)
- new /obj/effect/temp_visual/gravpush(T)
- playsound(T, 'sound/items/party_horn.ogg', 50, TRUE, -1)
+ new /obj/structure/alien/egg/delivery(spawn_turf)
+ new /obj/effect/temp_visual/gravpush(spawn_turf)
+ playsound(spawn_turf, 'sound/items/party_horn.ogg', 50, TRUE, -1)
- message_admins("An alien egg has been delivered to [ADMIN_VERBOSEJMP(T)].")
- log_game("An alien egg has been delivered to [AREACOORD(T)]")
- var/message = "Attention [station_name()], we have entrusted you with a research specimen in [get_area_name(T, TRUE)]. Remember to follow all safety precautions when dealing with the specimen."
+ message_admins("An alien egg has been delivered to [ADMIN_VERBOSEJMP(spawn_turf)].")
+ log_game("An alien egg has been delivered to [AREACOORD(spawn_turf)]")
+ var/message = "Attention [station_name()], we have entrusted you with a research specimen in [get_area_name(spawn_turf, TRUE)]. Remember to follow all safety precautions when dealing with the specimen."
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(print_command_report), message), announcement_time))
+
+/obj/structure/alien/egg/delivery
+ name = "xenobiological specimen egg"
+ desc = "A large mottled egg, sent as a part of a Xenobiological Research Initiative by the higher-ups. Handle with care!"
+ max_integrity = 300
+
+/obj/structure/alien/egg/delivery/Initialize(mapload)
+ . = ..()
+
+ SScommunications.xenomorph_egg_delivered = TRUE
+ SScommunications.captivity_area = get_area(src)
diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm
index 53ff49dec73..8ecede79c2a 100644
--- a/code/modules/antagonists/xeno/xeno.dm
+++ b/code/modules/antagonists/xeno/xeno.dm
@@ -1,3 +1,10 @@
+/// Captive Xeno has been found dead, regardless of location.
+#define CAPTIVE_XENO_DEAD "captive_xeno_dead"
+/// Captive Xeno has been found alive and within the captivity area.
+#define CAPTIVE_XENO_FAIL "captive_xeno_failed"
+/// Captive Xeno has been found alive and outside of the captivity area.
+#define CAPTIVE_XENO_PASS "captive_xeno_escaped"
+
/datum/team/xeno
name = "\improper Aliens"
@@ -17,10 +24,14 @@
show_to_ghosts = TRUE
var/datum/team/xeno/xeno_team
+/datum/antagonist/xeno/on_gain()
+ forge_objectives()
+ . = ..()
+
/datum/antagonist/xeno/create_team(datum/team/xeno/new_team)
if(!new_team)
for(var/datum/antagonist/xeno/X in GLOB.antagonists)
- if(!X.owner || !X.xeno_team)
+ if(!X.owner || !X.xeno_team || !istype(X.xeno_team, new_team)) //Make sure we don't add them to the wrong team
continue
xeno_team = X.xeno_team
return
@@ -36,11 +47,119 @@
/datum/antagonist/xeno/get_preview_icon()
return finish_preview_icon(icon('icons/mob/nonhuman-player/alien.dmi', "alienh"))
+/datum/antagonist/xeno/forge_objectives()
+ var/datum/objective/advance_hive/objective = new
+ objective.owner = owner
+ objectives += objective
+
+/datum/antagonist/xeno/captive
+ name = "\improper Captive Xenomorph"
+ ///Our associated antagonist team for captive xenomorphs
+ var/datum/team/xeno/captive/captive_team
+
+/datum/antagonist/xeno/captive/create_team(datum/team/xeno/captive/new_team)
+ if(!new_team)
+ for(var/datum/antagonist/xeno/captive/captive_xeno in GLOB.antagonists)
+ if(!captive_xeno.owner || !captive_xeno.captive_team)
+ continue
+ captive_team = captive_xeno.captive_team
+ return
+ captive_team = new
+ captive_team.progenitor = owner
+ else
+ if(!istype(new_team))
+ CRASH("Wrong xeno team type provided to create_team")
+ captive_team = new_team
+
+/datum/antagonist/xeno/captive/get_team()
+ return captive_team
+
+/datum/antagonist/xeno/captive/forge_objectives()
+ var/datum/objective/escape_captivity/objective = new
+ objective.owner = owner
+ objectives += objective
+ ..()
+
+//Xeno Objectives
+/datum/objective/escape_captivity
+
+/datum/objective/escape_captivity/New()
+ explanation_text = "Escape from captivity."
+
+/datum/objective/escape_captivity/check_completion()
+ if(!istype(get_area(owner), SScommunications.captivity_area))
+ return TRUE
+
+/datum/objective/advance_hive
+
+/datum/objective/advance_hive/New()
+ explanation_text = "Survive and advance the Hive."
+
+/datum/objective/advance_hive/check_completion()
+ return owner.current.stat != DEAD
+
+///Captive Xenomorphs team
+/datum/team/xeno/captive
+ name = "\improper Captive Aliens"
+ ///The first member of this team, presumably the queen.
+ var/datum/mind/progenitor
+
+/datum/team/xeno/captive/roundend_report()
+ var/list/parts = list()
+ var/escape_count = 0 //counts the number of xenomorphs that were born in captivity who ended the round outside of it
+ var/captive_count = 0 //counts the number of xenomorphs born in captivity who remained there until the end of the round (losers)
+
+ parts += "The [name] were:
"
+
+ if(check_captivity(progenitor))
+ parts += span_greentext("The progenitor of this hive was [progenitor.key], as [progenitor], who successfully escaped captivity!") + "
"
+ else
+ parts += span_redtext("The progenitor of this hive was [progenitor.key], as [progenitor], who failed to escape captivity") + "
"
+
+ for(var/datum/mind/alien_mind in members)
+ if(alien_mind == progenitor)
+ continue
+
+ switch(check_captivity(alien_mind.current))
+ if(CAPTIVE_XENO_DEAD)
+ parts += "[printplayer(alien_mind, fleecheck = FALSE)] while trying to escape captivity!"
+ if(CAPTIVE_XENO_FAIL)
+ parts += "[printplayer(alien_mind, fleecheck = FALSE)] in captivity!"
+ captive_count++
+ if(CAPTIVE_XENO_PASS)
+ parts += "[printplayer(alien_mind, fleecheck = FALSE)] and managed to [span_greentext("escape captivity!")]"
+ escape_count++
+
+ parts += "
Overall, [captive_count] xenomorphs remained alive and in captivity, and [escape_count] managed to escape!
"
+
+ var/thank_you_message
+ if(captive_count > escape_count)
+ thank_you_message = "xenobiological containment architecture"
+ else
+ thank_you_message = "xenofauna combat effectiveness"
+
+ parts += "Nanotrasen thanks the crew of [station_name()] for providing much needed research data on [thank_you_message]."
+
+ return "