diff --git a/code/__DEFINES/dcs/signals/signals_spell.dm b/code/__DEFINES/dcs/signals/signals_spell.dm index ebbdcc4e2be..1154eba338f 100644 --- a/code/__DEFINES/dcs/signals/signals_spell.dm +++ b/code/__DEFINES/dcs/signals/signals_spell.dm @@ -14,6 +14,10 @@ /// Return from before cast signals to prevent the spell from going on cooldown before aftercast. #define SPELL_NO_IMMEDIATE_COOLDOWN (1 << 2) +/// Sent from /datum/action/cooldown/spell/can_cast_check() to the spell: (feedback) +#define COMSIG_SPELL_CAN_CAST_CHECK "can_cast_spell" + // Return SPELL_CANCEL_CAST to prevent the spell from being cast + /// Sent to an mob when a [/datum/action/cooldown/spell] calls try_invoke() to the caster: (datum/action/cooldown/spell/spell, feedback) #define COMSIG_MOB_TRY_INVOKE_SPELL "try_invoke_spell" /// The spell gets canceled diff --git a/code/modules/antagonists/revenant/revenant_skill.dm b/code/modules/antagonists/revenant/revenant_skill.dm new file mode 100644 index 00000000000..e3259ba9143 --- /dev/null +++ b/code/modules/antagonists/revenant/revenant_skill.dm @@ -0,0 +1,124 @@ +/// Attach to revenant spells to make them cost essence to cast +/datum/component/revenant_ability + /// If it's locked, and needs to be unlocked before use + VAR_FINAL/locked = TRUE + /// How much essence it costs to unlock + var/unlock_amount = 100 + /// How much essence it costs to use + var/cast_amount = 50 + + /// How long it reveals the revenant + var/reveal_duration = 8 SECONDS + // How long it stuns the revenant + var/stun_duration = 2 SECONDS + + VAR_FINAL/image/locked_overlay + +/datum/component/revenant_ability/Initialize( + unlock_amount = 100, + cast_amount = 50, + reveal_duration = 8 SECONDS, + stun_duration = 2 SECONDS, +) + + if(!istype(parent, /datum/action/cooldown/spell)) + return COMPONENT_INCOMPATIBLE + + set_unlock_amount(unlock_amount) + set_cast_amount(cast_amount) + set_durations(reveal_duration, stun_duration) + + RegisterSignal(parent, COMSIG_SPELL_CAN_CAST_CHECK, PROC_REF(can_cast)) + RegisterSignal(parent, COMSIG_SPELL_BEFORE_CAST, PROC_REF(before_cast)) + RegisterSignal(parent, COMSIG_SPELL_AFTER_CAST, PROC_REF(after_cast)) + RegisterSignal(parent, COMSIG_ACTION_OVERLAY_APPLY, PROC_REF(add_locked_overlay)) + + locked_overlay = image('icons/mob/actions/actions_revenant.dmi', "locked") + +/datum/component/revenant_ability/vv_edit_var(var_name, var_value) + . = ..() + switch(var_name) + if(NAMEOF(src, unlock_amount)) + set_unlock_amount(var_value) + if(NAMEOF(src, cast_amount)) + set_cast_amount(var_value) + if(NAMEOF(src, reveal_duration), NAMEOF(src, stun_duration)) + set_durations(reveal_duration, stun_duration) + if(NAMEOF(src, locked)) + update_spell_name() + +/datum/component/revenant_ability/proc/update_spell_name() + var/datum/action/cooldown/spell/spell = parent + if(locked) + spell.name = "[initial(spell.name)] ([unlock_amount]SE)" + else + spell.name = "[initial(spell.name)] ([cast_amount]E)" + spell.build_all_button_icons() + +/datum/component/revenant_ability/proc/set_unlock_amount(new_value) + unlock_amount = new_value + update_spell_name() + +/datum/component/revenant_ability/proc/set_cast_amount(new_value) + cast_amount = new_value + update_spell_name() + +/datum/component/revenant_ability/proc/set_durations(new_reveal_duration, new_stun_duration) + reveal_duration = new_reveal_duration + stun_duration = new_stun_duration + +/datum/component/revenant_ability/proc/can_cast(datum/action/cooldown/spell/source, feedback) + SIGNAL_HANDLER + + var/mob/living/basic/revenant/ghost = source.owner + if(!istype(ghost)) + return NONE // just allow it anyways + + if(locked) + if(ghost.essence_excess >= unlock_amount) + return NONE + if(feedback) + to_chat(ghost, span_revenwarning("You don't have enough essence to unlock [initial(source.name)]!")) + return SPELL_CANCEL_CAST + + if(!ghost.cast_check(cast_amount, deduct_essence = FALSE, silent = !feedback)) + return SPELL_CANCEL_CAST + + return NONE + +/datum/component/revenant_ability/proc/before_cast(datum/action/cooldown/spell/source, atom/cast_on) + SIGNAL_HANDLER + + var/mob/living/basic/revenant/ghost = source.owner + if(!istype(ghost)) + return NONE // just allow it anyways + + if(locked) + if(ghost.unlock(unlock_amount)) + to_chat(ghost, span_revennotice("You have unlocked [initial(source.name)]!")) + locked = FALSE + update_spell_name() + else + to_chat(ghost, span_revenwarning("You don't have enough essence to unlock [initial(source.name)]!")) + return SPELL_CANCEL_CAST + + if(!ghost.cast_check(cast_amount, deduct_essence = TRUE, silent = FALSE)) + return SPELL_CANCEL_CAST + + return NONE + +/datum/component/revenant_ability/proc/after_cast(datum/action/cooldown/spell/source, atom/cast_on) + SIGNAL_HANDLER + + var/mob/living/caster = source.owner + if(reveal_duration > 0 SECONDS) + caster.apply_status_effect(/datum/status_effect/revenant/revealed, reveal_duration) + if(stun_duration > 0 SECONDS) + caster.apply_status_effect(/datum/status_effect/incapacitating/paralyzed/revenant, stun_duration) + +/datum/component/revenant_ability/proc/add_locked_overlay(datum/action/cooldown/spell/source, atom/movable/screen/movable/action_button/current_button, ...) + SIGNAL_HANDLER + + current_button.cut_overlay(locked_overlay) + if(locked) + current_button.add_overlay(locked_overlay) diff --git a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm index 4aae0ffa2d7..436f8e27c89 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm @@ -160,6 +160,7 @@ /mob/living/basic/revenant/proc/update_revenant_appearance() SIGNAL_HANDLER update_appearance(UPDATE_ICON) + update_mob_action_buttons() /mob/living/basic/revenant/AltClickOn(atom/target) if(CAN_I_SEE(target)) @@ -321,6 +322,7 @@ return ADD_TRAIT(src, TRAIT_NO_TRANSFORM, REVENANT_STUNNED_TRAIT) dormant = TRUE + update_mob_action_buttons() visible_message( span_warning("[src] lets out a waning screech as violet mist swirls around its dissolving body!"), @@ -410,28 +412,39 @@ return TRUE -/mob/living/basic/revenant/proc/cast_check(essence_cost) +/mob/living/basic/revenant/proc/cast_check(essence_cost, deduct_essence = TRUE, silent = FALSE) if(QDELETED(src)) return var/turf/current = get_turf(src) if(isclosedturf(current)) - to_chat(src, span_revenwarning("You cannot use abilities from inside of a wall.")) + if(!silent) + to_chat(src, span_revenwarning("You cannot use abilities from inside of a wall.")) return FALSE for(var/obj/thing in current) if(!thing.density || thing.CanPass(src, get_dir(current, src))) continue - to_chat(src, span_revenwarning("You cannot use abilities inside of a dense object.")) + if(!silent) + to_chat(src, span_revenwarning("You cannot use abilities inside of a dense object.")) return FALSE + if(dormant) + if(!silent) + to_chat(src, span_revenwarning("Your powers lie dormant right now!")) + return SPELL_CANCEL_CAST + if(HAS_TRAIT(src, TRAIT_REVENANT_INHIBITED)) - to_chat(src, span_revenwarning("Your powers have been suppressed by a nullifying energy!")) + if(!silent) + to_chat(src, span_revenwarning("Your powers have been suppressed by a nullifying energy!")) return FALSE - if(!change_essence_amount(essence_cost, TRUE)) - to_chat(src, span_revenwarning("You lack the essence to use that ability.")) + essence_cost = abs(essence_cost) * -1 + var/has_essence = deduct_essence ? change_essence_amount(essence_cost, silent = TRUE) : (essence + essence_cost >= 0) + if(!has_essence) + if(!silent) + to_chat(src, span_revenwarning("You lack the essence to use that ability!")) return FALSE return TRUE @@ -455,6 +468,7 @@ incorporeal_move = INCORPOREAL_MOVE_JAUNT RemoveInvisibility(type) alpha = 255 + update_mob_action_buttons() /mob/living/basic/revenant/proc/change_essence_amount(essence_to_change_by, silent = FALSE, source = null) if(QDELETED(src)) @@ -478,6 +492,19 @@ to_chat(src, span_revenminor("Lost [essence_to_change_by]E [source ? "from [source]":""].")) return TRUE +/mob/living/basic/revenant/mob_negates_gravity() + return TRUE // i don't gotta explain shit + +/mob/living/basic/revenant/vv_edit_var(vname, vval) + . = ..() + if(vname == NAMEOF(src, essence) || vname == NAMEOF(src, max_essence) || vname == NAMEOF(src, essence_excess)) + update_health_hud() + update_mob_action_buttons() + +/mob/living/basic/revenant/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + update_mob_action_buttons() + /mob/living/basic/revenant/proc/on_reflect(datum/source, atom/movable/reflecting_in, obj/effect/abstract/reflection) SIGNAL_HANDLER // powers are inhibited and we're not revealed so we can't project a reflect diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm index d456f104218..2030315410c 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm @@ -20,13 +20,10 @@ antimagic_flags = MAGIC_RESISTANCE_HOLY spell_requirements = NONE - /// If it's locked, and needs to be unlocked before use - var/locked = TRUE /// How much essence it costs to unlock var/unlock_amount = 100 /// How much essence it costs to use var/cast_amount = 50 - /// How long it reveals the revenant var/reveal_duration = 8 SECONDS // How long it stuns the revenant @@ -34,64 +31,27 @@ /datum/action/cooldown/spell/aoe/revenant/New(Target) . = ..() - if(!isrevenant(target)) - stack_trace("[type] was given to a non-revenant mob, please don't.") - qdel(src) - return - - if(locked) - name = "[initial(name)] ([unlock_amount]SE)" - else - name = "[initial(name)] ([cast_amount]E)" - -/datum/action/cooldown/spell/aoe/revenant/can_cast_spell(feedback = TRUE) - . = ..() - if(!.) - return FALSE - if(!isrevenant(owner)) - stack_trace("[type] was owned by a non-revenant mob, please don't.") - return FALSE - - var/mob/living/basic/revenant/ghost = owner - if(ghost.dormant || HAS_TRAIT(ghost, TRAIT_REVENANT_INHIBITED)) - return FALSE - if(locked && ghost.essence_excess <= unlock_amount) - return FALSE - if(ghost.essence <= cast_amount) - return FALSE - - return TRUE + AddComponent(/datum/component/revenant_ability, \ + unlock_amount = unlock_amount, \ + cast_amount = cast_amount, \ + reveal_duration = reveal_duration, \ + stun_duration = stun_duration, \ + ) /datum/action/cooldown/spell/aoe/revenant/get_things_to_cast_on(atom/center) return RANGE_TURFS(aoe_radius, center) -/datum/action/cooldown/spell/aoe/revenant/before_cast(mob/living/basic/revenant/cast_on) +/datum/action/cooldown/spell/aoe/revenant/vv_edit_var(var_name, var_value) . = ..() - if(. & SPELL_CANCEL_CAST) - return - - if(locked) - if(!cast_on.unlock(unlock_amount)) - to_chat(cast_on, span_revenwarning("You don't have enough essence to unlock [initial(name)]!")) - reset_spell_cooldown() - return . | SPELL_CANCEL_CAST - - name = "[initial(name)] ([cast_amount]E)" - to_chat(cast_on, span_revennotice("You have unlocked [initial(name)]!")) - locked = FALSE - reset_spell_cooldown() - return . | SPELL_CANCEL_CAST - - if(!cast_on.cast_check(-cast_amount)) - reset_spell_cooldown() - return . | SPELL_CANCEL_CAST - -/datum/action/cooldown/spell/aoe/revenant/after_cast(mob/living/basic/revenant/cast_on) - . = ..() - if(reveal_duration > 0 SECONDS) - cast_on.apply_status_effect(/datum/status_effect/revenant/revealed, reveal_duration) - if(stun_duration > 0 SECONDS) - cast_on.apply_status_effect(/datum/status_effect/incapacitating/paralyzed/revenant, stun_duration) + // gross getcomp, but this is solely to make life easier for badmins/debug. sue me + var/datum/component/revenant_ability/rev_comp = GetComponent(/datum/component/revenant_ability) + switch(var_name) + if(NAMEOF(src, unlock_amount)) + rev_comp.set_unlock_amount(var_value) + if(NAMEOF(src, cast_amount)) + rev_comp.set_cast_amount(var_value) + if(NAMEOF(src, reveal_duration), NAMEOF(src, stun_duration)) + rev_comp.set_durations(reveal_duration, stun_duration) //Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people. /datum/action/cooldown/spell/aoe/revenant/overload diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 5bdf8014769..ccbf0097cda 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -153,6 +153,9 @@ if(!owner) CRASH("[type] - can_cast_spell called on a spell without an owner!") + if(SEND_SIGNAL(src, COMSIG_SPELL_CAN_CAST_CHECK, feedback) & SPELL_CANCEL_CAST) + return FALSE + // Certain spells are not allowed on the centcom zlevel var/turf/caster_turf = get_turf(owner) // Spells which require being on the station diff --git a/icons/mob/actions/actions_revenant.dmi b/icons/mob/actions/actions_revenant.dmi index d657e8a3dae..3b75c4c1d30 100644 Binary files a/icons/mob/actions/actions_revenant.dmi and b/icons/mob/actions/actions_revenant.dmi differ diff --git a/tgstation.dme b/tgstation.dme index f4ae7c525a4..da8503ddcfc 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3563,6 +3563,7 @@ #include "code\modules\antagonists\revenant\haunted_item.dm" #include "code\modules\antagonists\revenant\revenant_antag.dm" #include "code\modules\antagonists\revenant\revenant_blight.dm" +#include "code\modules\antagonists\revenant\revenant_skill.dm" #include "code\modules\antagonists\revolution\enemy_of_the_state.dm" #include "code\modules\antagonists\revolution\revolution.dm" #include "code\modules\antagonists\revolution\revolution_handler.dm"