mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
567 lines
20 KiB
Plaintext
567 lines
20 KiB
Plaintext
#define TARGET_CLOSEST 1
|
|
#define TARGET_RANDOM 2
|
|
|
|
|
|
/obj/effect/proc_holder
|
|
var/panel = "Debug"//What panel the proc holder needs to go on.
|
|
var/active = FALSE //Used by toggle based abilities.
|
|
var/ranged_mousepointer
|
|
var/mob/living/ranged_ability_user
|
|
var/ranged_clickcd_override = -1
|
|
var/has_action = TRUE
|
|
var/datum/action/spell_action/action = null
|
|
var/action_icon = 'icons/mob/actions/actions_spells.dmi'
|
|
var/action_icon_state = "spell_default"
|
|
var/action_background_icon_state = "bg_spell"
|
|
var/base_action = /datum/action/spell_action
|
|
|
|
/obj/effect/proc_holder/Initialize()
|
|
. = ..()
|
|
if(has_action)
|
|
action = new base_action(src)
|
|
|
|
/obj/effect/proc_holder/proc/on_gain(mob/living/user)
|
|
return
|
|
|
|
/obj/effect/proc_holder/proc/on_lose(mob/living/user)
|
|
return
|
|
|
|
/obj/effect/proc_holder/proc/fire(mob/living/user)
|
|
return TRUE
|
|
|
|
/obj/effect/proc_holder/proc/get_panel_text()
|
|
return ""
|
|
|
|
GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for the badmin verb for now
|
|
|
|
/obj/effect/proc_holder/Destroy()
|
|
if (action)
|
|
qdel(action)
|
|
if(ranged_ability_user)
|
|
remove_ranged_ability()
|
|
return ..()
|
|
|
|
/obj/effect/proc_holder/singularity_act()
|
|
return
|
|
|
|
/obj/effect/proc_holder/singularity_pull()
|
|
return
|
|
|
|
/obj/effect/proc_holder/Topic(href, href_list)
|
|
. = ..()
|
|
if(href_list["click"])
|
|
// first of all make sure we valid
|
|
var/mob/living/as_living = usr
|
|
if(!(src in usr.mob_spell_list) && !(usr.mind && (src in usr.mind.spell_list)) && !(istype(as_living) && (src in as_living.abilities)))
|
|
message_admins("[key_name_admin(src)] clicked on an invalid proc_holder href! ([src])")
|
|
log_game("[key_name(src)] clicked on an invalid proc_holder href! ([src])")
|
|
return
|
|
Click()
|
|
|
|
/obj/effect/proc_holder/proc/InterceptClickOn(mob/living/caller, params, atom/A)
|
|
if(caller.ranged_ability != src || ranged_ability_user != caller) //I'm not actually sure how these would trigger, but, uh, safety, I guess?
|
|
to_chat(caller, span_warning("<b>[caller.ranged_ability.name]</b> has been disabled."))
|
|
caller.ranged_ability.remove_ranged_ability()
|
|
return TRUE //TRUE for failed, FALSE for passed.
|
|
if(ranged_clickcd_override >= 0)
|
|
ranged_ability_user.next_click = world.time + ranged_clickcd_override
|
|
else
|
|
ranged_ability_user.next_click = world.time + CLICK_CD_CLICK_ABILITY
|
|
ranged_ability_user.face_atom(A)
|
|
return FALSE
|
|
|
|
/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, msg, forced)
|
|
if(!user || !user.client)
|
|
return
|
|
if(user.ranged_ability && user.ranged_ability != src)
|
|
if(forced)
|
|
to_chat(user, span_warning("<b>[user.ranged_ability.name]</b> has been replaced by <b>[name]</b>."))
|
|
user.ranged_ability.remove_ranged_ability()
|
|
else
|
|
return
|
|
user.ranged_ability = src
|
|
user.click_intercept = src
|
|
user.update_mouse_pointer()
|
|
ranged_ability_user = user
|
|
if(msg)
|
|
to_chat(ranged_ability_user, msg)
|
|
active = TRUE
|
|
update_icon()
|
|
|
|
/obj/effect/proc_holder/proc/remove_ranged_ability(msg)
|
|
if(!ranged_ability_user || !ranged_ability_user.client || (ranged_ability_user.ranged_ability && ranged_ability_user.ranged_ability != src)) //To avoid removing the wrong ability
|
|
return
|
|
ranged_ability_user.ranged_ability = null
|
|
ranged_ability_user.click_intercept = null
|
|
ranged_ability_user.update_mouse_pointer()
|
|
if(msg)
|
|
to_chat(ranged_ability_user, msg)
|
|
ranged_ability_user = null
|
|
active = FALSE
|
|
update_icon()
|
|
|
|
/obj/effect/proc_holder/spell
|
|
name = "Spell"
|
|
desc = "A wizard spell."
|
|
panel = "Spells"
|
|
var/sound = null //The sound the spell makes when it is cast
|
|
anchored = TRUE // Crap like fireball projectiles are proc_holders, this is needed so fireballs don't get blown back into your face via atmos etc.
|
|
pass_flags = PASSTABLE
|
|
density = FALSE
|
|
opacity = 0
|
|
|
|
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/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that
|
|
|
|
var/charge_max = 10 SECONDS //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges"
|
|
var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges"
|
|
var/still_recharging_msg = span_notice("The spell is still recharging.")
|
|
var/recharging = TRUE
|
|
|
|
var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var"
|
|
var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used
|
|
|
|
var/clothes_req = TRUE //see if it requires clothes
|
|
var/cult_req = FALSE //SPECIAL SNOWFLAKE clothes required for cult only spells
|
|
var/human_req = FALSE //spell can only be cast by humans
|
|
var/nonabstract_req = FALSE //spell can only be cast by mobs that are physical entities
|
|
var/stat_allowed = FALSE //see if it requires being conscious/alive, need to set to 1 for ghostpells
|
|
var/phase_allowed = FALSE // If true, the spell can be cast while phased, eg. blood crawling, ethereal jaunting
|
|
var/antimagic_allowed = FALSE // If false, the spell cannot be cast while under the effect of antimagic
|
|
var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell
|
|
var/invocation_emote_self = null
|
|
var/invocation_type = "none" //can be none, whisper, emote and shout
|
|
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/selection_type = "view" //can be "range" or "view"
|
|
var/spell_level = 0 //if a spell can be taken multiple times, this raises
|
|
var/level_max = 4 //The max possible level_max is 4
|
|
var/cooldown_min = 0 //This defines what spell quickened four times has as a cooldown. Make sure to set this for every spell
|
|
var/player_lock = TRUE //If it can be used by simple mobs
|
|
|
|
var/overlay = 0
|
|
var/overlay_icon = 'icons/obj/wizard.dmi'
|
|
var/overlay_icon_state = "spell"
|
|
var/overlay_lifespan = 0
|
|
|
|
var/sparks_spread = 0
|
|
var/sparks_amt = 0 //cropped at 10
|
|
/// The typepath of the smoke to create on cast.
|
|
var/smoke_spread = null
|
|
/// The amount of smoke to create on case. This is a range so a value of 5 will create enough smoke to cover everything within 5 steps.
|
|
var/smoke_amt = 0
|
|
|
|
var/centcom_cancast = TRUE //Whether or not the spell should be allowed on z2
|
|
|
|
var/atom/movable/screen/cooldown_overlay/cooldown_overlay
|
|
|
|
action_icon = 'icons/mob/actions/actions_spells.dmi'
|
|
action_icon_state = "spell_default"
|
|
action_background_icon_state = "bg_spell"
|
|
base_action = /datum/action/spell_action/spell
|
|
|
|
/obj/effect/proc_holder/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(player_lock)
|
|
if(!user.mind || !(src in user.mind.spell_list) && !(src in user.mob_spell_list))
|
|
to_chat(user, span_warning("You shouldn't have this spell! Something's wrong."))
|
|
return FALSE
|
|
else
|
|
if(!(src in user.mob_spell_list))
|
|
return FALSE
|
|
|
|
var/turf/T = get_turf(user)
|
|
if(is_centcom_level(T.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
|
|
to_chat(user, span_notice("You can't cast this spell here."))
|
|
return FALSE
|
|
|
|
if(!skipcharge)
|
|
if(!charge_check(user))
|
|
return FALSE
|
|
|
|
if(user.stat && !stat_allowed)
|
|
to_chat(user, span_notice("Not when you're incapacitated."))
|
|
return FALSE
|
|
|
|
if(!antimagic_allowed)
|
|
var/antimagic = user.anti_magic_check(TRUE, FALSE, FALSE, 0, TRUE)
|
|
if(antimagic)
|
|
if(isitem(antimagic))
|
|
to_chat(user, span_notice("[antimagic] is interfering with your magic."))
|
|
else
|
|
to_chat(user, span_notice("Magic seems to flee from you, you can't gather enough power to cast this spell."))
|
|
return FALSE
|
|
|
|
if(!phase_allowed && istype(user.loc, /obj/effect/dummy))
|
|
to_chat(user, span_notice("[name] cannot be cast unless you are completely manifested in the material plane."))
|
|
return FALSE
|
|
|
|
if(ishuman(user))
|
|
|
|
var/mob/living/carbon/human/H = user
|
|
|
|
if((invocation_type == "whisper" || invocation_type == "shout") && !H.can_speak_vocal())
|
|
to_chat(user, span_notice("You can't get the words out!"))
|
|
return FALSE
|
|
|
|
var/list/casting_clothes = typecacheof(list(/obj/item/clothing/suit/wizrobe,
|
|
/obj/item/clothing/suit/space/hardsuit/wizard,
|
|
/obj/item/clothing/head/wizard,
|
|
/obj/item/clothing/head/wizard/armor,
|
|
/obj/item/clothing/suit/wizrobe/armor,
|
|
/obj/item/clothing/head/helmet/space/hardsuit/wizard,
|
|
/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard))
|
|
|
|
if(clothes_req) //clothes check
|
|
if(!is_type_in_typecache(H.wear_suit, casting_clothes))
|
|
to_chat(H, span_notice("I don't feel strong enough without my robe."))
|
|
return FALSE
|
|
if(!is_type_in_typecache(H.head, casting_clothes))
|
|
to_chat(H, span_notice("I don't feel strong enough without my hat."))
|
|
return FALSE
|
|
if(cult_req) //CULT_REQ CLOTHES CHECK
|
|
if(!istype(H.wear_suit, /obj/item/clothing/suit/magusred) && !istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/cult))
|
|
to_chat(H, span_notice("I don't feel strong enough without my armor."))
|
|
return FALSE
|
|
if(!istype(H.head, /obj/item/clothing/head/magus) && !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/cult))
|
|
to_chat(H, span_notice("I don't feel strong enough without my helmet."))
|
|
return FALSE
|
|
else
|
|
if(clothes_req || human_req)
|
|
to_chat(user, span_notice("This spell can only be cast by humans!"))
|
|
return FALSE
|
|
if(nonabstract_req && (isbrain(user) || ispAI(user)))
|
|
to_chat(user, span_notice("This spell can only be cast by physical beings!"))
|
|
return FALSE
|
|
|
|
|
|
if(!skipcharge)
|
|
switch(charge_type)
|
|
if("recharge")
|
|
charge_counter = 0 //doesn't start recharging until the targets selecting ends
|
|
if("charges")
|
|
charge_counter-- //returns the charge if the targets selecting fails
|
|
if("holdervar")
|
|
adjust_var(user, holder_var_type, holder_var_amount)
|
|
if(action)
|
|
action.UpdateButtonIcon()
|
|
return TRUE
|
|
|
|
/obj/effect/proc_holder/spell/proc/charge_check(mob/user, silent = FALSE)
|
|
switch(charge_type)
|
|
if("recharge")
|
|
if(charge_counter < charge_max)
|
|
if(!silent)
|
|
to_chat(user, still_recharging_msg)
|
|
return FALSE
|
|
if("charges")
|
|
if(!charge_counter)
|
|
if(!silent)
|
|
to_chat(user, span_notice("[name] has no charges left."))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount
|
|
switch(invocation_type)
|
|
if("shout")
|
|
if(prob(50))//Auto-mute? Fuck that noise
|
|
user.say(invocation, forced = "spell")
|
|
else
|
|
user.say(replacetext(invocation," ","`"), forced = "spell")
|
|
if("whisper")
|
|
if(prob(50))
|
|
user.whisper(invocation)
|
|
else
|
|
user.whisper(replacetext(invocation," ","`"))
|
|
if("emote")
|
|
user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm
|
|
|
|
/obj/effect/proc_holder/spell/proc/playMagSound()
|
|
playsound(get_turf(usr), sound,50,1)
|
|
|
|
/obj/effect/proc_holder/spell/Initialize()
|
|
. = ..()
|
|
START_PROCESSING(SSfastprocess, src)
|
|
|
|
still_recharging_msg = span_notice("[name] is still recharging.")
|
|
charge_counter = charge_max
|
|
|
|
/obj/effect/proc_holder/spell/Destroy()
|
|
STOP_PROCESSING(SSfastprocess, src)
|
|
qdel(action)
|
|
return ..()
|
|
|
|
/obj/effect/proc_holder/spell/Click()
|
|
if(cast_check())
|
|
choose_targets()
|
|
return 1
|
|
|
|
/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf
|
|
return
|
|
|
|
/obj/effect/proc_holder/spell/proc/can_target(mob/living/target)
|
|
return TRUE
|
|
|
|
/obj/effect/proc_holder/spell/proc/start_recharge()
|
|
if(cooldown_overlay)
|
|
QDEL_NULL(cooldown_overlay)
|
|
cooldown_overlay = start_cooldown(action.button, world.time + charge_max)
|
|
recharging = TRUE
|
|
|
|
/obj/effect/proc_holder/spell/process(delta_time)
|
|
if(recharging && charge_type == "recharge" && (charge_counter < charge_max))
|
|
charge_counter += delta_time * 10
|
|
cooldown_overlay?.tick()
|
|
if(charge_counter >= charge_max)
|
|
action.UpdateButtonIcon()
|
|
charge_counter = charge_max
|
|
recharging = FALSE
|
|
QDEL_NULL(cooldown_overlay)
|
|
|
|
/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = TRUE, mob/user = usr) //if recharge is started is important for the trigger spells
|
|
before_cast(targets)
|
|
invocation(user)
|
|
if(user && user.ckey)
|
|
user.log_message(span_danger("cast the spell [name]."), LOG_ATTACK)
|
|
if(recharge)
|
|
start_recharge()
|
|
if(sound)
|
|
playMagSound()
|
|
cast(targets,user=user)
|
|
after_cast(targets)
|
|
if(action)
|
|
action.UpdateButtonIcon()
|
|
|
|
/obj/effect/proc_holder/spell/proc/before_cast(list/targets)
|
|
if(overlay)
|
|
for(var/atom/target in targets)
|
|
var/location
|
|
if(isliving(target))
|
|
location = target.loc
|
|
else if(isturf(target))
|
|
location = target
|
|
var/obj/effect/overlay/spell = new /obj/effect/overlay(location)
|
|
spell.icon = overlay_icon
|
|
spell.icon_state = overlay_icon_state
|
|
spell.anchored = TRUE
|
|
spell.density = FALSE
|
|
QDEL_IN(spell, overlay_lifespan)
|
|
|
|
/obj/effect/proc_holder/spell/proc/after_cast(list/targets)
|
|
for(var/atom/target in targets)
|
|
var/location
|
|
if(isliving(target))
|
|
location = target.loc
|
|
else if(isturf(target))
|
|
location = target
|
|
if(isliving(target) && message)
|
|
to_chat(target, text("[message]"))
|
|
if(sparks_spread)
|
|
do_sparks(sparks_amt, FALSE, location)
|
|
if(ispath(smoke_spread, /datum/effect_system/fluid_spread/smoke)) // Dear god this code is :agony:
|
|
var/datum/effect_system/fluid_spread/smoke/smoke = new smoke_spread()
|
|
smoke.set_up(smoke_amt, location = location)
|
|
smoke.start()
|
|
|
|
|
|
/obj/effect/proc_holder/spell/proc/cast(list/targets,mob/user = usr)
|
|
return
|
|
|
|
/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge
|
|
switch(charge_type)
|
|
if("recharge")
|
|
charge_counter = charge_max
|
|
if("charges")
|
|
charge_counter++
|
|
if("holdervar")
|
|
adjust_var(user, holder_var_type, -holder_var_amount)
|
|
QDEL_NULL(cooldown_overlay)
|
|
if(action)
|
|
action.UpdateButtonIcon()
|
|
|
|
/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types
|
|
if (!istype(target))
|
|
return
|
|
switch(type)
|
|
if("bruteloss")
|
|
target.adjustBruteLoss(amount)
|
|
if("fireloss")
|
|
target.adjustFireLoss(amount)
|
|
if("toxloss")
|
|
target.adjustToxLoss(amount)
|
|
if("oxyloss")
|
|
target.adjustOxyLoss(amount)
|
|
if("stun")
|
|
target.AdjustStun(amount)
|
|
if("knockdown")
|
|
target.AdjustKnockdown(amount)
|
|
if("paralyze")
|
|
target.AdjustParalyzed(amount)
|
|
if("immobilize")
|
|
target.AdjustImmobilized(amount)
|
|
if("unconscious")
|
|
target.AdjustUnconscious(amount)
|
|
else
|
|
target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existent vars
|
|
|
|
/obj/effect/proc_holder/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, 1 for one selectable target 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/include_user = 0 //if it includes usr in the target list
|
|
var/random_target = 0 // chooses random viable target instead of asking the caster
|
|
var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target
|
|
|
|
|
|
/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends)
|
|
var/inner_radius = -1 //for all your ring spell needs
|
|
|
|
/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr)
|
|
var/list/targets = list()
|
|
|
|
switch(max_targets)
|
|
if(0) //unlimited
|
|
for(var/mob/living/target in view_or_range(range, user, selection_type))
|
|
if(!can_target(target))
|
|
continue
|
|
targets += target
|
|
if(1) //single target can be picked
|
|
if(range < 0)
|
|
targets += user
|
|
else
|
|
var/possible_targets = list()
|
|
|
|
for(var/mob/living/M in view_or_range(range, user, selection_type))
|
|
if(!include_user && user == M)
|
|
continue
|
|
if(!can_target(M))
|
|
continue
|
|
possible_targets += M
|
|
|
|
//targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets
|
|
//Adds a safety check post-input to make sure those targets are actually in range.
|
|
var/mob/M
|
|
if(!random_target)
|
|
M = input("Choose the target for the spell.", "Targeting") as null|mob in possible_targets
|
|
else
|
|
switch(random_target_priority)
|
|
if(TARGET_RANDOM)
|
|
M = pick(possible_targets)
|
|
if(TARGET_CLOSEST)
|
|
for(var/mob/living/L in possible_targets)
|
|
if(M)
|
|
if(get_dist(user,L) < get_dist(user,M))
|
|
if(los_check(user,L))
|
|
M = L
|
|
else
|
|
if(los_check(user,L))
|
|
M = L
|
|
if(M in view_or_range(range, user, selection_type))
|
|
targets += M
|
|
|
|
else
|
|
var/list/possible_targets = list()
|
|
for(var/mob/living/target in view_or_range(range, user, selection_type))
|
|
if(!can_target(target))
|
|
continue
|
|
possible_targets += target
|
|
for(var/i=1,i<=max_targets,i++)
|
|
if(!possible_targets.len)
|
|
break
|
|
if(target_ignore_prev)
|
|
var/target = pick(possible_targets)
|
|
possible_targets -= target
|
|
targets += target
|
|
else
|
|
targets += pick(possible_targets)
|
|
|
|
if(!include_user && (user in targets))
|
|
targets -= user
|
|
|
|
if(!targets.len) //doesn't waste the spell
|
|
revert_cast(user)
|
|
return
|
|
|
|
perform(targets,user=user)
|
|
|
|
/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr)
|
|
var/list/targets = list()
|
|
|
|
for(var/turf/target in view_or_range(range,user,selection_type))
|
|
if(!can_target(target))
|
|
continue
|
|
if(!(target in view_or_range(inner_radius,user,selection_type)))
|
|
targets += target
|
|
|
|
if(!targets.len) //doesn't waste the spell
|
|
revert_cast()
|
|
return
|
|
|
|
perform(targets,user=user)
|
|
|
|
/obj/effect/proc_holder/spell/proc/updateButtonIcon(status_only, force)
|
|
action.UpdateButtonIcon(status_only, force)
|
|
|
|
/obj/effect/proc_holder/spell/proc/can_be_cast_by(mob/caster)
|
|
if((human_req || clothes_req) && !ishuman(caster))
|
|
return 0
|
|
return 1
|
|
|
|
/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B)
|
|
//Checks for obstacles from A to B
|
|
var/obj/dummy = new(A.loc)
|
|
dummy.pass_flags |= PASSTABLE
|
|
for(var/turf/turf in getline(A,B))
|
|
for(var/atom/movable/AM in turf)
|
|
if(!AM.CanPass(dummy,turf,1))
|
|
qdel(dummy)
|
|
return 0
|
|
qdel(dummy)
|
|
return 1
|
|
|
|
/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr)
|
|
if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list))
|
|
return FALSE
|
|
|
|
if(!charge_check(user,TRUE))
|
|
return FALSE
|
|
|
|
if(user.stat && !stat_allowed)
|
|
return FALSE
|
|
|
|
if(!antimagic_allowed && user.anti_magic_check(TRUE, FALSE, FALSE, 0, TRUE))
|
|
return FALSE
|
|
|
|
if(!ishuman(user))
|
|
if(clothes_req || human_req)
|
|
return FALSE
|
|
if(nonabstract_req && (isbrain(user) || ispAI(user)))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/effect/proc_holder/spell/self //Targets only the caster. Good for buffs and heals, but probably not wise for fireballs (although they usually fireball themselves anyway, honke)
|
|
range = -1 //Duh
|
|
|
|
/obj/effect/proc_holder/spell/self/choose_targets(mob/user = usr)
|
|
if(!user)
|
|
revert_cast()
|
|
return
|
|
perform(null,user=user)
|
|
|
|
/obj/effect/proc_holder/spell/self/basic_heal //This spell exists mainly for debugging purposes, and also to show how casting works
|
|
name = "Lesser Heal"
|
|
desc = "Heals a small amount of brute and burn damage."
|
|
human_req = TRUE
|
|
clothes_req = FALSE
|
|
charge_max = 100
|
|
cooldown_min = 50
|
|
invocation = "Victus sano!"
|
|
invocation_type = "whisper"
|
|
school = "restoration"
|
|
sound = 'sound/magic/staff_healing.ogg'
|
|
|
|
/obj/effect/proc_holder/spell/self/basic_heal/cast(mob/living/carbon/human/user) //Note the lack of "list/targets" here. Instead, use a "user" var depending on mob requirements.
|
|
//Also, notice the lack of a "for()" statement that looks through the targets. This is, again, because the spell can only have a single target.
|
|
user.visible_message(span_warning("A wreath of gentle light passes over [user]!"), span_notice("You wreath yourself in healing light!"))
|
|
user.adjustBruteLoss(-10)
|
|
user.adjustFireLoss(-10)
|