First Time Timers +bit more

MagnaGuard

Active Member
Reaction score
49
JASS:
scope heatstruck
globals
    private unit attacked = GetTriggerUnit()
    private unit attacking = GetAttacker()
endglobals
private function Trig_Heat_Struck_Conditions takes nothing returns boolean
    return  GetUnitAbilityLevel(attacking,'A000')>=1 
endfunction

private function remove takes nothing returns nothing
    call UnitRemoveAbility(attacked,'A002')
endfunction

private function Trig_Heat_Struck_Actions takes nothing returns nothing
local timer t=CreateTimer()
local real level=(GetUnitAbilityLevel(attacking,'A000'))*10
local integer random = GetRandomInt(1,100)
        if random <= level then
        call UnitAddAbility(attacked,'A002')
        call TimerStart(t,10,false,function remove)
        endif
    call DestroyTimer(t)
    set attacked = null
    set attacking = null
endfunction

//===========================================================================
private function InitTrig_Heat_Struck takes nothing returns nothing
    set gg_trg_Heat_Struck = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Heat_Struck, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddCondition( gg_trg_Heat_Struck, Condition( function Trig_Heat_Struck_Conditions ) )
    call TriggerAddAction( gg_trg_Heat_Struck, function Trig_Heat_Struck_Actions )
endfunction
endscope


So basically the trigger is supposed to be, if a certain unit attacks another with a skill, then give the attacked unit an ability to be weak to fire spells. I haven't used timers before so don't quite know if I did it properly.

However it hasn't worked yet, the ability is supposed to put a buff on the enemy to show it is weak.
 

_whelp

New Member
Reaction score
54
The globals should be set in the actions function, and they can't just pick up the triggering unit at start time, as the event is not run yet. A global timer would also be better, and if you don't make it global, you need to null it. It isn't MUI either.
 

Kenny

Back for now.
Reaction score
202
You can try this:

JASS:
scope Heatstruck

    globals
        private constant integer ABIL_ID  = 'A000' // Ability the attacker should have
        private constant integer BUFF_ID  = 'A002' // Ability to give the attacked unit.
        private constant real    INTERVAL = 0.05   // Interval for timer. (Needed quick interval for struct stack).
    endglobals
    
    // What units will set this off. So far only enemy, non-structure units can get the "buff".
    private function Filter_enemy takes unit attacked, unit attacker returns boolean
        return IsUnitEnemy(attacked,GetOwningPlayer(attacker)) and IsUnitType(attacked,UNIT_TYPE_STRUCTURE) == false
    endfunction
    
    // Don't touch past here.
    private struct Data
    
        unit    attacked = null
        real    time     = 0.00
        boolean stop     = false
        
        static Data     array D
        static integer  D_total = 0
        static timer    Timer   = null
        static boolexpr Filt    = null
        
        static method filt takes nothing returns boolean
            return true
        endmethod
        
        method onDestroy takes nothing returns nothing
            if GetUnitAbilityLevel(.attacked,BUFF_ID) > 0 then
                call UnitRemoveAbility(.attacked,BUFF_ID)
            endif
            set .attacked = null
        endmethod
        
        static method update takes nothing returns nothing
            local integer i = 1
            
            loop
                exitwhen i > Data.D_total
                
                if Data.D<i>.stop or Data.D<i>.time &lt;= 0.00 or GetWidgetLife(Data.D<i>.attacked) &lt; 0.407 then
                    call Data.D<i>.destroy()
                    set Data.D<i> = Data.D[Data.D_total]
                    set Data.D_total = Data.D_total - 1
                    set i = i - 1
                endif
                
                set i = i + 1
            endloop
            
            if Data.D_total &lt;= 0 then
                call PauseTimer(Data.Timer)
                set Data.D_total = 0
            endif
        endmethod
        
        static method create takes nothing returns Data
            local Data    d = Data.allocate()
            local unit    u = GetAttacker()
            local integer i = GetUnitAbilityLevel(u,ABIL_ID) * 10
            
            set d.attacked = GetTriggerUnit()
            
            if GetRandomInt(1,100) &lt;= i then
                call UnitAddAbility(d.attacked,BUFF_ID)
            else
                set d.stop = true
            endif
            
            set Data.D_total = Data.D_total + 1
            set Data.D[Data.D_total] = d
            if Data.D_total == 1 then
                call TimerStart(Data.Timer,INTERVAL,true,function Data.update)
            endif
            
            return d
        endmethod
        
        static method conditions takes nothing returns boolean
            return GetUnitAbilityLevel(GetAttacker(),ABIL_ID) &gt; 0 and Filter_enemy(GetTriggerUnit(),GetAttacker())
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger trig = CreateTrigger()
            local integer i    = 0
            
            set Data.Timer = CreateTimer()
            set Data.Filt  = Filter(function Data.filt)
            
            loop
                call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_ATTACKED,Data.Filt)
                set i = i + 1
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
            
            call TriggerAddCondition(trig,Condition(function Data.conditions))
            call TriggerAddAction(trig,function Data.create)
        endmethod
        
    endstruct
            
endscope

</i></i></i></i></i>


It should compile, but I'm not sure if it will work as you want it. However, it is MUI, leakless and should be lagless :).
 

MagnaGuard

Active Member
Reaction score
49
but I'm not sure if it will work as you want it
It works thanks +rep :thup: Just learning structs so . . .
JASS:
            loop
                exitwhen i &gt; Data.D_total
                
                if Data.D<i>.stop or Data.D<i>.time &lt;= 0.00 or GetWidgetLife(Data.D<i>.attacked) &lt; 0.407 then
                    call Data.D<i>.destroy()
                    set Data.D<i> = Data.D[Data.D_total]
                    set Data.D_total = Data.D_total - 1
                    set i = i - 1
                endif</i></i></i></i></i>

>> I don't understand what that is supposed to do.

JASS:
            local Data    d = Data.allocate()
            local unit    u = GetAttacker()
            local integer i = GetUnitAbilityLevel(u,ABIL_ID) * 10
            
            set d.attacked = GetTriggerUnit()
            
            if GetRandomInt(1,100) &lt;= i then
                call UnitAddAbility(d.attacked,BUFF_ID)
            else
                set d.stop = true
            endif
            
            set Data.D_total = Data.D_total + 1
            set Data.D[Data.D_total] = d
            if Data.D_total == 1 then
                call TimerStart(Data.Timer,INTERVAL,true,function Data.update)
            endif
            
            return d

>> What does returning data do for the trigger?

Also I want to add a special effect because it's not working in the object editor, anyway want to show if someone has the weakness on or not.
 

Kenny

Back for now.
Reaction score
202
>> I don't understand what that is supposed to do.

That part of the script is just checking when the spell should end. It just checks if the time is up, or if the unit dies.

Returning Data is necessary for create methods, its just how they have been designed.

And yes there is a way to add a special effect. If I have time I will do that for you.

Also, would you like to make it check whether or not the attacked unit already has the buff, then not add the ability if it has it? Because, as of now, the spell will try to add the same buff to the unit even though it already has it. And I'm pretty sure you cant add the same abilities to a unit, so something might happen.
 

MagnaGuard

Active Member
Reaction score
49
Also, would you like to make it check whether or not the attacked unit already has the buff, then not add the ability if it has it? Because, as of now, the spell will try to add the same buff to the unit even though it already has it. And I'm pretty sure you cant add the same abilities to a unit, so something might happen.

Yes could you do that please? I would like to see the code ;) and learn from it.
 

Kenny

Back for now.
Reaction score
202
Okay this is what i have now:

JASS:
scope Heatstruck

    globals
        private constant integer ABIL_ID  = &#039;A000&#039;  // Ability the attacker should have
        private constant integer BUFF_ID  = &#039;A002&#039;  // Ability to give the attacked unit.
        private constant real    INTERVAL = 0.05    // Interval for timer. (Needed quick interval for struct stack).
        private constant string  EFFECT   = &quot; &quot;     // Your effect should go here.
        private constant string  POINT    = &quot;chest&quot; // Attachment point of above effect.
    endglobals
    
     // Chance for spell to proc.
    private function Chance takes integer lvl returns integer
        return 10 * lvl
    endfunction
        
    // What units will set this off. So far only enemy, non-structure units can get the &quot;buff&quot;.
    private function Filter_enemy takes unit attacked, unit attacker returns boolean
        return IsUnitEnemy(attacked,GetOwningPlayer(attacker)) and IsUnitType(attacked,UNIT_TYPE_STRUCTURE) == false
    endfunction
    
    // Don&#039;t touch past here.
    private struct Data
    
        unit    attacked = null
        effect  sfx      = null
        real    time     = 0.00
        
        static Data     array D
        static integer  D_total = 0
        static timer    Timer   = null
        static boolexpr Filt    = null
        
        static method filt takes nothing returns boolean
            return true
        endmethod
        
        method onDestroy takes nothing returns nothing
            if GetUnitAbilityLevel(.attacked,BUFF_ID) &gt; 0 then
                call UnitRemoveAbility(.attacked,BUFF_ID)
                call DestroyEffect(.sfx)
            endif
            set .attacked = null
            set .sfx      = null
        endmethod
        
        static method search takes unit whichunit returns boolean
            local integer i = 1
            
            loop
                exitwhen i &gt; Data.D_total
                if Data.D<i>.attacked == whichunit then
                    return true
                endif
                set i = i + 1
            endloop
            
            return false
        endmethod
        
        static method update takes nothing returns nothing
            local integer i = 1
            
            loop
                exitwhen i &gt; Data.D_total
                
                if Data.D<i>.time &lt;= 0.00 or GetWidgetLife(Data.D<i>.attacked) &lt; 0.407 then
                    call Data.D<i>.destroy()
                    set Data.D<i> = Data.D[Data.D_total]
                    set Data.D_total = Data.D_total - 1
                    set i = i - 1
                endif
                
                set i = i + 1
            endloop
            
            if Data.D_total &lt;= 0 then
                call PauseTimer(Data.Timer)
                set Data.D_total = 0
            endif
        endmethod
        
        static method create takes nothing returns Data
            local Data d = Data.allocate()
            
            set d.attacked = GetTriggerUnit()
            
            call UnitAddAbility(d.attacked,BUFF_ID)
            call AddSpecialEffectTarget(EFFECT,d.attacked,POINT)
            
            set Data.D_total = Data.D_total + 1
            set Data.D[Data.D_total] = d
            if Data.D_total == 1 then
                call TimerStart(Data.Timer,INTERVAL,true,function Data.update)
            endif
            
            return d
        endmethod
        
        static method conditions takes nothing returns boolean
            local unit    u = GetTriggerUnit()
            local unit    n = GetAttacker()
            local integer i = GetUnitAbilityLevel(n,ABIL_ID)
            local boolean t = i &gt; 0 and Filter_enemy(u,n) and Data.search(u) == false and GetRandomInt(1,100) &lt;= Chance(i)
            
            set u = null
            set n = null
            
            return t
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger trig = CreateTrigger()
            local integer i    = 0
            
            set Data.Timer = CreateTimer()
            set Data.Filt  = Filter(function Data.filt)
            
            loop
                call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_ATTACKED,Data.Filt)
                set i = i + 1
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
            
            call TriggerAddCondition(trig,Condition(function Data.conditions))
            call TriggerAddAction(trig,function Data.create)
        endmethod
        
    endstruct
            
endscope

</i></i></i></i></i>


Note: This was done on notepad. I'm pretty sure it should compile, I'm just not sure if it will work.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
Lol,
"kenny!", Why u do useless things? A simple trigger make it become huge
 

Kenny

Back for now.
Reaction score
202
His "simple" trigger had quite a few things wrong with it, and it is not in my nature to do something for someone half-assed.

Do you mind explaining the useless things please?

I admit, it could be made short, but you shouldn't measure the complexity of a script by its length. You shouldn't really measure any script by length.

The only thing that could be changed is the struct stack. If i knew what systems he used, I could make it shorter, but it is still efficient as it is now. Also if it didn't use a struct stack, it would probably have to use PUI, which would suck.

Anyway its only like 125 lines or something by the look of it :p.
 

wraithseeker

Tired.
Reaction score
122
Why would PUI suck? I think its useful and since MagmaGuard is using JNPG, I'm sure it wouldn't hurt to add in a library.

It looks good but why are you not using I heard from Flare that it does not leak but only leak in Enum functions.



For the initialization of the timers, you could have done it at the top :p

Data.D_total could have been .D_total. I fixed my EKB you see, just a simple and dumb maths issues.

set Data.D_total = 0 is used for what? the timer will never go to -1 at most it will go to 0 unless it bugs.

Other then that it's good kenny!.
 

Kenny

Back for now.
Reaction score
202
Why would PUI suck?

The library itself is pretty awesome, I just think it would be a waste using it for something so simplistic. Plus I dislike attaching to units anyway. What if he was using userdata or whatever it is in one of his other spells? Maybe in some GUI ones... It would become very difficult to change everything to suit one spells needs.

The way I did it ensured that he could just copy and paste the script and it would work fine. I could have made it use TimerUtils, PUI and GTrigger, but I didn't for the sake of simplicity. (Even if it means a little code bloating)

I heard from Flare that it does not leak but only leak in Enum functions.

Flare is correct. Using a null boolexpr for Event registration doesn't leak. But it is a question of person preference. I dislike red stuff in my code. And inlining it is not slower, nor faster, so it shouldn't be anyones concern.

For the initialization of the timers, you could have done it at the top

Again, personal preference. I never use natives in global blocks. Anyway, thats what the onInit function is for, to initialize things :p.

Data.D_total could have been .D_total.

Again, personal preference.

.D_total is a static member of the struct, therefore I use Data.D_total to show that it is static. Its how it should be anyway.

set Data.D_total = 0 is used for what?

Precautionary usage. Gives me peace of mind.

When it comes down to it, everything you spotted is just personal preference. I like things to look neat.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top