diff --git a/code/datums/spells/charge.dm b/code/datums/spells/charge.dm
index 6594ead232f..b4603f5e6b1 100644
--- a/code/datums/spells/charge.dm
+++ b/code/datums/spells/charge.dm
@@ -42,7 +42,7 @@
break
else if(istype(item, /obj/item/weapon/gun/magic))
var/obj/item/weapon/gun/magic/I = item
- if(prob(80))
+ if(prob(80) && !I.can_charge)
I.max_charges--
if(I.max_charges <= 0)
I.max_charges = 0
diff --git a/code/datums/spells/mind_transfer.dm b/code/datums/spells/mind_transfer.dm
index c905bf88a97..389091da2c8 100644
--- a/code/datums/spells/mind_transfer.dm
+++ b/code/datums/spells/mind_transfer.dm
@@ -10,10 +10,6 @@
range = 1
cooldown_min = 200 //100 deciseconds reduction per rank
var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell
- var/base_spell_loss_chance = 20 //base probability of the wizard losing a spell in the process
- var/spell_loss_chance_modifier = 7 //amount of probability of losing a spell added per spell (mind_transfer included)
- var/spell_loss_amount = 1 //the maximum amount of spells possible to lose during a single transfer
- var/msg_wait = 500 //how long in deciseconds it waits before telling that body doesn't feel right or mind swap robbed of a spell
var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell
var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell
icon_power_button = "spell_mind"
@@ -53,27 +49,6 @@ Also, you never added distance checking after target is selected. I've went ahea
var/mob/living/victim = target//The target of the spell whos body will be transferred to.
var/mob/caster = user//The wizard/whomever doing the body transferring.
- //SPELL LOSS BEGIN
- //NOTE: The caster must ALWAYS keep mind transfer, even when other spells are lost.
- var/obj/effect/proc_holder/spell/wizard/targeted/mind_transfer/m_transfer = locate() in user.spell_list//Find mind transfer directly.
- var/list/checked_spells = user.spell_list
- checked_spells -= m_transfer //Remove Mind Transfer from the list.
-
- if(caster.spell_list.len)//If they have any spells left over after mind transfer is taken out. If they don't, we don't need this.
- for(var/i=spell_loss_amount,(i>0&&checked_spells.len),i--)//While spell loss amount is greater than zero and checked_spells has spells in it, run this proc.
- for(var/j=checked_spells.len,(j>0&&checked_spells.len),j--)//While the spell list to check is greater than zero and has spells in it, run this proc.
- if(prob(base_spell_loss_chance))
- checked_spells -= pick(checked_spells)//Pick a random spell to remove.
- spawn(msg_wait)
- victim << "The mind transfer has robbed you of a spell."
- break//Spell lost. Break loop, going back to the previous for() statement.
- else//Or keep checking, adding spell chance modifier to increase chance of losing a spell.
- base_spell_loss_chance += spell_loss_chance_modifier
-
- checked_spells += m_transfer//Add back Mind Transfer.
- user.spell_list = checked_spells//Set user spell list to whatever the new list is.
- //SPELL LOSS END
-
//MIND TRANSFER BEGIN
if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list.
for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot.
@@ -105,7 +80,3 @@ Also, you never added distance checking after target is selected. I've went ahea
//Here we paralyze both mobs and knock them out for a time.
caster.Paralyse(paralysis_amount_caster)
victim.Paralyse(paralysis_amount_victim)
-
- //After a certain amount of time the victim gets a message about being in a different body.
- spawn(msg_wait)
- caster << "\red You feel woozy and lightheaded. Your body doesn't seem like your own."
diff --git a/code/game/gamemodes/wizard/rightandwrong.dm b/code/game/gamemodes/wizard/rightandwrong.dm
index e1b0c7a65f3..2aca47c87d7 100644
--- a/code/game/gamemodes/wizard/rightandwrong.dm
+++ b/code/game/gamemodes/wizard/rightandwrong.dm
@@ -1,6 +1,9 @@
/mob/proc/rightandwrong(var/summon_type) //0 = Summon Guns, 1 = Summon Magic
+ var/list/gunslist = list("taser","egun","laser","revolver","detective","c20r","nuclear","deagle","gyrojet","pulse","silenced","cannon","doublebarrel","shotgun","combatshotgun","bulldog","mateba","sabr","crossbow","saw","car")
+ var/list/magiclist = list("fireball","smoke","blind","mindswap","forcewall","knock","horsemask","charge","wandnothing", "wanddeath", "wandresurrection", "wandpolymorph", "wandteleport", "wanddoor", "wandfireball", "staffhealing", "armor", "scrying", "staffdoor", "special")
+ var/list/magicspeciallist = list("staffchange","staffanimation", "wandbelt", "contract", "staffchaos")
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)
@@ -19,8 +22,9 @@
for(var/datum/objective/OBJ in H.mind.objectives)
H << "Objective #[obj_count]: [OBJ.explanation_text]"
obj_count++
- var/randomizeguns = pick("taser","egun","laser","revolver","detective","c20r","nuclear","deagle","gyrojet","pulse","silenced","cannon","doublebarrel","shotgun","combatshotgun","bulldog","mateba","sabr","crossbow","saw","car")
- var/randomizemagic = pick("fireball","smoke","blind","mindswap","forcewall","knock","horsemask","charge","wandnothing", "wanddeath", "wandresurrection", "wandpolymorph", "wandteleport", "wanddoor", "wandfireball", "staffchange", "staffhealing", "armor", "scrying")
+ var/randomizeguns = pick(gunslist)
+ var/randomizemagic = pick(magiclist)
+ var/randomizemagicspecial = pick(magicspeciallist)
if(!summon_type)
switch (randomizeguns)
if("taser")
@@ -96,10 +100,10 @@
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("staffhealing")
new /obj/item/weapon/gun/magic/staff/healing(get_turf(H))
+ if("staffdoor")
+ new /obj/item/weapon/gun/magic/staff/door(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))
@@ -110,4 +114,19 @@
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."
+ H << "The walls suddenly disappear."
+
+ if("special")
+ magiclist -= "special" //only one super OP item per summoning max
+ switch (randomizemagicspecial)
+ 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("wandbelt")
+ new /obj/item/weapon/storage/belt/wands/full(get_turf(H))
+ if("contract")
+ new /obj/item/weapon/contract(get_turf(H))
+ if("staffchaos")
+ new /obj/item/weapon/gun/magic/staff/chaos(get_turf(H))
+ H << "You suddenly feel lucky."
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index efb7538cdb0..9c872420c93 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -11,6 +11,7 @@
var/temp = null
var/max_uses = 5
var/op = 1
+ var/activepage
/obj/item/weapon/spellbook/attackby(obj/item/O as obj, mob/user as mob)
if(istype(O, /obj/item/weapon/contract))
@@ -31,12 +32,22 @@
var/dat
if(temp)
dat = "[temp]
Clear"
- else
- dat = "The Book of Spells:
"
- dat += "Spells left to memorize: [uses]
"
+ else if(!activepage || activepage == "return")
+ dat = "The Book of Magic:
"
+ dat += "Uses remaining: [uses]
"
+ dat += "
"
+ dat += "Learn and Improve Magical Abilities
"
+ dat += "Summon Magical Tools and Weapons
"
+ dat += "Cast Powerful One Time Spells
"
+
+ dat += "
"
+ dat += "Re-memorize Spells
"
+ else if (activepage == "spells")
+ dat += "Spells:
"
+ dat += "Spells that can be reused endlessly. Unless stated otherwise all spells require full wizard garb as a focus.
"
+ dat += "The number after the spell name is the cooldown time. You can reduce this number by spending more points on the spell.
"
+
dat += "
"
- dat += "Memorize which spell:
"
- dat += "The number after the spell name is the cooldown time.
"
dat += "Remove Clothes Requirement"
dat += "Warning: this takes away 2 spell choices.
"
@@ -86,6 +97,17 @@
dat += "Flesh to Stone (60)
"
dat += "This spell will curse a person to immediately turn into an unmoving statue. The effect will eventually wear off if the statue is not destroyed.
"
+ dat += "
"
+
+ dat += "Return
"
+
+ else if (activepage == "oneuse")
+ dat += "One Time Spells:
"
+ dat += "These potent spells can only be used once and will be used automatically upon purchase.
"
+ dat += "The effects are global and irreversable, these spells cannot be unlearned, but they can be bought multiple times.
"
+
+ dat += "
"
+
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!
"
@@ -93,27 +115,44 @@
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 += "Return
"
+
+ else if (activepage == "artifacts")
+
dat += "Artefacts:
"
- dat += "Powerful items imbued with eldritch magics. Summoning one will count towards your maximum number of spells.
"
- dat += "It is recommended that only experienced wizards attempt to wield such artefacts.
"
+ 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.
"
dat += "
"
dat += "Staff of Change
"
- dat += "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself.
"
+ dat += "An artifact that spits bolts of coruscating energy which cause the target's very form to reshape itself.
"
dat += "
"
dat += "Six Soul Stone Shards and the spell Artificer
"
dat += "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.
"
dat += "
"
- dat += "Mastercrafted Armor Set
"
- dat += "An artefact suit of armor that allows you to cast spells while providing more protection against attacks and the void of space.
"
- dat += "
"
-
dat += "Staff of Animation
"
dat += "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines.
"
dat += "
"
+ dat += "Staff of Door Creation
"
+ dat += "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.
"
+ dat += "
"
+
+ dat += "Staff of Chaos
"
+ dat += "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.
"
+ dat += "
"
+
+ dat += "Wand Assortment
"
+ dat += "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.
"
+ dat += "
"
+
+ dat += "Mastercrafted Armor Set
"
+ dat += "An artifact suit of armor that allows you to cast spells while providing more protection against attacks and the void of space.
"
+ dat += "
"
+
dat += "Contract of Apprenticeship
"
dat += "A magical contract binding an apprentice wizard to your service, using it will summon them to your side.
"
dat += "
"
@@ -122,7 +161,7 @@
dat += "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.
"
dat += "
"
- dat += "Re-memorize Spells
"
+ dat += "Return
"
user << browse(dat, "window=radio")
onclose(user, "radio")
return
@@ -152,11 +191,13 @@
feedback_add_details("wizard_spell_learned","UM") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
else
temp = "You may only re-memorize spells whilst located inside the wizard sanctuary."
+ else if(href_list["spell_choice"] == "spells" || href_list["spell_choice"] == "artifacts" || href_list["spell_choice"] == "oneuse" || href_list["spell_choice"] == "return")
+ activepage = href_list["spell_choice"]
else if(uses >= 1 && max_uses >=1)
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", 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/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", staffchaos = "Staff of Chaos", staffdoor = "Staff of Door Creation", wands = "Wand Assortment")
var/already_knows = 0
for(var/obj/effect/proc_holder/spell/wizard/aspell in H.spell_list)
if(available_spells[href_list["spell_choice"]] == initial(aspell.name))
@@ -297,6 +338,21 @@
new /obj/item/weapon/gun/magic/staff/animate(get_turf(H))
temp = "You have purchased a staff of animation."
max_uses--
+ if("staffchaos")
+ feedback_add_details("wizard_spell_learned","SC") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
+ new /obj/item/weapon/gun/magic/staff/chaos(get_turf(H))
+ temp = "You have purchased a staff of chaos."
+ max_uses--
+ if("staffdoor")
+ feedback_add_details("wizard_spell_learned","SD") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
+ new /obj/item/weapon/gun/magic/staff/door(get_turf(H))
+ temp = "You have purchased a staff of door creation."
+ max_uses--
+ if("wands")
+ feedback_add_details("wizard_spell_learned","WA") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
+ new /obj/item/weapon/storage/belt/wands/full(get_turf(H))
+ temp = "You have purchased an assortment of wands."
+ max_uses--
if("contract")
feedback_add_details("wizard_spell_learned","CT") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
new /obj/item/weapon/contract(get_turf(H))
@@ -317,6 +373,7 @@
else
if(href_list["temp"])
temp = null
+ activepage = null
attack_self(H)
return
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index cb93c71d9d7..4c88b7b2b46 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -197,3 +197,26 @@
new /obj/item/ammo_casing/shotgun/beanbag(src)
new /obj/item/ammo_casing/shotgun/beanbag(src)
new /obj/item/ammo_casing/shotgun/beanbag(src)
+
+/obj/item/weapon/storage/belt/wands
+ name = "wand belt"
+ desc = "A belt designed to hold various rods of power. A veritable fanny pack of exotic magic."
+ icon_state = "soulstonebelt"
+ item_state = "soulstonebelt"
+ storage_slots = 6
+ can_hold = list(
+ "/obj/item/weapon/gun/magic/wand"
+ )
+
+/obj/item/weapon/storage/belt/wands/full/New()
+ ..()
+ new /obj/item/weapon/gun/magic/wand/death(src)
+ new /obj/item/weapon/gun/magic/wand/resurrection(src)
+ new /obj/item/weapon/gun/magic/wand/polymorph(src)
+ new /obj/item/weapon/gun/magic/wand/teleport(src)
+ new /obj/item/weapon/gun/magic/wand/door(src)
+ new /obj/item/weapon/gun/magic/wand/fireball(src)
+
+ for(var/obj/item/weapon/gun/magic/wand/W in contents) //All wands in this pack come in the best possible condition
+ W.max_charges = initial(W.max_charges)
+ W.charges = W.max_charges
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index ad0671867fc..0d3aab2751c 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -34,3 +34,28 @@ obj/item/weapon/gun/magic/staff/healing
icon_state = "staffofhealing"
item_state = "staffofhealing"
max_charges = 8 //8, 4, 4, 3
+
+obj/item/weapon/gun/magic/staff/chaos
+ name = "staff of chaos"
+ desc = "An artefact that spits bolts of chaotic magic that can potentially do anything."
+ projectile_type = "/obj/item/projectile/magic"
+ icon_state = "staffofhealing"
+ item_state = "staffofhealing"
+ max_charges = 10
+ recharge_rate = 2
+
+ /obj/item/weapon/gun/magic/staff/chaos/process_chambered() //Snowflake proc, because this uses projectile_type instead of ammo_casing for whatever reason.
+ projectile_type = pick(typesof(/obj/item/projectile/magic))
+ if(in_chamber) return 1
+ if(!charges) return 0
+ in_chamber = new projectile_type(src)
+ return 1
+
+obj/item/weapon/gun/magic/staff/door
+ name = "staff of door creation"
+ desc = "An artefact that spits bolts of transformative magic that can create doors in walls."
+ projectile_type = "/obj/item/projectile/magic/door"
+ icon_state = "staffofhealing"
+ item_state = "staffofhealing"
+ max_charges = 10
+ recharge_rate = 2
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
index d0841fff5e4..277036cc4cf 100644
--- a/code/modules/projectiles/guns/magic/wand.dm
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -8,6 +8,7 @@
can_charge = 0
max_charges = 100 //100, 50, 50, 34 (max charge distribution by 25%ths)
var/variable_charges = 1
+ var/drained = false
/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
@@ -35,9 +36,10 @@
..()
/obj/item/weapon/gun/magic/wand/afterattack(atom/target as mob, mob/living/user as mob)
- if(!charges)
+ if(!charges && !drained)
user << "The [name] whizzles quietly."
icon_state = "[icon_state]-drained"
+ drained = true
return
if(target == user)
zap_self(user)
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index da7ec9a02d3..74523a47289 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -151,7 +151,10 @@ proc/wabbajack(mob/living/M)
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)
+ if(prob(30))
+ new_mob = new /mob/living/silicon/robot/syndicate(M.loc)
+ else
+ new_mob = new /mob/living/silicon/robot(M.loc)
new_mob.gender = M.gender
new_mob.invisibility = 0
new_mob.job = "Cyborg"
@@ -179,12 +182,14 @@ proc/wabbajack(mob/living/M)
new_mob.universal_speak = 1*/
if("animal")
if(prob(50))
- var/beast = pick("carp","bear","mushroom","statue")
+ var/beast = pick("carp","bear","mushroom","statue", "bat", "goat")
switch(beast)
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/hostile/mushroom(M.loc)
if("statue") new_mob = new /mob/living/simple_animal/hostile/statue(M.loc)
+ if("bat") new_mob = new /mob/living/simple_animal/hostile/scarybat(M.loc)
+ if("goat") new_mob = new /mob/living/simple_animal/hostile/retaliate/goat(M.loc)
else
var/animal = pick("parrot","corgi","crab","pug","cat","tomato","mouse","chicken","cow","lizard","chick","fox")
switch(animal)
@@ -217,6 +222,9 @@ proc/wabbajack(mob/living/M)
for (var/obj/effect/proc_holder/spell/wizard/S in M.spell_list)
new_mob.spell_list += new S.type
+ new_mob.attack_log = M.attack_log
+ M.attack_log += text("\[[time_stamp()]\] [M.real_name] ([M.ckey]) became [new_mob.real_name].")
+
new_mob.a_intent = "harm"
if(M.mind)
M.mind.transfer_to(new_mob)
@@ -241,15 +249,16 @@ proc/wabbajack(mob/living/M)
if(istype(change, /obj/item) || istype(change, /obj/structure) && !is_type_in_list(change, protected_objects))
if(istype(change, /obj/structure/closet/statue))
for(var/mob/living/carbon/human/H in change.contents)
- var/mob/living/simple_animal/hostile/statue/S = new /mob/living/simple_animal/hostile/statue(change.loc)
+ var/mob/living/simple_animal/hostile/statue/S = new /mob/living/simple_animal/hostile/statue(change.loc, firer)
S.name = "statue of [H.name]"
S.faction = list("\ref[firer]")
S.icon = change.icon
if(H.mind)
H.mind.transfer_to(S)
- S << "You are an animate statue. You cannot move when monitored, but are nearly invincible and deadly when unobserved! Do not harm [firer.name], your creator."
- del(H)
- del(change)
+ S << "You are an animated statue. You cannot move when monitored, but are nearly invincible and deadly when unobserved! Do not harm [firer.name], your creator."
+ H = change
+ H.loc = S
+ del(src)
else
var/obj/O = change
new /mob/living/simple_animal/hostile/mimic/copy(O.loc, O, firer)