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

New Bonus [vJASS][LUA]

Intro
Hello, this is my first submission in the spell section, so apologies in advance for any mistake. I present to you the New Bonus System. Since Object Merger stopped working and we still have no ways to edit the bonus values of a unit natively, I decided to create this light weight system to manage that and give everyone a simpler way to work with bonus values than the Bonus Mod.
How it works?
By using the new Object API introduced to Warcraft, we can now modify an unit ability field value, so when giving an unit a bonus, we add a specific ability to that unit that gives such bonus, if it do not have it already, and change that ability field bonus value to the desired value. Retrieving the amount is also trivial, by just reading that field value.

How to Import?
Importing New Bonus is really simple. Just copy the 9 abilities with the prefix "NewBonus" from the Object Editor into your map [Raw Codes Z001 to Z009 in demo map] and match their new raw code to the bonus types in the global block of the library. You can also define their raw code when pasting to be Z001 to Z009, that's why I created them with this raw codes. Then create a trigger called NewBonus, convert it to custom text, delete all text in it and paste the Library code there. That's it, you are done!
Requirements
NewBonus requires patch 1.31+ and RegisterPlayerUnitEvent Library. NewBonus Extended also Requires DamageInterface, CooldownReduction and Tenacity libraries.

vJASS:
function GetUnitBonus takes unit u, integer bonus_type returns real
    -> Returns the specified bonus amount for the unit
    -> Example: set amount = GetUnitBonus(GetTriggerUnit(), BONUS_AGILITY)

function SetUnitBonus takes unit u, integer bonus_type, real amount returns real
    -> Set the specified bonus type to amount for the unit
    -> Example: call SetUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)

function RemoveUnitBonus takes unit u, integer bonus_type returns nothing
    -> Removes the Specified bonus type from unit
    -> Example: call RemoveUnitBonus(GetTriggerUnit(), BONUS_AGILITY)

function AddUnitBonus takes unit u, integer bonus_type, real amount returns real
    -> Add the specified amount for the specified bonus tyte for unit
    -> Example: call AddUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)

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())

function RegisterBonusEvent takes code c returns nothing
    -> Register code to run when any unit bonus is modified
    -> Example: RegisterBonusEvent(function YourFunction)

function RegisterBonusTypeEvent takes integer bonus, code c returns nothing
    -> Register code to run when a specific unit bonus is modified
    -> Example: RegisterBonusTypeEvent(BONUS_DAMAGE, function YourFunction)

function GetBonusUnit takes nothing returns unit
    -> Call this function to get the bonus event unit

function GetBonusType takes nothing returns integer
    -> Call this function to get the bonus event type

function SetBonusType takes integer bonus returns nothing
    -> Call this function to set the bonus event type

function GetBonusAmount takes nothing returns real
    -> Call this function to get the bonus event amount

function SetBonusAmount takes real amount returns nothing
    -> Call this function to set the bonus event amount
Lua:
function GetUnitBonus(unit, type)
    -> Returns the specified bonus amount for the unit
    -> Example: set amount = GetUnitBonus(GetTriggerUnit(), BONUS_AGILITY)

function SetUnitBonus(unit, type, value)
    -> Set the specified bonus type to amount for the unit
    -> Example: call SetUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)

function RemoveUnitBonus(unit, type)
    -> Removes the Specified bonus type from unit
    -> Example: call RemoveUnitBonus(GetTriggerUnit(), BONUS_AGILITY)

function AddUnitBonus(unit, type, value)
    -> Add the specified amount for the specified bonus tyte for unit
    -> Example: call AddUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)

function AddUnitBonusTimed(unit, type, amount, duration)
    -> 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(unit, type, amount, buff)
    -> 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(unit, type, amount, item)
    -> 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(source, target)
    -> Copy the source unit bonuses using the Add functionality to the target unit
    -> Example: call UnitCopyBonuses(GetTriggerUnit(), GetSummonedUnit())

function UnitMirrorBonuses(source, target)
    -> Copy the source unit bonuses using the Set functionality to the target unit
    -> Example: call UnitMirrorBonuses(GetTriggerUnit(), GetSummonedUnit())

function RegisterBonusEvent(code)
    -> Register code to run when any unit bonus is modified
    -> Example: RegisterBonusEvent(function() YourFunctionc end)

function RegisterBonusTypeEvent(type, code)
    -> Register code to run when a specific unit bonus is modified
    -> Example: RegisterBonusTypeEvent(BONUS_DAMAGE, function() YourFunction end)

function GetBonusUnit()
    -> Call this function to get the bonus event unit

function GetBonusType()
    -> Call this function to get the bonus event type

function SetBonusType(type)
    -> Call this function to set the bonus event type

function GetBonusAmount()
    -> Call this function to get the bonus event amount

function SetBonusAmount(real)
    -> Call this function to set the bonus event amount
  • BONUS_DAMAGE
  • BONUS_ARMOR
  • BONUS_AGILITY
  • BONUS_STRENGTH
  • BONUS_INTELLIGENCE
  • BONUS_HEALTH
  • BONUS_MANA
  • BONUS_MOVEMENT_SPEED
  • BONUS_SIGHT_RANGE
  • Real Value Bonusses
  • BONUS_HEALTH_REGEN
  • BONUS_MANA_REGEN
  • BONUS_ATTACK_SPEED
  • BONUS_MAGIC_RESISTANCE
  • BONUS_EVASION_CHANCE
  • BONUS_CRITICAL_CHANCE
  • BONUS_CRITICAL_DAMAGE
  • BONUS_LIFE_STEAL
  • BONUS_DAMAGE
  • BONUS_ARMOR
  • BONUS_AGILITY
  • BONUS_STRENGTH
  • BONUS_INTELLIGENCE
  • BONUS_HEALTH
  • BONUS_MANA
  • BONUS_MOVEMENT_SPEED
  • BONUS_SIGHT_RANGE
  • Real Value Bonusses
  • BONUS_HEALTH_REGEN
  • BONUS_MANA_REGEN
  • BONUS_ATTACK_SPEED
  • BONUS_MAGIC_RESISTANCE
  • BONUS_EVASION_CHANCE
  • BONUS_MISS_CHANCE
  • BONUS_CRITICAL_CHANCE
  • BONUS_CRITICAL_DAMAGE
  • BONUS_SPELL_POWER_FLAT
  • BONUS_SPELL_POWER_PERCENT
  • BONUS_LIFE_STEAL
  • BONUS_SPELL_VAMP
  • BONUS_COOLDOWN_REDUCTION
  • BONUS_COOLDOWN_REDUCTION_FLAT
  • BONUS_COOLDOWN_OFFSET
  • BONUS_TENACITY
  • BONUS_TENACITY_FLAT
  • BONUS_TENACITY_OFFSET

(v1.0)
  • Submission
(v1.1)
  • Updated the code to use BlzSetUnitMaxHP() and BlzSetUnitMaxMana() in order to make Bonus_Health and Bonus_Mana work properly.
(v1.2)
  • Implemented the ability to link a bonus amount to a buff or ability as per requested by @The_Silent . Refactored the code and added a new example of this new functionality in the test map.
(v1.3)
  • FIxed a bug on AddUnitBonusExTimed()
(v1.4)
  • Normalized the AMOUNT input parameter to Integer instead of real to fix a type conversion problem when converting real to integer for abilities which field value is a integer value.
(v1.5)
  • Updated the system to respect integer fields bounds when adding to existing bonus (max 2147483647 and min -2147483648).
  • It's still the users job to pass the correct amount, the system will only handle addtions and subtractions that overflow/underflow.
(v1.6)
  • Split NewBonus into 2 libraries
    • NewBonus contains the core system to set / remove / add bonus types
    • NewBonusUtils contains extra functionalities such as timed and linked bonuses
  • Redesigned timed and linked bonuses to improve performance.
  • New utility functions AddUnitBonusTimed(), LinkBonusToBuff(), LinkBonusToItem()
  • Renamed Constans to upper case to be more visible.
  • Updated test map: included examples for the 3 new utility functions.
(v1.7)
  • Fixed a bug when Adding Health/Mana bonus not keeping the unit Health/Mana Percentage
  • Added the funcitons: They manipuilate real bonuses values correctly.
    • GetUnitBonusReal()
    • SetUnitBonusReal()
    • RemoveUnitBonusReal()
    • AddUnitBonusReal()
  • Refactored the LinkBonusToItem() to use the On Drop event instead of a periodic timer to check the link
  • Included an Expanded version of NewBonus and NewBonusUtils that take advangtage of the Damage Inteface System and Cooldown Reduction System
  • Renamed 4 bonus types for better readability.
  • Removed the "Ex" from the end of the API functions
(v1.8)
  • 2 new bonus types:
    • Sight Range
    • Magic Resistence (real)
  • 2 new functionalities in the NewBonusUtils (See API for usage): Thx to SinisterLuffy for the idea
    • function UnitCopyBonuses takes unit source, unit target returns nothing
    • function UnitMirrorBonuses takes unit source, unit target returns nothing
(v1.9)
  • Correction of a minor spelling error.
(v2.0)
  • Fixed a minor syntax error for the non extended Utils library
(v2.1)
  • System ported to LUA
  • 4 new bonus types for the non-Extended version of the system
    • Evasion Chance
    • Critical Strike Chance
    • Critical Strike Damage
    • Life Steal
(v2.2)
  • Removed the functions GetUnitBonusReal, SetUnitBonusReal, AddUnitBonusReal and RemoveUnitBonusReal
  • Now the functions GetUnitBonus, SetUnitBonus, AddUnitBonus and RemoveUnitBonus handles both real and integer bonus types correctly
  • Fix a minor bug in the NewBonusUtils Library
(v2.3)
  • Support for Bonus Events!!
  • Merged the normal and extended version into one library. (You can control which version is enabled by setting the new EXTENDED constant in the global section)
  • Code Cleaning and formatting.
(v2.4)
  • Fixed a bug in bonus event evaluation
  • 3 new bonus types included from the new Tenacity system
    • BONUS_TENACITY,
    • BONUS_TENACITY_FLAT
    • BONUS_TENACITY_OFFSET


For a quick import of the abilities use the editor Object Data.
For those with problems to open the map because of Reforged 1.32 patch, please see this
Contents

NewBonus (Map)

New Bonus (Map)

Reviews
MyPad
Considering the test map alone, I would approve this system. However, certain things addressed in the Code section of the review would need to be resolved before approval. Hence, this has been moved to Awaiting Update.
Level 20
Joined
May 16, 2012
Messages
635
I just wanted to update to the new version (NewBonus, NOT extended), and I think there is a bug with BONUS_DAMAGE.

If my variable is a "Real" type I can get the DAMAGE_BONUS:
  • Custom script: set udg_Attack = GetUnitBonus(GetTriggerUnit(), BONUS_DAMAGE)

View attachment 382081

However, if I change the variable type to "Integer", I get an error:
  • Custom script: set udg_Attack = GetUnitBonus(GetTriggerUnit(), BONUS_DAMAGE)

View attachment 382082

Error message:

View attachment 382083

Edit: Same for Strength, Intelligence, Agility etc.
Or are we meant to always use "Real" variables, regardless if it's Magic resistance (previously Real) or Damage (previously Integer)?
If you are using the vJASS version than always use the Real type, if using the Lua than you need to use the bonus type.
 
Level 4
Joined
Aug 2, 2015
Messages
63
Hi, im found an issue with HEALTH, when i adding negative value more than 'default lvl' health of hero then the unit dies. its happen on acquire an item
ex.
1 - lvl 25 str hero with 1850 default health
2 - acquire an item which is increase 100 str = 2500 hp and decrease 2000 hp (if you ask why i do this, its for rebalanced STRENGTH hero on my map without having much difference on HEALTH against AGILITY or INTELLIGENCE hero)
3 - the unit dies
 
I really like this system. It does a lot of things well.

Extendibility + update to newer version is pretty bad experience though.
I have added a few of my own systems into the bonus system with my own BONUS_X constants and added them to the if-elses etc.
If I update I have to be careful and update it in correct places with my own extensions.

Idea/suggestion (something I might do if you don't like the idea, don't have time or something):
Create a "bonus core" where you register "bonus types". The bonus types must implement an interface, have an identifier and if there is a clash, print an error or something.
Instead of else-if, it will for-each registered type check if "id" == bonus.id, or have a map from id -> array index (keeping "registered bonuses" in an array) or something.

This way, I can create my armor penetration, register it to the system. When the core is updated, it'll probably be no problem (because of interfaces likely won't have to update).
Additional bonuses is that each "extension" can be it's own tiny library, depending on the bonus core, implementing the interface, and onInit register itself to the system, making it very easy to pick-and-choose.

I have thought about this last week (since I have not had time to work a lot on the map and know I have issues updating to newer version because I have custom bonuses in my version).
 
So: I've done my first implementation of my idea of an extendable bonus system.
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.

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/copy pasted parts of: chopinski New Bonus system
        https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/

        jassApi
    //! endnovjass

    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* --------------------------------------------------------------------------
    */
    private interface ExtendableBonusPlugin
        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
   
            if BlzSetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, value) then
                call IncUnitAbilityLevel(u, abilCode)
                call DecUnitAbilityLevel(u, abilCode)
            endif
   
            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
   
            if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, R2I(value+0.0001)) then
                call IncUnitAbilityLevel(u, abilCode)
                call DecUnitAbilityLevel(u, abilCode)
            endif
   
            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
    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
                return registeredBonuses[typeId].Set(u, value)
            endif
        endmethod

        static method Add takes unit u, integer typeId, real value returns real
            local real addedValue = Get(u, typeId) + value
            return Set(u, typeId, 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" (currently only damage)
JASS:
library ExtendableBonusesBasicBonuses requires ExtendableBonusSystem
    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'
        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 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
        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
    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
            call ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    /* UNTESTED BELOW
    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
            call 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
            call 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
            call 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
            call 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
            call 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 abilityintegerlevelfield
            return ATTACKSPEED_FIELD
        endmethod
        static method onInit takes nothing returns nothing
            call ExtendableBonus.Register(thistype.allocate())
        endmethod
    endstruct
    */
    //TODO: More!
endlibrary

Tweak to NewBonusUtils so it uses my library instead, basically replaceing 1 dependency and removed " extends NewBonus"
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)

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 not be constants and set them onInit?

Edit: Added a few commented out "basic bonuses" to the example (because they were written without WE and are untested).
 

Attachments

  • ExtendableBonusSystem_TestMap.w3x
    28.9 KB · Views: 21
Last edited:
Level 4
Joined
Jun 6, 2015
Messages
77
Noob quesetion: Using "wait" or TRIGGER SLEEP on triggers outside of this system makes the map not work? I found and old map of mine where importing this system made some already-existing triggers not work (triggers without "wait" or trigger_sleep).

System works fine elsewhere, very neat.
 
Level 20
Joined
May 16, 2012
Messages
635
Noob quesetion: Using "wait" or TRIGGER SLEEP on triggers outside of this system makes the map not work? I found and old map of mine where importing this system made some already-existing triggers not work (triggers without "wait" or trigger_sleep).

System works fine elsewhere, very neat.
Yeah, TriggerSleep will break things, but you should never use TriggerSleep anywhere unless you know exactly what you are doing or you have no other choice.
 
Level 2
Joined
Feb 24, 2020
Messages
11
Great system. Do you think it would be possible to extended the system to include casting time reduction? Not asking you to do so, more a question if whether you believe it's possible?
 
Great system. Do you think it would be possible to extended the system to include casting time reduction? Not asking you to do so, more a question if whether you believe it's possible?
"casting time" in Warcraft 3, the few spells that has one, are on the ability, not the unit, making it impossible without a custom cast system.
With a custom cast system, you'd go in to the system and add a new "case" in the "if-else" chain. If/When a new version of this system comes that does something that you want to have, you'll have to re-add your case in the system.

I have a draft for a system that has the same API as this, but in plug-in-able. It's not finished, but for now it's here:
 
Great system. Do you think it would be possible to extended the system to include casting time reduction? Not asking you to do so, more a question if whether you believe it's possible?
Casting time in Warcraft 3 is calced and stored when the unit gets the spell. Makes it difficult to change casting times in a non specific spell setup. I think It is possible doing such a mechanic by remove/add/reorder inside the channel ability event but it will break order queue.


For most spells UNIT_RF_CAST_POINT + ABILITY_RLF_CASTING_TIME Level 1 is the total cast time to the effect.

ABILITY_RLF_CASTING_TIME can not be changed (during the match), because you would need to change it before the unit got the spell and afterwards it has no effect.
Unit Cast Point is an option.

Edit: A working demo was simpler than expected: But I only tested Inner fire and shockwave
Lua:
Casters = {}
AddTasUnitAction(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function(spellCode, unit, target, x, y)
    print("SPELL_CHANNEL")
    if Casters[unit] then return end
    Casters[unit] = true
    local newCastTime = GetRandomReal(0.00, 5)
    local order = GetUnitCurrentOrder(unit)
    print(newCastTime)
    
    IssueImmediateOrder(unit, "stop")
    UnitRemoveAbility(unit, spellCode)
    BlzSetUnitRealField(unit, UNIT_RF_CAST_POINT, newCastTime)
    UnitAddAbility(unit, spellCode)
    if GetHandleId(target) > 0 then
        IssueTargetOrderById(unit, order, target)
    elseif x == 0 and y == 0 then
        IssueImmediateOrderById(unit, order)
    else
        IssuePointOrderById(unit, order, x, y)
    end
        print("Recasted")
       -- TimerStart(CreateTimer(), 0.01, false, function()
            Casters[unit] = false
            print("done")
    --end)
    
end)
 
Last edited:
Level 2
Joined
Feb 24, 2020
Messages
11
Thank you Tasyen

I have another question, if either of you feel like answering it that is.

I am trying to incorporate ZTS in to my map, but not really sure how to combine it with the New bonus system. I know I am suppose to

JASS:
call ZTS_ModifyThreat(GetEventDamageSource(), GetTriggerUnit(), GetEventDamage(), true)

But after reading through the New Bonus system for like 2h, I realise I am just not advanced enough to understand where to put the call. Basically I am trying to find the place where the actual damage is dealt I guess? So that I can then use that as a real in the call.
 
I would say new Bonus is the wrong place to generate Threat. As New bonus is about increasing or decreasing power of an unit.

One probably adds threat inside a damaged event, you should look into a damage detection system or use the player damage events added in Warcraft 3 V1.31.

But, I might talk nonsense, as i did not master New Bonus.
 
Level 2
Joined
Feb 24, 2020
Messages
11
Oh I just assumed it applied the spell damage by adding the spell power to a damage event and thus there was a damage event in the actual code. Perhaps I am completely off then.
I would say new Bonus is the wrong place to generate Threat. As New bonus is about increasing or decreasing power of an unit.

One probably adds threat inside a damaged event, you should look into a damage detection system or use the player damage events added in Warcraft 3 V1.31.

But, I might talk nonsense, as i did not master New Bonus.
 
you are right: when it has a unspecific % spell power, it has a damage event. The demo map has quite a lot of stuff, one is this DamageInterface lib. It has a function to add code that is executed by the systems damage system.
JASS:
function RegisterAnyDamageEvent takes code c returns nothing
   call TriggerAddCondition(Damage.damaged, Filter(c))
endfunction

You could call it at map init to add a function in which you generate threat.
JASS:
call ZTS_ModifyThreat(GetEventDamageSource(), GetTriggerUnit(), GetEventDamage(), true)
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
You can set variables with GUI command and then use them as arguments in calls to this system’s API, which is not difficult to do. You can even assign GUI variables to the output of those functions.

You can, in principle, just read and set the ability fields yourself with the GUI commands but… why?
 
Level 31
Joined
Aug 6, 2015
Messages
628
You can set variables with GUI command and then use them as arguments in calls to this system’s API, which is not difficult to do. You can even assign GUI variables to the output of those functions.

You can, in principle, just read and set the ability fields yourself with the GUI commands but… why?
I don't quite get it how the system works on item pick up.
The example provided didn't do much for me (though i know Jass very little)

How can one link a bonus to the item?
I tried this way (with 5 different rings but it did nothing)
Screenshot_4.png
How can one add/remove a bonus when an item is picked/dropped
(this example is not working)
Screenshot_5.png
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
I don't quite get it how the system works on item pick up.
The example provided didn't do much for me (though i know Jass very little)

How can one link a bonus to the item?
I tried this way (with 5 different rings but it did nothing)
View attachment 402333
How can one add/remove a bonus when an item is picked/dropped
(this example is not working)
View attachment 402334
Make sure you imported all the required abilities correctly. Also make sure you are comparing Item values properly, usually the first letter is UPPER case 'I002' for example no 'i002'. Attach the map so we can have a look at it and see if you did something else wrong
 
Level 31
Joined
Aug 6, 2015
Messages
628
Make sure you imported all the required abilities correctly. Also make sure you are comparing Item values properly, usually the first letter is UPPER case 'I002' for example no 'i002'. Attach the map so we can have a look at it and see if you did something else wrong
changed 'i002' to "I002" and everything worked (my bad).
I was doing the testing on the main map that contained the system.

Also i don't quite get it , why Current_Bonus means damage in this example?
Screenshot_6.png

One question. (and suggestion i guess)
is it possible to somehow tie different variables to items. (for example i have a variable that gives player partial evasion when taking damage , can i somehow tie it , so i could do something like
JASS:
call LinkBonusToItem(u, udg_PartialEvasion, 10 , i)

And some other suggestions - adding more Bonus types :
Bonus_Bash_chance

Bonus_bash_damage
Bonus_bash_duration
Damage_block_chance
Damage_block_ammount
Damage_block_ammount_percent
Damage_block_minimum

Damage_block_minimum_Percent
(could be useful when creating items/spells that deals N amount of damage with a chance or block N amount of damage)
 
Last edited:
Hmm , after importing the system into my map, i faced a problem.

View attachment 402743
The bonus is given but after dropping the item and picking it up back , the bonus is not maintaining.
That's because with that trigger, the Bracer of Agility (lvl2) bonus is added when... Upgrading?
You should add the bonus when picking up the item.

That is: Create a new trigger for when gaining an item, if it is the lvl 2 item, then link the bonuses. Then you'll only need to remove/add item in this trigger, and the bonuses is handled in the new trigger.
 
Level 31
Joined
Aug 6, 2015
Messages
628
That's because with that trigger, the Bracer of Agility (lvl2) bonus is added when... Upgrading?
You should add the bonus when picking up the item.

That is: Create a new trigger for when gaining an item, if it is the lvl 2 item, then link the bonuses. Then you'll only need to remove/add item in this trigger, and the bonuses is handled in the new trigger.
Thanks!
 
Level 31
Joined
Aug 6, 2015
Messages
628
So dunno if i imported somehow wrong the system.
But players seems to be experiencing a huge bug

JASS:
scope AddItemBonuses
    private struct AddItemBonuses
        static method onPickUp takes nothing returns nothing
            local unit u = GetManipulatingUnit()
            local item i = GetManipulatedItem()
// Bracer of Agility (lvl 1)
            if GetItemTypeId(i) == 'I0AE' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 2, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 7, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 2, i) 
                    call LinkBonusToItem(u, BONUS_DAMAGE, 9, i) 
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.08, i) 
// Bracer of Agility (lvl 2)
            elseif GetItemTypeId(i) == 'I0BG' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 4, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 4, i) 
                    call LinkBonusToItem(u, BONUS_DAMAGE, 12, i) 
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.11, i) 
// Bracer of Agility (lvl 3)              
            elseif GetItemTypeId(i) == 'I0AN' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 6, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 13, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 6, i) 
                    call LinkBonusToItem(u, BONUS_DAMAGE, 15, i) 
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.14, i) 
// Bracer of Agility (lvl 4)               
            elseif GetItemTypeId(i) == 'I0BF' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 8, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 16, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 8, i) 
                    call LinkBonusToItem(u, BONUS_DAMAGE, 18, i) 
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.17, i)
// Bracer of Agility (lvl 5)          
            elseif GetItemTypeId(i) == 'I0BH' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 19, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i) 
                    call LinkBonusToItem(u, BONUS_DAMAGE, 24, i) 
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.23, i) 
                    
// Bracer of Strength (lvl 1)               
            elseif GetItemTypeId(i) == 'I0B3' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 7, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 2, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 2, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 4, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH, 400, i) 
// Bracer of Strength (lvl 2)               
            elseif GetItemTypeId(i) == 'I0BJ' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 4, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 4, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 6, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH, 650, i)                                      
// Bracer of Strength (lvl 3)               
            elseif GetItemTypeId(i) == 'I0BK' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 13, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 6, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 6, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 8, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH, 900, i)  
// Bracer of Strength (lvl 4)               
            elseif GetItemTypeId(i) == 'I0BD' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 16, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 8, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 8, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 10, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH, 1150, i) 
// Bracer of Strength (lvl 5)               
            elseif GetItemTypeId(i) == 'I0BI' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 19, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 12, i) 
                    call LinkBonusToItem(u, BONUS_HEALTH, 1400, i) 
// Bracer of Intelligence (lvl 1)               
            elseif GetItemTypeId(i) == 'I0AV' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 2, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 2, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 7, i) 
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.12, i) 
                    call LinkBonusToItem(u, BONUS_MANA, 300, i) 
 // Bracer of Intelligence (lvl 2)               
            elseif GetItemTypeId(i) == 'I0BE' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 4, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 4, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i) 
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.16, i) 
                    call LinkBonusToItem(u, BONUS_MANA, 480, i) 
  // Bracer of Intelligence (lvl 3)               
            elseif GetItemTypeId(i) == 'I0BL' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 6, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 6, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 13, i) 
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.20, i) 
                    call LinkBonusToItem(u, BONUS_MANA, 660, i) 
  // Bracer of Intelligence (lvl 4)               
            elseif GetItemTypeId(i) == 'I0BM' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 8, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 8, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 16, i) 
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.24, i) 
                    call LinkBonusToItem(u, BONUS_MANA, 840, i) 
  // Bracer of Intelligence (lvl 5)               
            elseif GetItemTypeId(i) == 'I0BN' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i) 
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i) 
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 19, i) 
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.28, i) 
                    call LinkBonusToItem(u, BONUS_MANA, 1020, i) 
                     
            endif
            
            set i = null
            set u = null
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.onPickUp)
        endmethod   
    endstruct
endscope

Player Report
i died selling a common item. i think it was bracers of intelligence and i respawned and entered GOD-mode
unknown.png
 
So dunno if i imported somehow wrong the system.
But players seems to be experiencing a huge bug

JASS:
scope AddItemBonuses
    private struct AddItemBonuses
        static method onPickUp takes nothing returns nothing
            local unit u = GetManipulatingUnit()
            local item i = GetManipulatedItem()
// Bracer of Agility (lvl 1)
            if GetItemTypeId(i) == 'I0AE' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 2, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 7, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 2, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 9, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.08, i)
// Bracer of Agility (lvl 2)
            elseif GetItemTypeId(i) == 'I0BG' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 4, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 4, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 12, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.11, i)
// Bracer of Agility (lvl 3)            
            elseif GetItemTypeId(i) == 'I0AN' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 6, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 13, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 6, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 15, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.14, i)
// Bracer of Agility (lvl 4)             
            elseif GetItemTypeId(i) == 'I0BF' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 8, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 16, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 8, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 18, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.17, i)
// Bracer of Agility (lvl 5)        
            elseif GetItemTypeId(i) == 'I0BH' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 19, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 24, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.23, i)
                  
// Bracer of Strength (lvl 1)             
            elseif GetItemTypeId(i) == 'I0B3' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 7, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 2, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 2, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 4, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 400, i)
// Bracer of Strength (lvl 2)             
            elseif GetItemTypeId(i) == 'I0BJ' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 4, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 4, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 6, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 650, i)                                    
// Bracer of Strength (lvl 3)             
            elseif GetItemTypeId(i) == 'I0BK' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 13, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 6, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 6, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 8, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 900, i)
// Bracer of Strength (lvl 4)             
            elseif GetItemTypeId(i) == 'I0BD' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 16, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 8, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 8, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 10, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 1150, i)
// Bracer of Strength (lvl 5)             
            elseif GetItemTypeId(i) == 'I0BI' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 19, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 12, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 1400, i)
// Bracer of Intelligence (lvl 1)             
            elseif GetItemTypeId(i) == 'I0AV' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 2, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 2, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 7, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.12, i)
                    call LinkBonusToItem(u, BONUS_MANA, 300, i)
 // Bracer of Intelligence (lvl 2)             
            elseif GetItemTypeId(i) == 'I0BE' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 4, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 4, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.16, i)
                    call LinkBonusToItem(u, BONUS_MANA, 480, i)
  // Bracer of Intelligence (lvl 3)             
            elseif GetItemTypeId(i) == 'I0BL' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 6, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 6, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 13, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.20, i)
                    call LinkBonusToItem(u, BONUS_MANA, 660, i)
  // Bracer of Intelligence (lvl 4)             
            elseif GetItemTypeId(i) == 'I0BM' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 8, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 8, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 16, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.24, i)
                    call LinkBonusToItem(u, BONUS_MANA, 840, i)
  // Bracer of Intelligence (lvl 5)             
            elseif GetItemTypeId(i) == 'I0BN' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 19, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.28, i)
                    call LinkBonusToItem(u, BONUS_MANA, 1020, i)
                   
            endif
          
            set i = null
            set u = null
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.onPickUp)
        endmethod 
    endstruct
endscope

Player Report

View attachment 402761
Looks very similar to my trigger (although I have some items that needs to "refresh" and I have edit:ed the system to add a few more bonuses, as well as giving it capabilities that I needed).

JASS:
scope ItemsDefinitions
    globals
        boolean writeBonusesToItemText = false
        boolean isRefreshAction = false
        //BASIC ITEMS (Permanent):
        constant integer AXE_OF_ATTACK = 'I00Q'
        constant integer SWORD_OF_ATTACK = 'I00R'
        constant integer LEATHER_PANTS = 'I00P'
        constant integer SCALE_ARMOR = 'I00M'
        //... More Items...
    endglobals

    private struct ItemsDefinitions
        static method RefreshItem takes unit u, item i returns nothing
            if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
                return
            endif
            if GetItemTypeId(i) == CUP_OF_BLOOD then
                call RemoveSpecificBonusFromItem(i, BONUS_SPELL_POWER)
                call LinkBonusToItem(u, BONUS_SPELL_POWER, 5 + 0.1 * GetItemCharges(i), i)
            elseif GetItemTypeId(i) == GOLDEN_GOBLET_OF_GORE then
                call RemoveSpecificBonusFromItem(i, BONUS_SPELL_POWER)
                call RemoveSpecificBonusFromItem(i, BONUS_SPELL_HASTE)
                call LinkBonusToItem(u, BONUS_SPELL_POWER, 40 + 0.1 * GetItemCharges(i), i)
                call LinkBonusToItem(u, BONUS_SPELL_HASTE, 0.04 * GetItemCharges(i), i)
            elseif GetItemTypeId(i) == MAGE_BLADE then
                call RemoveSpecificBonusFromItem(i, BONUS_DAMAGE)
                if GetUnitStateSwap(UNIT_STATE_MANA, u) > 0.2 * MaxMana(u) then
                    call LinkBonusToItem(u, BONUS_DAMAGE, 25 + 0.02 * MaxMana(u), i)
                else
                    call LinkBonusToItem(u, BONUS_DAMAGE, 25, i)
                endif
            //... More Items that needs to "refresh"
            endif
        endmethod
        static method OnDropItem takes nothing returns nothing
            local unit u = GetManipulatingUnit()
            local item i = GetManipulatedItem()
            local group g
           
            if not isRefreshAction and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
                if GetItemTypeId(i) == RING_OF_POWER and CountItemsOfType(u, RING_OF_POWER) == 1 then
                    set g = LoadGroupHandle(udg_item_hash, StringHash("Ring of Power wearers"), 0)
                    call GroupRemoveUnit(g, u)
                    if BlzGroupGetSize(g) == 0 then
                        call DisableTrigger(gg_trg_RingOfPowerEvent)
                    endif
                endif
            endif
            set u = null
            set i = null
            set g = null
        endmethod
        static method OnPickUp takes nothing returns nothing
            local unit u = GetManipulatingUnit()
            local item i = GetManipulatedItem()
            if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
                set u = null
                set i = null
                return
            endif
//call BJDebugMsg("Pick up")
            //----- PERMANENT ITEMS ------
            if GetItemTypeId(i) == AXE_OF_ATTACK then
                call LinkBonusToItem(u, BONUS_DAMAGE, 8, i)
            //----- ARTIFACT ITEMS ------
            elseif GetItemTypeId(i) == AMULET_OF_FIRE then
                call LinkBonusToItem(u, BONUS_HEALTH, 200, i)
                call LinkBonusToItem(u, BONUS_SPELL_POWER, 15, i)
            elseif GetItemTypeId(i) == ARCANE_ARMOR_OF_BLASTING then
                call LinkBonusToItem(u, BONUS_ARMOR, 40, i)
                call LinkBonusToItem(u, BONUS_SPELL_POWER, 65, i)
                call LinkBonusToItem(u, BONUS_INTELLIGENCE, 14, i)
                call LinkBonusToItem(u, BONUS_SPELL_HASTE, 15, i)
            elseif GetItemTypeId(i) == ARCANE_STAFF_OF_ENERGY then
                call LinkBonusToItem(u, BONUS_MANA, 350, i)
                call LinkBonusToItem(u, BONUS_SPELL_POWER, 100, i)
                call LinkBonusToItem(u, BONUS_SPELL_POWER_MULTIPLIER, 0.25, i)
            elseif GetItemTypeId(i) == BLACK_NAVAJA then
                call LinkBonusToItem(u, BONUS_DAMAGE, 10, i)
                call LinkBonusToItem(u, BONUS_AGILITY, 5, i)
                call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 2, i)
            //... More Items...
            endif
            set i = null
            set u = null
        endmethod
        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.OnPickUp)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.OnDropItem)
        endmethod  
    endstruct
   
    function RefreshItem takes unit u, item i returns nothing
        call ItemsDefinitions.RefreshItem(u, i)
    endfunction
endscope

Edit: The bonus number seems to be Integer Max-value (around 2^31), meaning that something is messed up. Do you have other triggers that manupulated items picked up/dropped?
 
Level 20
Joined
May 16, 2012
Messages
635
So dunno if i imported somehow wrong the system.
But players seems to be experiencing a huge bug

JASS:
scope AddItemBonuses
    private struct AddItemBonuses
        static method onPickUp takes nothing returns nothing
            local unit u = GetManipulatingUnit()
            local item i = GetManipulatedItem()
// Bracer of Agility (lvl 1)
            if GetItemTypeId(i) == 'I0AE' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 2, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 7, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 2, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 9, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.08, i)
// Bracer of Agility (lvl 2)
            elseif GetItemTypeId(i) == 'I0BG' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 4, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 4, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 12, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.11, i)
// Bracer of Agility (lvl 3)            
            elseif GetItemTypeId(i) == 'I0AN' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 6, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 13, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 6, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 15, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.14, i)
// Bracer of Agility (lvl 4)             
            elseif GetItemTypeId(i) == 'I0BF' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 8, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 16, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 8, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 18, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.17, i)
// Bracer of Agility (lvl 5)        
            elseif GetItemTypeId(i) == 'I0BH' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 19, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i)
                    call LinkBonusToItem(u, BONUS_DAMAGE, 24, i)
                    call LinkBonusToItem(u, BONUS_ATTACK_SPEED, 0.23, i)
                  
// Bracer of Strength (lvl 1)             
            elseif GetItemTypeId(i) == 'I0B3' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 7, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 2, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 2, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 4, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 400, i)
// Bracer of Strength (lvl 2)             
            elseif GetItemTypeId(i) == 'I0BJ' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 4, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 4, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 6, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 650, i)                                    
// Bracer of Strength (lvl 3)             
            elseif GetItemTypeId(i) == 'I0BK' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 13, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 6, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 6, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 8, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 900, i)
// Bracer of Strength (lvl 4)             
            elseif GetItemTypeId(i) == 'I0BD' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 16, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 8, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 8, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 10, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 1150, i)
// Bracer of Strength (lvl 5)             
            elseif GetItemTypeId(i) == 'I0BI' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 19, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i)
                    call LinkBonusToItem(u, BONUS_HEALTH_REGEN, 12, i)
                    call LinkBonusToItem(u, BONUS_HEALTH, 1400, i)
// Bracer of Intelligence (lvl 1)             
            elseif GetItemTypeId(i) == 'I0AV' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 2, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 2, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 7, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.12, i)
                    call LinkBonusToItem(u, BONUS_MANA, 300, i)
 // Bracer of Intelligence (lvl 2)             
            elseif GetItemTypeId(i) == 'I0BE' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 4, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 4, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 10, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.16, i)
                    call LinkBonusToItem(u, BONUS_MANA, 480, i)
  // Bracer of Intelligence (lvl 3)             
            elseif GetItemTypeId(i) == 'I0BL' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 6, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 6, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 13, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.20, i)
                    call LinkBonusToItem(u, BONUS_MANA, 660, i)
  // Bracer of Intelligence (lvl 4)             
            elseif GetItemTypeId(i) == 'I0BM' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 8, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 8, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 16, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.24, i)
                    call LinkBonusToItem(u, BONUS_MANA, 840, i)
  // Bracer of Intelligence (lvl 5)             
            elseif GetItemTypeId(i) == 'I0BN' then
                    call LinkBonusToItem(u, BONUS_STRENGTH, 10, i)
                    call LinkBonusToItem(u, BONUS_AGILITY, 10, i)
                    call LinkBonusToItem(u, BONUS_INTELLIGENCE, 19, i)
                    call LinkBonusToItem(u, BONUS_MANA_REGEN, 0.28, i)
                    call LinkBonusToItem(u, BONUS_MANA, 1020, i)
                   
            endif
          
            set i = null
            set u = null
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.onPickUp)
        endmethod 
    endstruct
endscope

Player Report

View attachment 402761
The system already checks for integer overflow for the integer type bonusses. Most likely you are doing something wrong when adding the bonus to Strength. If you remove enough Strength so that the unit hp would go below 0 then the unit will die. Why are you manipulating such big numbers on your map????
 
Level 31
Joined
Aug 6, 2015
Messages
628
The system already checks for integer overflow for the integer type bonusses. Most likely you are doing something wrong when adding the bonus to Strength. If you remove enough Strength so that the unit hp would go below 0 then the unit will die. Why are you manipulating such big numbers on your map????
I don't have such big numbers , game barelly has any items that goes above 300 stat points into smth. (and over 10k into hp or mp)
 
Level 31
Joined
Aug 6, 2015
Messages
628
Do you have any triggers that could be increasing or decreasing the bonusses in a periodic or in a loop? Something that would eventually make a unit have those big values? That is the other possibility.
Regarding these specific items i don't.
(The bug is happening only with them)
The bug is not happening in single player though.
(it happened in a 8 player lobby in a 18 player map)
Player sold its item
- Got the bug with the stats and hp.
 
Level 31
Joined
Aug 6, 2015
Messages
628
Could maybe that cause the bug?
JASS:
private static method checkOverflow takes real current, real amount returns real      
  if amount > 0 and current > 2147483647 - amount then        
  return 2147483647 - current        
  elseif amount < 0 and current < -2147483648 - amount then          
    return -2147483648 - current   else   return amount          
 endif       
 endmethod
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
Could maybe that cause the bug?
JASS:
private static method checkOverflow takes real current, real amount returns real       
  if amount > 0 and current > 2147483647 - amount then         
  return 2147483647 - current         
  elseif amount < 0 and current < -2147483648 - amount then           
    return -2147483648 - current            else                return amount            endif        endmethod
That is what prevent the system from bugging lol
 
Level 25
Joined
Feb 2, 2006
Messages
1,667
Can I use LinkBonusToBuff and AddUnitBonusTimed together, so the bonus uses a buff which is automatically added from start and removed after the given time? I guess buffs need a dummy ability, so I have to add and remove this dummy ability using the buff manually? I think some combination of functions with a customized callback when then the buff ends would be very useful.

I am using RegisterPlayerUnitEvent v1.0.3.2 by Bannar which is required by UnitEventEx in my map. I get the syntax errors:

JASS:
 function s__NewBonusUtils_onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM , function s__NewBonusUtils_onDrop)
        endfunction

It is missing the player parameter.


Your system uses RegisterPlayerUnitEvent v5.1.0.1 By Magtheridon96.

Apparently, they are not compatible :(?
 
Last edited:

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,864
New Bonus uses RegisterPlayerUnitEvent (Magtheridon96) library, with function:
JASS:
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
Bannar's one is RegisterPlayerUnitEvent library, which requires RegisterNativeEvent library, with function:
JASS:
function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing

Both have the same name. I'm not sure what's happening on your end, but Magtheridon96's library is outdated, so @chopinski should update it by changing a few things:

JASS:
    function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
        local integer i = GetHandleId(p)
        local integer k = bj_MAX_PLAYER_SLOTS //Changed from 15 to bj_MAX_PLAYER_SLOTS
        if t[i] == null then
            set t[i] = CreateTrigger()
            loop
                call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
                exitwhen k == 0
                set k = k - 1
            endloop
        endif
        call TriggerAddCondition(t[i], Filter(c))
    endfunction
    
    function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
        local integer i = 28 * GetHandleId(p) + GetPlayerId(pl) //Changed from 16 to 28
        if t[i] == null then
            set t[i] = CreateTrigger()
            call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
        endif
        call TriggerAddCondition(t[i], Filter(c))
    endfunction

Or use Bannar's library. I suspect many of your resources are also dependent on this library.
 
Level 17
Joined
Apr 13, 2008
Messages
1,597
Hello,

I see you are missing some stats that could be easily crafted from existing abilities or by adjusting unitfields. I gave some examples. Tell me if you need more.

Mana % regen per sec.
I based mine on Fountain of Mana Regen. Just make sure that the percent boolean field is set to true:

JASS:
            if GetUnitAbilityLevel(u, 'DQMR') < 1 then
            call UnitAddAbility(u, 'DQMR')
            endif
        set a = BlzGetUnitAbility(u, 'DQMR')
        call BlzSetAbilityRealLevelField(a, ConvertAbilityRealLevelField('Arm1'), 0, DInvUnitHandleDB[uhndl][5].real[9] - amount)
        call IncUnitAbilityLevel(u, 'DQMR')
        call DecUnitAbilityLevel(u, 'DQMR')

HP % regen per sec.
Based on fountain Life Regen.

JASS:
            if GetUnitAbilityLevel(u, 'DQLR') < 1 then
            call UnitAddAbility(u, 'DQLR')
            endif
        set a = BlzGetUnitAbility(u, 'DQLR')
        call BlzSetAbilityRealLevelField(a, ConvertAbilityRealLevelField('Oar1'), 0, DInvUnitHandleDB[uhndl][5].real[6] - amount)
        call IncUnitAbilityLevel(u, 'DQLR')
        call DecUnitAbilityLevel(u, 'DQLR')


Movement speed %

I based mine on Unholy Aura:
JASS:
            if GetUnitAbilityLevel(u, 'DQHM') < 1 then
            call UnitAddAbility(u, 'DQHM')
            endif
        set a = BlzGetUnitAbility(u, 'DQHM')
        call BlzSetAbilityRealLevelField(a, ABILITY_RLF_MOVEMENT_SPEED_INCREASE_PERCENT_UAU1, 0, DInvUnitHandleDB[uhndl][5].real[22] - amount)      
        call BlzUnitDisableAbility(u, 'DQHM', TRUE, TRUE)
        call UnitRemoveAbility(u, 'BDQ0')
        call BlzUnitDisableAbility(u, 'DQHM', FALSE, TRUE)
        call BlzUnitDisableAbility(u, 'BDQ0', FALSE, TRUE)

Armor %
Life on hit (not % lifesteal)
Attack Range

JASS:
        call BlzSetUnitWeaponRealField(u, ConvertUnitWeaponRealField('ua1r'), 0, BlzGetUnitWeaponRealField(u, ConvertUnitWeaponRealField('ua1r'), 0) - amount)
        call BlzSetUnitWeaponRealField(u, ConvertUnitWeaponRealField('ua1r'), 1, BlzGetUnitWeaponRealField(u, ConvertUnitWeaponRealField('ua1r'), 1) - amount)

Melee % DMG
Ranged % DMG
Melee DMG
Ranged DMG
Thorns
Thorns %

And probably some others.
Any plans to add them?
 
Last edited:

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,456
Am i doing something wrong here? (doesn't apply the de-bonus)
View attachment 461499
You're using the wrong Event, you need to use "A unit Starts the effect of an ability" which occurs at the exact time your ability executes (spends mana, goes on cooldown, launches effects, etc). If you need to wait until the Buff is applied before running your Custom Script then you should account for this using a method which periodically checks the state of the target unit. A Unit Group and Periodic Interval come to mind.

Finishes casting is reserved for rare situations like detecting the end of a channeled ability. It doesn't have Event Responses like (Target unit of ability being cast) nor would it be a good time to check for this sort of thing anyway.
 
Last edited:
Level 31
Joined
Aug 6, 2015
Messages
628
If you need to wait until the Buff is applied before running your Custom Script then you should account for this using a method which periodically checks the state of the target unit. A Unit Group and Periodic Interval come to mind.
Well i am using a default ability (Ensnare)
I did try "A unit Starts the effect of an ability" , but for some reason it only applied the minus armor only for half a sec or smth, and then instantly removed it, but the buff of the ensnare was still on the target.

So i did more testings , for some reason if casting ensnare from far range it wont' work (the - armor will be removed instantly) , but if casting it from melee range - the buff will be applied.
image.png
 
Last edited:

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,456
Well i am using a default ability (Ensnare)
I did try "A unit Starts the effect of an ability" , but for some reason it only applied the minus armor only for half a sec or smth, and then instantly removed it, but the buff of the ensnare was still on the target.

So i did more testings , for some reason if casting ensnare from far range it wont' work (the - armor will be removed instantly) , but if casting it from melee range - the buff will be applied.
Of course, Ensnare uses a missile which delays the application of the buff. The NewBonus system has no idea about this delay, it's going to run the LinkBonusToBuff() function the exact moment you tell it to. The fact that it works at close range tells us that the Buff has been applied in time for the Link() function to detect it.

I suggested a solution to this problem -> You'll want to account for this Missile delay by calling the Custom Script once the target unit actually has the Ensnare buff. This can be handled with a Unit Group variable and a Periodic Interval trigger. The idea is to periodically check if the units targeted by this ability have the buff or not. Once they have it, run the LinkBonus() function and Remove them from the Unit Group. If they die, remove them from the Unit Group as well.

You can probably edit the system to do this for you or request Chopinski to add it as a feature if it doesn't exist already. Something like: LinkBonusToBuffDelayed()
 
Last edited:
Level 31
Joined
Aug 6, 2015
Messages
628
LinkBonusToBuffDelayed()
That would be nice
I suggested a solution to this problem -> You'll want to account for this Missile delay by calling the Custom Script once the target unit actually has the Ensnare buff. This can be handled with a Unit Group and a Periodic Interval trigger which periodically checks if the target units inside the Unit Group have the buff or not. Once they have it, run the LinkBonus() function and Remove them from the Unit Group. If they die, remove them from the Unit Group as well.

You can probably edit the system to do this for you or request Chopinski to add it as a feature if it doesn't exist already. Something like: LinkBonusToBuffDelayed()
The thing is, I am not capable of doing it effectively since
- It is a hero spell (has few levels with different duration and different armor values)
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,456
That would be nice

The thing is, I am not capable of doing it effectively since
- It is a hero spell (has few levels with different duration and different armor values)
You might as well use AddUnitBonusTimed() in the meantime. You can make it last something like "Buff Duration + 0.50" so the Armor reduction lasts the duration of the buff plus an extra 0.50 seconds to account for the Missle delay.

You could also delay it by 0.50 seconds using a Wait action. Just remember that certain Event Responses like (Target unit of ability being cast) can't be used after Waits, so some local variables or the global Shadowing technique would be useful here:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to Ensnare
  • Actions
    • Custom script: local unit udg_AbilityTarget = GetSpellTargetUnit()
    • Wait 0.50 game-time seconds
    • Set Variable AbilityLevel = (Level of Ensare for (Triggering unit))
    • Set Variable ArmorReduction = EnsnareArmorReduction[AbilityLevel]
    • Set Variable BuffDuration = EnsnareBuffDuration[AbilityLevel]
    • Custom script: call AddUnitBonusTimed( udg_AbilityTarget, BONUS_ARMOR, udg_ArmorReduction, udg_BuffDuration )
  • Events
    • Time - Elapsed game time is 0.00 seconds
  • Conditions
  • Actions
    • Set Variable EnsnareArmorReduction[1] = 2.00
    • Set Variable EnsnareArmorReduction[2] = 4.00
    • Set Variable EnsnareArmorReduction[3] = 6.00
    • Set Variable EnsnareBuffDuration[1] = 5.00
    • Set Variable EnsnareBuffDuration[2] = 6.00
    • Set Variable EnsnareBuffDuration[3] = 7.00
 
Level 5
Joined
Nov 3, 2018
Messages
73
question i'm getting a "redeclared" error unless i delete the damage interface folder so for damage so it seems it seems it may not compatible be with damage engine 5.A.0.0 ?
 
Level 5
Joined
Nov 3, 2018
Messages
73
Redeclared sounds like you have a duplicate of a system that needs to be deleted.
i just double checked but just to be sure ill make a new map and import just the damage and the new bonus systems
thanks uncle! you're everywhere :D also would you happen to know if is there a way so that these bonuses do not stack?
i was trying to reduce the ammount of damage detection triggers i have and i currently i got an ability that makes basic attack give extra agility temporarily but due to my limited knowledge i ended up making a temporal 10 second agility boost whithin a trigger that turns it self on after 10 wait seconds (because trying to place it in another trigger that detects that damage too makes it stack to the infinite)

JASS:
function Trig_Cazadora_de_magos_Conditions takes nothing returns boolean
    if ( not ( GetUnitTypeId(GetAttacker()) == 'H000' ) ) then
        return false
    endif
    return true
endfunction

function Trig_Cazadora_de_magos_Actions takes nothing returns nothing
    call DisableTrigger( GetTriggeringTrigger() )
    call DisplayTimedTextToForce( GetPlayersAll(), 1.00, R2S(udg_DamageEventAmount) )
    call AddUnitBonusTimed(udg_Hero_Kerrigan, BONUS_AGILITY, 10,15)
    call PolledWait( 15.00 )
    call EnableTrigger( GetTriggeringTrigger() )
endfunction

//===========================================================================
function InitTrig_Cazadora_de_magos takes nothing returns nothing
    set gg_trg_Cazadora_de_magos = CreateTrigger(  )
    call DisableTrigger( gg_trg_Cazadora_de_magos )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Cazadora_de_magos, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddCondition( gg_trg_Cazadora_de_magos, Condition( function Trig_Cazadora_de_magos_Conditions ) )
    call TriggerAddAction( gg_trg_Cazadora_de_magos, function Trig_Cazadora_de_magos_Actions )
endfunction
 
Level 18
Joined
Oct 17, 2012
Messages
818
BONUS_DAMAGE is not a String but rather an integer. More precisely, it is the name of an integer variable. Change the variable type of INBonus to integer and then set it to the corresponding integer for a bonus type. In your specific example, you would set INBonus to 1.
Refer to this list below for their representation:
Lua:
-- The bonus types
    BONUS_DAMAGE                     = 1
    BONUS_ARMOR                      = 2
    BONUS_AGILITY                    = 3
    BONUS_STRENGTH                   = 4
    BONUS_INTELLIGENCE               = 5
    BONUS_HEALTH                     = 6
    BONUS_MANA                       = 7
    BONUS_MOVEMENT_SPEED             = 8
    BONUS_SIGHT_RANGE                = 9
    BONUS_HEALTH_REGEN               = 10
    BONUS_MANA_REGEN                 = 11
    BONUS_ATTACK_SPEED               = 12
    BONUS_MAGIC_RESISTANCE           = 13
    BONUS_EVASION_CHANCE             = 14
    BONUS_CRITICAL_DAMAGE            = 15
    BONUS_CRITICAL_CHANCE            = 16
    BONUS_LIFE_STEAL                 = 17
    BONUS_MISS_CHANCE                = 18
    BONUS_SPELL_POWER_FLAT           = 19
    BONUS_SPELL_POWER_PERCENT        = 20
    BONUS_SPELL_VAMP                 = 21
    BONUS_COOLDOWN_REDUCTION         = 22
    BONUS_COOLDOWN_REDUCTION_FLAT    = 23
    BONUS_COOLDOWN_OFFSET            = 24
    BONUS_TENACITY                   = 25
    BONUS_TENACITY_FLAT              = 26
    BONUS_TENACITY_OFFSET            = 27
 
Last edited:

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,456
Thanks Elprede
I also have another question , how do one set the real value numbers? (like hp regen : for ex 2.5 hp/s or attack speed like 5%)
You just write it the way you'd normally write a Real. Does this not work?
  • Custom script: call LinkBonusToItem( GetTriggerUnit(), BONUS_HEALTH_REGEN, 2.5, GetManipulatedItem() )
  • Custom script: call LinkBonusToItem( GetTriggerUnit(), BONUS_ATTACK_SPEED, 5, GetManipulatedItem() )
I assume the system is smart enough to convert these between Int/Real when necessary.
 
Level 31
Joined
Aug 6, 2015
Messages
628
You just write it the way you'd normally write a Real. Does this not work?
  • Custom script: call LinkBonusToItem( GetTriggerUnit(), BONUS_HEALTH_REGEN, 2.5, GetManipulatedItem() )
  • Custom script: call LinkBonusToItem( GetTriggerUnit(), BONUS_ATTACK_SPEED, 5, GetManipulatedItem() )
I assume the system is smart enough to convert these between Int/Real when necessary.
Thanks, it does work with real numbers and real type variables.
 
Top