Magical fixes.

The wizard den is no longer pitch black.
Fixes targeted spells using the wrong caster/source.
Adds some missing checks using the caster source above.
Re-logging should no longer cause a loss of spell UI icons.
Mind transfer should no longer cause unintended spell loss.
This commit is contained in:
PsiOmegaDelta
2015-07-02 09:58:23 +02:00
parent bfe21b66c6
commit 616b4b60e4
12 changed files with 351 additions and 268 deletions

View File

@@ -1633,11 +1633,12 @@
#include "code\modules\spells\targeted\flesh_to_stone.dm" #include "code\modules\spells\targeted\flesh_to_stone.dm"
#include "code\modules\spells\targeted\genetic.dm" #include "code\modules\spells\targeted\genetic.dm"
#include "code\modules\spells\targeted\harvest.dm" #include "code\modules\spells\targeted\harvest.dm"
#include "code\modules\spells\targeted\horsemask.dm"
#include "code\modules\spells\targeted\mind_transfer.dm" #include "code\modules\spells\targeted\mind_transfer.dm"
#include "code\modules\spells\targeted\shift.dm" #include "code\modules\spells\targeted\shift.dm"
#include "code\modules\spells\targeted\subjugate.dm" #include "code\modules\spells\targeted\subjugate.dm"
#include "code\modules\spells\targeted\targeted.dm" #include "code\modules\spells\targeted\targeted.dm"
#include "code\modules\spells\targeted\equip\equip.dm"
#include "code\modules\spells\targeted\equip\horsemask.dm"
#include "code\modules\spells\targeted\projectile\dumbfire.dm" #include "code\modules\spells\targeted\projectile\dumbfire.dm"
#include "code\modules\spells\targeted\projectile\fireball.dm" #include "code\modules\spells\targeted\projectile\fireball.dm"
#include "code\modules\spells\targeted\projectile\magic_missile.dm" #include "code\modules\spells\targeted\projectile\magic_missile.dm"

View File

@@ -12,6 +12,18 @@
var/mob/spell_holder var/mob/spell_holder
/obj/screen/movable/spell_master/Destroy()
..()
for(var/obj/screen/spell/spells in spell_objects)
spells.spellmaster = null
spell_objects = null
if(spell_holder)
spell_holder.spell_masters -= src
/obj/screen/movable/spell_master/ResetVars()
..("spell_objects")
spell_objects = list()
/obj/screen/movable/spell_master/MouseDrop() /obj/screen/movable/spell_master/MouseDrop()
if(showing) if(showing)
return return
@@ -57,16 +69,23 @@
/obj/screen/movable/spell_master/proc/add_spell(var/spell/spell) /obj/screen/movable/spell_master/proc/add_spell(var/spell/spell)
if(!spell) return if(!spell) return
for(var/obj/screen/spell/spellscreen in spell_objects) if(spell.connected_button) //we have one already, for some reason
if(spellscreen.spell == spell) if(spell.connected_button in spell_objects)
return
else
spell_objects.Add(spell.connected_button)
toggle_open(2)
return return
if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one
return return
var/obj/screen/spell/newscreen = new var/obj/screen/spell/newscreen = PoolOrNew(/obj/screen/spell)
newscreen.spellmaster = src
newscreen.spell = spell newscreen.spell = spell
spell.connected_button = newscreen
if(!spell.override_base) //if it's not set, we do basic checks if(!spell.override_base) //if it's not set, we do basic checks
if(spell.spell_flags & CONSTRUCT_CHECK) if(spell.spell_flags & CONSTRUCT_CHECK)
newscreen.spell_base = "const" //construct spells newscreen.spell_base = "const" //construct spells
@@ -80,16 +99,13 @@
toggle_open(2) //forces the icons to refresh on screen toggle_open(2) //forces the icons to refresh on screen
/obj/screen/movable/spell_master/proc/remove_spell(var/spell/spell) /obj/screen/movable/spell_master/proc/remove_spell(var/spell/spell)
for(var/obj/screen/spell/s_object in spell_objects) qdel(spell.connected_button)
if(s_object.spell == spell)
spell_objects.Remove(s_object) spell.connected_button = null
qdel(s_object)
break
if(spell_objects.len) if(spell_objects.len)
toggle_open(showing + 1) toggle_open(showing + 1)
else else
spell_holder.spell_masters.Remove(src)
qdel(src) qdel(src)
/obj/screen/movable/spell_master/proc/silence_spells(var/amount) /obj/screen/movable/spell_master/proc/silence_spells(var/amount)
@@ -125,9 +141,18 @@
var/spell/spell = null var/spell/spell = null
var/handle_icon_updates = 0 var/handle_icon_updates = 0
var/obj/screen/movable/spell_master/spellmaster
var/icon/last_charged_icon var/icon/last_charged_icon
/obj/screen/spell/Destroy()
..()
if(spellmaster)
spellmaster.spell_objects -= src
if(spellmaster && !spellmaster.spell_objects.len)
qdel(spellmaster)
spellmaster = null
/obj/screen/spell/proc/update_charge(var/forced_update = 0) /obj/screen/spell/proc/update_charge(var/forced_update = 0)
if(!spell) if(!spell)
qdel(src) qdel(src)

View File

@@ -601,31 +601,35 @@
/mob/Stat() /mob/Stat()
..() ..()
. = (client && client.inactivity < 1200)
if(client && client.holder) if(.)
if(statpanel("Status")) if(client.holder)
statpanel("Status","Location:","([x], [y], [z])") if(statpanel("Status"))
statpanel("Status","CPU:","[world.cpu]") statpanel("Status","Location:","([x], [y], [z])")
statpanel("Status","Instances:","[world.contents.len]") statpanel("Status","CPU:","[world.cpu]")
if(statpanel("Status") && processScheduler && processScheduler.getIsRunning()) statpanel("Status","Instances:","[world.contents.len]")
for(var/datum/controller/process/P in processScheduler.processes) if(statpanel("Status") && processScheduler && processScheduler.getIsRunning())
statpanel("Status",P.getStatName(), P.getTickTime()) for(var/datum/controller/process/P in processScheduler.processes)
else statpanel("Status",P.getStatName(), P.getTickTime())
statpanel("Status","processScheduler is not running.") else
statpanel("Status","processScheduler is not running.")
if(listed_turf && client) if(listed_turf && client)
if(!TurfAdjacent(listed_turf)) if(!TurfAdjacent(listed_turf))
listed_turf = null listed_turf = null
else else
statpanel(listed_turf.name, null, listed_turf) statpanel(listed_turf.name, null, listed_turf)
for(var/atom/A in listed_turf) for(var/atom/A in listed_turf)
if(!A.mouse_opacity) if(!A.mouse_opacity)
continue continue
if(A.invisibility > see_invisible) if(A.invisibility > see_invisible)
continue continue
if(is_type_in_list(A, shouldnt_see)) if(is_type_in_list(A, shouldnt_see))
continue continue
statpanel(listed_turf.name, null, A) statpanel(listed_turf.name, null, A)
sleep(4) //Prevent updating the stat panel for the next .4 seconds, prevents clientside latency from updates
// facing verbs // facing verbs
/mob/proc/canface() /mob/proc/canface()
@@ -1026,4 +1030,4 @@ mob/proc/yank_out_object()
/mob/proc/throw_mode_on() /mob/proc/throw_mode_on()
src.in_throw_mode = 1 src.in_throw_mode = 1
if(src.throw_icon) if(src.throw_icon)
src.throw_icon.icon_state = "act_throw_on" src.throw_icon.icon_state = "act_throw_on"

View File

@@ -1,25 +1,25 @@
/* /*
Aoe turf spells target a ring of tiles around the user Aoe turf spells target a ring of tiles around the user
This ring has an outer radius (range) and an inner radius (inner_radius) This ring has an outer radius (range) and an inner radius (inner_radius)
Aoe turf spells have two useful flags: IGNOREDENSE and IGNORESPACE. These are explained in setup.dm Aoe turf spells have two useful flags: IGNOREDENSE and IGNORESPACE. These are explained in setup.dm
*/ */
/spell/aoe_turf //affects all turfs in view or range (depends) /spell/aoe_turf //affects all turfs in view or range (depends)
spell_flags = IGNOREDENSE spell_flags = IGNOREDENSE
var/inner_radius = -1 //for all your ring spell needs var/inner_radius = -1 //for all your ring spell needs
/spell/aoe_turf/choose_targets(mob/user = usr) /spell/aoe_turf/choose_targets(mob/user = usr)
var/list/targets = list() var/list/targets = list()
for(var/turf/target in view_or_range(range,user,selection_type)) for(var/turf/target in view_or_range(range, holder, selection_type))
if(!(target in view_or_range(inner_radius,user,selection_type))) if(!(target in view_or_range(inner_radius, holder, selection_type)))
if(target.density && (spell_flags & IGNOREDENSE)) if(target.density && (spell_flags & IGNOREDENSE))
continue continue
if(istype(target, /turf/space) && (spell_flags & IGNORESPACE)) if(istype(target, /turf/space) && (spell_flags & IGNORESPACE))
continue continue
targets += target targets += target
if(!targets.len) //doesn't waste the spell if(!targets.len) //doesn't waste the spell
return return
return targets return targets

View File

@@ -1,9 +1,9 @@
var/list/spells = typesof(/spell) //needed for the badmin verb for now var/list/spells = typesof(/spell) //needed for the badmin verb for now
/spell /spell
name = "Spell" var/name = "Spell"
desc = "A spell" var/desc = "A spell"
parent_type = /atom/movable parent_type = /datum
var/panel = "Spells"//What panel the proc holder needs to go on. var/panel = "Spells"//What panel the proc holder needs to go on.
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/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?
@@ -25,8 +25,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
var/range = 7 //the range of the spell; outer radius for aoe spells var/range = 7 //the range of the spell; outer radius for aoe spells
var/message = "" //whatever it says to the guy affected by it var/message = "" //whatever it says to the guy affected by it
var/selection_type = "view" //can be "range" or "view" var/selection_type = "view" //can be "range" or "view"
var/atom/movable/holder //where the spell is. Normally the user, can be a projectile var/atom/movable/holder //where the spell is. Normally the user, can be an item
var/duration = 0 //how long the spell lasts var/duration = 0 //how long the spell lasts
var/list/spell_levels = list(Sp_SPEED = 0, Sp_POWER = 0) //the current spell levels - total spell levels can be obtained by just adding the two values var/list/spell_levels = list(Sp_SPEED = 0, Sp_POWER = 0) //the current spell levels - total spell levels can be obtained by just adding the two values
@@ -53,6 +52,8 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
var/hud_state = "" //name of the icon used in generating the spell hud object var/hud_state = "" //name of the icon used in generating the spell hud object
var/override_base = "" var/override_base = ""
var/obj/screen/connected_button
/////////////////////// ///////////////////////
///SETUP AND PROCESS/// ///SETUP AND PROCESS///
/////////////////////// ///////////////////////
@@ -69,11 +70,6 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
sleep(1) sleep(1)
return return
/spell/Click()
..()
perform(usr)
///////////////// /////////////////
/////CASTING///// /////CASTING/////
///////////////// /////////////////
@@ -182,18 +178,18 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
/spell/proc/cast_check(skipcharge = 0,mob/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 /spell/proc/cast_check(skipcharge = 0,mob/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(!(src in user.spell_list)) if(!(src in user.spell_list) && holder == user)
user << "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>" user << "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>"
return 0 return 0
if(silenced > 0) if(silenced > 0)
return return
var/turf/Turf = get_turf(user) var/turf/user_turf = get_turf(user)
if(!Turf) if(!user_turf)
user << "<span class='warning'>You cannot cast spells in null space!</span>" user << "<span class='warning'>You cannot cast spells in null space!</span>"
if(spell_flags & Z2NOCAST && (Turf.z in config.admin_levels)) //Certain spells are not allowed on the centcomm zlevel if(spell_flags & Z2NOCAST && (user_turf.z in config.admin_levels)) //Certain spells are not allowed on the centcomm zlevel
return 0 return 0
if(spell_flags & CONSTRUCT_CHECK) if(spell_flags & CONSTRUCT_CHECK)
@@ -201,7 +197,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
if(findNullRod(T)) if(findNullRod(T))
return 0 return 0
if(istype(user, /mob/living/simple_animal)) if(istype(user, /mob/living/simple_animal) && holder == user)
var/mob/living/simple_animal/SA = user var/mob/living/simple_animal/SA = user
if(SA.purge) if(SA.purge)
SA << "<span class='warning'>The nullrod's power interferes with your own!</span>" SA << "<span class='warning'>The nullrod's power interferes with your own!</span>"
@@ -210,7 +206,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
if(!src.check_charge(skipcharge, user)) //sees if we can cast based on charges alone if(!src.check_charge(skipcharge, user)) //sees if we can cast based on charges alone
return 0 return 0
if(!(spell_flags & GHOSTCAST)) if(!(spell_flags & GHOSTCAST) && holder == user)
if(user.stat && !(spell_flags & STATALLOWED)) if(user.stat && !(spell_flags & STATALLOWED))
usr << "Not when you're incapacitated." usr << "Not when you're incapacitated."
return 0 return 0
@@ -221,7 +217,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now
return 0 return 0
var/spell/noclothes/spell = locate() in user.spell_list var/spell/noclothes/spell = locate() in user.spell_list
if((spell_flags & NEEDSCLOTHES) && !(spell && istype(spell)))//clothes check if((spell_flags & NEEDSCLOTHES) && !(spell && istype(spell)) && holder == user)//clothes check
if(!user.wearing_wiz_garb()) if(!user.wearing_wiz_garb())
return 0 return 0

View File

@@ -211,7 +211,7 @@
temp = "You have learned knock." temp = "You have learned knock."
if("horseman") if("horseman")
feedback_add_details("wizard_spell_learned","HH") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells feedback_add_details("wizard_spell_learned","HH") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
H.add_spell(new/spell/targeted/horsemask) H.add_spell(new/spell/targeted/equip_item/horsemask)
temp = "You have learned curse of the horseman." temp = "You have learned curse of the horseman."
if("fleshtostone") if("fleshtostone")
feedback_add_details("wizard_spell_learned","FS") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells feedback_add_details("wizard_spell_learned","FS") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells
@@ -424,7 +424,7 @@
user.Weaken(20) user.Weaken(20)
/obj/item/weapon/spellbook/oneuse/horsemask /obj/item/weapon/spellbook/oneuse/horsemask
spell = /spell/targeted/horsemask spell = /spell/targeted/equip_item/horsemask
spellname = "horses" spellname = "horses"
icon_state ="bookhorses" icon_state ="bookhorses"
desc = "This book is more horse than your mind has room for." desc = "This book is more horse than your mind has room for."

View File

@@ -4,17 +4,26 @@
for(var/obj/screen/movable/spell_master/spell_master in spell_masters) for(var/obj/screen/movable/spell_master/spell_master in spell_masters)
spell_master.update_spells(0, src) spell_master.update_spells(0, src)
/mob/Stat() /mob/Login()
..() ..()
if(spell_list && spell_list.len && statpanel("Spells")) if(spell_masters)
for(var/obj/screen/movable/spell_master/spell_master in spell_masters)
spell_master.toggle_open(1)
client.screen -= spell_master
/mob/Stat()
. = ..()
if(. && spell_list && spell_list.len)
for(var/spell/S in spell_list) for(var/spell/S in spell_list)
if((!S.connected_button) || !statpanel(S.panel))
continue //Not showing the noclothes spell
switch(S.charge_type) switch(S.charge_type)
if(Sp_RECHARGE) if(Sp_RECHARGE)
statpanel("Spells","[S.charge_counter/10.0]/[S.charge_max/10]",S) statpanel(S.panel,"[S.charge_counter/10.0]/[S.charge_max/10]",S.connected_button)
if(Sp_CHARGES) if(Sp_CHARGES)
statpanel("Spells","[S.charge_counter]/[S.charge_max]",S) statpanel(S.panel,"[S.charge_counter]/[S.charge_max]",S.connected_button)
if(Sp_HOLDVAR) if(Sp_HOLDVAR)
statpanel("Spells","[S.holder_var_type] [S.holder_var_amount]",S) statpanel(S.panel,"[S.holder_var_type] [S.holder_var_amount]",S.connected_button)
/mob/proc/add_spell(var/spell/spell_to_add, var/spell_base = "wiz_spell_ready", var/master_type = /obj/screen/movable/spell_master) /mob/proc/add_spell(var/spell/spell_to_add, var/spell_base = "wiz_spell_ready", var/master_type = /obj/screen/movable/spell_master)
if(!spell_masters) if(!spell_masters)

View File

@@ -0,0 +1,40 @@
//You can set duration to 0 to have the items last forever
/spell/targeted/equip_item
name = "equipment spell"
var/list/equipped_summons = list() //assoc list of text ids and paths to spawn
var/list/summoned_items = list() //list of items we summoned and will dispose when the spell runs out
var/delete_old = 1 //if the item previously in the slot is deleted - otherwise, it's dropped
/spell/targeted/equip_item/cast(list/targets, mob/user = usr)
..()
for(var/mob/living/L in targets)
for(var/slot_id in equipped_summons)
var/to_create = equipped_summons[slot_id]
slot_id = text2num(slot_id) //because the index is text, we access this instead
var/obj/item/new_item = summon_item(to_create)
var/obj/item/old_item = L.get_equipped_item(slot_id)
L.equip_to_slot(new_item, slot_id)
if(old_item)
L.remove_from_mob(old_item)
if(delete_old)
qdel(old_item)
else
old_item.loc = L.loc
if(duration)
summoned_items += new_item //we store it in a list to remove later
if(duration)
spawn(duration)
for(var/obj/item/to_remove in summoned_items)
if(istype(to_remove.loc, /mob))
var/mob/M = to_remove.loc
M.remove_from_mob(to_remove)
qdel(to_remove)
/spell/targeted/equip_item/proc/summon_item(var/newtype)
return new newtype

View File

@@ -1,35 +1,38 @@
/spell/targeted/horsemask /spell/targeted/equip_item/horsemask
name = "Curse of the Horseman" name = "Curse of the Horseman"
desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes."
school = "transmutation" school = "transmutation"
charge_type = Sp_RECHARGE charge_type = Sp_RECHARGE
charge_max = 150 charge_max = 150
charge_counter = 0 charge_counter = 0
spell_flags = 0 spell_flags = 0
invocation = "KN'A FTAGHU, PUCK 'BTHNK!" invocation = "KN'A FTAGHU, PUCK 'BTHNK!"
invocation_type = SpI_SHOUT invocation_type = SpI_SHOUT
range = 7 range = 7
max_targets = 1 max_targets = 1
cooldown_min = 30 //30 deciseconds reduction per rank cooldown_min = 30 //30 deciseconds reduction per rank
selection_type = "range" selection_type = "range"
compatible_mobs = list(/mob/living/carbon/human) compatible_mobs = list(/mob/living/carbon/human)
hud_state = "wiz_horse" hud_state = "wiz_horse"
/spell/targeted/horsemask/cast(list/targets, mob/user = usr) /spell/targeted/equip_item/horsemask/New()
..() ..()
for(var/mob/living/target in targets) equipped_summons = list("[slot_wear_mask]" = /obj/item/clothing/mask/horsehead)
var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead
magichead.canremove = 0 //curses! /spell/targeted/equip_item/horsemask/cast(list/targets, mob/user = usr)
magichead.flags_inv = null //so you can still see their face ..()
magichead.voicechange = 1 //NEEEEIIGHH for(var/mob/living/target in targets)
target.visible_message( "<span class='danger'>[target]'s face lights up in fire, and after the event a horse's head takes its place!</span>", \ target.visible_message( "<span class='danger'>[target]'s face lights up in fire, and after the event a horse's head takes its place!</span>", \
"<span class='danger'>Your face burns up, and shortly after the fire you realise you have the face of a horse!</span>") "<span class='danger'>Your face burns up, and shortly after the fire you realise you have the face of a horse!</span>")
var/obj/old_mask = target.wear_mask flick("e_flash", target.flash)
if(old_mask)
target.drop_from_inventory(old_mask) /spell/targeted/equip_item/horsemask/summon_item(var/new_type)
qdel(old_mask) //get rid of this shit var/obj/item/new_item = new new_type
target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) if(istype(new_item, /obj/item/clothing/mask/horsehead))
var/obj/item/clothing/mask/horsehead/magichead = new_item
flick("e_flash", target.flash) magichead.canremove = 0 //curses!
magichead.flags_inv = null //so you can still see their face
magichead.voicechange = 1 //NEEEEIIGHH
return new_item

View File

@@ -57,7 +57,7 @@ code\game\dna\genes\goon_powers.dm
spell_flags = Z2NOCAST | NEEDSCLOTHES | INCLUDEUSER spell_flags = Z2NOCAST | NEEDSCLOTHES | INCLUDEUSER
invocation = "BIRUZ BENNAR" invocation = "BIRUZ BENNAR"
invocation_type = SpI_SHOUT invocation_type = SpI_SHOUT
message = "\blue You feel strong! You feel a pressure building behind your eyes!" message = "<span class='notice'>You feel strong! You feel a pressure building behind your eyes!</span>"
range = 0 range = 0
max_targets = 1 max_targets = 1

View File

@@ -51,7 +51,10 @@
ghost.spell_list = victim.spell_list//If they have spells, transfer them. Now we basically have a backup mob. ghost.spell_list = victim.spell_list//If they have spells, transfer them. Now we basically have a backup mob.
caster.mind.transfer_to(victim) caster.mind.transfer_to(victim)
victim.spell_list = caster.spell_list//Now they are inside the victim's body. victim.spell_list = list() //clear those out
for(var/spell/S in caster.spell_list)
victim.add_spell(S) //Now they are inside the victim's body - this also generates the HUD
caster.spell_list = list() //clean that out as well
if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster. if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster.
for(var/V in caster.mind.special_verbs)//Not too important but could come into play. for(var/V in caster.mind.special_verbs)//Not too important but could come into play.
@@ -59,7 +62,9 @@
ghost.mind.transfer_to(caster) ghost.mind.transfer_to(caster)
caster.key = ghost.key //have to transfer the key since the mind was not active caster.key = ghost.key //have to transfer the key since the mind was not active
caster.spell_list = ghost.spell_list for(var/spell/S in ghost.spell_list)
caster.add_spell(S)
ghost.spell_list = list()
if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here. if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here.
for(var/V in caster.mind.special_verbs) for(var/V in caster.mind.special_verbs)
@@ -71,4 +76,4 @@
//After a certain amount of time the victim gets a message about being in a different body. //After a certain amount of time the victim gets a message about being in a different body.
spawn(msg_wait) spawn(msg_wait)
caster << "\red You feel woozy and lightheaded. <b>Your body doesn't seem like your own.</b>" caster << "<span class='danger'>You feel woozy and lightheaded. Your body doesn't seem like your own.</span>"

View File

@@ -1,145 +1,145 @@
/* /*
Targeted spells (with the exception of dumbfire) select from all the mobs in the defined range Targeted spells (with the exception of dumbfire) select from all the mobs in the defined range
Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are explained in setup.dm Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are explained in setup.dm
*/ */
/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob /spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob
var/max_targets = 1 //leave 0 for unlimited targets in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range var/max_targets = 1 //leave 0 for unlimited targets in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range
var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast
var/amt_weakened = 0 var/amt_weakened = 0
var/amt_paralysis = 0 var/amt_paralysis = 0
var/amt_stunned = 0 var/amt_stunned = 0
var/amt_dizziness = 0 var/amt_dizziness = 0
var/amt_confused = 0 var/amt_confused = 0
var/amt_stuttering = 0 var/amt_stuttering = 0
//set to negatives for healing //set to negatives for healing
var/amt_dam_fire = 0 var/amt_dam_fire = 0
var/amt_dam_brute = 0 var/amt_dam_brute = 0
var/amt_dam_oxy = 0 var/amt_dam_oxy = 0
var/amt_dam_tox = 0 var/amt_dam_tox = 0
var/amt_eye_blind = 0 var/amt_eye_blind = 0
var/amt_eye_blurry = 0 var/amt_eye_blurry = 0
var/list/compatible_mobs = list() var/list/compatible_mobs = list()
/spell/targeted/choose_targets(mob/user = usr) /spell/targeted/choose_targets(mob/user = usr)
var/list/targets = list() var/list/targets = list()
if(max_targets == 0) //unlimited if(max_targets == 0) //unlimited
if(range == -2) if(range == -2)
targets = living_mob_list targets = living_mob_list
else else
for(var/mob/living/target in view_or_range(range, user, selection_type)) for(var/mob/living/target in view_or_range(range, holder, selection_type))
targets += target targets += target
else if(max_targets == 1) //single target can be picked else if(max_targets == 1) //single target can be picked
if((range == 0 || range == -1) && spell_flags & INCLUDEUSER) if((range == 0 || range == -1) && spell_flags & INCLUDEUSER)
targets += user targets += user
else else
var/list/possible_targets = list() var/list/possible_targets = list()
var/list/starting_targets var/list/starting_targets
if(range == -2) if(range == -2)
starting_targets = living_mob_list starting_targets = living_mob_list
else else
starting_targets = view_or_range(range, user, selection_type) starting_targets = view_or_range(range, holder, selection_type)
for(var/mob/living/M in starting_targets) for(var/mob/living/M in starting_targets)
if(!(spell_flags & INCLUDEUSER) && M == user) if(!(spell_flags & INCLUDEUSER) && M == user)
continue continue
if(compatible_mobs && compatible_mobs.len) if(compatible_mobs && compatible_mobs.len)
if(!is_type_in_list(M, compatible_mobs)) continue if(!is_type_in_list(M, compatible_mobs)) continue
if(compatible_mobs && compatible_mobs.len && !is_type_in_list(M, compatible_mobs)) if(compatible_mobs && compatible_mobs.len && !is_type_in_list(M, compatible_mobs))
continue continue
possible_targets += M possible_targets += M
if(possible_targets.len) if(possible_targets.len)
if(spell_flags & SELECTABLE) //if we are allowed to choose. see setup.dm for details if(spell_flags & SELECTABLE) //if we are allowed to choose. see setup.dm for details
var/mob/temp_target = input(user, "Choose the target for the spell.", "Targeting") as null|mob in possible_targets var/mob/temp_target = input(user, "Choose the target for the spell.", "Targeting") as null|mob in possible_targets
if(temp_target) if(temp_target)
targets += temp_target targets += temp_target
else else
targets += pick(possible_targets) targets += pick(possible_targets)
//Adds a safety check post-input to make sure those targets are actually in range. //Adds a safety check post-input to make sure those targets are actually in range.
else else
var/list/possible_targets = list() var/list/possible_targets = list()
var/list/starting_targets var/list/starting_targets
if(range == -2) if(range == -2)
starting_targets = living_mob_list starting_targets = living_mob_list
else else
starting_targets = view_or_range(range, user, selection_type) starting_targets = view_or_range(range, holder, selection_type)
for(var/mob/living/target in starting_targets) for(var/mob/living/target in starting_targets)
if(!(spell_flags & INCLUDEUSER) && target == user) if(!(spell_flags & INCLUDEUSER) && target == user)
continue continue
if(compatible_mobs && !is_type_in_list(target, compatible_mobs)) if(compatible_mobs && !is_type_in_list(target, compatible_mobs))
continue continue
possible_targets += target possible_targets += target
if(spell_flags & SELECTABLE) if(spell_flags & SELECTABLE)
for(var/i = 1; i<=max_targets, i++) for(var/i = 1; i<=max_targets, i++)
if(!possible_targets.len) if(!possible_targets.len)
break break
var/mob/M = input(user, "Choose the target for the spell.", "Targeting") as null|mob in possible_targets var/mob/M = input(user, "Choose the target for the spell.", "Targeting") as null|mob in possible_targets
if(!M) if(!M)
break break
if(range != -2) if(range != -2)
if(!(M in view_or_range(range, user, selection_type))) if(!(M in view_or_range(range, holder, selection_type)))
continue continue
targets += M targets += M
possible_targets -= M possible_targets -= M
else else
for(var/i=1,i<=max_targets,i++) for(var/i=1,i<=max_targets,i++)
if(!possible_targets.len) if(!possible_targets.len)
break break
if(target_ignore_prev) if(target_ignore_prev)
var/target = pick(possible_targets) var/target = pick(possible_targets)
possible_targets -= target possible_targets -= target
targets += target targets += target
else else
targets += pick(possible_targets) targets += pick(possible_targets)
if(!(spell_flags & INCLUDEUSER) && (user in targets)) if(!(spell_flags & INCLUDEUSER) && (user in targets))
targets -= user targets -= user
if(compatible_mobs && compatible_mobs.len) if(compatible_mobs && compatible_mobs.len)
for(var/mob/living/target in targets) //filters out all the non-compatible mobs for(var/mob/living/target in targets) //filters out all the non-compatible mobs
if(!is_type_in_list(target, compatible_mobs)) if(!is_type_in_list(target, compatible_mobs))
targets -= target targets -= target
return targets return targets
/spell/targeted/cast(var/list/targets, mob/user) /spell/targeted/cast(var/list/targets, mob/user)
for(var/mob/living/target in targets) for(var/mob/living/target in targets)
if(range >= 0) if(range >= 0)
if(!(target in view_or_range(range, user, selection_type))) //filter at time of casting if(!(target in view_or_range(range, holder, selection_type))) //filter at time of casting
targets -= target targets -= target
continue continue
apply_spell_damage(target) apply_spell_damage(target)
/spell/targeted/proc/apply_spell_damage(mob/living/target) /spell/targeted/proc/apply_spell_damage(mob/living/target)
target.adjustBruteLoss(amt_dam_brute) target.adjustBruteLoss(amt_dam_brute)
target.adjustFireLoss(amt_dam_fire) target.adjustFireLoss(amt_dam_fire)
target.adjustToxLoss(amt_dam_tox) target.adjustToxLoss(amt_dam_tox)
target.adjustOxyLoss(amt_dam_oxy) target.adjustOxyLoss(amt_dam_oxy)
//disabling //disabling
target.Weaken(amt_weakened) target.Weaken(amt_weakened)
target.Paralyse(amt_paralysis) target.Paralyse(amt_paralysis)
target.Stun(amt_stunned) target.Stun(amt_stunned)
if(amt_weakened || amt_paralysis || amt_stunned) if(amt_weakened || amt_paralysis || amt_stunned)
if(target.buckled) if(target.buckled)
target.buckled = null target.buckled = null
target.eye_blind += amt_eye_blind target.eye_blind += amt_eye_blind
target.eye_blurry += amt_eye_blurry target.eye_blurry += amt_eye_blurry
target.dizziness += amt_dizziness target.dizziness += amt_dizziness
target.confused += amt_confused target.confused += amt_confused
target.stuttering += amt_stuttering target.stuttering += amt_stuttering