Snippet CustomStun System

Flare

Stops copies me!
Reaction score
662
JASS:
    static method stununit takes nothing returns nothing
        local data d
        call ForGroup(d.g,stun.Stun)
    endmethod

1) Uninitialized variable error, thread crashes there at the ForGroup call
2) Why are you using a group? If you are using a timer per instance, just handle the data within the timer callback i.e.
JASS:
static method stununit takes nothing returns nothing
  local data d = GetTimerData (GetExpiredTimer ()) //this data, and the data attached to the unit, should be the same if things are done right
  
//assuming everything is in order, which it should be
  call UnitRemoveAbility (whichUnit, buffId)
  call d.release ()
//and any other cleanup that may be necessary
endmethod


JASS:
method Stun takes nothing returns nothing

Anything that requires a code type (i.e. ForGroup, TimerStart, and some others) cannot have a function that takes parameters. ALL non-static methods take a parameter (integer this), you just don't see it when you are calling them i.e.
JASS:
call data.dynMethod ()

would compile to something like
JASS:
call structName__dynMethod (data)
 

Flare

Stops copies me!
Reaction score
662
Attaching data without a separate system, is it? Well, dirt-basic method would be
JASS:
globals
  integer array Attached[size]
//For safety, you could just do what TimerUtils Blue does and change size to 408000 for a good degree of safety
//You could lower it to gain some speed, but there's no really good reason as to why you would need to that do
//as long as you don't exceed the first array bounds, speed isnt really a big issue
  constant integer FIRST_INDEX = 0x100000
endglobals

function H2I takes handle h returns integer
  return h
  return null
endfunction

function SetData takes handle h, integer i returns nothing
  set Attached[H2I (h) - FIRST_INDEX] = i
endfunction

function GetData takes handle h returns integer
  return Attached[H2I (h) - FIRST_INDEX]
endfunction


That'd be the very simplest method of doing it without another system

Also, you could change the taken type in Get/Set from handle to timer if you wish, it's not really a very big deal though

EDIT: Oops, forgot the subtraction - fixed
 

wraithseeker

Tired.
Reaction score
122
Actually does this system really need a change? Try it out, my fps didn't drop alot even and it was accurate.

For that stuff, I might do that tomorrow, depends.
 

Flare

Stops copies me!
Reaction score
662
Actually does this system really need a change? Try it out, my fps didn't drop alot even and it was accurate.

For that stuff, I might do that tomorrow, depends.
How many units did you test with? The difference will be negligible with a minor number of instances - the main reason I would like to see attachment used is the fact that for most of the time the system spends being active, it's nothing nothing more than 'set var = var + increment', which is a waste. Why spend upwards of 90% of activity doing next to nothing? With timer per instance, it's one execution, and that's the end of it.

Single timer is fine for things that need the high frequency (such as knockbacks, since updating a sliding motion at anything greater than 0.03 or 0.04 seconds is a bad idea) since most (if not all) execution time is spent doing something useful. For things where you don't need high frequency (e.g. this), it means the timer isn't really doing anything (since the timer executes a number of times during that interval, but only 1 of those executions actually matters...)


As regards accuracy, there is always an inaccuracy with a single timer and multiple instances, and there is no way of getting around it (the degree of inaccuracy being TIMER_INTERVAL seconds). Let's say you apply a stun 3 seconds into the game - system starts the timer for the first time, no inaccuracy here (since the timer will always be accurate for the first instance after a rest period i.e. where the timer was never started, or after it was paused). Now, another stun is applied at 3.01 seconds into the game. Now, since the timer is currently running, we have to wait 0.02125 seconds before the timer can execute and 'pick up' the instance, and so on.

It's a slight inaccuracy, but it's false to say that it's completely accurate


Now, provided I'm not lazy, time to abuse the interface =)

EDIT: ...
Rules said:
Every submission must have a demo map
Ye, I see none - get it sorted, ASAP
 

Jesus4Lyf

Good Idea™
Reaction score
397
>there is always an inaccuracy with a single timer and multiple instances
That's a fallacy. There is actually a very, very nice way to fix that. But there's a bunch of unknown timer bugs that smack you in the face if you do. :D (I've done it.)

In regards to your timer loop... Geez. Use KT2 instead. You said you needed more than 256 timers so you can't use TU, but internally KT2 implements a highly optimised version of your timer loop (it does it in such a way that it doesn't have to do that increment every second, and it is also more accurate so you don't have to worry about Flare).

Of course, you'd need to remove your onrun method. Good thing.

This is what I'd call an overcomplicated system. And honestly, I couldn't use it knowing that interally it spams ticks internally. I'd sooner write my own in 10-20 lines.
 

wraithseeker

Tired.
Reaction score
122
KT2? Yeah it's nice but I don't like the fact I saw it in your documention that timer cannot be 10.24 seconds.

Let's have a example, mapper A wants to do a stun that stuns for 50 seconds, well it would screw up doesn't it?

Onrun method was there for fun :p, will remove it.

Why is this overcomplicated?
 

Sim

Forum Administrator
Staff member
Reaction score
534
> KT2? Yeah it's nice but I don't like the fact I saw it in your documention that timer cannot be 10.24 seconds.

Isn't this in an earlier version?
 

wraithseeker

Tired.
Reaction score
122
I just opened the thread and looked at the code and saw that.

JASS:

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ KT ~~ Key Timers 2 ~~ By Jesus4Lyf ~~ Version 1.6.1 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is Key Timers?
//         - Key Timers attaches structs to timers, or more to the point timed
//           effects.
//         - You can specify different periods.
//         - Key Timers only uses one timer with one trigger per low period
//           to keep things efficient, especially within the looping.
//         - Key Timers alternatively uses one single timer for all higher periods
//           to allow accurate expirations in a stable and reasonably efficient fashion.
//
//    =Pros=
//         - Easy to use.
//         - Fastest attachment loading system (storing in parallel arrays).
//         - Fastest execution system for low periods (attaching all functions to one trigger).
//         - Allows multiple periods to be used.
//
//    =Cons=
//         - The code passed into KT2 must call KT_GetData exactly once.
//         - Periods must be a multiple of 0.00125 seconds. Not 0.007, for example.
//
//    Functions:
//         - KT_Add(userFunc, struct, period)
//         - KT_GetData returns the struct
//
//         - userFunc is to be a user function that takes nothing and returns boolean.
//           It will be executed by the system every period until it returns true.
//
//         - KT_GetData is to be used inside func, it will return the struct passed to
//           to the Add function. It must be called exactly once within the func.
//
//  Details:
//         - KT2 treats low periods and high periods differently, optimizing each
//           with appropriate speed and accuracy, although this effect is invisible
//           to you, the mapper.
//
//         - While func returns false the timer will continue to call it each period.
//           Once func returns true the instance will be detached from system.
//
//  Thanks:
//         - Daxtreme: For encouraging me to return to Key Timers 2, rather than
//           leave it to rot. His interest in the system restored it, and helped
//           it to become what it is now. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />
//
//         - Captain Griffen: For his work on Rapid Timers, demonstrating that it
//           is possible to attach all functions to one trigger, and that it is
//           indeed faster.
//
//         - Cohadar: Told me to make Key Timers without a textmacro.
//           Thanks to him for helping me with the original Key Timers system too.
//           Also, I&#039;d like to thank him for his work on Timer Ticker (TT) which
//           demonstrated how to use triggers/conditions in this sort of system,
//           which has been used in Key Timers 2.
//
//  How to import:
//         - Create a trigger named KT.
//         - Convert it to custom text and replace the whole trigger text with this.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library KT initializer Init
    ///////////////
    // Constants //
    ////////////////////////////////////////////////////////////////////////////
    // That bit that users may play with if they know what they&#039;re doing.
    // Not touching these at all is recommended.
    globals
        private constant real PERIODTHRESHOLD=0.3 // MUST be below 10.24 seconds.
        private constant real HT_MARGINOFERROR=0.01
    endglobals
 

Viikuna

No Marlo no game.
Reaction score
265
Actually, Im starting to believe that
JASS:
 
if this.timer==null then
   set this.timer=CreateTimer()
endif

together with gamecache timer attaching is the best way to do this.
 

Flare

Stops copies me!
Reaction score
662
And I remembered browsing through the snippet section and found some without maps.
1) This is more of a system than a snippet

2) This has Object Data - I tested last night with my own customized Storm Bolt and buff in a new demo map, and the system wasn't working properly (i.e. the buff wasn't getting removed and yes, I did have the constants configured properly, incase you think that may have been the issue). We need the stun ability/buff as it is found in the map you made it in. Unless I/we can see your demo map, there's no way to prove that the system actually works.

And the interface is completely useless right now. Do you really know how they work? Extending the struct that is extending the interface doesn't magically make interfaces work ¬_¬ My suggestion still stands, but now has further reason for why
1) It's very situational. Would you develop something just so that one person out of 100 could make the system work slightly differently to how it's supposed to?

2) The interface usage here doesn't even work...

That's a fallacy. There is actually a very, very nice way to fix that. But there's a bunch of unknown timer bugs that smack you in the face if you do.
Fixing something, only to result in bugs appearing? Ye, great fix there :D
 

wraithseeker

Tired.
Reaction score
122
It worked perfectly for me.


JASS:
////! external ObjectMerger w3h BPSE STNB fnam &quot;Custom Stun&quot;
////! external ObjectMerger w3a AHtb STUN abuf 1 STNB alev 1 aher 1 ahdu 1 0 adur 1 0 Htb1 1 0 amat &quot;&quot; amcs 1 0 anam &quot;Custom Stun&quot;
library CustomStun initializer Init uses PUI

    globals
        private constant real       INTERVAL    = 0.03125       //Interval of the timer, thus, delta value
        private constant integer    Dummy_Id    = &#039;h003&#039;    //Dummy ID and did you set all the animation speed and such to 0? Else there is no MUI.
        private constant integer    Bolt_Id     = &#039;STUN&#039;    //Storm bolt well the stormbolt! For me it&#039;s a hero ability that only has targets allowed set to not self. ID
        private constant integer    Buff_Id     = &#039;B005&#039;    //Stun buff ID // your buff ID!! just copy and paste a Stunned(Pause) buff and assign its&#039; ID to this.
        private unit stunner
    endglobals
    
private interface face
    method OnStart takes nothing returns nothing defaults nothing
    method OnStop takes nothing returns nothing defaults nothing
    method OnRun takes nothing returns nothing defaults nothing
endinterface
    
    
struct stun extends face
    //! runtextmacro PUI()
    real Stunned = 0.
    unit target
    real duration
   static timer Timer = CreateTimer()
    static stun array D
    static integer Count = 0
        
    static method create takes unit target returns stun
        local stun d = stun.allocate()
        set d.target = target
        set d.duration = 0.
        if stun.Count == 0 then
            call TimerStart(stun.Timer,INTERVAL,true,function stun.Periodic)
        endif
        if d.OnStart.exists then
            call d.OnStart()
        endif
        set stun.D[stun.Count] = d
        set stun.Count = stun.Count + 1
        return d
    endmethod
    
    static method Periodic takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i &gt;= .Count
            if .D<i>.action() then
                set .Count = .Count - 1
                if .Count &gt; 0 then
                    set .D<i> = .D[.Count]
                    set i = i - 1
                else
                    call PauseTimer(.Timer)
                endif
                endif
            set i = i + 1
            endloop
        endmethod
        
    private method action takes nothing returns boolean
        if .duration &gt;= .Stunned or GetWidgetLife(.target) &lt; 0.405 then
            if .OnStop.exists then
                call .OnStop()
            endif
            call .release()
            return true
        endif
         if .OnRun.exists then
            call .OnRun()
        endif
        set .duration = .duration + INTERVAL
        return false
    endmethod
    
   private method onDestroy takes nothing returns nothing
        set .Stunned = 0
        call UnitRemoveAbility(.target, Buff_Id) 
    endmethod
endstruct
    
function IsStunned takes unit target returns boolean
    return stun[target] != 0
endfunction
    
function StunUnit takes unit target, real duration returns nothing
    local stun d = stun[target]
    call SetUnitX(stunner,GetUnitX(target))
    call SetUnitY(stunner,GetUnitY(target))
    call IssueTargetOrder(stunner, &quot;thunderbolt&quot;, target )
    if d == 0 then
        set d = stun.create(target)
        set stun[target] = d
        set d.Stunned = duration
    else
        set d = stun[target]
        set d.Stunned = duration
        set d.duration = 0.
    endif
endfunction

private function Init takes nothing returns nothing
    set stunner = CreateUnit(Player(12),Dummy_Id,0,0,0)
    call UnitAddAbility(stunner,Bolt_Id )
endfunction
endlibrary</i></i>


2) why does it not work?

3) A example of using it

JASS:

function Trig_Untitled_Trigger_003_Actions takes nothing returns nothing
    call StunUnit(GetTriggerUnit(),3)
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_003 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_003 = CreateTrigger(  )
    call TriggerRegisterPlayerSelectionEventBJ( gg_trg_Untitled_Trigger_003, Player(0), true )
    call TriggerAddAction( gg_trg_Untitled_Trigger_003, function Trig_Untitled_Trigger_003_Actions )
endfunction
 

Flare

Stops copies me!
Reaction score
662
It worked perfectly for me.
You have configured the stun ability differently to what I have done. Unless you have added a demo map, I have nothing more to go on than your word. Also, what's to stop other people running into the same issue that I encountered? Make things easy for people, add a demo map, let them copy the fully-configured Object Data. It's not that hard to click Manage Attachments, find the file and click Upload - it's a few kb, and takes about 15-20 seconds (if even that).
Until a demo map appears, I can't do anything but assume it doesn't work because (1) it didn't work when I tested and (2) you haven't given me any proof other than "it works for me" - the system has to work for everyone, not just you

2) why does it not work?
You allow people to extend the struct, yet the system only permits you to create instances of the parent struct - how can you call the OnStart/OnRun/OnStop method of a child struct if the instances created are based on the parent struct.

For simplicity, just get rid of the interface altogether, seriously. It has very little useful-ness. If there is anyone (not likely) who wants to do something when the stun starts
JASS:
function test takes nothing returns nothing
  local unit target = GetSpellTargetUnit ()
  call StunUnit (target, duration)
  //they can do it here - it&#039;s not complicated for anyone to understand
  //and it doesn&#039;t require interface usage to do something here either
endfunction


And it doesn't have the design flaw that your interface usage would've had (but, since the interface usage isn't done correctly, I can't exploit the flaw)

3) A example of using it
I know how to use it...
 

Tamisrah

Active Member
Reaction score
16
Suggestion for the OM Macro (the one for stormbolt):
You should add 'arlv 2' to it so the field 'required Level' would be >1 and therefore the thunderbolt could affect spell immune units.
 

wraithseeker

Tired.
Reaction score
122
Hold on.. I discovered some bugs.

JASS:


////! external ObjectMerger w3h BPSE STNB fnam &quot;Custom Stun&quot;
////! external ObjectMerger w3a AHtb STUN abuf 1 STNB alev 1 aher 1 ahdu 1 0 adur 1 0 Htb1 1 0 amat &quot;&quot; amcs 1 0 anam &quot;Custom Stun&quot;
library CustomStun initializer Init uses AutoIndex

    globals
        private constant real       INTERVAL    = 0.03125       //Interval of the timer, thus, delta value
        private constant integer    Dummy_Id    = &#039;h003&#039;    //Dummy ID
        private constant integer    Bolt_Id     = &#039;STUN&#039;    //Storm bolt ID
        private constant integer    Buff_Id     = &#039;B005&#039;    //Stun buff ID
        private constant integer LOCUST = &#039;Aloc&#039;
        private unit stunner = null
    endglobals
        
struct stun
    real Stunned = 0.
    unit target
    real duration
   static timer Timer = CreateTimer()
    static stun array D
    static integer Count = 0
        
    static method create takes unit target, real duration returns stun
        local stun d = stun.allocate()
        set d.target = target
        set d.duration = 0.
        set d.Stunned = duration
        if stun.Count == 0 then
            call TimerStart(stun.Timer,INTERVAL,true,function stun.Periodic)
        endif
        set stun.D[stun.Count] = d
        set stun.Count = stun.Count + 1
        return d
    endmethod
    
    static method Periodic takes nothing returns nothing
        local stun this
        local integer i = 0
        loop
            exitwhen i &gt;= .Count
            set this = .D<i>
            if .duration &gt;= .Stunned or GetWidgetLife(.target) &lt; 0.405 then
                call .destroy()
                set .Count = .Count - 1
                if .Count &gt; 0 then
                    set .D<i> = .D[.Count]
                    set i = i - 1
                else
                    call PauseTimer(.Timer)
                endif
            endif
            set .duration = .duration + INTERVAL
            set i = i + 1
        endloop
    endmethod
            
   private method onDestroy takes nothing returns nothing
        set .Stunned = 0
        call UnitRemoveAbility(.target, Buff_Id) 
    endmethod
    
    implement AutoData
endstruct
    
function IsStunned takes unit target returns boolean
    return stun[target] != 0
endfunction

function StopStun takes unit target returns nothing
    call UnitRemoveAbility(target,Buff_Id)
endfunction
    
function StunUnit takes unit target, real duration returns nothing
    local stun d = stun[target]
    call SetUnitX(stunner,GetUnitX(target))
    call SetUnitY(stunner,GetUnitY(target))
    call IssueTargetOrder(stunner, &quot;thunderbolt&quot;, target )
    if d == 0 then
        set d = stun.create(target,duration)
        set stun[target] = d
    else
        set d = stun[target]
        set d.Stunned = duration
        set d.duration = 0.
    endif
endfunction

private function Init takes nothing returns nothing
    set stunner = CreateUnit(Player(12),Dummy_Id,0,0,0)
    call UnitAddAbility(stunner,Bolt_Id )
    call UnitAddAbility(stunner,LOCUST)
endfunction
endlibrary</i></i>


That's the new verison.

JASS:
scope TidalWave initializer Init



globals
    private constant integer SPELL = &#039;A008&#039;
    private constant integer STOMP = &#039;A009&#039;
    private constant string EFFECT = &quot;MDX\\TidalErruption.mdx&quot;
    private boolexpr b
endglobals



private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == SPELL
endfunction



private function LineFilter takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(GetTriggerUnit())) and GetWidgetLife(GetFilterUnit()) &gt; 0.0451
endfunction

private function Actions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local location target = GetSpellTargetLoc()
    local unit dummy
    local group g = NewGroup()
    local group hitgroup = NewGroup()
    local integer i = 1
    local integer effects = 4
    local real dist = 600
    local real divdist = dist/effects
    local real cx = GetUnitX(caster)
    local real cy = GetUnitY(caster)
    local real tx = GetLocationX(target)
    local real ty = GetLocationY(target)
    local real angle = Atan2(ty-cy,tx-cx)
    local real x
    local real y
    local unit u
    local integer c = 0
    loop
        exitwhen i &gt; effects
        call TriggerSleepAction(0.1)
        set x = GetUnitX(caster) + (divdist * i) * Cos(angle)
        set y = GetUnitY(caster) + (divdist * i) * Sin(angle)
        set dummy = CreateUnit(GetOwningPlayer(caster),DUMMY,x,y,GetUnitFacing(caster))
        call SetUnitScale(dummy,2,2,2)
        call AddTimedEffectTarget(EFFECT,dummy,&quot;origin&quot;,3)
        call UnitApplyTimedLife(dummy,&#039;BTLF&#039;,3.5)
        call GroupEnumUnitsInRangeOfSegment(g,cx,cy,x,y,100,b)
        loop
            set u = FirstOfGroup(g)
            exitwhen u == null
            if not IsUnitInGroup(u,hitgroup) then
                call ThrowUnitToAir(u,28)
                call StunUnit(u,3)
                call GroupAddUnit(hitgroup,u)
            endif
            call GroupRemoveUnit(g,u)
        endloop
        set i = i + 1
        set c = c + 150
    endloop
        set u = null
    set caster = null
    set dummy = null
    call RemoveLocation(target)
    call ReleaseGroup(g)
    call ReleaseGroup(hitgroup)
    set target = null
endfunction



//===========================================================================

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction( t, function Actions )
    call TriggerAddCondition(t,Condition(function Conditions))
    set b = Filter(function LineFilter)
endfunction

endscope


Here when I use this spell 3 or 4 times stacking together, the stun buff never gets removed. I think it's not Multiple Unit Stackable?
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top