diff --git a/code/datums/spell.dm b/code/datums/spell.dm
index fe2e7431cf3..f5356474019 100644
--- a/code/datums/spell.dm
+++ b/code/datums/spell.dm
@@ -70,6 +70,8 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
var/ghost = 0 // Skip life check.
var/clothes_req = 1 //see if it requires clothes
+ var/human_req = 0 //spell can only be cast by humans
+ var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities
var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells
var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell
var/invocation_emote_self = null
@@ -102,11 +104,11 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
var/sound = null //The sound the spell makes when it is cast
/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell
-
if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list))
to_chat(user, "You shouldn't have this spell! Something's wrong.")
return 0
- if(istype(user, /mob/living/carbon/human))
+
+ if(ishuman(user))
var/mob/living/carbon/human/caster = user
if(caster.remoteview_target)
caster.remoteview_target = null
@@ -129,26 +131,31 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
if(!ghost)
if(user.stat && !stat_allowed)
- to_chat(user, "Not when you're incapacitated.")
+ to_chat(user, "You can't cast this spell while incapacitated.")
return 0
-
if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled())
to_chat(user, "Mmmf mrrfff!")
return 0
- var/obj/effect/proc_holder/spell/noclothes/spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list()))
- if(clothes_req && !(spell && istype(spell)))//clothes check
- if(!istype(user, /mob/living/carbon/human))
- to_chat(user, "You aren't a human, Why are you trying to cast a human spell, silly non-human? Casting human spells is for humans.")
+
+ var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list()))
+ if((ishuman(user) && clothes_req) && !(clothes_spell && istype(clothes_spell)))//clothes check
+ var/mob/living/carbon/human/H = user
+ if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(H.wear_suit, /obj/item/clothing/suit/space/rig/wizard))
+ to_chat(user, "I don't feel strong enough without my robe.")
return 0
- if(!istype(user:wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(user:wear_suit, /obj/item/clothing/suit/space/rig/wizard))
- to_chat(user, "I don't feel strong enough without my robe.")
+ if(!istype(H.shoes, /obj/item/clothing/shoes/sandal))
+ to_chat(user, "I don't feel strong enough without my sandals.")
return 0
- if(!istype(user:shoes, /obj/item/clothing/shoes/sandal))
- to_chat(user, "I don't feel strong enough without my sandals.")
- return 0
- if(!istype(user:head, /obj/item/clothing/head/wizard) && !istype(user:head, /obj/item/clothing/head/helmet/space/rig/wizard))
+ if(!istype(H.head, /obj/item/clothing/head/wizard) && !istype(H.head, /obj/item/clothing/head/helmet/space/rig/wizard))
to_chat(user, "I don't feel strong enough without my hat.")
return 0
+ else if(!ishuman(user))
+ if(clothes_req || human_req)
+ to_chat(user, "This spell can only be cast by humans!")
+ return 0
+ if(nonabstract_req && (isbrain(user) || ispAI(user)))
+ to_chat(user, "This spell can only be cast by physical beings!")
+ return 0
if(!skipcharge)
switch(charge_type)
@@ -426,7 +433,6 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
return 0
if(ishuman(user))
-
var/mob/living/carbon/human/H = user
if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled())
@@ -442,8 +448,8 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
if(!istype(H.head, /obj/item/clothing/head/wizard) && !istype(H.head, /obj/item/clothing/head/helmet/space/rig/wizard))
return 0
else
- if(clothes_req)
+ if(clothes_req || human_req)
return 0
- if(isbrain(user) || ispAI(user))
+ if(nonabstract_req && (isbrain(user) || ispAI(user)))
return 0
return 1
diff --git a/code/datums/spells/area_teleport.dm b/code/datums/spells/area_teleport.dm
index badf94d60d6..c213828cf29 100644
--- a/code/datums/spells/area_teleport.dm
+++ b/code/datums/spells/area_teleport.dm
@@ -1,7 +1,8 @@
/obj/effect/proc_holder/spell/targeted/area_teleport
name = "Area teleport"
desc = "This spell teleports you to a type of area of your selection."
-
+ nonabstract_req = 1
+
var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list
var/invocation_area = 1 //if the invocation appends the selected area
diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm
index 9e8a6941f0b..b9ad3d22e70 100644
--- a/code/datums/spells/ethereal_jaunt.dm
+++ b/code/datums/spells/ethereal_jaunt.dm
@@ -10,6 +10,7 @@
range = -1
cooldown_min = 100 //50 deciseconds reduction per rank
include_user = 1
+ nonabstract_req = 1
centcom_cancast = 0 //Prevent people from getting to centcom
var phaseshift = 0
diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm
index b0b85f4b47c..296521405a0 100644
--- a/code/datums/spells/mime.dm
+++ b/code/datums/spells/mime.dm
@@ -11,6 +11,7 @@
clothes_req = 0
range = 0
cast_sound = null
+ human_req = 1
action_icon_state = "mime"
action_background_icon_state = "bg_mime"
@@ -35,6 +36,7 @@
charge_max = 3000
range = -1
include_user = 1
+ human_req = 1
action_icon_state = "mime"
action_background_icon_state = "bg_mime"
diff --git a/code/datums/spells/shapeshift.dm b/code/datums/spells/shapeshift.dm
new file mode 100644
index 00000000000..a5f1de1baec
--- /dev/null
+++ b/code/datums/spells/shapeshift.dm
@@ -0,0 +1,82 @@
+/obj/effect/proc_holder/spell/targeted/shapeshift
+ name = "Shapechange"
+ desc = "Take on the shape of another for a time to use their natural abilities. Once you've made your choice it cannot be changed."
+ clothes_req = 0
+ human_req = 0
+ charge_max = 200
+ cooldown_min = 50
+ range = -1
+ include_user = 1
+ invocation = "RAC'WA NO!"
+ invocation_type = "shout"
+ action_icon_state = "shapeshift"
+
+ var/shapeshift_type
+ var/list/current_shapes = list()
+ var/list/current_casters = list()
+ var/list/possible_shapes = list(/mob/living/simple_animal/mouse,\
+ /mob/living/simple_animal/pet/corgi,\
+ /mob/living/simple_animal/bot/ed209,\
+ /mob/living/simple_animal/hostile/construct/armoured)
+
+/obj/effect/proc_holder/spell/targeted/shapeshift/cast(list/targets, mob/user = usr)
+ for(var/mob/living/M in targets)
+ if(!shapeshift_type)
+ var/list/animal_list = list()
+ for(var/path in possible_shapes)
+ var/mob/living/simple_animal/A = path
+ animal_list[initial(A.name)] = path
+ shapeshift_type = input(M, "Choose Your Animal Form!", "It's Morphing Time!", null) as anything in animal_list
+ if(!shapeshift_type) //If you aren't gonna decide I am!
+ shapeshift_type = pick(animal_list)
+ shapeshift_type = animal_list[shapeshift_type]
+ if(M in current_shapes)
+ Restore(M)
+ else
+ Shapeshift(M)
+
+/obj/effect/proc_holder/spell/targeted/shapeshift/proc/Shapeshift(mob/living/caster)
+ for(var/mob/living/M in caster)
+ if(M.status_flags & GODMODE)
+ to_chat(caster, "You're already shapeshifted!")
+ return
+
+ var/mob/living/shape = new shapeshift_type(caster.loc)
+ caster.loc = shape
+ caster.status_flags |= GODMODE
+
+ current_shapes |= shape
+ current_casters |= caster
+ clothes_req = 0
+ human_req = 0
+
+ caster.mind.transfer_to(shape)
+
+/obj/effect/proc_holder/spell/targeted/shapeshift/proc/Restore(mob/living/shape)
+ var/mob/living/caster
+ for(var/mob/living/M in shape)
+ if(M in current_casters)
+ caster = M
+ break
+ if(!caster)
+ return
+ caster.loc = shape.loc
+ caster.status_flags &= ~GODMODE
+
+ clothes_req = initial(clothes_req)
+ human_req = initial(human_req)
+ current_casters.Remove(caster)
+ current_shapes.Remove(shape)
+
+ shape.mind.transfer_to(caster)
+ qdel(shape) //Gib it maybe ?
+
+/obj/effect/proc_holder/spell/targeted/shapeshift/dragon
+ name = "Dragon Form"
+ desc = "Take on the shape a lesser ash drake."
+ invocation = "RAAAAAAAAWR!"
+
+ shapeshift_type = /mob/living/simple_animal/hostile/megafauna/dragon/lesser
+ list/current_shapes = list(/mob/living/simple_animal/hostile/megafauna/dragon/lesser)
+ list/current_casters = list()
+ list/possible_shapes = list(/mob/living/simple_animal/hostile/megafauna/dragon/lesser)
\ No newline at end of file
diff --git a/code/datums/spells/turf_teleport.dm b/code/datums/spells/turf_teleport.dm
index ce3f5720cf0..bcde1d0c2e9 100644
--- a/code/datums/spells/turf_teleport.dm
+++ b/code/datums/spells/turf_teleport.dm
@@ -1,6 +1,7 @@
/obj/effect/proc_holder/spell/targeted/turf_teleport
name = "Turf Teleport"
desc = "This spell teleports the target to the turf in range."
+ nonabstract_req = 1
var/inner_tele_radius = 1
var/outer_tele_radius = 2
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index a2b11c8a214..84636fdf4fd 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -192,9 +192,10 @@
to_chat(user, "Your flesh begins to melt! Miraculously, you seem fine otherwise.")
H.set_species("Skeleton")
if(2)
- to_chat(user, "You don't feel so good...")
- message_admins("[key_name_admin(user)] has started transforming into a dragon via dragon's blood.")
- H.ForceContractDisease(new /datum/disease/transformation/dragon(0))
+ to_chat(user, "Power courses through you! You can now shift your form at will.")
+ if(user.mind)
+ var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/D = new
+ user.mind.AddSpell(D)
if(3)
to_chat(user, "You feel like you could walk straight through lava now.")
H.weather_immunities |= "lava"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
index 5832842b4ca..5ced32e8bfe 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
@@ -264,8 +264,8 @@ Difficulty: Medium
/mob/living/simple_animal/hostile/megafauna/dragon/lesser
name = "lesser ash drake"
- maxHealth = 300
- health = 300
+ maxHealth = 200
+ health = 200
faction = list("neutral")
melee_damage_upper = 30
melee_damage_lower = 30
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
index c29a6e8a4f8..f939dbdc91f 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
@@ -73,7 +73,7 @@
if(isliving(target))
var/mob/living/L = target
if(L.stat != DEAD)
- if(ranged && ranged_cooldown <= world.time)
+ if(!client && ranged && ranged_cooldown <= world.time)
OpenFire()
else
devour(L)
diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi
index 325d886af8a..9bf1a315131 100644
Binary files a/icons/mob/actions.dmi and b/icons/mob/actions.dmi differ
diff --git a/paradise.dme b/paradise.dme
index e7698d5eeed..d9b5b155bb0 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -304,6 +304,7 @@
#include "code\datums\spells\mind_transfer.dm"
#include "code\datums\spells\projectile.dm"
#include "code\datums\spells\rathens.dm"
+#include "code\datums\spells\shapeshift.dm"
#include "code\datums\spells\summonitem.dm"
#include "code\datums\spells\touch_attacks.dm"
#include "code\datums\spells\trigger.dm"