• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece!🔗 Click here to enter!

[JASS] Removing Individual "Links" of a lightning effect array correctly

Status
Not open for further replies.
Level 4
Joined
Sep 25, 2005
Messages
71
Say I have something along the lines of a radial lightning spell. It creates dummy units, it links them with lightning effects. If I want any of these dummies to die on collision with something else, I want the lightning effect to go away with them. Problem is, I can kill the link to the next one, but if I try to use lightningarraynamehere[iterator - 1] (or iterator + 1) the game crashes.
JASS:
                           if INDIVID_DIE_ONHIT == true then
                                call KillUnit(localTest.Missiles[Missilecount])
                                call DestroyLightning(localTest.beams[Missilecount])
                                set localTest.beams[Missilecount] = null
                                set localTest.Missiles[Missilecount] = null
                           endif

The current code kills the dummy correctly, nulls it, kills the lightning it links to the next unit with, nulls it, but then the previous lightning defaults to it's first position and center of playable map area. I was to just get rid of the lightning that is linked to the now non-existent unit.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Are you sure you stored the lightning handles correctly. If I remember correctly, destroying non-existing lightning causes the game to crash.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Could you post the struct declaration? A complete script would be nice too, if it's ok with you.
 
Level 4
Joined
Sep 25, 2005
Messages
71
There should be a comment down there somewhere.

JASS:
//==================================/ Projectile Framework /=================================================

scope LightningBlast initializer init

globals

    private constant integer        MISSILE_NUMBER          = 6
    private constant integer        ABIL_ID                 = 'A006'
    private constant integer        DUMMY_ID                = 'o002'
    private constant integer        CASTER_DUMMY_ID         = 0
    private constant integer        CASTER_DUMMY_EX_BUFF    = 0
    private constant integer        CASTER_DUMMY_ABIL_ID    = 0    
    private constant integer        UNIT_HIDE_INTERVAL      = 1   
    
    private constant string         CAST_FX                 = "Abilities\\Spells\\Orc\\LightningBolt\\LightningBoltMissile.mdl"
    private constant string         DUMMY_CAST_FX           = null
    private constant string         HIT_FX                  = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
    private constant string         DEATH_MISSILE_FX        = null
    private constant string         DEATH_ENEMY_FX          = null
    private constant string         TRAIL_FX                = null
    private constant string         LIGHTNING_FX            = "FORK"
    private constant string         FX_ATTACH_POINT         = "chest"
    private constant string         DEATH_FX_ATTACH_POINT   = null   
    private constant string         CASTER_DUMMY_ORDER      = null
    
    private constant real           COLLISION_SIZE          = 128.
    private constant real           DUMMY_EX_TIME           = 1.
    private constant real           FX_RED                  = 0.35
    private constant real           FX_GREEN                = 0.65
    private constant real           FX_BLUE                 = 1.
    private constant real           CAST_RANGE              = 350.
    private constant real           RANGE_PER_LEVEL         = 0.
    private constant real           TRANSPARENCY_THRESH     = 0.5
    private constant real           TRANSPARENCY_INCREMENT  = 0.1
    private constant real           MISSILE_ANGLE           = 20.
    private constant real           MISSILE_ANGLE_LVL_REDUC = 0.
    private constant real           MISSILE_MOVESPEED       = 20.
    private constant real           FX_Z                    = 36.
    private constant real           DAMAGE_AMOUNT           = 85.
    private constant real           DAMAGE_PER_LEVEL        = 0.
	private constant real           TIMING_INTERVAL         = 0.5// Slowed down from 0.02 temporarily to make it visible
    private constant real           CAST_ANGLE_OFFSET       = ((MISSILE_NUMBER * MISSILE_ANGLE) * .5) + (.5 * MISSILE_ANGLE)
    
    private constant boolean        KILLS_TREES             = false
    private constant boolean        HAS_DUMMY_CAST_FX       = false
    private constant boolean        HAS_TRAIL_FX            = false
    private constant boolean        FX_ON_COLLISION         = true
    private constant boolean        FX_ON_DEATH_MISSILE     = false
    private constant boolean        FX_ON_DEATH_ENEMY       = false
    private constant boolean        DIES_ON_COLLISION       = false
    private constant boolean        INDIVID_DIE_ONHIT       = true
    private constant boolean        MISSILES_EXPLODE        = false
    private constant boolean        MISSILES_EXPLODE_ONHIT  = false
    private constant boolean        DEALS_DAMAGE            = true
    private constant boolean        CASTS_SPELL_ON_TARGET   = false
    private constant boolean        CASTS_SPELL_ON_POINT    = false
    private constant boolean        CASTS_SPELL_NOTARGET    = false
    private constant boolean        SPELL_HAS_LEVELS        = false
    private constant boolean        IS_TARGETED             = true
    private constant boolean        IS_UNIT_TARGETED        = false
    private constant boolean        HAS_HOMING              = false
    private constant boolean        HIDING_UNITS            = false
    private constant boolean        HIDING_UNITS_INTERVAL   = false    
    private constant boolean        IS_CIRCLE               = MISSILE_ANGLE * MISSILE_NUMBER == 360.

	private timer LightningBlastTimer = CreateTimer()
	private integer LightningBlastInstances = 0
	private group LightningBlastEnumGroup = CreateGroup()
	private LightningBlastData array LightningBlastArray
endglobals

private function TreeFilter takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == 'ATtr' or d == 'BTtw' or d == 'KTtw' or d == 'YTft' or d == 'JTct' or d == 'YTst' or d == 'YTct' or d == 'YTwt' or d == 'JTwt' or d == 'JTwt' or d == 'FTtw' or d == 'CTtr' or d == 'ITtw' or d == 'NTtw' or d == 'OTtw' or d == 'ZTtw' or d == 'WTst' or d == 'LTlt' or d == 'GTsh' or d == 'Xtlt' or d == 'WTtw' or d == 'Attc' or d == 'BTtc' or d == 'CTtc' or d == 'ITtc' or d == 'NTtc' or d == 'ZTtc' /*
    */and GetDestructableLife(GetFilterDestructable()) > 0.405
endfunction

private function KillTree takes nothing returns nothing
	call KillDestructable(GetEnumDestructable())
endfunction

private function MoveUnit takes unit movingUnit, real speed, real angle returns nothing
        local real x
        local real y
        set x = GetUnitX(movingUnit) + speed * Cos(angle * bj_DEGTORAD)
        set y = GetUnitY(movingUnit) + speed * Sin(angle * bj_DEGTORAD)
        call SetUnitBoundedX(movingUnit, x)
        call SetUnitBoundedY(movingUnit, y)
endfunction

struct LightningBlastData

    boolean Missilesexplode
    unit caster
    unit targetedunit
    unit array Missiles[MISSILE_NUMBER]
    player casterOwner
    lightning array beams[MISSILE_NUMBER]
    integer casterLevel
    integer dummySpellLevel
    real distance
    real maxDist
    real transparency
    group LightningBlastTargets
   
    private static method LightningBlastExecution takes nothing returns nothing
        local boolexpr TreeCheck
        local integer i = 0
        local integer d
        local integer Missilecount = 0
        local location zLoc
        local unit enemy
        local unit casterDummy
        local effect collisionFX
        local effect trailFX
        local effect dummycastFX
        local real x
        local real y
        local real array z
    	local rect area  
        local LightningBlastData localLightningBlast
        
        loop
            exitwhen i >= LightningBlastInstances
            set localLightningBlast = LightningBlastArray[i]
    
            if localLightningBlast.distance < localLightningBlast.maxDist then
            if localLightningBlast.transparency > 0. and (localLightningBlast.distance / localLightningBlast.maxDist) > TRANSPARENCY_THRESH then
                set localLightningBlast.transparency = localLightningBlast.transparency - TRANSPARENCY_INCREMENT
            endif
            loop
                exitwhen Missilecount == MISSILE_NUMBER    
                    call MoveUnit(localLightningBlast.Missiles[Missilecount], MISSILE_MOVESPEED, GetUnitFacing(localLightningBlast.Missiles[Missilecount]))
                    if HAS_HOMING == true and IS_UNIT_TARGETED == true then
                        call SetUnitFacing(localLightningBlast.Missiles[Missilecount], /*
                        */bj_RADTODEG*Atan2((GetUnitY(localLightningBlast.targetedunit)-GetUnitY(localLightningBlast.Missiles[Missilecount])), /*
                        */(GetUnitX(localLightningBlast.targetedunit)-GetUnitX(localLightningBlast.Missiles[Missilecount]))))
                    endif
                    set x = GetUnitX(localLightningBlast.Missiles[Missilecount])
                    set y = GetUnitY(localLightningBlast.Missiles[Missilecount])
                    set TreeCheck = Filter(function TreeFilter)
                    set area =  Rect(x - COLLISION_SIZE, y - COLLISION_SIZE, x + COLLISION_SIZE, y + COLLISION_SIZE)
                    set zLoc = Location(x, y)
                    set z[Missilecount] = FX_Z + GetLocationZ(zLoc)
                    call SetUnitFlyHeight(localLightningBlast.Missiles[Missilecount], FX_Z, 0.)
                    if HAS_TRAIL_FX == true then
                        set trailFX = AddSpecialEffect(TRAIL_FX, GetUnitX(localLightningBlast.Missiles[Missilecount]), GetUnitY(localLightningBlast.Missiles[Missilecount]))
                        call DestroyEffect(trailFX)
                    endif
                    if ModuloInteger(Missilecount, UNIT_HIDE_INTERVAL) == 0 and HIDING_UNITS == true then
                        call SetUnitVertexColor(localLightningBlast.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), 0)
                    else 
                        if ModuloInteger(Missilecount, UNIT_HIDE_INTERVAL) != 0 and HIDING_UNITS_INTERVAL == true then
                            call SetUnitVertexColor(localLightningBlast.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), 0)
                        else
                            call SetUnitVertexColor(localLightningBlast.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), R2I(localLightningBlast.transparency*255))   
                        endif
                    endif
                    if Missilecount != MISSILE_NUMBER then
                        call MoveLightningEx(localLightningBlast.beams[Missilecount], true, GetUnitX(localLightningBlast.Missiles[Missilecount-1]), GetUnitY(localLightningBlast.Missiles[Missilecount-1]), z[Missilecount-1], GetUnitX(localLightningBlast.Missiles[Missilecount]), GetUnitY(localLightningBlast.Missiles[Missilecount]), z[Missilecount])
                        if localLightningBlast.beams[0] != null then
                            call MoveLightningEx(localLightningBlast.beams[0], true, GetUnitX(localLightningBlast.Missiles[0]), GetUnitY(localLightningBlast.Missiles[0]), z[0], GetUnitX(localLightningBlast.Missiles[Missilecount]), GetUnitY(localLightningBlast.Missiles[Missilecount]), z[Missilecount])
                        endif
                    endif
                    call SetLightningColor(localLightningBlast.beams[Missilecount],FX_RED,FX_GREEN,FX_BLUE,localLightningBlast.transparency)
                    if KILLS_TREES == true then
                        call EnumDestructablesInRect(area, TreeCheck, function KillTree)
                    endif
                    call GroupEnumUnitsInRange(LightningBlastEnumGroup, GetUnitX(localLightningBlast.Missiles[Missilecount]), GetUnitY(localLightningBlast.Missiles[Missilecount]), COLLISION_SIZE, null)
                    loop
                        set enemy = FirstOfGroup(LightningBlastEnumGroup)
                        exitwhen enemy == null
                        if ValidAliveTargetGroupedGround(enemy, localLightningBlast.caster, localLightningBlast.LightningBlastTargets) == true then
                            call GroupAddUnit(localLightningBlast.LightningBlastTargets, enemy)
                            if DEALS_DAMAGE == true then
                                call UnitDamageTarget(localLightningBlast.caster, enemy, DAMAGE_AMOUNT + (DAMAGE_PER_LEVEL * localLightningBlast.casterLevel), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
                            endif
                            if CASTS_SPELL_ON_TARGET == true then
                                set casterDummy = CreateUnit(localLightningBlast.casterOwner,CASTER_DUMMY_ID,/*
                                */GetUnitX(enemy),/*
                                */GetUnitY(enemy),/*
                                */bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localLightningBlast.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localLightningBlast.Missiles[Missilecount])))
                                call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
                                if SPELL_HAS_LEVELS == true then
                                    call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localLightningBlast.dummySpellLevel)
                                endif
                                call IssueTargetOrder(casterDummy, CASTER_DUMMY_ORDER, enemy)
                                if HAS_DUMMY_CAST_FX == true then
                                    set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
                                    call DestroyEffect(dummycastFX)
                                endif
                                call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
                            endif
                            if CASTS_SPELL_ON_POINT == true then
                                set casterDummy = CreateUnit(localLightningBlast.casterOwner,CASTER_DUMMY_ID,/*
                                */GetUnitX(enemy),/*
                                */GetUnitY(enemy),/*
                                */bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localLightningBlast.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localLightningBlast.Missiles[Missilecount])))
                                call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
                                if SPELL_HAS_LEVELS == true then
                                    call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localLightningBlast.dummySpellLevel)
                                endif
                                call IssuePointOrder(casterDummy, CASTER_DUMMY_ORDER, GetUnitX(enemy), GetUnitY(enemy))
                                if HAS_DUMMY_CAST_FX == true then
                                    set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
                                    call DestroyEffect(dummycastFX)
                                endif
                                call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
                            endif
                            if CASTS_SPELL_NOTARGET == true then
                                set casterDummy = CreateUnit(localLightningBlast.casterOwner,CASTER_DUMMY_ID,/*
                                */GetUnitX(enemy),/*
                                */GetUnitY(enemy),/*
                                */bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localLightningBlast.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localLightningBlast.Missiles[Missilecount])))
                                call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
                                if SPELL_HAS_LEVELS == true then
                                    call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localLightningBlast.dummySpellLevel)
                                endif
                                call IssueImmediateOrder(casterDummy, CASTER_DUMMY_ORDER)
                                if HAS_DUMMY_CAST_FX == true then
                                    set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
                                    call DestroyEffect(dummycastFX)
                                endif
                                call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
                            endif
                            if FX_ON_COLLISION == true then
                                set collisionFX = AddSpecialEffectTarget(HIT_FX, enemy, FX_ATTACH_POINT)
                                call DestroyEffect(collisionFX)
                            endif
                            if DIES_ON_COLLISION == true then
                                if FX_ON_DEATH_MISSILE == true then
                                    set collisionFX = AddSpecialEffect(DEATH_MISSILE_FX, GetUnitX(localLightningBlast.Missiles[Missilecount]),GetUnitY(localLightningBlast.Missiles[Missilecount]) )
                                    call DestroyEffect(collisionFX)
                                endif
                                if FX_ON_DEATH_ENEMY == true then
                                    set collisionFX = AddSpecialEffectTarget(DEATH_ENEMY_FX, enemy, DEATH_FX_ATTACH_POINT)
                                    call DestroyEffect(collisionFX)
                                endif
                                if MISSILES_EXPLODE_ONHIT == true then
                                    set localLightningBlast.Missilesexplode = true
                                endif
                                set localLightningBlast.distance = localLightningBlast.maxDist
                            endif
                            if INDIVID_DIE_ONHIT == true then
                                if FX_ON_DEATH_MISSILE == true then
                                    set collisionFX = AddSpecialEffect(DEATH_MISSILE_FX, GetUnitX(localLightningBlast.Missiles[Missilecount]),GetUnitY(localLightningBlast.Missiles[Missilecount]) )
                                    call DestroyEffect(collisionFX)
                                endif
                                if FX_ON_DEATH_ENEMY == true then
                                    set collisionFX = AddSpecialEffectTarget(DEATH_ENEMY_FX, enemy, DEATH_FX_ATTACH_POINT)
                                    call DestroyEffect(collisionFX)
                                endif
                                if MISSILES_EXPLODE_ONHIT == true then
                                    set localLightningBlast.Missilesexplode = true
                                endif
                                // Here's where I'm trying to implement the good stuff
                                
                                call DestroyLightning(localLightningBlast.beams[Missilecount])
                                set localLightningBlast.beams[Missilecount] = null
                                
                                
                                // 
                                if localLightningBlast.Missilesexplode == false then
                                    call RemoveUnit(localLightningBlast.Missiles[Missilecount])
                                endif
                                if localLightningBlast.Missilesexplode == true then
                                    call KillUnit(localLightningBlast.Missiles[Missilecount])
                                endif
                                set localLightningBlast.Missiles[Missilecount] = null
                            endif
                        endif
                        call GroupRemoveUnit(LightningBlastEnumGroup, enemy)
                    endloop
                                  
               set Missilecount = Missilecount + 1
            endloop
                set Missilecount = 0
                set localLightningBlast.distance = localLightningBlast.distance + MISSILE_MOVESPEED
            endif
            
            if localLightningBlast.distance >= localLightningBlast.maxDist then
                set LightningBlastInstances = LightningBlastInstances - 1
                set LightningBlastArray[i] = LightningBlastArray[LightningBlastInstances]
                call DestroyGroup(localLightningBlast.LightningBlastTargets)
                call localLightningBlast.destroy()
            endif	
            call RemoveRect(area)
            call DestroyBoolExpr(TreeCheck)
            set area = null
            set casterDummy = null
            set zLoc = null
            set trailFX = null
            set collisionFX = null
            set dummycastFX = null
            set i = i + 1
        endloop

        if LightningBlastInstances == 0 then
            call PauseTimer(LightningBlastTimer)
        endif
    endmethod
    
    static method create takes unit whichUnit, real range returns LightningBlastData
        local integer i = 0
        local LightningBlastData data = LightningBlastData.allocate()
        local effect casterFX
        local location targetLoc = GetSpellTargetLoc()
        local real targetLocX = GetLocationX(targetLoc)
        local real targetLocY = GetLocationY(targetLoc)
        local real MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
        if IS_TARGETED == false then
            set targetLoc = Location(GetUnitX(whichUnit), GetUnitY(whichUnit))
            set targetLocX = GetLocationX(targetLoc)
            set targetLocY = GetLocationY(targetLoc)
            set MissileFacing = GetUnitFacing(whichUnit) - CAST_ANGLE_OFFSET
        endif
        if IS_UNIT_TARGETED == true then
            set data.targetedunit = GetSpellTargetUnit()
            set targetLoc = Location(GetUnitX(data.targetedunit), GetUnitY(data.targetedunit))
            set targetLocX = GetLocationX(targetLoc)
            set targetLocY = GetLocationY(targetLoc)
            set MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
        endif
        set data.LightningBlastTargets = CreateGroup()
        set data.caster =  whichUnit
        set data.casterLevel = GetUnitAbilityLevel(data.caster, ABIL_ID)
        if SPELL_HAS_LEVELS == true then
            set data.dummySpellLevel = GetUnitAbilityLevel(data.caster, ABIL_ID)
        endif
        set data.casterOwner = GetOwningPlayer(data.caster)
        set data.distance = 0.
        set data.maxDist = range + (RANGE_PER_LEVEL * data.casterLevel)
        set data.transparency = 1.
        set data.Missilesexplode = MISSILES_EXPLODE
        
        set casterFX = AddSpecialEffect(CAST_FX, GetUnitX(whichUnit), GetUnitY(whichUnit))
        call DestroyEffect(casterFX)
        set casterFX = null     
        loop
            exitwhen i == MISSILE_NUMBER
            
            call SetUnitPathing(data.caster, false)
            set data.Missiles[i] = CreateUnit(data.casterOwner, DUMMY_ID, GetUnitX(data.caster), GetUnitY(data.caster), (MissileFacing))
            call SetUnitPathing(data.Missiles[i], false)
            call SetUnitX(data.Missiles[i], GetUnitX(data.caster))
            call SetUnitY(data.Missiles[i], GetUnitY(data.caster))
            call SetUnitPathing(data.caster, true)
            call UnitAddAbility(data.Missiles[i], 'Amrf')
            call UnitRemoveAbility(data.Missiles[i], 'Amrf')
            call SetUnitVertexColor(data.Missiles[i], 255, 255, 255, 255)
            set MissileFacing = MissileFacing+(MISSILE_ANGLE - (MISSILE_ANGLE_LVL_REDUC * data.casterLevel))
            call SetUnitFacing(data.Missiles[i], MissileFacing)
            if IS_CIRCLE == true then
                set data.beams[0] = AddLightningEx(LIGHTNING_FX, true, GetUnitX(data.Missiles[0]), GetUnitY(data.Missiles[0]), FX_Z, GetUnitX(data.Missiles[i]), GetUnitY(data.Missiles[i]), FX_Z)
            endif
            if i != 0 then
                set data.beams[i] = AddLightningEx(LIGHTNING_FX, true, GetUnitX(data.Missiles[i-1]), GetUnitY(data.Missiles[i-1]), FX_Z, GetUnitX(data.Missiles[i]), GetUnitY(data.Missiles[i]), FX_Z)
            endif
            
            set i = i + 1
        endloop
                
        set LightningBlastArray[LightningBlastInstances] = data        
        
    	if LightningBlastInstances <= 0 then
            call TimerStart(LightningBlastTimer, TIMING_INTERVAL, true, function LightningBlastData.LightningBlastExecution)
        endif
        
        set LightningBlastInstances = LightningBlastInstances + 1
        set targetLoc = null
        
        return data
    endmethod
    
    method onDestroy takes nothing returns nothing
    local integer i = 0
    if .Missilesexplode == false then
    loop
        exitwhen i == MISSILE_NUMBER
        call RemoveUnit(.Missiles[i])
        set .Missiles[i] = null
        set i = i + 1
    endloop
    endif
    if .Missilesexplode == true then
    loop
        exitwhen i == MISSILE_NUMBER
        call KillUnit(.Missiles[i])
        set .Missiles[i] = null
        set i = i + 1
    endloop
    endif
    set i = 0
    loop
        exitwhen i == MISSILE_NUMBER
        call DestroyLightning(.beams[i])
        set .beams[i] = null
        set i = i + 1
    endloop
    set .caster = null
    set .targetedunit = null
    set .casterOwner = null
    set .LightningBlastTargets = null
    endmethod
    
endstruct

//===================================/ Condition /===========================================================

private function checkLightningBlast takes nothing returns boolean
    local LightningBlastData localLightningBlast
	if GetSpellAbilityId() == ABIL_ID then
        set localLightningBlast = LightningBlastData.create(GetTriggerUnit(), CAST_RANGE)
	endif
	return false
endfunction

//===================================/ Init /================================================================

private function init takes nothing returns nothing
	local trigger localTrigVar = CreateTrigger()
	call TriggerRegisterAnyUnitEventBJ(localTrigVar, EVENT_PLAYER_UNIT_SPELL_EFFECT)
	call TriggerAddCondition( localTrigVar, Condition(function checkLightningBlast))
	set localTrigVar = null
endfunction

endscope
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Have you tried checking if the other lightning handles are being saved properly? I think it has to do with the struct array. I'll still examine some more.

@mckill
He didn't initialize anything on the struct member declaration.
 
There are a few weird things:
JASS:
if IS_CIRCLE == true then
                set data.beams[0] = AddLightningEx(LIGHTNING_FX, true, GetUnitX(data.Missiles[0]), GetUnitY(data.Missiles[0]), FX_Z, GetUnitX(data.Missiles[i]), GetUnitY(data.Missiles[i]), FX_Z)
            endif
This is a bit odd, because IS_CIRCLE is a constant. If it is true, then it will be true permanently (meaning, it will create a new lightning every time in the loop, so beams[0] will be assigned to one lightning, but there will be 5 or so left over).

Now, the reason why you may get crashes is because you are either using an invalid index or you are referencing a lightning that is already destroyed. To fix this, you first have to perform boundary checks. Let's say you want to destroy the lightning at [i+1], you would want to make sure:
if (i < MISSILE_NUMBER - 1) then
If you want to destroy the lightning at [i-1], you would want to make sure:
if (i > 0) then
Because otherwise you'll end up with -1 as an index.

Finally, whenever you destroy a lightning, you want to make sure you set the beam reference to null. Like so:
JASS:
call DestroyLightning(localLightningBlast.beams[i])
set localLightningBlast.beams[i] = null
Then whenever you are about to destroy a lightning, just make sure it is not null:
JASS:
if localLightningBlast.beams[i] != null then
    call DestroyLightning(localLightningBlast.beams[i])
    set localLightningBlast.beams[i] = null
endif
This way, you won't accidentally destroy a nonexistent lightning. (you may want to perform the null check on your other functions too, like MoveLightningEx, etc.) By setting the variable to null after you destroy it, it gives you a way to check if the handle was destroyed.
 
Level 4
Joined
Sep 25, 2005
Messages
71
There are a few weird things:
JASS:
if IS_CIRCLE == true then
                set data.beams[0] = AddLightningEx(LIGHTNING_FX, true, GetUnitX(data.Missiles[0]), GetUnitY(data.Missiles[0]), FX_Z, GetUnitX(data.Missiles[i]), GetUnitY(data.Missiles[i]), FX_Z)
            endif
This is a bit odd, because IS_CIRCLE is a constant. If it is true, then it will be true permanently (meaning, it will create a new lightning every time in the loop, so beams[0] will be assigned to one lightning, but there will be 5 or so left over).

I can demonstrate. Basically, there's no evidence this actually happens. I mean, I could set it to "if data.beams[0] != null" and that'd be fine though.

Now, the reason why you may get crashes is because you are either using an invalid index or you are referencing a lightning that is already destroyed. To fix this, you first have to perform boundary checks. Let's say you want to destroy the lightning at [i+1], you would want to make sure:
if (i < MISSILE_NUMBER - 1) then
If you want to destroy the lightning at [i-1], you would want to make sure:
if (i > 0) then
Because otherwise you'll end up with -1 as an index.

Finally, whenever you destroy a lightning, you want to make sure you set the beam reference to null. Like so:
JASS:
call DestroyLightning(localLightningBlast.beams[i])
set localLightningBlast.beams[i] = null
Then whenever you are about to destroy a lightning, just make sure it is not null:
JASS:
if localLightningBlast.beams[i] != null then
    call DestroyLightning(localLightningBlast.beams[i])
    set localLightningBlast.beams[i] = null
endif
This way, you won't accidentally destroy a nonexistent lightning. (you may want to perform the null check on your other functions too, like MoveLightningEx, etc.) By setting the variable to null after you destroy it, it gives you a way to check if the handle was destroyed.

I have the issue with as follows:

JASS:
                                if localLightningBlast.beams[Missilecount] != null then
                                    call DestroyLightning(localLightningBlast.beams[Missilecount])
                                    set localLightningBlast.beams[Missilecount] = null
                                endif
                                
                                if localLightningBlast.beams[Missilecount - 1] != null then
                                    call DestroyLightning(localLightningBlast.beams[Missilecount - 1])
                                    set localLightningBlast.beams[Missilecount] = null
                                endif

EDIT: Tried +1 instead of -1. Working so far.
 
Status
Not open for further replies.
Top