Makes most targeted spells select a target on click with a radius (#13220)

* Basis

* Make most targeted spells use a click with radius instead of a list

* Fixes

* Selection code

* return ..()

* Auto targeting

* Forgot a comment change

* Merge issue fix
This commit is contained in:
farie82
2020-09-18 16:30:32 +02:00
committed by GitHub
parent 84c6b4ab2f
commit 8e8ad65906
22 changed files with 631 additions and 632 deletions

View File

@@ -1102,8 +1102,8 @@
special_role = null
to_chat(current,"<span class='userdanger'>Your infernal link has been severed! You are no longer a devil!</span>")
RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt)
RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish)
RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract)
RemoveSpell(/obj/effect/proc_holder/spell/targeted/click/fireball/hellish)
RemoveSpell(/obj/effect/proc_holder/spell/targeted/click/summon_contract)
RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork)
RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater)
RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/ascended)

View File

@@ -24,6 +24,20 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
user.face_atom(A)
return FALSE
/datum/click_intercept/proc_holder
var/obj/effect/proc_holder/spell
/datum/click_intercept/proc_holder/New(client/C, obj/effect/proc_holder/spell_to_cast)
. = ..()
spell = spell_to_cast
/datum/click_intercept/proc_holder/InterceptClickOn(user, params, atom/object)
spell.InterceptClickOn(user, params, object)
/datum/click_intercept/proc_holder/quit()
spell.remove_ranged_ability(spell.ranged_ability_user)
return ..()
/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg)
if(!user || !user.client)
return
@@ -32,7 +46,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
user.ranged_ability.remove_ranged_ability(user)
user.ranged_ability = src
ranged_ability_user = user
user.client.click_intercept = user.ranged_ability
user.client.click_intercept = new /datum/click_intercept/proc_holder(user.client, user.ranged_ability)
add_mousepointer(user.client)
active = TRUE
if(msg)
@@ -48,15 +62,17 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
C.mouse_pointer_icon = initial(C.mouse_pointer_icon)
/obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg)
if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability
if(!user || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability
return
user.ranged_ability = null
ranged_ability_user = null
user.client.click_intercept = null
remove_mousepointer(user.client)
active = FALSE
if(msg)
to_chat(user, msg)
if(user.client)
qdel(user.client.click_intercept)
user.client.click_intercept = null
remove_mousepointer(user.client)
if(msg)
to_chat(user, msg)
update_icon()
/obj/effect/proc_holder/spell
@@ -114,10 +130,14 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
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, "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>")
return 0
/* Checks if the user can cast the spell
* @param charge_check If the proc should do the cooldown check
* @param start_recharge If the proc should set the cooldown
* @param user The caster of the spell
*/
/obj/effect/proc_holder/spell/proc/cast_check(charge_check = TRUE, start_recharge = TRUE, 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(!can_cast(user, charge_check, TRUE))
return FALSE
if(ishuman(user))
var/mob/living/carbon/human/caster = user
@@ -126,49 +146,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
caster.reset_perspective(0)
return 0
if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
return 0
if(!skipcharge)
switch(charge_type)
if("recharge")
if(charge_counter < charge_max)
to_chat(user, still_recharging_msg)
return 0
if("charges")
if(!charge_counter)
to_chat(user, "<span class='notice'>[name] has no charges left.</span>")
return 0
if(!ghost)
if(user.stat && !stat_allowed)
to_chat(user, "<span class='notice'>You can't cast this spell while incapacitated.</span>")
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/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list()))
if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check
var/mob/living/carbon/human/H = user
var/obj/item/clothing/robe = H.wear_suit
var/obj/item/clothing/hat = H.head
var/obj/item/clothing/shoes = H.shoes
if(!robe || !hat || !shoes)
to_chat(user, "<span class='notice'>Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.</span>")
return 0
if(!robe.magical || !hat.magical || !shoes.magical)
to_chat(user, "<span class='notice'>Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.</span>")
return 0
else if(!ishuman(user))
if(clothes_req || human_req)
to_chat(user, "<span class='notice'>This spell can only be cast by humans!</span>")
return 0
if(nonabstract_req && (isbrain(user) || ispAI(user)))
to_chat(user, "<span class='notice'>This spell can only be cast by physical beings!</span>")
return 0
if(!skipcharge)
if(start_recharge)
switch(charge_type)
if("recharge")
charge_counter = 0 //doesn't start recharging until the targets selecting ends
@@ -442,6 +420,100 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
return
/obj/effect/proc_holder/spell/targeted/click
var/click_radius = 1 // How big the radius around the clicked atom is to find a suitable target. -1 is only the selected atom is considered
var/selection_activated_message = "<span class='notice'>Click on a target to cast the spell.</span>"
var/selection_deactivated_message = "<span class='notice'>You choose to not cast this spell.</span>"
var/allowed_type = /mob/living // Which type the targets have to be
var/auto_target_single = TRUE // If the spell should auto select a target if only one is found
/obj/effect/proc_holder/spell/targeted/click/Click()
var/mob/living/user = usr
if(!istype(user))
return
if(active)
remove_ranged_ability(user, selection_deactivated_message)
else
if(cast_check(TRUE, FALSE, user))
if(auto_target_single && attempt_auto_target(user))
return
add_ranged_ability(user, selection_activated_message)
else
to_chat(user, "<span class='warning'>[src] is not ready to be used yet.</span>")
/obj/effect/proc_holder/spell/targeted/click/proc/attempt_auto_target(mob/user)
var/atom/target
for(var/atom/A in view_or_range(range, user, selection_type))
if(valid_target(A, user))
if(target)
return FALSE // Two targets found. ABORT
target = A
if(target && cast_check(TRUE, TRUE, user)) // Singular target found. Cast it instantly
to_chat(user, "<span class='warning'>Only one target found. Casting [src] on [target]!</span>")
perform(list(target), user = user)
return TRUE
return FALSE
/obj/effect/proc_holder/spell/targeted/click/InterceptClickOn(mob/living/user, params, atom/A)
if(..() || !cast_check(TRUE, TRUE, user))
remove_ranged_ability(user)
revert_cast(user)
return TRUE
var/list/targets = list()
if(valid_target(A, user))
targets.Add(A)
if((!max_targets || max_targets > targets.len) && click_radius >= 0)
var/list/found_others = list()
for(var/atom/target in range(click_radius, A))
if(valid_target(target, user))
found_others |= target
if(!max_targets)
targets.Add(found_others)
else
if(max_targets <= found_others.len + targets.len)
targets.Add(found_others)
else
switch(random_target_priority) //Add in the rest
if(TARGET_RANDOM)
while(targets.len < max_targets && found_others.len) // Add the others
targets.Add(pick_n_take(found_others))
if(TARGET_CLOSEST)
var/list/distances = list()
for(var/target in found_others)
distances[target] = get_dist(user, target)
sortTim(distances, /proc/cmp_numeric_asc, TRUE) // Sort on distance
for(var/target in distances)
targets.Add(target)
if(targets.len >= max_targets)
break
if(!targets.len)
to_chat(user, "<span class='warning'>No suitable target found.</span>")
revert_cast(user)
return FALSE
perform(targets, user = user)
remove_ranged_ability(user)
return TRUE
/* Checks if a target is valid
* Should not include to_chats or other types of messages since this is used often on tons of targets.
* @param target The target to check
* @param user The user of the spell
*/
/obj/effect/proc_holder/spell/targeted/click/proc/valid_target(target, user)
return istype(target, allowed_type) && (include_user || target != user) && \
(target in view_or_range(range, user, selection_type))
/obj/effect/proc_holder/spell/targeted/click/choose_targets(mob/living/user, atom/A) // Not used
return
/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr)
var/list/targets = list()
@@ -475,30 +547,39 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
qdel(dummy)
return 1
/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr)
/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE)
if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list))
if(show_message)
to_chat(user, "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>")
return 0
if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
return 0
switch(charge_type)
if("recharge")
if(charge_counter < charge_max)
return 0
if("charges")
if(!charge_counter)
return 0
if(user.stat && !stat_allowed)
return 0
if(charge_check)
switch(charge_type)
if("recharge")
if(charge_counter < charge_max)
if(show_message)
to_chat(user, still_recharging_msg)
return 0
if("charges")
if(!charge_counter)
if(show_message)
to_chat(user, "<span class='notice'>[name] has no charges left.</span>")
return 0
if(!ghost)
if(user.stat && !stat_allowed)
if(show_message)
to_chat(user, "<span class='notice'>You can't cast this spell while incapacitated.</span>")
return 0
if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled())
if(show_message)
to_chat(user, "Mmmf mrrfff!")
return 0
if(ishuman(user))
var/mob/living/carbon/human/H = user
if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled())
return 0
var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list
var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list)
if(clothes_req && !clothcheck && !clothcheck2) //clothes check
@@ -506,12 +587,20 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
var/obj/item/clothing/hat = H.head
var/obj/item/clothing/shoes = H.shoes
if(!robe || !hat || !shoes)
if(show_message)
to_chat(user, "<span class='notice'>Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.</span>")
return 0
if(!robe.magical || !hat.magical || !shoes.magical)
if(show_message)
to_chat(user, "<span class='notice'>Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.</span>")
return 0
else
if(clothes_req || human_req)
if(show_message)
to_chat(user, "<span class='notice'>This spell can only be cast by humans!</span>")
return 0
if(nonabstract_req && (isbrain(user) || ispAI(user)))
if(show_message)
to_chat(user, "<span class='notice'>This spell can only be cast by physical beings!</span>")
return 0
return 1

View File

@@ -11,7 +11,7 @@
/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr)
var/thearea = before_cast(targets)
if(!thearea || !cast_check(1))
if(!thearea || !cast_check(TRUE, FALSE, user))
revert_cast()
return
invocation(thearea)

View File

@@ -1,25 +1,31 @@
/obj/effect/proc_holder/spell/targeted/chaplain_bless
/obj/effect/proc_holder/spell/targeted/click/chaplain_bless
name = "Bless"
desc = "Blesses a single person."
school = "transmutation"
charge_max = 60
clothes_req = 0
clothes_req = FALSE
invocation = "none"
invocation_type = "none"
max_targets = 1
include_user = 0
humans_only = 1
include_user = FALSE
allowed_type = /mob/living/carbon/human
selection_activated_message = "<span class='notice'>You prepare a blessing. Click on a target to start blessing.</span>"
selection_deactivated_message = "<span class='notice'>The crew will be blessed another time.</span>"
range = 1
click_radius = -1 // Only precision clicking
cooldown_min = 20
action_icon_state = "shield"
/obj/effect/proc_holder/spell/targeted/click/chaplain_bless/valid_target(mob/living/carbon/human/target, user)
if(!..())
return FALSE
/obj/effect/proc_holder/spell/targeted/chaplain_bless/cast(list/targets, mob/living/user = usr, distanceoverride)
return target.mind && target.ckey && !target.stat
/obj/effect/proc_holder/spell/targeted/click/chaplain_bless/cast(list/targets, mob/living/user = usr)
if(!istype(user))
to_chat(user, "Somehow, you are not a living mob. This should never happen. Report this bug.")
revert_cast()
@@ -35,32 +41,7 @@
revert_cast()
return
var/mob/living/carbon/human/target = targets[range]
if(!istype(target))
to_chat(user, "No target.")
revert_cast()
return
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
to_chat(user, "[target] is too far away!")
revert_cast()
return
if(!target.mind)
to_chat(user, "[target] appears to be catatonic. Your blessing would have no effect.")
revert_cast()
return
if(!target.ckey)
to_chat(user, "[target] appears to be too out of it to benefit from this.")
revert_cast()
return
if(target.stat == DEAD)
to_chat(user, "[target] is already dead. There is no point.")
revert_cast()
return
var/mob/living/carbon/human/target = targets[1]
spawn(0) // allows cast to complete even if recipient ignores the prompt
if(alert(target, "[user] wants to bless you, in the name of [user.p_their()] religion. Accept?", "Accept Blessing?", "Yes", "No") == "Yes") // prevents forced conversions

View File

@@ -21,13 +21,18 @@
action_background_icon_state = "bg_demon"
/obj/effect/proc_holder/spell/targeted/summon_contract
/obj/effect/proc_holder/spell/targeted/click/summon_contract
name = "Summon infernal contract"
desc = "Skip making a contract by hand, just do it by magic."
invocation_type = "whisper"
invocation = "Just sign on the dotted line."
include_user = 0
selection_activated_message = "<span class='notice'>You prepare a detailed contract. Click on a target to summon the contract in his hands.</span>"
selection_deactivated_message = "<span class='notice'>You archive the contract for later use.</span>"
include_user = FALSE
range = 5
auto_target_single = FALSE // Prevent an accidental contract from summoning
click_radius = -1 // Precision clicking required
allowed_type = /mob/living/carbon
clothes_req = FALSE
school = "conjuration"
charge_max = 150
@@ -35,8 +40,9 @@
action_icon_state = "spell_default"
action_background_icon_state = "bg_demon"
/obj/effect/proc_holder/spell/targeted/summon_contract/cast(list/targets, mob/user = usr)
for(var/mob/living/carbon/C in targets)
/obj/effect/proc_holder/spell/targeted/click/summon_contract/cast(list/targets, mob/user = usr)
for(var/target in targets)
var/mob/living/carbon/C = target
if(C.mind && user.mind)
if(C.stat == DEAD)
if(user.drop_item())
@@ -63,7 +69,7 @@
to_chat(user,"<span class='notice'>[C] seems to not be sentient. You are unable to summon a contract for them.</span>")
/obj/effect/proc_holder/spell/fireball/hellish
/obj/effect/proc_holder/spell/targeted/click/fireball/hellish
name = "Hellfire"
desc = "This spell launches hellfire at the target."
school = "evocation"
@@ -74,7 +80,7 @@
fireball_type = /obj/item/projectile/magic/fireball/infernal
action_background_icon_state = "bg_demon"
/obj/effect/proc_holder/spell/fireball/hellish/cast(list/targets, mob/living/user = usr)
/obj/effect/proc_holder/spell/targeted/click/fireball/hellish/cast(list/targets, mob/living/user = usr)
add_attack_logs(user, targets, "has fired a Hellfire ball", ATKLOG_FEW)
.=..()

View File

@@ -1,39 +1,31 @@
/obj/effect/proc_holder/spell/targeted/horsemask
/obj/effect/proc_holder/spell/targeted/click/horsemask
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."
school = "transmutation"
charge_type = "recharge"
charge_max = 150
charge_counter = 0
clothes_req = 0
stat_allowed = 0
clothes_req = FALSE
stat_allowed = FALSE
invocation = "KN'A FTAGHU, PUCK 'BTHNK!"
invocation_type = "shout"
range = 7
cooldown_min = 30 //30 deciseconds reduction per rank
selection_type = "range"
selection_activated_message = "<span class='notice'>You start to quietly neigh an incantation. Click on or near a target to cast the spell.</span>"
selection_deactivated_message = "<span class='notice'>You stop neighing to yourself.</span>"
allowed_type = /mob/living/carbon/human
action_icon_state = "barn"
sound = 'sound/magic/HorseHead_curse.ogg'
/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr)
/obj/effect/proc_holder/spell/targeted/click/horsemask/cast(list/targets, mob/user = usr)
if(!targets.len)
to_chat(user, "<span class='notice'>No target found in range.</span>")
return
var/mob/living/carbon/target = targets[1]
if(!target)
return
if(!ishuman(target))
to_chat(user, "<span class='notice'>It'd be stupid to curse [target] with a horse's head!</span>")
return
if(!(target in oview(range)))//If they are not in overview after selection.
to_chat(user, "<span class='notice'>They are too far away!</span>")
return
var/mob/living/carbon/human/target = targets[1]
var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead
magichead.flags |= NODROP | DROPDEL //curses!

View File

@@ -25,10 +25,10 @@
/obj/effect/proc_holder/spell/targeted/lightning/Click()
if(!ready && start_time == 0)
if(cast_check())
if(cast_check(TRUE, FALSE, usr))
StartChargeup()
else
if(ready && cast_check(skipcharge=1))
if(ready && cast_check(TRUE, TRUE, usr))
choose_targets()
return 1

View File

@@ -20,10 +20,10 @@
/obj/effect/proc_holder/spell/targeted/magnet/Click()
if(!ready && start_time == 0)
if(cast_check())
if(cast_check(TRUE, FALSE, usr))
StartChargeup()
else
if(ready && cast_check(skipcharge=1))
if(ready && cast_check(TRUE, TRUE, usr))
choose_targets()
return 1

View File

@@ -1,4 +1,4 @@
/obj/effect/proc_holder/spell/targeted/mind_transfer
/obj/effect/proc_holder/spell/targeted/click/mind_transfer
name = "Mind Transfer"
desc = "This spell allows the user to switch bodies with a target."
@@ -8,33 +8,29 @@
invocation = "GIN'YU CAPAN"
invocation_type = "whisper"
range = 1
click_radius = 0 // Still gotta be pretty accurate
selection_activated_message = "<span class='notice'>You prepare to transfer your mind. Click on a target to cast the spell.</span>"
selection_deactivated_message = "<span class='notice'>You decide that your current form is good enough.</span>"
cooldown_min = 200 //100 deciseconds reduction per rank
var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the 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
action_icon_state = "mindswap"
/obj/effect/proc_holder/spell/targeted/click/mind_transfer/valid_target(mob/living/target, user)
if(!..())
return FALSE
return target.stat != DEAD && target.key && target.mind
/*
Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do.
Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering.
Also, you never added distance checking after target is selected. I've went ahead and did that.
*/
/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride)
/obj/effect/proc_holder/spell/targeted/click/mind_transfer/cast(list/targets, mob/user = usr)
var/mob/living/target = targets[range]
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
to_chat(user, "They are too far away!")
return
if(target.stat == DEAD)
to_chat(user, "You don't particularly want to be dead.")
return
if(!target.key || !target.mind)
to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.")
return
if(user.suiciding)
to_chat(user, "<span class='warning'>You're killing yourself! You can't concentrate enough to do this!</span>")
return

View File

@@ -308,76 +308,50 @@
duration = 300
sound = 'sound/magic/blind.ogg'
/obj/effect/proc_holder/spell/fireball
/obj/effect/proc_holder/spell/targeted/click/fireball
name = "Fireball"
desc = "This spell fires a fireball at a target and does not require wizard garb."
school = "evocation"
charge_max = 60
clothes_req = 0
clothes_req = FALSE
invocation = "ONI SOMA"
invocation_type = "shout"
auto_target_single = FALSE // Having this true won't ever find a single target and is just lost processing power
range = 20
cooldown_min = 20 //10 deciseconds reduction per rank
click_radius = -1
selection_activated_message = "<span class='notice'>Your prepare to cast your fireball spell! <B>Left-click to cast at a target!</B></span>"
selection_deactivated_message = "<span class='notice'>You extinguish your fireball...for now.</span>"
allowed_type = /atom // FIRE AT EVERYTHING
var/fireball_type = /obj/item/projectile/magic/fireball
action_icon_state = "fireball0"
sound = 'sound/magic/fireball.ogg'
active = FALSE
/obj/effect/proc_holder/spell/fireball/Click()
var/mob/living/user = usr
if(!istype(user))
return
var/msg
if(!can_cast(user))
msg = "<span class='warning'>You can no longer cast Fireball.</span>"
remove_ranged_ability(user, msg)
return
if(active)
msg = "<span class='notice'>You extinguish your fireball...for now.</span>"
remove_ranged_ability(user, msg)
else
msg = "<span class='notice'>Your prepare to cast your fireball spell! <B>Left-click to cast at a target!</B></span>"
add_ranged_ability(user, msg)
/obj/effect/proc_holder/spell/fireball/update_icon()
/obj/effect/proc_holder/spell/targeted/click/fireball/update_icon()
if(!action)
return
action.button_icon_state = "fireball[active]"
action.UpdateButtonIcon()
/obj/effect/proc_holder/spell/fireball/InterceptClickOn(mob/living/user, params, atom/target)
if(..())
return FALSE
if(!cast_check(0, user))
remove_ranged_ability(user)
return FALSE
var/list/targets = list(target)
perform(targets, user = user)
return TRUE
/obj/effect/proc_holder/spell/fireball/cast(list/targets, mob/living/user = usr)
/obj/effect/proc_holder/spell/targeted/click/fireball/cast(list/targets, mob/living/user = usr)
var/target = targets[1] //There is only ever one target for fireball
var/turf/T = user.loc
var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction
if(!isturf(U) || !isturf(T))
return 0
return FALSE
var/obj/item/projectile/magic/fireball/FB = new fireball_type(user.loc)
FB.current = get_turf(user)
FB.preparePixelProjectile(target, get_turf(target), user)
FB.fire()
user.newtonian_move(get_dir(U, T))
remove_ranged_ability(user)
return 1
return TRUE
/obj/effect/proc_holder/spell/aoe_turf/repulse
name = "Repulse"