HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Can't find the bug, is it in one of the systems I use?

04-22-2009, 09:22 PM#1
Flame_Phoenix
Anitarf, I think I may have found a bug ... You know that last optional parameter from the create function?

Collapse JASS:
function ABuffApply takes aBuffType id, unit u, unit caster, real duration, integer level, integer data returns boolean

Well, I am using it to save a structure with information about my caster and about a skill he gets.
I am now trying to mix UnitProperties (from Litany) with ABuff, to create the ultimate way of making spells, however, to do so, I need to use structs, there is extra data that depends on each caster that I need to save. To do this I created an auxiliary structure called aCaster. As you can see in the next code example, I set it on the Create function.
So mainly, the spell should be MUI, however it is not. If 2 units use the spell at the same time, the second unit gets the "new armor bonus + old armor bonus" and it should only get the "new armor bonus".
I think it is a bug (you know when users are newbs they usually blame the tool lol) but, in case it is not a bug, what am I doing wrong? Is there a way to fix it?
Btw, I want your quick opinion, do you think the coding practice I use is "healthy?" Would you approve such a spell?
Collapse JASS:
//===========================================================================
//This ability gives the caster bonus armor depending on the number of 
//allied ChaosOrc units and Undead units nearby. It gains 1 armor per each unit
//and can get a maximum armor bonus of 5.
//
//Requires:
// - Abuff
// - UnitProperties
//
//@author Flame_Phoenix 
//
//@version 1.1
//===========================================================================
scope ChaoticDetermination initializer Init 
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
    globals
        private constant integer AID = 'A052'
        private constant integer AURA_ID = 'A03O'
        private constant integer BUFF_ID = 'B00B'
        private constant string EFF = "Abilities\\Spells\\NightElf\\Taunt\\TauntCaster.mdl"
    endglobals
    
    private constant function MaxBonus takes integer level returns integer 
    //the maximum amount of armor the caster can receive
        return 5
    endfunction
    
    private constant function Radius takes integer level returns real
    //the AOE of the spell
        return 300.
    endfunction
    
    private constant function Duration takes integer level returns real
    //the duration of the spell
        return 10.
    endfunction
    
    private function CountUnits takes unit caster, unit helper returns boolean
    //the units that will add the bonus to the caster
        return IsUnitAlly(helper, GetOwningPlayer(caster)) and (IsUnitType(helper, UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(helper, UNIT_TYPE_MECHANICAL) == false) and (GetWidgetLife(helper) > 0.405) and (caster != helper) and (IsUnitType(helper, UNIT_TYPE_UNDEAD) or (Race[GetPlayerId(GetOwningPlayer(helper))+1] == "Chaos_Orc"))
    endfunction
//===========================================================================
//=============================SETUP END=====================================
//=========================================================================== 
    globals
        private group helpers
        private boolexpr chooseHelpers
        private unit tmpCaster
        public aBuffType id = 0
    endglobals
    
    private struct aCaster
        unit caster
        integer bonus
        
        method onDestroy takes nothing returns nothing
            call BJDebugMsg(I2S(.bonus))
        endmethod
    endstruct
//===========================================================================
    private function ChooseHelpers takes nothing returns boolean
        return CountUnits(tmpCaster, GetFilterUnit())
    endfunction
//===========================================================================
    private function Cleanup takes aBuff eventBuff returns nothing
        local aCaster data = eventBuff.data
        
        call UnitModifyArmor(data.caster, -data.bonus)
        call data.destroy()
        
        call UnitRemoveAbility(eventBuff.target.u, AURA_ID)
        
        //so we don't wait 2 seconds because of the aura
        call UnitRemoveAbility(eventBuff.target.u, BUFF_ID) 
    endfunction
//===========================================================================
    private function Create takes aBuff eventBuff returns nothing
        local aCaster data = aCaster.create()
        set data.caster = eventBuff.target.u
        
        call DestroyEffect(AddSpecialEffect(EFF, GetUnitX(data.caster), GetUnitY(data.caster)))
        
        //here we count the bonus we will add and clear the group
        set tmpCaster = data.caster
        call GroupEnumUnitsInRange(helpers, GetUnitX(data.caster), GetUnitY(data.caster), Radius(eventBuff.level), chooseHelpers)
        set data.bonus = CountUnitsInGroup(helpers)
        call GroupClear(helpers)
        
        //if we have more then 0 units, then we add the ability, else we don't
        if data.bonus > 0 then
            //now we add the ability to the caster and set it's level
            if data.bonus > MaxBonus(eventBuff.level) then
                set data.bonus = MaxBonus(eventBuff.level)
                call UnitModifyArmor(data.caster, data.bonus)
            else
                call UnitModifyArmor(data.caster, data.bonus)
            endif
        endif
        
        //now we add the buff, we always add the buff xD
        call UnitAddAbility(data.caster, AURA_ID)
        
        //prevent morphing from removing the ability
        call UnitMakeAbilityPermanent(data.caster, true, AURA_ID) 
        
        set eventBuff.data = integer(data)
    endfunction
//===========================================================================
    private function Conditions takes nothing returns boolean
        local integer level
        local real duration 
        local unit caster
        
        if GetSpellAbilityId() == AID then
            set caster = GetTriggerUnit()
            set level = GetUnitAbilityLevel(caster, AID)
            set duration = Duration(level) 
            call ABuffApply(id, caster, caster, duration, level, 0)
        endif
        
        set caster = null
        
        return false
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger ChaoticDetermTrg = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ(ChaoticDetermTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(ChaoticDetermTrg, Condition(function Conditions))
        
        //setting our globals
        set helpers = CreateGroup()
        set chooseHelpers = Condition(function ChooseHelpers)
        set id = aBuffType.create()
        
        //setting Abuff
        set id.eventCreate = ABuffEvent_Create.Create
        set id.eventCleanup = ABuffEvent_Cleanup.Cleanup
        
        //preloading effects
        call Preload(EFF)
    endfunction
endscope
04-22-2009, 10:05 PM#2
Anitarf
I don't see any way how the messed up bonuses could be my fault, sorry. All ABuff does is call your functions and debug messages in them should show that they are being called correctly when the buff is created or cleaned up.

By the way, you don't need the aCaster struct since you could store the bonus integer to the buff's data field directly. Also, your buff lacks a refresh function which might not be an issue if the duration of the spell is shorter than the cooldown because it's a self-target spell, however if you plan on making this a public spell then that might not always be the case.
04-23-2009, 01:34 PM#3
Flame_Phoenix
Quote:
I don't see any way how the messed up bonuses could be my fault, sorry. All ABuff does is call your functions and debug messages in them should show that they are being called correctly when the buff is created or cleaned up.
It makes no sense, imagine I have 2 casters. Caster A casts the spell and gets 5 armor bonus(per example); caster B casts the spell, and in theory, let us imagine it should get 2 armor bonus. Happens that the caster gets in fact 7 armor bonus (5 + 2) instead of 2, and I can't figure out why that happens ....
It just makes no sense to me, if all your unit, duration and level variables are MUI, why this one is not!?...
I will post a replay or a video soon... this beats my brain...
Quote:
By the way, you don't need the aCaster struct since you could store the bonus integer to the buff's data field directly.
I know that. However I opted for this solution instead. This is a mere "sample" code. Codes I plan to do will change more stats such as speed, agility, attack and I will need additional fields for the struct in a future. Thing is, if this sample doesn't work ... all the other won't work as well...

Quote:
Also, your buff lacks a refresh function which might not be an issue if the duration of the spell is shorter than the cooldown because it's a self-target spell, however if you plan on making this a public spell then that might not always be the case.
I see... this mater will come into my attention once I find the reason why the code is buggy ...
Thx for the quick review.

EDIT EDIT EDIT

Ok, here is the replay. It is like 20 seconds + 10 seconds of loading screen, it is really short.I hope you understand the problem when seeing the bug because afaik, the code I posted is clean ... =S
Attached Files
File type: w3gLastReplay.w3g (2.6 KB)
04-23-2009, 07:35 PM#4
Bobo_The_Kodo
Your indexing system is probably failing, so the bonuses get stored all at position 0, meaning it adds them all to one
04-23-2009, 07:45 PM#5
Flame_Phoenix
Quote:
Your indexing system is probably failing, so the bonuses get stored all at position 0, meaning it adds them all to one
Indexing system? What do you mean with that? Abuff does all the unit indexing with Table, which is one of the many reasons why I use it.
Maybe ABuff bugs if caster = target?

I made a few more tests. The results are funny, I think that the problem is from Litany's approved system (UnitProperties) and not from Abuff ... but I am really NOT sure ...Here is the code for your joy!

Collapse JASS:
//===========================================================================
//This ability gives the caster bonus armor depending on the number of 
//allied ChaosOrc units and Undead units nearby. It gains 1 armor per each unit
//and can get a maximum armor bonus of 5.
//
//Requires:
// - Abuff
// - UnitProperties
//
//@author Flame_Phoenix 
//
//@version 1.1
//===========================================================================
scope ChaoticDetermination initializer Init 
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
    globals
        private constant integer AID = 'A052'
        private constant integer AURA_ID = 'A03O'
        private constant integer BUFF_ID = 'B00B'
        private constant string EFF = "Abilities\\Spells\\NightElf\\Taunt\\TauntCaster.mdl"
    endglobals
    
    private constant function MaxBonus takes integer level returns integer 
    //the maximum amount of armor the caster can receive
        return 5
    endfunction
    
    private constant function Radius takes integer level returns real
    //the AOE of the spell
        return 300.
    endfunction
    
    private constant function Duration takes integer level returns real
    //the duration of the spell
        return 10.
    endfunction
    
    private function CountUnits takes unit caster, unit helper returns boolean
    //the units that will add the bonus to the caster
        return true
    endfunction
//===========================================================================
//=============================SETUP END=====================================
//=========================================================================== 
    globals
        private group helpers
        private boolexpr chooseHelpers
        private unit tmpCaster
        public aBuffType id = 0
    endglobals

//===========================================================================
    private function ChooseHelpers takes nothing returns boolean
        return CountUnits(tmpCaster, GetFilterUnit())
    endfunction
//===========================================================================
    private function Cleanup takes aBuff eventBuff returns nothing

        call UnitModifyArmor(eventBuff.target.u, -eventBuff.data)
        
        call UnitRemoveAbility(eventBuff.target.u, AURA_ID)
        
        //so we don't wait 2 seconds because of the aura
        call UnitRemoveAbility(eventBuff.target.u, BUFF_ID) 
    endfunction
//===========================================================================
    private function Create takes aBuff eventBuff returns nothing 
        call DestroyEffect(AddSpecialEffect(EFF, GetUnitX(eventBuff.target.u), GetUnitY(eventBuff.target.u)))
        

                call BJDebugMsg("armor= "+I2S(eventBuff.data))
                call UnitModifyArmor(eventBuff.target.u, eventBuff.data)

        
        //now we add the buff, we always add the buff xD
        call UnitAddAbility(eventBuff.target.u, AURA_ID)
        
        //prevent morphing from removing the ability
        call UnitMakeAbilityPermanent(eventBuff.target.u, true, AURA_ID) 
        
        //set eventBuff.data = integer(data)
    endfunction
//===========================================================================
    private function Conditions takes nothing returns boolean
        local integer level
        local real duration 
        local unit caster
        
        if GetSpellAbilityId() == AID then
            set caster = GetTriggerUnit()
            set level = GetUnitAbilityLevel(caster, AID)
            set duration = Duration(level) 
            call ABuffApply(id, caster, caster, duration, level, 2)
        endif
        
        set caster = null
        
        return false
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger ChaoticDetermTrg = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ(ChaoticDetermTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(ChaoticDetermTrg, Condition(function Conditions))
        
        //setting our globals
        set helpers = CreateGroup()
        set chooseHelpers = Condition(function ChooseHelpers)
        set id = aBuffType.create()
        
        //setting Abuff
        set id.eventCreate = ABuffEvent_Create.Create
        set id.eventCleanup = ABuffEvent_Cleanup.Cleanup
        
        //preloading effects
        call Preload(EFF)
    endfunction
endscope

So basically, the eventBuff.data parameter seems to be correct, for all instances (the BJDebugg message is always "2" which is correct), however my second unit gets +4 armor ... I make no idea why... is this a bug from Litany system or from Abuff?
I am sure of one thing, it is not a bug from this small piece of code ...

Here is the replay...
04-23-2009, 08:33 PM#6
Bobo_The_Kodo
The indexing system you are using with UnitProperties to store data :o
04-23-2009, 09:02 PM#7
Flame_Phoenix
Quote:
The indexing system you are using with UnitProperties to store data :o
I am not sure how UnitProperties works in the core, but I am making a basic use of it's system. I don't even keep track of the caster in any Unit Indexing system in this code (well, only with Abuff, but that doesn't count) I make a basic use of both systems, I don't use anything external. There is a bug, and I am convinced it is not my fault...
And btw, I don't think Unitproperties indexes units since it just adds abilities to units (not sure though).

Ani, do you think this bug (from ABuff or UnitProperties?) should be moved to triggers section?
04-23-2009, 10:59 PM#8
Anitarf
Moved.
04-23-2009, 11:26 PM#9
tamisrah
Neither of them is your problem, you missed the part that you have to call call CreateUnitProperties(YourUnit) before doing anything to a unit via UnitProperties.
04-25-2009, 06:05 PM#10
Flame_Phoenix
Quote:
Neither of them is your problem, you missed the part that you have to call call CreateUnitProperties(YourUnit) before doing anything to a unit via UnitProperties.
Ops ... this is weird, so is this supposed to work when I don't use that function ? Because I have an item spell that works =S

Anyway, thx for helping!
rep++!