diff --git a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm
index e9862be49dd5..9846e974827e 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm
@@ -62,3 +62,12 @@
/// For any additional things that should happen when a xeno's melee_attack_additional_effects_self() proc is called
#define COMSIG_XENO_SLASH_ADDITIONAL_EFFECTS_SELF "xeno_slash_additional_effects_self"
+
+/// From /datum/action/xeno_action/onclick/plant_weeds/use_ability(): (atom/A)
+#define COMSIG_XENO_PLANT_RESIN_NODE "xeno_plant_resin_node"
+
+/// From //mob/living/carbon/xenomorph/proc/emit_pheromones(): (pheromone, emit_cost)
+#define COMSIG_XENO_START_EMIT_PHEROMONES "xeno_start_emit_pheromones"
+
+/// From /obj/effect/alien/resin/special/eggmorph/attack_alien: (mob/living/carbon/xenomorph/M)
+#define COMSIG_XENO_TAKE_HUGGER_FROM_MORPHER "xeno_take_hugger_from_morpher"
diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
index 58021ba564a2..710e4d9ae20a 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
@@ -59,6 +59,9 @@
#define COMSIG_MOB_WEED_SLOWDOWN "mob_weeds_slowdown"
#define COMSIG_MOB_TAKE_DAMAGE "mob_take_damage" // TODO: move COMSIG_XENO_TAKE_DAMAGE & COMSIG_HUMAN_TAKE_DAMAGE to this
+
+///From /mob/living/carbon/human/attack_alien(): (mob/living/carbon/xenomorph/M, dam_bonus)
+#define COMSIG_MOB_TACKLED_DOWN "mob_tackled_down"
///called in /client/change_view()
#define COMSIG_MOB_CHANGE_VIEW "mob_change_view"
#define COMPONENT_OVERRIDE_VIEW (1<<0)
@@ -170,3 +173,5 @@
#define COMSIG_MOB_EFFECT_CLOAK_CANCEL "mob_effect_cloak_cancel"
#define COMSIG_MOB_END_TUTORIAL "mob_end_tutorial"
+
+#define COMSIG_MOB_NESTED "mob_nested"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 9cd69e61c8b2..31914815ae93 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -134,7 +134,9 @@
#define XENO_HIVE_YAUTJA "xeno_hive_yautja"
#define XENO_HIVE_RENEGADE "xeno_hive_renegade"
-#define ALL_XENO_HIVES list(XENO_HIVE_NORMAL, XENO_HIVE_CORRUPTED, XENO_HIVE_ALPHA, XENO_HIVE_BRAVO, XENO_HIVE_CHARLIE, XENO_HIVE_DELTA, XENO_HIVE_FERAL, XENO_HIVE_TAMED, XENO_HIVE_MUTATED, XENO_HIVE_FORSAKEN, XENO_HIVE_YAUTJA, XENO_HIVE_RENEGADE)
+#define XENO_HIVE_TUTORIAL "xeno_hive_tutorial"
+
+#define ALL_XENO_HIVES list(XENO_HIVE_NORMAL, XENO_HIVE_CORRUPTED, XENO_HIVE_ALPHA, XENO_HIVE_BRAVO, XENO_HIVE_CHARLIE, XENO_HIVE_DELTA, XENO_HIVE_FERAL, XENO_HIVE_TAMED, XENO_HIVE_MUTATED, XENO_HIVE_FORSAKEN, XENO_HIVE_YAUTJA, XENO_HIVE_RENEGADE, XENO_HIVE_TUTORIAL)
//=================================================
diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm
index 6e1b229e562f..66eaf13df282 100644
--- a/code/_globalvars/global_lists.dm
+++ b/code/_globalvars/global_lists.dm
@@ -180,6 +180,7 @@ GLOBAL_LIST_INIT_TYPED(hive_datum, /datum/hive_status, list(
XENO_HIVE_FORSAKEN = new /datum/hive_status/forsaken(),
XENO_HIVE_YAUTJA = new /datum/hive_status/yautja(),
XENO_HIVE_RENEGADE = new /datum/hive_status/corrupted/renegade(),
+ XENO_HIVE_TUTORIAL = new /datum/hive_status/tutorial()
))
GLOBAL_LIST_INIT(xeno_evolve_times, setup_xeno_evolve_times())
diff --git a/code/datums/tutorial/xenomorph/_xenomorph.dm b/code/datums/tutorial/xenomorph/_xenomorph.dm
new file mode 100644
index 000000000000..bd85cdb35f44
--- /dev/null
+++ b/code/datums/tutorial/xenomorph/_xenomorph.dm
@@ -0,0 +1,31 @@
+/datum/tutorial/xenomorph
+ category = TUTORIAL_CATEGORY_XENO
+ parent_path = /datum/tutorial/xenomorph
+ icon_state = "xeno"
+ ///Starting xenomorph type (caste) of type /mob/living/carbon/xenomorph/...
+ var/mob/living/carbon/xenomorph/starting_xenomorph_type = /mob/living/carbon/xenomorph/drone
+ ///Reference to the actual xenomorph mob
+ var/mob/living/carbon/xenomorph/xeno
+ ///If TRUE remove all actions from the tutorial xenomorph. If FALSE none will be removed. You can give actions back in the tutorial with give_action()
+ var/remove_all_actions = TRUE
+
+/datum/tutorial/xenomorph/init_mob()
+ var/mob/living/carbon/xenomorph/new_character = new starting_xenomorph_type(bottom_left_corner, null, XENO_HIVE_TUTORIAL)
+ new_character.lastarea = get_area(bottom_left_corner)
+
+ //Remove all actions from the tutorial xenomorph if remove_all_actions is TRUE
+ if(remove_all_actions)
+ for(var/datum/action/action_path as anything in new_character.base_actions)
+ remove_action(new_character, action_path)
+
+ setup_xenomorph(new_character, tutorial_mob, is_late_join = FALSE)
+
+ // We don't want people talking to other xenomorphs across tutorials
+ new_character.can_hivemind_speak = FALSE
+
+ tutorial_mob = new_character
+ xeno = new_character
+ RegisterSignal(tutorial_mob, COMSIG_LIVING_GHOSTED, PROC_REF(on_ghost))
+ RegisterSignal(tutorial_mob, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_DEATH, COMSIG_MOB_END_TUTORIAL), PROC_REF(signal_end_tutorial))
+ RegisterSignal(tutorial_mob, COMSIG_MOB_LOGOUT, PROC_REF(on_logout))
+ return ..()
diff --git a/code/datums/tutorial/xenomorph/xenomorph_basic.dm b/code/datums/tutorial/xenomorph/xenomorph_basic.dm
new file mode 100644
index 000000000000..0415977835aa
--- /dev/null
+++ b/code/datums/tutorial/xenomorph/xenomorph_basic.dm
@@ -0,0 +1,229 @@
+#define WAITING_HEALTH_THRESHOLD 300
+
+/datum/tutorial/xenomorph/basic
+ name = "Xenomorph - Basic"
+ desc = "A tutorial to get you acquainted with the very basics of how to play a xenomorph."
+ icon_state = "xeno"
+ tutorial_template = /datum/map_template/tutorial/s12x12
+ starting_xenomorph_type = /mob/living/carbon/xenomorph/drone
+
+// START OF SCRITPING
+
+/datum/tutorial/xenomorph/basic/start_tutorial(mob/starting_mob)
+ . = ..()
+ if(!.)
+ return
+
+ init_mob()
+
+ xeno.plasma_stored = 0
+ xeno.plasma_max = 0
+ xeno.melee_damage_lower = 40
+ xeno.melee_damage_upper = 40
+
+ message_to_player("Welcome to the Xenomorph basic tutorial. You are [xeno.name], a drone, the workhorse of the hive.")
+
+ addtimer(CALLBACK(src, PROC_REF(on_stretch_legs)), 10 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/on_stretch_legs()
+ message_to_player("As a drone you can perform most basic functions of the Xenomorph Hive. Such as weeding, building, planting eggs and nesting captured humans.")
+ addtimer(CALLBACK(src, PROC_REF(on_inform_health)), 5 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/on_inform_health()
+ message_to_player("The green icon on the right of your screen and green bar next to your character represents your health.")
+ addtimer(CALLBACK(src, PROC_REF(on_give_plasma)), 10 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/on_give_plasma()
+ message_to_player("You have been given plasma, a resource used for casting your abilities. This is represented by the blue icon at the right of your screen and the blue bar next to your character.")
+ xeno.plasma_max = 200
+ xeno.plasma_stored = 200
+ addtimer(CALLBACK(src, PROC_REF(on_damage_xenomorph)), 15 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/on_damage_xenomorph()
+ xeno.apply_damage(350)
+ xeno.emote("hiss")
+ message_to_player("Oh no! You've been damaged. Notice your green health bars have decreased. Xenomorphs can recover their health by standing or resting on weeds.")
+ addtimer(CALLBACK(src, PROC_REF(request_player_plant_weed)), 10 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/request_player_plant_weed()
+ update_objective("Plant a weed node using the new ability Plant Weeds you've just been given.")
+ give_action(xeno, /datum/action/xeno_action/onclick/plant_weeds)
+ message_to_player("Plant a weed node to spread weeds using your new ability at the top of the screen. Weeds heal xenomorphs and regenerate their plasma. They also slow humans, making them easier to fight.")
+ RegisterSignal(xeno, COMSIG_XENO_PLANT_RESIN_NODE, PROC_REF(on_plant_resinode))
+
+/datum/tutorial/xenomorph/basic/proc/on_plant_resinode()
+ SIGNAL_HANDLER
+ UnregisterSignal(xeno, COMSIG_XENO_PLANT_RESIN_NODE)
+ message_to_player("Well done. You can rest on the weeds to heal faster using the Rest ability or with the [retrieve_bind("rest")] key.")
+ message_to_player("We have increased your plasma reserves. Notice also your plasma will regenerate while you are on weeds.")
+ give_action(xeno, /datum/action/xeno_action/onclick/xeno_resting)
+ update_objective("Rest or wait until you are at least [WAITING_HEALTH_THRESHOLD] health.")
+ xeno.plasma_max = 500
+ RegisterSignal(xeno, COMSIG_XENO_ON_HEAL_WOUNDS, PROC_REF(on_xeno_gain_health))
+
+/datum/tutorial/xenomorph/basic/proc/on_xeno_gain_health()
+ SIGNAL_HANDLER
+ UnregisterSignal(xeno, COMSIG_XENO_ON_HEAL_WOUNDS)
+ message_to_player("Even on weeds. Healing is a slow process. This can be sped up using pheromones. Emit \"Recovery\" pheromones now using your new ability to speed up your healing.")
+ give_action(xeno, /datum/action/xeno_action/onclick/emit_pheromones)
+ update_objective("Emit recovery pheromones.")
+ RegisterSignal(xeno, COMSIG_XENO_START_EMIT_PHEROMONES, PROC_REF(on_xeno_emit_pheromone))
+
+/datum/tutorial/xenomorph/basic/proc/on_xeno_emit_pheromone(emitter, pheromone)
+ SIGNAL_HANDLER
+ if(!(pheromone == "recovery"))
+ message_to_player("These are not recovery pheromones. Click your ability again to stop emitting, and choose Recovery instead.")
+ else if(xeno.health > WAITING_HEALTH_THRESHOLD)
+ reach_health_threshold()
+ UnregisterSignal(xeno, COMSIG_XENO_START_EMIT_PHEROMONES)
+ else
+ UnregisterSignal(xeno, COMSIG_XENO_START_EMIT_PHEROMONES)
+ message_to_player("Well done. Recovery Pheromones will significantly speed up your health regeneration. Rest or wait until your health is at least [WAITING_HEALTH_THRESHOLD].")
+ message_to_player("Pheromones also provide their effects to other xenomorph sisters nearby!")
+ RegisterSignal(xeno, COMSIG_XENO_ON_HEAL_WOUNDS, PROC_REF(reach_health_threshold))
+
+/datum/tutorial/xenomorph/basic/proc/reach_health_threshold()
+ SIGNAL_HANDLER
+ if(xeno.health < WAITING_HEALTH_THRESHOLD)
+ return
+
+ UnregisterSignal(xeno, COMSIG_XENO_ON_HEAL_WOUNDS)
+
+ message_to_player("Good. Well done.")
+ message_to_player("A hostile human or \"tallhost\" has appeared. Use your harm intent to kill it in melee!")
+ update_objective("Kill the human!")
+
+ var/mob/living/carbon/human/human_dummy = new(loc_from_corner(7,7))
+ add_to_tracking_atoms(human_dummy)
+ add_highlight(human_dummy, COLOR_RED)
+ RegisterSignal(human_dummy, COMSIG_MOB_DEATH, PROC_REF(on_human_death_phase_one))
+
+/datum/tutorial/xenomorph/basic/proc/on_human_death_phase_one()
+ SIGNAL_HANDLER
+
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+
+ UnregisterSignal(human_dummy, COMSIG_MOB_DEATH)
+ message_to_player("Well done. Killing humans is one of many ways to help the hive.")
+ message_to_player("Another way is to capture them. This will grow a new xenomorph inside them which will eventually burst into a new playable xenomorph!")
+ addtimer(CALLBACK(human_dummy, TYPE_PROC_REF(/mob/living, rejuvenate)), 8 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(proceed_to_tackle_phase)), 10 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/proceed_to_tackle_phase()
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+ remove_highlight(human_dummy)
+ RegisterSignal(human_dummy, COMSIG_MOB_TAKE_DAMAGE, PROC_REF(on_tackle_phase_human_damage))
+ RegisterSignal(human_dummy, COMSIG_MOB_TACKLED_DOWN, PROC_REF(proceed_to_cap_phase))
+ message_to_player("Tackle the human to the ground using your disarm intent. This can take up to four tries as a drone.")
+ update_objective("Tackle the human to the ground!")
+
+/datum/tutorial/xenomorph/basic/proc/on_tackle_phase_human_damage(source, damagedata)
+ SIGNAL_HANDLER
+ if(damagedata["damage"] <= 0)
+ return
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+ // Rejuvenate the dummy if it's less than half health so our player can't kill it and softlock themselves.
+ if(human_dummy.health < (human_dummy.maxHealth / 2))
+ message_to_player("Don't harm the human!")
+ human_dummy.rejuvenate()
+
+/datum/tutorial/xenomorph/basic/proc/proceed_to_cap_phase()
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+
+ UnregisterSignal(human_dummy, COMSIG_MOB_TACKLED_DOWN)
+
+ ADD_TRAIT(human_dummy, TRAIT_KNOCKEDOUT, TRAIT_SOURCE_TUTORIAL)
+ ADD_TRAIT(human_dummy, TRAIT_FLOORED, TRAIT_SOURCE_TUTORIAL)
+ xeno.melee_damage_lower = 0
+ xeno.melee_damage_upper = 0
+ message_to_player("Well done. Under normal circumstances, you would have to keep tackling the human to keep them down, but for the purposes of this tutorial they will stay down forever.")
+ addtimer(CALLBACK(src, PROC_REF(cap_phase)), 10 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/cap_phase()
+ var/obj/effect/alien/resin/special/eggmorph/morpher = new(loc_from_corner(2,2), GLOB.hive_datum[XENO_HIVE_TUTORIAL])
+ morpher.stored_huggers = 1
+ add_to_tracking_atoms(morpher)
+ add_highlight(morpher, COLOR_YELLOW)
+ message_to_player("In the south west is an egg morpher. Click the egg morpher to take a facehugger.")
+ RegisterSignal(xeno, COMSIG_XENO_TAKE_HUGGER_FROM_MORPHER, PROC_REF(take_facehugger_phase))
+
+/datum/tutorial/xenomorph/basic/proc/take_facehugger_phase(source, hugger)
+ SIGNAL_HANDLER
+ UnregisterSignal(xeno, COMSIG_XENO_TAKE_HUGGER_FROM_MORPHER)
+ TUTORIAL_ATOM_FROM_TRACKING(/obj/effect/alien/resin/special/eggmorph, morpher)
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+ add_to_tracking_atoms(hugger)
+ remove_highlight(morpher)
+
+ add_highlight(hugger, COLOR_YELLOW)
+ message_to_player("This is a facehugger, highlighted in yellow. Pick up the facehugger by clicking it.")
+ message_to_player("Stand next to the downed human and click them to apply the facehugger. Or drop the facehugger near them to see it leap onto their face automatically.")
+ RegisterSignal(human_dummy, COMSIG_HUMAN_IMPREGNATE, PROC_REF(nest_cap_phase))
+
+/datum/tutorial/xenomorph/basic/proc/nest_cap_phase()
+ SIGNAL_HANDLER
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+ TUTORIAL_ATOM_FROM_TRACKING(/obj/item/clothing/mask/facehugger, hugger)
+ UnregisterSignal(human_dummy, COMSIG_MOB_TAKE_DAMAGE)
+ UnregisterSignal(human_dummy, COMSIG_HUMAN_IMPREGNATE)
+ remove_highlight(hugger)
+
+ message_to_player("We should nest the infected human to make sure they don't get away.")
+ message_to_player("Humans cannot escape nests without help, and the nest will keep them alive long enough for our new sister to burst forth.")
+ addtimer(CALLBACK(src, PROC_REF(nest_cap_phase_two)), 10 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_two()
+
+ loc_from_corner(8,0).ChangeTurf(/turf/closed/wall/resin/tutorial)
+ loc_from_corner(8,1).ChangeTurf(/turf/closed/wall/resin/tutorial)
+ loc_from_corner(9,1).ChangeTurf(/turf/closed/wall/resin/tutorial)
+
+ addtimer(CALLBACK(src, PROC_REF(nest_cap_phase_three)), 5 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_three()
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+ message_to_player("Grab the human using your grab intent. Or use control + click.")
+ RegisterSignal(human_dummy, COMSIG_MOVABLE_XENO_START_PULLING, PROC_REF(nest_cap_phase_four))
+
+/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_four()
+ SIGNAL_HANDLER
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+ UnregisterSignal(human_dummy, COMSIG_MOVABLE_XENO_START_PULLING)
+ message_to_player("Well done. Now devour the human by clicking on your character with the grab selected in your hand. You must not move during this process.")
+ RegisterSignal(human_dummy, COMSIG_MOB_DEVOURED, PROC_REF(nest_cap_phase_five))
+
+/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_five()
+ SIGNAL_HANDLER
+ message_to_player("Well done, you can reguritate the human using the new ability you have gained.")
+ message_to_player("Be careful. Real humans may put up a fight and can try to cut out of you from inside!")
+ give_action(xeno, /datum/action/xeno_action/onclick/regurgitate)
+ addtimer(CALLBACK(src, PROC_REF(nest_cap_phase_six)), 15 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_six()
+ message_to_player("Humans can only be nested on hive weeds. These are special weeds created by structures such as the hive core, or hive clusters.")
+ message_to_player("We have set up hive weeds and walls for you in the south east.")
+ addtimer(CALLBACK(src, PROC_REF(nest_cap_phase_seven)), 10 SECONDS)
+
+/datum/tutorial/xenomorph/basic/proc/nest_cap_phase_seven()
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+ UnregisterSignal(human_dummy, COMSIG_MOB_DEVOURED)
+ RegisterSignal(human_dummy, COMSIG_MOB_NESTED, PROC_REF(on_mob_nested))
+ message_to_player("Nest the captive human!")
+ update_objective("Nest the captive human!")
+ message_to_player("Drag the human next to the wall so both you and human are directly adjacent to the wall.")
+ message_to_player("With the grab selected in your hand. Click on the wall. Or click and drag the mouse from the human onto the wall. You must not move during this process.")
+ new /obj/effect/alien/resin/special/cluster(loc_from_corner(9,0), GLOB.hive_datum[XENO_HIVE_TUTORIAL])
+
+/datum/tutorial/xenomorph/basic/proc/on_mob_nested()
+ SIGNAL_HANDLER
+ TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, human_dummy)
+ UnregisterSignal(human_dummy, COMSIG_MOB_NESTED)
+
+ message_to_player("Well done, this concludes the basic Xenomorph tutorial.")
+ message_to_player("This tutorial will end shortly.")
+ tutorial_end_in(10 SECONDS)
+
+// END OF SCRIPTING
+
+/datum/tutorial/xenomorph/basic/init_map()
+ loc_from_corner(9,0).ChangeTurf(/turf/closed/wall/resin/tutorial)
diff --git a/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm b/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
index 63681d948620..a3ccffc466c4 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
@@ -248,6 +248,7 @@
do_buckle(mob, user)
ADD_TRAIT(mob, TRAIT_NESTED, TRAIT_SOURCE_BUCKLE)
+ SEND_SIGNAL(mob, COMSIG_MOB_NESTED, user)
if(!human)
return TRUE
diff --git a/code/game/turfs/walls/wall_types.dm b/code/game/turfs/walls/wall_types.dm
index 3e87af54c7e0..d546e7274331 100644
--- a/code/game/turfs/walls/wall_types.dm
+++ b/code/game/turfs/walls/wall_types.dm
@@ -767,6 +767,14 @@ INITIALIZE_IMMEDIATE(/turf/closed/wall/indestructible/splashscreen)
icon_state = "thickresin"
walltype = WALL_THICKRESIN
+/turf/closed/wall/resin/tutorial
+ name = "tutorial resin wall"
+ desc = "Weird slime solidified into a wall. Remarkably resilient."
+ hivenumber = XENO_HIVE_TUTORIAL
+
+/turf/closed/wall/resin/tutorial/attack_alien(mob/living/carbon/xenomorph/xeno)
+ return
+
/turf/closed/wall/resin/membrane
name = "resin membrane"
desc = "Weird slime translucent enough to let light pass through."
diff --git a/code/modules/cm_aliens/structures/special/egg_morpher.dm b/code/modules/cm_aliens/structures/special/egg_morpher.dm
index bcd0ecc03be5..e24ff8d167d8 100644
--- a/code/modules/cm_aliens/structures/special/egg_morpher.dm
+++ b/code/modules/cm_aliens/structures/special/egg_morpher.dm
@@ -190,7 +190,8 @@
if(stored_huggers)
to_chat(M, SPAN_XENONOTICE("You retrieve a child."))
stored_huggers = max(0, stored_huggers - 1)
- new /obj/item/clothing/mask/facehugger(loc, linked_hive.hivenumber)
+ var/obj/item/clothing/mask/facehugger/hugger = new(loc, linked_hive.hivenumber)
+ SEND_SIGNAL(M, COMSIG_XENO_TAKE_HUGGER_FROM_MORPHER, hugger)
return XENO_NONCOMBAT_ACTION
..()
diff --git a/code/modules/mob/living/carbon/xenomorph/Embryo.dm b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
index 9a1dfcb0e9a5..bba6b48c4f38 100644
--- a/code/modules/mob/living/carbon/xenomorph/Embryo.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
@@ -78,6 +78,9 @@
process_growth(delta_time)
/obj/item/alien_embryo/proc/process_growth(delta_time)
+ //Tutorial embryos do not progress.
+ if(hivenumber == XENO_HIVE_TUTORIAL)
+ return
var/datum/hive_status/hive = GLOB.hive_datum[hivenumber]
//Low temperature seriously hampers larva growth (as in, way below livable), so does stasis
if(!hive.hardcore) // Cannot progress if the hive has entered hardcore mode.
diff --git a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
index cfdf126fe769..c23b320618f5 100644
--- a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
@@ -392,6 +392,9 @@
M.stored_huggers++
qdel(src)
return
+ // Tutorial facehuggers never time out
+ if(hivenumber == XENO_HIVE_TUTORIAL)
+ return
die()
/obj/item/clothing/mask/facehugger/proc/die()
diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
index 3f83451a6386..fc25c80e795f 100644
--- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
@@ -461,7 +461,7 @@
time_of_birth = world.time
//Minimap
- if(z)
+ if(z && hivenumber != XENO_HIVE_TUTORIAL)
INVOKE_NEXT_TICK(src, PROC_REF(add_minimap_marker))
//Sight
@@ -1095,3 +1095,12 @@
else
//If we somehow use all 999 numbers fallback on 0
nicknumber = 0
+
+/proc/setup_xenomorph(mob/living/carbon/xenomorph/target, mob/new_player/new_player, is_late_join = FALSE)
+ new_player.spawning = TRUE
+ new_player.close_spawn_windows()
+ new_player.client.prefs.copy_all_to(target, new_player.job, is_late_join = FALSE)
+
+ if(new_player.mind)
+ new_player.mind_initialize()
+ new_player.mind.transfer_to(target, TRUE)
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
index 63cc4cb93431..d91da72505ba 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
@@ -78,6 +78,7 @@
playsound(xeno.loc, "alien_resin_build", 25)
apply_cooldown()
+ SEND_SIGNAL(xeno, COMSIG_XENO_PLANT_RESIN_NODE)
return ..()
/mob/living/carbon/xenomorph/lay_down()
@@ -363,6 +364,7 @@
current_aura = pheromone
visible_message(SPAN_XENOWARNING("\The [src] begins to emit strange-smelling pheromones."), \
SPAN_XENOWARNING("We begin to emit '[pheromone]' pheromones."), null, 5)
+ SEND_SIGNAL(src, COMSIG_XENO_START_EMIT_PHEROMONES, pheromone)
playsound(loc, "alien_drool", 25)
if(isqueen(src) && hive && hive.xeno_leader_list.len && anchored)
diff --git a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
index d57df232cda4..ee1d032e0031 100644
--- a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
@@ -209,6 +209,7 @@
KnockDown(strength) // Purely for knockdown visuals. All the heavy lifting is done by Stun
M.visible_message(SPAN_DANGER("[M] tackles down [src]!"), \
SPAN_DANGER("We tackle down [src]!"), null, 5, CHAT_TYPE_XENO_COMBAT)
+ SEND_SIGNAL(src, COMSIG_MOB_TACKLED_DOWN, M)
else
playsound(loc, 'sound/weapons/alien_claw_swipe.ogg', 25, 1)
if (body_position == LYING_DOWN)
diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm
index 63aea4007482..bb67eaa055a8 100644
--- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm
+++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm
@@ -1085,6 +1085,29 @@
/datum/hive_status/forsaken/can_delay_round_end(mob/living/carbon/xenomorph/xeno)
return FALSE
+/datum/hive_status/tutorial
+ name = "Tutorial Hive"
+ reporting_id = "tutorial"
+ hivenumber = XENO_HIVE_TUTORIAL
+ prefix = "Inquisitive "
+ latejoin_burrowed = FALSE
+
+ dynamic_evolution = FALSE
+ allow_queen_evolve = TRUE
+ evolution_without_ovipositor = FALSE
+ allow_no_queen_actions = TRUE
+
+ ///Can have many tutorials going at once.
+ hive_structures_limit = list(
+ XENO_STRUCTURE_CORE = 999,
+ XENO_STRUCTURE_CLUSTER = 999,
+ XENO_STRUCTURE_EGGMORPH = 999,
+ XENO_STRUCTURE_RECOVERY = 999,
+ )
+
+/datum/hive_status/tutorial/can_delay_round_end(mob/living/carbon/xenomorph/xeno)
+ return FALSE
+
/datum/hive_status/yautja
name = "Hellhound Pack"
reporting_id = "hellhounds"
diff --git a/colonialmarines.dme b/colonialmarines.dme
index 236f38d3e7c7..c10b77938115 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -673,6 +673,8 @@
#include "code\datums\tutorial\ss13\_ss13.dm"
#include "code\datums\tutorial\ss13\basic_ss13.dm"
#include "code\datums\tutorial\ss13\intents.dm"
+#include "code\datums\tutorial\xenomorph\_xenomorph.dm"
+#include "code\datums\tutorial\xenomorph\xenomorph_basic.dm"
#include "code\datums\weather\weather_event.dm"
#include "code\datums\weather\weather_map_holder.dm"
#include "code\datums\weather\weather_events\big_red.dm"
diff --git a/icons/misc/tutorial.dmi b/icons/misc/tutorial.dmi
index d4a4e65963ba..31c9f72d3853 100644
Binary files a/icons/misc/tutorial.dmi and b/icons/misc/tutorial.dmi differ