/datum/spellbook_entry var/name = "Entry Name" var/spell_type = null var/desc = "" var/category = "Offensive Spells" var/log_name = "XX" //What it shows up as in logs var/cost = 1 var/refundable = 1 var/surplus = -1 // -1 for infinite, not used by anything atm var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell var/buy_word = "Learn" var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook /datum/spellbook_entry/proc/IsAvailible() // For config prefs / gamemode restrictions - these are round applied return 1 /datum/spellbook_entry/proc/CanBuy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) // Specific circumstances if(book.uses= aspell.level_max) user << "This spell cannot be improved further." return 0 else aspell.name = initial(aspell.name) aspell.spell_level++ aspell.charge_max = round(initial(aspell.charge_max) - aspell.spell_level * (initial(aspell.charge_max) - aspell.cooldown_min)/ aspell.level_max) if(aspell.charge_max < aspell.charge_counter) aspell.charge_counter = aspell.charge_max switch(aspell.spell_level) if(1) user << "You have improved [aspell.name] into Efficient [aspell.name]." aspell.name = "Efficient [aspell.name]" if(2) user << "You have further improved [aspell.name] into Quickened [aspell.name]." aspell.name = "Quickened [aspell.name]" if(3) user << "You have further improved [aspell.name] into Free [aspell.name]." aspell.name = "Free [aspell.name]" if(4) user << "You have further improved [aspell.name] into Instant [aspell.name]." aspell.name = "Instant [aspell.name]" if(aspell.spell_level >= aspell.level_max) user << "This spell cannot be strengthened any further." return 1 //No same spell found - just learn it feedback_add_details("wizard_spell_learned",log_name) user.mind.AddSpell(S) user << "You have learned [S.name]." return 1 /datum/spellbook_entry/proc/CanRefund(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) if(!refundable) return 0 if(!S) S = new spell_type() for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) if(initial(S.name) == initial(aspell.name)) return 1 return 0 /datum/spellbook_entry/proc/Refund(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) //return point value or -1 for failure var/area/wizard_station/A = locate() if(!(user in A.contents)) user << "You can only refund spells at the wizard lair" return -1 if(!S) S = new spell_type() var/spell_levels = 0 for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) if(initial(S.name) == initial(aspell.name)) spell_levels = aspell.spell_level user.mind.spell_list.Remove(aspell) qdel(S) S = null return cost * (spell_levels+1) return -1 /datum/spellbook_entry/proc/GetInfo() if(!S) S = new spell_type() var/dat ="" dat += "[initial(S.name)]" if(S.charge_type == "recharge") dat += " Cooldown:[S.charge_max/10]" dat += " Cost:[cost]
" dat += "[S.desc][desc]
" dat += "[S.clothes_req?"Needs wizard garb":"Can be cast without wizard garb"]
" return dat /datum/spellbook_entry/noclothes name = "Remove Clothes Requirement" spell_type = /obj/effect/proc_holder/spell/noclothes log_name = "NC" /datum/spellbook_entry/fireball name = "Fireball" spell_type = /obj/effect/proc_holder/spell/dumbfire/fireball log_name = "FB" /datum/spellbook_entry/magicm name = "Magic Missile" spell_type = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile log_name = "MM" /datum/spellbook_entry/disintegrate name = "Disintegrate" spell_type = /obj/effect/proc_holder/spell/targeted/touch/disintegrate log_name = "DG" /datum/spellbook_entry/disabletech name = "Disable Tech" spell_type = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech log_name = "DT" category = "Utility Spells" /datum/spellbook_entry/repulse name = "Repulse" spell_type = /obj/effect/proc_holder/spell/aoe_turf/repulse log_name = "RP" /datum/spellbook_entry/timestop name = "Time Stop" spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop log_name = "TS" category = "Defensive" /datum/spellbook_entry/smoke name = "Smoke" spell_type = /obj/effect/proc_holder/spell/targeted/smoke log_name = "SM" category = "Utility Spells" /datum/spellbook_entry/blind name = "Blind" spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind log_name = "BD" /datum/spellbook_entry/mindswap name = "Mindswap" spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer log_name = "MT" category = "Utility Spells" /datum/spellbook_entry/forcewall name = "Force Wall" spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/forcewall log_name = "FW" category = "Utility Spells" /datum/spellbook_entry/blink name = "Blink" spell_type = /obj/effect/proc_holder/spell/targeted/turf_teleport/blink log_name = "BL" category = "Utility Spells" /datum/spellbook_entry/teleport name = "Teleport" spell_type = /obj/effect/proc_holder/spell/targeted/area_teleport/teleport log_name = "TP" category = "Utility Spells" /datum/spellbook_entry/mutate name = "Mutate" spell_type = /obj/effect/proc_holder/spell/targeted/genetic/mutate log_name = "MU" category = "Utility Spells" /datum/spellbook_entry/jaunt name = "Ethereal Jaunt" spell_type = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt log_name = "EJ" category = "Utility Spells" /datum/spellbook_entry/knock name = "Knock" spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock log_name = "KN" category = "Utility Spells" /datum/spellbook_entry/horseman name = "Curse of The Horseman" spell_type = /obj/effect/proc_holder/spell/targeted/horsemask log_name = "HH" /datum/spellbook_entry/fleshtostone name = "Flesh to Stone" spell_type = /obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone log_name = "FS" /datum/spellbook_entry/summonitem name = "Summon Item" spell_type = /obj/effect/proc_holder/spell/targeted/summonitem log_name = "IS" category = "Utility Spells" /datum/spellbook_entry/lightningbolt name = "Lightning Bolt" spell_type = /obj/effect/proc_holder/spell/targeted/lightning log_name = "LB" /datum/spellbook_entry/item name = "Buy Item" category = "Artifacts" refundable = 0 buy_word = "Summon" var/item_path= null /datum/spellbook_entry/item/Buy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) new item_path(get_turf(user)) feedback_add_details("wizard_spell_learned",log_name) return 1 /datum/spellbook_entry/item/GetInfo() var/dat ="" dat += "[name]" dat += " Cost:[cost]
" dat += "[desc]
" if(surplus>=0) dat += "[surplus] left.
" return dat /datum/spellbook_entry/item/staffchange name = "Staff of Change" desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." item_path = /obj/item/weapon/gun/magic/staff/change log_name = "ST" /datum/spellbook_entry/item/staffanimation name = "Staff of Animation" desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines." item_path = /obj/item/weapon/gun/magic/staff/animate log_name = "SA" /datum/spellbook_entry/item/staffchaos name = "Staff of Chaos" desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended." item_path = /obj/item/weapon/gun/magic/staff/chaos log_name = "SC" /datum/spellbook_entry/item/staffdoor name = "Staff of Door Creation" desc = "A particular staff that can mold solid metal into ornate wooden doors. Useful for getting around in the absence of other transportation. Does not work on glass." item_path = /obj/item/weapon/gun/magic/staff/door log_name = "SD" /datum/spellbook_entry/item/scryingorb name = "Scrying Orb" desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you x-ray vision." item_path = /obj/item/weapon/scrying log_name = "SO" /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." item_path = /obj/item/weapon/antag_spawner/slaughter_demon log_name = "BB" limit = 3 /datum/spellbook_entry/item/tarotdeck name = "Tarot Deck" desc = "A deck of tarot cards that can be used to summon a spirit companion for the wizard." item_path = /obj/item/weapon/guardiancreator log_name = "TD" limit = 1 /datum/spellbook_entry/item/scryingorb/Buy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) if(..()) if (!(XRAY in user.mutations)) user.mutations.Add(XRAY) user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS) user.see_in_dark = 8 user.see_invisible = SEE_INVISIBLE_LEVEL_TWO user << "\blue The walls suddenly disappear." return 1 /datum/spellbook_entry/item/soulstones name = "Six Soul Stone Shards and the spell Artificer" desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot." item_path = /obj/item/weapon/storage/belt/soulstone/full log_name = "SS" /datum/spellbook_entry/item/soulstones/Buy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) . =..() if(.) user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/construct(null)) return . /datum/spellbook_entry/item/necrostone name = "A Necromantic Stone" desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command." item_path = /obj/item/device/necromantic_stone log_name = "NS" /datum/spellbook_entry/item/wands name = "Wand Assortment" desc = "A collection of wands that allow for a wide variety of utility. Wands do not recharge, so be conservative in use. Comes in a handy belt." item_path = /obj/item/weapon/storage/belt/wands/full log_name = "WA" /datum/spellbook_entry/item/armor name = "Mastercrafted Armor Set" desc = "An artefact suit of armor that allows you to cast spells while providing more protection against attacks and the void of space." item_path = /obj/item/clothing/suit/space/rig/wizard log_name = "HS" /datum/spellbook_entry/item/armor/Buy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) . = ..() if(.) new /obj/item/clothing/shoes/sandal(get_turf(user)) //In case they've lost them. new /obj/item/clothing/gloves/color/purple(get_turf(user))//To complete the outfit new /obj/item/clothing/head/helmet/space/rig/wizard(get_turf(user)) /datum/spellbook_entry/item/contract name = "Contract of Apprenticeship" desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side." item_path = /obj/item/weapon/contract log_name = "CT" /datum/spellbook_entry/summon name = "Summon Stuff" category = "Rituals" refundable = 0 buy_word = "Cast" var/active = 0 /datum/spellbook_entry/summon/CanBuy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) return ..() && !active /datum/spellbook_entry/summon/GetInfo() var/dat ="" dat += "[name]" if(cost>0) dat += " Cost:[cost]
" else dat += " No Cost
" dat += "[desc]
" if(active) dat += "Already cast!
" return dat /datum/spellbook_entry/summon/guns name = "Summon Guns" category = "Challenges" desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. Just be careful not to stand still too long!" cost = 0 log_name = "SG" /datum/spellbook_entry/summon/guns/Buy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) feedback_add_details("wizard_spell_learned",log_name) user.rightandwrong(0) book.uses += 1 active = 1 user << "You have cast summon guns and gained an extra charge for your spellbook." return 1 /datum/spellbook_entry/summon/magic name = "Summon Magic" category = "Challenges" 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." cost = 0 log_name = "SU" /datum/spellbook_entry/summon/magic/Buy(var/mob/living/carbon/human/user,var/obj/item/weapon/spellbook/book) feedback_add_details("wizard_spell_learned",log_name) user.rightandwrong(1) book.uses += 1 active = 1 user << "You have cast summon magic and gained an extra charge for your spellbook." return 1 /obj/item/weapon/spellbook name = "spell book" desc = "The legendary book of spells of the wizard." icon = 'icons/obj/library.dmi' icon_state ="book" throw_speed = 2 throw_range = 5 w_class = 1.0 var/uses = 5 var/temp = null var/op = 1 var/tab = null var/mob/living/carbon/human/owner var/list/datum/spellbook_entry/entries = list() var/list/categories = list() /obj/item/weapon/spellbook/New() ..() var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon for(var/T in entry_types) var/datum/spellbook_entry/E = new T if(E.IsAvailible()) entries |= E categories |= E.category else qdel(E) tab = categories[1] /obj/item/weapon/spellbook/attackby(obj/item/O as obj, mob/user as mob, params) if(istype(O, /obj/item/weapon/contract)) var/obj/item/weapon/contract/contract = O if(contract.used) user << "The contract has been used, you can't get your points back now!" else user << "You feed the contract back into the spellbook, refunding your points." uses++ qdel(O) if(istype(O, /obj/item/weapon/antag_spawner/slaughter_demon)) user << "On second thought, maybe summoning a demon is a bad idea. You refund your points." uses++ for(var/datum/spellbook_entry/item/bloodbottle/BB in entries) if(!isnull(BB.limit)) BB.limit++ qdel(O) /obj/item/weapon/spellbook/proc/GetCategoryHeader(var/category) var/dat = "" switch(category) if("Offensive Spells") dat += "Spells that can be reused endlessly.
" dat += "The number after the spell name is the cooldown time.
" dat += "You can reduce this number by spending more points on the spell.
" if("Utility Spells") dat += "Spells that can be reused endlessly.
" dat += "The number after the spell name is the cooldown time.
" dat += "You can reduce this number by spending more points on the spell.
" if("Artifacts") dat += "Powerful items imbued with eldritch magics. Summoning one will count towards your maximum number of uses.
" dat += "These items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" if("Challenges") dat += "The Wizard Federation typically has hard limits on the potency and number of spells brought to the station based on risk.
" dat += "Arming the station against you will increases the risk, but will grant you one more charge for your spellbook.
" if("Rituals") dat += "These powerful spells change the very fabric of reality. Not always in your favour.
" return dat /obj/item/weapon/spellbook/proc/wrap(var/content) var/dat = "" dat +="Spellbook" dat += {" "} dat += {"[content]"} return dat /obj/item/weapon/spellbook/attack_self(mob/user as mob) if(!owner) user << "You bind the spellbook to yourself." owner = user return if(user != owner) user << "The [name] does not recognize you as it's owner and refuses to open!" return user.set_machine(src) var/dat = "" dat += "" var/datum/spellbook_entry/E for(var/i=1,i<=entries.len,i++) var/spell_info = "" E = entries[i] spell_info += E.GetInfo() if(E.CanBuy(user,src)) spell_info+= "[E.buy_word]
" else spell_info+= "Can't [E.buy_word]
" if(E.CanRefund(user,src)) spell_info+= "Refund
" spell_info += "
" if(cat_dat[E.category]) cat_dat[E.category] += spell_info for(var/category in categories) dat += "
" dat += GetCategoryHeader(category) dat += cat_dat[category] dat += "
" user << browse(wrap(dat), "window=spellbook;size=700x300") onclose(user, "spellbook") return /obj/item/weapon/spellbook/Topic(href, href_list) if(..()) return 1 var/mob/living/carbon/human/H = usr if(!ishuman(H)) return 1 if(H.mind.special_role == "apprentice") temp = "If you got caught sneaking a peak from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not." return 1 var/datum/spellbook_entry/E = null if(loc == H || (in_range(src, H) && istype(loc, /turf))) H.set_machine(src) if(href_list["buy"]) E = entries[text2num(href_list["buy"])] if(E && E.CanBuy(H,src)) if(E.Buy(H,src)) if(E.limit) E.limit-- uses -= E.cost else if(href_list["refund"]) E = entries[text2num(href_list["refund"])] if(E && E.refundable) var/result = E.Refund(H,src) if(result > 0) if(!isnull(E.limit)) E.limit += result uses += result else if(href_list["page"]) tab = sanitize(href_list["page"]) attack_self(H) return 1 //Single Use Spellbooks// /obj/item/weapon/spellbook/oneuse var/spell = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic var/spellname = "sandbox" var/used = 0 name = "spellbook of " uses = 1 desc = "This template spellbook was never meant for the eyes of man..." /obj/item/weapon/spellbook/oneuse/New() ..() name += spellname /obj/item/weapon/spellbook/oneuse/attack_self(mob/user as mob) var/obj/effect/proc_holder/spell/S = new spell for(var/obj/effect/proc_holder/spell/knownspell in user.spell_list) if(knownspell.type == S.type) if(user.mind) if(user.mind.special_role == "apprentice" || user.mind.special_role == "Wizard") user <<"You're already far more versed in this spell than this flimsy how-to book can provide." else user <<"You've already read this one." return if(used) recoil(user) else user.mind.AddSpell(S) user <<"you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!" user.attack_log += text("\[[time_stamp()]\] [user.real_name] ([user.ckey]) learned the spell [spellname] ([S]).") onlearned(user) /obj/item/weapon/spellbook/oneuse/proc/recoil(mob/user as mob) user.visible_message("[src] glows in a black light!") /obj/item/weapon/spellbook/oneuse/proc/onlearned(mob/user as mob) used = 1 user.visible_message("[src] glows dark for a second!") /obj/item/weapon/spellbook/oneuse/attackby() return /obj/item/weapon/spellbook/oneuse/fireball spell = /obj/effect/proc_holder/spell/dumbfire/fireball spellname = "fireball" icon_state ="bookfireball" desc = "This book feels warm to the touch." /obj/item/weapon/spellbook/oneuse/fireball/recoil(mob/user as mob) ..() explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) qdel(src) /obj/item/weapon/spellbook/oneuse/smoke spell = /obj/effect/proc_holder/spell/targeted/smoke spellname = "smoke" icon_state ="booksmoke" desc = "This book is overflowing with the dank arts." /obj/item/weapon/spellbook/oneuse/smoke/recoil(mob/user as mob) ..() user <<"Your stomach rumbles..." if(user.nutrition) user.nutrition -= 200 if(user.nutrition <= 0) user.nutrition = 0 /obj/item/weapon/spellbook/oneuse/blind spell = /obj/effect/proc_holder/spell/targeted/trigger/blind spellname = "blind" icon_state ="bookblind" desc = "This book looks blurry, no matter how you look at it." /obj/item/weapon/spellbook/oneuse/blind/recoil(mob/user as mob) ..() user <<"You go blind!" user.eye_blind = 10 /obj/item/weapon/spellbook/oneuse/mindswap spell = /obj/effect/proc_holder/spell/targeted/mind_transfer spellname = "mindswap" icon_state ="bookmindswap" desc = "This book's cover is pristine, though its pages look ragged and torn." var/mob/stored_swap = null //Used in used book recoils to store an identity for mindswaps /obj/item/weapon/spellbook/oneuse/mindswap/onlearned() spellname = pick("fireball","smoke","blind","forcewall","knock","horses","charge") icon_state = "book[spellname]" name = "spellbook of [spellname]" //Note, desc doesn't change by design ..() /obj/item/weapon/spellbook/oneuse/mindswap/recoil(mob/user as mob) ..() if(stored_swap in dead_mob_list) stored_swap = null if(!stored_swap) stored_swap = user user <<"For a moment you feel like you don't even know who you are anymore." return if(stored_swap == user) user <<"You stare at the book some more, but there doesn't seem to be anything else to learn..." return if(user.mind.special_verbs.len) for(var/V in user.mind.special_verbs) user.verbs -= V if(stored_swap.mind.special_verbs.len) for(var/V in stored_swap.mind.special_verbs) stored_swap.verbs -= V var/mob/dead/observer/ghost = stored_swap.ghostize(0) ghost.spell_list = stored_swap.spell_list user.mind.transfer_to(stored_swap) stored_swap.spell_list = user.spell_list if(stored_swap.mind.special_verbs.len) for(var/V in user.mind.special_verbs) user.verbs += V ghost.mind.transfer_to(user) user.key = ghost.key user.spell_list = ghost.spell_list if(user.mind.special_verbs.len) for(var/V in user.mind.special_verbs) user.verbs += V stored_swap <<"You're suddenly somewhere else... and someone else?!" user <<"Suddenly you're staring at [src] again... where are you, who are you?!" stored_swap = null /obj/item/weapon/spellbook/oneuse/forcewall spell = /obj/effect/proc_holder/spell/aoe_turf/conjure/forcewall spellname = "forcewall" icon_state ="bookforcewall" desc = "This book has a dedication to mimes everywhere inside the front cover." /obj/item/weapon/spellbook/oneuse/forcewall/recoil(mob/user as mob) ..() user <<"You suddenly feel very solid!" var/obj/structure/closet/statue/S = new /obj/structure/closet/statue(user.loc, user) S.timer = 30 user.drop_item() /obj/item/weapon/spellbook/oneuse/knock spell = /obj/effect/proc_holder/spell/aoe_turf/knock spellname = "knock" icon_state ="bookknock" desc = "This book is hard to hold closed properly." /obj/item/weapon/spellbook/oneuse/knock/recoil(mob/user as mob) ..() user <<"You're knocked down!" user.Weaken(20) /obj/item/weapon/spellbook/oneuse/horsemask spell = /obj/effect/proc_holder/spell/targeted/horsemask spellname = "horses" icon_state ="bookhorses" desc = "This book is more horse than your mind has room for." /obj/item/weapon/spellbook/oneuse/horsemask/recoil(mob/living/carbon/user as mob) if(istype(user, /mob/living/carbon/human)) user <<"HOR-SIE HAS RISEN" var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead magichead.flags |= NODROP //curses! magichead.flags_inv = null //so you can still see their face magichead.voicechange = 1 //NEEEEIIGHH if(!user.unEquip(user.wear_mask)) qdel(user.wear_mask) user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) qdel(src) else user <<"I say thee neigh" /obj/item/weapon/spellbook/oneuse/charge spell = /obj/effect/proc_holder/spell/targeted/charge spellname = "charging" icon_state ="bookcharge" desc = "This book is made of 100% post-consumer wizard." /obj/item/weapon/spellbook/oneuse/charge/recoil(mob/user as mob) ..() user <<"[src] suddenly feels very warm!" empulse(src, 1, 1) /obj/item/weapon/spellbook/oneuse/summonitem spell = /obj/effect/proc_holder/spell/targeted/summonitem spellname = "instant summons" icon_state ="booksummons" desc = "This book is bright and garish, very hard to miss." /obj/item/weapon/spellbook/oneuse/summonitem/recoil(mob/user as mob) ..() user <<"[src] suddenly vanishes!" qdel(src) /obj/item/weapon/spellbook/oneuse/fake_gib spell = /obj/effect/proc_holder/spell/targeted/fake_gib spellname = "disintegrate" icon_state ="bookfireball" desc = "This book feels like it will rip stuff apart."