diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index fd9bbb5edc2..fac462ce0e1 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -23,6 +23,7 @@
#define APPRENTICE_BLUESPACE "bluespace"
#define APPRENTICE_ROBELESS "robeless"
#define APPRENTICE_HEALING "healing"
+#define APPRENTICE_WILDMAGIC "wildmagic"
//Blob
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index 8aee94711f9..c63a225ad6f 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -30,13 +30,15 @@
dat += "If you are unable to establish contact with your apprentice, you can feed the contract back to the spellbook to refund your points.
"
dat += "Which school of magic is your apprentice studying?:
"
dat += "Destruction
"
- dat += "Your apprentice is skilled in offensive magic. They know Magic Missile and Fireball.
"
+ dat += "Your apprentice is skilled in offensive magic. They know Magic Missile and Fireball.
"
dat += "Bluespace Manipulation
"
- dat += "Your apprentice is able to defy physics, melting through solid objects and travelling great distances in the blink of an eye. They know Teleport and Ethereal Jaunt.
"
+ dat += "Your apprentice is able to defy physics, melting through solid objects and travelling great distances in the blink of an eye. They know Teleport and Ethereal Jaunt.
"
dat += "Healing
"
- dat += "Your apprentice is training to cast spells that will aid your survival. They know Forcewall and Charge and come with a Staff of Healing.
"
+ dat += "Your apprentice is training to cast spells that will aid your survival. They know Forcewall and Charge and come with a Staff of Healing.
"
dat += "Robeless
"
- dat += "Your apprentice is training to cast spells without their robes. They know Knock and Mindswap.
"
+ dat += "Your apprentice is training to cast spells without their robes. They know Knock and Mindswap.
"
+ dat += "Wild Magic
"
+ dat += "Your apprentice is training wild magic. You don't know which spells they got from the wild magic, but it's how the school of wild magic is.
"
user << browse(dat, "window=radio")
onclose(user, "radio")
return
diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm
index baf7719cb50..a8a84e58981 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook.dm
@@ -1,3 +1,5 @@
+#define WIZARD_WILDMAGIC_SPELLPOINT_MULTIPLIER 1.7
+
/datum/spellbook_entry
var/name = "Entry Name"
@@ -387,7 +389,9 @@
/datum/spellbook_entry/item/bloodbottle
name = "Bottle of Blood"
- desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim."
+ desc = "A bottle of magically infused blood, the smell of which will attract extradimensional \
+ beings when broken. Be careful though, the kinds of creatures summoned by blood magic are \
+ indiscriminate in their killing, and you yourself may become a victim."
item_path = /obj/item/antag_spawner/slaughter_demon
limit = 1
category = "Assistance"
@@ -446,6 +450,7 @@
refundable = FALSE
buy_word = "Cast"
var/active = FALSE
+ var/ritual_invocation // This does nothing. This is a flavor to ghosts observing a wizard.
/datum/spellbook_entry/summon/CanBuy(mob/living/carbon/human/user,obj/item/spellbook/book)
return ..() && !active
@@ -462,10 +467,15 @@
dat += "Already cast!
"
return dat
+/datum/spellbook_entry/summon/proc/say_invocation(mob/living/carbon/human/user)
+ if(ritual_invocation)
+ user.say(ritual_invocation, forced = "spell")
+
/datum/spellbook_entry/summon/ghosts
name = "Summon Ghosts"
desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilities to frustrate you."
cost = 0
+ ritual_invocation = "ALADAL DESINARI ODORI'IN TUUR'IS OVOR'E POR"
/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
@@ -473,11 +483,13 @@
active = TRUE
to_chat(user, "You have cast summon ghosts!")
playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1)
+ say_invocation(user)
return TRUE
/datum/spellbook_entry/summon/guns
name = "Summon Guns"
desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. There is a good chance that they will shoot each other first."
+ ritual_invocation = "ALADAL DESINARI ODORI'IN DOL'G FLAM OVOR'E POR"
/datum/spellbook_entry/summon/guns/IsAvailable()
if(!SSticker.mode) // In case spellbook is placed on map
@@ -494,11 +506,13 @@
active = TRUE
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "You have cast summon guns!")
+ say_invocation(user)
return TRUE
/datum/spellbook_entry/summon/magic
name = "Summon Magic"
desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time."
+ ritual_invocation = "ALADAL DESINARI ODORI'IN IDO'LEX SPERMITA OVOR'E POR"
/datum/spellbook_entry/summon/magic/IsAvailable()
if(!SSticker.mode) // In case spellbook is placed on map
@@ -515,12 +529,14 @@
active = TRUE
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "You have cast summon magic!")
+ say_invocation(user)
return TRUE
/datum/spellbook_entry/summon/events
name = "Summon Events"
desc = "Give Murphy's law a little push and replace all events with special wizard ones that will confound and confuse everyone. Multiple castings increase the rate of these events."
var/times = 0
+ ritual_invocation = "ALADAL DESINARI ODORI'IN IDO'LEX MANAG'ROKT OVOR'E POR"
/datum/spellbook_entry/summon/events/IsAvailable()
if(!SSticker.mode) // In case spellbook is placed on map
@@ -537,6 +553,7 @@
times++
playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, 1)
to_chat(user, "You have cast summon events.")
+ say_invocation(user)
return TRUE
/datum/spellbook_entry/summon/events/GetInfo()
@@ -549,18 +566,46 @@
name = "Curse of Madness"
desc = "Curses the station, warping the minds of everyone inside, causing lasting traumas. Warning: this spell can affect you if not cast from a safe distance."
cost = 4
+ ritual_invocation = "ALADAL DESINARI ODORI'IN PORES ENHIDO'LEN MORI MAKA TU"
/datum/spellbook_entry/summon/curse_of_madness/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
active = TRUE
- var/message = stripped_input(user, "Whisper a secret truth to drive your victims to madness.", "Whispers of Madness")
- if(!message)
- return FALSE
+ var/message
+ while(!message)
+ message = stripped_input(user, "Whisper a secret truth to drive your victims to madness.", "Whispers of Madness")
curse_of_madness(user, message)
to_chat(user, "You have cast the curse of insanity!")
playsound(user, 'sound/magic/mandswap.ogg', 50, 1)
return TRUE
+/datum/spellbook_entry/summon/wild_magic
+ name = "Wild Magic Manipulation"
+ desc = "multiply your remaining spell points by 70%(round down) and expand all of them to Wild Magic Manipulation. \
+ You purchase random spells and items upto the spell points you expanded. Spells from this ritual will no longer be refundable even if you learned it manually, but also the book will no longer accept items to refund."
+ cost = 0
+ ritual_invocation = "ALADAL DESINARI ODORI'IN A'EN SPERMITEN G'ATUA H'UN OVORA DUN SPERMITUN"
+
+/datum/spellbook_entry/summon/wild_magic/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
+ if(!book.uses)
+ to_chat(user, "You have no spell points for this ritual.") // You can cast it again as long as you get more spell points somehow
+ return FALSE
+ SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
+ book.uses = round(book.uses*WIZARD_WILDMAGIC_SPELLPOINT_MULTIPLIER) // more spell points
+ book.refuses_refund = TRUE
+ book.desc = "An unearthly tome that once had a great power."
+ while(book.uses)
+ var/datum/spellbook_entry/target = pick(book.entries)
+ if(istype(target, /datum/spellbook_entry/summon/wild_magic))
+ continue // Too lucky to get more spell points, but no.
+ if(target.CanBuy(user,book))
+ if(target.Buy(user,book))
+ book.uses -= target.cost
+ target.refundable = FALSE
+ say_invocation(user)
+ return TRUE
+
+
#undef MINIMUM_THREAT_FOR_RITUALS
/obj/item/spellbook
@@ -574,6 +619,7 @@
var/uses = 10
var/temp = null
var/tab = null
+ var/refuses_refund = FALSE
var/mob/living/carbon/human/owner
var/list/datum/spellbook_entry/entries = list()
var/list/categories = list()
@@ -601,6 +647,9 @@
tab = categories[1]
/obj/item/spellbook/attackby(obj/item/O, mob/user, params)
+ if(refuses_refund)
+ to_chat(user, "Your book is powerless because of Wild Magic Manipulation ritual. The book doesn't accept the item.")
+ return
if(istype(O, /obj/item/antag_spawner/contract))
var/obj/item/antag_spawner/contract/contract = O
if(contract.used)
@@ -750,3 +799,5 @@
tab = sanitize(href_list["page"])
attack_self(H)
return
+
+#undef WIZARD_WILDMAGIC_SPELLPOINT_MULTIPLIER
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 2d227758fcf..6499d2846dd 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -210,20 +210,39 @@
if(APPRENTICE_DESTRUCTION)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball(null))
- to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball.")
+ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball.")
if(APPRENTICE_BLUESPACE)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
- to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.")
+ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.")
if(APPRENTICE_HEALING)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall(null))
H.put_in_hands(new /obj/item/gun/magic/staff/healing(H))
- to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
+ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
if(APPRENTICE_ROBELESS)
owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null))
- to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.")
+ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.")
+ if(APPRENTICE_WILDMAGIC)
+ var/static/list/spell_lists = subtypesof(/datum/spellbook_entry)-typesof(/datum/spellbook_entry/item)-typesof(/datum/spellbook_entry/summon)
+ var/spells_left = 2
+ while(spells_left)
+ var/failsafe = FALSE
+ var/datum/spellbook_entry/chosen_spell = pick(spell_lists)
+ for(var/obj/effect/proc_holder/spell/my_spell in owner.spell_list)
+ if(initial(chosen_spell.name) == initial(my_spell.name)) // You don't learn the same spell
+ failsafe = TRUE
+ break
+ if(is_type_in_typecache(my_spell, initial(chosen_spell.no_coexistance_typecache))) // You don't learn a spell that isn't compatible with another
+ failsafe = TRUE
+ break
+ if(failsafe)
+ continue
+ var/obj/effect/proc_holder/spell/new_spell = initial(chosen_spell.spell_type)
+ owner.AddSpell(new new_spell(null))
+ spells_left--
+ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned special spells that aren't available to standard apprentices.")
/datum/antagonist/wizard/apprentice/create_objectives()
var/datum/objective/protect/new_objective = new /datum/objective/protect