• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!
  • ✅ The POLL for Hive's Texturing Contest #34 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!
  • ✅ The POLL for Hive's Techtree Contest #20 is OPEN! Vote for the TOP 3 FACTIONS! 🔗Click here to cast your vote!

Lightning spell does desync sometimes

Level 10
Joined
May 24, 2016
Messages
358
The ability is based on Pocket Factory. After casting, a Factory (nfac) is created (this is by defauth). The code waits for the projectile travel time, then finds the spawned Factory and stores its handle.


A periodic timer (0.03 s) is then started. Every tick it:


  • checks whether both the caster and the Factory are still alive;
  • if the caster is within the Factory's range, it creates (or updates) a lightning effect between the Factory and the caster;
  • if the caster leaves the range, it destroys the lightning;
  • if the caster enters the range again, it recreates the lightning;
  • while the lightning is active, it periodically restores mana to the caster;
  • when either the caster or the Factory dies, it destroys the lightning, visual effects, sound, flushes the hashtable, and stops the timer.

The issue: the desync does not occur when the lightning is created for the first time. It only happens after the lightning has been destroyed and then recreated.


Reproduction steps:


  1. Cast the ability and create the Factory.
  2. The lightning is created and works correctly.
  3. Move outside the Factory's range so the lightning is destroyed.
  4. Move back into range so the lightning is recreated.
  5. The game desyncs immediately after the lightning is recreated.

The first AddLightningEx() always works correctly. The desync appears to be related specifically to the DestroyLightning() → AddLightningEx() reconnection cycle, or to code executed during that transition.
JASS:
//Spell registration. Nothing strange here
// L_point, L_point2 are global locs that are declared before 
if spellid == 'ANsy' then
        set x = GetSpellTargetX()
    set y = GetSpellTargetY()

    set cx = GetUnitX(c)
    set cy = GetUnitY(c)

set dx = x - cx
set dy = y - cy

set L_point = GetUnitLoc(c)
set L_point2 = Location(x,y)

set dz = GetLocationZ(L_point2) - GetLocationZ(L_point)

set dist = SquareRoot(dx*dx + dy*dy + dz*dz)

call RemoveLocation(L_point)
call RemoveLocation(L_point2)

    set t = CreateTimer()
    set idT = GetHandleId(t)

    call SaveUnitHandle(Hash,idT,0,c)
    call SaveReal(Hash,idT,1,x)
    call SaveReal(Hash,idT,2,y)
    call SaveInteger(Hash,idT,7171,GetUnitAbilityLevel(c,'ANsy'))

    call TimerStart(t,dist/1000.+0.5,false,function MechaGenerator_L)

JASS:
function MechaGenerator_Destroy takes timer t returns nothing
    local integer idT = GetHandleId(t)

    local unit gen = LoadUnitHandle(Hash,idT,3)
    local lightning l = LoadLightningHandle(Hash,idT,66)
    local effect ef = LoadEffectHandle(Hash,idT,67)
    local sound s = LoadSoundHandle(Hash,idT,73)

    if gen != null and not IsUnitDead(gen) then
        call KillUnit(gen)
    endif

    if l != null then
        call DestroyLightning(l)
    endif

    if ef != null then
        call DestroyEffect(ef)
    endif

    if s != null then
        call StopSound(s,true,false)
        call KillSoundWhenDone(s)
    endif

    call FlushChildHashtable(Hash,idT)
    call DestroyTimer(t)

    set gen = null
    set l = null
    set ef = null
    set s = null
    set t = null
endfunction
    
    function MechaGenerator_FindGenerator takes unit caster, real x, real y, integer lvl returns unit
    local group g = CreateGroup()
    local unit u
    local unit result = null

    call GroupEnumUnitsInRange(g, x, y, 600., null)

    loop
        set u = FirstOfGroup(g)
        exitwhen u == null

        call GroupRemoveUnit(g, u)

        if GetUnitTypeId(u) == 'nfac' and GetOwningPlayer(u) == GetOwningPlayer(caster) and not IsUnitDead(u) then

            if lvl > 1 then
                call UnitAddAbility(u,'A04C')

                if lvl == 2 then
                    call SetUnitAbilityLevel(u,'A04C',3)
                else
                    call SetUnitAbilityLevel(u,'A04C',5)
                endif

                call SetUnitScale(u,1.2,1.2,1.2)
                call UnitRemoveAbility(u,'A04C')
            endif

            set result = u
            exitwhen true
        endif
    endloop

    call DestroyGroup(g)

    set g = null
    set u = null

    return result
endfunction


function MechaGenerator_L2 takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer idT = GetHandleId(t)

    local unit c = LoadUnitHandle(Hash,idT,0)
    local unit gen = LoadUnitHandle(Hash,idT,3)

    local lightning l = LoadLightningHandle(Hash,idT,66)
    local effect ef = LoadEffectHandle(Hash,idT,67)
    local sound s = LoadSoundHandle(Hash,idT,73)

    local real x = LoadReal(Hash,idT,1)
    local real y = LoadReal(Hash,idT,2)

    local real cx
    local real cy
    local real gx
    local real gy

    local real dist
    local real maxDistance = 600.

    local integer lvl = LoadInteger(Hash,idT,7171)

    local real manaRestore = 0.6

   // local location L_point
    //local location L_point2

    if lvl < 1 then
        set lvl = 1
    endif

    if lvl == 1 then
        set maxDistance = 650.
        set manaRestore = 0.6
    elseif lvl == 2 then
        set maxDistance = 850.
        set manaRestore = 0.85
    else
        set maxDistance = 1050.
        set manaRestore = 1.1
    endif

    // Кастер умер
    if c == null or IsUnitDead(c) then
        call MechaGenerator_Destroy(t)
        return
    endif

    // Первый тик — ищем генератор
    if gen == null then

        set gen = MechaGenerator_FindGenerator(c,x,y,lvl)

        if gen == null then
            call MechaGenerator_Destroy(t)
            return
        endif

        call SaveUnitHandle(Hash,idT,3,gen)

        // Запускаем периодический режим
        call TimerStart(t,0.03,true,function MechaGenerator_L2)

    endif

    // Генератор умер
    if IsUnitDead(gen) then
        call MechaGenerator_Destroy(t)
        return
    endif

    set cx = GetUnitX(c)
    set cy = GetUnitY(c)

    set gx = GetUnitX(gen)
    set gy = GetUnitY(gen)

    set dist = SquareRoot((gx-cx)*(gx-cx)+(gy-cy)*(gy-cy))
        // Кастер вышел из радиуса действия генератора
    if dist > maxDistance then

        if l != null then

            call DestroyLightning(l)
            call SaveLightningHandle(Hash,idT,66,null)

            if ef != null then
                call DestroyEffect(ef)
                call SaveEffectHandle(Hash,idT,67,null)
            endif

            if s != null then
                call StopSound(s,true,false)
                call KillSoundWhenDone(s)
                call SaveSoundHandle(Hash,idT,73,null)
            endif

        endif

    else

        // Создаем молнию первый раз
        if l == null then

            set L_point = GetUnitLoc(c)
            set L_point2 = GetUnitLoc(gen)

            set l = AddLightningEx("CLPB",true,GetLocationX(L_point),GetLocationY(L_point),GetLocationZ(L_point)+50.,GetLocationX(L_point2),GetLocationY(L_point2),GetLocationZ(L_point2)+50.)

            call RemoveLocation(L_point)
            call RemoveLocation(L_point2)

            call SetLightningColor(l,100./255.,60./255.,255./255.,1.)

            call SaveLightningHandle(Hash,idT,66,l)

            call DestroyEffect(AddSpecialEffectTarget("war3mapImported\\GearChangeBlue.mdx",c,"chest"))

            set ef = AddSpecialEffectTarget("war3mapImported\\RefreshingSurge.mdx",c,"chest")
            call SaveEffectHandle(Hash,idT,67,ef)

            set s = CreateSound("Abilities\\Spells\\Human\\CloudOfFog\\CloudOfFogLoop1.wav",true,true,true,10,10,"DefaultEAXON")
            call SetSoundVolume(s,60)
            call AttachSoundToUnit(s,c)
            call StartSound(s)

            call SaveSoundHandle(Hash,idT,73,s)

        else

            // Просто двигаем существующую молнию

            set L_point = GetUnitLoc(c)
            set L_point2 = GetUnitLoc(gen)

            call MoveLightningEx(l,true,GetLocationX(L_point),GetLocationY(L_point),GetLocationZ(L_point)+50.,GetLocationX(L_point2),GetLocationY(L_point2),GetLocationZ(L_point2)+50.)

            call RemoveLocation(L_point)
            call RemoveLocation(L_point2)

        endif

        // Восполняем ману
        call SetUnitState(c,UNIT_STATE_MANA,GetUnitState(c,UNIT_STATE_MANA)+manaRestore)

    endif
    
        set c = null
    set gen = null

    set l = null
    set ef = null
    set s = null

    set L_point = null
    set L_point2 = null

    set t = null
endfunction
 
Back
Top