• 🏆 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!

[JASS] Respawn trigger

Status
Not open for further replies.
Level 3
Joined
Oct 7, 2011
Messages
37
Hi,
I have heroes owned by Neutral Hostile, that are controlled by an AI script state machine that I wrote. Every iteration of the logic they check if they are alive or not first before entering any other state.

I have included more code than necessary for clarity the actual problem is that ReviveHeroLoc is not working for me I am using this in the Respawn function at the bottom of this post if you want to skip ahead.

The alive state is checked by the following function
JASS:
private function isAlive takes unit aUnit returns boolean
        if(aUnit != null and GetWidgetLife(aUnit) > 0.405) then
            return true
        endif
        return false
   endfunction
if this returns false then it enters this code
JASS:
if(currentState[id] == dead) then
                    debug call DisplayTextToForce( GetPlayersAll(), "Id="+I2S(id)+" dead limit expired, calling respawn")
                    call Respawn(id)
                else
                    set duration = transitionToState(id,dead) 
                    debug call DisplayTextToForce( GetPlayersAll(), "Id="+I2S(id)+" is dead, waiting for respawn in "+I2S(duration)+" seconds!")
                endif
On the first entry into this code we will be in another state so currentState will not == dead so it will execute the else first
On the second entry into this code currentState is dead because we transitioned into it in the previous iteration so it will execute the first statement and call respawn. Switching states returns a delay in seconds which in the dead state transition is unitlevel * 5 + 1

if state is dead which it will be on the 2nd visit to the statement then call respawn

At the end of each state check it restarts the logic after a set duration by calling TimerUtils
JASS:
call TimerStart(NewTimerEx(id),duration,false,function AILoop)

hope this is making sense so far

The respawn function is fairly simple it picks a home base position to respawn at if it isn't already set then calls ReviveHeroLoc for the dead hero at that position my problem is this seems to return false and doesn't revive the hero
JASS:
private function Respawn takes integer id returns nothing
        local location position
        debug local boolean respawnResult = false
        if(homeBase[id] == null) then
            set position = GetUnitLoc(heroes[id])
            set homeBase[id] = Spice_GetClosestSpice(position)
            call RemoveLocation(position)
        endif
        set position = GetUnitLoc(homeBase[id])
        set respawnResult = ReviveHeroLoc(heroes[id],position,false)  
         debug if(respawnResult == true) then
            debug call DisplayTextToForce( GetPlayersAll(), "Id="+I2S(id)+" named "+GetHeroProperName(heroes[id])+" is respawned, respawn call returned true at x"+R2S(GetLocationX(position))+" y"+R2S(GetLocationY(position)))
        debug else
            debug call DisplayTextToForce( GetPlayersAll(), "Id="+I2S(id)+" named "+GetHeroProperName(heroes[id])+" is respawned, respawn call returned false at x"+R2S(GetLocationX(position))+" y"+R2S(GetLocationY(position)))
        debug endif
        call RemoveLocation(position)
        
        set position = null
        call transitionToState(id,searching)
    endfunction
The debug calls seem to indicate that the heroes die echo the correct amount of time to respawn, swap states to dead correctly the respawn function is called ok. Inside the respawn function, the home base is a valid position, the hero is not null tested with the GetHeroProperName call but still the ReviveHeroLoc call returns false and does not respawn the hero

After this things tend to repeat as the first logic check is IsAlive which returns false since the hero didn't actually revive then it goes back to step one where the unit isn't alive and isn't in the dead state since we transition back to searching in the respawn function

So does anyone know why ReviveHeroLoc is returning false? and not actually respawning nuetral hostile owned heroes

Thanks for any help :)
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
Im not an expert on this but can you give me a few more debugs?

1) print unit name of heroes[id]
2) print handle id of heroes[id]
3) switch it to ReviveHero and use GetLocationX/GetLocationY
4) revive hero at Location(0,0) than move to point
5) remove the debug prefix on respawnResult


(Unit Name != Proper Name)

e/ just tested this myself and it worked fine
 
Level 3
Joined
Oct 7, 2011
Messages
37
The unit name is identical to the hero name for this unit and changing it to GetUnitName yielded identical results the name was printed fine

I switched to ReviveHero using coordinates 0,0 then used SetUnitPositionLoc to place them at the intended coordinates this once again did not work the hero is not revived. and the function ReviveHero is also returning false when called

The coordinates of the bases change as they should each map but for this current test they were x(-8144.00) y(-7904.00) my map is the biggest size i think those coordinates should be well within the scope of the map

How do I convert the unit variable into something I can print the handle for?
I tried I2S(GetUnitTypeId(heroes[id])) that works fine prints a integer string 1211117898 but i think this is not what you were after?

Removing debug changed nothing as i am running in debug mode

Thanks for the response, sorry no luck yet ;(

Hmm I just made a test map and it seems to work not sure why its not working in mine then, does anyone know what would cause this to fail? the heroes[id] is not null tested with an if statement and all the other calls are working i.e. getname, getheroname, the position is not invalid as we are reviving at 0,0 before changing location and the unit is not reviving, what could i be doing to the unit variable that would mess it up like this?
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
GetHandleId(Unit)

Are you sure that your AI isnt forcing one of the computer's altars to revive the hero,which is blocking the trigger from doing so?
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
A few tips:
  • Don't use GetWidgetLife; use this instead: if IsUnitType(myUnit,UNIT_TYPE_DEAD)==false then
  • Don't use locations; use X/Y coordinates instead. They're a lot more faster, and easier to work with, and they don't mess with the handle counter.

I'd like to see the whole code in one piece, would make it easier to find where the problem lies.
 
Level 3
Joined
Oct 7, 2011
Messages
37
Hey Arhowk, I swapped getunittypeid to getHandleId
getHandleId
GetHandleId(Unit) is returning a different id for each unit 1055102 is one I checked the numbers are consistent through the iterations of the AIloop so the handles aren't getting corrupted or altered

Altars
Neutral hostile doesn't own any hero resurrection buildings in this map or regular bases and cant build anything on its own the buildings they do have are the same for other players and those players can revive ok when they die using a similar trigger

Hi Luorax, thanks for helping
IsAlive check
There are many posts here requesting the most efficient unit is alive check and most of them that I have seen seem to say that GetWidgetLife is the most efficient correct me if i am wrong? anyway I tried the new method and the result was the same it is detecting that the units are dead with no problems but they still aren't reviving successfully

Locations vs reals
How do i remove locations from the equation? doesn't it always come to a location eventually everything else is just obfuscation? if i get the position of the base i have to call GetLocationX() and Y or GetUnitX and Y? using location twice instead of just once to pass when calling a Loc function and I would still need to assign the location to a variable to get the x and y and then remove it later anyway, I'm not sure about that with GetUnitX and Y though? If I didn't assign the loc to a variable then it would be a leak?

The only way I could see an improvement here is if the Loc versions of functions are significantly higher overhead and that you are calling more than 1 in a function therefore making sense to take the additional overhead of calling GetLocationX and Y worthwhile to minimize the calls to the more expensive Loc multiple times but if its only 1 call in a function it would be more overhead calling the X and Y versions? meh damn dodgy war3 heh is that right? or not?

Issue
I think the code is fine after all I posted the Respawn function and that is where the units are failing to be revived I think the true issue is somewhere in another part of the map I am doing something to neutral units when they die that is stopping the unit from being resurrectable Does anyone have an idea what would stop units from being resurrectable ? so I can look for something specific please. I'm going to continue scrolling through the code checking but it would very much help if i knew a likely cause for this

Thanks
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
Strange...

Try killing the unit, waiting 0.00 seconds, than reviving

another thing you can try

if respawn = false

create abomination at positon of heroes[id] and wyrm at homebase[id]
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
Hi Luorax, thanks for helping
IsAlive check
There are many posts here requesting the most efficient unit is alive check and most of them that I have seen seem to say that GetWidgetLife is the most efficient correct me if i am wrong? anyway I tried the new method and the result was the same it is detecting that the units are dead with no problems but they still aren't reviving successfully

There's a topic somewhere that GetWidgetLife()<=0.405 doesn't filter out dead units properly. To be honest I'd recommend using the UnitAlive native (you have to declare it yourself), I'm using it too and it works perfectly.

Locations vs reals
How do i remove locations from the equation? doesn't it always come to a location eventually everything else is just obfuscation? if i get the position of the base i have to call GetLocationX() and Y or GetUnitX and Y? using location twice instead of just once to pass when calling a Loc function and I would still need to assign the location to a variable to get the x and y and then remove it later anyway, I'm not sure about that with GetUnitX and Y though? If I didn't assign the loc to a variable then it would be a leak?

The only way I could see an improvement here is if the Loc versions of functions are significantly higher overhead and that you are calling more than 1 in a function therefore making sense to take the additional overhead of calling GetLocationX and Y worthwhile to minimize the calls to the more expensive Loc multiple times but if its only 1 call in a function it would be more overhead calling the X and Y versions? meh damn dodgy war3 heh is that right? or not?

You have to both remove locations and null their variables, and working with them is clearly a lot slower than simply using the X/Y coordinates.

I don't know exactly how the WC3 engine is written, but I'm pretty sure that when you call a function that returns a location, a new instance is created, that messes with the handle counter and does a lot more than it should. I'm also pretty sure that they're based on the X/Y values that you'd access directly if you used the X/Y natives.

Just do this and you'll be good to go:
JASS:
local real unitX=GetUnitX(heroes[id])
local real unitY=GetUnitY(heroes[id])
local real spawnX=GetUnitX(homeBase[id])
local real spawnY=GetUnitY(homeBase[id])
 
Level 3
Joined
Oct 7, 2011
Messages
37
After a ton of changes I've ironed out a few issues now if i kill all of the units in one sweeping trigger about 1/3 to 1/2 of them will fail to resurrect but the rest will be fine, if i do it slowly say one unit every 2 seconds they all resurrect fine so I've been working on streamlining the code thinking its a MUI issue where to many die at once they are somehow overwriting themselves but

This unit can burrow to change into a different unit could that be causing this? so if the altered form dies is the original hero dead? or in some limbo state waiting to un-burrow still? I am currently trying to test this now

I will probably post the code soon to get some more help I have managed to fix alot of stuff so far though I was originally using UnitUserData to have an id for the heroes that was the array value in my library however I recently added UnitIndexer as a requirement from the Streak System and it uses the UserData value anyway Ive now written a slower function to loop through my heroes and return the id when necessary which is less efficient but more UnitIndexer friendly
I've swapped to not using locations where possible for now as well

Luorax you mentioned
To be honest I'd recommend using the UnitAlive native (you have to declare it yourself)
do i have to declare something for that check to work? or it works fine normally? just checking heh
 
Level 3
Joined
Oct 7, 2011
Messages
37
Ok I think I'm getting closer a little I've been testing for the faulty code by saving the map with an alternate name and going through deleting trigger categories then running the map. I ended up with an almost empty map and I started changing the AI till it was working again (The post above at this point) then i copied the changes back to my original map and bam broken again when tested

So i restarted deleting categories until i found one that killed the respawning, then verified it was the culprit. Unfortunately it has nothing to do with anything and was basically just a resource hungry trigger that ran far to often

What i have found now is that the respawning works ok for about 45 seconds or so then goes haywire
Coincidentally I have a trigger that starts at 43 seconds that is fairly intensive at first run anyway but unfortunately again it just creates units doesnt do anything that should affect the ai heroes
So i think the second the map gets bogged down in a loop that prevents the ai from respawning instantly it breaks the system somehow forever because at this point all the respawning stops working

Woo actually another idea both these intensive triggers use GetLastCreatedUnit does reviving a hero set the last created unit variable?

Ill check that anyway (UPDATE: It doesn't)

Still looking for the problem ill post the AI code soon so someone can point out the terrible flaws in it heh

Just trying to narrow down the scope of the problem still ;(

:goblin_boom:

Ok lol seem to have fixed the problem I changed the owner of the heroes to orange and it fixed all the errors on a quick check i saw the heroes still used food so I set that to 0 and reset the owner to neut hostile again and wala fixed
So when I was calling revive the neut hostile player was hitting the food limit after that 45 second mark only because that was the main spawning trigger for nuet creeps all over the map after that point the revive function would fail cause the food limit was over which is why it returned false

So that is the answer to the question or one of them Revive must have the food limit for the hero to be revived and even nuet hostile is bound by a food limit apparently

Thanks both of you for helping Luorax and Arhowk ;)
 
Last edited:
Status
Not open for further replies.
Top