- Joined
- Jan 10, 2023
- Messages
- 260
Hello all,
I'm wondering where the best place to post this is, but I think this is the right place to start, in case there is already a library that contains this or if there is a library this would fit well in.
I've recently come across a niche-scenario that concerns any/all Channel-based abilities in regard to Refunding Mana, as well as a secondary niche-scenario which may occur depending on a channel-based ability's "Follow Through Time", which requires some special treatment when it comes to triggering said ability.
The issue with Refunding Mana for a Channel-based ability is a well known one, described well here in this thread from five years ago.
If there is a better solution than the one I am about to share, I would like to know it, if you know a better way to handle this please share, there are a few ways I have found of dealing with it, some are better than others, depending on some finer details.
Some education:
The 'issue' is that when you try to refund the mana, if the unit is at maximum mana, the unit will not be refunded unless you use either a timer or TriggerSleepAction to ensure that the mana has actually been lost.
The problem is that immediately as the EVENT_PLAYER_UNIT_SPELL_EFFECT Event fires, the Caster has not yet been charged the Mana, so the Mana is "added", but without the desired effect, and then the cost comes.
This may easily go unnoticed if the Casting Unit is not at its Maximum Mana, because it happens so fast it is often, if not always too fast to see.
Again, this is a well-known and long-solved situation and I don't pretend to have made an advancement here, but I do have a tried solution that I share here for the sake of spreading the knowledge, and because it relates to the secondary niche Ability property of a Channel-based ability with a "Follow Through Time", and how to cancel that Ability early and effectively ending the Unit's Follow Through Time.
It may be that the following concepts regarding cancelling an ability follow through apply to abilities other than Channel-based abilities, and perhaps for abilities that use a Duration in place of a Follow Through Time, but I have not tested for this.
Here is a copy of the snippets I used, followed by instructions for best practices for usage, as determined by my testing:
(Explaination is within the Script as comments)
In short there are Three useful functions for certain situations.
NONE of these functions will work properly if called before the Spell's Effect starts.
(Use these only after EVENT_PLAYER_UNIT_SPELL_EFFECT)
Details for each Function:
Edit - I noticed something I had to fix: I had to update because the original snippet I shared was giving the Caster maximum mana when the Caster's pre-cast mana amount was ( Max - X ), where X is the mana cost to be refunded.
EDIT: Some forgotten mentions:
Thanks for reading, I appreciate any feedback.
I'm wondering where the best place to post this is, but I think this is the right place to start, in case there is already a library that contains this or if there is a library this would fit well in.
I've recently come across a niche-scenario that concerns any/all Channel-based abilities in regard to Refunding Mana, as well as a secondary niche-scenario which may occur depending on a channel-based ability's "Follow Through Time", which requires some special treatment when it comes to triggering said ability.
The issue with Refunding Mana for a Channel-based ability is a well known one, described well here in this thread from five years ago.
If there is a better solution than the one I am about to share, I would like to know it, if you know a better way to handle this please share, there are a few ways I have found of dealing with it, some are better than others, depending on some finer details.
Some education:
- When an Ability "Starts the Effect" for a Casting Unit, the Mana is charged to the Casting Unit immediately AFTER the Event fires. (additional source: This thread from ten years ago).
- If the Casting Unit's Mana is at its Maximum, you need to use a timer or TriggerSleepAction' ("call TriggerSleepAction( 0.00 )" works)
- Timers are the superior solution because TriggerSleepAction is much slower, a TSA argument of 0.00 does not wait 0.00 seconds
- A timer with a runtime of 0.00 will work!
The 'issue' is that when you try to refund the mana, if the unit is at maximum mana, the unit will not be refunded unless you use either a timer or TriggerSleepAction to ensure that the mana has actually been lost.
The problem is that immediately as the EVENT_PLAYER_UNIT_SPELL_EFFECT Event fires, the Caster has not yet been charged the Mana, so the Mana is "added", but without the desired effect, and then the cost comes.
This may easily go unnoticed if the Casting Unit is not at its Maximum Mana, because it happens so fast it is often, if not always too fast to see.
Again, this is a well-known and long-solved situation and I don't pretend to have made an advancement here, but I do have a tried solution that I share here for the sake of spreading the knowledge, and because it relates to the secondary niche Ability property of a Channel-based ability with a "Follow Through Time", and how to cancel that Ability early and effectively ending the Unit's Follow Through Time.
It may be that the following concepts regarding cancelling an ability follow through apply to abilities other than Channel-based abilities, and perhaps for abilities that use a Duration in place of a Follow Through Time, but I have not tested for this.
Here is a copy of the snippets I used, followed by instructions for best practices for usage, as determined by my testing:
(Explaination is within the Script as comments)
JASS:
library CAS // Common Ability Systems
globals
private constant timer CAS_MR_T = CreateTimer()
private integer CAS_MR_N = 0
private integer array CAS_MR_A
private unit array CAS_MR_U
endglobals
private function CAS_MR_TF takes nothing returns nothing
local integer i = 0
loop
call SetUnitState( CAS_MR_U[i], UNIT_STATE_MANA, GetUnitState( CAS_MR_U[i], UNIT_STATE_MANA ) + CAS_MR_A[i] )
set CAS_MR_U[i] = null
set i = i + 1
exitwhen i > CAS_MR_N
endloop
set CAS_MR_N = 0
endfunction
private function CAS_MR_AddRefundUnit takes unit trgt, integer amnt returns nothing
set CAS_MR_U[CAS_MR_N] = trgt
set CAS_MR_A[CAS_MR_N] = amnt
set CAS_MR_N = CAS_MR_N + 1
set trgt = null
call TimerStart( CAS_MR_T, 0.00, false, function CAS_MR_TF )
endfunction
// CAS_MR_RefundActions should be called before CAS_FT_UnitStopAbility
// otherwise the mana cost will return 0 for BlzGetUnitAbilityManaCost
// seemingly because GetSpellAbilityId will be lost, I didn't test it.
// This must be called as an action to get the abilcode & TriggerUnit.
// If it can refund Unit's Mana immediately, we will avoid the hoopla.
function CAS_MR_RefundActions takes nothing returns nothing
local integer cost = BlzGetUnitAbilityManaCost( GetTriggerUnit(), GetSpellAbilityId(), GetUnitAbilityLevel( GetTriggerUnit(), GetSpellAbilityId() ) - 1 )
local real maxMana = GetUnitState( GetTriggerUnit(), UNIT_STATE_MAX_MANA )
local real mana = GetUnitState( GetTriggerUnit(), UNIT_STATE_MANA ) + cost
if mana < maxMana then
call SetUnitState( GetTriggerUnit(), UNIT_STATE_MANA, mana )
else
call CAS_MR_AddRefundUnit( GetTriggerUnit(), cost )
endif
endfunction
// For channel based abilities, this will only interupt the Follow Through
// Time if it is called on or after the Spell's Effect Event. If called on
// before that, it is too early and a Unit will be uneffected. This relies
// on the Spell being removed on or after the Spell's Effect Event firing.
// It would seem that if the Unit gets its Ability "Stopped" at an earlier
// time it will continue with Follow Through like nothing happened at all.
function CAS_FT_UnitStopAbility takes unit u, integer abilcode returns boolean
local integer abillvl = GetUnitAbilityLevel( u, abilcode )
if UnitRemoveAbility( u, abilcode ) and UnitAddAbility( u, abilcode ) then
if SetUnitAbilityLevel( u, abilcode, abillvl ) == abillvl then
set u = null
return true
endif
endif
set u = null
return false
endfunction
// This function is meant to ensure one function can simultaneously refund
// and stop an ability, to avoid the chance of wrongly ordered functions.
// This must be called as an action to get the abilcode & TriggerUnit.
function CAS_FT_CancelAbilityActions takes nothing returns nothing
call CAS_MR_RefundActions()
call CAS_FT_UnitStopAbility( GetTriggerUnit(), GetSpellAbilityId() )
endfunction
endlibrary
In short there are Three useful functions for certain situations.
NONE of these functions will work properly if called before the Spell's Effect starts.
(Use these only after EVENT_PLAYER_UNIT_SPELL_EFFECT)
Details for each Function:
- CAS_MR_RefundActions() - no arguments, uses Event Responses
- This must be called as an Event Response (as a Trigger Action).
- This is intentional, because it is not necessary whatsoever unless as a response to EVENT_PLAYER_UNIT_SPELL_EFFECT.
- This may work (I think it is most likely) in a Trigger Condition - I have not tested by see no reason why it should not.
- CAS_FT_UnitStopAbility( u, abilcode ) - arguments: unit u, integer abilcode
- This may be called at any time during the Follow Through Time of an Ability, as long as you know the Unit and the Ability's abilcode.
- This functionality is to allow an Ability to be stopped midway in its Follow Through Time, long after the Spell's Effect Event fires.
- This may be desired without a Mana Refund, if the Caster was able to use its Ability sufficiently.
- This function will temporarily cause CAS_MR_RefundActions() to be unable to Get the Ability's Mana Cost.
- This function should only be used in the same function as CAS_MR_RefundActions() if it is read AFTER CAS_MR_RefundActions()!
- CAS_FT_UnitCancelAbility() - no arguments, uses Event Responses
- This must be called as an Event Response (as a Trigger Action).
- This is intentional, because it is not necessary whatsoever unless as a response to EVENT_PLAYER_UNIT_SPELL_EFFECT.
- This may work (I think it is most likely) in a Trigger Condition - I have not tested by see no reason why it should not.
- This function exists solely for the purpose of calling CAS_RM_RefundActions() and CAS_FT_UnitStopAbility(u,abilcode) in that order
- This is not an essential function if the system is used considerately.
Edit - I noticed something I had to fix: I had to update because the original snippet I shared was giving the Caster maximum mana when the Caster's pre-cast mana amount was ( Max - X ), where X is the mana cost to be refunded.
EDIT: Some forgotten mentions:
- Cancelling/Stopping an Ability this way does not interrupt the Casting Unit's Order Queue.
- Cancelling/Stopping an Ability at the "Begins to Cast" or "Begins Channeling" event may have varying results:
- The Unit becomes somewhat locked, it would be maybe a good way to suspend a unit if done right.
- A Unit will be in the semi-locked state for the duration of its Follow Through - it is unable to move if given a move, patrol, or attack order, nor can it take any other orders and it will not stop or hold-position, although these two icons alone will glow if the Unit is so ordered.
- If given an order to stop or to hold position, the Unit will become "semi-unlocked"
- From the semi-unlocked state, if a Unit is given a move, patrol, or attack order, or any other order other than a stop or hold position order, it will follow that new order and stop following through the cancelled Ability.
- If a semi-unlocked Unit is given a stop or hold-position order, it will change nothing; the Unit will remain in its semi-unlocked state, still following through until given a non-stop/hold-position order.
- The Unit becomes somewhat locked, it would be maybe a good way to suspend a unit if done right.
- When testing and Cancelling/Stopping Big Bad Voodoo at the typical Start of Effect Event, the Player had the capability of initiating an uncancellable Big Bad Voodoo "Aura" that would remain effective even while the Unit was moving and attacking and being given orders: if the Player Queued an Order other than stop/hold-position to be carried out after the cancelled Big Bad Voodoo (using the [SHIFT]+CLICK Order Queue), the Big Bad Voodoo would remain active in this "uncancellable" state - I didn't test the duration of the uncancelled Big Bad Voodoo.
- I expect strange results for other Abilities that require a sustained channel effect.
- The Mana Refund is safe in all cases (when used at the Start of Effect Event - before this point is too early).
- The Cancelling and Stopping of Abilities can only be guaranteed to be safe if it is used with Channel-based Trigger Abilities that are being handled properly and do not otherwise interfere with this system in their process.
- Non-Channel-based Abilities that are Cancelled or Stopped may have strange effects, some might be interesting to mess with, like that Big Bad Voodoo one, others might have adverse effects, like that Big Bad Voodoo one. Be warned!
Thanks for reading, I appreciate any feedback.
Last edited: