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

[vJASS] function doesn't return

Status
Not open for further replies.
Level 4
Joined
May 25, 2009
Messages
100
have Problem with one of my functions: When my Hero gets attacked, a timer starts and checks if there is an obstruction between the Hero and the Attacker.

JASS:
function ordercollisioncheck takes nothing returns nothing
    local timer t= GetExpiredTimer()
    local unit stunit = LoadUnitHandle(udg_hashtable,GetHandleId(t),0)
    local location heropos = GetUnitLoc(LoadUnitHandle(udg_hashtable,GetHandleId(udg_Hero),4))
    local integer collisionbool =0
    //call BJDebugMsg(GetUnitName(stunit))
    set collisionbool = global_collisioncheck(stunit)
   //call BJDebugMsg(I2S(collisionbool))
    if collisionbool == 2 then
        call IssuePointOrderLoc( LoadUnitHandle(udg_hashtable,GetHandleId(stunit),4), "move", heropos )
    endif
endfunction

function Trig_UnitAttack_Conditions takes nothing returns boolean
    return GetTriggerUnit()== LoadUnitHandle(udg_hashtable,GetHandleId(udg_Hero),4)
endfunction

function Trig_UnitAttack_Actions takes nothing returns nothing
    local timer t = CreateTimer()
    local unit stunit = LoadUnitHandle(udg_hashtable,GetHandleId(GetAttacker()),4)
    call SaveUnitHandle(udg_hashtable,GetHandleId(t),0,stunit)
    call TimerStart(t,0.5,true,function ordercollisioncheck)
endfunction

//===========================================================================
function InitTrig_UnitAttack takes nothing returns nothing
    set gg_trg_UnitAttack = CreateTrigger(  )
    call TriggerRegisterPlayerUnitEventSimple( gg_trg_UnitAttack, Player(0), EVENT_PLAYER_UNIT_ATTACKED )
    //call TriggerAddCondition( gg_trg_UnitAttack, Condition( function Trig_UnitAttack_Conditions ) )
    call TriggerAddAction( gg_trg_UnitAttack, function Trig_UnitAttack_Actions )
endfunction

JASS:
function global_collisionchecktimer takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local unit stunit = LoadUnitHandle(udg_hashtable, GetHandleId(t), 0)
    local unit dummy = LoadUnitHandle(udg_hashtable, GetHandleId(t), 1)
    local real range = LoadReal(udg_hashtable, GetHandleId(t), 2)
    local location heropos = GetUnitLoc(udg_Hero)
    local location stunitpos = GetUnitLoc(stunit)
    local location dummypos = GetUnitLoc(dummy)
    call SaveReal(udg_hashtable, GetHandleId(t),2,range+30) 
    set range = LoadReal(udg_hashtable, GetHandleId(t), 2)
    call SetUnitPositionLoc( dummy, PolarProjectionBJ(stunitpos, range, AngleBetweenPoints(stunitpos, heropos)) )
    set dummypos = GetUnitLoc(dummy)
    set stunituebergabe = stunit
    call EnumDestructablesInCircleBJ( 60.00, dummypos, function lis_crouchcheck )
    if (DistanceBetweenPoints(stunitpos, dummypos) > ( range + 1.00 )  or (DistanceBetweenPoints(stunitpos, dummypos)) < ( range - 1.00 )) and not LoadBoolean(udg_hashtable,GetHandleId(stunit),7) then
        call SaveReal(udg_hashtable,GetHandleId(stunit),2,0.00)
        call RemoveUnit(dummy)
        call PauseTimer(t)
        call DestroyTimer(t)
        call FlushChildHashtable(udg_hashtable,GetHandleId(t))
        call SaveInteger(udg_hashtable,GetHandleId(t),2,2)
    else
        if DistanceBetweenPoints(heropos, dummypos) <= 20 then
            if LoadBoolean(udg_hashtable,GetHandleId(stunit),7) and LoadBoolean(udg_hashtable,GetHandleId(udg_Hero),0) then
                call SaveReal(udg_hashtable,GetHandleId(stunit),2,0.00)
                call RemoveUnit(dummy)
                call PauseTimer(t)
                call DestroyTimer(t)
                call FlushChildHashtable(udg_hashtable,GetHandleId(t))
                call SaveInteger(udg_hashtable,GetHandleId(t),2,2)  
            else
                if IsUnitAliveBJ(stunit)  then
                    call SaveReal(udg_hashtable,GetHandleId(stunit),2,0.00)
                    call RemoveUnit(dummy)
                    call PauseTimer(t)
                    call DestroyTimer(t)
                    call FlushChildHashtable(udg_hashtable,GetHandleId(t))
                    call SaveInteger(udg_hashtable,GetHandleId(t),2,1)
                else
                    call RemoveUnit(dummy)
                    call PauseTimer(t)
                    call DestroyTimer(t)
                    call FlushChildHashtable(udg_hashtable,GetHandleId(t))
                    call SaveInteger(udg_hashtable,GetHandleId(t),2,2)
                endif
            endif
        endif
    endif
    if range>721 then
        //call BJDebugMsg("range reset")
        call RemoveUnit(dummy)
        call PauseTimer(t)
        call DestroyTimer(t)
        call FlushChildHashtable(udg_hashtable,GetHandleId(t))
        call SaveInteger(udg_hashtable,GetHandleId(t),2,2)
    endif
    //call BJDebugMsg(I2S(LoadInteger(udg_hashtable,GetHandleId(t),2)))
    call RemoveLocation(heropos)
    call RemoveLocation(stunitpos)
    call RemoveLocation(dummypos)
endfunction

function global_collisioncheck takes unit stunit returns integer
    local timer t= CreateTimer()
    local location stunitpos = GetUnitLoc(stunit)
    local unit dummy = CreateUnitAtLoc( Player(PLAYER_NEUTRAL_PASSIVE), 'e000', PolarProjectionBJ(stunitpos, 0, GetUnitFacing(stunit)), bj_UNIT_FACING )
    local real range=0
    call SaveInteger(udg_hashtable,GetHandleId(t),2,0)
    call SaveBoolean(udg_hashtable,GetHandleId(stunit),7,false)
    call SaveUnitHandle(udg_hashtable, GetHandleId(t), 0, stunit)
    call SaveUnitHandle(udg_hashtable, GetHandleId(t), 1, dummy)
    call SaveReal(udg_hashtable, GetHandleId(t),2,range)
    call TimerStart(t, 0.01, true, function global_collisionchecktimer)
    loop
        call PolledWait(0.01)
        exitwhen LoadInteger(udg_hashtable,GetHandleId(t),2) !=0
    endloop
    //call BJDebugMsg(I2S(LoadInteger(udg_hashtable,GetHandleId(t),2)))
    return LoadInteger(udg_hashtable,GetHandleId(t),2)
endfunction


I figured out the problem so far, that there is a Problem with the return from "global_collisioncheck" to the Trigger "UnitAttack".
I get the BJDebugMsg(GetUnitName(stunit)) but not the BJDebugMsg(I2S(collisionbool))...not even "0" or "Null"

But when i call "global_collisioncheck" with "SkipCinematic" everything is fine
JASS:
function walkietalkie_Actions takes nothing returns nothing
   call BJDebugMsg(I2S(global_collisioncheck(gg_unit_h002_0004)))
endfunction

//===========================================================================
function walkietalkie_Actions takes nothing returns nothing
    call BJDebugMsg(I2S(global_collisioncheck(gg_unit_h002_0004)))
endfunction

//===========================================================================
function InitTrig_EscTest takes nothing returns nothing
    set gg_trg_EscTest = CreateTrigger(  )
    call TriggerRegisterPlayerEventEndCinematic( gg_trg_EscTest, Player(0) )
    call TriggerAddAction( gg_trg_EscTest, function walkietalkie_Actions )
endfunction

The Triggers are not checked for leaks and stuff and thats schouldn't be the topic. I just want to know what's the problem and how to fix it (global Vars are not an option, 'cause the function schould run vor more than one unit at the time.)
Thanks for your help!

Cataract92
 
Last edited:
Level 29
Joined
Oct 24, 2012
Messages
6,543
you should learn to indent it makes everything so much easier to read and it looks a lot better.
also u can change this
JASS:
function Trig_UnitAttack_Conditions takes nothing returns boolean
if GetTriggerUnit()== LoadUnitHandle(udg_hashtable,GetHandleId(udg_Hero),4) then
return true
endif
return false
endfunction
to this
JASS:
function Trig_UnitAttack_Conditions takes nothing returns boolean
    return GetTriggerUnit()== LoadUnitHandle(udg_hashtable,GetHandleId(udg_Hero),4)
endfunction
also if using jass u should learn to inline. (get rid of all the red text / bjs)

this may be ur problem. its hard to read so plz change ur indenting.
JASS:
loop
call PolledWait(0.01)
exitwhen LoadInteger(udg_hashtable,GetHandleId(t),2) !=0
endloop

polledwaits are very inaccurate and should not be used

also u shouldnt create units using locations anymore as they leak inst use CreateUnit and use x and y values
 
Level 4
Joined
May 25, 2009
Messages
100
Oh i'm sorry. I think i made a mistake with pasting it so it wasn't indent. Hope i fixed it. I also fixed the conditions.

But i have to wait somehow until global_collisiontimer ran long enough to get the result (LoadInteger(udg_hashtable,GetHandleId(t),2)). Do you have an idea to manage it?

And why is the SkipCinematic-Event working but not the UnitAttack-Event?
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
already said i believe ur problem is the polled wait just get rid of that.

im guessing ur loading a null unit handle for the attack trigger and thats the problem.

u really should update this then get back to us if it still doesnt work. also put messages to see were it stops in the attack trigger.

by update i mean inline the bjs and get rid of the polled wait and the create unit at location. change it over to use xy values. also what exactly is this meant to do it may help with fixing it.

for this
JASS:
IsUnitAliveBJ
change it over to this
JASS:
if GetUnitState( stunit, UNIT_STATE_LIFE) <= 0.405 then

the loop is definitely your problem as it is an infinite loop
 
Level 4
Joined
May 25, 2009
Messages
100
If i remove the polledwait it doesn't work at all, even if i call the function with SkipCinematic.

I already checked that all with DebugMsg: I don't load nullhandles and the Trigger stops after (or during) the return of "global_collisioncheck" . respectively then the loop doesn't end...thats right.
Cataract92 said:
I figured out the problem so far, that there is a Problem with the return from "global_collisioncheck" to the Trigger "UnitAttack".
I get the BJDebugMsg(GetUnitName(stunit)) but not the BJDebugMsg(I2S(collisionbool))...not even "0" or "Null"

And the hole functions are working, when i call them with SkipCinematic. So i'm not sure if it's a Problem with the Bjs or UnitLocs.
 
Last edited:
Level 4
Joined
Jan 27, 2010
Messages
133
You can only use waits in threads created by triggers. That is why the cinematic-ESC works (because you run it from a trigger).

PolledWait, actually uses normal wait. It's a bit more directly named: TriggerSleepAction.

Trig_UnitAttack_Actions starts a timer, so that code is run outside a trigger. Hence, waits won't work.
 
Not sure if I understood this piece of code right, but basicly what you want is for the whole thing to wait until a unit collides with a destructable?

If that is the case, a recoding of the whole thing would be useful, as the whole script is a mess.
If you need suggestions on how to do it, just ask.


An easy way to fix that (but I really need to encourage you to recode the whole thing) would be using a periodic timer instead of a polled wait.

However, the whole logic behind your code is flawed and could be optimized instead.
Why not make the move order based on the collision check part of the collisioncheck script itself instead of externalizing it? This removes the neccessity of waiting until a condition is true.
 
Level 4
Joined
May 25, 2009
Messages
100
You understood it pretty much.
But as you said the code is a mess and i started to recode it already and i wish to hear your suggestions!
I want to but the hole global_collisiontimer in a loop in global_collision.
I don't want to but the move order in the collisioncheck, 'cause i want a function, which i just can call (like global_collisioncheck(unit)) and it returns me if there is a obstruction between the Hero and the unit (true or false) and i can use it everywhere i want.
 
You can not expect a function to return a result instantly that is calculated over a time span. This requires a time machine (and a flux capacitor).
The only thing you can do is run your current collision detection periodically and keep the result in a variable that gets updated and then just get this variable.
Or create a collision detection algorythm that retrieves an instant result (which is not that hard actually, if you do the maths).
 
Level 4
Joined
May 25, 2009
Messages
100
That i know that jass can't travel through time^^
And my idea was to wait with the loop until the variable get updated to a result
(0=not updated;1=collision; 2=no collision)

Yeah i try to code a function for an instant result, where i just put my collisiontimer in a loop in collisioncheck and just pick destructables in a small loc, wich moves from the unit to the hero.Any Destructables picked->collision.
Do you have another idea for that?
 
That i know that jass can't travel through time^^
And my idea was to wait with the loop until the variable get updated to a result
(0=not updated;1=collision; 2=no collision)

Yeah i try to code a function for an instant result, where i just put my collisiontimer in a loop in collisioncheck and just pick destructables in a small loc, wich moves from the unit to the hero.Any Destructables picked->collision.
Do you have another idea for that?
A far better solution would be to pick all destructables within range of the hero and check for each of them to be inside a rectangle.

This is the maths for it:
http://www.hiveworkshop.com/forums/graveyard-418/ispointinrectangle-180617/

All you need now is the coordinates of the corner points of your rectangle. Which is as simple as getting the unit move angle, then creating polar offsets in a 90° angle from them and do the same for the projected end of the unit movement.

For example, your unit can move 300 range within one interval.
You get it's current movement angle, create a point (and with point I usually mean coordinates. Don't use locations) with polar offset at 300 range, then create 4 more points using those two created points as a base and create a polar offset at HeroMoveAngle+90° and HeroMoveAngle-90°, at a distance of your unit collision size.
This way you create all four corner points of the rectangle.

Then you can use the above snippet to find out which of those destructables are inside the rectangle.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
A far better solution would be to pick all destructables within range of the hero and check for each of them to be inside a rectangle.

This is the maths for it:
http://www.hiveworkshop.com/forums/graveyard-418/ispointinrectangle-180617/

All you need now is the coordinates of the corner points of your rectangle. Which is as simple as getting the unit move angle, then creating polar offsets in a 90° angle from them and do the same for the projected end of the unit movement.

For example, your unit can move 300 range within one interval.
You get it's current movement angle, create a point (and with point I usually mean coordinates. Don't use locations) with polar offset at 300 range, then create 4 more points using those two created points and create a polar offset at HeroMoveAngle+90° and HeroMoveAngle-90°, at a distance of your unit collision size.
This way you create all for corner points of the rectangle.

Then you can use the above snippet to find out which of those destructables are inside the rectangle.

ill have to remember this. this is a good alternative to his old method of doing this trigger.
 
Level 4
Joined
May 25, 2009
Messages
100
Thanks 4 that o_O
i will try that. My current unitsystem (working):
JASS:
function global_collisioncheck takes unit stunit returns boolean
    local location stunitpos = GetUnitLoc(stunit)
    local unit dummy = CreateUnitAtLoc( Player(PLAYER_NEUTRAL_PASSIVE), 'e000', PolarProjectionBJ(stunitpos, 0, GetUnitFacing(stunit)), bj_UNIT_FACING )
    local real range=-30
    local location heropos = GetUnitLoc(udg_Hero)
    local location dummypos = GetUnitLoc(dummy)
    local boolean bool1
    local boolean bool2
    local boolean collisionbool
    //call SaveInteger(udg_hashtable,GetHandleId(stunit),7,2)
    loop
        set range=range+30
        call SetUnitPositionLoc(dummy,Location(GetLocationX(PolarProjectionBJ(stunitpos, range, AngleBetweenPoints(stunitpos, heropos))),GetLocationY(PolarProjectionBJ(stunitpos, range, AngleBetweenPoints(stunitpos, heropos)))))
        set dummypos = GetUnitLoc(dummy)
        exitwhen DistanceBetweenPoints(heropos, dummypos) <= 20 or ((DistanceBetweenPoints(stunitpos, dummypos) > ( range + 1.00 )  or (DistanceBetweenPoints(stunitpos, dummypos)) < ( range - 1.00 )))
        call RemoveLocation(dummypos)
    endloop
    if DistanceBetweenPoints(heropos, dummypos) <= 20 then
        set collisionbool=true
    else
        set collisionbool=false
    endif
    call RemoveUnit(dummy)
    call RemoveLocation(heropos)
    call RemoveLocation(stunitpos)
    return collisionbool
endfunction
I know about the leaks. I wanted to get the trigger working befor fixing BJs etc.


I gave up my first intention: In my map i have two basic obstructions. If beetween hero and unit:
1. Hero doesn't need to duck
2. Hero has to duck

if there is no obstructions at all ->collision
if the hero doesn't have to duck ->No collision
if he has to duck, but doesn't->collision
if he has to duck and does ->no collision
(Hero has a duckabillity)

Problem:
If i use Timers to teleport the dummy until it collides, i have to wait before i know if it will collide at all, but i can't wait (polledwait doesn't work)

If i use loops instead of Timers it seems that i can't pick destructables to find out there TypeId (or at least it didn't work for me)

->So i decided to forget about it and just check if there is any obstruction ("duck" or "nonduck") and so i got my current function (which is working fine)

But maybe with your idea i can check the TypeId...but it will be difficult if there are more obstructions than one between the unit and the hero and than i have to rank then to "duck" and "nonduck" obstructions etc.

But i try it out!

PS: I made a full working cone of vision system with the two types of obstructions, but i used timers and the unitcollision, because i don't have to wait to return something. But i also wanted a function, which returns a boolean for quick checks. (Like: Can this unit attack the Hero or is there an obstruction in the way -> walk around...shooting through walls etc. is lame ;P)

Edit: I'm not sure how EnumDestructables work. Does it work with the path/model of the destructable or the center? 'Cause that would be a problem for Doodas with long,small path (like a wall)...Or i just spam small pathingblockers^^
 
Last edited:
Problem:
If i use Timers to teleport the dummy until it collides, i have to wait before i know if it will collide at all, but i can't wait (polledwait doesn't work)
You don't even need to move a unit around, let alone timed. Just use the maths I posted above to get all destructables between your hero and target point. Then, if there is one single destructable of that specific type you want enumed, make it return true for collision.

If i use loops instead of Timers it seems that i can't pick destructables to find out there TypeId (or at least it didn't work for me)
You can. Enuming destructables is instant.

PS: I made a full working cone of vision system with the two types of obstructions, but i used timers and the unitcollision, because i don't have to wait to return something. But i also wanted a function, which returns a boolean for quick checks. (Like: Can this unit attack the Hero or is there an obstruction in the way -> walk around...shooting through walls etc. is lame ;P)
All can be done by doing the maths and enuming destructables.
Edit: I'm not sure how EnumDestructables work. Does it work with the path/model of the destructable or the center? 'Cause that would be a problem for Doodas with long,small path (like a wall)...Or i just spam small pathingblockers^^
This is a problem, though. I recommend using smaller circular pathingblockers instead and then increase the rectangle edge size by the collision diameter.
 
Level 4
Joined
May 25, 2009
Messages
100
To be honest:
I keep my system like it is for know. It's working like it should be, i removed the leaks and its pretty much a light version of Nestharus' Is Point Pathable so it can't be that bad :D

I also had a other idea like: PickDestructables in Range and create a Rectangle based on the a table (Path) i create and use a "Line collides with Rectangle" (Vectormath). Problem is: I can't get the facing of descs, so i just could use normal pathblockers were the facing is not important (but then i have to spam them in a map^^ )

Anyway:
Thanks for your help Zwiebelchen (I really like Gaja ;D ) I want to focus on other systems for my map first and maybe, if my system doesn't work in later states i come back to your system. But as said: My system is working so far and thats all i wanted :D
+rep
 
To be honest:
I keep my system like it is for know. It's working like it should be, i removed the leaks and its pretty much a light version of Nestharus' Is Point Pathable so it can't be that bad :D

I also had a other idea like: PickDestructables in Range and create a Rectangle based on the a table (Path) i create and use a "Line collides with Rectangle" (Vectormath). Problem is: I can't get the facing of descs, so i just could use normal pathblockers were the facing is not important (but then i have to spam them in a map^^ )

Anyway:
Thanks for your help Zwiebelchen (I really like Gaja ;D ) I want to focus on other systems for my map first and maybe, if my system doesn't work in later states i come back to your system. But as said: My system is working so far and thats all i wanted :D
+rep
Just curious, why is the facing of destructables important in your case?

IsPointPathable is much more efficient than your approach, because it only moves the unit once, whereas yours moves a unit multiple times per check.
But I guess that's alright. It might not be the most efficient or elegant solution, but it works.
 
Level 4
Joined
May 25, 2009
Messages
100
Because if i want to create a Rectange for a doodad which has no quarter as a
path, like a wall, i have to know which facing the doodad has:
__________
|_________|
or
.____
|.....|
|.....|
|.....|
|___|

to create the right Rectangle:

Pick Destructables
Get DestructablesID
GetRectangeSize (Depending on a Table i would create e.g: GetDestID() == 'wall' -> 2x6)
CreateRectange with DestructableFacing and RectangeSize
Check if Vector (Unit->Hero) collides with Rectangle


I think IsPointPathable just checks one point and i check multiple points
between the unit and the hero, so i have to move the unit multiple times.
 
The problem is: your solution checks all entities with pathing, not just destructables.
So if you want doodads to have pathing, but not being considered an obstruction (like a bush or a flat stone), you can't do that with your move unit method. Except you make those obstruction objects also block flying units and make your checker unit a flying unit.
I guess both methods have their advantages. Yours allows doodads to be considered. Calculating allows doing stuff with the destructables hit (like rocks getting destroyed by missiles).
 
Level 4
Joined
May 25, 2009
Messages
100
My unit is already a flying unit and i use flyblockers. I considered it already :D

I just want to use my collisioncheck for is-in-sight-systems and i don't need the stuff calculation would provide.
But when i need this in further states i will come back to your system!
 
Status
Not open for further replies.
Top