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

Extendable Bonus System

Status
Not open for further replies.
So, New Bonus [vJASS][LUA] exists and is pretty good.

It is a simple interface to add bonuses with a few utils to give bonuses to items, or timed bonuses etc.
The problem with it is that if you want to add something, you must modify the system, add another "if" to set and get the values.

I have talked a bit with @chopinski and posted this to his "New Bonus System" in a comment, but was interested to see comments on my code.

The reason for having this is to make it easier to add custom bonuses that might only be relevant to your specific map.
For example: My map has "Spell Power" that is an artificial stat that adds some damage/power to abilities (similar to League of Legends).
It doesn't do anything by itself, but (triggered) abilities can read the value of it and add a % of it to damage/healing/power.

Other non-common bonuses are: Life on hit, armor penetration % and flat, execute damage (damage added to attacks based on target missing hp), etc.
I've been thinking of doing a "camouflaged" (League of Legends mechanic) system, and bonuses to detection range could be a bonus you might want to have...

This, in my opinion, makes it easy to give bonuses to anything in a simple and unified API.

Anyways, to the code:

JASS:
library ExtendableBonusSystem
    /* ----------------------- ExtendableBonusSystem v0.1 by ThompZon ----------------------- */
    //! novjass
        Inspired by chopinski New Bonus system, I wanted a system that you can add your own bonuses
        using a simple interface. Hopefully the interface will be simple so it is easy to implement
        but also this means that the core can upgrade without impacting the extensions!

        Inspired by: chopinski New Bonus system
        https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/

        jassApi
    //! endnovjass

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* --------------------------------------------------------------------------
    */
    private interface ExtendableBonusPlugin
        method IsIntegerBonus takes nothing returns boolean defaults false
        method Get takes unit u returns real
        method Set takes unit u, real value returns real
    endinterface

    struct RealAbilityBonus extends ExtendableBonusPlugin
        stub method AbilityCode takes nothing returns integer
            call BJDebugMsg("RealAbilityBonus missing AbilityCode!")
            return -1
        endmethod

        stub method Field takes nothing returns abilityreallevelfield
            call BJDebugMsg("RealAbilityBonus missing Field!")
            return null
        endmethod

        method Set takes unit u, real value returns real
            local integer abilCode = AbilityCode()
            if GetUnitAbilityLevel(u, abilCode) == 0 then
                call UnitAddAbility(u, abilCode)
                call UnitMakeAbilityPermanent(u, true, abilCode)
            endif
      
            call BlzSetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, value)
            call IncUnitAbilityLevel(u, abilCode)
            call DecUnitAbilityLevel(u, abilCode)
      
            return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0)
        endmethod

        method Get takes unit u returns real
            return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, AbilityCode()), Field(), 0)
        endmethod
    endstruct

    struct IntegerAbilityBonus extends ExtendableBonusPlugin
        stub method AbilityCode takes nothing returns integer
            call BJDebugMsg("IntegerAbilityBonus missing AbilityCode!")
            return -1
        endmethod

        stub method Field takes nothing returns abilityintegerlevelfield
            call BJDebugMsg("IntegerAbilityBonus missing Field!")
            return null
        endmethod

        method Set takes unit u, real value returns real
            local integer abilCode = AbilityCode()
            if GetUnitAbilityLevel(u, abilCode) == 0 then
                call UnitAddAbility(u, abilCode)
                call UnitMakeAbilityPermanent(u, true, abilCode)
            endif
            call BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, R2I(value))
            call IncUnitAbilityLevel(u, abilCode)
            call DecUnitAbilityLevel(u, abilCode)
      
            return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0))
        endmethod

        method Get takes unit u returns real
            return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, AbilityCode()), Field(), 0))
        endmethod

        method IsIntegerBonus takes nothing returns boolean
            return true
        endmethod
    endstruct

    struct ExtendableBonus
        private static ExtendableBonusPlugin array registeredBonuses
        //private static integer maxTypeId = -1

        static method Register takes ExtendableBonusPlugin bonus returns integer
            set registeredBonuses[bonus.getType()] = bonus
            //set maxTypeId = IMaxBJ(maxTypeId, bonus.getType())
            //call BJDebugMsg("maxTypeId=" + I2S(maxTypeId) + ", registedTypeId=" + I2S(bonus.getType()) + ", bonus=" + I2S(bonus))
            return bonus.getType()
        endmethod

        static method GetBonus takes integer typeId returns ExtendableBonusPlugin
            return registeredBonuses[typeId]
        endmethod

        static method Get takes unit u, integer typeId returns real
            if (registeredBonuses[typeId] == 0) then
                call BJDebugMsg("type is not registered! id=" + I2S(typeId))
                return -1.0
            else
                return registeredBonuses[typeId].Get(u)
            endif
        endmethod

        static method Set takes unit u, integer typeId, real value returns real
            if (registeredBonuses[typeId] == 0) then
                call BJDebugMsg("type is not registered! id=" + I2S(typeId))
                return -1.0
            else
                //call BJDebugMsg("Setting type=" + I2S(typeId) + " to " + R2S(value))
                return registeredBonuses[typeId].Set(u, value)
            endif
        endmethod

        static method Add takes unit u, integer typeId, real value returns real
            local ExtendableBonusPlugin currentBonus = GetBonus(typeId)
            local real addedValue
            if currentBonus.IsIntegerBonus() then
                set addedValue = R2I(value)
            else
                set addedValue = value
            endif
            call Set(u, typeId, currentBonus.Get(u) + addedValue)
            return addedValue
        endmethod
    endstruct

    function GetUnitBonus takes unit u, integer typeId returns real
        return ExtendableBonus.Get(u, typeId)
    endfunction

    function SetUnitBonus takes unit u, integer typeId, real value returns real
        return ExtendableBonus.Set(u, typeId, value)
    endfunction

    function AddUnitBonus takes unit u, integer typeId, real value returns real
        return ExtendableBonus.Add(u, typeId, value)
    endfunction

    function RemoveUnitBonus takes unit u, integer typeId returns nothing
        call ExtendableBonus.Set(u, typeId, 0)
    endfunction
endlibrary

The "basic bonuses"
JASS:
library ExtendableBonusesBasicBonuses requires ExtendableBonusSystem
    /* ----------------------- ExtendableBonusSystemBasics v0.1 by ThompZon ----------------------- */
    //! novjass
        Inspired by chopinski New Bonus system, I wanted a system that you can add your own bonuses
        using a simple interface. Hopefully the interface will be simple so it is easy to implement
        but also this means that the core can upgrade without impacting the extensions!

        Inspired by: chopinski New Bonus system
        https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/

        jassApi
    //! endnovjass

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* --------------------------------------------------------------------------
    */

    globals
        //The abilities codes for each bonus
        //When pasting the abilities over to your map
        //their raw code should match the bonus here
        private constant integer DAMAGE_ABILITY           = 'Z001'
        private constant integer ARMOR_ABILITY            = 'Z002'
        private constant integer STATS_ABILITY            = 'Z003'
        private constant integer HEALTH_ABILITY           = 'Z004'
        private constant integer MANA_ABILITY             = 'Z005'
        private constant integer HEALTHREGEN_ABILITY      = 'Z006'
        private constant integer MANAREGEN_ABILITY        = 'Z007'
        private constant integer ATTACKSPEED_ABILITY      = 'Z008'
        private constant integer MOVEMENTSPEED_ABILITY    = 'Z009'
        private constant integer SIGHT_RANGE_ABILITY      = 'Z00A'
        //The abilities fields that are modified. For the sake of readability
        private constant abilityintegerlevelfield DAMAGE_FIELD           = ABILITY_ILF_ATTACK_BONUS
        private constant abilityintegerlevelfield ARMOR_FIELD            = ABILITY_ILF_DEFENSE_BONUS_IDEF
        private constant abilityintegerlevelfield AGILITY_FIELD          = ABILITY_ILF_AGILITY_BONUS
        private constant abilityintegerlevelfield STRENGTH_FIELD         = ABILITY_ILF_STRENGTH_BONUS_ISTR
        private constant abilityintegerlevelfield INTELLIGENCE_FIELD     = ABILITY_ILF_INTELLIGENCE_BONUS
        private constant abilityintegerlevelfield HEALTH_FIELD           = ABILITY_ILF_MAX_LIFE_GAINED
        private constant abilityintegerlevelfield MANA_FIELD             = ABILITY_ILF_MAX_MANA_GAINED
        private constant abilityintegerlevelfield MOVEMENTSPEED_FIELD    = ABILITY_ILF_MOVEMENT_SPEED_BONUS
        private constant abilityintegerlevelfield SIGHT_RANGE_FIELD      = ABILITY_ILF_SIGHT_RANGE_BONUS
        private constant abilityreallevelfield    HEALTHREGEN_FIELD      = ABILITY_RLF_AMOUNT_OF_HIT_POINTS_REGENERATED
        private constant abilityreallevelfield    MANAREGEN_FIELD        = ABILITY_RLF_AMOUNT_REGENERATED
        private constant abilityreallevelfield    ATTACKSPEED_FIELD      = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1

        //These are not required by the system, but they were the way to interact with NewBonus by chopinski
        //One could use these or use "BonusDamage.typeid"
        integer BONUS_DAMAGE
        integer BONUS_ARMOR
        integer BONUS_AGILITY
        integer BONUS_STRENGTH
        integer BONUS_INTELLIGENCE
        integer BONUS_HEALTH
        integer BONUS_MANA
        integer BONUS_MOVEMENT_SPEED
        integer BONUS_SIGHT_RANGE
        integer BONUS_HEALTH_REGEN
        integer BONUS_MANA_REGEN
        integer BONUS_ATTACK_SPEED
    endglobals
    //===== Integer Ability Based =====
    struct BonusDamage extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return DAMAGE_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return DAMAGE_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_DAMAGE = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusArmor extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return ARMOR_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return ARMOR_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_ARMOR = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusHealth extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return HEALTH_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return HEALTH_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_HEALTH = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusMana extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return MANA_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return MANA_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_MANA = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusMovementSpeed extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return MOVEMENTSPEED_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return MOVEMENTSPEED_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_MOVEMENT_SPEED = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusSightRange extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return SIGHT_RANGE_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return SIGHT_RANGE_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_SIGHT_RANGE = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    //Hero stats
    struct BonusAgility extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return STATS_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return AGILITY_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_AGILITY = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusStrength extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return STATS_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return STRENGTH_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_STRENGTH = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusIntelligence extends IntegerAbilityBonus
        method AbilityCode takes nothing returns integer
            return STATS_ABILITY
        endmethod
        method Field takes nothing returns abilityintegerlevelfield
            return INTELLIGENCE_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_INTELLIGENCE = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct

    //===== Real Ability Based =====
    struct BonusAttackSpeed extends RealAbilityBonus
        method AbilityCode takes nothing returns integer
            return ATTACKSPEED_ABILITY
        endmethod
        method Field takes nothing returns abilityreallevelfield
            return ATTACKSPEED_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_ATTACK_SPEED = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusHealthRegen extends RealAbilityBonus
        method AbilityCode takes nothing returns integer
            return HEALTHREGEN_ABILITY
        endmethod
        method Field takes nothing returns abilityreallevelfield
            return HEALTHREGEN_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_HEALTH_REGEN = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusManaRegen extends RealAbilityBonus
        method AbilityCode takes nothing returns integer
            return MANAREGEN_ABILITY
        endmethod
        method Field takes nothing returns abilityreallevelfield
            return MANAREGEN_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_MANA_REGEN = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
endlibrary

JASS:
library ExtendableBonusesBasicAdditionalBonuses requires ExtendableBonusSystem
    /*
        These are the non-basic stuff that also are included in NewBonus that I'd prefer to use a triggered approach instead of ability-based approach.
      
        Import this if you don't want to use triggered: crit, life-steal, magic resist and evasion.
    */
    globals
        //The abilities codes for each bonus
        //When pasting the abilities over to your map
        //their raw code should match the bonus here
        private constant integer MAGIC_RESISTANCE_ABILITY = 'Z00B'
        private constant integer CRITICAL_STRIKE_ABILITY  = 'Z00C'
        private constant integer EVASION_ABILITY          = 'Z00D'
        private constant integer LIFE_STEAL_ABILITY       = 'Z00E'
        //The abilities fields that are modified. For the sake of readability
        private constant abilityreallevelfield    MAGIC_RESISTANCE_FIELD = ABILITY_RLF_DAMAGE_REDUCTION_ISR2
        private constant abilityreallevelfield    CRITICAL_CHANCE_FIELD  = ABILITY_RLF_CHANCE_TO_CRITICAL_STRIKE
        private constant abilityreallevelfield    CRITICAL_DAMAGE_FIELD  = ABILITY_RLF_DAMAGE_MULTIPLIER_OCR2
        private constant abilityreallevelfield    EVASION_FIELD          = ABILITY_RLF_CHANCE_TO_EVADE_EEV1
        private constant abilityreallevelfield    LIFE_STEAL_FIELD       = ABILITY_RLF_LIFE_STOLEN_PER_ATTACK

        integer BONUS_MAGIC_RESISTANCE
        integer BONUS_EVASION_CHANCE
        integer BONUS_CRITICAL_DAMAGE
        integer BONUS_CRITICAL_CHANCE
        integer BONUS_LIFE_STEAL
        integer BONUS_MISS_CHANCE

        /*
        Stuff from NewBonus that have not been migrated:

        integer BONUS_SPELL_POWER_FLAT
        integer BONUS_SPELL_POWER_PERCENT
        integer BONUS_SPELL_VAMP
        integer BONUS_COOLDOWN_REDUCTION
        integer BONUS_COOLDOWN_REDUCTION_FLAT
        integer BONUS_COOLDOWN_OFFSET
        */
    endglobals
    //===== Real Ability Based =====
    struct BonusMagicResistance extends RealAbilityBonus
        method AbilityCode takes nothing returns integer
            return MAGIC_RESISTANCE_ABILITY
        endmethod
        method Field takes nothing returns abilityreallevelfield
            return MAGIC_RESISTANCE_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_MAGIC_RESISTANCE = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusCritChance extends RealAbilityBonus
        method AbilityCode takes nothing returns integer
            return CRITICAL_STRIKE_ABILITY
        endmethod
        method Field takes nothing returns abilityreallevelfield
            return CRITICAL_CHANCE_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_CRITICAL_CHANCE = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusCritDamage extends RealAbilityBonus
        method AbilityCode takes nothing returns integer
            return CRITICAL_STRIKE_ABILITY
        endmethod
        method Field takes nothing returns abilityreallevelfield
            return CRITICAL_DAMAGE_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_CRITICAL_DAMAGE = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusEvasion extends RealAbilityBonus
        method AbilityCode takes nothing returns integer
            return EVASION_ABILITY
        endmethod
        method Field takes nothing returns abilityreallevelfield
            return EVASION_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_EVASION_CHANCE = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    struct BonusLifeSteal extends RealAbilityBonus
        method AbilityCode takes nothing returns integer
            return LIFE_STEAL_ABILITY
        endmethod
        method Field takes nothing returns abilityreallevelfield
            return LIFE_STEAL_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_LIFE_STEAL = ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
endlibrary

Tweak to NewBonusUtils so it uses my library instead, basically replaceing 1 dependency and removed " extends NewBonus". 2 tiny changes!
JASS:
library NewBonusUtils requires ExtendableBonusSystem, RegisterPlayerUnitEvent
    /* ----------------------- NewBonusUtils v2.2 by Chopinski ----------------------- */
    //! novjass
   Required Library: RegisterPlayerUnitEvent -> www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/

        API:
        function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
            -> Add the specified amount for the specified bonus type for unit for a duration
            -> Example: call AddUnitBonusTimed(GetTriggerUnit(), BONUS_ARMOR, 13, 10.5)

        function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
            -> Links the bonus amount specified to a buff or ability. As long as the unit has the buff or
            -> the ability represented by the parameter buffId the bonus is not removed.
            -> Example: call LinkBonusToBuff(GetTriggerUnit(), BONUS_ARMOR, 10, 'B000')
 
        function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
            -> Links the bonus amount specified to an item. As long as the unit has that item the bonus is not removed.
            -> Note that it will work for items with the same id, because it takes as parameter the item object.
            -> Example: call LinkBonusToItem(GetManipulatingUnit(), BONUS_ARMOR, 10, GetManipulatedItem())

        function UnitCopyBonuses takes unit source, unit target returns nothing
            -> Copy the source unit bonuses using the Add functionality to the target unit
            -> Example: call UnitCopyBonuses(GetTriggerUnit(), GetSummonedUnit())

        function UnitMirrorBonuses takes unit source, unit target returns nothing
            -> Copy the source unit bonuses using the Set functionality to the target unit
            -> Example: call UnitMirrorBonuses(GetTriggerUnit(), GetSummonedUnit())
    //! endnovjass
    /* ----------------------------------- END ---------------------------------- */
 
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* --------------------------------------------------------------------------
    */
    private struct NewBonusUtils
        static timer timer = CreateTimer()

        static integer key = -1
        static thistype array array
 
        static integer k = -1
        static thistype array items

        unit    unit
        item    item
        real    ticks
        integer type
        integer buff
        real    amount
        boolean link

        method remove takes integer i, boolean isItem returns integer
            call AddUnitBonus(unit, type, -amount)

            if isItem then
                set items[i] = items[k]
                set k = k - 1
            else
                set array[i] = array[key]
                set key = key - 1

                if key == -1 then
                    call PauseTimer(timer)
                endif
            endif

            set unit = null
            set item = null
  
            call deallocate()

            return i - 1
        endmethod

        static method removeBonusTypeForItem takes item itm, integer bonusToRemove returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > k
                    set this = items[i]

                    if item == itm and type == bonusToRemove then
                        set i = remove(i, true)
                    endif
                set i = i + 1
            endloop
        endmethod

        static method onDrop takes nothing returns nothing
            local item itm = GetManipulatedItem()
            local integer i = 0
            local thistype this

            loop
                exitwhen i > k
                    set this = items[i]

                    if item == itm then
                        set i = remove(i, true)
                    endif
                set i = i + 1
            endloop
        endmethod
 
        static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > key
                    set this = array[i]

                    if link then
                        set ticks = ticks - 1

                        if ticks <= 0 then
                            set i = remove(i, false)
                        endif
                    else
                        if GetUnitAbilityLevel(unit, buff) == 0 then
                            set i = remove(i, false)
                        endif
                    endif
                set i = i + 1
            endloop
        endmethod

        static method linkTimed takes unit u, integer bonus_type, real amount, real duration, boolean link returns nothing
            local thistype this = thistype.allocate()

            set this.unit = u
            set this.type = bonus_type
            set this.ticks = duration/0.03125000
            set this.link = link
            set this.amount = AddUnitBonus(u, bonus_type, amount)
            set key = key + 1
            set array[key] = this
 
            if key == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod

        static method linkBuff takes unit u, integer bonus_type, real amount, integer buffId, boolean link returns nothing
            local thistype this = thistype.allocate()

            set this.unit = u
            set this.type = bonus_type
            set this.buff = buffId
            set this.link = link
            set this.amount = AddUnitBonus(u, bonus_type, amount)
            set key = key + 1
            set array[key] = this
 
            if key == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod

        static method linkItem takes unit u, integer bonus_type, real amount, item i returns nothing
            local thistype this = thistype.allocate()

            set this.unit = u
            set this.item = i
            set this.type = bonus_type
            set this.amount = AddUnitBonus(u, bonus_type, amount)
            set k = k + 1
            set items[k] = this
        endmethod

        static method copy takes unit source, unit target returns nothing
            local integer i = 1

            loop
                exitwhen i > 17
                    if GetUnitBonus(source, i) != 0 then
                        call AddUnitBonus(target, i, GetUnitBonus(source, i))
                    endif
                set i = i + 1
            endloop
        endmethod

        static method mirror takes unit source, unit target returns nothing
            local integer i = 1

            loop
                exitwhen i > 17
                    call SetUnitBonus(target, i, GetUnitBonus(source, i))
                set i = i + 1
            endloop
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                  JASS API                                  */
    /* --------------------------------------------------------------------------
    */
    function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
        call NewBonusUtils.linkTimed(u, bonus_type, amount, duration, true)
    endfunction

    function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
        call NewBonusUtils.linkBuff(u, bonus_type, amount, buffId, false)
    endfunction

    function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
        call NewBonusUtils.linkItem(u, bonus_type, amount, i)
    endfunction

    function RemoveSpecificBonusFromItem takes item i, integer bonus_type returns nothing
        call NewBonusUtils.removeBonusTypeForItem(i, bonus_type)
    endfunction

    function UnitCopyBonuses takes unit source, unit target returns nothing
        call NewBonusUtils.copy(source, target)
    endfunction

    function UnitMirrorBonuses takes unit source, unit target returns nothing
        call NewBonusUtils.mirror(source, target)
    endfunction
endlibrary

Used by doing:
JASS:
//Note: BonusDamage is a struct in ExtendableBonusesBasicBonuses
call AddUnitBonus(GetTriggerUnit(), BonusDamage.typeid, 3.0)  // +3 damage
call AddUnitBonus(GetTriggerUnit(), BonusAttackSpeed.typeid, 0.25) //+25% attack speed

The "flow" of the code is:
The core registers bonus types in an array and get them based on vjass "typeid", basically.
I was thinking of how to handle the "bonus_type" integers of the NewBonus system and think it works decently.

The API to add a new type is fairly minimal and has mostly the same API and power of NewBonus.

It is possible to add an optional function to the interface for "onSetEvent" or "onAnyBonusEvent" easily.

I don't know how to make it fully compatible with current constants though... Maybe let the constants be variables and set them onInit?

Note: Testmap has most of NewBonus ported, but not 100% of extensions. Saved with version 1.31.
Testmap has added a few effects to a Warden to show off some basics of how this can be used.

Edit 2021-11-07:
Plugins now has optional function for "IsIntegerBonus" that returns false by default.
If true using "add" will R2I the added values, solving issues with adding 0.5 causing it to sometimes leave +1/-1 when it shouldn't.
"Basic Plugin" now feature complete and 100% compatible with NewBonus (I think).
Added new "optional" plugin for: lifesteal, evasion, critical hit using basic critical hit ability and magic resist using ability-based Magic Resist.
I wouldn't use the "optional" plugin because I would've used triggered versions of such things.
 

Attachments

  • ExtendableBonusSystem_TestMap.w3x
    42.9 KB · Views: 18
Last edited:
Status
Not open for further replies.
Top