• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] Creeps Respawn System

Status
Not open for further replies.
Level 5
Joined
Jul 14, 2008
Messages
121
I've been trying to make a vjass creeps respawn system, but as I just started using jass less then a week ago I can't seem to make it work.

JASS:
library CreepsRespawn initializer CRInit
    
    function UnitRespawn takes unit u returns nothing
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real f = GetUnitFacing(u)
        local integer i = GetUnitTypeId(u)
        call TriggerSleepAction(5)
        call CreateUnit(Player(11), i, x, y, f)
    endfunction
    
    function CreepDeath takes nothing returns nothing
        if (GetOwningPlayer(GetTriggerUnit()) == Player(11)) then
            call UnitRespawn(GetTriggerUnit())
        endif
    endfunction
        
    function CRInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(t, function CreepDeath)
    endfunction
    
endlibrary
 
Last edited:
Level 25
Joined
Jul 10, 2006
Messages
3,315
You don't need those private u/x/y etc globals, e.g. in UnitRespawn, you can reference du instead of u in all those calls.

A better way to create an initializer is like this:
JASS:
library CreepsRespawn initializer InitCRDeath
    function InitCRDeath takes nothing returns nothing
        call TriggerRegisterAnyUnitEventBJ(gg_trg_Creeps_Respawn, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(gg_trg_Creeps_Respawn, function CreepRespawnDeath)
    endfunction
endlibrary

This will run InitCRDeath on initialization, so you don't need a GUI trigger for it.

On that topic, the init function can be changed to:
JASS:
    function InitCRDeath takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(t, function CreepRespawnDeath)
    endfunction

I also see that you aren't calling InitCRGroup anywhere, you can add its actions to the above init function.

A handy debug method is to put
call BJDebugMsg("text") in all functions everywhere, so you can see if something is working on not.

EDIT: A few weeks ago when I tackled vJASS I started writing a tutorial; I didn't get very far but here it is for you to look at: http://www.hiveworkshop.com/forums/pastebin.php?id=xw6rdo
 
Level 5
Joined
Jul 14, 2008
Messages
121
InitCRGroup was there to implement something later, I wanted to save unit initial location to respawn them at the same place each time. I removed it, I will try and figure the basic first.

I don't need to make these globals private?

Btw I edited my code based on what I understood from your post.

Edit: Thx for the tutorials.

Edit2: Ah finally works! Anything else I should change?
 
Last edited:
Level 25
Joined
Jul 10, 2006
Messages
3,315
The problem here is that this isn't MUI.

Because you're using globals, they get overwritten when another unit dies.

Although frowned upon, here you could rather use TriggerSleepAction (Wait in GUI)

You can then remove those globals and the end timer function.

The respawn function becomes:
JASS:
    function UnitRespawn takes unit u returns nothing
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real f = GetUnitFacing(u)
        local integer i = GetUnitTypeId(u)
        call TriggerSleepAction(5)
        call CreateUnit(Player(11), i, x, y, f)
    endfunction
 
Level 5
Joined
Jul 14, 2008
Messages
121
TriggerSleepAction shouldn't cause any problems only using locals ?

Edit: I could probably make CreateUnit one line w/o the locals too.. just using u ?
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
TriggerSleepAction shouldn't cause any problems only using locals ?

No problems.

Edit: I could probably make CreateUnit one line w/o the locals too.. just using u ?

If the wait time is longer than death time + decay time of the unit, trying to access the unit will return null as the unit has been removed from the game.

The default decay time for flesh is 8 seconds and 88 seconds for bones. Not all units decay.

That is why it is good to use locals.
 
Level 5
Joined
Jul 14, 2008
Messages
121
Aaah, thx guys. I'll be working on it to try to make it respawn creeps at their initial location instead of their death location. I'm not exactly sure how to do that though.
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Here's the basic idea, it is in GUI though.

  • Init Respawn
    • Events
      • Time - Elapsed game time is 0.00 seconds
      • Unit - A unit enters (Playable map area)
    • Conditions
    • Actions
      • Custom script: set udg_i1 = GetHandleId(GetTriggerEventId())
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • i1 Equal to 4
        • Then - Actions
          • Set rtime = 5.00
          • Hashtable - Create a hashtable
          • Set rhash = (Last created hashtable)
          • Custom script: set bj_wantDestroyGroup = true
          • Unit Group - Pick every unit in (Units owned by Neutral Hostile) and do (Actions)
            • Loop - Actions
              • Custom script: call SaveReal( udg_rhash , GetHandleId(GetEnumUnit()) , StringHash("x") , GetUnitX(GetEnumUnit()) )
              • Custom script: call SaveReal( udg_rhash , GetHandleId(GetEnumUnit()) , StringHash("y") , GetUnitY(GetEnumUnit()) )
              • Custom script: call SaveReal( udg_rhash , GetHandleId(GetEnumUnit()) , StringHash("facing") , GetUnitFacing(GetEnumUnit()) )
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Owner of (Triggering unit)) Equal to Neutral Hostile
            • Then - Actions
              • Custom script: call SaveReal( udg_rhash , GetHandleId(GetTriggerUnit()) , StringHash("x") , GetUnitX(GetTriggerUnit()) )
              • Custom script: call SaveReal( udg_rhash , GetHandleId(GetTriggerUnit()) , StringHash("y") , GetUnitY(GetTriggerUnit()) )
              • Custom script: call SaveReal( udg_rhash , GetHandleId(GetTriggerUnit()) , StringHash("facing") , GetUnitFacing(GetTriggerUnit()) )
            • Else - Actions
  • Respawn
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Owner of (Triggering unit)) Equal to Neutral Hostile
    • Actions
      • Custom script: local integer id = GetHandleId(GetTriggerUnit())
      • Custom script: local integer utype = GetUnitTypeId(GetTriggerUnit())
      • Custom script: local player p = GetOwningPlayer(GetTriggerUnit())
      • Custom script: local real x = LoadReal( udg_rhash , id , StringHash("x") )
      • Custom script: local real y = LoadReal( udg_rhash , id , StringHash("y") )
      • Custom script: local real facing = LoadReal( udg_rhash ,id , StringHash("facing") )
      • Custom script: call FlushChildHashtable( udg_rhash , id)
      • Wait rtime seconds
      • Custom script: call CreateUnit( p , utype , x , y , facing )
      • Custom script: set p = null


You could use unit indexer and arrays instead.
 
Last edited:
Level 5
Joined
Jul 14, 2008
Messages
121
So this is storing all units position that you can later use on respawn. God your fast man, I just got time to start trying out stuff with hashtables and you got 2 triggers ready.

Could this possibly be done all in one vJass trigger with my current, just asking to know if I'm throwing myself into an impossible job XD

Edit: I'm not sure I understand this:

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
  • If - Conditions
  • i1 Equal to 4
  • Then - Actions
  • Set rtime = 5.00
Edit2: I just understood the Set rtime = 5.00, but I really don't get the confition "i1 Equal to 4".
 
Level 5
Joined
Jul 14, 2008
Messages
121
I'm probably missing something, but I'm really not getting why there's this condition:
  • i1 Equal to 4
 
Level 5
Joined
Jul 14, 2008
Messages
121
Ok I just finished making it vJass, but I'm getting an error that I can't figure out + I also can't figure out how to make it in 1 trigger...

JASS:
library CRInit initializer CreepsInit
    
    globals
        hashtable CRHashT = InitHashtable()
        real CRTime
        private integer i
    endglobals
    
    function CRGroupLoop takes nothing returns nothing
        call SaveReal(CRHashT, GetHandleId(GetEnumUnit()), StringHash("x"), GetUnitX(GetEnumUnit()))
        call SaveReal(CRHashT, GetHandleId(GetEnumUnit()), StringHash("y"), GetUnitY(GetEnumUnit()))
        call SaveReal(CRHashT, GetHandleId(GetEnumUnit()), StringHash("facing"), GetUnitFacing(GetEnumUnit()))
    endfunction
    
    function InitRespawn takes nothing returns nothing
        set i = GetHandleId(GetTriggerEventId())
        if i == 4 then
            set CRTime = 5
            set bj_wantDestroyGroup = true
            call ForGroup(GetUnitsOfPlayerAll(Player(11)), function CRGroupLoop)
        else
            if ( GetOwningPlayer(GetTriggerUnit()) == Player(11) ) then
                call SaveReal(CRHashT, GetHandleId(GetTriggerUnit()), StringHash("x"), GetUnitX(GetTriggerUnit()))
                call SaveReal(CRHashT, GetHandleId(GetTriggerUnit()), StringHash("y"), GetUnitY(GetTriggerUnit()))
                call SaveReal(CRHashT, GetHandleId(GetTriggerUnit()), StringHash("facing"), GetUnitFacing(GetTriggerUnit()))
            endif
        endif
    endfunction
    
    function CreepsInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterTimerEventSingle(t, 0.00)
        call TriggerRegisterEnterRectSimple(t, GetPlayableMapRect())
        call TriggerAddAction(t, function InitRespawn)
    endfunction
    
endlibrary

JASS:
library CreepsRespawn initializer CRInit

    function UnitRespawn takes nothing returns nothing
        local integer id = GetHandleId(GetTriggerUnit())
        local integer utype = GetUnitTypeId()
        local player p = GetOwningPlayer(GetTriggerUnit())
        local real x = LoadReal(CRHashT, id, StringHash("x"))
        local real y = LoadReal(CRHashT, id, StringHash("y"))
        local real facing = LoadReal(CRHashT, id, String("facing"))
        if (GetOwningPlayer(GetTriggerUnit()) == Player(11)) then
            call FlushChildHashtable(CRHashT, id)
            call TriggerSleepAction(CRTime)
            call CreateUnit(p, utype, x, y, facing)
            set p = null
        endif
    endfuntion
    
    function CRInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(t, function UnitRespawn)
    endfunction
    
endlibrary


The Second trigger is giving me "Syntax Error, unexpected: "takes"?" on this line:

JASS:
function CRInit takes nothing returns nothing
 
Level 25
Joined
Jul 10, 2006
Messages
3,315
The names might be conflicting.

You can prefix the initialization function with "private".

JASS:
private function CRInit takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddAction(t, function UnitRespawn)
endfunction

This means that no other function anywhere else can access this function.

Ok I just finished making it vJass, but I'm getting an error that I can't figure out + I also can't figure out how to make it in 1 trigger...

You can merge all the actions in the init functions (remember to use a second local trigger variable), and put the UnitRespawn function in the first library.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
suggestion, if it is not spell and system use scope, it has same syntax as scope, libraries are generally used for systems because code from library is put to top of the map so you can call them from anywhere

your error is because instead of endfunction you have endfuntion there
 
Level 5
Joined
Jul 14, 2008
Messages
121
Ok I'll try scope, anything should know about scope that is different then Library? Since I mostly learn for reading Spells Library, I don't know much about scopes.

Between, thanks for spotting the typo I probably would've took a few hours finding that out.

JASS:
scope CreepsRespawn initializer CreepsInit
    
    globals
        private hashtable ht = InitHashtable()
        private real r
        private integer i
    endglobals
    
    private function CRGroupLoop takes nothing returns nothing
        call SaveReal(ht, GetHandleId(GetEnumUnit()), StringHash("x"), GetUnitX(GetEnumUnit()))
        call SaveReal(ht, GetHandleId(GetEnumUnit()), StringHash("y"), GetUnitY(GetEnumUnit()))
        call SaveReal(ht, GetHandleId(GetEnumUnit()), StringHash("facing"), GetUnitFacing(GetEnumUnit()))
    endfunction
    
    private function InitRespawn takes nothing returns nothing
        set i = GetHandleId(GetTriggerEventId())
        if i == 4 then
            set r = 5
            set bj_wantDestroyGroup = true
            call ForGroup(GetUnitsOfPlayerAll(Player(11)), function CRGroupLoop)
        else
            if ( GetOwningPlayer(GetTriggerUnit()) == Player(11) ) then
                call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("x"), GetUnitX(GetTriggerUnit()))
                call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("y"), GetUnitY(GetTriggerUnit()))
                call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("facing"), GetUnitFacing(GetTriggerUnit()))
            endif
        endif
    endfunction
    
    private function UnitRespawn takes nothing returns nothing
        local integer id = GetHandleId(GetTriggerUnit())
        local integer utype = GetUnitTypeId(GetTriggerUnit())
        local player p = GetOwningPlayer(GetTriggerUnit())
        local real x = LoadReal(ht, id, StringHash("x"))
        local real y = LoadReal(ht, id, StringHash("y"))
        local real facing = LoadReal(ht, id, StringHash("facing"))
        if (GetOwningPlayer(GetTriggerUnit()) == Player(11)) then
            call FlushChildHashtable(ht, id)
            call TriggerSleepAction(r)
            call CreateUnit(p, utype, x, y, facing)
            set p = null
        endif
    endfunction
    
    private function CreepsInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local trigger t2 = CreateTrigger()
        call TriggerRegisterTimerEvent(t, 0.00, false)
        call TriggerRegisterEnterRectSimple(t, bj_mapInitialPlayableArea)
        call TriggerAddAction(t, function InitRespawn)
        call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(t2, function UnitRespawn)
    endfunction
    
endscope

Edit: So I just learn hashtables and how events works :D.
 
Last edited:
Level 25
Joined
Jul 10, 2006
Messages
3,315
Not sure, but if it compiles, it compiles!

Another handy tip for the rest of your code: anything that highlights red is usually bad. Red = BJ, a type of function, many of which that simply call a native (think of it as a more efficient function).

Whereever you see red, look up that function in JASS Helper and see if it calls a similar native, then rather use that native in your code.
 
Level 5
Joined
Jul 14, 2008
Messages
121
Yea I realize earlier that they call tons of code for another native that is much shorter code. I am working on that, I'm learning to use some of at the moment :D

Edit:

JASS:
function GetUnitsOfPlayerAll takes player whichPlayer returns group
    return GetUnitsOfPlayerMatching(whichPlayer, null)
endfunction

So I could be using GetUnitsOfPlayerMatchting(whichPlayer, null) instead of GetUnitsOfPlayer(whichPlayer). But I don't get what the "null" is...

JASS:
function GetUnitsOfPlayerMatching takes player whichPlayer, boolexpr filter returns group
    local group g = CreateGroup()
    call GroupEnumUnitsOfPlayer(g, whichPlayer, filter)
    call DestroyBoolExpr(filter)
    return g
endfunction

And I could use GroupEnumUnitsOfPlayer(g, whichPlayer, filter) instead of GetUnitsOfPlayerMatchting(whichPlayer, null) but I'm not understanding the "g" and the "filter"...
 
Level 25
Joined
Jul 10, 2006
Messages
3,315
In this case, "null" is a boolexpr, which stands for Boolean Expression, e.g. "is unit alive" or "is unit a hero". Null just tells the function that there is no condition/filtering.

You should use this:
JASS:
private function InitRespawn takes nothing returns nothing
    local group g = CreateGroup() //locals always come at the start of a function
    set i = GetHandleId(GetTriggerEventId())
    if i == 4 then
        set r = 5
        call GroupEnumUnitsOfPlayer(g, Player(11), null) 
        call ForGroup(g, function CRGroupLoop) //g is a variable that replaces that ugly function
    else
        if ( GetOwningPlayer(GetTriggerUnit()) == Player(11) ) then
            call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("x"), GetUnitX(GetTriggerUnit()))
            call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("y"), GetUnitY(GetTriggerUnit()))
            call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("facing"), GetUnitFacing(GetTriggerUnit()))
        endif
        call DestroyGroup(g) //clean the leak
    endif
endfunction
 
Level 5
Joined
Jul 14, 2008
Messages
121
So I would have to create a function for the condition if I was going to use a boolexpr ?

Just asking for general knowledge, it ain't useful for this trigger.

Changed like you said:

JASS:
scope CreepsRespawn initializer CreepsInit
    
    globals
        private hashtable ht = InitHashtable()
        private real r
        private integer i
    endglobals
    
    private function CRGroupLoop takes nothing returns nothing
        call SaveReal(ht, GetHandleId(GetEnumUnit()), StringHash("x"), GetUnitX(GetEnumUnit()))
        call SaveReal(ht, GetHandleId(GetEnumUnit()), StringHash("y"), GetUnitY(GetEnumUnit()))
        call SaveReal(ht, GetHandleId(GetEnumUnit()), StringHash("facing"), GetUnitFacing(GetEnumUnit()))
    endfunction
    
    private function InitRespawn takes nothing returns nothing
        local group g = CreateGroup()
        set i = GetHandleId(GetTriggerEventId())
        if i == 4 then
            set r = 5
            call GroupEnumUnitsOfPlayer(g, Player(11), null)
            call ForGroup(g, function CRGroupLoop)
        else
            if ( GetOwningPlayer(GetTriggerUnit()) == Player(11) ) then
                call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("x"), GetUnitX(GetTriggerUnit()))
                call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("y"), GetUnitY(GetTriggerUnit()))
                call SaveReal(ht, GetHandleId(GetTriggerUnit()), StringHash("facing"), GetUnitFacing(GetTriggerUnit()))
            endif
        endif
        call DestroyGroup(g)
    endfunction
    
    private function UnitRespawn takes nothing returns nothing
        local integer id = GetHandleId(GetTriggerUnit())
        local integer utype = GetUnitTypeId(GetTriggerUnit())
        local player p = GetOwningPlayer(GetTriggerUnit())
        local real x = LoadReal(ht, id, StringHash("x"))
        local real y = LoadReal(ht, id, StringHash("y"))
        local real facing = LoadReal(ht, id, StringHash("facing"))
        if (GetOwningPlayer(GetTriggerUnit()) == Player(11)) then
            call FlushChildHashtable(ht, id)
            call TriggerSleepAction(r)
            call CreateUnit(p, utype, x, y, facing)
            set p = null
        endif
    endfunction
    
    private function CreepsInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local trigger t2 = CreateTrigger()
        call TriggerRegisterTimerEvent(t, 0.00, false)
        call TriggerRegisterEnterRectSimple(t, bj_mapInitialPlayableArea)
        call TriggerAddAction(t, function InitRespawn)
        call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(t2, function UnitRespawn)
    endfunction
    
endscope
 
Level 25
Joined
Jul 10, 2006
Messages
3,315
So I would have to create a function for the condition if I was going to use a boolexpr ?

Just asking for general knowledge, it ain't useful for this trigger.

I guess so.

Further optimisations:
- You can remove i from your globals, and declare it as a local in the function that uses it.
- Can't you find a native for these:
call TriggerRegisterEnterRectSimple(t, bj_mapInitialPlayableArea)
call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DEATH)
 
Level 5
Joined
Jul 14, 2008
Messages
121
For:
call TriggerRegisterEnterRectSimple(t, bj_mapInitialPlayableArea)
I think something like that:
JASS:
 private function CreepsInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local region reg = CreateRegion()
        call RegionAddRect(reg, bj_mapInitialPlayableArea)
        call TriggerRegisterTimerEvent(t, 0.00, false)
        call TriggerRegisterEnterRegion(t, reg, null)
        call TriggerAddAction(t, function InitRespawn)
        call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DEATH)
        call TriggerAddAction(t2, function UnitRespawn)
endfunction

Edit: TriggerRegisterAnyUnitEventBJ I'm not really sure.

Edit2: I think I got it.

JASS:
private function CreepsInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local region reg = CreateRegion()
        call RegionAddRect(reg, bj_mapInitialPlayableArea)
        call TriggerRegisterTimerEvent(t, 0.00, false)
        call TriggerRegisterEnterRegion(t, reg, null)
        call TriggerAddAction(t, function InitRespawn)
        call TriggerRegisterPlayerUnitEvent(t2, Player(11), EVENT_PLAYER_UNIT_DEATH, null)
        call TriggerAddAction(t2, function UnitRespawn)
endfunction
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
point of the TriggerRegisterAnyUnitEventBj is that it loops through all 15 players so you dont need to
I personally dont care about that BJ, because there are even some useful ones(BJDebugMsg for instance)

also, when you declare local variable that is of type handle(anything except integer, real, boolean , code, string) you have tovnull it at the end end of the function
the exceptions may be triggers that are in game for all the time or groups that are nor destroyed(only global ones)
 
Level 5
Joined
Jul 14, 2008
Messages
121
Yea I realize what TriggerRegisterAnyUnitEventBj did and since I only needed to check 1 player it wasn't needed.
 
Status
Not open for further replies.
Top