• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] Spell Bug

Status
Not open for further replies.
My vJass spell keeps bugging up and I can't fix it because I can't figure out what's causing the problem >_>

JASS:
library AtomicBomb requires CTL, SpellEffectEvent, Particle
    
    globals
        // The ability raw code
        private constant integer ABIL_CODE = 'A000'
        // The plane dummy unit raw code
        private constant integer PLANE_CODE = 'h003'
        // The bomb dummy unit raw code
        private constant integer BOMB_CODE = 'h001'
        // Flying Height Enabler
        private constant integer FLYING_HEIGHT_ENABLER = 'Amrf'
        // Explosion Effect
        private constant string EXPLOSION_EFFECT = "Abilities\\Weapons\\FragDriller\\FragDriller.mdl"
        // The effect that is created when the plane is created.
        private constant string PLANE_IN_EFFECT = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
        // The effect that is created when the plane is destroyed.
        private constant string PLANE_OUT_EFFECT = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
        // Bomb and Plane height
        private constant real BOMB_HEIGHT = 700.
        // Bomb Drop Speed Base (It will accelerate starting from this base-speed)
        private constant real BOMB_DROP_SPEED = 1.
        // Bomb Drop Acceleration
        private constant real BOMB_DROP_ACCEL = 1.
        // The dummy owning player
        private constant player DUMMY_OWNER = Player(13)
        // Destroy trees around the explosion?
        private constant boolean DESTROY_TREES = true
        // The offset from the point from the target point for plane creation
        private constant real PLANE_DISTANCE = 800.
        // How long does the plane stay after it drops the bomb?
        private constant real PLANE_AFTER_DROP = 2.
        // Attack type
        private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
        // Damage type
        private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
    endglobals
    
    private function GetPlaneSpeedFactor takes integer level returns real
        return 9.
    endfunction
    
    private function GetDamage takes integer level returns real
        return 150 + level * 75.
    endfunction
    
    private function GetDamageRadius takes integer level returns real
        return 275 + level * 75.
    endfunction
    
    // How close do units have to be for the spell to deal bonus damage?
    private function GetBonusDamageRadius takes integer level returns real
        return 137.5 + level * 37.5
    endfunction
    
    // I want the spell to deal double the damage to units closer than the bonus damage radius.
    private function GetBonusDamageFactor takes integer level returns real
        return 2.
    endfunction
    
    private function TargetFilter takes player owner, unit caster, unit target returns boolean
        // Placeholder
        return true
    endfunction
    
    private struct Spell extends array
    
        private static unit array caster
        private static unit array plane
        private static unit array bomb
        private static real array xDis
        private static real array yDis
        private static real array planeX
        private static real array planeY
        private static real array dropX
        private static real array dropY
        private static real array damage
        private static real array radius
        private static real array bonusDamage
        private static real array bonusRadius
        private static real array speed
        private static real array height
        private static integer array counter
        private static integer array level
        private static boolean array dropped
        private static boolean array exploded
        private static player array owner
        private static Particle array fx
        
        private static unit treeDetector = null
        private static real treesRadius = 0
        private static real treesX = 0
        private static real treesY = 0
        private static method isTree takes destructable d returns boolean
            return IssueTargetOrderById(treeDetector, 852018, d) and IssueImmediateOrderById(treeDetector, 851972)
        endmethod
        private static method destroyTree takes nothing returns nothing
            local destructable d = GetEnumDestructable()
            local real x = GetWidgetX(d)-treesX
            local real y = GetWidgetY(d)-treesY
            if x*x+y*y <= treesRadius and isTree(d) then
                // will add line to destroy tree here
            endif
        endmethod
        
        implement CTL
        
            local rect r
            local unit u
            local real x
            local real y
            
        implement CTLExpire
        
            // Set the planeX and planeY coordinates
            set planeX[this] = planeX[this] + xDis[this]
            set planeY[this] = planeY[this] + yDis[this]
            
            // Move the plane
            call SetUnitX(plane[this], planeX[this])
            call SetUnitY(plane[this], planeY[this])
            
            // If the bomb has been dropped
            if dropped[this] then
                // If the bomb exploded
                if exploded[this] then
                    // Decrease the counter by 1
                    set counter[this] = counter[this] - 1
                    // If the counter is 0, we can end the spell
                    if counter[this] == 0 then
                        // Destroy the current instance of the spell
                        call this.destroy()
                        // Remove the plane unit
                        call RemoveUnit(plane[this])
                        
                        // Destroy the first particle
                        call Particle(fx[this]).destroy()
                        // Create a new particle. We need particles here because effects can't be given a Z-coordinate.
                        set fx[this] = Particle.createEx(Player(13), planeX[this], planeY[this], BOMB_HEIGHT, 0, 0, 1, 255, 255, 255, 255, PLANE_OUT_EFFECT)
                        // I know I'm leaking Particles, but I'll fix that later
                        
                        // Null variables
                        set caster[this] = null
                        set plane[this] = null
                        set bomb[this] = null
                        set owner[this] = null
                    endif
                else
                    // If the height of the bomb is close to 0
                    if height[this] <= 5 then
                        // Remove the bomb
                        call RemoveUnit(bomb[this])
                        // Create the explosion
                        call DestroyEffect(AddSpecialEffect(EXPLOSION_EFFECT, dropX[this], dropY[this]))
                        
                        // Loop through all units in the area
                        call GroupEnumUnitsInRange(bj_lastCreatedGroup, dropX[this], dropY[this], radius[this], null)
                        loop
                            // Set u to a random unit in the group
                            set u = FirstOfGroup(bj_lastCreatedGroup)
                            // If u == null, the group is empty, so we exit the loop
                            exitwhen u == null
                            // We remove the unit u from the group
                            call GroupRemoveUnit(bj_lastCreatedGroup, u)
                            
                            // If the target unit passes the filter,
                            if TargetFilter(owner[this], caster[this], u) then
                                // We set the x and y differences of the unit and the bomb drop point
                                set x = GetUnitX(u)-dropX[this]
                                set y = GetUnitY(u)-dropY[this]
                                // If the unit is closer than the bonusRange
                                if x*x+y*y <= bonusRadius[this] then
                                    // We deal the bonus damage
                                    call UnitDamageTarget(caster[this], u, bonusDamage[this], false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
                                else
                                    // Else, we deal the normal damage
                                    call UnitDamageTarget(caster[this], u, damage[this], false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
                                endif
                            endif
                        endloop
                        
                        static if DESTROY_TREES then
                            set r = Rect(dropX[this] - radius[this], dropY[this] - radius[this], dropX[this] + radius[this], dropY[this] + radius[this])
                            set treesRadius = radius[this]*radius[this]
                            set treesX = dropX[this]
                            set treesY = dropY[this]
                            call EnumDestructablesInRect(r, null, function thistype.destroyTree)
                            call RemoveRect(r)
                            set r = null
                        endif
                        
                        // Utilize the counter variable to specify a number of iterations before we can destroy the spell instance from now.
                        set counter[this] = R2I(PLANE_AFTER_DROP*32)
                        // We have exploded the bomb
                        set exploded[this] = true
                    else
                        // Decrease the height of the bomb
                        set height[this] = height[this] - speed[this]
                        // Accelerate the bomb
                        set speed[this] = speed[this] + BOMB_DROP_ACCEL
                        // Set the height of the bomb
                        call SetUnitFlyHeight(bomb[this], height[this], 0)
                    endif
                endif
            else
                // Decrease the counter by 1
                set counter[this] = counter[this] - 1
                // When the counter is 0, the plane has reached it's destination.
                if counter[this] == 0 then
                    // Create the bomb under the plane
                    set bomb[this] = CreateUnit(DUMMY_OWNER, BOMB_CODE, planeX[this], planeY[this], 0)
                    // Set the bomb-drop coordinates that we will use later
                    set dropX[this] = planeX[this]
                    set dropY[this] = planeY[this]
                    // Give the bomb the ability to fly
                    call UnitAddAbility(bomb[this], FLYING_HEIGHT_ENABLER)
                    call UnitRemoveAbility(bomb[this], FLYING_HEIGHT_ENABLER)
                    // Set it's height to the height of the plane
                    call SetUnitFlyHeight(bomb[this], BOMB_HEIGHT, 0)
                    // We have dropped the bomb
                    set dropped[this] = true
                    // Set the height variable to the height of the plane
                    set height[this] = BOMB_HEIGHT
                    // Set the speed of the bomb to the BOMB_DROP_SPEED variable
                    set speed[this] = BOMB_DROP_SPEED
                endif
            endif
            
        implement CTLNull
        
            set u = null
            
        implement CTLEnd
        
        private static method run takes nothing returns nothing
            local thistype this = create()
            local real x = GetSpellTargetX()
            local real y = GetSpellTargetY()
            local real a
            local real s
            local real cos
            local real sin
            
            // Set the caster
            set caster[this] = GetTriggerUnit()
            // Set the owner
            set owner[this] = GetTriggerPlayer()
            
            // Get the angle between the caster and the target point in radians
            set a = Atan2(y - GetUnitX(caster[this]), x - GetUnitY(caster[this]))
            
            // Cache the Cos and Sin of the angle
            set cos = Cos(a)
            set sin = Sin(a)
            
            // Set the plane coordinates
            set planeX[this] = x + PLANE_DISTANCE * -cos
            set planeY[this] = y + PLANE_DISTANCE * -sin
            
            // Create the plane at planeX[this] and planeY[this] and make it face the angle between the caster and the target point
            set plane[this] = CreateUnit(DUMMY_OWNER, PLANE_CODE, planeX[this], planeY[this], a*bj_RADTODEG)
            
            // Set the level of the ability
            set level[this] = GetUnitAbilityLevel(caster[this], ABIL_CODE)
            
            // Set the speed of the plane per 1/32th of a second
            set s = GetPlaneSpeedFactor(level[this])
            
            // Set the vector data of the plane's movement
            set xDis[this] = cos*s
            set yDis[this] = sin*s
            
            // We haven't dropped or exploded the bomb
            set dropped[this] = false
            set exploded[this] = false
            
            // Set the counter to determine the number of iterations before we drop the bomb
            set counter[this] = R2I(PLANE_DISTANCE/s)
            
            // Give the plane the ability to fly (lol)
            call UnitAddAbility(plane[this], FLYING_HEIGHT_ENABLER)
            call UnitRemoveAbility(plane[this], FLYING_HEIGHT_ENABLER)
            
            // Set the plane's height
            call SetUnitFlyHeight(plane[this], BOMB_HEIGHT, 0)
            
            // Create a particle at the position of the plane
            set fx[this] = Particle.createEx(Player(13), planeX[this], planeY[this], BOMB_HEIGHT, 0, 0, 1, 255, 255, 255, 255, PLANE_IN_EFFECT)
            
            // Set the damage and the radius
            set damage[this] = GetDamage(level[this])
            set radius[this] = GetDamageRadius(level[this])
            
            // Set the bonus damage and the bonus radius
            set bonusRadius[this] = GetBonusDamageRadius(level[this])
            set bonusRadius[this] = bonusRadius[this]*bonusRadius[this]
            set bonusDamage[this] = GetBonusDamageFactor(level[this])*damage[this]
        endmethod
        
        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ABIL_CODE, function thistype.run)
            set treeDetector = CreateUnit(Player(13), 'hpea', 0, 0, 0)
            call UnitAddAbility(treeDetector, 'Aloc')
            call UnitAddAbility(treeDetector, 'Avul')
            call SetUnitScale(treeDetector, 0.01, 0, 0)
        endmethod
    endstruct
endlibrary

The plane and the bomb move and drop faster with every cast :O
What could be the cause of this? D:

I posted a map.
 

Attachments

  • Atomic bomb.w3x
    60.4 KB · Views: 79
Level 37
Joined
Mar 6, 2006
Messages
9,240
The same instance gets looped as many times as the spell has been cast.

For example if the spell has been cast once, the plane will be moved once during every 0.03 seconds.
If the spell has been cast twice, the plane gets moved twice every 0.03 seconds.
If the spell has been cast thrice, the plane gets moved thrice every 0.03 seconds, and so on.
 
Level 6
Joined
Feb 10, 2008
Messages
300
It is a bug in CTL :/

Don't know why, but in the CT function (line 140-ish) you have this funny thing:
set ct[r]=TriggerAddCondition(t,rc[r])
set ct[r] = null

Remove the set ct[r] = null line and it should run fine :)
 
That's actually quite odd O-o

edit
I left CTL as is, fixed the angle bug, and it worked O_O

What the hell is going on? D:>

qgj84chuea24c6y9_v4_.png
 
Level 6
Joined
Feb 10, 2008
Messages
300
When trying to start a timer for a list that is empty, CTL does a TriggerAddCondition(...).
When stopping a timer for a list, CTL removes the previously added triggercondition if the list becomes empty.

The problem was that the triggercondition reference was = null.
So CTL kept on adding the same triggercondition to the evaluation trigger.
 
Status
Not open for further replies.
Top