HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Illogical Bug - Anybody Know?

11-10-2006, 08:36 PM#1
xombie
Here is the script:
Collapse JASS:
function EnergySphere_cond takes nothing returns boolean
    return GetSpellAbilityId()=='A000'
endfunction


function EnergySphere_Detonate_UnitFilter takes unit target, unit caster returns boolean
    if not ( IsUnitEnemy( target, GetOwningPlayer( caster ) ) ) then
        return false
    endif
    if IsUnitType( target, UNIT_TYPE_STRUCTURE ) or IsUnitType( target, UNIT_TYPE_MAGIC_IMMUNE ) or IsUnitType( target, UNIT_TYPE_DEAD ) then
        return false
    endif
    return true
endfunction

function EnergySphere_Detonate_Knockback_MoveTimer takes nothing returns nothing
    local timer move = GetExpiredTimer()
    local unit target = GetAttachedUnit( move, "unit" )
    local unit caster = GetAttachedUnit( move, "caster" )
    local real speed = GetAttachedReal( move, "speed" )
    local real angle = GetAttachedReal( move, "angle" )
    local integer index = GetAttachedInteger( move, "index" )
    local real x1 = GetUnitX( target )
    local real y1 = GetUnitY( target )
    local real x2 = x1 + speed * Cos( angle * bj_DEGTORAD )
    local real y2 = y1 + speed * Sin( angle * bj_DEGTORAD )

        if not ( IsTerrainPathable( x2, y2, PATHING_TYPE_WALKABILITY ) or ( speed <= 0.2 ) ) then
            call MoveUnit( target, x2, y2, true )
            if ModuloInteger( index, 20 ) == 0 then
                call DestroyEffect( AddSpecialEffect( "", x2, y2 ) )
            endif
            set speed = speed - 0.6
            call AttachReal( move, "speed", speed )
            call AttachInteger( move, "index", index + 1 )
        else
            call FlushObjectCache( move )
            call PauseTimer( move )
            call DestroyTimer( move )
        endif
        
    set move = null
    set target = null
    set caster = null
endfunction        

function EnergySphere_Detonate_Knockback takes unit target, unit caster, real sourceX, real sourceY returns nothing
    local timer move = CreateTimer()
    local real x = GetUnitX( target )
    local real y = GetUnitY( target )
        local location l = Location( x, y )
        local location m = Location( sourceX, sourceY )
    local real angle = Atan2( y - sourceY, x - sourceX ) * bj_RADTODEG
    local real dist = DistanceBetweenPoints( l, m )
    local real speed = 28 - ( dist / 20 )

                if ( speed > 30 ) then
                        set speed = 30
                endif

        call AttachObject( move, "unit", target )
        call AttachObject( move, "caster", caster )
        call AttachInteger( move, "index", 0 )
        call AttachReal( move, "angle", angle )
        call AttachReal( move, "speed", speed )
        call TimerStart( move, 0.025, true, function EnergySphere_Detonate_Knockback_MoveTimer )
        
        call RemoveLocation( l )
        call RemoveLocation( m )
        set l = null
        set m = null
    set move = null
endfunction

function EnergySphere_Detonate takes unit caster, unit sphere, real damage returns nothing
    local group units1 = CreateGroup()
    local group units2 = CreateGroup()
    local real x = GetUnitX( sphere )
    local real y = GetUnitY( sphere )
    local integer index = 0
    local unit temp
    local unit detonation 
    
                set detonation = CreateUnit( GetOwningPlayer( caster ), 'h001', x, y, GetUnitFacing( sphere ) )
        call SetUnitX( detonation, x )
        call SetUnitY( detonation, y )
        call KillUnit( detonation )
        call KillUnit( sphere )

        call GroupEnumUnitsInRange( units1, x, y, 275, null ) 
        
        //call PolledWait( 0.10 )
        //I use the polled wait so that it will not screw up in BNet games; the problem here is that
        //if the polled wait IS used, then nothing past the call PolledWait(..) will work.
        
        loop
            set temp = FirstOfGroup( units1 )
            exitwhen ( temp == null )
            
            if EnergySphere_Detonate_UnitFilter( temp, caster ) then
                set index = index + 1
                                call GroupAddUnit( units2, temp )
            endif
            call GroupRemoveUnit( units1, temp )
        endloop
        call DestroyGroup( units1 )
        
        loop
            set temp = FirstOfGroup( units2 )
            exitwhen ( temp == null )
            
                        call UnitDamageTarget( caster, temp, ( damage / index ), false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS )
            call EnergySphere_Detonate_Knockback( temp, caster, x, y )
            call GroupRemoveUnit( units2, temp )
        endloop
        call DestroyGroup( units2 )
        
    set units1 = null
    set units2 = null
    set temp = null
endfunction

function EnergySphere_MoveTimer takes nothing returns nothing 
    local timer move = GetExpiredTimer()
    local unit caster = GetAttachedUnit( move, "caster" )
    local unit sphere = GetAttachedUnit( move, "sphere" )
    local real random = GetAttachedReal( move, "random" )
    local real counter = GetAttachedReal( move, "counter" )
    local real angle = GetAttachedReal( move, "angle" )
    local real damage = GetAttachedReal( move, "damage" )
        local integer quit = GetAttachedInteger( move, "quit" )
    
        local real stopTime = GetAttachedReal( move, "stopTime" )
        local real endTime = counter / 40

    local real x1 = GetUnitX( sphere )
    local real y1 = GetUnitY( sphere )
    local real x2 = x1 + 7 * Cos( angle * bj_DEGTORAD )
    local real y2 = y1 + 7 * Sin( angle * bj_DEGTORAD )

        if ( ( counter / 40 ) > ( random ) or IsTerrainPathable( x2, y2, PATHING_TYPE_WALKABILITY ) ) then
            if ( quit == 0 ) then
                                set quit = 1
                                call AttachInteger( move, "quit", quit )
                                call AttachReal( move, "stopTime", endTime )
                        endif
        else
            call MoveUnit( sphere, x2, y2, true )
            set counter = counter + 1.00
            call AttachReal( move, "counter", counter )
        endif
        
                if ( ( endTime - stopTime ) == 1.50 ) then
                    call PauseTimer( move )
                    call FlushObjectCache( move )
                    call DestroyTimer( move )
                    call EnergySphere_Detonate( caster, sphere, damage )
                endif

    set move = null
    set caster = null
    set sphere = null
endfunction

function EnergySphere_main takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local unit sphere
    local location target = GetSpellTargetLoc()
    local timer move = CreateTimer()
    local real x1 = GetUnitX( caster )
    local real y1 = GetUnitY( caster )
    local real x2 = GetLocationX( target )
    local real y2 = GetLocationY( target )
    local real angle = Atan2( y2 - y1, x2 - x1 ) * bj_RADTODEG
    local real x3 = x1 + 40 * Cos( angle * bj_DEGTORAD )
    local real y3 = y1 + 40 * Sin( angle * bj_DEGTORAD )
    local real counter = 0
    local real random = GetRandomReal( 1, 2.5 )
        local real cost = GetHeroInt( caster, true ) * 7
    
        call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareMissile.mdl", caster, "hand,right" ) )
    
        set sphere = CreateUnit( GetOwningPlayer( caster ), 'h002', x3, y3, angle )
        call SetUnitX( sphere, x3 )
        call SetUnitY( sphere, y3 )
        
        call AttachObject( move, "caster", caster )
        call AttachObject( move, "sphere", sphere )
        call AttachReal( move, "random", random )
        call AttachReal( move, "counter", counter )
        call AttachReal( move, "angle", angle )
        call AttachReal( move, "damage", cost )
        call TimerStart( move, 0.025, true, function EnergySphere_MoveTimer )
    
    call RemoveLocation( target )
    set caster = null
    set sphere = null
    set target = null
    set move = null        
endfunction

function InitTrig_EnergySphere takes nothing returns nothing
    local trigger spell = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( spell, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( spell, Condition( function EnergySphere_cond ) )
        call TriggerAddAction( spell, function EnergySphere_main )
endfunction
Okay, now all of THIS code works, but if you remove the "//" infront of the PolledWait I added, it does not (hence the illogical bug that I cannot figure out).. The reason for this wait is to create more realism because when it detonates the model does not blow up instantly, it takes roughly 0.1 seconds.
11-10-2006, 08:39 PM#2
The)TideHunter(
I dont see what PolledWait is helping you do here, it does nothing to help small parts of code like yours, i would only use it to aviod op limit, and its inaccurate.

And please use [jass] tags in future, it really makes your code easier to read.
Code:
Jass tags:
[jass]CODE[/jass]

Example:
[jass]function Hi takes nothing returns nothing
    call SomeFunc()
endfunction[/jass]
11-10-2006, 08:41 PM#3
Captain Griffen
Threads created from timer expirations (not from trigger events, though) break on waits.
11-10-2006, 08:44 PM#4
xombie
Okay, I changed it to for you, but I think this guy solved my problem already, thanks..

Though it really sucks =S

:: edit ::

Oh hold on, are you saying that if you create a thread FROM a timer expiration function, it will pause the thread created? or the timer?
11-10-2006, 10:03 PM#5
Anitarf
Functions that are called when a timer expires, and all the functions that are called from that function (and all functions called from those functions etc... all these functions are executed in one thread), will just die if they hit a wait action. Not sure how it works if you use ExecuteFunc(), though, since that creates a new thread, maybe functions in that thread can then call PolledWait().
11-10-2006, 10:48 PM#6
xombie
I'm just wondering why this happens, shouldn't it just pause the whole thread for the given wait time? Not completely stop it?
11-11-2006, 02:13 AM#7
Vexorian
Use a trigger with a periodic event instead of the timer ... , make sure you don't EVER destroy the trigger before it and every single thread created by the trigger or by a thread created by the trigger ends, though.
11-11-2006, 04:07 AM#8
Toink
Wow, thanks this one helped me, I didn't know how to do knockback functions lol .
11-11-2006, 05:28 AM#9
DioD
trigger is save to destroy if it stored in some array.
just make 64off array with cyclic use and destroy trigger will be always safe.
just becouse trigger leave pointer in this array.
11-11-2006, 11:04 AM#10
Vexorian
That is awful, he could just not set the trigger references to null...
11-11-2006, 06:56 PM#11
xombie
Actually what I did was (when I want the wait) I took all of my actions that I wanted happening after the wait and put them into a new function, then created a timer that would expire in my desired wait time; then it would call the function where all my new actions are...

Just for the record, is PolledWait exactly the same as TriggerSleepAction except on BNet or something when somebody lags it will actually take heed of the lag; It uses a timer so I'm pretty sure that it does, but I wanted to clarify.
11-11-2006, 07:06 PM#12
Captain Griffen
Collapse JASS:
function PolledWait takes real duration returns nothing
    local timer t
    local real  timeRemaining

    if (duration > 0) then
        set t = CreateTimer()
        call TimerStart(t, duration, false, null)
        loop
            set timeRemaining = TimerGetRemaining(t)
            exitwhen timeRemaining <= 0

            // If we have a bit of time left, skip past 10% of the remaining
            // duration instead of checking every interval, to minimize the
            // polling on long waits.
            if (timeRemaining > bj_POLLED_WAIT_SKIP_THRESHOLD) then
                call TriggerSleepAction(0.1 * timeRemaining)
            else
                call TriggerSleepAction(bj_POLLED_WAIT_INTERVAL)
            endif
        endloop
        call DestroyTimer(t)
    endif
endfunction
11-11-2006, 08:01 PM#13
Jazradel
constant real bj_POLLED_WAIT_INTERVAL = 0.10
constant real bj_POLLED_WAIT_SKIP_THRESHOLD = 2.00
As well.
11-11-2006, 08:17 PM#14
xombie
Okay, well I know this is getting a little off-topic (but its still on the original theme of polls and waits);

Isn't the smallest possible TriggerSleepAction and PolledWait value 0.27? I could have swarn it was. I know I could probably set up a timer to detect it but I think its easier to ask; so then also, my question was no answered fully, does PolledWait account for players lagging (and bringing up the lag screen, hence pausing the game) as well as being accurate?
11-11-2006, 08:20 PM#15
Captain Griffen
It accounts for it more. Tends to be 0.2-0.4 seconds too late, while TriggerSleepAction on it's own can vary wildly.

TriggerSleepAction can get down to about 0.1, but it isn't accurate at all.