[vJASS] Laggy system? Creep respawn and return system.

Level 4
Joined
May 23, 2010
Messages
83
I made a pretty basic system to make my creeps in dungeons return to their spots whenever they are too far away from the camp.

JASS:
library Creep initializer onInit

    globals
        private unit array    CREEP
        private real array    CREEP_X
        private real array    CREEP_Y
        private real array    CREEP_FACING
        private integer array CREEP_TYPE
        private timer array   CREEP_RETURN_TIMER
        private integer array CREEP_NORMAL
        private integer array CREEP_HEROIC
        
        private integer       CREEP_COUNT
        private timer         CREEP_CHECK_TIMER = CreateTimer()
        
        private constant real CREE_RETURN_RANGE = 1300
    endglobals
    
    private function CreepCheck takes nothing returns boolean
        return GetOwningPlayer(GetFilterUnit()) == Player(PLAYER_NEUTRAL_AGGRESSIVE) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) and GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0
    endfunction
    
    private function ReturnTimeout takes nothing returns nothing
        local integer i = 0
        local timer t = GetExpiredTimer()
        local real d
        local real dx
        local real dy
        loop
            exitwhen i > CREEP_COUNT
            if t == CREEP_RETURN_TIMER[i] then
                set dx = CREEP_X[i] - GetUnitX(CREEP[i])
                set dy = CREEP_Y[i] - GetUnitY(CREEP[i])
                set d = SquareRoot(dx*dx + dy*dy)
                if d < 0 then
                    set d = -d
                endif
                if d > 10 then
                    call SetUnitX(CREEP[i], CREEP_X[i])
                    call SetUnitY(CREEP[i], CREEP_Y[i])
                endif
                call IssueImmediateOrder(CREEP[i], "stop")
                call SetUnitFacing(CREEP[i], CREEP_FACING[i])
                call SetUnitInvulnerable(CREEP[i], false)
                call PauseTimer(CREEP_RETURN_TIMER[i])
                call DestroyTimer(CREEP_RETURN_TIMER[i])
            endif
            set i = i + 1
        endloop
    endfunction
    
    private function ReturnGroup takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > CREEP_COUNT
                if CREEP[i] == GetEnumUnit() then
                    call SetUnitInvulnerable(CREEP[i], true)
                    call IssueImmediateOrder(CREEP[i], "stop")
                    call IssuePointOrder(CREEP[i], "move", CREEP_X[i], CREEP_Y[i])
                    call TimerStart(CREEP_RETURN_TIMER[i], 7., false, function ReturnTimeout)
                endif
            set i = i + 1
        endloop
    endfunction
    
    private function Return takes unit u returns nothing
        local group g = CreateGroup()
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        call GroupEnumUnitsInRange(g, x, y, 600, function CreepCheck)
        call ForGroup(g, function ReturnGroup)
        call DestroyGroup(g)
    endfunction
    
    private function TempCheck takes nothing returns nothing
        local integer i = 0
        local unit u
        local real dx
        local real dy
        local real d
        local group g = CreateGroup()
        loop
            exitwhen i > CREEP_COUNT
            set u = CREEP[i]
            set dx = GetUnitX(u) - CREEP_X[i]
            set dy = GetUnitY(u) - CREEP_Y[i]
            set d = SquareRoot(dx*dx + dy*dy)
            if d < 0 then
                set d = -d
            endif
            if d > 1300 then
                call Return(u)
                call BJDebugMsg("You pulled the creeps too far away from their spot! They are returning to their original spot and will regenerate life.")
            endif
            set i = i + 1
        endloop
        
    endfunction
    
    private function SaveUnits takes nothing returns nothing
        local integer i = CREEP_COUNT
        local unit u = GetEnumUnit()
        set CREEP[i] = u
        set CREEP_X[i] = GetUnitX(u)
        set CREEP_Y[i] = GetUnitY(u)
        set CREEP_FACING[i] = GetUnitFacing(u)
        set CREEP_TYPE[i] = GetUnitTypeId(u)
        set CREEP_RETURN_TIMER[i] = CreateTimer()
        set CREEP_COUNT = i + 1
    endfunction
    
    private function Reset takes nothing returns nothing
        local player p = GetTriggerPlayer()
        local integer i = GetPlayerId(p)
        local real x = GetRectCenterX(gg_rct_dung01exited)
        local real y = GetRectCenterY(gg_rct_dung01exited)
        local unit u
        if Hero[i] != null then
            set i = 0
            loop
                exitwhen i > 9
                call SetUnitX(Hero[i], x)
                call SetUnitY(Hero[i], y)
                set i = i + 1
            endloop
            set i = 0
            loop
                exitwhen i > CREEP_COUNT
                set u = CREEP[i]
                if u != null then
                    call RemoveUnit(u)                    
                endif
                call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), CREEP_TYPE[i], CREEP_X[i], CREEP_Y[i], CREEP_FACING[i])
                set i = i + 1
            endloop
            call BJDebugMsg("All dungeons have been reset!")
        else
            call BJDebugMsg("You cannot perform this command without picking a hero first")
        endif
    endfunction
    
    private function onInit takes nothing returns nothing
        local trigger reset = CreateTrigger()
        local group g = CreateGroup()
        local integer i = 0
        
        set CREEP_COUNT = 0
        call TimerStart(CREEP_CHECK_TIMER, 0.5, true, function TempCheck)
        
        call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, function CreepCheck)
        call ForGroup(g, function SaveUnits)
        
        call TriggerRegisterPlayerChatEvent(reset, Player(0), "-reset", true)
        call TriggerAddAction(reset, function Reset)
        
    endfunction
endlibrary

I want to know if this system may lag lower configuration computers (i've got 6gb ram and 1gb video card, so it's hard for me to notice lag). And if there is any other more efficient way to make this, can someone suggest me? I'm also thinking about coding a threat system (where the creeps will attack the most "threaten" target). I'll code a 'call for help' system too. So I need something that makes it all work together. Thanks in advance.

BTW, I pretend to have ~50 creeps per dungeon (about 5 dungeons in the first release).
 
Protip:
If you create the units for many different players instead of the same player, performance will increase by a very large percentange ^.^
This is because of how laggy orders are in Warcraft III.
Having 100 units for the same player ordered is much slower than having 10 units for 10 players ordered.
In this case, I think you can use any empty player slots and Player(15)

Also, You really shouldn't be using ForGroup .. ever :/

You should use a loop like this:

JASS:
local unit u

// the boolexpr is null
call GroupEnumUnitsInRange(GROUP, ... , null)
loop
    set u = FirstOfGroup(GROUP)
    exitwhen u == null
    call GroupRemoveUnit(GROUP, u)
    if FILTER_CONDITIONS(u) then
        // THE ACTIONS HERE
    endif
endloop

This approach is much faster because it doesn't spam threads for conditions (it gives a null boolexpr instead) and it doesn't use ForGroups, thus avoiding more thread spam :D

You should also be nulling local variables.
And you should NEVER be using local groups. Use global ones.
TempCheck creates a useless group.

JASS:
            set d = SquareRoot(dx*dx + dy*dy)
            if d < 0 then
                set d = -d
            endif
            if d > 1300 then
                call Return(u)
                call BJDebugMsg("You pulled the creeps too far away from their spot! They are returning to their original spot and will regenerate life.")
            endif

You can remove the check here to see if d is less than 0 because it will NEVER be 0 :3
And you can totally remove the SquareRoot function.
You're going to have to square 1300 though

It would look like this:

JASS:
            if dx*dx + dy*dy > 1690000 then
                call Return(u)
                call BJDebugMsg("You pulled the creeps too far away from their spot! They are returning to their original spot and will regenerate life.")
            endif

Also, you don't need to set u to CREEP, you can use CREEP itself.
Trust me, caching it like that won't increase efficiency :p (in wc3 anyway)

GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0
->
GetWidgetLife(GetFilterUnit()) > 0.405

OR, for even safer death checks:
not IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) and GetUnitTypeId() != 0

I hope this helps :D
 
Level 4
Joined
May 23, 2010
Messages
83
whoa, nice explanation! I like that.. So, using those loops (to check for creeps) won't lag on lower configuration computers?

Maybe it would be better to have a unit group for each camp in the dungeon? Then the 'return' would be for each spot, not for each unit (reducing lag, I suppose).
 
Level 25
Joined
Aug 18, 2009
Messages
4,081
And you should NEVER be using local groups. Use global ones.

Recursive-friendlyness?

JASS:
            set d = SquareRoot(dx*dx + dy*dy)
            if d < 0 then
                set d = -d
            endif
            if d > 1300 then
                call Return(u)
                call BJDebugMsg("You pulled the creeps too far away from their spot! They are returning to their original spot and will regenerate life.")
            endif

You can remove the check here to see if d is less than 0 because it will NEVER be 0 :3

It will never be negative.*

And you can totally remove the SquareRoot function.
You're going to have to square 1300 though

It would look like this:

JASS:
            if dx*dx + dy*dy > 1690000 then
                call Return(u)
                call BJDebugMsg("You pulled the creeps too far away from their spot! They are returning to their original spot and will regenerate life.")
            endif

Of course, you would not manually calculate the square and input it that way.

GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0
->
GetWidgetLife(GetFilterUnit()) > 0.405

Advantage? A unit's life is set to 0 when it dies and it can become any value during death without the unit being revived.


Why do you create a timer for each creep but loop through all creeps anyway in the timer expiration function? Why do you order a creep to stop and immediately to move again? Move should be enough. Yeah, why do you even pick nearby units in the Return function, should not just the entire camp retreat at once?
 
It will never be negative.*

Correct, I hadn't slept in over 21 hours when I made that post.

Recursive-friendlyness?

No problems would arise in this case.

Advantage? A unit's life is set to 0 when it dies and it can become any value during death without the unit being revived.

Yes, that check is more accurate. But just under that, I told him to use this death check instead:
not IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) and GetUnitTypeId() != 0
 
Top