Reworked Spellcasting System v0.9

- Very flexible - you can edit some of the spell's vars on the fly, or hardcode variations of the core spells.
 - Everyone can access it - you could even have observers with spells.
 - Slightly better UI - no longer will the spell verbs blink in and out of your verb panel.
1.0 will convert the existing spell sources (ie wizard spellbook) to this system and convert the last two spells to it.

git-svn-id: http://tgstation13.googlecode.com/svn/trunk@860 316c924e-a436-60f5-8080-3fe189b3f50e
This commit is contained in:
uporotiy
2011-01-16 00:25:45 +00:00
parent b0e2e22aa2
commit 16ca227fdc
19 changed files with 785 additions and 2 deletions
+56
View File
@@ -0,0 +1,56 @@
var/list/spells = list(/obj/spell/blind,/obj/spell/blink,/obj/spell/disintegrate,/obj/spell/ethereal_jaunt,/obj/spell/fireball,/obj/spell/forcewall,/obj/spell/knock,/obj/spell/magic_missile,/obj/spell/mutate,/obj/spell/teleport) //needed for the badmin verb for now
/obj/spell
name = "Spell"
desc = "A wizard spell"
var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit?
var/recharge = 100 //recharge time in deciseconds
var/clothes_req = 1 //see if it requires clothes
var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell
var/invocation_type = "none" //can be none, whisper and shout
var/range = 7 //the range of the spell
var/cast = 0 //the only way I could think of making it temporarily disable
var/message = "derp herp" //whatever it says to the guy affected by it. not always needed
/obj/spell/proc/cast_check() //checks if the spell can be cast based on its settings, plus handles chanting and recharge
if(!(src in usr.spell_list))
usr << "\red You shouldn't have this spell! Something's wrong."
return 0
if(cast)
usr << "[name] is still recharging."
return 0
if(usr.stat)
usr << "Not when you're incapacitated."
return 0
if(clothes_req) //clothes check
if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe))
usr << "I don't feel strong enough without my robe."
return 0
if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal))
usr << "I don't feel strong enough without my sandals."
return 0
if(!istype(usr:head, /obj/item/clothing/head/wizard))
usr << "I don't feel strong enough without my hat."
return 0
return 1
/obj/spell/proc/invocation() //spelling the spell out and setting it on recharge
src.cast = 1
var/old_name = src.name
src.name += " (cast)"
spawn(recharge)
src.cast = 0
src.name = old_name
switch(invocation_type)
if("shout")
usr.say(invocation)
if(usr.gender=="male")
playsound(usr.loc, pick('vs_chant_conj_hm.wav','vs_chant_conj_lm.wav','vs_chant_ench_hm.wav','vs_chant_ench_lm.wav','vs_chant_evoc_hm.wav','vs_chant_evoc_lm.wav','vs_chant_illu_hm.wav','vs_chant_illu_lm.wav','vs_chant_necr_hm.wav','vs_chant_necr_lm.wav'), 100, 1)
else
playsound(usr.loc, pick('vs_chant_conj_hf.wav','vs_chant_conj_lf.wav','vs_chant_ench_hf.wav','vs_chant_ench_lf.wav','vs_chant_evoc_hf.wav','vs_chant_evoc_lf.wav','vs_chant_illu_hf.wav','vs_chant_illu_lf.wav','vs_chant_necr_hf.wav','vs_chant_necr_lf.wav'), 100, 1)
if("whisper")
usr.whisper(invocation)
+43
View File
@@ -0,0 +1,43 @@
/obj/spell/blind
name = "Blind"
desc = "This spell temporarly blinds a single person and does not require wizard garb."
school = "transmutation"
recharge = 300
clothes_req = 0
invocation = "STI KALY"
invocation_type = "whisper"
message = "\blue Your eyes cry out in pain!"
var/blind_time = 300 //in deciseconds
var/eye_blind = 10 //I have no idea what these two do
var/eye_blurry = 20 //but eh, they're in the code
//I would add the ability to choose different disabilities (do ho ho), but I have no idea which bit corresponds to which disability
/obj/spell/blind/Click()
..()
if(!cast_check())
return
var/mob/M = input("Choose whom to blind", "ABRAKADABRA") as mob in oview(usr,range)
invocation()
var/obj/overlay/B = new /obj/overlay( M.loc )
B.icon_state = "blspell"
B.icon = 'wizard.dmi'
B.name = "spell"
B.anchored = 1
B.density = 0
B.layer = 4
M.canmove = 0
spawn(5)
del(B)
M.canmove = 1
M << text("[message]")
M.disabilities |= 1
spawn(blind_time)
M.disabilities &= ~1
M.eye_blind = eye_blind
M.eye_blurry = eye_blurry
return
+50
View File
@@ -0,0 +1,50 @@
/obj/spell/blink
name = "Blink"
desc = "This spell randomly teleports you a short distance."
school = "abjuration"
recharge = 20
clothes_req = 1
invocation = "none"
invocation_type = "none"
range = -1 //can affect only the user by default, but with var editing can be a teleport other spell
var/outer_teleport_radius = 6 //the radius of the area in which it picks turfs to teleport to
var/inner_teleport_radius = 0 //so with var fuckery you can have it teleport in a ring, not in a circle
var/smoke_spread = 1 //if set to 0, no smoke spreads when teleporting
/obj/spell/blink/Click()
..()
if(!cast_check())
return
var/mob/M
if(range>=0)
M = input("Choose whom to blink", "ABRAKADABRA") as mob in view(usr,range)
else
M = usr
invocation()
var/list/turfs = new/list()
for(var/turf/T in orange(M,outer_teleport_radius))
if(T in orange(M,inner_teleport_radius)) continue
if(istype(T,/turf/space)) continue
if(T.density) continue
if(T.x>world.maxx-outer_teleport_radius || T.x<outer_teleport_radius) continue //putting them at the edge is dumb
if(T.y>world.maxy-outer_teleport_radius || T.y<outer_teleport_radius) continue
turfs += T
if(!turfs.len)
var/list/turfs_to_pick_from = list()
for(var/turf/T in orange(M,outer_teleport_radius))
if(!(T in orange(M,inner_teleport_radius)))
turfs_to_pick_from += T
turfs += pick(/turf in turfs_to_pick_from)
if(smoke_spread)
var/datum/effects/system/harmless_smoke_spread/smoke = new /datum/effects/system/harmless_smoke_spread()
smoke.set_up(10, 0, M.loc)
smoke.start()
var/turf/picked = pick(turfs)
if(!isturf(picked)) return
M.loc = picked
+1
View File
@@ -0,0 +1 @@
//gotta test the framework in its fullest first, since it messes with it
+1
View File
@@ -0,0 +1 @@
//not gonna happen until muskets makes his emp_act() proc. we have enough copies of that proc
+47
View File
@@ -0,0 +1,47 @@
/obj/spell/disintegrate
name = "Disintegrate"
desc = "This spell instantly kills somebody adjacent to you with the vilest of magick."
school = "evocation"
recharge = 600
clothes_req = 1
invocation = "EI NATH"
invocation_type = "shout"
range = 1
var/sparks_spread = 1 //if set to 0, no sparks spread when disintegrating
var/kill_type = "disintegrate" //"disintegrate" leaves a pile of ash and bones (remains), "gib" gibs, "kill" adds damage_amount of damage_type
var/damage_amount = 2000 //only used if kill_type = "damage"
var/damage_type = "brute" //can be "brute", "fire", "toxin" or "oxygen"
/obj/spell/disintegrate/Click()
..()
if(!cast_check())
return
var/mob/M = input("Choose whom to [kill_type]", "ABRAKADABRA") as mob in oview(usr,range)
invocation()
if(sparks_spread)
var/datum/effects/system/spark_spread/s = new /datum/effects/system/spark_spread
s.set_up(4, 1, M)
s.start()
switch(kill_type)
if("disintegrate")
M.dust()
if("gib")
M.gib()
if("kill")
for(var/i=0,i<damage_amount,i++)
sleep(0) //to avoid troubles with instantly applying lots of damage, it seems to be buggy
switch(damage_type)
if("brute")
M.bruteloss++
if("toxin")
M.toxloss++
if("oxygen")
M.oxyloss++
if("fire")
M.fireloss++
+97
View File
@@ -0,0 +1,97 @@
/obj/spell/ethereal_jaunt
name = "Ethereal Jaunt"
desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls."
school = "transmutation"
recharge = 300
clothes_req = 1
invocation = "none"
invocation_type = "none"
range = -1 //can affect only the user by default, but with var editing can be an invis other spell
var/jaunt_duration = 50 //in deciseconds
/obj/spell/ethereal_jaunt/Click()
..()
if(!cast_check())
return
var/mob/M
if(range>=0)
M = input("Choose whom to jaunt", "ABRAKADABRA") as mob in view(usr,range)
else
M = usr
invocation()
spawn(0)
var/mobloc = get_turf(M.loc)
var/obj/dummy/spell_jaunt/holder = new /obj/dummy/spell_jaunt( mobloc )
var/atom/movable/overlay/animation = new /atom/movable/overlay( mobloc )
animation.name = "water"
animation.density = 0
animation.anchored = 1
animation.icon = 'mob.dmi'
animation.icon_state = "liquify"
animation.layer = 5
animation.master = holder
flick("liquify",animation)
M.loc = holder
M.client.eye = holder
var/datum/effects/system/steam_spread/steam = new /datum/effects/system/steam_spread()
steam.set_up(10, 0, mobloc)
steam.start()
sleep(jaunt_duration)
mobloc = get_turf(M.loc)
animation.loc = mobloc
steam.location = mobloc
steam.start()
M.canmove = 0
sleep(20)
flick("reappear",animation)
sleep(5)
M.loc = mobloc
M.canmove = 1
M.client.eye = M
del(animation)
del(holder)
/obj/dummy/spell_jaunt
name = "water"
icon = 'effects.dmi'
icon_state = "nothing"
var/canmove = 1
density = 0
anchored = 1
/obj/dummy/spell_jaunt/relaymove(var/mob/user, direction)
if (!src.canmove) return
switch(direction)
if(NORTH)
src.y++
if(SOUTH)
src.y--
if(EAST)
src.x++
if(WEST)
src.x--
if(NORTHEAST)
src.y++
src.x++
if(NORTHWEST)
src.y++
src.x--
if(SOUTHEAST)
src.y--
src.x++
if(SOUTHWEST)
src.y--
src.x--
src.canmove = 0
spawn(2) src.canmove = 1
/obj/dummy/spell_jaunt/ex_act(blah)
return
/obj/dummy/spell_jaunt/bullet_act(blah,blah)
return
+46
View File
@@ -0,0 +1,46 @@
/obj/spell/fireball
name = "Fireball"
desc = "This spell fires a fireball at a target and does not require wizard garb."
school = "evocation"
recharge = 200
clothes_req = 0
invocation = "ONI SOMA"
invocation_type = "shout"
var/radius_devastation = -1
var/radius_heavy = -1
var/radius_light = 2
var/radius_flash = 2
var/bruteloss = 20 // apparently fireball deals damage in addition to the explosion
var/fireloss = 25 // huh
var/lifetime = 200 // in deciseconds
/obj/spell/fireball/Click()
..()
if(!cast_check())
return
var/mob/M = input("Choose whom to fireball", "ABRAKADABRA") as mob|obj|turf in oview(usr,range)
invocation()
var/obj/overlay/A = new /obj/overlay( usr.loc )
A.icon_state = "fireball"
A.icon = 'wizard.dmi'
A.name = "a fireball"
A.anchored = 0
A.density = 0
var/i
for(i=0, i<lifetime, i++)
step_to(A,M,0)
if(get_dist(A,M) <= 1)
if(istype(M,/mob))
M:bruteloss += bruteloss
M:fireloss += fireloss
explosion(M.loc, radius_devastation, radius_heavy, radius_light, radius_flash)
del(A)
return
sleep(1)
del(A)
return
+30
View File
@@ -0,0 +1,30 @@
/obj/spell/forcewall
name = "Forcewall"
desc = "This spell creates an unbreakable wall that lasts for 30 seconds and does not need wizard garb."
school = "transmutation"
recharge = 100
clothes_req = 0
invocation = "TARCOL MINTI ZHERI"
invocation_type = "whisper"
range = 0 //default creates only a 1x1 forcewall in the user's tile
var/wall_lifespan = 300 //in deciseconds
var/wall_visibility = 1 //if 0, the created wall(s) is(are) invisible
/obj/spell/forcewall/Click()
..()
if(!cast_check())
return
invocation()
var/list/forcefields = list()
for(var/turf/T in range(usr,range))
forcefields += new /obj/forcefield(T)
spawn (wall_lifespan)
del (forcefields)
return
+23
View File
@@ -0,0 +1,23 @@
/obj/spell/knock
name = "Knock"
desc = "This spell opens nearby doors and does not require wizard garb."
school = "transmutation"
recharge = 100
clothes_req = 0
invocation = "AULIE OXIN FIERA"
invocation_type = "whisper"
range = 3
/obj/spell/knock/Click()
..()
if(!cast_check())
return
invocation()
for(var/obj/machinery/door/G in oview(usr,range))
spawn(1)
G.open()
return
+57
View File
@@ -0,0 +1,57 @@
/obj/spell/magic_missile
name = "Magic missile"
desc = "This spell fires several, slow moving, magic projectiles at nearby targets."
school = "evocation"
recharge = 100
clothes_req = 1
invocation = "FORTI GY AMA"
invocation_type = "shout"
range = 7
var/max_targets = 0 //max targets for the spell. set to 0 for no limit
var/missile_lifespan = 20 //in deciseconds * missile_step_delay
var/missile_step_delay = 5 //lower = faster missile
var/missile_weaken_amt = 5 //the amount by which the missile weakens the target it hits
var/missile_damage = 10 //the amount of fireloss each missile deals
/obj/spell/magic_missile/Click()
..()
if(!cast_check())
return
invocation()
var/targets = 0
for (var/mob/M as mob in oview(usr,range))
if(max_targets)
if(targets >= max_targets)
break
spawn(0)
var/obj/overlay/A = new /obj/overlay( usr.loc )
A.icon_state = "magicm"
A.icon = 'wizard.dmi'
A.name = "a magic missile"
A.anchored = 0
A.density = 0
A.layer = 4
var/i
for(i=0, i<missile_lifespan, i++)
var/obj/overlay/B = new /obj/overlay( A.loc )
B.icon_state = "magicmd"
B.icon = 'wizard.dmi'
B.name = "trail"
B.anchored = 1
B.density = 0
B.layer = 3
spawn(5)
del(B)
step_to(A,M,0)
if (get_dist(A,M) == 0)
M.weakened += missile_weaken_amt
M.fireloss += missile_damage
del(A)
return
sleep(missile_step_delay)
del(A)
targets++
+47
View File
@@ -0,0 +1,47 @@
/obj/spell/mutate
name = "Mutate"
desc = "This spell causes you to turn into a hulk and gain telekinesis for a short while."
school = "transmutation"
recharge = 400
clothes_req = 1
invocation = "BIRUZ BENNAR"
invocation_type = "shout"
message = "\blue You feel strong! Your mind expands!"
range = -1 //can affect only the user by default, but with var editing can be a mutate other spell
var/mutate_duration = 300 //in deciseconds
var/list/mutation_types = list("hulk","tk") //right now understands only "hulk", "tk", "cold resist", "xray" and "clown"
/obj/spell/mutate/Click()
..()
if(!cast_check())
return
var/mob/M
if(range>=0)
M = input("Choose whom to mutate", "ABRAKADABRA") as mob in view(usr,range)
else
M = usr
invocation()
M << text("[message]")
var/mutation = 0
for(var/MT in mutation_types)
switch(MT)
if("tk")
mutation |= 1
if("cold resist")
mutation |= 2
if("xray")
mutation |= 4
if("hulk")
mutation |= 8
if("clown")
mutation |= 16
M.mutations |= mutation
spawn (mutate_duration)
M.mutations &= ~mutation
return
+61
View File
@@ -0,0 +1,61 @@
/obj/spell/teleport
name = "Teleport"
desc = "This spell teleports you to a type of area of your selection."
school = "abjuration"
recharge = 600
clothes_req = 1
invocation = "SCYAR NILA"
invocation_type = "none" //hardcoded into the spell due to its specifics
range = -1 //can affect only the user by default, but with var editing can be a teleport other spell
var/smoke_spread = 1 //if set to 0, no smoke spreads when teleporting
/obj/spell/blink/Click()
..()
if(!cast_check())
return
var/mob/M
if(range>=0)
M = input("Choose whom to teleport", "ABRAKADABRA") as mob in view(usr,range)
else
M = usr
invocation()
var/A
A = input("Area to jump to", "BOOYEA", A) in teleportlocs
var/area/thearea = teleportlocs[A]
usr.say("SCYAR NILA [uppertext(A)]")
if(usr.gender=="male")
playsound(usr.loc, pick('vs_chant_conj_hm.wav','vs_chant_conj_lm.wav','vs_chant_ench_hm.wav','vs_chant_ench_lm.wav','vs_chant_evoc_hm.wav','vs_chant_evoc_lm.wav','vs_chant_illu_hm.wav','vs_chant_illu_lm.wav','vs_chant_necr_hm.wav','vs_chant_necr_lm.wav'), 100, 1)
else
playsound(usr.loc, pick('vs_chant_conj_hf.wav','vs_chant_conj_lf.wav','vs_chant_ench_hf.wav','vs_chant_ench_lf.wav','vs_chant_evoc_hf.wav','vs_chant_evoc_lf.wav','vs_chant_illu_hf.wav','vs_chant_illu_lf.wav','vs_chant_necr_hf.wav','vs_chant_necr_lf.wav'), 100, 1)
var/datum/effects/system/harmless_smoke_spread/smoke = new /datum/effects/system/harmless_smoke_spread()
if(smoke_spread)
smoke.set_up(5, 0, usr.loc)
smoke.attach(usr)
smoke.start()
var/list/L = list()
for(var/turf/T in get_area_turfs(thearea.type))
if(!T.density)
var/clear = 1
for(var/obj/O in T)
if(O.density)
clear = 0
break
if(clear)
L+=T
M.loc = pick(L)
if(smoke_spread)
smoke.start()