diff --git a/code/__DEFINES/dcs/signals/signals_spell.dm b/code/__DEFINES/dcs/signals/signals_spell.dm index 8c81adeb8f5..1f9b7516a3d 100644 --- a/code/__DEFINES/dcs/signals/signals_spell.dm +++ b/code/__DEFINES/dcs/signals/signals_spell.dm @@ -29,6 +29,14 @@ #define COMSIG_SPELL_AFTER_CAST "spell_after_cast" /// Sent from /datum/action/cooldown/spell/reset_spell_cooldown() to the spell: () #define COMSIG_SPELL_CAST_RESET "spell_cast_reset" +/// Sent from /datum/action/cooldown/spell/proc/invocation() to the mob: (datum/source, /datum/action/cooldown/spell/spell, list/invocation) +#define COMSIG_MOB_PRE_INVOCATION "spell_pre_invocation" + ///index for the invocation message string + #define INVOCATION_MESSAGE 1 + ///index for the invocation type string + #define INVOCATION_TYPE 2 + ///index for the invocation garble probability number + #define INVOCATION_GARBLE_PROB 3 // Spell type signals diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index 78291bb0a28..c953e648c10 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -77,7 +77,7 @@ #define PLASMAMAN_HELMET_EXEMPT (1<<14) /// Prevents plasmamen from igniting when wearing this #define PLASMAMAN_PREVENT_IGNITION (1<<15) -/// Usable as casting clothes by wizards (only matters for suits and headwear) +/// Usable as casting clothes by wizards (matters for suits, glasses and headwear) #define CASTING_CLOTHES (1<<16) ///Moths can't eat the clothing that has this flag. #define INEDIBLE_CLOTHING (1<<17) diff --git a/code/datums/components/chuunibyou.dm b/code/datums/components/chuunibyou.dm new file mode 100644 index 00000000000..00396a289b8 --- /dev/null +++ b/code/datums/components/chuunibyou.dm @@ -0,0 +1,109 @@ + +/// how much health healed from casting a chuuni spell +#define CHUUNIBYOU_HEAL_AMOUNT 3 +///cooldown between healing to prevent stuff like instant blink spell spam healing +#define CHUUNIBYOU_COOLDOWN_TIME 5 SECONDS + +/** + * ## chuunibyou component! + * + * Component that makes casted spells always a shout invocation, a very dumb one. And their projectiles are dumb too. + * Oh, but it does heal after each spell cast. + */ +/datum/component/chuunibyou + /// invocations per school the spell is from + var/static/list/chuunibyou_invocations + /// amount healed per spell cast + var/heal_amount = CHUUNIBYOU_HEAL_AMOUNT + /// cooldown for healing + COOLDOWN_DECLARE(heal_cooldown) + /// are we casting a spell right now + var/casting_spell = FALSE + +/datum/component/chuunibyou/Initialize() + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + if(!chuunibyou_invocations) + chuunibyou_invocations = list( + SCHOOL_UNSET = "This is embarrassing... I can't remember the words... um... maybe if I just wave my hand like this... no, that's not wor- Ah! There it goes!", + SCHOOL_HOLY = "By the grace of the holy one, I summon the light of salvation. Let my allies rejoice. O, Heaven! Bless them!", + SCHOOL_PSYCHIC = "By the secret of the hidden one, I reveal the truth of creation. Let my mind expand. O, Mystery! Enlighten me!", + SCHOOL_MIME = "O, Silence! Embrace my soul and amplify my gesture. Let me create the illusion and manipulate the perception!", + SCHOOL_RESTORATION = "I invoke the name of the goddess of mercy, hear my plea and grant your blessing to this soul! Divine Grace!", + + SCHOOL_EVOCATION = "Behold, the ultimate power of the Dark Flame Master! I call upon the ancient forces of chaos and destruction to unleash their wrath upon my enemies!", + SCHOOL_TRANSMUTATION = "I invoke the law of equivalent exchange, the balance of the cosmos. As I offer this sacrifice, I demand a new creation. Reveal, the mystery of transmutation!", + SCHOOL_TRANSLOCATION = "By the power of the spatial rifts, I bend the fabric of reality and move across the dimensions! Let nothing stand in my way as I travel to my destination!", + SCHOOL_CONJURATION = "With the eye of fate, I see through the threads of destiny. Nothing can hide from me. Witness me, witness the miracle of manifestation!", + + SCHOOL_NECROMANCY = "I am the Lord of the Dead, the Master of Bones, the Ruler of Shadows. I command the legions of the damned to rise from their graves and serve me!", + SCHOOL_FORBIDDEN = "I renounce the laws of this world and embrace the chaos of the old gods! Let the forbidden power flow through me and destroy everything in its path!", + SCHOOL_SANGUINE = "I cover my eye with an eyepatch to seal my true power, but now I will unleash it upon you. I feast on the life force of my prey and grow stronger with every drop!", + ) + +/datum/component/chuunibyou/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_MOB_SPELL_PROJECTILE, PROC_REF(on_spell_projectile)) + RegisterSignal(parent, COMSIG_MOB_PRE_INVOCATION, PROC_REF(on_pre_invocation)) + RegisterSignal(parent, COMSIG_LIVING_TRY_SPEECH, PROC_REF(on_try_speech)) + RegisterSignal(parent, COMSIG_MOB_AFTER_SPELL_CAST, PROC_REF(on_after_spell_cast)) + +/datum/component/chuunibyou/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, list( + COMSIG_MOB_SPELL_PROJECTILE, + COMSIG_MOB_PRE_INVOCATION, + COMSIG_LIVING_TRY_SPEECH, + COMSIG_MOB_AFTER_SPELL_CAST, + )) + +/// signal sent when the parent tries to speak. we let speech pass if we are casting a spell so mimes still chuuni their spellcasts +/// (this may end in the mime dying) +/datum/component/chuunibyou/proc/on_try_speech(datum/source, message, ignore_spam, forced) + SIGNAL_HANDLER + + if(casting_spell) + return COMPONENT_CAN_ALWAYS_SPEAK + +///signal sent when the parent casts a spell that has a projectile +/datum/component/chuunibyou/proc/on_spell_projectile(mob/living/source, datum/action/cooldown/spell/spell, atom/cast_on, obj/projectile/to_fire) + SIGNAL_HANDLER + + playsound(to_fire,'sound/magic/staff_change.ogg', 75, TRUE) + to_fire.color = "#f825f8" + to_fire.name = "chuuni-[to_fire.name]" + to_fire.set_light(2, 2, LIGHT_COLOR_PINK, TRUE) + +///signal sent before parent invokes a spell +/datum/component/chuunibyou/proc/on_pre_invocation(mob/living/source, datum/action/cooldown/spell/spell, list/invocation_list) + SIGNAL_HANDLER + + // this makes it bypass speech checks (being a mime) until the spell is done casting + // this lets mimes cast with it, but, um... might get them lynched + casting_spell = TRUE + invocation_list[INVOCATION_TYPE] = INVOCATION_SHOUT + invocation_list[INVOCATION_GARBLE_PROB] = 0 + var/chuuni_invocation = chuunibyou_invocations[spell.school] + if(!chuuni_invocation) // someone forgot to update the CHUUNI LIST to include a desc for the new school + stack_trace("Chunnibyou invocations is missing a line for spell school \"[spell.school]\"") + chuuni_invocation = chuunibyou_invocations[SCHOOL_UNSET] + invocation_list[INVOCATION_MESSAGE] = chuuni_invocation + +///signal sent after parent casts a spell +/datum/component/chuunibyou/proc/on_after_spell_cast(mob/living/source, datum/action/cooldown/spell/spell, atom/cast_on) + SIGNAL_HANDLER + + casting_spell = FALSE + if(!COOLDOWN_FINISHED(src, heal_cooldown)) + return + COOLDOWN_START(src, heal_cooldown, CHUUNIBYOU_COOLDOWN_TIME) + + source.heal_overall_damage(heal_amount) + playsound(source, 'sound/magic/staff_healing.ogg', 30) + to_chat(source, span_danger("You feel slightly healed by your chuuni powers.")) + +/datum/component/chuunibyou/no_healing + heal_amount = 0 + +#undef CHUUNIBYOU_HEAL_AMOUNT +#undef CHUUNIBYOU_COOLDOWN_TIME diff --git a/code/game/objects/items/granters/chuuni_granter.dm b/code/game/objects/items/granters/chuuni_granter.dm new file mode 100644 index 00000000000..ebd2109c656 --- /dev/null +++ b/code/game/objects/items/granters/chuuni_granter.dm @@ -0,0 +1,29 @@ +/// Turns the user into a chuunibyou. +/obj/item/book/granter/chuunibyou + name = "I Found a Mysterious Book in the Library That Teaches Me How to Become a Chuunibyou, But It Turns Out It's Actually a Grimoire That Unlocks My Hidden Powers!" + desc = "I'd rather get caught holding a syndicate revolver, honestly." + icon_state ="chuuni_manga" + remarks = list( + "How can anyone believe this stuff?", + "...Why am I wasting my time on this?", + "Coming up with the invocations on demand is actually a skill...", + "I should get a medical eyepatch to complete the look...", + "According to this manga, my power goes up by 5000% by using invocations...", + "Who is this \"dark lord\" fellow? Why does he grant all the powers?", + ) + +/obj/item/book/granter/chuunibyou/can_learn(mob/living/user) + if (!isliving(user)) + return + if (user.GetComponent(/datum/component/chuunibyou)) + to_chat(user, span_warning("You're already a chuunibyou!")) + return + return TRUE + +/obj/item/book/granter/chuunibyou/recoil(mob/living/user) + to_chat(user, span_warning("You just can't bring yourself to read it... it's just not worth the cringe...")) + +/obj/item/book/granter/chuunibyou/on_reading_finished(mob/living/user) + ..() + to_chat(user, span_notice("You've learned how to cast spells in a more chuunibyou-like style!")) + user.AddComponent(/datum/component/chuunibyou/no_healing) diff --git a/code/modules/antagonists/wizard/equipment/chuunibyou_spell.dm b/code/modules/antagonists/wizard/equipment/chuunibyou_spell.dm new file mode 100644 index 00000000000..3e590517f21 --- /dev/null +++ b/code/modules/antagonists/wizard/equipment/chuunibyou_spell.dm @@ -0,0 +1,39 @@ +/datum/action/cooldown/spell/chuuni_invocations + name = "Chuuni Invocations" + desc = "Makes all your spells shout invocations, and the invocations become... stupid. You heal slightly after casting a spell." + button_icon_state = "chuuni" + + school = SCHOOL_FORBIDDEN + cooldown_time = 1 SECONDS + + invocation = "By the decree of the dark lord, I invoke the curse of the chuuni. Let all my spells be tainted by the power of delusion. O, Reality! Bend to my will!" + invocation_type = INVOCATION_SHOUT + spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC|SPELL_REQUIRES_STATION|SPELL_REQUIRES_MIND + antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY + spell_max_level = 1 + +/datum/action/cooldown/spell/chuuni_invocations/cast(mob/living/cast_on) + . = ..() + + to_chat(cast_on, span_green("You focus your arcane knowledge into a slice-of-life format...")) + if(!do_after(cast_on, 5 SECONDS)) + to_chat(cast_on, span_warning("Your focus is broken, and the episodic rom-com moments slowly fade.")) + return + + playsound(cast_on, 'sound/effects/bamf.ogg', 75, TRUE, 5) + to_chat(cast_on, span_danger("You feel your very essense binding to a slice-of-life format!")) + + cast_on.AddComponent(/datum/component/chuunibyou) + + if(ishuman(cast_on)) + var/mob/living/carbon/human/human_cast_on = cast_on + human_cast_on.dropItemToGround(human_cast_on.glasses) + var/obj/item/clothing/head/wizard/wizhat = human_cast_on.head + if(istype(wizhat)) + to_chat(human_cast_on, span_notice("Your [wizhat] transforms into an eyepatch.")) + qdel(wizhat) + else + to_chat(human_cast_on, span_notice("An eyepatch pops into existence over one of your eyes.")) + human_cast_on.equip_to_slot_or_del(new /obj/item/clothing/glasses/eyepatch/medical/chuuni(human_cast_on), ITEM_SLOT_EYES) + + qdel(src) diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm index bb14b8fdd20..1ffac3cf3af 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm @@ -56,6 +56,12 @@ category = "Defensive" no_coexistance_typecache = list(/datum/action/cooldown/spell/splattercasting) +/datum/spellbook_entry/chuunibyou + name = "Chuuni Invocations" + desc = "Makes all your spells shout invocations, and the invocations become... stupid. You heal slightly after casting a spell." + spell_type = /datum/action/cooldown/spell/chuuni_invocations + category = "Defensive" + /datum/spellbook_entry/spacetime_dist name = "Spacetime Distortion" desc = "Entangle the strings of space-time in an area around you, \ diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index ee210d5a980..e55d45ba6c5 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -201,9 +201,34 @@ icon_state = (icon_state == base_icon_state) ? "[base_icon_state]_flipped" : base_icon_state user.update_worn_glasses() -/datum/armor/glasses_science - fire = 80 - acid = 100 +/obj/item/clothing/glasses/eyepatch/medical + name = "medical eyepatch" + desc = "Used by space weeaboos to pretend their eye isn't there, and crewmembers who actually lost their eye to pretend their eye is there." + icon_state = "eyepatch_medical" + base_icon_state = "eyepatch_medical" + inhand_icon_state = null + +/// wizard version +/obj/item/clothing/glasses/eyepatch/medical/chuuni + resistance_flags = FIRE_PROOF | ACID_PROOF + clothing_flags = CASTING_CLOTHES + +/obj/item/clothing/glasses/eyepatch/medical/chuuni/equipped(mob/living/user, slot) + . = ..() + if(slot == ITEM_SLOT_EYES) + ADD_TRAIT(src, TRAIT_NODROP, type) + +/obj/item/clothing/glasses/eyepatch/medical/chuuni/Initialize(mapload) + . = ..() + var/static/list/chuuni_backstories + if(!chuuni_backstories) + chuuni_backstories = list( + "This eyepatch is a seal that contains the power of the demon king. If I remove it, I will unleash a cataclysmic destruction upon the world.", + "This eyepatch is a gift from the angel of light. It allows me to see the true nature of things and protect the innocent from harm.", + "This eyepatch is a mark of my contract with the dragon god. It grants me access to his ancient wisdom and fiery breath.", + "This eyepatch is a symbol of my sacrifice for the sake of love. It hides the scar that I received from saving my beloved from a fatal attack.", + ) + desc = pick(chuuni_backstories) /obj/item/clothing/glasses/monocle name = "monocle" diff --git a/code/modules/library/bookcase.dm b/code/modules/library/bookcase.dm index 3eb9c5de2cb..1cb7eb3d572 100644 --- a/code/modules/library/bookcase.dm +++ b/code/modules/library/bookcase.dm @@ -42,12 +42,17 @@ else SSlibrary.shelves_to_load += src +///proc for doing things after a bookcase is randomly populated +/obj/structure/bookcase/proc/after_random_load() + return + ///Loads the shelf, both by allowing it to generate random items, and by adding its contents to a list used by library machines /obj/structure/bookcase/proc/load_shelf() //Loads a random selection of books in from the db, adds a copy of their info to a global list //To send to library consoles as a starting inventory if(load_random_books) create_random_books(books_to_load, src, FALSE, random_category) + after_random_load() update_appearance() //Make sure you look proper var/area/our_area = get_area(src) diff --git a/code/modules/library/random_books.dm b/code/modules/library/random_books.dm index c9bf7f21aac..1b15eeb5e97 100644 --- a/code/modules/library/random_books.dm +++ b/code/modules/library/random_books.dm @@ -71,12 +71,22 @@ /obj/structure/bookcase/random/fiction name = "bookcase (Fiction)" random_category = "Fiction" + ///have we spawned the chuuni granter + var/static/chuuni_book_spawned = FALSE + +/obj/structure/bookcase/random/fiction/after_random_load() + if(!chuuni_book_spawned && is_station_level(z)) + chuuni_book_spawned = TRUE + new /obj/item/book/granter/chuunibyou(src) + /obj/structure/bookcase/random/nonfiction name = "bookcase (Non-Fiction)" random_category = "Non-fiction" + /obj/structure/bookcase/random/religion name = "bookcase (Religion)" random_category = "Religion" + /obj/structure/bookcase/random/adult name = "bookcase (Adult)" random_category = "Adult" diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 782fd69bd62..dd6f7136efe 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -65,6 +65,8 @@ var/invocation /// What is shown in chat when the user casts the spell, only matters for INVOCATION_EMOTE var/invocation_self_message + /// if true, doesn't garble the invocation sometimes with backticks + var/garbled_invocation_prob = 50 /// What type of invocation the spell is. /// Can be "none", "whisper", "shout", "emote" var/invocation_type = INVOCATION_NONE @@ -190,7 +192,7 @@ if(feedback) to_chat(owner, span_warning("You don't feel strong enough without your robe!")) return FALSE - if(!(human_owner.head?.clothing_flags & CASTING_CLOTHES)) + if(!(human_owner.head?.clothing_flags & CASTING_CLOTHES) || (human_owner.glasses?.clothing_flags & CASTING_CLOTHES)) if(feedback) to_chat(owner, span_warning("You don't feel strong enough without your hat!")) return FALSE @@ -321,28 +323,35 @@ if(!owner) return - if(invocation_type != INVOCATION_NONE) - invocation() + ///even INVOCATION_NONE should go through this because the signal might change that + invocation() if(sound) playsound(get_turf(owner), sound, 50, TRUE) /// The invocation that accompanies the spell, called from spell_feedback() before cast(). /datum/action/cooldown/spell/proc/invocation() - switch(invocation_type) + //lists can be sent by reference, a string would be sent by value + var/list/invocation_list = list(invocation, invocation_type, garbled_invocation_prob) + SEND_SIGNAL(owner, COMSIG_MOB_PRE_INVOCATION, src, invocation_list) + var/used_invocation_message = invocation_list[INVOCATION_MESSAGE] + var/used_invocation_type = invocation_list[INVOCATION_TYPE] + var/used_invocation_garble_prob = invocation_list[INVOCATION_GARBLE_PROB] + + switch(used_invocation_type) if(INVOCATION_SHOUT) - if(prob(50)) - owner.say(invocation, forced = "spell ([src])") + if(prob(used_invocation_garble_prob)) + owner.say(replacetext(used_invocation_message," ","`"), forced = "spell ([src])") else - owner.say(replacetext(invocation," ","`"), forced = "spell ([src])") + owner.say(used_invocation_message, forced = "spell ([src])") if(INVOCATION_WHISPER) - if(prob(50)) - owner.whisper(invocation, forced = "spell ([src])") + if(prob(used_invocation_garble_prob)) + owner.whisper(replacetext(used_invocation_message," ","`"), forced = "spell ([src])") else - owner.whisper(replacetext(invocation," ","`"), forced = "spell ([src])") + owner.whisper(used_invocation_message, forced = "spell ([src])") if(INVOCATION_EMOTE) - owner.visible_message(invocation, invocation_self_message) + owner.visible_message(used_invocation_message, invocation_self_message) /// Checks if the current OWNER of the spell is in a valid state to say the spell's invocation /datum/action/cooldown/spell/proc/try_invoke(feedback = TRUE) diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm index ec4f58b5bac..e6096a582f1 100644 --- a/code/modules/vending/autodrobe.dm +++ b/code/modules/vending/autodrobe.dm @@ -56,6 +56,7 @@ /obj/item/clothing/under/costume/draculass = 1, /obj/item/clothing/suit/costume/gothcoat = 1, /obj/item/clothing/glasses/eyepatch = 1, + /obj/item/clothing/glasses/eyepatch/medical = 1, /obj/item/clothing/under/costume/gi = 1, ), ), diff --git a/code/modules/vending/medical.dm b/code/modules/vending/medical.dm index 4031e296d0c..635b31137e7 100644 --- a/code/modules/vending/medical.dm +++ b/code/modules/vending/medical.dm @@ -18,6 +18,7 @@ /obj/item/stack/medical/suture = 2, /obj/item/stack/medical/bone_gel/four = 4, /obj/item/cane/white = 2, + /obj/item/clothing/glasses/eyepatch/medical = 2, ) contraband = list( /obj/item/storage/box/gum/happiness = 3, diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi index b9fc18544ab..9d657c6b88c 100644 Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ diff --git a/icons/mob/clothing/eyes.dmi b/icons/mob/clothing/eyes.dmi index 115b27140ed..29cff130c4b 100644 Binary files a/icons/mob/clothing/eyes.dmi and b/icons/mob/clothing/eyes.dmi differ diff --git a/icons/obj/clothing/glasses.dmi b/icons/obj/clothing/glasses.dmi index b950677d0a0..a9d213d9fd8 100644 Binary files a/icons/obj/clothing/glasses.dmi and b/icons/obj/clothing/glasses.dmi differ diff --git a/icons/obj/library.dmi b/icons/obj/library.dmi index b78c8db97a8..b079fd32949 100644 Binary files a/icons/obj/library.dmi and b/icons/obj/library.dmi differ diff --git a/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_glasses.dm b/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_glasses.dm index 96b3aa755b0..02196efff16 100644 --- a/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_glasses.dm +++ b/modular_skyrat/modules/loadouts/loadout_items/loadout_datum_glasses.dm @@ -99,10 +99,14 @@ GLOBAL_LIST_INIT(loadout_glasses, generate_loadout_items(/datum/loadout_item/gla name = "Eyepatch" item_path = /obj/item/clothing/glasses/eyepatch -/datum/loadout_item/glasses/whiteeyepatch +/datum/loadout_item/glasses/white_eyepatch name = "White Eyepatch" item_path = /obj/item/clothing/glasses/eyepatch/white +/datum/loadout_item/glasses/medical_eyepatch + name = "Medical Eyepatch" + item_path = /obj/item/clothing/glasses/eyepatch/medical + /datum/loadout_item/glasses/blindfold name = "Blindfold" item_path = /obj/item/clothing/glasses/blindfold diff --git a/tgstation.dme b/tgstation.dme index 0b301fb93db..6a2163cdc3f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -946,6 +946,7 @@ #include "code\datums\components\butchering.dm" #include "code\datums\components\caltrop.dm" #include "code\datums\components\chasm.dm" +#include "code\datums\components\chuunibyou.dm" #include "code\datums\components\cleaner.dm" #include "code\datums\components\clickbox.dm" #include "code\datums\components\clothing_fov_visor.dm" @@ -2025,6 +2026,7 @@ #include "code\game\objects\items\food\sweets.dm" #include "code\game\objects\items\food\vegetables.dm" #include "code\game\objects\items\granters\_granters.dm" +#include "code\game\objects\items\granters\chuuni_granter.dm" #include "code\game\objects\items\granters\oragami.dm" #include "code\game\objects\items\granters\sign_language.dm" #include "code\game\objects\items\granters\crafting\_crafting_granter.dm" @@ -2748,6 +2750,7 @@ #include "code\modules\antagonists\wizard\slaughter_antag.dm" #include "code\modules\antagonists\wizard\wizard.dm" #include "code\modules\antagonists\wizard\equipment\artefact.dm" +#include "code\modules\antagonists\wizard\equipment\chuunibyou_spell.dm" #include "code\modules\antagonists\wizard\equipment\enchanted_clown_suit.dm" #include "code\modules\antagonists\wizard\equipment\soulstone.dm" #include "code\modules\antagonists\wizard\equipment\wizard_spellbook.dm"