1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  4. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  5. The results are out! Check them out.
    Dismiss Notice
  6. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  7. The raddest synthwave tracks were chosen - Check out our Music Contest #12 - Results and congratulate the winners!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[JASS] Peculiar Bug with Triggered Movement

Discussion in 'Triggers & Scripts' started by McQvaBlood, Aug 20, 2014.

  1. McQvaBlood

    McQvaBlood

    Joined:
    Jul 3, 2010
    Messages:
    536
    Resources:
    44
    Packs:
    4
    Maps:
    40
    Resources:
    44
    READ FIRST:
    • These triggers use vanilla JASS. If that is unacceptable for you, please do not bother to post. I have my reasons for not wanting to switch to any other version of JASS and I will simply ignore any post along the lines of "You should switch to [whatever your favourite version of JASS is]".
    • This problem requires a general understanding of Parallel Arrays and Recycled Timers.
    • I will not use Hashtables.

    PROBLEM:
    I have created two functions:
    • (1) moves a projectly unit at a constant real speed towards a fixed real angle a constant maximum distance.
    • (2) moves a projectly unit at a constant real speed towards a target unit.
    The syntax for these two functions, as you can imagine, are extremely similiar. However, while (1) works, (2) does a peculiar bug where it will only execute properly if the target unit is standing perfectly still. If the target unit is issued a move command or otherwise moves, the projectly unit seems to also move towards target point of issued order. At other times, the projectly unit stops completely and instead shakes around its current location, which would mean that it is either moved by multiple sources or moved back and forth by the same trigger.
    By the way, no other triggers in my map ever move a unit, currently.

    BACKGROUND:
    I am fairly certain that the problem is not with these background functions. I am posting them so you can be sure they work and on the off chance the problem is with these functions.
    Recycled Timers + IsUnitAlive

    globals:
    • boolean udg_SysTim_Boo[], arrayed with size 100.
    • timer udg_SysTim_Timer[], arrayed with size 100.

    Code (vJASS):
    // (1)
    function RecycleTimers_GetNewIndex takes nothing returns integer
        local integer Index = 0
        local integer ReturnIndex = 0
        loop
            set Index = Index + 1
            if udg_SysTim_Boo[Index] == FALSE then
                set ReturnIndex = Index
                set udg_SysTim_Boo[Index] = TRUE
            endif
            exitwhen ReturnIndex != 0
        endloop
        return ReturnIndex
    endfunction

    // (2)
    function RecycleTimers_GetCurrentIndex takes timer WhichTimer returns integer
        local integer Index = 0
        local integer ReturnIndex = 0
        loop
            set Index = Index + 1
            if udg_SysTim_Timer[Index] == WhichTimer then
                set ReturnIndex = Index
            endif
            exitwhen ReturnIndex != 0
        endloop
        return ReturnIndex
    endfunction

    // (3)
    function RecycleTimers_RecycleIndex takes integer WhichIndex returns nothing
        set udg_SysTim_Boo[WhichIndex] = FALSE
    endfunction

    // (4)
    function IsUnitAlive takes unit WhichUnit returns boolean
        return not IsUnitType(WhichUnit, UNIT_TYPE_DEAD) and (GetUnitTypeId(WhichUnit) != 0)
    endfunction

    Basically, all they do is recycle global declared timers so reals and the sort can be attached to them. This is so they can be used in a timer's callback function.

    CODE
    • I swapped the order of the call trigger and the callback for clarity. This is of course done properly in WE.
    • The real 0.04 is otherwise a decleared global that point to the real 0.04. I substituted it for clarity.
    • You will probably notice that from right after the local declarations of (2) it is basically analogous to (1). It still doesn't work.
    (1) works
    Code (vJASS):
    function Projectly_LineMove takes real Distance, real Speed, real Angle, unit Projectly returns nothing
        local integer Index = RecycleTimers_GetNewIndex()
        set udg_SysTim_Save_Real_01[Index] = Distance
        set udg_SysTim_Save_Real_02[Index] = Speed
        set udg_SysTim_Save_Real_03[Index] = Angle
        set udg_SysTim_Save_Unit_01[Index] = Projectly
        set Projectly = null
        call TimerStart(udg_SysTim_Timer[Index], 0.04, false, function Projectly_LineMove_Callback)
    endfunction

    function Projectly_LineMove_Callback takes nothing returns nothing
    // LOCALS
        local integer Index = RecycleTimers_GetCurrentIndex(GetExpiredTimer())
        local real Distance = udg_SysTim_Save_Real_01[Index]
        local real Speed = udg_SysTim_Save_Real_02[Index]
        local real Angle = udg_SysTim_Save_Real_03[Index]
        local unit Projectly = udg_SysTim_Save_Unit_01[Index]
        local location TempPoint = GetUnitLoc(Projectly)

    // MAIN
        if Distance >= 0.04 * Speed then
            call SetUnitX(Projectly, (GetLocationX(TempPoint) + Cos(Angle) * 0.04 * Speed))
            call SetUnitY(Projectly, (GetLocationY(TempPoint) + Sin(Angle) * 0.04 * Speed))
            set Distance = Distance - (0.04 * Speed)
        else
            call SetUnitX(Projectly, (GetLocationX(TempPoint) + Cos(Angle) * Distance))
            call SetUnitY(Projectly, (GetLocationY(TempPoint) + Sin(Angle) * Distance))
            set Distance = 0
        endif
        if Distance != 0 and IsUnitAlive(Projectly) == TRUE then
            set udg_SysTim_Save_Real_01[Index] = Distance
            call TimerStart(udg_SysTim_Timer[Index], 0.04, false, function Projectly_LineMove_Callback)
        else
        // END OF DISTANCE NULL
            set udg_SysTim_Save_Real_01[Index] = 0.00
            set udg_SysTim_Save_Real_02[Index] = 0.00
            set udg_SysTim_Save_Real_03[Index] = 0.00
            set udg_SysTim_Save_Unit_01[Index] = null
            call RecycleTimers_RecycleIndex(Index)
            if IsUnitAlive(Projectly) == FALSE then
                call KillUnit(Projectly)
            endif
        endif
    // NULL
        set Projectly = null
        call RemoveLocation(TempPoint)
    endfunction
    (2) bugs
    Code (vJASS):
    function Projectly_ToUnitMove takes real Speed, unit Projectly, unit Target returns nothing
        local integer Index = RecycleTimers_GetNewIndex()
        set udg_SysTim_Save_Real_01[Index] = Speed
        set udg_SysTim_Save_Unit_01[Index] = Projectly
        set udg_SysTim_Save_Unit_02[Index] = Target
        set Projectly = null
        set Target = null
        call TimerStart(udg_SysTim_Timer[Index], 0.04, false, function Projectly_ToUnitMove_Callback)
    endfunction

    function Projectly_ToUnitMove_Callback takes nothing returns nothing
    // LOCALS
        local integer Index = RecycleTimers_GetCurrentIndex(GetExpiredTimer())
        local real Speed = udg_SysTim_Save_Real_01[Index]
        local unit Projectly = udg_SysTim_Save_Unit_01[Index]
        local unit Target = udg_SysTim_Save_Unit_02[Index]
        local location TempPoint_Proc = GetUnitLoc(Projectly)
        local location TempPoint_Targ = GetUnitLoc(Target)
        local real Angle = AngleBetweenPoints(TempPoint_Proc, TempPoint_Targ)
        local real Distance = DistanceBetweenPoints(TempPoint_Proc, TempPoint_Targ)

    // MAIN
        if Distance >= 0.04 * Speed then
            call SetUnitX(Projectly, (GetLocationX(TempPoint_Proc) + Cos(Angle) * 0.04 * Speed))
            call SetUnitY(Projectly, (GetLocationY(TempPoint_Proc) + Sin(Angle) * 0.04 * Speed))
        else
            call SetUnitX(Projectly, (GetLocationX(TempPoint_Proc) + Cos(Angle) * Distance))
            call SetUnitY(Projectly, (GetLocationY(TempPoint_Proc) + Sin(Angle) * Distance))
        endif
        if IsUnitAlive(Projectly) == TRUE then
            call TimerStart(udg_SysTim_Timer[Index], 0.04, false, function Projectly_ToUnitMove_Callback)
        else
            set udg_SysTim_Save_Real_01[Index] = 0.00
            set udg_SysTim_Save_Unit_01[Index] = null
            set udg_SysTim_Save_Unit_02[Index] = null
        endif

    // NULL
        set Projectly = null
        set Target = null
        call RemoveLocation(TempPoint_Proc)
        call RemoveLocation(TempPoint_Targ)
    endfunction
     
     
  2. chobibo

    chobibo

    Joined:
    Sep 24, 2005
    Messages:
    2,692
    Resources:
    0
    Resources:
    0
    You forgot to add the termination condition(the condition to terminate the knockback) on code (2). Also, your RecycledTimer system might also cause the knockback to fail by a limit op termination due to the seek algorithm if there are a lot of timers on the stack.
     
  3. McQvaBlood

    McQvaBlood

    Joined:
    Jul 3, 2010
    Messages:
    536
    Resources:
    44
    Packs:
    4
    Maps:
    40
    Resources:
    44
    You're right, of course, about the RecycledTimer system. I will fix that.

    However, the lack of such condition on (2) is intended. You may notice that the timer only keeps restarting so long as the projectly unit is still alive. The intention here is that another trigger will kill the projectly unit when it has done whatever was required of it. (Such as simply reach the target, or follow it for X seconds.) Once that other trigger kills it, the movement also stops.

    (1) has a condition of its own because I know when I want it to clear up, when it has reached its maximum distance. However, (1) also stops if the unit is killed, for when I need it to stop prematurely, such as if it only affects the first target it acquires. (Which would once again be done by another trigger.)
     
  4. chobibo

    chobibo

    Joined:
    Sep 24, 2005
    Messages:
    2,692
    Resources:
    0
    Resources:
    0
    Ah, I think I got it now. You need to convert the angle to radians. IIRC, AngleBetweenPoints return angle in degrees and jass' Trigonometric functions require them in radians.

    local real Angle = AngleBetweenPoints(TempPoint_Proc, TempPoint_Targ)*bj_DEGTORAD


    EDIT: I'd suggest inlining the function, since the BJ function uses bj_RADTODEG.
     
  5. McQvaBlood

    McQvaBlood

    Joined:
    Jul 3, 2010
    Messages:
    536
    Resources:
    44
    Packs:
    4
    Maps:
    40
    Resources:
    44
    Oh my, many thanks to you.
    I never thought of this as the same problem is present in (1). However, I tested (1) with 45 as an angle. Turns out 45 radians are about 58 degrees. I didn't notice that (1) was slightly off and just assumed the problem has to be with (2).

    Another thank you for the heads up on my timers.

    This is now resolved and can be closed.