Snippet CustomStun System

wraithseeker

Tired.
Reaction score
122
attachment.php

Requires AutoIndex, TimerUtils

Here is the documention
JASS:

CustomStun uses AutoIndex, TimerUtils
    v1.04
                        Made by wraithseeker
                    
                    
                    
                Changelog v1.01
                    Fixed many issues and now works like a charm.
                
                Changelog v1.02
                    Ported to AutoIndex and added spell immune function.
                    
                Changelog v1.03
                    Ported to use TimerUtils
                    
                Changelog v1.04
                    - Added a function to check for Mechanical units.
                    - Fixed spell immune bug.
                    
function StunUnit takes unit target, real duration returns nothing
                
    unit target refers to the unit who is going to get stunned
    real duration is how long do you want the unit to be stunned.
            
    call StunUnit(target,3)
                
It means that the unit target will get stunned for 3 seconds.
    
        
function StopStun takes unit target returns nothing
           
           call StopStun(whichUnit)
                
Stops the stun for the targeted unit.
            
function IsStunned takes unit target returns boolean
             
            call IsStunned(whichUnit)
                
It tells you whether the unit is stunned or not.
          
Creation of the ability          

The objectmerger calls are available for you to use to create them automatically, after creation,
uncomment the objectmerger calls to make saving your map faster.


JASS:
library CustomStun initializer Init uses AutoIndex, TimerUtils

//! external ObjectMerger w3h BPSE STNB fnam "Custom Stun"
//! external ObjectMerger w3a AHtb STUN anam "Stun" alev 1 Htb1 1 0 acdn 1 0 amcs 1 0 amat " " abuf 1 STNB atar 1 notself aran 1 999999  aani "" ahdu 1 0 adur 1 0 amsp 0
// Use this to create the abilities and uncomment it after it is created
globals
    private constant integer    StunDummyId     = 'h003'    //Dummy ID
    private constant integer    BoltId          = 'STUN'    //Storm bolt ID
    private constant integer    BuffId          = 'STNB'    //Stun buff ID
    private constant string     StunOrderId     = "thunderbolt" // string order of StormBolt Id
endglobals

globals
    private unit stunner  = null
    private group Stunned = CreateGroup()
endglobals

struct stun
    unit target
    timer T
        
static method Periodic takes nothing returns nothing
    call stun(GetTimerData(GetExpiredTimer())).destroy()
endmethod

static method create takes unit target, real duration returns stun
    local stun d = stun.allocate()
    set d.target = target
    set d.T = NewTimer()
    call GroupAddUnit(Stunned,target)
    call SetUnitX(stunner,GetUnitX(target))
    call SetUnitY(stunner,GetUnitY(target))
    call IssueTargetOrder(stunner,StunOrderId,target)
    call SetTimerData(d.T,d)
    call TimerStart(d.T,duration,false,function stun.Periodic)
    return d
endmethod

private method onDestroy takes nothing returns nothing
    call UnitRemoveAbility(.target, BuffId) 
    call GroupRemoveUnit(Stunned,.target)
    call ReleaseTimer(.T)
endmethod
    
    implement AutoData
endstruct
    
function IsStunned takes unit target returns boolean
    return IsUnitInGroup(target,Stunned)
endfunction

function StopStun takes unit target returns nothing
    local stun d = stun[target]
    if IsUnitInGroup(target,Stunned) then
        call d.destroy()
    endif
endfunction

function StunUnit takes unit target, real duration returns nothing
    local stun d
    local real remaining = 0.
    if not IsUnitInGroup(target,Stunned) then
        set stun[target] = stun.create(target,duration)
    else
        set d = stun[target]
        set remaining = TimerGetRemaining(d.T)
        if duration > remaining then
            set remaining = duration
        endif
        call ReleaseTimer(d.T)
        set d.T = NewTimer()
        call SetTimerData(d.T,d)
        call TimerStart(d.T,remaining,false,function stun.Periodic)
    endif
endfunction
    
function StunUnitEx takes unit target, real duration, boolean Immune, boolean organic returns nothing
    local stun d
    local real remaining = 0.
    local boolean b = false
    local boolean organ = false
    if not IsUnitInGroup(target,Stunned) then
        if Immune and IsUnitType(target,UNIT_TYPE_MAGIC_IMMUNE) then
            set b = true
        endif
        if organic and IsUnitType(target,UNIT_TYPE_MECHANICAL) then
            set organ = true
        endif
        if not b and not organ then
            set stun[target] = stun.create(target,duration)
        endif
    else
        set d = stun[target]
        set remaining = TimerGetRemaining(d.T)
        if duration > remaining then
            set remaining = duration
        endif
        call ReleaseTimer(d.T)
        set d.T = NewTimer()
        call SetTimerData(d.T,d)
        call TimerStart(d.T,remaining,false,function stun.Periodic)
    endif
endfunction

private function Init takes nothing returns nothing
    set stunner = CreateUnit(Player(12),StunDummyId,0,0,0)
    call UnitAddAbility(stunner,BoltId )
endfunction

endlibrary
 

Attachments

  • CustomStun.jpg
    CustomStun.jpg
    41.7 KB · Views: 290
  • CustomStun v1.04.w3x
    49.6 KB · Views: 250

wraithseeker

Tired.
Reaction score
122
I realised that you can't get a unit stunned forever and there will always be a 0.1 ~ 0.2 second delay to get the buff off the unit and then on, anybody know how to fix it? Joker[div] system had that bug too, I tested.
 

Darius34

New Member
Reaction score
30
How is this system different from Joker's?

Anyway, have you considered using //! ObjectMerger to generate the necessary object data? Makes implementation a lot easier.

You're better off using TimerUtils for this, or just recycling the timers with the struct instances as you go along. From what I've heard using a single timer for this stuff is less efficient.

Also, why are you using the interface? There's no inheritence here. Users can't extend the stun struct, or configure the methods inside easily (not that you'd want them to without giving them more documentation).
 

wraithseeker

Tired.
Reaction score
122
A static array timer loop would be better as I myself personally use TimerUtils red and is afraid of the Timerlimit that might exceed since I will use this system for all my stunning stuffs. Imagine you have 60 spells and each spell has a stun duration of 5 second for a unit within 600 range of you, it might bug too easily and I wanted to make it safe.

I could do objectmerger if you gave me a tutorial on it or at least taught me how :)

Interface, well is for the more advanced users. Why can't they extend it? I don't think documention is needed for this really.

I use one timer so howda I recycle them? I pause them. Why is it less efficient though? I heard in his thread many people asking him to do a single array timer loop.
 

Flare

Stops copies me!
Reaction score
662
On the interface part, Darius is correct. This is just senseless use of functionality, purely for the sake of 'because i lernd it, lol'

Now, to explain why:
1) As Darius said, no inheritance. Since your struct is private, users are unable to extend it. Thus, it has zero useful-ness.

2) Can you suggest any useful additional functionality the end-user may want to add to this? The system is designed to stun a unit, it doesn't need to be able to damage the unit, move them around, or any other crap like that. Things shouldn't be unnecessarily complicated for no good reason. Addition of the interface doesn't magically make the code awesome - in it's current form, it's just making things worse

Also, why are Periodic and action 2 separate methods? They could easily be combined into one. What gave you the mad idea to do that? :confused:
 

wraithseeker

Tired.
Reaction score
122
it could be but it was just more neat :p for me to keep track of things.

In interface you can do stuff for the unit like if the stun duration is over you might want to do some stuff over it. For the start interface I have no idea too, it is just extra functionality. For the OnTimer it is just to see if anybody want to do any mad stuff in the timer every 0.03 second. God knows what can be done :cool:

For the struct part my bad, i'll remove the private thing now.

Also, Flare, you know of any reason why does this sometimes have a 0.1 second slow to add or remove the buff?
 

Flare

Stops copies me!
Reaction score
662
In interface you can do stuff for the unit like if the stun duration is over you might want to do some stuff over it. For the start interface I have no idea too, it is just extra functionality. For the OnTimer it is just to see if anybody want to do any mad stuff in the timer every 0.03 second.
You still haven't answered the question and, until a good answer (there are few things that require a 0.03s frequency), this is just unnecessary functionality abuse ¬_¬

God knows what can be done
No, he/she doesn't :p

For the struct part my bad, i'll remove the private thing now.
I foresee bad stuff happening if multiple structs with OnTimer methods extend the struct (and I shall abuse them, and you shall see why bad things shall happen =D)

For starters, let's start creating numerous struct instances, shall we, now that the struct is not private :)

(Won't get around to this for a while, I won't be home from college until ~17:30 or 18:00 GMT, so about 5 or 6 hours from now)

Also, Flare, you know of any reason why does this sometimes have a 0.1 second slow to add or remove the buff?
For the addition of the buff, did you set the dummy's Animation - Backswing and Animation - Cast Point to 0.00? How fast is the Storm Bolt projectile?

As regards removing the buff, it shouldn't have any delay...
 

wraithseeker

Tired.
Reaction score
122
1) Alright, I'll remove OnTimer method since it's extra functionability.

2) I am sure that if he doesn't know what crashes / make something fail, may god bless him.

3) Removed, so nothing else :)

4) They are set to 0, projectile speed is 1000000000000000.
 

Darius34

New Member
Reaction score
30
TimerUtils is perfectly okay to use. I doubt you'll have 60 5-second-stuns running at the same time... And besides, for most applications you won't exceed the timer limit.

To recycle the timers yourself you do this:

JASS:
struct stun
    timer t

    static method create takes nothing returns stun
        local stun s = stun.allocate()
        if s.t == null then
            set s.t = CreateTimer()
        endif
        call TimerStart(s.t, ...)
        return s
    endmethod

    method onDestroy takes nothing returns nothing
        call PauseTimer(.t)
    endmethod
endstruct


Using individual timers is better if you want to wait arbitrary periods, or just run functions a short while later. Using a static timer loop is better if you're doing some kind of high-frequency periodic effect, like a knockback.

Here, for each stun you have to wait out the duration of the stun, and once only. It's better to use individual timers.

I could do objectmerger if you gave me a tutorial on it or at least taught me how
Look in your NewGen folder, it's in the grimext directory.

Also, right now it doesn't make sense for users to be able to extend the stun struct. What would they do with it, seeing as StunUnit() (which contains the PUI attachment, stun-applying itself...) uses the internal stun structs? It was right to encapsulate it away in the first place, and leave only one user function.

If you want users to be able to extend the stun struct, well, the system should be designed a little differently. For example, make members private, put the code in StunUnit() in the constructor, etc.
 

wraithseeker

Tired.
Reaction score
122
It makes sense though for the TimerUtils part, but if I do
JASS:
TimerStart(d.t,duration,true,function test)
and then running the removing of buffs and such after the period would be good? Because I see no reason to use TimerUtils as the same way as it is doing now.

Besides, I am using TimerUtils red which has a limit of 256 timers which I have alot of custom spells in my map + some systems and with this system, If there were many units crowding round the caster and do a GroupEnumUnitsInRange, it would probaby crash with the TimerLimit since my map is a 12 player hero defense map.

If you want users to be able to extend the stun struct, well, the system should be designed a little differently. For example, make members private, put the code in StunUnit() in the constructor, etc.

You mean using method operators? I don't get what you mean, explain. Isi t like creating the data for a unit and then

JASS:
set d.duration = 5
set d.abc = whatever
 

Flare

Stops copies me!
Reaction score
662
You mean using method operators? I don't get what you mean, explain
He means (assuming struct is non-private):
1) Privatise your struct members, so that people can't play around with them in child structs
2) Instead of having 'function StunUnit takes ... returns nothing', put that stuff in your create method and let the create method instantiate the system i.e.
JASS:
static method create takes unit target, real duration returns nothing
//the code that is in StunUnit at the moment, will be here instead
//obviously, said code will have to be arranged in such a way that it works <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite7" alt=":p" title="Stick Out Tongue    :p" loading="lazy" data-shortname=":p" />
endmethod

then people who are extending the struct can just call the .create method to stun a unit

I am using TimerUtils red which has a limit of 256 timers
Then either switch to TimerUtils Blue (I'm not sure if Purple has a timer limit or not - if it doesn't you could also use that), or do as Darius showed you and use internal timer recycling.

and then running the removing of buffs and such after the period would be good?
It would be a bit more accurate with TimerUtils (if, say, the single timer frequency doesn't divide evenly into the stun duration, there would be a small discrepancy), but I don't think it would be enough to conclusively say that switching to TimerUtils would solve a 0.1s delay in buff removal. Are you sure that the buff is being removed, but the special effect is just playing it's death animation? I suggest you try:
JASS:
call UnitRemoveAbility (whichUnit, buffId)
if GetUnitAbilityLevel (whichUnit, buffId) == 0 then
  call BJDebugMsg (&quot;Buff is removed instantly&quot;)
endif

just to ensure that what you're seeing as a problem isn't just because of the effect's animations
 

wraithseeker

Tired.
Reaction score
122
I spam clicked a unit which each click gives a unit 3 second stun, after spamming for quite some time, I spammed orders to a unit and he was able to turn magically but couldn't do much because the time was too small.

But Look at Joker[div] system, Romek , Viikuna they all said to use a static struct array loop, I am really confused as to listen to whose's comments or even... orders.

I don't think that's the effect's animation and what would that do if I put in on my onDestroy method? It would just remove the unit or do you mean to put it in create method?

Will do the necessary changes.
 

Flare

Stops copies me!
Reaction score
662
But Look at Joker[div] system, Romek , Viikuna they all said to use a static struct array loop, I am really confused as to listen to whose's comments or even... orders.
Well, most stuns* usually last more than 1 second. Do you really want a timer to be executing 33+ times per second per stun instance especially when most of that time is spent being as-good-as idle and doing nothing more than incrementing a variable, when it could just execute once, for any imaginable period of time?

*Obviously, there are some exceptions like mini-stuns/interrupts, but in general, a stun is used to stun rather than interrupt and usually lasts for a second or two, if not more

Single timer is fine for things that need a high frequency (such as knockbacks), but if it doesn't, a single timer becomes less effective (since a single timer with a large interval is very inaccurate, and it becomes inefficient to handle long intervals with a high frequency and a ticker, since the timer spends alot of the time being idle)

I don't think that's the effect's animation and what would that do if I put in on my onDestroy method? It would just remove the unit or do you mean to put it in create method?
It wouldn't remove the unit, because such a function doesn't exist :p
And I've fixed the mistake =)

Just test it out, see if it displays (obviously, with the fixed version :p). I've never had a problem with it before (I primarily use it to remove Slow Aura (Tornado) based debuffs, which I was using as (de)buff indicators, and have never had an issue with delayed removal)

If the message displays, it's the special effect playing the death animation. Otherwise, I don't really know
 

wraithseeker

Tired.
Reaction score
122
The message displayed, test the example code with the system with this on any map.

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


Sometimes the unit is able to turned (wth) still.
 

wraithseeker

Tired.
Reaction score
122
Fixed the turning issue, turned out to be that I forgot to set d.duration = 0. :(

I think this will be the final verison, I will still use static timer array loop for it and not TimerUtils. Tried to use a way of using PUI only but failed miserably.
 

Flare

Stops copies me!
Reaction score
662
I think is stays easier to use a dummy that has stun bolt, it's my opinion...
That's what the system is currently using for causing the stun

Tried to use a way of using PUI only but failed miserably.
Ahm... that's what you're doing now? ¬_¬

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
        private constant integer    Bolt_Id     = &#039;STUN&#039;    //Storm bolt ID
        private constant integer    Buff_Id     = &#039;B005&#039;    //Stun buff ID

1) Why are the Object Merger lines there if they aren't being used (i.e. the 2 extra comment notation prefixing it)
2) Check the buff creation in the Object Merger line, and check your constant - notice anything wrong?

JASS:
set stunner = CreateUnit(Player(1),Dummy_Id,0,0,0)

Why are you creating the dummy for Player 2 (Blue)? Also, that has potential issues where Player 2 (Blue) is allied to the target, and Targets Allowed would prevent the dummy from targeting an ally. Haven't checked the demo map yet (and can't right now, no WE) so I don't know if you have already thought of this possibility

And you've still got the interface, yet no idea as to why it would be useful. Once I get to a computer with WE, assuming I've got this figured out correctly, you shall understand why it has such potential for making things go wrong (and it will be resulting from a system design flaw)
 

wraithseeker

Tired.
Reaction score
122
Ahm... that's what you're doing now? ¬_¬

Lol, no you got me wrong, I tried to use one timer for one unit and then using PUI to attach the data to the unit but failed miserably.

How do you do that ~_~ sign?

1) Why are the Object Merger lines there if they aren't being used (i.e. the 2 extra comment notation prefixing it)
2) Check the buff creation in the Object Merger line, and check your constant - notice anything wrong?

1)It is for lazy people who don't want to create it manually but it is not very confirmed that it works, Azlier made that for me and untill he finishes his tutorial, I won't change it probaby.

2) Same.

For interface, tell me what can make it go wrong when you got a computer with WE.
 

Flare

Stops copies me!
Reaction score
662
Lol, no you got me wrong, I tried to use one timer for one unit and then using PUI to attach the data to the unit but failed miserably.
You tried using a timer per instance along with PUI? If so, how could you get that wrong? :confused: If there is any code, post it?

How do you do that ~_~ sign?
Hold Shift, and press the key above Tab, and to the left of the 1 key (it's got 3 different characters on the key with this keyboard, ¬ ` and a vertical line)

it is not very confirmed that it works
What isn't confirmed to work? ObjectMerger works just fine - take a look at Cohadar's Bonus system if you need proof... there are probably other systems using it, but Bonus is the only one I can think of off the top of my head)
 

wraithseeker

Tired.
Reaction score
122
You tried using a timer per instance along with PUI? If so, how could you get that wrong? If there is any code, post it?

Devilish code


JASS:
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
        private constant integer    Bolt_Id     = &#039;STUN&#039;    //Storm bolt ID
        private constant integer    Buff_Id     = &#039;B005&#039;    //Stun buff ID
        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
    timer t
    group g
   
        
    static method create takes unit target, real duration returns stun
        local stun d = stun.allocate()
        set d = stun[target]
        set d.target = target
        set d.duration = 0.
        if d.g == null then
            set d.g = CreateGroup()
        endif
        if d.t == null then
            set d.t = CreateTimer()
        endif
        call GroupAddUnit(d.g,target)
        call TimerStart(d.t,duration,false,function stun.stununit)
        if d.OnStart.exists then
            call d.OnStart()
        endif
        call SetUnitX(stunner,GetUnitX(target))
        call SetUnitY(stunner,GetUnitY(target))
        call IssueTargetOrder(stunner, &quot;thunderbolt&quot;, target )
        if d == 0 then
            set stun[target] = d
            set d.Stunned = duration
        else
            set d = stun[target]
            set d.Stunned = duration
        endif
        return d
    endmethod
    
    static method stununit takes nothing returns nothing
        local data d
        call ForGroup(d.g,stun.Stun)
    endmethod
    
    method Stun takes nothing returns nothing
        local stun d = stun[GetEnumUnit()]
        if d.duration &gt;= d.Stunned or GetWidgetLife(d.target) &lt; 0.405 then
            if d.OnStop.exists then
                call d.OnStop()
            endif
            call d.release()
        endif
         if d.OnRun.exists then
            call d.OnRun()
        endif
        set d.duration = d.duration + INTERVAL
    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
    
private function Init takes nothing returns nothing
    set stunner = CreateUnit(Player(0),Dummy_Id,0,0,0)
    call UnitAddAbility(stunner,Bolt_Id )
endfunction
endlibrary


I stopped here because I couldn't find out how to solve the syntax error without TimerUtils.

Hold Shift, and press the key above Tab, and to the left of the 1 key (it's got 3 different characters on the key with this keyboard, ¬ ` and a vertical line)

Lol I did the way you did! Alt 0172! ¬_¬

What isn't confirmed to work? ObjectMerger works just fine - take a look at Cohadar's Bonus system if you need proof... there are probably other systems using it, but Bonus is the only one I can think of off the top of my head)

I know it works, but I am not very sure if mine works as I have never used ObjectMerger.
 
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