From a80e8cae9d2e9d593761ce643fc53c19cde859e1 Mon Sep 17 00:00:00 2001
From: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Date: Sat, 18 Apr 2026 19:00:15 -0400
Subject: [PATCH] Adds Wizard adept midround (#31784)
* Adds Wizard adept midround
* Fixes HUD bug
* Teaches fireproofing for sacred flame
* Teaches them better
* Mugwort compatibility
---
code/__DEFINES/gamemode.dm | 1 +
code/datums/mind.dm | 32 ++++++
code/game/gamemodes/game_mode.dm | 2 +
.../modules/antagonists/wizard/datum_adept.dm | 104 ++++++++++++++++++
code/modules/events/event_container.dm | 1 +
code/modules/events/wizard/wizard_adept.dm | 83 ++++++++++++++
.../chemistry/reagents/food_reagents.dm | 2 +-
paradise.dme | 2 +
8 files changed, 226 insertions(+), 1 deletion(-)
create mode 100644 code/modules/antagonists/wizard/datum_adept.dm
create mode 100644 code/modules/events/wizard/wizard_adept.dm
diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm
index d4c9eebed37..0208c376777 100644
--- a/code/__DEFINES/gamemode.dm
+++ b/code/__DEFINES/gamemode.dm
@@ -53,6 +53,7 @@
#define SPECIAL_ROLE_MIND_FLAYER "Mind Flayer"
#define SPECIAL_ROLE_VAMPIRE_THRALL "Vampire Thrall"
#define SPECIAL_ROLE_WIZARD "Wizard"
+#define SPECIAL_ROLE_WIZARD_ADEPT "Wizard Adept"
#define SPECIAL_ROLE_WIZARD_APPRENTICE "Wizard Apprentice"
#define SPECIAL_ROLE_XENOMORPH "Xenomorph"
#define SPECIAL_ROLE_XENOMORPH_QUEEN "Xenomorph Queen"
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 21245dc3fde..a696947cbfc 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -420,6 +420,18 @@
. += _memory_edit_role_enabled(ROLE_NINJA)
+/datum/mind/proc/memory_edit_wizard_adept(mob/living/carbon/human/H)
+ . = _memory_edit_header("wizard_adept")
+ var/datum/antagonist/wizard/wiz = has_antag_datum(/datum/antagonist/wizard/adept)
+ if(wiz)
+ . += "WIZARD ADEPT|no"
+ if(!wiz.has_antag_objectives())
+ . += "
Objectives are empty! Randomize!"
+ else
+ . += "wizard_adept|NO"
+
+ . += _memory_edit_role_enabled(ROLE_WIZARD)
+
/datum/mind/proc/memory_edit_mind_flayer(mob/living/carbon/human/H)
. = _memory_edit_header("mind_flayer")
var/datum/antagonist/mindflayer/flayer = has_antag_datum(/datum/antagonist/mindflayer)
@@ -619,6 +631,8 @@
sections["vampire"] = memory_edit_vampire(H)
/** SPACE NINJA */
sections["space_ninja"] = memory_edit_space_ninja(H)
+ /** WIZARD ADEPT **/
+ sections["wizard_adept"] = memory_edit_wizard_adept(H)
/** MINDFLAYER ***/
sections["mind_flayer"] = memory_edit_mind_flayer(H)
/** HERETIC ***/
@@ -1196,6 +1210,19 @@
to_chat(current, "Your training awakens, and a myserious set of gear teleports in around you... You are a Space Ninja!")
message_admins("[key_name(usr)] has ninja'd [key_name(current)].")
+ else if(href_list["wizard_adept"])
+ switch(href_list["wizard_adept"])
+ if("clear")
+ if(has_antag_datum(/datum/antagonist/wizard/adept))
+ remove_antag_datum(/datum/antagonist/wizard/adept)
+ log_admin("[key_name(usr)] has de-wizard adept'd [key_name(current)].")
+ message_admins("[key_name(usr)] has de-wizard adept'd [key_name(current)].")
+ if("wizard_adept")
+ make_wizard_adept()
+ log_admin("[key_name(usr)] has wizard adept'd [key_name(current)].")
+ to_chat(current, "Your sorcerous mind remembers your spellcasting! You are a Space Wizard Adept!")
+ message_admins("[key_name(usr)] has wizard adept'd [key_name(current)].")
+
else if(href_list["vampthrall"])
switch(href_list["vampthrall"])
if("clear")
@@ -1858,6 +1885,11 @@
add_antag_datum(/datum/antagonist/space_ninja)
SSticker.mode.ninjas |= src
+/datum/mind/proc/make_wizard_adept()
+ if(!has_antag_datum(/datum/antagonist/wizard/adept))
+ add_antag_datum(/datum/antagonist/wizard/adept)
+ SSticker.mode.wizards |= src
+
/datum/mind/proc/make_heretic()
if(!has_antag_datum(/datum/antagonist/heretic))
add_antag_datum(/datum/antagonist/heretic)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 56390217672..5bfe341dadb 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -72,6 +72,8 @@
var/list/datum/mind/pre_mindflayers = list()
/// A list of all minds which have the wizard special role
var/list/datum/mind/wizards = list()
+ /// A list of all minds that are wizard adepts
+ var/list/datum/mind/adepts = list()
/// A list of all minds that are wizard apprentices
var/list/datum/mind/apprentices = list()
/// A list of all minds that are ninjas
diff --git a/code/modules/antagonists/wizard/datum_adept.dm b/code/modules/antagonists/wizard/datum_adept.dm
new file mode 100644
index 00000000000..1865c8e828c
--- /dev/null
+++ b/code/modules/antagonists/wizard/datum_adept.dm
@@ -0,0 +1,104 @@
+/datum/antagonist/wizard/adept
+ name = "Wizard Adept"
+ special_role = SPECIAL_ROLE_WIZARD_ADEPT
+ antag_datum_blacklist = list(/datum/antagonist/wizard/construct, /datum/antagonist/wizard/apprentice)
+
+ /// Potential offensive spell list
+ var/list/offense_spells = list(
+ /datum/spell/genetic/mutate,
+ /datum/spell/blind,
+ /datum/spell/fireball,
+ /datum/spell/fireball/toolbox,
+ /datum/spell/sacred_flame,
+ /datum/spell/rod_form,
+ /datum/spell/charge_up/bounce/lightning,
+ /datum/spell/infinite_guns,
+ )
+ /// Potential defensive spell list
+ var/list/defense_spells = list(
+ /datum/spell/projectile/magic_missile,
+ /datum/spell/emplosion/disable_tech,
+ /datum/spell/forcewall,
+ /datum/spell/aoe/conjure/timestop,
+ /datum/spell/aoe/repulse,
+ /datum/spell/rathens,
+ )
+ /// Potential mobility spell list
+ var/list/mobility_spells = list(
+ /datum/spell/turf_teleport/blink,
+ /datum/spell/area_teleport/teleport,
+ /datum/spell/ethereal_jaunt,
+ /datum/spell/spacetime_dist,
+ /datum/spell/aoe/knock,
+ )
+ /// Potential support spell list
+ var/list/support_spells = list(
+ /datum/spell/smoke,
+ /datum/spell/disguise_self,
+ /datum/spell/aoe/conjure/summon_cheese,
+ )
+ /// Potential support item list
+ var/list/support_items = list(
+ /obj/item/gun/magic/wand/resurrection,
+ /obj/item/guardiancreator,
+ /obj/item/tarot_generator/wizard,
+ /obj/item/gun/magic/wand/fireball,
+ )
+
+/datum/antagonist/wizard/adept/greet()
+ . = ..()
+ . += SPAN_DANGER("You are an adept wizard! You are a fully trained wizard, ready to embark on one of your first journeys into the application of space magic!")
+
+/datum/antagonist/wizard/adept/give_objectives()
+ var/datum/objective/wizchaos/new_objective = new /datum/objective/wizchaos
+ new_objective.explanation_text = "Distract, harass, and impede Nanotrasen's crew!"
+ add_antag_objective(new_objective)
+
+/datum/antagonist/wizard/adept/equip_wizard()
+ var/mob/living/carbon/human/new_wiz = owner.current
+ new_wiz.delete_equipment()
+ new_wiz.equip_to_slot_or_del(new /obj/item/radio/headset(new_wiz), ITEM_SLOT_LEFT_EAR)
+ new_wiz.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(new_wiz), ITEM_SLOT_JUMPSUIT)
+ new_wiz.equip_to_slot_or_del(new /obj/item/storage/backpack(new_wiz), ITEM_SLOT_BACK)
+ new_wiz.equip_to_slot_or_del(new /obj/item/teleportation_scroll/apprentice(new_wiz), ITEM_SLOT_RIGHT_POCKET)
+ new_wiz.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(new_wiz), ITEM_SLOT_SHOES)
+ new_wiz.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(new_wiz), ITEM_SLOT_OUTER_SUIT)
+ new_wiz.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(new_wiz), ITEM_SLOT_HEAD)
+ if(new_wiz.dna.species.speciesbox)
+ new_wiz.equip_to_slot_or_del(new new_wiz.dna.species.speciesbox(new_wiz), ITEM_SLOT_IN_BACKPACK)
+ else
+ new_wiz.equip_to_slot_or_del(new /obj/item/storage/box/survival(new_wiz), ITEM_SLOT_IN_BACKPACK)
+ if(isvox(new_wiz))
+ new_wiz.equip_to_slot_or_del(new /obj/item/clothing/mask/breath/vox/respirator(new_wiz), ITEM_SLOT_MASK)
+ new_wiz.equip_to_slot_or_del(new /obj/item/tank/internals/emergency_oxygen/double/vox(new_wiz), ITEM_SLOT_RIGHT_HAND)
+ new_wiz.internal = new_wiz.r_hand
+
+ var/datum/spell/offense = pick(offense_spells)
+ new_wiz.mind.AddSpell(new offense(null))
+ if(offense == /datum/spell/sacred_flame)
+ ADD_TRAIT(new_wiz, TRAIT_RESISTHEAT, MAGIC_TRAIT)
+ ADD_TRAIT(new_wiz, TRAIT_RESISTHIGHPRESSURE, MAGIC_TRAIT)
+
+ var/datum/spell/defense = pick(defense_spells)
+ new_wiz.mind.AddSpell(new defense(null))
+
+ var/datum/spell/mobility = pick(mobility_spells)
+ new_wiz.mind.AddSpell(new mobility(null))
+
+ if(pick(0, 1))
+ var/datum/spell/support = pick(support_spells)
+ new_wiz.mind.AddSpell(new support(null))
+ else
+ var/obj/item/support = pick(support_items)
+ new_wiz.equip_to_slot_or_del(new support(new_wiz), ITEM_SLOT_IN_BACKPACK)
+
+ new_wiz.rejuvenate()
+
+/datum/antagonist/wizard/adept/add_owner_to_gamemode()
+ SSticker.mode.adepts |= owner
+
+/datum/antagonist/wizard/adept/remove_owner_from_gamemode()
+ SSticker.mode.adepts -= owner
+
+/datum/antagonist/wizard/adept/full_on_wizard()
+ return FALSE
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 8e587c2ad04..b0344b7234c 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -219,6 +219,7 @@ GLOBAL_LIST_EMPTY(event_last_fired)
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/disease_outbreak, 8, TRUE),
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/revenant, 9),
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/space_ninja, 9),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/wizard_adept, 5),
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_morph, 16, is_one_shot = TRUE),
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/aurora_caelus, 5, is_one_shot = TRUE),
//new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_pulsedemon, 20, is_one_shot = TRUE)
diff --git a/code/modules/events/wizard/wizard_adept.dm b/code/modules/events/wizard/wizard_adept.dm
new file mode 100644
index 00000000000..0df34a3a173
--- /dev/null
+++ b/code/modules/events/wizard/wizard_adept.dm
@@ -0,0 +1,83 @@
+/datum/event/wizard_adept
+ name = "Wizard Adept"
+ role_weights = list(ASSIGNMENT_SECURITY = 2)
+ role_requirements = list(ASSIGNMENT_CREW = 20, ASSIGNMENT_SECURITY = 2)
+ nominal_severity = EVENT_LEVEL_MAJOR
+
+/datum/event/wizard_adept/start()
+ INVOKE_ASYNC(src, PROC_REF(poll_wiz_adept))
+
+/datum/event/wizard_adept/proc/poll_wiz_adept(list/potential_wizards)
+ var/list/candidates = list()
+ if(!potential_wizards)
+ candidates = SSghost_spawns.poll_candidates("Do you want to play as a Wizard Adept?", ROLE_WIZARD, TRUE)
+ else
+ candidates = potential_wizards
+ if(!length(candidates))
+ kill()
+ return
+ var/mob/new_wizard
+ while(length(candidates))
+ new_wizard = pick_n_take(candidates)
+ if(jobban_isbanned(new_wizard, ROLE_TRAITOR))
+ continue
+ break
+ if(!new_wizard)
+ kill()
+ return
+
+ var/gender_pref = input_async(new_wizard, "Please select a gender (10 seconds):", list("Male", "Female"))
+ addtimer(CALLBACK(src, PROC_REF(get_species), candidates, new_wizard, gender_pref), 10 SECONDS)
+
+/datum/event/wizard_adept/proc/get_species(list/potential_wizards, mob/new_wizard, datum/async_input/gender_pref)
+ gender_pref.close()
+ var/sel_gender = gender_pref.result
+ if(!sel_gender) // Player was AFK and did not select a gender
+ poll_wiz_adept(potential_wizards)
+ return
+ var/species_pref = input_async(new_wizard, "Please select a species (10 seconds):", list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian", "Drask", "Kidan", "Grey", "Skkulakin", "Vox", "Slime", "Machine", "Random"))
+ addtimer(CALLBACK(src, PROC_REF(create_ninja), potential_wizards, new_wizard, sel_gender, species_pref), 10 SECONDS)
+
+/datum/event/wizard_adept/proc/create_ninja(list/potential_wizards, mob/new_wizard, sel_gender, datum/async_input/species_pref)
+ species_pref.close()
+ var/sel_species = species_pref.result
+ if(!sel_species) // Player was AFK and did not select a species
+ poll_wiz_adept(potential_wizards)
+ return
+ if(!new_wizard || !new_wizard.client)
+ poll_wiz_adept(potential_wizards)
+ return
+
+ if(!length(GLOB.wizardstart))
+ message_admins("Unable to spawn wizard adept - no valid spawn locations.")
+ return
+
+ var/mob/living/carbon/human/M = new(pick(GLOB.wizardstart))
+
+ M.ckey = new_wizard.ckey
+ dust_if_respawnable(new_wizard)
+
+ if(sel_gender == "Male")
+ M.change_gender(MALE)
+ M.change_body_type(MALE)
+ else
+ M.change_gender(FEMALE)
+ M.change_body_type(FEMALE)
+
+ if(sel_species == "Random")
+ sel_species = pick("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian", "Drask", "Kidan", "Grey", "Skkulakin", "Vox", "Slime", "Machine")
+ if(sel_species == "Slime")
+ sel_species = "Slime People"
+ var/datum/species/S = GLOB.all_species[sel_species]
+ var/species = S.type
+ M.set_species(species, TRUE)
+ M.dna.ready_dna(M)
+ M.cleanSE()
+ M.overeatduration = 0
+ M.age = rand(30, 50)
+ M.regenerate_icons()
+
+ M.mind = new
+ M.mind.bind_to(M)
+ M.mind.set_original_mob(M)
+ M.mind.make_wizard_adept()
diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm
index 4fab420b4d4..cc9696833e0 100644
--- a/code/modules/reagents/chemistry/reagents/food_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm
@@ -583,7 +583,7 @@
/datum/reagent/consumable/mugwort/on_mob_life(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
- if(ishuman(M) && M.mind?.special_role == SPECIAL_ROLE_WIZARD)
+ if(ishuman(M) && (M.mind?.special_role == SPECIAL_ROLE_WIZARD || M.mind?.special_role == SPECIAL_ROLE_WIZARD_ADEPT))
update_flags |= M.adjustToxLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE)
update_flags |= M.adjustOxyLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE)
update_flags |= M.adjustBruteLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE)
diff --git a/paradise.dme b/paradise.dme
index 7e7300afa9c..63db91f3361 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1901,6 +1901,7 @@
#include "code\modules\antagonists\vampire\vampire_powers\hemomancer_powers.dm"
#include "code\modules\antagonists\vampire\vampire_powers\umbrae_powers.dm"
#include "code\modules\antagonists\vampire\vampire_powers\vampire_powers.dm"
+#include "code\modules\antagonists\wizard\datum_adept.dm"
#include "code\modules\antagonists\wizard\datum_apprentice.dm"
#include "code\modules\antagonists\wizard\datum_wiz_construct.dm"
#include "code\modules\antagonists\wizard\datum_wizard.dm"
@@ -2317,6 +2318,7 @@
#include "code\modules\events\shuttle_loan\shuttle_loan_datum.dm"
#include "code\modules\events\shuttle_loan\shuttle_loan_event.dm"
#include "code\modules\events\wizard\ghost_wizard_spells.dm"
+#include "code\modules\events\wizard\wizard_adept.dm"
#include "code\modules\fish\fish_eggs.dm"
#include "code\modules\fish\fish_items.dm"
#include "code\modules\fish\fish_types.dm"