Is this script MUI?

Kenny

Back for now.
Reaction score
202
The only reason I am asking this is because I have been getting mixed signals on where or not this is MUI. I was looking over on another site and it seemed as though a script like mine (not a knockback one, just using struct arrays) is 100% MUI, but when posting on here I get the feeling that it isn't. So this time I'm making sure buy making a big deal out of it :p.

So heres my script as of now:

JASS:
library KBS initializer Init

globals
    // CONFIGURABLES:
    private constant integer Dummy_id = 'h000'           // This is the dummy unit.
    private constant real Period = 0.03125               // Recommended Interval.
    private constant real Radius = 150.00                // Radius for killing trees.
    private constant string Attachment_point = "origin"  // Attachment point for effects.
    private constant string Water_SFX = "SlideWater.mdx" // Water slide effect.
    private constant string Dirt_SFX = "Dust.mdx"        // Ground slide effect.
endglobals
     
globals
    private integer Total = 0
    private integer array DataArray
    private real Game_maxX
    private real Game_minX
    private real Game_maxY
    private real Game_minY
    private rect Rect1 = Rect(0.00,0.00,1.00,1.00)
    private timer Timer = CreateTimer()  
endglobals

private function KillEnumDestructables takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction

// Thanks to PitzerMike for this function.
private function TreeCheck takes nothing returns boolean
    local destructable dest = GetFilterDestructable()
    local boolean bool = IsDestructableInvulnerable(dest)
    local unit dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),Dummy_id,GetDestructableX(dest),GetDestructableY(dest),0.00)
    local boolean result = false

    call UnitAddAbility(dummy,'Ahrl')
    if bool then
        call SetDestructableInvulnerable(dest,false)
    endif
    set result = IssueTargetOrder(dummy,"harvest",dest)
    call RemoveUnit(dummy)
    if bool then
        call SetDestructableInvulnerable(dest,true)
    endif

    set dest = null
    set dummy = null
    return result
endfunction

private struct Data
    unit targ
    
    real speed
    real decrease
    real sin
    real cos
    
    effect SFX
    integer sfxmode
    boolean killdest
    
    // Checking for terrain type.
    static method TerrainType takes unit u, string sfx returns integer
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        
        if sfx != "" and sfx != null then
            return 0
        elseif IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
            return 1
        elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
            return 2
        endif
        
        return 3
    endmethod
    
    static method create takes unit Target, real Startspeed, real Deceleration, real Angle, string Specialeffect, boolean Killtrees returns Data
        local Data d 
        
        if Target == null or Startspeed <= 0.00 or Deceleration <= 0.00 then
            call BJDebugMsg("Error: Invalid input in KBS_Begin") // Error message.
            return 0
        endif
        
        // Allocates struct members to user defined variables.
        set d = Data.allocate()
        set d.targ = Target
        set d.speed = Startspeed
        set d.decrease = Deceleration
        set d.sin = Sin(Angle)
        set d.cos = Cos(Angle)
        set d.killdest = Killtrees
        set d.sfxmode = d.TerrainType(d.targ,Specialeffect)
        
        // Adding effects to the unit.
        if d.sfxmode == 0 then
            set d.SFX = AddSpecialEffectTarget(Specialeffect,d.targ,Attachment_point)
        elseif d.sfxmode == 1 then
            set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
        elseif d.sfxmode == 2 then
            set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
        elseif d.sfxmode == 3 then
            call BJDebugMsg("Error: Unknown terrain type (KBS)")
        endif
        
        if Total == 0 then
            call TimerStart(Timer,Period,true,function Data.Execute)

        endif
        
        set DataArray[Total] = integer(d)
        set Total = Total + 1
        
        return d
    endmethod
    
    static method Execute takes nothing returns nothing
        local integer i = Total - 1
        local Data d
        local real x
        local real y
        local real newx
        local real newy
        
        loop
            exitwhen i < 0
            set d = Data(DataArray<i>)
            
            set x = GetUnitX(d.targ)
            set y = GetUnitY(d.targ)
            
            if d.speed &lt;= 0 or (x &gt; Game_maxX or y &gt; Game_maxY or x &lt; Game_minX or y &lt; Game_minY) then
                call d.destroy() // Finish knockback if speed == 0 or unit has hit map borders.
                set Total = Total - 1
                if Total &lt; 0 then
                    call PauseTimer(Timer)
                    set Total = 0
                else
                    set DataArray<i> = DataArray[Total]
                endif
            else
                set newx = x+d.speed*d.cos
                set newy = y+d.speed*d.sin
                call SetUnitPosition(d.targ,newx,newy)
                
                if d.killdest then // Kills trees if boolean is true.
                    call SetRect(Rect1,x-Radius,y-Radius,x+Radius,y+Radius)
                    call EnumDestructablesInRect(Rect1,Condition(function TreeCheck),function KillEnumDestructables)
                endif
                
                if d.sfxmode == 0 then
                    set d.sfxmode = d.TerrainType(d.targ,&quot;sfx&quot;)
                else
                    set d.sfxmode = d.TerrainType(d.targ,&quot;&quot;)
                endif
                
                // Adds special effects.
                if d.sfxmode == 1 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 2
                elseif d.sfxmode == 2 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 1
                elseif d.sfxmode == 3 then
                    call BJDebugMsg(&quot;Error: Unknown terrain type (KBS)&quot;)
                endif
                
                set d.speed = d.speed - d.decrease
            endif
            
            set i = i - 1
        endloop
    endmethod
    
    private method onDestroy takes nothing returns nothing
        set .targ = null
        if .SFX != null then
            call DestroyEffect(.SFX)
            set .SFX = null
        endif
    endmethod
endstruct

public function BeginEx takes unit Target, real Startspeed, real Deceleration, real Angle, string SpecialEffect, boolean Killtrees returns nothing
    call Data.create(Target,Startspeed,Deceleration,(Angle*0.01745328),SpecialEffect,Killtrees)
endfunction

public function Begin takes unit Target, real Startspeed, real Deceleration, real Angle, boolean Killtrees returns nothing    
    call KBS_BeginEx(Target,Startspeed,Deceleration,(Angle*0.01745328),&quot;&quot;,Killtrees)
endfunction                                                                                                

// Sets map boundries.
private function Init takes nothing returns nothing
    set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea)-50.00
    set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea)-50.00
    set Game_minX = GetRectMinX(bj_mapInitialPlayableArea)+50.00
    set Game_minY = GetRectMinY(bj_mapInitialPlayableArea)+50.00
endfunction

endlibrary</i></i>


A clearly defined and definite answer will be greatly appreciated, and +repped.
 

Kenny

Back for now.
Reaction score
202
From the looks of it, it should be MUI. Why not try it out (place a few units on the map and slide them GetRandomReal (300, 600) distance or so towards GetRandomReal (0, 360) degrees?)

I have tried something similar and it seemed to work. But nothing to that extent, ill give it a try when i can, thanks for the idea.

Also, shouldn't the type for DataArray be Data?

Typecasting :). Well at least thats what i think it is. lol. I heard from a source of mine that it isnt a bad thing to do and may even be required in future versions of NewGen.

Typecast

For the moment, assigning a value of an struct type to an integer variable / or variable of any other struct type will already change the type of that reference for the compiler

Notice that sometimes using a variable might get tedious or even create unneeded overhead, that is the reason the type cast operator was added.

Its syntax is mostly like a function but the name of the function is the name of a custom type. It will soon allow native types as well.

JASS:
interface  wack
    //... some declarations
endinterface

struct wek extends wack
   integer x
   // some more declarations
endstruct

function test takes wack W returns nothing
   //You are certain that W is of type wek, a way to cast the value is:
 local wek jo = W
   set jo.x= 5 //done

   // but sometimes creating a variable is too much work for the virtual machine and you
   // are only accessing it once 

   set wek(W).x=5 //also works.
endfunction


type anarrayofdata extends integer array [6]

function getdata5 takes unit u returns integer
    //a reference to an object of type anarrayofdata is saved as the unit&#039;s custom value
    return anarrayofdata(GetUnitUserData(u))[5]
endfunction

function setdata5 takes unit u, anarrayofdata x returns nothing
    // Here we are doing the opposite, notice that integer may be used as a type cast
    // operator for struct and dynamic array types.
    call SetUnitUserData(u,  integer(x))
endfunction

*EDIT*

Does anyone have that clearly defined and definite answer that i am looking for? I can test it in about 15-16 hours but until then i need your opinion.
 

Strilanc

Veteran Scripter
Reaction score
42
The design is MUI (store in a list and iterate over it), so unless you have bugs in there it's MUI.

It's a bit confusing what's actually running the code though. For clarity you might want to start the timer in the init function instead of when the first element is inserted in the list. You're also starting that timer without pausing it, even though it might already be running. At most you should just be pausing it when the list is emptied, and resuming it when the list becomes non-empty.
 

Tukki

is Skeleton Pirate.
Reaction score
29
Yepp, but I must say that it looks very similar to a knockback system made by Rising_Dusk.
 

Kenny

Back for now.
Reaction score
202
Yepp, but I must say that it looks very similar to a knockback system made by Rising_Dusk.

I just found a thread from a while backing about someone asking how to use Dusks knockback.

And unfortunately it does look similar. :( This was however, not intended. I don't even know where to get Dusks knockback system. Im guessing from wc3campaigns or something. Ive never actually gone there specifically for JASS stuff though so i wouldn't be sure.

Thanks for bringing that to my attention. I might try to think of a way to change mine around a bit.

It's a bit confusing what's actually running the code though. For clarity you might want to start the timer in the init function instead of when the first element is inserted in the list. You're also starting that timer without pausing it, even though it might already be running. At most you should just be pausing it when the list is emptied, and resuming it when the list becomes non-empty.

Im not to sure on what your trying to say... I am pausing the timer before i start it. And it does pause when the list is empty.

JASS:
if d.speed &lt;= 0 or (x &gt; Game_maxX or y &gt; Game_maxY or x &lt; Game_minX or y &lt; Game_minY) then
    call d.destroy() // Finish knockback if speed == 0 or unit has hit map borders.
    set Total = Total - 1
    if Total &lt; 0 then
        call PauseTimer(Timer)
        set Total = 0
    else
        set DataArray<i> = DataArray[Total]
    endif
else
</i>


If Total < 0 then PauseTimer(Timer)... So if the list is empty it will pause. Then reset the list for the next instance. If not it will just keep on doing what its doing.

Oh and thanks for your input guys, my doubts about its MUI'ness have vanished... almost.
 

Strilanc

Veteran Scripter
Reaction score
42
Ah, I didn't see that PauseTimer (which is a big hint that you shouldn't be doing that in the middle of the loop).
 

Kenny

Back for now.
Reaction score
202
I see no other way of pausing a timer after checking if an instance of the knockback has finished. Besides, i thought that this is how a struct array worked for this type of spell. I havent seen another one that has PauseTimer() outside of the loop. And im pretty sure it does not matter if it is in the middle or not, PauseTimer() in that situation has never given me problems in the past.

*EDIT*

Would it be more efficient to make the condition in:

JASS:
call EnumDestructablesInRect(Rect1,Condition(function TreeCheck),function KillEnumDestructables) // Condition(function TreeCheck)


A global boolean, as it is checking the same thing everytime?

Also, to make it a bit more different to Dusk's system, should i make it use 1 distance instead of two, and a user defined time? I thought that would be ok.

Then i could use something like this:

JASS:
local real count = (duation/Interva)l // for a two second duration with a 0.03125 interval that would be like 64

set startspeed = ((distance*2)/count) // With a distance of 500 and the above count, thats like 15.625 startspeed
set deceleration = (startspeed/count) // and thats like 0.24414


Its just a thought but. All that math kinda puts me off changing it but lol.

*EDIT 2*

Ok is there a problem in doing this:

JASS:
public function BeginEx takes unit Target, real Startspeed, real Deceleration, real Angle, string SpecialEffect, boolean Killtrees returns nothing
    call Data.create(Target,Startspeed,Deceleration,(Angle*0.01745328),SpecialEffect,Killtrees)
endfunction

public function Begin takes unit Target, real Startspeed, real Deceleration, real Angle, boolean Killtrees returns nothing    
    call Data.create(Target,Startspeed,Deceleration,(Angle*0.01745328),&quot;&quot;,Killtrees)
endfunction


What i mean is: Will using Data.create() in both, instead of the Begin function calling the BeginEx function, be a problem at all? The reason i am using two Data.create's is that it doesn't seem to work properly if i do it the other way.
 

Tukki

is Skeleton Pirate.
Reaction score
29
As Strilanc posted a good idea would be to move the PauseTimer below the main loop function.

Like:
JASS:
           
       loop
            exitwhen i &lt; 0
            set d = Data(DataArray<i>)
            
            set x = GetUnitX(d.targ)
            set y = GetUnitY(d.targ)
            
            if d.speed &lt;= 0 or (x &gt; Game_maxX or y &gt; Game_maxY or x &lt; Game_minX or y &lt; Game_minY) then
                call d.destroy() // Finish knockback if speed == 0 or unit has hit map borders.
                set Total = Total - 1
                set DataArray<i> = 0 // As a uninitialized struct is 0.
            else
                set newx = x+d.speed*d.cos
                set newy = y+d.speed*d.sin
                call SetUnitPosition(d.targ,newx,newy)
                
                if d.killdest then // Kills trees if boolean is true.
                    call SetRect(Rect1,x-Radius,y-Radius,x+Radius,y+Radius)
                    call EnumDestructablesInRect(Rect1,Condition(function TreeCheck),function KillEnumDestructables)
                endif
                
                if d.sfxmode == 0 then
                    set d.sfxmode = d.TerrainType(d.targ,&quot;sfx&quot;)
                else
                    set d.sfxmode = d.TerrainType(d.targ,&quot;&quot;)
                endif
                
                // Adds special effects.
                if d.sfxmode == 1 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 2
                elseif d.sfxmode == 2 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 1
                elseif d.sfxmode == 3 then
                    call BJDebugMsg(&quot;Error: Unknown terrain type (KBS)&quot;)
                endif
                
                set d.speed = d.speed - d.decrease
            endif
            
            set i = i - 1
        endloop
        if (Total &lt;= 0) then // like this, so you only stop it once.
            call PauseTimer(Timer)
            set Total = 0
        endif
    endmethod</i></i>


EDIT:

I see no reason why you must create Data in both functions, can't you just do something like this:

JASS:
public function BeginEx takes unit Target, real Startspeed, real Deceleration, real Angle, string SpecialEffect, boolean Killtrees returns nothing
    call Data.create(Target,Startspeed,Deceleration,(Angle*0.01745328),SpecialEffect,Killtrees)
endfunction

public function Begin takes unit Target, real Startspeed, real Deceleration, real Angle, boolean Killtrees returns nothing    
    call BeginEx(Target, Startspeed, Deceleration, Angle, &quot;&quot;, Killtrees // Calls the BeginEx function with same values as it would call Data.create().
endfunction


EDIT_2: Only minior things changed.

Library code:
JASS:
library KBS initializer Init

globals
    // CONFIGURABLES:
    private constant integer Dummy_id = &#039;h000&#039;           // This is the dummy unit.
    private constant real Period = 0.03125               // Recommended Interval.
    private constant real Radius = 150.00                // Radius for killing trees.
    private constant string Attachment_point = &quot;origin&quot;  // Attachment point for effects.
    private constant string Water_SFX = &quot;SlideWater.mdx&quot; // Water slide effect.
    private constant string Dirt_SFX = &quot;Dust.mdx&quot;        // Ground slide effect.
endglobals

//===========================================================================
private keyword Data // We use a keyword to be able to use un-initialized stuff, like our array <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />

//===========================================================================
globals
    private integer Total      = 0
    private rect Rect1         = Rect(0.00,0.00,1.00,1.00)
    private Data array DataArray         // Now we can get rid of that &#039;integer&#039; typecasting.
    private timer Timer           = null // Initialize the timer in the Init function instead.
    private boolexpr TreeBOOLEXPR = null // We create a global boolexpr.
    private real Game_maxX
    private real Game_minX
    private real Game_maxY
    private real Game_minY
endglobals

//===========================================================================
private function KillEnumDestructables takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction

//===========================================================================
private function IsPointOutside takes real x, real y returns boolean // Function for detection when a point is outside map boundaries.
    return (x &gt; Game_maxX or y &gt; Game_maxY or x &lt; Game_minX or y &lt; Game_minY)
endfunction // Just for readability.

//===========================================================================
// Thanks to PitzerMike for this function.
private function TreeCheck takes nothing returns boolean
    local destructable dest = GetFilterDestructable()
    local boolean bool = IsDestructableInvulnerable(dest)
    local unit dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),Dummy_id,GetDestructableX(dest),GetDestructableY(dest),0.00)
    local boolean result = false

    call UnitAddAbility(dummy,&#039;Ahrl&#039;)
    if bool then
        call SetDestructableInvulnerable(dest,false)
    endif
    set result = IssueTargetOrder(dummy,&quot;harvest&quot;,dest)
    call RemoveUnit(dummy)
    if bool then
        call SetDestructableInvulnerable(dest,true)
    endif

    set dest = null
    set dummy = null
    return result
endfunction

//===========================================================================
private struct Data
    unit targ
    
    real speed
    real decrease
    real sin
    real cos
    
    effect SFX
    integer sfxmode
    boolean killdest
    
    //===========================================================================
    // Checking for terrain type.
    static method TerrainType takes unit u, string sfx returns integer
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        
        if sfx != &quot;&quot; and sfx != null then
            return 0
        elseif IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
            return 1
        elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
            return 2
        endif
        
        return 3
    endmethod
    
    //===========================================================================
    static method create takes unit Target, real Startspeed, real Deceleration, real Angle, string Specialeffect, boolean Killtrees returns Data
        local Data d 
        
        if Target == null or Startspeed &lt;= 0.00 or Deceleration &lt;= 0.00 then
            call BJDebugMsg(&quot;Error: Invalid input in KBS_Begin&quot;) // Error message.
            return 0
        endif
        
        // Allocates struct members to user defined variables.
        set d = Data.allocate()
        set d.targ = Target
        set d.speed = Startspeed
        set d.decrease = Deceleration
        set d.sin = Sin(Angle)
        set d.cos = Cos(Angle)
        set d.killdest = Killtrees
        set d.sfxmode = d.TerrainType(d.targ,Specialeffect)
        
        // Adding effects to the unit.
        if d.sfxmode == 0 then
            set d.SFX = AddSpecialEffectTarget(Specialeffect,d.targ,Attachment_point)
        elseif d.sfxmode == 1 then
            set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
        elseif d.sfxmode == 2 then
            set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
        elseif d.sfxmode == 3 then
            call BJDebugMsg(&quot;Error: Unknown terrain type (KBS)&quot;)
        endif
        
        if Total == 0 then
            call TimerStart(Timer,Period,true,function Data.Execute)
        endif
        
        set Total = Total + 1     
        set DataArray[Total] = d // no typecasting required anymore.
        return d
    endmethod
    
    //===========================================================================
    static method Execute takes nothing returns nothing
        local integer i = Total // Also the -1 can be taken away.
        local Data d
        local real x
        local real y
        local real newx
        local real newy
        
        loop
            exitwhen i &lt; 0
            set d = DataArray<i>
            
            set x = GetUnitX(d.targ)
            set y = GetUnitY(d.targ)
            
            if d.speed &lt;= 0 or IsPointOutside(x, y) then
                call d.destroy() // Finish knockback if speed == 0 or unit has hit map borders.
                set Total = Total - 1
                set DataArray<i> = 0
            else
                set newx = x+d.speed*d.cos
                set newy = y+d.speed*d.sin
                // call SetUnitPosition(d.targ,newx,newy) 
                call SetUnitX(d.targ, newx)
                call SetUnitY(d.targ, newy) // This is much faster, but does not check pathing.
                
                if d.killdest then // Kills trees if boolean is true.
                    call SetRect(Rect1, x-Radius, y-Radius, x+Radius, y+Radius)
                    call EnumDestructablesInRect(Rect1, TreeBOOLEXPR, function KillEnumDestructables)
                endif
                
                if d.sfxmode == 0 then
                    set d.sfxmode = d.TerrainType(d.targ,&quot;sfx&quot;)
                else
                    set d.sfxmode = d.TerrainType(d.targ,&quot;&quot;)
                endif
                
                // Adds special effects.
                if d.sfxmode == 1 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 2
                elseif d.sfxmode == 2 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 1
                elseif d.sfxmode == 3 then
                    call BJDebugMsg(&quot;Error: Unknown terrain type (KBS)&quot;)
                endif
                
                set d.speed = d.speed - d.decrease
            endif
            
            set i = i - 1
        endloop
        if (Total &lt;= 0) then // Pause it outside the loop.
            call PauseTimer(Timer)
            set Total = 0
        endif
    endmethod
    
    //===========================================================================
    private method onDestroy takes nothing returns nothing
        set .targ = null
        if .SFX != null then
            call DestroyEffect(.SFX)
            set .SFX = null
        endif
    endmethod
endstruct

//===========================================================================
public function BeginEx takes unit Target, real Startspeed, real Deceleration, real Angle, string SpecialEffect, boolean Killtrees returns nothing
    call Data.create(Target,Startspeed,Deceleration,(Angle*0.01745328),SpecialEffect,Killtrees)
endfunction

//===========================================================================
public function Begin takes unit Target, real Startspeed, real Deceleration, real Angle, boolean Killtrees returns nothing    
    call BeginEx(Target, Startspeed, Deceleration, (Angle*0.01745328), &quot;&quot;, Killtrees)
endfunction  
                                                                                              
//===========================================================================
// Sets map boundries, and initializes some stuff.
private function Init takes nothing returns nothing
    set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea) - 64.00 // 64 is closer <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" />
    set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea) - 64.00
    set Game_minX = GetRectMinX(bj_mapInitialPlayableArea) + 64.00
    set Game_minY = GetRectMinY(bj_mapInitialPlayableArea) + 64.00
    
    set Timer = CreateTimer()
    set TreeBOOLEXPR = Condition(function TreeCheck)
endfunction
endlibrary</i></i>
 

Kenny

Back for now.
Reaction score
202
As Strilanc posted a good idea would be to move the PauseTimer below the main loop function.

Whats your reasoning behind this? Is it just less calls through the loop or what?

I see no reason why you must create Data in both functions, can't you just do something like this:

Hmm, good idea, thanks for that.

As for your second edit:

- Initialising both the timer and boolean in the init function has already been done in my updated one, but thanks.

- I dont think IsPointOutside() is very necessary. You say it is for readability, but the only thing users will need to read are the first set of globals. I may think about it but, i have some other ideas for map bounds.

- I was using SetUnitPosition for a reason. I want to check for pathing. Anyway i realise the X/Y are faster, but not noticeably.

- I already addressed the public functions and timer pausing above.

- Whats the point of getting rid of typecasting? I don't think it is detrimental to a script, thats why typecast syntax was introduced... to be used. Oh and why can i now remove the -1 from Total at the beginning of the Execute method? Oh yeah and typecasting wasnt required in the first place. I heard that it may been needed in future versions on NewGen, so i did it, it doesn't affect anything. As for:
JASS:
private integer array DataArray
// This can easily be changed to private Data array Dataarray
// by making another global block underneath the struct, but i don&#039;t feel like doing that


- Why is +/- 64.00 closer than +/- 50.00 ? I'm not quite sure i understand the logic of that.

*EDIT*

Also, wouldnt:

JASS:
if d.speed &lt;= 0 or IsPointOutside(x, y) then
    call d.destroy()
    set Total = Total - 1
    set DataArray<i> = 0 // this need to be DataArray<i> = DataArray[Total], or does doing it your way change that completely.
else
</i></i>


Oh and:

JASS:
if (Total &lt;= 0) then // Thats gotta be only &lt; doesn&#039;t it? Or does that change as well with what you have done.
    call PauseTimer(Timer)
    set Total = 0
endif


*EDIT 2*

What would be a better solution?

1. Using the TreeCheck function i already have. (Original idea came from pitzermike)

or

2.
JASS:
private function TreeCheck takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == &#039;ATtr&#039; or d == &#039;BTtw&#039; or d == &#039;KTtw&#039; or d == &#039;YTft&#039; or d == &#039;JTct&#039; or d == &#039;YTst&#039; or d == &#039;YTct&#039; or d == &#039;YTwt&#039; or d == &#039;JTwt&#039; or d == &#039;JTwt&#039; or d == &#039;FTtw&#039; or d == &#039;CTtr&#039; or d == &#039;ITtw&#039; or d == &#039;NTtw&#039; or d == &#039;OTtw&#039; or d == &#039;ZTtw&#039; or d == &#039;WTst&#039; or d == &#039;LTlt&#039; or d == &#039;GTsh&#039; or d == &#039;Xtlt&#039; or d == &#039;WTtw&#039; or d == &#039;Attc&#039; or d == &#039;BTtc&#039; or d == &#039;CTtc&#039; or d == &#039;ITtc&#039; or d == &#039;NTtc&#039; or d == &#039;ZTtc&#039;
endfunction


Number 2 would remove the need for a dummy unit, but which one would be move efficient?
 

Tukki

is Skeleton Pirate.
Reaction score
29
Whats your reasoning behind this? Is it just less calls through the loop or what?
You only stop the timer once here, and there's no reason to try to stop i mutliple times.

- Whats the point of getting rid of typecasting? I don't think it is detrimental to a script, thats why typecast syntax was introduced... to be used. Oh and why can i now remove the -1 from Total at the beginning of the Execute method? Oh yeah and typecasting wasnt required in the first place. I heard that it may been needed in future versions on NewGen, so i did it, it doesn't affect anything
You doesn't have to use the integer() all the time. If you look at the create method you'll see that I put the Total + 1 over the set DataArray[Total] = <yourdata>, this is only needed if you use DataArray as struct type as structs are un-initialized at 0.

Why is +/- 64.00 closer than +/- 50.00 ? I'm not quite sure i understand the logic of that
It's more correct to the map bounds.

JASS:
if d.speed &lt;= 0 or IsPointOutside(x, y) then
    call d.destroy()
    set Total = Total - 1
    set DataArray<i> = 0 // this need to be DataArray<i> = DataArray[Total], or does doing it your way change that completely.
else</i></i>

Yeah, seems I did something. It needs to be like you posted.

JASS:
if (Total &lt;= 0) then // Thats gotta be only &lt; doesn&#039;t it? Or does that change as well with what you have done.
    call PauseTimer(Timer)
    set Total = 0
endif
That depends on if you are using the array as a struct or not, if you are that leave it as structs are un-initialized at 0. Else if you are using it as a integer then you can make it only <, but I'm not that sure if you would ever pause the timer.

JASS:
private function TreeCheck takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == &#039;ATtr&#039; or d == &#039;BTtw&#039; or d == &#039;KTtw&#039; or d == &#039;YTft&#039; or d == &#039;JTct&#039; or d == &#039;YTst&#039; or d == &#039;YTct&#039; or d == &#039;YTwt&#039; or d == &#039;JTwt&#039; or d == &#039;JTwt&#039; or d == &#039;FTtw&#039; or d == &#039;CTtr&#039; or d == &#039;ITtw&#039; or d == &#039;NTtw&#039; or d == &#039;OTtw&#039; or d == &#039;ZTtw&#039; or d == &#039;WTst&#039; or d == &#039;LTlt&#039; or d == &#039;GTsh&#039; or d == &#039;Xtlt&#039; or d == &#039;WTtw&#039; or d == &#039;Attc&#039; or d == &#039;BTtc&#039; or d == &#039;CTtc&#039; or d == &#039;ITtc&#039; or d == &#039;NTtc&#039; or d == &#039;ZTtc&#039;
endfunction
This way it's more efficient but it lacks the thing that it works for custom-made trees.
Since your system should be easy to use, I say go for PitzerMike's.
 

Kenny

Back for now.
Reaction score
202
Thanks for all your help :). Just need some final confirmation on the system, to see if everything is in order.

I went with your keyword idea after reading up on them in the jasshelper manual. They can be pretty handy. Therefore, I also changed the integer array to the struct array and it now doesn't use typecasting (and stuff like: local integer i = Total - 1). And i changed the +/-50.00's to +/-64.00's and used the IsPointOutside(). I already had that function in my other library.

And now im basically just wondering about one thing. Its at the bottom of the Execute function:

JASS:
library KBS initializer Init

globals
    // CONFIGURABLES:
    private constant integer Dummy_id = &#039;h000&#039;           // This is the dummy unit.
    private constant real Period = 0.03125               // Recommended Interval.
    private constant real Radius = 150.00                // Radius for killing trees.
    private constant string Attachment_point = &quot;origin&quot;  // Attachment point for effects.
    private constant string Water_SFX = &quot;SlideWater.mdx&quot; // Water slide effect.
    private constant string Dirt_SFX = &quot;Dust.mdx&quot;        // Ground slide effect.
endglobals

//***************************************************************************************
//**                                                                                   ** 
//**          DO NOT TOUCH BELOW HERE UNLESS YOU KNOW WHAT YOUR ARE DOING!             **
//**                                                                                   **
//***************************************************************************************     

private keyword Data

globals
    private integer Total = 0
    private Data array DataArray
    private real Game_maxX
    private real Game_minX
    private real Game_maxY
    private real Game_minY
    private rect Rect1 = null
    private timer Timer = null
    private boolexpr Treefilt = null
endglobals

private function KillEnumDestructables takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction

// Thanks to PitzerMike for this function.
private function TreeCheck takes nothing returns boolean
    local destructable dest = GetFilterDestructable()
    local boolean bool = IsDestructableInvulnerable(dest)
    local unit dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),Dummy_id,GetDestructableX(dest),GetDestructableY(dest),0.00)
    local boolean result = false

    call UnitAddAbility(dummy,&#039;Ahrl&#039;)
    if bool then
        call SetDestructableInvulnerable(dest,false)
    endif
    set result = IssueTargetOrder(dummy,&quot;harvest&quot;,dest)
    call RemoveUnit(dummy)
    if bool then
        call SetDestructableInvulnerable(dest,true)
    endif

    set dest = null
    set dummy = null
    return result
endfunction

// Checks if a point is on the map boundaries.
private function IsPointOutside takes real x, real y returns boolean
    return (x &gt; Game_maxX or y &gt; Game_maxY or x &lt; Game_minX or y &lt; Game_minY)
endfunction

private struct Data
    unit targ
    
    real speed
    real decrease
    real sin
    real cos
    
    effect SFX
    integer sfxmode
    boolean killdest
    
    // Checking for terrain type.
    static method TerrainType takes unit u, string sfx returns integer
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        
        if sfx != &quot;&quot; and sfx != null then
            return 0
        elseif IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
            return 1
        elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
            return 2
        endif
        
        return 3
    endmethod
    
    static method create takes unit Target, real Startspeed, real Deceleration, real Angle, string Specialeffect, boolean Killtrees returns Data
        local Data d 
        
        if Target == null or Startspeed &lt;= 0.00 or Deceleration &lt;= 0.00 then
            call BJDebugMsg(&quot;Error: Invalid input in KBS_Begin&quot;) // Error message.
            return 0
        endif
        
        // Allocates struct members to user defined variables.
        set d = Data.allocate()
        set d.targ = Target
        set d.speed = Startspeed
        set d.decrease = Deceleration
        set d.sin = Sin(Angle)
        set d.cos = Cos(Angle)
        set d.killdest = Killtrees
        set d.sfxmode = d.TerrainType(d.targ,Specialeffect)
        
        // Adding effects to the unit.
        if d.sfxmode == 0 then
            set d.SFX = AddSpecialEffectTarget(Specialeffect,d.targ,Attachment_point)
        elseif d.sfxmode == 1 then
            set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
        elseif d.sfxmode == 2 then
            set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
        endif
        
        if Total == 0 then
            call TimerStart(Timer,Period,true,function Data.Execute)
        endif
        
        set Total = Total + 1
        set DataArray[Total] = d
        
        return d
    endmethod
    
    static method Execute takes nothing returns nothing
        local integer i = Total
        local Data d
        local real x
        local real y
        local real newx
        local real newy
        
        loop
            exitwhen i &lt; 0
            set d = DataArray<i>
            
            set x = GetUnitX(d.targ)
            set y = GetUnitY(d.targ)
            
            if d.speed &lt;= 0 or IsPointOutside(x,y) then
                call d.destroy() // Finish knockback if speed == 0 or unit has hit map borders.
                set Total = Total - 1
            else
                set newx = x+d.speed*d.cos
                set newy = y+d.speed*d.sin
                call SetUnitPosition(d.targ,newx,newy) // Set units new position.
                
                if d.killdest then // Kills trees if boolean is true.
                    call SetRect(Rect1,x-Radius,y-Radius,x+Radius,y+Radius)
                    call EnumDestructablesInRect(Rect1,Treefilt,function KillEnumDestructables)
                endif
                
                if d.sfxmode == 0 then
                    set d.sfxmode = d.TerrainType(d.targ,&quot;sfx&quot;)
                else
                    set d.sfxmode = d.TerrainType(d.targ,&quot;&quot;) // Checks for pathing again.
                endif
                
                // Adds special effects.
                if d.sfxmode == 1 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 2
                elseif d.sfxmode == 2 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 1
                endif
                
                set d.speed = d.speed - d.decrease // Sets new speed.
            endif
            
            set i = i - 1
        endloop
        // Heres where i still need help.
        if (Total &lt;= 0) then
            call PauseTimer(Timer)
            set Total = 0
        else
            set DataArray<i> = DataArray[Total] // Can i leave this here or does it have to
        endif                                   // be up near the d.destroy() ? 
    endmethod
    
    private method onDestroy takes nothing returns nothing
        set .targ = null
        if .SFX != null then
            call DestroyEffect(.SFX) // Destroys effects if needed.
            set .SFX = null
        endif
    endmethod
endstruct

public function BeginEx takes unit Target, real Startspeed, real Deceleration, real Angle, string SpecialEffect, boolean Killtrees returns nothing
    call Data.create(Target,Startspeed,Deceleration,(Angle*0.01745328),SpecialEffect,Killtrees)
endfunction

public function Begin takes unit Target, real Startspeed, real Deceleration, real Angle, boolean Killtrees returns nothing    
    call BeginEx(Target,Startspeed,Deceleration,Angle,&quot;&quot;,Killtrees)
endfunction                                                                                                

// Sets map boundries and sets timer, rect and filter.
private function Init takes nothing returns nothing
    set Timer = CreateTimer()
    set Rect1 = Rect(0.00,0.00,1.00,1.00)
    set Treefilt = Condition(function TreeCheck)
    set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea)-64.00
    set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea)-64.00
    set Game_minX = GetRectMinX(bj_mapInitialPlayableArea)+64.00
    set Game_minY = GetRectMinY(bj_mapInitialPlayableArea)+64.00
endfunction

endlibrary</i></i>


I can't test this at the moment as i cant use world editor. School work :(.
 

Artificial

Without Intelligence
Reaction score
326
> Can i leave this here or does it have to be up near the d.destroy() ?
It should be after the destroying. :p
 

Kenny

Back for now.
Reaction score
202
Thanks for the confirmation, i was pretty sure it had to go up there, i just dont have editor to test it. Most of this has been done in notepad, lol.

Ah well +rep to you to. :).

*EDIT*

Yeah um... I quickly just jumped on WE for a sec to test it, and it doesn't work. It gives this error when used:

Attempt to destroy a null struct of type: KBS___Data

Seeing as though im not 100% sure what has happened with the struct array stuff in the system at the moment, i don't want to touch it in case i make it worse, lol. So help please :p.
 

Tukki

is Skeleton Pirate.
Reaction score
29
I'm away for the weekend, but;

When you called the function, did it work? And this message appeard when you stopped the knockback? Also you should check if your struct has an index of 0. Gtg but gl.
 

Tukki

is Skeleton Pirate.
Reaction score
29
JASS:
loop
            exitwhen i &lt; 0 . . .


Try to count upwards instead, begin at 1 and exitwhen i >= Total.
 

Kenny

Back for now.
Reaction score
202
I was just lookin at another system on this sight and it had something like that. Ill try it.
 

Kenny

Back for now.
Reaction score
202
Ok so i tried: exitwhen i >= Total and the error didn't appear, which is good, however the unit still didnt move. Then i tried just > and i got like 50 of the errors so that didn't work lol.

Script as of now:

JASS:
library KBS initializer Init

globals
    // CONFIGURABLES:
    private constant integer Dummy_id = &#039;h000&#039;           // This is the dummy unit.
    private constant real Period = 0.03125               // Recommended Interval.
    private constant real Radius = 150.00                // Radius for killing trees.
    private constant string Attachment_point = &quot;origin&quot;  // Attachment point for effects.
    private constant string Water_SFX = &quot;SlideWater.mdx&quot; // Water slide effect.
    private constant string Dirt_SFX = &quot;Dust.mdx&quot;        // Ground slide effect.
endglobals

//***************************************************************************************
//**                                                                                   ** 
//**          DO NOT TOUCH BELOW HERE UNLESS YOU KNOW WHAT YOUR ARE DOING!             **
//**                                                                                   **
//***************************************************************************************     

private keyword Data

globals
    private integer Total = 0
    private Data array DataArray
    private real Game_maxX
    private real Game_minX
    private real Game_maxY
    private real Game_minY
    private rect Rect1 = null
    private timer Timer = null
    private boolexpr Treefilt = null
endglobals

private function KillEnumDestructables takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction

// Thanks to PitzerMike for this function.
private function TreeCheck takes nothing returns boolean
    local destructable dest = GetFilterDestructable()
    local boolean bool = IsDestructableInvulnerable(dest)
    local unit dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),Dummy_id,GetDestructableX(dest),GetDestructableY(dest),0.00)
    local boolean result = false

    call UnitAddAbility(dummy,&#039;Ahrl&#039;)
    if bool then
        call SetDestructableInvulnerable(dest,false)
    endif
    set result = IssueTargetOrder(dummy,&quot;harvest&quot;,dest)
    call RemoveUnit(dummy)
    if bool then
        call SetDestructableInvulnerable(dest,true)
    endif

    set dest = null
    set dummy = null
    return result
endfunction

// Checks if a point is on the map boundaries.
private function IsPointOutside takes real x, real y returns boolean
    return (x &gt; Game_maxX or y &gt; Game_maxY or x &lt; Game_minX or y &lt; Game_minY)
endfunction

private struct Data
    unit targ
    
    real speed
    real decrease
    real sin
    real cos
    
    effect SFX
    integer sfxmode
    boolean killdest
    
    // Checking for terrain type.
    static method TerrainType takes unit u, string sfx returns integer
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        
        if sfx != &quot;&quot; and sfx != null then
            return 0
        elseif IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
            return 1
        elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
            return 2
        endif
        
        return 3
    endmethod
    
    static method create takes unit Target, real Startspeed, real Deceleration, real Angle, string Specialeffect, boolean Killtrees returns Data
        local Data d 
        
        if Target == null or Startspeed &lt;= 0.00 or Deceleration &lt;= 0.00 then
            call BJDebugMsg(&quot;Error: Invalid input in KBS_Begin&quot;) // Error message.
            return 0
        endif
        
        // Allocates struct members to user defined variables.
        set d = Data.allocate()
        set d.targ = Target
        set d.speed = Startspeed
        set d.decrease = Deceleration
        set d.sin = Sin(Angle)
        set d.cos = Cos(Angle)
        set d.killdest = Killtrees
        set d.sfxmode = d.TerrainType(d.targ,Specialeffect)
        
        // Adding effects to the unit.
        if d.sfxmode == 0 then
            set d.SFX = AddSpecialEffectTarget(Specialeffect,d.targ,Attachment_point)
        elseif d.sfxmode == 1 then
            set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
        elseif d.sfxmode == 2 then
            set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
        endif
        
        if Total == 0 then
            call TimerStart(Timer,Period,true,function Data.Execute) // Starting timer.
        endif
        
        set Total = Total + 1
        set DataArray[Total] = d
        
        return d
    endmethod
    
    static method Execute takes nothing returns nothing
        local integer i = Total
        local Data d
        local real x
        local real y
        local real newx
        local real newy
        
        loop
            exitwhen i &gt;= Total
            set d = DataArray<i>
            
            set x = GetUnitX(d.targ)
            set y = GetUnitY(d.targ)
            
            if d.speed &lt;= 0 or IsPointOutside(x,y) then
                call d.destroy() // Finish knockback if speed == 0 or unit has hit map borders.
                set Total = Total - 1
                set DataArray<i> = DataArray[Total]
            else
                set newx = x+d.speed*d.cos
                set newy = y+d.speed*d.sin
                call SetUnitPosition(d.targ,newx,newy) // Set units new position.
                
                if d.killdest then // Kills trees if boolean is true.
                    call SetRect(Rect1,x-Radius,y-Radius,x+Radius,y+Radius)
                    call EnumDestructablesInRect(Rect1,Treefilt,function KillEnumDestructables)
                endif
                
                if d.sfxmode == 0 then
                    set d.sfxmode = d.TerrainType(d.targ,&quot;sfx&quot;)
                else
                    set d.sfxmode = d.TerrainType(d.targ,&quot;&quot;) // Checks for pathing again.
                endif
                
                // Adds special effects.
                if d.sfxmode == 1 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Dirt_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 2
                elseif d.sfxmode == 2 then
                    call DestroyEffect(d.SFX)
                    set d.SFX = AddSpecialEffectTarget(Water_SFX,d.targ,Attachment_point)
                    set d.sfxmode = 1
                endif
                
                set d.speed = d.speed - d.decrease // Sets new speed.
            endif
            
            set i = i - 1
        endloop
        
        if Total &lt;= 0 then
            call PauseTimer(Timer)
            set Total = 0
        endif
    endmethod
    
    private method onDestroy takes nothing returns nothing
        set .targ = null
        if .SFX != null then
            call DestroyEffect(.SFX) // Destroys effects if needed.
            set .SFX = null
        endif
    endmethod
endstruct

//***************************************************************************************
//** Only two functions used by this system. KBS_BeginEx and KBS_Begin.                **
public function BeginEx takes unit Target, real Startspeed, real Deceleration, real Angle, string SpecialEffect, boolean Killtrees returns nothing
    call Data.create(Target,Startspeed,Deceleration,(Angle*0.01745328),SpecialEffect,Killtrees)
endfunction
//**                                                                                   **
public function Begin takes unit Target, real Startspeed, real Deceleration, real Angle, boolean Killtrees returns nothing    
    call BeginEx(Target,Startspeed,Deceleration,Angle,&quot;&quot;,Killtrees)
endfunction                                                                                                
//**                                                                                   **
//***************************************************************************************

// Sets map boundries and sets timer, rect and filter.
private function Init takes nothing returns nothing
    set Timer = CreateTimer()
    set Rect1 = Rect(0.00,0.00,1.00,1.00)
    set Treefilt = Condition(function TreeCheck)
    set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea)-64.00
    set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea)-64.00
    set Game_minX = GetRectMinX(bj_mapInitialPlayableArea)+64.00
    set Game_minY = GetRectMinY(bj_mapInitialPlayableArea)+64.00
endfunction

endlibrary</i></i>
 
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