• 🏆 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!

Why does this bug?

Status
Not open for further replies.
Level 17
Joined
Feb 11, 2011
Messages
1,860
I have a spell that is meant to do the following:

When activated, deals damage per second to nearby enemies. Also slows their speeds. I have made it deal the damage every 0.1 seconds. This allows new units to be damaged if they come within range.

However, the effect around the caster does not play the second time the spell is cast and the damage is also not dealt. Any help will be appreciated.

JASS:
scope BurningArmour

    private struct BurningArmourData
    
        unit caster
        integer level
        integer ticks
        effect chest
    
        method destroy takes nothing returns nothing
            set .caster = null
            call DestroyEffect(.chest)
            set .chest = null
            call .deallocate()
        endmethod
    
        static method Timer_Actions takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype D = GetTimerData(t)
            local real x = GetUnitX(D.caster)
            local real y = GetUnitY(D.caster)
            local real damage = 20 * D.level * 0.1
            local unit u
            local unit dummy
            if D.ticks < 20 then
                call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, 200, null)
                loop
                    set u = FirstOfGroup(bj_lastCreatedGroup)
                    exitwhen u == null
                    if IsUnitEnemy(u, GetOwningPlayer(D.caster)) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) then
                        call UnitDamageTarget(D.caster, u, damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
                        set dummy = CreateUnit(GetOwningPlayer(D.caster), 'h001', x, y, 0)
                        call RemoveGuardPosition(dummy)
                        call UnitAddAbility(dummy, 'A006')
                        call SetUnitAbilityLevel(dummy, 'A006', GetUnitAbilityLevel(D.caster, 'A005'))
                        call IssueTargetOrder(dummy, "slow", u)
                        call UnitApplyTimedLife(dummy, 'BTLF', 1)
                    endif
                    call GroupRemoveUnit(bj_lastCreatedGroup, u)
                endloop
                set D.ticks = D.ticks + 1
            else
                call ReleaseTimer(t)
                call D.destroy()
            endif
            set dummy = null
            set t = null
        endmethod
    
        static method Actions takes nothing returns thistype
            local thistype D = thistype.allocate()
            local timer t = NewTimer()
            set D.caster = GetTriggerUnit()
            set D.level = GetUnitAbilityLevel(D.caster, 'A005')
            set D.chest = AddSpecialEffectTarget("Abilities\\Spells\\Other\\ImmolationRed\\ImmolationRedTarget.mdl", D.caster, "chest")
            call SetTimerData(t, D)
            call TimerStart(t, 0.1, true, function thistype.Timer_Actions)
            set t = null
            return D
        endmethod
    
        static method Conditions takes nothing returns boolean
            if GetSpellAbilityId() == 'A005' then
                call thistype.Actions()
            endif
            return false
        endmethod
    
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function thistype.Conditions))
            set t = null
        endmethod
        
    endstruct

endscope
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Probably because you don't set the member ticks to 0 inside your destroy method, so the if condition becomes already == false when the struct instance got recycled.

Anyway it's a good practice to null all members, regardless they are handle or not.
 
Level 17
Joined
Feb 11, 2011
Messages
1,860
Probably because you don't set the member ticks to 0 inside your destroy method, so the if condition becomes already == false when the struct instance got recycled.

Thanks, this was the issue. When should I set integer and real variables to 0? When they are part of a struct? Is it safe to declare local variables like this:
local integer i or do I have to say local integer i = 0?
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
In jass(2) :

no array variables (local or global) -> no defaults values given
array variable (local or global) -> default null value given

Using a variable with no value -> crash the thread (like a return if you prefer)

Now, no static struct members are just arrays, that's why you don't need to set the member ticks to 0 on struct allocation.
static struct members -> no array global
A struct instance is just an integer -> index of the members (array).

So, you don't need to give an initial value to struct members if you plan to use the default value, except for static members, as they are just no array globals.

I hope it answers your questions.
 
Status
Not open for further replies.
Top