Adds 5 new Heretic spells. Rebalances some aspects of Heretics. Refactors some spells as well, and makes it so emote spells require free hands. (#71044)

## About The Pull Request

- Adds 5 new heretic spells!
- For Flesh: Flesh Surgery. This spell is a touch spell that can either
be used to heal your minions or extract organs from mobs.
- For Void: Cone of Cold. This is a simple spell - it shoots out a cone,
of cold, that freezes and damages people caught in it.
- For Ash: Volcano Blast. This spell functions like Tesla Blast, but
instead of electricity, it shoots out of a beam of fire that hurts to
walk over.
- For Blade: Realignment. Think of this like "Fleshmend but for stuns /
stamcrit". It rapidly regenerates stamina damage and reduces stuns,
while making you a pacifist. It can also be cast in rapid succession,
but this will increase the cooldown.
- For Rust: Rust Construction. Point at a rusted tile, and a wall will
be raised where it was instantly. This even damages people and throws
them aside - Or, if on a multi-z map, can lift up.

- Number of influences has increased.
  - 5 at 1 heretic
  - 9 at 2 
  - 12 at 3
  - 14 at 4
  - 15 at 5
  - 16 at 6, and so on

- Heretics are given a 5th sacrifice target, selected randomly. On
average an additional sacrifice is needed for their objectives.

- Being sacrificed grants you a permanent phobia of the supernatural.
Phobia of the supernatural has been expanded to cover heretic items and
mobs.

- The equation for offhand damage of blade heretics was tweaked. Actual
result unchanged, it's just more resilient to future changes now.

- Touch spells were refactored a bit, and overall expanded to be easier
to use

- Charged spells were added, and charged beam spells. Tesla blast uses
this.

- Cone spells were refactored to be easier to setup.

- Jaunting will now hide your runechat when it triggers, to make it less
easy to follow.

- Heretic Ghouls now take less stamina damage based on how low their
health pool is.

- Emote based spells now require hands to be unblocked to be cast, like
mime spells.
- Yes this gets rid of handcuffed invisible walls... Not 100% on this,
but I figured it's good for consistency? Open to discussion

## Why It's Good For The Game

A lotta feedback has passed through about heretic and it's time to
address some of it

- Problem: Not enough cool flash spells. Makes Focus not worth it. 
   - Solution: Adds some more spells to encourage focus use. 

- Problem: Sacrifice targets being too willing or not harmed enough
   - Solution: A permanent trauma. 

- Problem: Not enough ways to power up.
- Solution: Adding more influences around, though I think there should
be more variety in knowledge rituals as well.

- Problem: Ash Passage sucks
   - Solution: Makes it a smidge better to stay hidden with it.

- Problem: Heretic Ghouls get one hit by batons
- Solution: Stamina modifier should put them on par with unmodified
humans.

## Changelog

🆑 Melbert
add: Added five new heretic spells, one for each path. They come after
the Ritual of Knowledge.
add: Cone of Cold, for Void heretics. Shoots out a freezing chill in a
cone which deal damage and freezes.
add: Flesh Surgery for Flesh heretics. A touch spell which can either
heal minions or be used on mobs to extract organs without surgery.
add: Volcano Blast for Ash heretics. A beam spell, like Tesla Blast,
which fires out a beam of fire that bounces between people.
add: Realignment for Blade heretics. Fleshmend, but for stuns and
stamina damage. Makes you a pacifist, but rapidly regenerates stamina.
add: Rust Construction for Rust heretics. Places a wall of rust on the
target rusted flooring. Can even be used to ascend z-levels!
balance: Nerfed the cooldown of Cleave slightly, buffed the cooldown of
Lesser Cleave slightly.
balance: Slightly more influences will spawn on the station per heretic.
balance: Heretics require an additional sacrifice on average for
ascension, but are given a fifth sacrifice target (randomly selected).
balance: Being sacrificed by a heretic now gives you a permanent phobia
of spooky things, including heretic mobs and items.
balance: Heretic ghouls now take reduced stamina damage, depending on
how small their health pool is.
balance: Using Jaunts will conceal your runechat for their duration. 
balance: Spells which require emoting (Mime spells) require your hands
not be blocked to use.
refactor: Touch Spells were improved a bit. Added some new template
spells - Charged spells, and Charged beam spells.
fix: Fixes a runtime from losing heretic.
/🆑
This commit is contained in:
MrMelbert
2022-11-21 21:30:46 -08:00
committed by GitHub
parent b238e5d363
commit 7dde8a5e66
63 changed files with 1539 additions and 443 deletions
+11 -4
View File
@@ -181,10 +181,12 @@
if(spell_requirements & SPELL_REQUIRES_WIZARD_GARB)
var/mob/living/carbon/human/human_owner = owner
if(!(human_owner.wear_suit?.clothing_flags & CASTING_CLOTHES))
to_chat(owner, span_warning("You don't feel strong enough without your robe!"))
if(feedback)
to_chat(owner, span_warning("You don't feel strong enough without your robe!"))
return FALSE
if(!(human_owner.head?.clothing_flags & CASTING_CLOTHES))
to_chat(owner, span_warning("You don't feel strong enough without your hat!"))
if(feedback)
to_chat(owner, span_warning("You don't feel strong enough without your hat!"))
return FALSE
else
@@ -208,6 +210,7 @@
/**
* Check if the target we're casting on is a valid target.
* For self-casted spells, the target being checked (cast_on) is the caster.
* For click_to_activate spells, the target being checked is the clicked atom.
*
* Return TRUE if cast_on is valid, FALSE otherwise
*/
@@ -384,7 +387,7 @@
return FALSE
spell_level++
cooldown_time = max(cooldown_time - cooldown_reduction_per_rank, 0)
cooldown_time = max(cooldown_time - cooldown_reduction_per_rank, 0.25 SECONDS) // 0 second CD starts to break things.
update_spell_name()
return TRUE
@@ -400,7 +403,11 @@
return FALSE
spell_level--
cooldown_time = min(cooldown_time + cooldown_reduction_per_rank, initial(cooldown_time))
if(cooldown_reduction_per_rank > 0 SECONDS)
cooldown_time = min(cooldown_time + cooldown_reduction_per_rank, initial(cooldown_time))
else
cooldown_time = max(cooldown_time + cooldown_reduction_per_rank, initial(cooldown_time))
update_spell_name()
return TRUE
@@ -0,0 +1,158 @@
/**
* ## Channelled spells
*
* These spells do something after a channel time.
* To use this template, all that's needed is for cast() to be implemented.
*/
/datum/action/cooldown/spell/charged
/// What message do we display when we start chanelling?
var/channel_message
/// Whether we're currently channelling / charging the spell
var/currently_channeling = FALSE
/// How long it takes to channel the spell.
var/channel_time = 10 SECONDS
// Overlay optional, applied when we start channelling
/// What icon should we use for our overlay
var/charge_overlay_icon
/// What icon state should we use for our overlay
var/charge_overlay_state
/// The actual appearance / our overlay. Don't mess with this
var/mutable_appearance/charge_overlay_instance
// Sound optional, played when we start chanelling
/// What soundpath should we play when we start chanelling
var/charge_sound
/// The actual sound we generate, don't mess with this
var/sound/charge_sound_instance
/datum/action/cooldown/spell/charged/New(Target, original)
. = ..()
if(!channel_message)
channel_message = span_notice("You start chanelling [src]...")
if(charge_sound)
charge_sound_instance = sound(charge_sound, channel = CHANNEL_CHARGED_SPELL)
if(charge_overlay_icon && charge_overlay_state)
charge_overlay_instance = mutable_appearance(charge_overlay_icon, charge_overlay_state, EFFECTS_LAYER)
/datum/action/cooldown/spell/charged/Destroy()
if(owner)
stop_channel_effect(owner)
charge_overlay_instance = null
charge_sound_instance = null
return ..()
/datum/action/cooldown/spell/charged/Remove(mob/living/remove_from)
stop_channel_effect(remove_from)
return ..()
/datum/action/cooldown/spell/charged/can_cast_spell(feedback = TRUE)
. = ..()
if(!.)
return FALSE
if(currently_channeling)
if(feedback)
to_chat(owner, span_warning("You're already channeling [src]!"))
return FALSE
return TRUE
/datum/action/cooldown/spell/charged/before_cast(atom/cast_on)
. = ..()
if(. & SPELL_CANCEL_CAST)
return
to_chat(cast_on, channel_message)
if(charge_sound_instance)
playsound(cast_on, charge_sound_instance, 50, FALSE)
if(charge_overlay_instance)
cast_on.add_overlay(charge_overlay_instance)
currently_channeling = TRUE
UpdateButtons(status_only = TRUE)
if(!do_after(cast_on, channel_time, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_HELD_ITEM)))
stop_channel_effect(cast_on)
return . | SPELL_CANCEL_CAST
/datum/action/cooldown/spell/charged/cast(atom/cast_on)
. = ..()
stop_channel_effect(cast_on)
/datum/action/cooldown/spell/charged/set_statpanel_format()
. = ..()
if(!islist(.))
return
if(currently_channeling)
.[PANEL_DISPLAY_STATUS] = "CHANNELING"
/// Interrupts the chanelling effect, removing any overlay or sound playing (for the passed mob)
/datum/action/cooldown/spell/charged/proc/stop_channel_effect(mob/for_who)
if(charge_overlay_instance)
for_who.cut_overlay(charge_overlay_instance)
if(charge_sound_instance)
for_who.stop_sound_channel(CHANNEL_CHARGED_SPELL)
// Play a null sound in to cancel the sound playing, because byond
playsound(for_who, sound(null, repeat = 0, channel = CHANNEL_CHARGED_SPELL), 50, FALSE)
currently_channeling = FALSE
UpdateButtons(status_only = TRUE)
/**
* ### Channelled "Beam" spells
*
* Channelled spells that pick a random target from nearby atoms to cast a spell on.
* Commonly used for beams, hence the name, but nothing's stopping projectiles or whatever from working.
*
* If no targets are nearby, cancels the spell and refunds the cooldown.
*/
/datum/action/cooldown/spell/charged/beam
/// The radius around the caster to find a target.
var/target_radius = 5
/// The maximum number of bounces the beam will go before stopping.
var/max_beam_bounces = 1
/// Who's our initial beam target? Set in before cast, used in cast.
var/atom/initial_target
/datum/action/cooldown/spell/charged/beam/Destroy()
initial_target = null // This like shouuld never hang references but I've seen some cursed things so let's be safe
return ..()
/datum/action/cooldown/spell/charged/beam/before_cast(atom/cast_on)
. = ..()
if(. & SPELL_CANCEL_CAST)
return
initial_target = get_target(cast_on)
if(isnull(initial_target))
cast_on.balloon_alert(cast_on, "no targets nearby!")
stop_channel_effect(cast_on)
return . | SPELL_CANCEL_CAST
/datum/action/cooldown/spell/charged/beam/cast(atom/cast_on)
. = ..()
send_beam(cast_on, initial_target, max_beam_bounces)
initial_target = null
/datum/action/cooldown/spell/charged/beam/proc/send_beam(atom/origin, atom/to_beam, bounces)
SHOULD_CALL_PARENT(FALSE)
CRASH("[type] did not implement send_beam and either has no effects or implemented the spell incorrectly.")
/datum/action/cooldown/spell/charged/beam/proc/get_target(atom/center)
var/list/things = list()
for(var/atom/nearby_thing in range(target_radius, center))
if(nearby_thing == owner || nearby_thing == center)
continue
things += nearby_thing
if(!length(things))
return null
return pick(things)
@@ -0,0 +1,62 @@
/datum/action/cooldown/spell/charged/beam/tesla
name = "Tesla Blast"
desc = "Charge up a tesla arc and release it at random nearby targets! \
You can move freely while it charges. The arc jumps between targets and can knock them down."
button_icon_state = "lightning"
sound = 'sound/magic/lightningbolt.ogg'
cooldown_time = 30 SECONDS
cooldown_reduction_per_rank = 6.75 SECONDS
invocation = "UN'LTD P'WAH!"
invocation_type = INVOCATION_SHOUT
school = SCHOOL_EVOCATION
channel_message = span_notice("You start gathering power...")
charge_overlay_icon = 'icons/effects/effects.dmi'
charge_overlay_state = "electricity"
charge_sound = 'sound/magic/lightning_chargeup.ogg'
target_radius = 7
max_beam_bounces = 5
/// How much energy / damage does the initial bounce
var/initial_energy = 30
/// How much energy / damage is lost per bounce
var/energy_lost_per_bounce = 5
/// Zaps a target, the bolt originating from origin.
/datum/action/cooldown/spell/charged/beam/tesla/send_beam(atom/origin, mob/living/carbon/to_beam, bolt_energy = 30, bounces = 5)
origin.Beam(to_beam, icon_state = "lightning[rand(1,12)]", time = 0.5 SECONDS)
playsound(get_turf(to_beam), 'sound/magic/lightningshock.ogg', 50, TRUE, -1)
if(to_beam.can_block_magic(antimagic_flags))
to_beam.visible_message(
span_warning("[to_beam] absorbs the spell, remaining unharmed!"),
span_userdanger("You absorb the spell, remaining unharmed!"),
)
else
to_beam.electrocute_act(bolt_energy, "Lightning Bolt", flags = SHOCK_NOGLOVES)
// Bounce again! Call our proc recursively to keep the chain going (even if our mob blocked it with antimagic)
if(bounces < 1)
return
var/mob/living/carbon/to_beam_next = get_target(to_beam)
if(isnull(to_beam_next))
return
send_beam(to_beam, to_beam_next, max(bolt_energy - energy_lost_per_bounce, energy_lost_per_bounce), bounces - 1)
/datum/action/cooldown/spell/charged/beam/tesla/get_target(atom/center)
var/list/possibles = list()
for(var/mob/living/carbon/to_check in view(target_radius, center))
if(to_check == center || to_check == owner)
continue
if(!length(get_path_to(center, to_check, max_distance = target_radius, simulated_only = FALSE)))
continue
possibles += to_check
if(!length(possibles))
return null
return pick(possibles)
+30 -19
View File
@@ -70,39 +70,50 @@
left_dir = SOUTH
right_dir = NORTH
for(var/i in 1 to cone_levels)
// Go though every level of the cone levels and generate the cone.
for(var/level in 1 to cone_levels)
var/list/level_turfs = list()
// Our center turf always exists, it's straight ahead of the caster.
turf_to_use = get_step(turf_to_use, dir_to_use)
level_turfs += turf_to_use
if(i != 1)
left_turf = get_step(turf_to_use, left_dir)
level_turfs += left_turf
right_turf = get_step(turf_to_use, right_dir)
level_turfs += right_turf
for(var/left_i in 1 to i -calculate_cone_shape(i))
if(left_turf.density && respect_density)
// Level 1 only ever has 1 turf, it's a cone.
if(level != 1)
var/level_width_in_each_direction = round((calculate_cone_shape(level) - 1) / 2)
left_turf = turf_to_use
right_turf = turf_to_use
// Check turfs to the left...
for(var/left_of_center in 1 to level_width_in_each_direction)
if(respect_density && left_turf.density)
break
left_turf = get_step(left_turf, left_dir)
level_turfs += left_turf
for(var/right_i in 1 to i -calculate_cone_shape(i))
if(right_turf.density && respect_density)
// And turfs to the right.
for(var/right_of_enter in 1 to level_width_in_each_direction)
if(respect_density && right_turf.density)
break
right_turf = get_step(right_turf, right_dir)
level_turfs += right_turf
// Add the list of all turfs on this level to the turfs to return
turfs_to_return += list(level_turfs)
if(i == cone_levels)
continue
if(turf_to_use.density && respect_density)
// If we're at the last level, we're done
if(level == cone_levels)
break
// But if we're not at the last level, we should check that we can keep going
if(respect_density && turf_to_use.density)
break
return turfs_to_return
///This proc adjusts the cones width depending on the level.
/**
* Adjusts the width of the cone at the passed level.
* This is never called on the first level of the cone (level 1 is always 1 width)
*
* Return a number - the TOTAL width of the cone at the passed level.
*/
/datum/action/cooldown/spell/cone/proc/calculate_cone_shape(current_level)
var/end_taper_start = round(cone_levels * 0.8)
if(current_level > end_taper_start)
return (current_level % end_taper_start) * 2 //someone more talented and probably come up with a better formula.
else
return 2
// Default formula: (1 (innate) -> 3 -> 5 -> 5 -> 7 -> 7 -> 9 -> 9 -> ...)
return current_level + (current_level % 2) + 1
/**
* ### Staggered Cone
@@ -0,0 +1,67 @@
/datum/action/cooldown/spell/cone/staggered/cone_of_cold
name = "Cone of Cold"
desc = "Shoots out a freezing cone in front of you."
school = SCHOOL_EVOCATION
cooldown_time = 30 SECONDS
cooldown_reduction_per_rank = 4 SECONDS
invocation = "ISAGE!" // What killed the dinosaurs? THE ICE AGE
invocation_type = INVOCATION_SHOUT
cone_levels = 4
respect_density = TRUE
delay_between_level = 0.05 SECONDS
/// What flags do we pass to MakeSlippery when affecting turfs?
/// null / NONE / TURF_DRY means the turf is unaffected
var/turf_freeze_type = TURF_WET_PERMAFROST
/// How long do turfs remain slippery / frozen for?
/// 0 seconds means the turf is unaffected, INFINITY means it's made perma-wet
var/unfreeze_turf_duration = 45 SECONDS
/// What status effect do we apply when affecting mobs?
/// null means no status effect is applied
var/datum/status_effect/frozen_status_effect_path = /datum/status_effect/freon/lasting
/// How long do mobs remain frozen for?
/// 0 seconds means no status effect is applied, INFINITY means infinite duration (or default duration of the status effect)
var/unfreeze_mob_duration = 20 SECONDS
/// How much brute do we apply on freeze?
var/on_freeze_brute_damage = 10
/// How much burn do we apply on freeze?
var/on_freeze_burn_damage = 20
/// How long do objects remain frozen for?
/// 0 seconds mean no objects are frozen, INFINITY means infinite duration freeze
var/unfreeze_object_duration = 20 SECONDS
/datum/action/cooldown/spell/cone/staggered/cone_of_cold/do_turf_cone_effect(turf/target_turf, atom/caster, level)
if(!turf_freeze_type || unfreeze_turf_duration <= 0 SECONDS) // 0 duration = don't apply the slip
return
if(!isopenturf(target_turf))
return
var/turf/open/frozen_floor = target_turf
frozen_floor.MakeSlippery(turf_freeze_type, unfreeze_turf_duration, permanent = (unfreeze_turf_duration == INFINITY))
/datum/action/cooldown/spell/cone/staggered/cone_of_cold/do_mob_cone_effect(mob/living/target_mob, atom/caster, level)
if(target_mob.can_block_magic(antimagic_flags) || target_mob == caster)
return
if(ispath(frozen_status_effect_path) && unfreeze_mob_duration > 0 SECONDS) // 0 duration = don't apply the status effect
var/datum/status_effect/freeze = target_mob.apply_status_effect(frozen_status_effect_path)
if(unfreeze_mob_duration != INFINITY)
freeze.duration = world.time + unfreeze_mob_duration
if(on_freeze_brute_damage || on_freeze_burn_damage)
target_mob.take_overall_damage(on_freeze_brute_damage, on_freeze_burn_damage)
to_chat(target_mob, span_userdanger("You feel a bitter cold!"))
/datum/action/cooldown/spell/cone/staggered/cone_of_cold/do_obj_cone_effect(obj/target_obj, atom/caster, level)
if(unfreeze_object_duration <= 0 SECONDS) // 0 duration = don't apply a freeze
return
if(!target_obj.freeze())
return
if(unfreeze_object_duration == INFINITY) // Infinity duration = don't set an unfreeze timer
return
addtimer(CALLBACK(target_obj, TYPE_PROC_REF(/obj/, unfreeze)), unfreeze_object_duration)
@@ -4,6 +4,7 @@
background_icon_state = "bg_mime"
icon_icon = 'icons/mob/actions/actions_mime.dmi'
button_icon_state = "invisible_chair"
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
panel = "Mime"
sound = null
@@ -4,6 +4,7 @@
background_icon_state = "bg_mime"
icon_icon = 'icons/mob/actions/actions_mime.dmi'
button_icon_state = "invisible_wall"
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
panel = "Mime"
sound = null
@@ -6,6 +6,7 @@
background_icon_state = "bg_mime"
icon_icon = 'icons/mob/actions/actions_mime.dmi'
button_icon_state = "invisible_box"
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
panel = "Mime"
sound = null
@@ -19,6 +19,9 @@
/// What dummy mob type do we put jaunters in on jaunt?
var/jaunt_type = /obj/effect/dummy/phased_mob
/datum/action/cooldown/spell/jaunt/before_cast(atom/cast_on)
return ..() | SPELL_NO_FEEDBACK // Don't do the feedback until after we're jaunting
/datum/action/cooldown/spell/jaunt/can_cast_spell(feedback = TRUE)
. = ..()
if(!.)
@@ -49,10 +52,16 @@
* Returns the holder mob that was created
*/
/datum/action/cooldown/spell/jaunt/proc/enter_jaunt(mob/living/jaunter, turf/loc_override)
SHOULD_CALL_PARENT(TRUE)
var/obj/effect/dummy/phased_mob/jaunt = new jaunt_type(loc_override || get_turf(jaunter), jaunter)
RegisterSignal(jaunt, COMSIG_MOB_EJECTED_FROM_JAUNT, PROC_REF(on_jaunt_exited))
spell_requirements |= SPELL_CASTABLE_WHILE_PHASED
ADD_TRAIT(jaunter, TRAIT_MAGICALLY_PHASED, REF(src))
ADD_TRAIT(jaunter, TRAIT_RUNECHAT_HIDDEN, REF(src))
// Don't do the feedback until we have runechat hidden.
// Otherwise the text will follow the jaunt holder, which reveals where our caster is travelling.
spell_feedback()
// This needs to happen at the end, after all the traits and stuff is handled
SEND_SIGNAL(jaunter, COMSIG_MOB_ENTER_JAUNT, src, jaunt)
@@ -68,6 +77,8 @@
* Returns TRUE on successful exit, FALSE otherwise
*/
/datum/action/cooldown/spell/jaunt/proc/exit_jaunt(mob/living/unjaunter, turf/loc_override)
SHOULD_CALL_PARENT(TRUE)
var/obj/effect/dummy/phased_mob/jaunt = unjaunter.loc
if(!istype(jaunt))
return FALSE
@@ -92,13 +103,10 @@
SHOULD_CALL_PARENT(TRUE)
spell_requirements &= ~SPELL_CASTABLE_WHILE_PHASED
REMOVE_TRAIT(unjaunter, TRAIT_MAGICALLY_PHASED, REF(src))
REMOVE_TRAIT(unjaunter, TRAIT_RUNECHAT_HIDDEN, REF(src))
// This needs to happen at the end, after all the traits and stuff is handled
SEND_SIGNAL(unjaunter, COMSIG_MOB_AFTER_EXIT_JAUNT, src)
/// Simple helper to check if the passed mob is currently jaunting or not
/datum/action/cooldown/spell/jaunt/proc/is_jaunting(mob/living/user)
return istype(user.loc, /obj/effect/dummy/phased_mob)
/datum/action/cooldown/spell/jaunt/Remove(mob/living/remove_from)
exit_jaunt(remove_from)
if (!is_jaunting(remove_from)) // In case you have made exit_jaunt conditional, as in mirror walk
@@ -23,7 +23,7 @@
/// List of valid exit points
var/list/exit_point_list
/datum/action/cooldown/spell/jaunt/ethereal_jaunt/enter_jaunt(mob/living/jaunter)
/datum/action/cooldown/spell/jaunt/ethereal_jaunt/enter_jaunt(mob/living/jaunter, turf/loc_override)
. = ..()
if(!.)
return
@@ -87,12 +87,17 @@
to_chat(owner, span_warning("You cannot cast [src] on yourself!"))
return FALSE
if(get_dist(owner, cast_on) > cast_range)
to_chat(owner, span_warning("[cast_on.p_theyre(TRUE)] too far away!"))
return FALSE
return TRUE
/datum/action/cooldown/spell/pointed/before_cast(atom/cast_on)
. = ..()
if(. & SPELL_CANCEL_CAST)
return
if(owner && get_dist(get_turf(owner), get_turf(cast_on)) > cast_range)
cast_on.balloon_alert(owner, "too far away!")
return . | SPELL_CANCEL_CAST
/**
* ### Pointed projectile spells
*
@@ -5,6 +5,7 @@
background_icon_state = "bg_mime"
icon_icon = 'icons/mob/actions/actions_mime.dmi'
button_icon_state = "finger_guns0"
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
panel = "Mime"
sound = null
@@ -45,6 +45,7 @@
background_icon_state = "bg_mime"
icon_icon = 'icons/mob/actions/actions_mime.dmi'
button_icon_state = "invisible_blockade"
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
panel = "Mime"
sound = null
@@ -1,125 +0,0 @@
/datum/action/cooldown/spell/tesla
name = "Tesla Blast"
desc = "Charge up a tesla arc and release it at random nearby targets! \
You can move freely while it charges. The arc jumps between targets and can knock them down."
button_icon_state = "lightning"
cooldown_time = 30 SECONDS
cooldown_reduction_per_rank = 6.75 SECONDS
invocation = "UN'LTD P'WAH!"
invocation_type = INVOCATION_SHOUT
school = SCHOOL_EVOCATION
/// Whether we're currently channelling a tesla blast or not
var/currently_channeling = FALSE
/// How long it takes to channel the zap.
var/channel_time = 10 SECONDS
/// The radius around (either the caster or people shocked) to which the tesla blast can reach
var/shock_radius = 7
/// The halo that appears around the caster while charging the spell
var/static/mutable_appearance/halo
/// The sound played while charging the spell
/// Quote: "the only way i can think of to stop a sound, thank MSO for the idea."
var/sound/charge_sound
/datum/action/cooldown/spell/tesla/Remove(mob/living/remove_from)
reset_tesla(remove_from)
return ..()
/datum/action/cooldown/spell/tesla/set_statpanel_format()
. = ..()
if(!islist(.))
return
if(currently_channeling)
.[PANEL_DISPLAY_STATUS] = "CHANNELING"
/datum/action/cooldown/spell/tesla/can_cast_spell(feedback = TRUE)
. = ..()
if(!.)
return FALSE
if(currently_channeling)
if(feedback)
to_chat(owner, span_warning("You're already channeling [src]!"))
return FALSE
return TRUE
/datum/action/cooldown/spell/tesla/before_cast(atom/cast_on)
. = ..()
if(. & SPELL_CANCEL_CAST)
return
to_chat(cast_on, span_notice("You start gathering power..."))
charge_sound = new /sound('sound/magic/lightning_chargeup.ogg', channel = 7)
halo ||= mutable_appearance('icons/effects/effects.dmi', "electricity", EFFECTS_LAYER)
cast_on.add_overlay(halo)
playsound(get_turf(cast_on), charge_sound, 50, FALSE)
currently_channeling = TRUE
if(!do_after(cast_on, channel_time, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_HELD_ITEM)))
reset_tesla(cast_on)
return . | SPELL_CANCEL_CAST
/datum/action/cooldown/spell/tesla/reset_spell_cooldown()
reset_tesla(owner)
return ..()
/// Resets the tesla effect.
/datum/action/cooldown/spell/tesla/proc/reset_tesla(atom/to_reset)
to_reset.cut_overlay(halo)
currently_channeling = FALSE
/datum/action/cooldown/spell/tesla/cast(atom/cast_on)
. = ..()
// byond, why you suck?
charge_sound = sound(null, repeat = 0, wait = 1, channel = charge_sound.channel)
// Sorry MrPerson, but the other ways just didn't do it the way i needed to work, this is the only way.
playsound(get_turf(cast_on), charge_sound, 50, FALSE)
var/mob/living/carbon/to_zap_first = get_target(cast_on)
if(QDELETED(to_zap_first))
cast_on.balloon_alert(cast_on, "no targets nearby!")
reset_spell_cooldown()
return FALSE
playsound(get_turf(cast_on), 'sound/magic/lightningbolt.ogg', 50, TRUE)
zap_target(cast_on, to_zap_first)
reset_tesla(cast_on)
/// Zaps a target, the bolt originating from origin.
/datum/action/cooldown/spell/tesla/proc/zap_target(atom/origin, mob/living/carbon/to_zap, bolt_energy = 30, bounces = 5)
origin.Beam(to_zap, icon_state = "lightning[rand(1,12)]", time = 0.5 SECONDS)
playsound(get_turf(to_zap), 'sound/magic/lightningshock.ogg', 50, TRUE, -1)
if(to_zap.can_block_magic(antimagic_flags))
to_zap.visible_message(
span_warning("[to_zap] absorbs the spell, remaining unharmed!"),
span_userdanger("You absorb the spell, remaining unharmed!"),
)
else
to_zap.electrocute_act(bolt_energy, "Lightning Bolt", flags = SHOCK_NOGLOVES)
if(bounces >= 1)
var/mob/living/carbon/to_zap_next = get_target(to_zap)
if(!QDELETED(to_zap_next))
zap_target(to_zap, to_zap_next, max((bolt_energy - 5), 5), bounces - 1)
/// Get a target in view of us to zap next. Returns a carbon, or null if none were found.
/datum/action/cooldown/spell/tesla/proc/get_target(atom/center)
var/list/possibles = list()
for(var/mob/living/carbon/to_check in view(shock_radius, center))
if(to_check == center || to_check == owner)
continue
if(!length(get_path_to(center, to_check, max_distance = shock_radius, simulated_only = FALSE)))
continue
possibles += to_check
if(!length(possibles))
return null
return pick(possibles)
+108 -16
View File
@@ -1,3 +1,23 @@
/**
* ## Touch Spell
*
* Touch spells are spells which function through the power of an item attack.
*
* Instead of the spell triggering when the caster presses the button,
* pressing the button will give them a hand object.
* The spell's effects are cast when the hand object makes contact with something.
*
* To implement a touch spell, all you need is to implement:
* * is_valid_target - to check whether the slapped target is valid
* * cast_on_hand_hit - to implement effects on cast
*
* However, for added complexity, you can optionally implement:
* * on_antimagic_triggered - to cause effects when antimagic is triggered
* * cast_on_secondary_hand_hit - to implement different effects if the caster r-clicked
* It is not necessarily to touch any of the core functions, and is
* (generally) inadvisable unless you know what you're doing
*/
/datum/action/cooldown/spell/touch
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
sound = 'sound/items/welder.ogg'
@@ -12,6 +32,8 @@
var/draw_message = span_notice("You channel the power of the spell to your hand.")
/// The message displayed upon willingly dropping / deleting / cancelling the touch hand before using it
var/drop_message = span_notice("You draw the power out of your hand.")
/// If TRUE, the caster can willingly hit themselves with the hand
var/can_cast_on_self = FALSE
/datum/action/cooldown/spell/touch/Destroy()
// If we have an owner, the hand is cleaned up in Remove(), which Destroy() calls.
@@ -23,6 +45,10 @@
remove_hand(remove_from)
return ..()
// PreActivate is overridden to not check is_valid_target on the caster, as it makes less sense.
/datum/action/cooldown/spell/touch/PreActivate(atom/target)
return Activate(target)
/datum/action/cooldown/spell/touch/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force = FALSE)
. = ..()
if(!button)
@@ -42,6 +68,9 @@
. = ..()
if(!.)
return FALSE
// Currently, hard checks for carbons.
// If someone wants to add support for simplemobs
// adminbussed to have hands, go for it
if(!iscarbon(owner))
return FALSE
var/mob/living/carbon/carbon_owner = owner
@@ -49,8 +78,9 @@
return FALSE
return TRUE
// Checks if the mob slapped with the hand is a valid target.
/datum/action/cooldown/spell/touch/is_valid_target(atom/cast_on)
return iscarbon(cast_on)
return isliving(cast_on)
/**
* Creates a new hand_path hand and equips it to the caster.
@@ -59,6 +89,8 @@
* Otherwise, registers signals and returns TRUE.
*/
/datum/action/cooldown/spell/touch/proc/create_hand(mob/living/carbon/cast_on)
SHOULD_CALL_PARENT(TRUE)
var/obj/item/melee/touch_attack/new_hand = new hand_path(cast_on, src)
if(!cast_on.put_in_hands(new_hand, del_on_fail = TRUE))
reset_spell_cooldown()
@@ -69,10 +101,7 @@
return FALSE
attached_hand = new_hand
RegisterSignal(attached_hand, COMSIG_ITEM_AFTERATTACK, PROC_REF(on_hand_hit))
RegisterSignal(attached_hand, COMSIG_ITEM_AFTERATTACK_SECONDARY, PROC_REF(on_secondary_hand_hit))
RegisterSignal(attached_hand, COMSIG_PARENT_QDELETING, PROC_REF(on_hand_deleted))
RegisterSignal(attached_hand, COMSIG_ITEM_DROPPED, PROC_REF(on_hand_dropped))
register_hand_signals()
to_chat(cast_on, draw_message)
return TRUE
@@ -83,8 +112,10 @@
* If reset_cooldown_after is FALSE, we will instead just start the spell's cooldown
*/
/datum/action/cooldown/spell/touch/proc/remove_hand(mob/living/hand_owner, reset_cooldown_after = FALSE)
SHOULD_CALL_PARENT(TRUE)
if(!QDELETED(attached_hand))
UnregisterSignal(attached_hand, list(COMSIG_ITEM_AFTERATTACK, COMSIG_ITEM_AFTERATTACK_SECONDARY, COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED))
unregister_hand_signals()
hand_owner?.temporarilyRemoveItemFromInventory(attached_hand)
QDEL_NULL(attached_hand)
@@ -95,6 +126,26 @@
else
StartCooldown()
/// Registers all signal procs for the hand.
/datum/action/cooldown/spell/touch/proc/register_hand_signals()
SHOULD_CALL_PARENT(TRUE)
RegisterSignal(attached_hand, COMSIG_ITEM_AFTERATTACK, PROC_REF(on_hand_hit))
RegisterSignal(attached_hand, COMSIG_ITEM_AFTERATTACK_SECONDARY, PROC_REF(on_secondary_hand_hit))
RegisterSignal(attached_hand, COMSIG_ITEM_DROPPED, PROC_REF(on_hand_dropped))
RegisterSignal(attached_hand, COMSIG_PARENT_QDELETING, PROC_REF(on_hand_deleted))
/// Unregisters all signal procs for the hand.
/datum/action/cooldown/spell/touch/proc/unregister_hand_signals()
SHOULD_CALL_PARENT(TRUE)
UnregisterSignal(attached_hand, list(
COMSIG_ITEM_AFTERATTACK,
COMSIG_ITEM_AFTERATTACK_SECONDARY,
COMSIG_ITEM_DROPPED,
COMSIG_PARENT_QDELETING,
))
// Touch spells don't go on cooldown OR give off an invocation until the hand is used itself.
/datum/action/cooldown/spell/touch/before_cast(atom/cast_on)
return ..() | SPELL_NO_FEEDBACK | SPELL_NO_IMMEDIATE_COOLDOWN
@@ -114,12 +165,11 @@
*/
/datum/action/cooldown/spell/touch/proc/on_hand_hit(datum/source, atom/victim, mob/caster, proximity_flag, click_parameters)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE) // DEFINITELY don't put effects here, put them in cast_on_hand_hit
if(!proximity_flag)
return
if(victim == caster)
return
if(!can_cast_spell(feedback = FALSE))
if(!can_hit_with_hand(victim, caster))
return
INVOKE_ASYNC(src, PROC_REF(do_hand_hit), source, victim, caster)
@@ -131,22 +181,43 @@
*/
/datum/action/cooldown/spell/touch/proc/on_secondary_hand_hit(datum/source, atom/victim, mob/caster, proximity_flag, click_parameters)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE) // DEFINITELY don't put effects here, put them in cast_on_secondary_hand_hit
if(!proximity_flag)
return
if(victim == caster)
return
if(!can_cast_spell(feedback = FALSE))
if(!can_hit_with_hand(victim, caster))
return
INVOKE_ASYNC(src, PROC_REF(do_secondary_hand_hit), source, victim, caster)
return COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN
/// Checks if the passed victim can be cast on by the caster.
/datum/action/cooldown/spell/touch/proc/can_hit_with_hand(atom/victim, mob/caster)
if(!can_cast_on_self && victim == caster)
return FALSE
if(!is_valid_target(victim))
return FALSE
if(!can_cast_spell(feedback = TRUE))
return FALSE
return TRUE
/**
* Calls cast_on_hand_hit() from the caster onto the victim.
* It's worth noting that victim will be guaranteed to be whatever checks are implemented in is_valid_target by this point.
*
* Implements checks for antimagic.
*/
/datum/action/cooldown/spell/touch/proc/do_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster)
SHOULD_NOT_OVERRIDE(TRUE) // Don't put effects here, put them in cast_on_hand_hit
SEND_SIGNAL(src, COMSIG_SPELL_TOUCH_HAND_HIT, victim, caster, hand)
if(!cast_on_hand_hit(hand, victim, caster))
var/mob/mob_victim = victim
if(istype(mob_victim) && mob_victim.can_block_magic(antimagic_flags))
on_antimagic_triggered(hand, victim, caster)
else if(!cast_on_hand_hit(hand, victim, caster))
return
log_combat(caster, victim, "cast the touch spell [name] on", hand)
@@ -155,8 +226,13 @@
/**
* Calls do_secondary_hand_hit() from the caster onto the victim.
* It's worth noting that victim will be guaranteed to be whatever checks are implemented in is_valid_target by this point.
* Does NOT check for antimagic on its own. Implement your own checks if you want the r-click to abide by it.
*/
/datum/action/cooldown/spell/touch/proc/do_secondary_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster)
SHOULD_NOT_OVERRIDE(TRUE) // Don't put effects here, put them in cast_on_secondary_hand_hit
var/secondary_result = cast_on_secondary_hand_hit(hand, victim, caster)
switch(secondary_result)
// Continue will remove the hand here and stop
@@ -216,6 +292,22 @@
remove_hand(dropper, reset_cooldown_after = TRUE)
/**
* Called whenever our spell is cast, but blocked by antimagic.
*/
/datum/action/cooldown/spell/touch/proc/on_antimagic_triggered(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster)
return
/**
* ## Touch attack item
*
* Used for touch spells to have something physical to slap people with.
*
* Try to avoid adding behavior onto these for your touch spells!
* The spells themselves should handle most, if not all, of the casted effects.
*
* These should generally just be dummy objects - holds name and icon stuff.
*/
/obj/item/melee/touch_attack
name = "\improper outstretched hand"
desc = "High Five?"
@@ -224,7 +316,7 @@
righthand_file = 'icons/mob/inhands/items/touchspell_righthand.dmi'
icon_state = "latexballon"
inhand_icon_state = null
item_flags = NEEDS_PERMIT | ABSTRACT
item_flags = NEEDS_PERMIT | ABSTRACT | HAND_ITEM
w_class = WEIGHT_CLASS_HUGE
force = 0
throwforce = 0
@@ -243,7 +335,7 @@
if(!iscarbon(user)) //Look ma, no hands
return TRUE
if(!(user.mobility_flags & MOBILITY_USE))
to_chat(user, span_warning("You can't reach out!"))
user.balloon_alert(user, "can't reach out!")
return TRUE
return ..()
@@ -254,7 +346,7 @@
* However, if you want to consume the hand and not give a cooldown,
* such as adding a unique behavior to the hand specifically, this function will do that.
*/
/obj/item/melee/touch_attack/mansus_fist/proc/remove_hand_with_no_refund(mob/holder)
/obj/item/melee/touch_attack/proc/remove_hand_with_no_refund(mob/holder)
var/datum/action/cooldown/spell/touch/hand_spell = spell_which_made_us?.resolve()
if(!QDELETED(hand_spell))
hand_spell.remove_hand(holder, reset_cooldown_after = FALSE)
@@ -14,11 +14,7 @@
hand_path = /obj/item/melee/touch_attack/duffelbag
/datum/action/cooldown/spell/touch/duffelbag/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster)
if(!iscarbon(victim))
return FALSE
var/mob/living/carbon/duffel_victim = victim
/// Some meme "elaborate backstories" to use.
var/static/list/elaborate_backstory = list(
"spacewar origin story",
"military background",
@@ -28,52 +24,57 @@
"upbringing on the space farm",
"fond memories with your buddy Keith",
)
if(duffel_victim.can_block_magic(antimagic_flags))
to_chat(caster, span_warning("The spell can't seem to affect [duffel_victim]!"))
to_chat(duffel_victim, span_warning("You really don't feel like talking about your [pick(elaborate_backstory)] with complete strangers today."))
return TRUE
/datum/action/cooldown/spell/touch/duffelbag/is_valid_target(atom/cast_on)
return iscarbon(cast_on)
/datum/action/cooldown/spell/touch/duffelbag/on_antimagic_triggered(obj/item/melee/touch_attack/hand, mob/living/carbon/victim, mob/living/carbon/caster)
to_chat(caster, span_warning("The spell can't seem to affect [victim]!"))
to_chat(victim, span_warning("You really don't feel like talking about your [pick(elaborate_backstory)] with complete strangers today."))
/datum/action/cooldown/spell/touch/duffelbag/cast_on_hand_hit(obj/item/melee/touch_attack/hand, mob/living/carbon/victim, mob/living/carbon/caster)
// To get it started, stun and knockdown the person being hit
duffel_victim.flash_act()
duffel_victim.Immobilize(5 SECONDS)
duffel_victim.apply_damage(80, STAMINA)
duffel_victim.Knockdown(5 SECONDS)
victim.flash_act()
victim.Immobilize(5 SECONDS)
victim.apply_damage(80, STAMINA)
victim.Knockdown(5 SECONDS)
// If someone's already cursed, don't try to give them another
if(HAS_TRAIT(duffel_victim, TRAIT_DUFFEL_CURSE_PROOF))
to_chat(caster, span_warning("The burden of [duffel_victim]'s duffel bag becomes too much, shoving them to the floor!"))
to_chat(duffel_victim, span_warning("The weight of this bag becomes overburdening!"))
if(HAS_TRAIT(victim, TRAIT_DUFFEL_CURSE_PROOF))
to_chat(caster, span_warning("The burden of [victim]'s duffel bag becomes too much, shoving them to the floor!"))
to_chat(victim, span_warning("The weight of this bag becomes overburdening!"))
return TRUE
// However if they're uncursed, they're fresh for getting a cursed bag
var/obj/item/storage/backpack/duffelbag/cursed/conjured_duffel = new get_turf(victim)
duffel_victim.visible_message(
span_danger("A growling duffel bag appears on [duffel_victim]!"),
victim.visible_message(
span_danger("A growling duffel bag appears on [victim]!"),
span_danger("You feel something attaching itself to you, and a strong desire to discuss your [pick(elaborate_backstory)] at length!"),
)
// This duffelbag is now cuuuurrrsseed! Equip it on them
ADD_TRAIT(conjured_duffel, TRAIT_DUFFEL_CURSE_PROOF, CURSED_ITEM_TRAIT(conjured_duffel.name))
conjured_duffel.pickup(duffel_victim)
conjured_duffel.forceMove(duffel_victim)
conjured_duffel.pickup(victim)
conjured_duffel.forceMove(victim)
// Put it on their back first
if(duffel_victim.dropItemToGround(duffel_victim.back))
duffel_victim.equip_to_slot_if_possible(conjured_duffel, ITEM_SLOT_BACK, TRUE, TRUE)
if(victim.dropItemToGround(victim.back))
victim.equip_to_slot_if_possible(conjured_duffel, ITEM_SLOT_BACK, TRUE, TRUE)
return TRUE
// If the back equip failed, put it in their hands first
if(duffel_victim.put_in_hands(conjured_duffel))
if(victim.put_in_hands(conjured_duffel))
return TRUE
// If they had no empty hands, try to put it in their inactive hand first
duffel_victim.dropItemToGround(duffel_victim.get_inactive_held_item())
if(duffel_victim.put_in_hands(conjured_duffel))
victim.dropItemToGround(victim.get_inactive_held_item())
if(victim.put_in_hands(conjured_duffel))
return TRUE
// If their inactive hand couldn't be emptied or found, put it in their active hand
duffel_victim.dropItemToGround(duffel_victim.get_active_held_item())
if(duffel_victim.put_in_hands(conjured_duffel))
victim.dropItemToGround(victim.get_active_held_item())
if(victim.put_in_hands(conjured_duffel))
return TRUE
// Well, we failed to give them the duffel bag,
@@ -12,14 +12,13 @@
hand_path = /obj/item/melee/touch_attack/flesh_to_stone
/datum/action/cooldown/spell/touch/flesh_to_stone/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster)
if(!isliving(victim))
return FALSE
/datum/action/cooldown/spell/touch/flesh_to_stone/on_antimagic_triggered(obj/item/melee/touch_attack/hand, mob/living/victim, mob/living/carbon/caster)
to_chat(caster, span_warning("The spell can't seem to affect [victim]!"))
to_chat(victim, span_warning("You feel your flesh turn to stone for a moment, then revert back!"))
/datum/action/cooldown/spell/touch/flesh_to_stone/cast_on_hand_hit(obj/item/melee/touch_attack/hand, mob/living/victim, mob/living/carbon/caster)
var/mob/living/living_victim = victim
if(living_victim.can_block_magic(antimagic_flags))
to_chat(caster, span_warning("The spell can't seem to affect [victim]!"))
to_chat(victim, span_warning("You feel your flesh turn to stone for a moment, then revert back!"))
return TRUE
living_victim.Stun(4 SECONDS)
+20 -18
View File
@@ -14,26 +14,28 @@
hand_path = /obj/item/melee/touch_attack/smite
/datum/action/cooldown/spell/touch/smite/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster)
if(!isliving(victim))
return FALSE
/// Smite is pretty extravagant, so whenever we get casted, we blind everyone nearby.
/datum/action/cooldown/spell/touch/smite/proc/blind_everyone_nearby(mob/living/victim, atom/center)
do_sparks(sparks_amt, FALSE, get_turf(victim))
for(var/mob/living/nearby_spectator in view(caster, 7))
if(nearby_spectator == caster)
for(var/mob/living/nearby_spectator in view(center, 7))
if(nearby_spectator == center)
continue
nearby_spectator.flash_act(affect_silicon = FALSE)
var/mob/living/living_victim = victim
if(living_victim.can_block_magic(antimagic_flags))
caster.visible_message(
span_warning("The feedback blows [caster]'s arm off!"),
span_userdanger("The spell bounces from [living_victim]'s skin back into your arm!"),
)
caster.flash_act()
var/obj/item/bodypart/to_dismember = caster.get_holding_bodypart_of_item(hand)
to_dismember?.dismember()
return TRUE
/datum/action/cooldown/spell/touch/smite/on_antimagic_triggered(obj/item/melee/touch_attack/hand, mob/living/victim, mob/living/carbon/caster)
caster.visible_message(
span_warning("The feedback blows [caster]'s arm off!"),
span_userdanger("The spell bounces from [victim]'s skin back into your arm!"),
)
// Off goes the arm we were casting with!
var/obj/item/bodypart/to_dismember = caster.get_holding_bodypart_of_item(hand)
to_dismember?.dismember()
// And do the blind (us included)
caster.flash_act()
blind_everyone_nearby(caster, caster)
/datum/action/cooldown/spell/touch/smite/cast_on_hand_hit(obj/item/melee/touch_attack/hand, mob/living/victim, mob/living/carbon/caster)
blind_everyone_nearby(victim, caster)
if(ishuman(victim))
var/mob/living/carbon/human/human_victim = victim
@@ -45,8 +47,8 @@
new /obj/effect/gibspawner(get_turf(victim))
return TRUE
living_victim.investigate_log("has been gibbed by the smite spell.", INVESTIGATE_DEATHS)
living_victim.gib()
victim.investigate_log("has been gibbed by the smite spell.", INVESTIGATE_DEATHS)
victim.gib()
return TRUE
/obj/item/melee/touch_attack/smite