diff --git a/code/datums/spells/charge.dm b/code/datums/spells/charge.dm
new file mode 100644
index 00000000000..342e11049eb
--- /dev/null
+++ b/code/datums/spells/charge.dm
@@ -0,0 +1,82 @@
+/obj/effect/proc_holder/spell/targeted/charge
+ name = "Charge"
+ desc = "This spell can be used to charge up spent magical artifacts, among other things."
+
+ school = "transmutation"
+ charge_max = 600
+ clothes_req = 0
+ invocation = "DIRI CEL"
+ invocation_type = "whisper"
+ range = -1
+ cooldown_min = 400 //50 deciseconds reduction per rank
+ include_user = 1
+
+
+/obj/effect/proc_holder/spell/targeted/charge/cast(list/targets)
+ for(var/mob/living/user in targets)
+ var/list/hand_items = list(user.get_active_hand(),user.get_inactive_hand())
+ var/charged_item = null
+ var/burnt_out = 0
+ for(var/obj/item in hand_items)
+ if(istype(item, /obj/item/weapon/grab))
+ var/obj/item/weapon/grab/G = item
+ if(G.affecting)
+ var/mob/M = G.affecting
+ if(M.spell_list.len != 0)
+ for(var/obj/effect/proc_holder/spell/S in M.spell_list)
+ S.charge_counter = S.charge_max
+ M <<"you feel raw magic flowing through you, it feels good!"
+ else
+ M <<"you feel very strange for a moment, but then it passes."
+ burnt_out = 1
+ charged_item = M
+ break
+ else if(istype(item, /obj/item/weapon/spellbook/oneuse))
+ var/obj/item/weapon/spellbook/oneuse/I = item
+ if(prob(80))
+ user.visible_message("[I] catches fire!")
+ del(I)
+ else
+ I.used = 0
+ charged_item = I
+ break
+ else if(istype(item, /obj/item/weapon/gun/magic))
+ var/obj/item/weapon/gun/magic/I = item
+ if(prob(80))
+ I.max_charges--
+ if(I.max_charges <= 0)
+ I.max_charges = 0
+ burnt_out = 1
+ I.charges = I.max_charges
+ charged_item = I
+ break
+ else if(istype(item, /obj/item/weapon/cell/))
+ var/obj/item/weapon/cell/C = item
+ if(prob(80))
+ C.maxcharge -= 200
+ if(C.maxcharge <= 1) //Div by 0 protection
+ C.maxcharge = 1
+ burnt_out = 1
+ C.charge = C.maxcharge
+ charged_item = C
+ break
+ else if(item.contents)
+ var/obj/I = null
+ for(I in item.contents)
+ if(istype(I, /obj/item/weapon/cell/))
+ var/obj/item/weapon/cell/C = I
+ if(prob(80))
+ C.maxcharge -= 200
+ if(C.maxcharge <= 1) //Div by 0 protection
+ C.maxcharge = 1
+ burnt_out = 1
+ C.charge = C.maxcharge
+ item.update_icon()
+ charged_item = item
+ break
+ if(!charged_item)
+ user << "you feel magical power surging to your hands, but the feeling rapidly fades..."
+ else if(burnt_out)
+ user << "[charged_item] doesn't seem to be reacting to the spell..."
+ else
+ user << "[charged_item] suddenly feels very warm!"
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index 5bad56a6135..3d404dfc846 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -27,6 +27,8 @@
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 += "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.
"
user << browse(dat, "window=radio")
@@ -66,6 +68,11 @@
M.spell_list += new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(M)
M.spell_list += new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(M)
M << "Your service has not gone unrewarded, however. Studying under [H.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt."
+ if("healing")
+ M.spell_list += new /obj/effect/proc_holder/spell/targeted/charge(M)
+ M.spell_list += new /obj/effect/proc_holder/spell/aoe_turf/conjure/forcewall(M)
+ M.equip_to_slot_or_del(new /obj/item/weapon/gun/magic/staff/healing(M), slot_r_hand)
+ M << "Your service has not gone unrewarded, however. Studying under [H.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall."
if("robeless")
M.spell_list += new /obj/effect/proc_holder/spell/aoe_turf/knock(M)
M.spell_list += new /obj/effect/proc_holder/spell/targeted/mind_transfer(M)
diff --git a/code/game/gamemodes/wizard/rightandwrong.dm b/code/game/gamemodes/wizard/rightandwrong.dm
index c18732cdb01..891a2dbff7c 100644
--- a/code/game/gamemodes/wizard/rightandwrong.dm
+++ b/code/game/gamemodes/wizard/rightandwrong.dm
@@ -1,11 +1,12 @@
-/mob/proc/rightandwrong()
- usr << "You summoned guns!"
- message_admins("[key_name_admin(usr, 1)] summoned guns!")
+/mob/proc/rightandwrong(var/summon_type) //0 = Summon Guns, 1 = Summon Magic
+ usr << "You summoned [summon_type ? "magic" : "guns"]!"
+ message_admins("[key_name_admin(usr, 1)] summoned [summon_type ? "magic" : "guns"]!")
for(var/mob/living/carbon/human/H in player_list)
if(H.stat == 2 || !(H.client)) continue
- if(is_special_character(H)) continue
+ if(H.mind)
+ if(H.mind.special_role == "Wizard" || H.mind.special_role == "apprentice") continue
if(prob(25))
ticker.mode.traitors += H.mind
H.mind.special_role = "traitor"
@@ -18,46 +19,95 @@
for(var/datum/objective/OBJ in H.mind.objectives)
H << "Objective #[obj_count]: [OBJ.explanation_text]"
obj_count++
- var/randomize = pick("taser","egun","laser","revolver","detective","smg","nuclear","deagle","gyrojet","pulse","silenced","cannon","doublebarrel","shotgun","combatshotgun","mateba","smg","uzi","crossbow","saw")
- switch (randomize)
- if("taser")
- new /obj/item/weapon/gun/energy/taser(get_turf(H))
- if("egun")
- new /obj/item/weapon/gun/energy/gun(get_turf(H))
- if("laser")
- new /obj/item/weapon/gun/energy/laser(get_turf(H))
- if("revolver")
- new /obj/item/weapon/gun/projectile/revolver(get_turf(H))
- if("detective")
- new /obj/item/weapon/gun/projectile/revolver/detective(get_turf(H))
- if("smg")
- new /obj/item/weapon/gun/projectile/automatic/c20r(get_turf(H))
- if("nuclear")
- new /obj/item/weapon/gun/energy/gun/nuclear(get_turf(H))
- if("deagle")
- new /obj/item/weapon/gun/projectile/automatic/deagle/camo(get_turf(H))
- if("gyrojet")
- new /obj/item/weapon/gun/projectile/automatic/gyropistol(get_turf(H))
- if("pulse")
- new /obj/item/weapon/gun/energy/pulse_rifle(get_turf(H))
- if("silenced")
- new /obj/item/weapon/gun/projectile/automatic/pistol(get_turf(H))
- new /obj/item/weapon/silencer(get_turf(H))
- if("cannon")
- new /obj/item/weapon/gun/energy/lasercannon(get_turf(H))
- if("doublebarrel")
- new /obj/item/weapon/gun/projectile/revolver/doublebarrel(get_turf(H))
- if("shotgun")
- new /obj/item/weapon/gun/projectile/shotgun/(get_turf(H))
- if("combatshotgun")
- new /obj/item/weapon/gun/projectile/shotgun/combat(get_turf(H))
- if("mateba")
- new /obj/item/weapon/gun/projectile/revolver/mateba(get_turf(H))
- if("smg")
- new /obj/item/weapon/gun/projectile/automatic(get_turf(H))
- if("uzi")
- new /obj/item/weapon/gun/projectile/automatic/mini_uzi(get_turf(H))
- if("crossbow")
- new /obj/item/weapon/gun/energy/crossbow(get_turf(H))
- if("saw")
- new /obj/item/weapon/gun/projectile/automatic/l6_saw(get_turf(H))
\ No newline at end of file
+ var/randomizeguns = pick("taser","egun","laser","revolver","detective","smg","nuclear","deagle","gyrojet","pulse","silenced","cannon","doublebarrel","shotgun","combatshotgun","mateba","smg","uzi","crossbow","saw")
+ var/randomizemagic = pick("fireball","smoke","blind","mindswap","forcewall","knock","horsemask","charge","wandnothing", "wanddeath", "wandresurrection", "wandpolymorph", "wandteleport", "wanddoor", "wandfireball", "staffchange", "staffanimation", "staffhealing", "armor", "scrying")
+ if(!summon_type)
+ switch (randomizeguns)
+ if("taser")
+ new /obj/item/weapon/gun/energy/taser(get_turf(H))
+ if("egun")
+ new /obj/item/weapon/gun/energy/gun(get_turf(H))
+ if("laser")
+ new /obj/item/weapon/gun/energy/laser(get_turf(H))
+ if("revolver")
+ new /obj/item/weapon/gun/projectile/revolver(get_turf(H))
+ if("detective")
+ new /obj/item/weapon/gun/projectile/revolver/detective(get_turf(H))
+ if("smg")
+ new /obj/item/weapon/gun/projectile/automatic/c20r(get_turf(H))
+ if("nuclear")
+ new /obj/item/weapon/gun/energy/gun/nuclear(get_turf(H))
+ if("deagle")
+ new /obj/item/weapon/gun/projectile/automatic/deagle/camo(get_turf(H))
+ if("gyrojet")
+ new /obj/item/weapon/gun/projectile/automatic/gyropistol(get_turf(H))
+ if("pulse")
+ new /obj/item/weapon/gun/energy/pulse_rifle(get_turf(H))
+ if("silenced")
+ new /obj/item/weapon/gun/projectile/automatic/pistol(get_turf(H))
+ new /obj/item/weapon/silencer(get_turf(H))
+ if("cannon")
+ new /obj/item/weapon/gun/energy/lasercannon(get_turf(H))
+ if("doublebarrel")
+ new /obj/item/weapon/gun/projectile/revolver/doublebarrel(get_turf(H))
+ if("shotgun")
+ new /obj/item/weapon/gun/projectile/shotgun/(get_turf(H))
+ if("combatshotgun")
+ new /obj/item/weapon/gun/projectile/shotgun/combat(get_turf(H))
+ if("mateba")
+ new /obj/item/weapon/gun/projectile/revolver/mateba(get_turf(H))
+ if("smg")
+ new /obj/item/weapon/gun/projectile/automatic(get_turf(H))
+ if("uzi")
+ new /obj/item/weapon/gun/projectile/automatic/mini_uzi(get_turf(H))
+ if("crossbow")
+ new /obj/item/weapon/gun/energy/crossbow(get_turf(H))
+ if("saw")
+ new /obj/item/weapon/gun/projectile/automatic/l6_saw(get_turf(H))
+ else
+ switch (randomizemagic)
+ if("fireball")
+ new /obj/item/weapon/spellbook/oneuse/fireball(get_turf(H))
+ if("smoke")
+ new /obj/item/weapon/spellbook/oneuse/smoke(get_turf(H))
+ if("blind")
+ new /obj/item/weapon/spellbook/oneuse/blind(get_turf(H))
+ if("mindswap")
+ new /obj/item/weapon/spellbook/oneuse/mindswap(get_turf(H))
+ if("forcewall")
+ new /obj/item/weapon/spellbook/oneuse/forcewall(get_turf(H))
+ if("knock")
+ new /obj/item/weapon/spellbook/oneuse/knock(get_turf(H))
+ if("horsemask")
+ new /obj/item/weapon/spellbook/oneuse/horsemask(get_turf(H))
+ if("charge")
+ new /obj/item/weapon/spellbook/oneuse/charge(get_turf(H))
+ if("wandnothing")
+ new /obj/item/weapon/gun/magic/wand(get_turf(H))
+ if("wanddeath")
+ new /obj/item/weapon/gun/magic/wand/death(get_turf(H))
+ if("wandresurrection")
+ new /obj/item/weapon/gun/magic/wand/resurrection(get_turf(H))
+ if("wandpolymorph")
+ new /obj/item/weapon/gun/magic/wand/polymorph(get_turf(H))
+ if("wandteleport")
+ new /obj/item/weapon/gun/magic/wand/teleport(get_turf(H))
+ if("wanddoor")
+ new /obj/item/weapon/gun/magic/wand/door(get_turf(H))
+ if("staffchange")
+ new /obj/item/weapon/gun/magic/staff/change(get_turf(H))
+ if("staffanimation")
+ new /obj/item/weapon/gun/magic/staff/animate(get_turf(H))
+ if("staffhealing")
+ new /obj/item/weapon/gun/magic/staff/healing(get_turf(H))
+ if("armor")
+ new /obj/item/clothing/suit/space/rig/wizard(get_turf(H))
+ new /obj/item/clothing/head/helmet/space/rig/wizard(get_turf(H))
+ if("scrying")
+ new /obj/item/weapon/scrying(get_turf(H))
+ if (!(XRAY in H.mutations))
+ H.mutations.Add(XRAY)
+ H.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS)
+ H.see_in_dark = 8
+ H.see_invisible = SEE_INVISIBLE_LEVEL_TWO
+ H << "\blue The walls suddenly disappear."
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index 156554d486d..36417f0bd36 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -86,6 +86,9 @@
dat += "Summon Guns (One time use, global spell)
"
dat += "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill eachother. Just be careful not to get hit in the crossfire!
"
+ dat += "Summon Magic (One time use, global spell)
"
+ dat += "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time.
"
+
dat += "
"
dat += "Artefacts:
"
dat += "Powerful items imbued with eldritch magics. Summoning one will count towards your maximum number of spells.
"
@@ -150,7 +153,7 @@
uses--
/*
*/
- var/list/available_spells = list(magicmissile = "Magic Missile", fireball = "Fireball", disintegrate = "Disintegrate", disabletech = "Disable Tech", smoke = "Smoke", blind = "Blind", mindswap = "Mind Transfer", forcewall = "Forcewall", blink = "Blink", teleport = "Teleport", mutate = "Mutate", etherealjaunt = "Ethereal Jaunt", knock = "Knock", horseman = "Curse of the Horseman", fleshtostone = "Flesh to Stone", summonguns = "Summon Guns", staffchange = "Staff of Change", soulstone = "Six Soul Stone Shards and the spell Artificer", armor = "Mastercrafted Armor Set", staffanimate = "Staff of Animation")
+ var/list/available_spells = list(magicmissile = "Magic Missile", fireball = "Fireball", disintegrate = "Disintegrate", disabletech = "Disable Tech", smoke = "Smoke", blind = "Blind", mindswap = "Mind Transfer", forcewall = "Forcewall", blink = "Blink", teleport = "Teleport", mutate = "Mutate", etherealjaunt = "Ethereal Jaunt", knock = "Knock", horseman = "Curse of the Horseman", fleshtostone = "Flesh to Stone", summonguns = "Summon Guns", summonmagic = "Summon Magic", staffchange = "Staff of Change", soulstone = "Six Soul Stone Shards and the spell Artificer", armor = "Mastercrafted Armor Set", staffanimate = "Staff of Animation")
var/already_knows = 0
for(var/obj/effect/proc_holder/spell/aspell in H.spell_list)
if(available_spells[href_list["spell_choice"]] == initial(aspell.name))
@@ -246,12 +249,17 @@
temp = "You have learned flesh to stone."
if("summonguns")
feedback_add_details("wizard_spell_learned","SG") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
- H.rightandwrong()
+ H.rightandwrong(0)
max_uses--
temp = "You have cast summon guns."
+ if("summonmagic")
+ feedback_add_details("wizard_spell_learned","SM") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
+ H.rightandwrong(1)
+ max_uses--
+ temp = "You have cast summon magic."
if("staffchange")
feedback_add_details("wizard_spell_learned","ST") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
- new /obj/item/weapon/gun/energy/staff(get_turf(H))
+ new /obj/item/weapon/gun/magic/staff/change(get_turf(H))
temp = "You have purchased a staff of change."
max_uses--
if("soulstone")
@@ -270,7 +278,7 @@
max_uses--
if("staffanimation")
feedback_add_details("wizard_spell_learned","SA") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
- new /obj/item/weapon/gun/energy/staff/animate(get_turf(H))
+ new /obj/item/weapon/gun/magic/staff/animate(get_turf(H))
temp = "You have purchased a staff of animation."
max_uses--
if("contract")
@@ -295,3 +303,153 @@
attack_self(H)
return
+
+//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
+ max_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.spell_list += S
+ user <<"you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!"
+ 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)
+ del(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."
+
+/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)
+ ..()
+ user <<"You suddenly don't feel like yourself!"
+ wabbajack(user)
+
+/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.canremove = 0 //curses!
+ magichead.flags_inv = null //so you can still see their face
+ magichead.voicechange = 1 //NEEEEIIGHH
+ user.drop_from_inventory(user.wear_mask)
+ user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1)
+ del(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)
\ No newline at end of file
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index 4968f427ea2..11668d874c5 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -25,8 +25,6 @@
if (istype(G, /obj/item/weapon/gun/energy/gun/nuclear) || istype(G, /obj/item/weapon/gun/energy/crossbow))
user << "Your gun's recharge port was removed to make room for a miniaturized reactor."
return
- if (istype(G, /obj/item/weapon/gun/energy/staff))
- return
user.drop_item()
G.loc = src
charging = G
diff --git a/code/game/machinery/turrets.dm b/code/game/machinery/turrets.dm
index 39da02b0e1d..2316f12f45a 100644
--- a/code/game/machinery/turrets.dm
+++ b/code/game/machinery/turrets.dm
@@ -219,7 +219,7 @@
if(3)
A = new /obj/item/projectile/beam/pulse( loc )
if(4)
- A = new /obj/item/projectile/change( loc )
+ A = new /obj/item/projectile/magic/change( loc )
if(5)
A = new /obj/item/projectile/bluetag( loc )
if(6)
diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm
index e3b03bad0cb..4c04ea6ce13 100644
--- a/code/modules/library/lib_items.dm
+++ b/code/modules/library/lib_items.dm
@@ -60,7 +60,7 @@
state = 0
if(2)
- if(istype(I, /obj/item/weapon/book))
+ if(istype(I, /obj/item/weapon/book) || istype(I, /obj/item/weapon/spellbook))
user.drop_item()
I.loc = src
update_icon()
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 6b0d70beece..c1ed4531c26 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -26,55 +26,6 @@
charge_cost = 100
projectile_type = "/obj/item/projectile/energy/declone"
-obj/item/weapon/gun/energy/staff
- name = "staff of change"
- desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself"
- icon = 'icons/obj/gun.dmi'
- icon_state = "staffofchange"
- item_state = "staffofchange"
- fire_sound = 'sound/weapons/emitter.ogg'
- flags = FPRINT | TABLEPASS | CONDUCT
- slot_flags = SLOT_BACK
- w_class = 5
- charge_cost = 200
- projectile_type = "/obj/item/projectile/change"
- origin_tech = null
- clumsy_check = 0
- var/charge_tick = 0
-
-
- New()
- ..()
- processing_objects.Add(src)
-
-
- Del()
- processing_objects.Remove(src)
- ..()
-
-
- process()
- charge_tick++
- if(charge_tick < 4) return 0
- charge_tick = 0
- if(!power_supply) return 0
- power_supply.give(200)
- return 1
-
- update_icon()
- return
-
- shoot_with_empty_chamber(mob/living/user as mob|obj)
- user << "The [name] whizzles quietly."
- return
-
-/obj/item/weapon/gun/energy/staff/animate
- name = "staff of animation"
- desc = "An artefact that spits bolts of life-force which causes objects which are hit by it to animate and come to life! This magic doesn't affect machines."
- projectile_type = "/obj/item/projectile/animate"
- icon_state = "staffofanimation"
- item_state = "staffofanimation"
-
/obj/item/weapon/gun/energy/floragun
name = "floral somatoray"
desc = "A tool that discharges controlled radiation which induces mutation in plant cells."
diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm
new file mode 100644
index 00000000000..b5146ca47ed
--- /dev/null
+++ b/code/modules/projectiles/guns/magic.dm
@@ -0,0 +1,53 @@
+/obj/item/weapon/gun/magic
+ name = "staff of nothing"
+ desc = "This staff is boring to watch because even though it came first you've seen everything it can do in other staves for years."
+ icon = 'icons/obj/gun.dmi'
+ icon_state = "staffofnothing"
+ item_state = "staff"
+ fire_sound = 'sound/weapons/emitter.ogg'
+ flags = FPRINT | TABLEPASS | CONDUCT
+ w_class = 5
+ var/projectile_type = "/obj/item/projectile/magic"
+ var/max_charges = 6
+ var/charges = 0
+ var/recharge_rate = 4
+ var/charge_tick = 0
+ var/can_charge = 1
+ origin_tech = null
+ clumsy_check = 0
+
+/obj/item/weapon/gun/magic/process_chambered()
+ if(in_chamber) return 1
+ if(!charges) return 0
+ if(!projectile_type) return 0
+ in_chamber = new projectile_type(src)
+ return 1
+
+/obj/item/weapon/gun/magic/afterattack(atom/target as mob, mob/living/user as mob, flag)
+ ..()
+ if(charges && !in_chamber && !flag) charges--
+
+/obj/item/weapon/gun/magic/New()
+ ..()
+ charges = max_charges
+ if(can_charge) processing_objects.Add(src)
+
+
+/obj/item/weapon/gun/magic/Del()
+ if(can_charge) processing_objects.Remove(src)
+ ..()
+
+
+/obj/item/weapon/gun/magic/process()
+ charge_tick++
+ if(charge_tick < recharge_rate || charges >= max_charges) return 0
+ charge_tick = 0
+ charges++
+ return 1
+
+/obj/item/weapon/gun/magic/update_icon()
+ return
+
+/obj/item/weapon/gun/magic/shoot_with_empty_chamber(mob/living/user as mob|obj)
+ user << "The [name] whizzles quietly."
+ return
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
new file mode 100644
index 00000000000..dd38641e184
--- /dev/null
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -0,0 +1,23 @@
+obj/item/weapon/gun/magic/staff/
+ slot_flags = SLOT_BACK
+
+obj/item/weapon/gun/magic/staff/change
+ name = "staff of change"
+ desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself"
+ projectile_type = "/obj/item/projectile/magic/change"
+ icon_state = "staffofchange"
+ item_state = "staffofchange"
+
+obj/item/weapon/gun/magic/staff/animate
+ name = "staff of animation"
+ desc = "An artefact that spits bolts of life-force which causes objects which are hit by it to animate and come to life! This magic doesn't affect machines."
+ projectile_type = "/obj/item/projectile/magic/animate"
+ icon_state = "staffofanimation"
+ item_state = "staffofanimation"
+
+obj/item/weapon/gun/magic/staff/healing
+ name = "staff of healing"
+ desc = "An artefact that spits bolts of restoring magic which can remove ailments of all kinds and even raise the dead."
+ projectile_type = "/obj/item/projectile/magic/resurrection"
+ icon_state = "staffofhealing"
+ item_state = "staffofhealing"
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
new file mode 100644
index 00000000000..1bfc24b7ba2
--- /dev/null
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -0,0 +1,119 @@
+/obj/item/weapon/gun/magic/wand/
+ name = "wand of nothing"
+ desc = "It's not just a stick, it's a MAGIC stick!"
+ projectile_type = "/obj/item/projectile/magic"
+ icon_state = "wand6"
+ item_state = "wand"
+ w_class = 2
+ can_charge = 0
+ max_charges = 100 //100, 50, 50, 34 (max charge distribution by 25%ths)
+ var/variable_charges = 1
+
+/obj/item/weapon/gun/magic/wand/New()
+ if(prob(75) && variable_charges) //25% chance of listed max charges, 50% chance of 1/2 max charges, 25% chance of 1/3 max charges
+ if(prob(33))
+ max_charges = Ceiling(max_charges / 3)
+ else
+ max_charges = Ceiling(max_charges / 2)
+ ..()
+
+/obj/item/weapon/gun/magic/wand/examine()
+ ..()
+ usr << "Has [charges] charge\s remaining."
+ return
+
+/obj/item/weapon/gun/magic/wand/attack(atom/target as mob, mob/living/user as mob)
+ if(target == user)
+ return
+ ..()
+
+/obj/item/weapon/gun/magic/wand/afterattack(atom/target as mob, mob/living/user as mob)
+ if(target == user)
+ if(charges)
+ zap_self(user)
+ else
+ user << "The [name] whizzles quietly."
+ else
+ ..()
+
+/obj/item/weapon/gun/magic/wand/proc/zap_self(mob/living/user as mob)
+ user.visible_message("[user] zaps \himself with [src]!")
+ playsound(user, fire_sound, 50, 1)
+ user.attack_log += "\[[time_stamp()]\] [user]/[user.ckey] zapped \himself with a [src]"
+
+/obj/item/weapon/gun/magic/wand/death
+ name = "wand of death"
+ desc = "This deadly wand overwhelms the victim's body with pure energy, slaying them without fail."
+ projectile_type = "/obj/item/projectile/magic/death"
+ icon_state = "wand4"
+ max_charges = 3 //3, 2, 2, 1
+
+/obj/item/weapon/gun/magic/wand/death/zap_self(mob/living/user as mob)
+ var/message ="You irradiate yourself with pure energy! "
+ message += pick("Do not pass go. Do not collect 200 zorkmids.","You feel more confident in your spell casting skills.","You Die...","Do you want your possessions identified?")
+ user << message
+ user.adjustOxyLoss(500)
+ charges--
+ ..()
+
+/obj/item/weapon/gun/magic/wand/resurrection
+ name = "wand of resurrection"
+ desc = "This wand uses healing magics to heal and revive. They are rarely utilized within the Wizard Federation for some reason."
+ projectile_type = "/obj/item/projectile/magic/resurrection"
+ icon_state = "wand1"
+ max_charges = 3 //3, 2, 2, 1
+
+/obj/item/weapon/gun/magic/wand/resurrection/zap_self(mob/living/user as mob)
+ user.revive()
+ user << "You feel great!"
+ charges--
+ ..()
+
+/obj/item/weapon/gun/magic/wand/polymorph
+ name = "wand of polymorph"
+ desc = "This wand is attuned to chaos and will radically alter the victim's form."
+ projectile_type = "/obj/item/projectile/magic/change"
+ icon_state = "wand5"
+ max_charges = 10 //10, 5, 5, 4
+
+/obj/item/weapon/gun/magic/wand/polymorph/zap_self(mob/living/user as mob)
+ ..() //because the user mob ceases to exists by the time wabbajack fully resolves
+ wabbajack(user)
+ charges--
+
+/obj/item/weapon/gun/magic/wand/teleport
+ name = "wand of teleportation"
+ desc = "This wand will wrench targets through space and time to move them somewhere else."
+ projectile_type = "/obj/item/projectile/magic/teleport"
+ icon_state = "wand3"
+ max_charges = 10 //10, 5, 5, 4
+
+/obj/item/weapon/gun/magic/wand/teleport/zap_self(mob/living/user as mob)
+ do_teleport(user, user, 10)
+ var/datum/effect/effect/system/harmless_smoke_spread/smoke = new /datum/effect/effect/system/harmless_smoke_spread()
+ smoke.set_up(10, 0, user.loc)
+ smoke.start()
+ charges--
+ ..()
+
+/obj/item/weapon/gun/magic/wand/door
+ name = "wand of door creation"
+ desc = "This particular wand can create doors in any wall for the unscrupulous wizard who shuns teleportation magics."
+ projectile_type = "/obj/item/projectile/magic/door"
+ icon_state = "wand0"
+ max_charges = 20 //20, 10, 10, 7
+
+/obj/item/weapon/gun/magic/wand/door/zap_self()
+ return
+
+/obj/item/weapon/gun/magic/wand/fireball
+ name = "wand of fireball"
+ desc = "This wand shoots scorching balls of fire that explode into destructive flames."
+ projectile_type = "/obj/item/projectile/magic/fireball"
+ icon_state = "wand2"
+ max_charges = 8 //8, 4, 4, 3
+
+/obj/item/weapon/gun/magic/wand/fireball/zap_self(mob/living/user as mob)
+ explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2)
+ charges--
+ ..()
\ No newline at end of file
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
new file mode 100644
index 00000000000..bec53531f8b
--- /dev/null
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -0,0 +1,223 @@
+/obj/item/projectile/magic
+ name = "bolt of nothing"
+ icon_state = "energy"
+ damage = 0
+ damage_type = OXY
+ nodamage = 1
+ flag = "magic"
+
+/obj/item/projectile/magic/death
+ name = "bolt of death"
+ icon_state = "pulse1_bl"
+ damage = 9001
+ damage_type = OXY
+ nodamage = 0
+ flag = "magic"
+
+/obj/item/projectile/magic/fireball
+ name = "bolt of fireball"
+ icon_state = "fireball"
+ damage = 25 //The spell fireball additionally does 20 burn, so the wand fireball is marginally less painful
+ damage_type = BRUTE
+ nodamage = 0
+ flag = "magic"
+
+/obj/item/projectile/magic/fireball/on_hit(var/target)
+ var/turf/T = get_turf(target)
+ explosion(T, -1, 0, 2, 3, 0, flame_range = 2)
+
+/obj/item/projectile/magic/resurrection
+ name = "bolt of resurrection"
+ icon_state = "ion"
+ damage = 0
+ damage_type = OXY
+ nodamage = 1
+ flag = "magic"
+
+/obj/item/projectile/magic/resurrection/on_hit(var/mob/living/carbon/target)
+
+ if(istype(target,/mob))
+ var/old_stat = target.stat
+ target.revive()
+ target.suiciding = 0
+ if(!target.ckey)
+ for(var/mob/dead/observer/ghost in player_list)
+ if(target.real_name == ghost.real_name)
+ ghost.reenter_corpse()
+ break
+ if(old_stat != DEAD)
+ target << "You feel great!"
+ else
+ target << "You rise with a start, you're alive!!!"
+
+/obj/item/projectile/magic/teleport
+ name = "bolt of teleportation"
+ icon_state = "bluespace"
+ damage = 0
+ damage_type = OXY
+ nodamage = 1
+ flag = "magic"
+ var/inner_tele_radius = 0
+ var/outer_tele_radius = 6
+
+/obj/item/projectile/magic/teleport/on_hit(var/mob/target)
+ var/teleammount = 0
+ var/teleloc = target
+ if(!isturf(target))
+ teleloc = target.loc
+ for(var/atom/movable/stuff in teleloc)
+ if(!stuff.anchored && stuff.loc)
+ teleammount++
+ do_teleport(stuff, stuff, 10)
+ var/datum/effect/effect/system/harmless_smoke_spread/smoke = new /datum/effect/effect/system/harmless_smoke_spread()
+ smoke.set_up(max(round(10 - teleammount),1), 0, stuff.loc) //Smoke drops off if a lot of stuff is moved for the sake of sanity
+ smoke.start()
+
+/obj/item/projectile/magic/door
+ name = "bolt of door creation"
+ icon_state = "energy"
+ damage = 0
+ damage_type = OXY
+ nodamage = 1
+ flag = "magic"
+
+/obj/item/projectile/magic/door/on_hit(var/atom/target)
+ var/atom/T = target.loc
+ if(isturf(target))
+ if(target.density)
+ new /obj/structure/mineral_door/wood(target)
+ target:ChangeTurf(/turf/simulated/floor/plating)
+ else if (isturf(T))
+ if(T.density)
+ new /obj/structure/mineral_door/wood(T)
+ T:ChangeTurf(/turf/simulated/floor/plating)
+
+/obj/item/projectile/magic/change
+ name = "bolt of change"
+ icon_state = "ice_1"
+ damage = 0
+ damage_type = BURN
+ nodamage = 1
+ flag = "magic"
+
+/obj/item/projectile/magic/change/on_hit(var/atom/change)
+ wabbajack(change)
+
+proc/wabbajack(mob/living/M)
+ if(istype(M))
+ if(istype(M, /mob/living) && M.stat != DEAD)
+ if(M.monkeyizing) return
+ M.monkeyizing = 1
+ M.canmove = 0
+ M.icon = null
+ M.overlays.Cut()
+ M.invisibility = 101
+
+ if(istype(M, /mob/living/silicon/robot))
+ var/mob/living/silicon/robot/Robot = M
+ if(Robot.mmi) del(Robot.mmi)
+ else
+ for(var/obj/item/W in M)
+ if(istype(W, /obj/item/weapon/implant)) //TODO: Carn. give implants a dropped() or something
+ del(W)
+ continue
+ W.layer = initial(W.layer)
+ W.loc = M.loc
+ W.dropped(M)
+
+ var/mob/living/new_mob
+
+ var/randomize = pick("monkey","robot","slime","xeno","human","animal")
+ switch(randomize)
+ if("monkey")
+ new_mob = new /mob/living/carbon/monkey(M.loc)
+ new_mob.universal_speak = 1
+ if("robot")
+ new_mob = new /mob/living/silicon/robot(M.loc)
+ new_mob.gender = M.gender
+ new_mob.invisibility = 0
+ new_mob.job = "Cyborg"
+ var/mob/living/silicon/robot/Robot = new_mob
+ Robot.mmi = new /obj/item/device/mmi(new_mob)
+ Robot.mmi.transfer_identity(M) //Does not transfer key/client.
+ if("slime")
+ if(prob(50)) new_mob = new /mob/living/carbon/slime/adult(M.loc)
+ else new_mob = new /mob/living/carbon/slime(M.loc)
+ new_mob.universal_speak = 1
+ if("xeno")
+ if(prob(50))
+ new_mob = new /mob/living/carbon/alien/humanoid/hunter(M.loc)
+ else
+ new_mob = new /mob/living/carbon/alien/humanoid/sentinel(M.loc)
+ new_mob.universal_speak = 1
+
+ /*var/alien_caste = pick("Hunter","Sentinel","Drone","Larva")
+ switch(alien_caste)
+ if("Hunter") new_mob = new /mob/living/carbon/alien/humanoid/hunter(M.loc)
+ if("Sentinel") new_mob = new /mob/living/carbon/alien/humanoid/sentinel(M.loc)
+ if("Drone") new_mob = new /mob/living/carbon/alien/humanoid/drone(M.loc)
+ else new_mob = new /mob/living/carbon/alien/larva(M.loc)
+ new_mob.universal_speak = 1*/
+ if("animal")
+ var/animal = pick("parrot","corgi","crab","pug","cat","carp","bear","mushroom","tomato","mouse","chicken","cow","lizard","chick")
+ switch(animal)
+ if("parrot") new_mob = new /mob/living/simple_animal/parrot(M.loc)
+ if("corgi") new_mob = new /mob/living/simple_animal/corgi(M.loc)
+ if("crab") new_mob = new /mob/living/simple_animal/crab(M.loc)
+ if("pug") new_mob = new /mob/living/simple_animal/pug(M.loc)
+ if("cat") new_mob = new /mob/living/simple_animal/cat(M.loc)
+ if("carp") new_mob = new /mob/living/simple_animal/hostile/carp(M.loc)
+ if("bear") new_mob = new /mob/living/simple_animal/hostile/bear(M.loc)
+ if("mushroom") new_mob = new /mob/living/simple_animal/mushroom(M.loc)
+ if("tomato") new_mob = new /mob/living/simple_animal/tomato(M.loc)
+ if("mouse") new_mob = new /mob/living/simple_animal/mouse(M.loc)
+ if("chicken") new_mob = new /mob/living/simple_animal/chicken(M.loc)
+ if("cow") new_mob = new /mob/living/simple_animal/cow(M.loc)
+ if("lizard") new_mob = new /mob/living/simple_animal/lizard(M.loc)
+ else new_mob = new /mob/living/simple_animal/chick(M.loc)
+ new_mob.universal_speak = 1
+ if("human")
+ new_mob = new /mob/living/carbon/human(M.loc)
+
+ var/datum/preferences/A = new() //Randomize appearance for the human
+ A.copy_to(new_mob)
+
+ var/mob/living/carbon/human/H = new_mob
+ ready_dna(H)
+ if(H.dna)
+ H.dna.mutantrace = pick("lizard","golem","slime","plant","fly","shadow","adamantine","skeleton",8;"")
+ H.update_body()
+ else
+ return
+
+ for (var/obj/effect/proc_holder/spell/S in M.spell_list)
+ new_mob.spell_list += new S.type
+
+ new_mob.a_intent = "harm"
+ if(M.mind)
+ M.mind.transfer_to(new_mob)
+ else
+ new_mob.key = M.key
+
+ new_mob << "Your form morphs into that of a [randomize]."
+
+ del(M)
+ return new_mob
+
+/obj/item/projectile/magic/animate
+ name = "bolt of animation"
+ icon_state = "red_1"
+ damage = 0
+ damage_type = BURN
+ nodamage = 1
+ flag = "magic"
+
+/obj/item/projectile/magic/animate/Bump(var/atom/change)
+ . = ..()
+ if(istype(change, /obj/item) || istype(change, /obj/structure) && !is_type_in_list(change, protected_objects))
+ var/obj/O = change
+ new /mob/living/simple_animal/hostile/mimic/copy(O.loc, O, firer)
+ else if(istype(change, /mob/living/simple_animal/hostile/mimic/copy))
+ // Change our allegiance!
+ var/mob/living/simple_animal/hostile/mimic/copy/C = change
+ C.ChangeOwner(firer)
\ No newline at end of file
diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi
index f48b9281daa..32bfa869468 100644
Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ
diff --git a/icons/mob/items_lefthand.dmi b/icons/mob/items_lefthand.dmi
index 0e3993dfd06..8489d84c792 100644
Binary files a/icons/mob/items_lefthand.dmi and b/icons/mob/items_lefthand.dmi differ
diff --git a/icons/mob/items_righthand.dmi b/icons/mob/items_righthand.dmi
index 97a9cff6b32..cbb4183793f 100644
Binary files a/icons/mob/items_righthand.dmi and b/icons/mob/items_righthand.dmi differ
diff --git a/icons/obj/gun.dmi b/icons/obj/gun.dmi
index 53237188526..98283e0ee84 100644
Binary files a/icons/obj/gun.dmi and b/icons/obj/gun.dmi differ
diff --git a/icons/obj/library.dmi b/icons/obj/library.dmi
index c59263e514b..e5f084762af 100644
Binary files a/icons/obj/library.dmi and b/icons/obj/library.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index e37f31b61a0..cbbb0dab917 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -174,6 +174,7 @@
#include "code\datums\helper_datums\teleport.dm"
#include "code\datums\helper_datums\topic_input.dm"
#include "code\datums\spells\area_teleport.dm"
+#include "code\datums\spells\charge.dm"
#include "code\datums\spells\conjure.dm"
#include "code\datums\spells\construct_spells.dm"
#include "code\datums\spells\dumbfire.dm"
@@ -1080,6 +1081,7 @@
#include "code\modules\projectiles\ammunition\bullets.dm"
#include "code\modules\projectiles\ammunition\magazines.dm"
#include "code\modules\projectiles\guns\energy.dm"
+#include "code\modules\projectiles\guns\magic.dm"
#include "code\modules\projectiles\guns\projectile.dm"
#include "code\modules\projectiles\guns\energy\laser.dm"
#include "code\modules\projectiles\guns\energy\nuclear.dm"
@@ -1087,15 +1089,16 @@
#include "code\modules\projectiles\guns\energy\special.dm"
#include "code\modules\projectiles\guns\energy\stun.dm"
#include "code\modules\projectiles\guns\energy\temperature.dm"
+#include "code\modules\projectiles\guns\magic\staff.dm"
+#include "code\modules\projectiles\guns\magic\wand.dm"
#include "code\modules\projectiles\guns\projectile\automatic.dm"
#include "code\modules\projectiles\guns\projectile\pistol.dm"
#include "code\modules\projectiles\guns\projectile\revolver.dm"
#include "code\modules\projectiles\guns\projectile\shotgun.dm"
-#include "code\modules\projectiles\projectile\animate.dm"
#include "code\modules\projectiles\projectile\beams.dm"
#include "code\modules\projectiles\projectile\bullets.dm"
-#include "code\modules\projectiles\projectile\change.dm"
#include "code\modules\projectiles\projectile\energy.dm"
+#include "code\modules\projectiles\projectile\magic.dm"
#include "code\modules\projectiles\projectile\special.dm"
#include "code\modules\reagents\Chemistry-Colours.dm"
#include "code\modules\reagents\Chemistry-Holder.dm"