channeled spells

Grundy

Ultra Cool Member
Reaction score
35
does anyone have a good way of doing custom channeled spells?

heres an example of what i mean, while the unit channels a spell, it deals damage to enemy units in the targetted area, each second increases the amount of damage done, and as soon as the unit stops channeling the damage stops. for a regular spell this is really easy

e: unit begins the effect of a spell
c: ability being cast equal to plague mist
a:
Code:
local location targetloc = GetSpellTargetLoc()
local group targets = CreateGroup()
local unit caster = GetSpellAbilityUnit()
local unit tempTarget
local integer level = GetUnitAbilityLevel( caster, GetSpellAbilityId() )
local real dmgAmount = 50
local integer count = 0
local player owner = GetOwningPlayer( caster )
loop
exitwhen (count == (level+4))
    call TriggerSleepAction(1.0)
    call GroupEnumUnitsInRangeOfLoc( targets, targetloc, 200, null )
    loop
        set tempTarget = FirstOfGroup( targets )
    exitwhen ( tempTarget == null )
        call GroupRemoveUnit( targets, tempTarget )
        if ( IsUnitEnemy( tempTarget, owner ) ) then
            call UnitDamageTarget( caster, tempTarget, dmgAmount, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_SONIC, WEAPON_TYPE_WHOKNOWS )
        endif
    endloop
    set dmgAmount = dmgAmount + 10
    set count = count+1
endloop
call DestroyGroup( targets )
call RemoveLocation( targetloc )
set caster = null
set tempTarget = null
set owner = null

this will keep going even after the unit stops channeling, so i need some way to check if the unit is still channeling after the wait like...


global Unit Group called ChannelingGroup
Code:
start channeling
    Events
        Unit - A unit Begins channeling an ability
    Conditions
    Actions
        Unit Group - Add (Casting unit) to ChannelingGroup
Code:
stop channeling
    Events
        Unit - A unit Stops casting an ability
    Conditions
    Actions
        Unit Group - Remove (Casting unit) from ChannelingGroup
Code:
local location targetloc = GetSpellTargetLoc()
local group targets = CreateGroup()
local unit caster = GetSpellAbilityUnit()
local unit tempTarget
local integer level = GetUnitAbilityLevel( caster, GetSpellAbilityId() )
local real dmgAmount = 50
local integer count = 0
local player owner = GetOwningPlayer( caster )
loop
exitwhen (count == (level+4))
    call TriggerSleepAction(1.0)
//==================================================================
// checks if unit is still channeling
exitwhen ( IsUnitInGroup( caster, udg_ChannelingGroup ) == false )
//==================================================================
    call GroupEnumUnitsInRangeOfLoc( targets, targetloc, 200, null )
    loop
        set tempTarget = FirstOfGroup( targets )
    exitwhen ( tempTarget == null )
        call GroupRemoveUnit( targets, tempTarget )
        if ( IsUnitEnemy( tempTarget, owner ) ) then
            call UnitDamageTarget( caster, tempTarget, dmgAmount, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_SONIC, WEAPON_TYPE_WHOKNOWS )
        endif
    endloop
    set dmgAmount = dmgAmount + 10
    set count = count+1
endloop
call DestroyGroup( targets )
call RemoveLocation( targetloc )
set caster = null
set tempTarget = null
set owner = null
this kind of works... but its just ghetto-rigged to work, and it is easily broken. it only checks if the unit is currently channeling a spell, it doesnt have to be the same cast of the spell, and it doesnt even have to be the same spell thats being channeled. if a unit started channeling this spell and then immediately started channeling a new spell this trigger would still keep looping through because the unit would be readded to the ChannelingGroup, so its works but its not reliable. anyone have a better way?

...wish there was a
"((Unit) is channeling Ability) Equal to True" boolean comparison....


edit: ok so i found a better way, but its still not perfect:
Code:
local location targetloc = GetSpellTargetLoc()
local group targets = CreateGroup()
local unit caster = GetSpellAbilityUnit()
local unit tempTarget
local integer level = GetUnitAbilityLevel( caster, GetSpellAbilityId() )
local real dmgAmount = 50
local integer count = 0
local player owner = GetOwningPlayer( caster )
local integer orderId = GetUnitCurrentOrder( caster )
loop
exitwhen (count == (level+4))
    call TriggerSleepAction(1.0)
//==================================================================
// checks if unit is still channeling
exitwhen ( not (GetUnitCurrentOrder(caster) == orderId) )
//==================================================================
    call GroupEnumUnitsInRangeOfLoc( targets, targetloc, 200, null )
    loop
        set tempTarget = FirstOfGroup( targets )
    exitwhen ( tempTarget == null )
        call GroupRemoveUnit( targets, tempTarget )
        if ( IsUnitEnemy( tempTarget, owner ) ) then
            call UnitDamageTarget( caster, tempTarget, dmgAmount, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_SONIC, WEAPON_TYPE_WHOKNOWS )
        endif
    endloop
    set dmgAmount = dmgAmount + 10
    set count = count+1
endloop
call DestroyGroup( targets )
call RemoveLocation( targetloc )
set caster = null
set tempTarget = null
set owner = null

so now it has to be the same spell channeled, but if they stop and rechannel the same spell this trigger will keep going plus start a new instance of itself so its still not exactly right
 

AceHart

Your Friendly Neighborhood Admin
Reaction score
1,494
> "((Unit) is channeling Ability) Equal to True" boolean comparison....

exitwhen GetUnitCurrentOrder(u) != OrderId("tranquility")

Where "u" is the Casting Unit, and "tranquility" the base spell's order.
 

Grundy

Ultra Cool Member
Reaction score
35
if you read the bottom of my first post, thats what i have, but a unit can stop and recast the spell again which should break out of the trigger and start over again but instead it stays inside the same trigger and creates a new instance of it

in the example ability it would be like this

second 1: 50 damage
second 2: 60 damage
second 3: 70 damage
second 3.5: unit stops and recasts the channeled spell
second 4: (when it check, the unit's order is the same so the trigger thinks it never stopped) 80 damage, (plus since he started a new cast, it starts over in a new instance) 50 damage
second 5: 90 damage, 60 damage
second 6: 70 damage
second 7: 80 damage
second 8: 90 damage
total of: 700 damage, but it should be

second 1: 50 damage
second 2: 60 damage
second 3: 70 damage
second 3.5: unit stops and recasts the channeled spell
second 4: (first cast should stop) 0 damage, (plus since he started a new cast, it starts over in a new instance) 50 damage
second 5: 60 damage
second 6: 70 damage
second 7: 80 damage
second 8: 90 damage
total should be: 530 damage
 

AceHart

Your Friendly Neighborhood Admin
Reaction score
1,494
> if you read the bottom of my first post, thats what i have

Well, that test does work.

Now, there's a wait and an inner loop...

This test should run before the wait, after the wait and inside the inner loop.
Or, rather, before the wait and in the inner loop.


Other than that, I sure hope your spell has a higher cooldown than duration though.
It should simply be impossible for one same unit to recast a currently running spell...
 

emjlr3

Change can be a good thing
Reaction score
395
create a local trigger that runs when the units stops casting the ability, and stop all your actions through that
 

Grundy

Ultra Cool Member
Reaction score
35
local trigger!? how do you do that? i never heard of that or seen it done before
 

Rabarber

You can change this now in User CP.
Reaction score
94
I bet he only means making all variables local.

(local trigger T :p)

Actually, that's great :) You can have the trigger set as a local variable, running itself :p
Or... isn't that what it already does? :p
 

Rinpun

Ex TH Member
Reaction score
105
To address some things:

1) Yes, you can have a local trigger. It's called local trigger some_trig. Then, you gotta add an event to it and an action and stuff and voila! You have a trigger that writes a trigger :p

2) If you're going with a for loop, that's perfectly fine, but it takes .30 seconds minimum to loop through and check. I suggest using a local timer with a local game cache (basically through KaTTana's system as his does it for you very easily). Then, I would give the timer it's own function and pass it handles to everything you're interested in. For instance, I think you could make a local variable that would basically be the time (in seconds) you're interested in making the spell take, and then divide it by the rapidity of the timer (probably should be around .05, this works fantastic I find) in order to find the maximum amount of times the trigger should run. Have it increment a local integer and load it back into the local game cache. Then, once the integer meets its maximum point, stop the spell.

Also, besides that kind of ending, you probably want it to stop after the unit moves. This can be done by saving the caster's X and Y position as "real" values in the game cache. Then, you load them upon each timer start and set them to be the "old" x and y. You should then take the x and y position of the caster and name them the "new" x and y. If the values are different, the unit has moved and the spell effect should be stopped.

With a timer system, I would of course make each effect of the spell inside the timer itself, so if all of the incrementing went as planned and the spell should still go on, the effect should go on as well inside the timer. This would mean that to stop the effect all you have to do is destroy the timer and clear the local game cache.
 

Grundy

Ultra Cool Member
Reaction score
35
the 1 problem with all of this is...
say i have a unit, Unit_01 and it has ability Channel with a target point and 10 second followthrough, 5 second cooldown time

Unit_01 starts channeling Channel where he is standing
5 seconds later it is refreshed and still channeling
its current orderid is the order for the spell
you click the icon for the ability and target an area on the opposite side of the map - unit starts moving to where you click untill he gets close enough to start channeling again, while is is moving there his current orderid is the order for the spell, so even though he is moving and not casting he still has the same order so all these triggers that check orders wouldnt work. only thing i can think of is

creating a local trigger with
event: for unit is issued an order targetting an object, unit, and no target.
condition: ordered unit is the caster.
action: i have no idea, something that would tell the other trigger that the channeling should stop but i dont know what, can a local trigger access local variables in the function its created in?

function some_function takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local boolean stillChanneling = true
local trigger trg_getOrder = CreateTrigger( )
call TriggerRegisterUnitEvent( trg_getOrder, caster, EVENT_UNIT_ISSUED_TARGET_ORDER )
call TriggerRegisterUnitEvent( trg_getOrder, caster, EVENT_UNIT_ISSUED_POINT_ORDER )
call TriggerRegisterUnitEvent( trg_getOrder, caster, EVENT_UNIT_ISSUED_ORDER )
call TriggerAddAction( trg_getOrder, Trig_getOrder_Actions )
.....
....
....
endfunction

is there anyway i can make the trg_getOrder trigger change the local boolean? i dont think there is but maybe someone knows some trick to make it work or something, then if the unit is issued any new order then it clearly has to stop channeling the spell
 

emjlr3

Change can be a good thing
Reaction score
395
Code:
local trigger trig = CreateTrigger()
local triggeraction ta = TriggerAddAction( trig, Your_Actions_Function)

call TriggerRegisterUnitEvent( trig,Unit, EVENT_UNIT_SPELL_ENDCAST  )

in your actions function, stop your timer and flush its locals to destroy leaks

this will run when the unit stop casting the ability, so basically when ever he does anything other then channel that ability, such as start channeling again

just I like I said above
 

Grundy

Ultra Cool Member
Reaction score
35
sorry emjlr3 i know you're trying to help and you're probably right with what you're saying but for some reason im just not getting it.
 

Grundy

Ultra Cool Member
Reaction score
35
ok i think i got it now...

create 1 local trigger with no actions and no conditions, event is the unit event EVENT_UNIT_SPELL_ENDCAST so the local trigger will automatically execute when this spell stops channeling regardless of what the units current order is. the order can be anything but if unit stops casting the spell, the local trigger will know. then inside the loop you check how many times the local trigger has executed with the GetTriggerExecCount() function, if it is greater than 0, that means the unit stopped casting so you exit the loop and do your trigger clean up functions, like removing locations, destroying effects, destroy the local trigger, etc...

Code:
local location targetloc = GetSpellTargetLoc()
local group targets = CreateGroup()
local unit caster = GetSpellAbilityUnit()
local unit tempTarget
local integer level = GetUnitAbilityLevel( caster, GetSpellAbilityId() )
local real dmgAmount = 50
local integer count = 0
local player owner = GetOwningPlayer( caster )
local integer orderId = GetUnitCurrentOrder( caster )
local trigger trg_endSpell = CreateTrigger( )
call TriggerRegisterUnitEvent( trg_endSpell, caster, EVENT_UNIT_SPELL_ENDCAST )
loop
exitwhen (count == (level+4))
    call TriggerSleepAction(1.0)
//==================================================================
// checks if unit is still channeling
exitwhen ( GetTriggerExecCount( trg_endSpell ) > 0 )
//==================================================================
    call GroupEnumUnitsInRangeOfLoc( targets, targetloc, 200, null )
    loop
        set tempTarget = FirstOfGroup( targets )
    exitwhen ( tempTarget == null )
        call GroupRemoveUnit( targets, tempTarget )
        if ( IsUnitEnemy( tempTarget, owner ) ) then
            call UnitDamageTarget( caster, tempTarget, dmgAmount, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_SONIC, WEAPON_TYPE_WHOKNOWS )
        endif
    endloop
    set dmgAmount = dmgAmount + 10
    set count = count+1
endloop
call DestroyTrigger( trg_endSpell )
call DestroyGroup( targets )
call RemoveLocation( targetloc )
set caster = null
set tempTarget = null
set owner = null

i think this is probably the best and easiest way to do this
best because it doesnt check orders periodically which can possibly still be the same even if the unit stopped casting, so it is more reliable. and easy because theres only 3 extra lines of code and 1 modified line to make it work.

local trigger trig_name = CreateTrigger()
...
call TriggerRegisterUnitEvent( trig_name, unit, EVENT_UNIT_SPELL_ENDCAST )
...
exitwhen( GetTriggerExecCount( trig_name ) > 0 )
...
DestroyTrigger( trig_name )

just 1 more little thing...does anyone see any memory leaks in that trigger? i think i took care of everything but i sometimes miss things
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top