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

[Wurst] BonusHandler

Level 14
Joined
Jun 27, 2008
Messages
1,325
[WurstScript] BonusHandler 1.5

BonusHandler is a library for WurstScript which provides an easy way to manipulate unit bonuses via trigger, similar to BonusMod for vJass.

Available bonuses:
  • Life
  • Mana
  • Damage
  • Armor
  • Sightrange
  • Liferegen
  • Manaregen (percental or absolute)
  • Attackspeed
  • Strength
  • Agility
  • Intelligence

There are no requirements except for the obligatory WurstScript StandardLib.

The required objectdata gets generated by WurstScripts Objectediting-Compiletimenatives. Unlike the JNGP objectmerger the WurstScript objectgeneration doesnt require LUA, subsequently it works with newer operating systems where LUA is broken.

Code:
Wurst:
package BonusHandlerConfig
/**
* Values are the exponents to base 2.
* 2^9=512, 2^10=1024, 2^11=2048, 2^12=4096 etc.
*/

public constant MAX_BONUS_LIFE = 12
public constant MAX_BONUS_MANA = 12
public constant MAX_BONUS_DAMAGE = 10
public constant MAX_BONUS_ARMOR = 10
public constant MAX_BONUS_SIGHTRANGE = 11
public constant MAX_BONUS_LIFEREGEN = 4
public constant MAX_BONUS_MANAREGEN = 10
public constant MAX_BONUS_ATTACKSPEED = 9
public constant MAX_BONUS_STRENGTH = 9
public constant MAX_BONUS_AGILITY = 9
public constant MAX_BONUS_INTELLIGENCE = 9
public constant IS_MANAREGEN_PERCENTAL = false
Wurst:
package BonusHandler
// Version 1.5 - Credits: Frotty, Overkane, muzzel, Crigges, Earth-Fury, Halithor
import BonusHandlerConfig
import AbilityObjEditing
import ObjectIds
import Preloader

int array powersOf2
int array rawShifts
int rawShiftNeg
BonusType array bonusTypes

public enum Bonus
     LIFE
     MANA
    DAMAGE
    ARMOR
    SIGHTRANGE
    LIFEREGEN
    MANAREGEN
    ATTACKSPEED
    STRENGTH
    AGILITY
    INTELLIGENCE

//Gets the current bonus value
public function getUnitBonus(unit u, Bonus bonusType) returns int
    let bonus = bonusTypes[bonusType castTo int]
    var result = 0
    if u.hasAbility(bonus.baseRaw + rawShiftNeg)
         result -= powersOf2[bonus.maxBonus]
    for i = 0 to bonus.maxBonus - 1
         result += u.hasAbility(bonus.baseRaw + shiftRaw(i)) ? powersOf2[i] : 0
    return result

//Adds to current bonus bonusValue
public function addUnitBonus(unit u, Bonus bonusType, int bonusValue)
    int currentBonusValue = getUnitBonus(u, bonusType)
    int afterBonusValue = currentBonusValue + bonusValue
    setUnitBonus(u, bonusType, afterBonusValue)

/** Sets the bonus of specified type for the unit to the given amount. */
public function setUnitBonus(unit u, Bonus bonusType, int amount)
    bonusTypes[bonusType castTo int].setBonus(u, amount)

/** Sets the bonus of specified type for the unit to zero. */
public function clearUnitBonus(unit u, Bonus bonusType)
    bonusTypes[bonusType castTo int].clearBonus(u)

/** Gets the highest applicable value for the specified bonus type. */
public function getMaxBonus(Bonus bonusType) returns int
    return powersOf2[bonusTypes[bonusType castTo int].maxBonus - 1]

/** Gets the smallest applicable value for the specified bonus type. */
public function getMinBonus(Bonus bonusType) returns int
    return -powersOf2[bonusTypes[bonusType castTo int].maxBonus]

/** Removes all bonuses from the specified unit. */
public function clearAllUnitBonuses(unit u)
    bonusTypes[Bonus.LIFE castTo int].clearBonus(u)
    bonusTypes[Bonus.MANA castTo int].clearBonus(u)
    bonusTypes[Bonus.DAMAGE castTo int].clearBonus(u)
    bonusTypes[Bonus.ARMOR castTo int].clearBonus(u)
    bonusTypes[Bonus.SIGHTRANGE castTo int].clearBonus(u)
    bonusTypes[Bonus.LIFEREGEN castTo int].clearBonus(u)
    bonusTypes[Bonus.MANAREGEN castTo int].clearBonus(u)
    bonusTypes[Bonus.ATTACKSPEED castTo int].clearBonus(u)
    bonusTypes[Bonus.STRENGTH castTo int].clearBonus(u)
    bonusTypes[Bonus.AGILITY castTo int].clearBonus(u)
    bonusTypes[Bonus.INTELLIGENCE castTo int].clearBonus(u)


/** Enumeration of the bonustypes. */
class BonusType
    int baseRaw
    int maxBonus

    construct(int baseRaw, int maxBonus)
         this.baseRaw = baseRaw
         this.maxBonus = maxBonus

    function setBonus(unit u, int bonus)
         if bonus >= powersOf2[maxBonus]
              printLog(Loglevel.WARNING, "Bonus exceeds maximum")
              return
         if bonus < -powersOf2[maxBonus]
              printLog(Loglevel.WARNING, "Bonus exceeds maximum")
              return
     
         int val
         bool addNeg = false
         var raw = baseRaw + rawShiftNeg
         if bonus < 0
              addNeg = true
              val = bonus + powersOf2[maxBonus]
         else
              val = bonus
              UnitMakeAbilityPermanent(u, false, raw)
              u.removeAbility(raw)
         
         for n = maxBonus-1 downto 0
              raw = baseRaw + rawShifts[n]
              if val >= powersOf2[n]
                    u.addAbility(raw)
                    UnitMakeAbilityPermanent(u, true, raw)
                    val -= powersOf2[n]
              else
                    UnitMakeAbilityPermanent(u, false, raw)
                    u.removeAbility(raw)
         // Add negative last to avoid setting stat to <0 (killing the unit, etc)
         if addNeg
              u.addAbility(baseRaw + rawShiftNeg)
              UnitMakeAbilityPermanent(u, true, baseRaw + rawShiftNeg)

    function clearBonus(unit u)
         var raw = baseRaw + rawShiftNeg
         u.makeAbilityPermanent(raw, false)
         u.removeAbility(raw)
         for n = 0 to maxBonus-1
              raw = baseRaw + rawShifts[n]
              u.makeAbilityPermanent(raw, false)
              u.removeAbility(raw)
     

/** Example: 'XY00' + shiftRaw(15) -> 'XY15' */
function shiftRaw(int n) returns int
    if n < 10
         return n
    else
         return (n div 10) * 256 + n.moduloInt(10)


/** Example: pow2(x) -> 2^x */
function pow2(int e) returns int
    if e == 0
         return 1
    return 2 * pow2(e-1)

function setProperties(AbilityDefinition a, string suffix, string bonusName)
    a.setItemAbility(false)
    a.setEditorSuffix("(" + suffix + ")")
    a.setIconNormal("ReplaceableTextures\\CommandButtons\\BTNPenguin.blp")
    a.setName("BonusHandler - " + bonusName)


init
    for n = 0 to 30 // if u need bonuses higher than 1 billion u should rethink your map design...
         powersOf2[n] = Pow(2, n.toReal()).toInt()
    for n = 0 to 30
         rawShifts[n] = shiftRaw(n)
    rawShiftNeg = '00--' - '0000'

    bonusTypes[Bonus.LIFE castTo int] = new BonusType('$L00', MAX_BONUS_LIFE)
    bonusTypes[Bonus.MANA castTo int] = new BonusType('$M00', MAX_BONUS_MANA)
    bonusTypes[Bonus.DAMAGE castTo int] = new BonusType('$D00', MAX_BONUS_DAMAGE)
    bonusTypes[Bonus.ARMOR castTo int] = new BonusType('$R00', MAX_BONUS_ARMOR)
    bonusTypes[Bonus.SIGHTRANGE castTo int] = new BonusType('$V00', MAX_BONUS_SIGHTRANGE)
    bonusTypes[Bonus.LIFEREGEN castTo int] = new BonusType('$l00', MAX_BONUS_LIFEREGEN)
    bonusTypes[Bonus.MANAREGEN castTo int] = new BonusType('$m00', MAX_BONUS_MANAREGEN)
    bonusTypes[Bonus.ATTACKSPEED castTo int] = new BonusType('$P00', MAX_BONUS_ATTACKSPEED)
    bonusTypes[Bonus.STRENGTH castTo int] = new BonusType('$S00', MAX_BONUS_STRENGTH)
    bonusTypes[Bonus.AGILITY castTo int] = new BonusType('$A00', MAX_BONUS_AGILITY)
    bonusTypes[Bonus.INTELLIGENCE castTo int] = new BonusType('$I00', MAX_BONUS_INTELLIGENCE)

@compiletime function genAbilities()
    string rawCode
    // Life:
    rawCode = "$L"
    for n = 0 to MAX_BONUS_LIFE
         string raw
         int sign = 1
         if n < MAX_BONUS_LIFE
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionMaxLifeBonusLeast(fourchar2int(raw))
         a.setMaxLifeGained(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Life")
     
    // Mana:
    rawCode = "$M"
    for n = 0 to MAX_BONUS_MANA
         string raw
         int sign = 1
         if n < MAX_BONUS_MANA
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionMaxManaBonusMost(fourchar2int(raw))
         a.setMaxManaGained(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Mana")
    // Damage:
    rawCode = "$D"
    for n = 0 to MAX_BONUS_DAMAGE
         string raw
         int sign = 1
         if n < MAX_BONUS_DAMAGE
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionAttackBonus(fourchar2int(raw))
         a.setAttackBonus(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Damage")
     
    // Armor:
    rawCode = "$R"
    for n = 0 to MAX_BONUS_ARMOR
         string raw
         int sign = 1
         if n < MAX_BONUS_ARMOR
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionDefenseBonusPlus1(fourchar2int(raw))
         a.setDefenseBonus(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Armor")

    // Sightrange:
    rawCode = "$V"
    for n = 0 to MAX_BONUS_SIGHTRANGE
         string raw
         int sign = 1
         if n < MAX_BONUS_SIGHTRANGE
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionSightBonus(fourchar2int(raw))
         a.setSightRangeBonus(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Sightrange")

    // Liferegen:
    rawCode = "$l"
    for n = 0 to MAX_BONUS_LIFEREGEN
         string raw
         int sign = 1
         if n < MAX_BONUS_LIFEREGEN
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionRegenLife(fourchar2int(raw))
         a.setHitPointsRegeneratedPerSecond(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Liferegen")

    // Manaregen:
    rawCode = "$m"
    for n = 0 to MAX_BONUS_MANAREGEN
         string raw
         int sign = 1
         if n < MAX_BONUS_MANAREGEN
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         if IS_MANAREGEN_PERCENTAL
              let a = new AbilityDefinitionItemRegenMana(fourchar2int(raw))
              a.setManaRegenerationBonusasfractionofnormal(1, sign * pow2(n) / 100.)
              setProperties(a, I2S(sign * pow2(n)) + "%", "Manaregen")
         else
              let a = new AbilityDefinitionNeutralRegenmanaonly(fourchar2int(raw))
              a.setPercentage(1, false)
              a.setAreaofEffect(1, 1)
              a.setTargetsAllowed(1, "self")
              a.setAmountRegenerated(1, sign * pow2(n) / 200.)
              a.setRace(Race.Other)
              setProperties(a, I2S(sign * pow2(n)), "Manaregen")

    // Attackspeed:
    rawCode = "$P"
    for n = 0 to MAX_BONUS_ATTACKSPEED
         string raw
         int sign = 1
         if n < MAX_BONUS_ATTACKSPEED
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionAttackSpeedIncrease(fourchar2int(raw))
         a.setAttackSpeedIncrease(1, sign * pow2(n) / 100.)
         setProperties(a, I2S(sign * pow2(n)) + "%", "Attackspeed")

    // Strength:
    rawCode = "$S"
    for n = 0 to MAX_BONUS_STRENGTH
         string raw
         int sign = 1
         if n < MAX_BONUS_STRENGTH
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionStrengthBonusPlus1(fourchar2int(raw))
         a.setStrengthBonus(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Strength")

    // Agility:
    rawCode = "$A"
    for n = 0 to MAX_BONUS_AGILITY
         string raw
         int sign = 1
         if n < MAX_BONUS_AGILITY
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionAgilityBonusPlus1(fourchar2int(raw))
         a.setAgilityBonus(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Agility")

    // Intelligence:
    rawCode = "$I"
    for n = 0 to MAX_BONUS_INTELLIGENCE
         string raw
         int sign = 1
         if n < MAX_BONUS_INTELLIGENCE
              raw = rawCode
              if n < 10
                    raw = raw + "0"
              raw = raw + I2S(n)
         else
              raw = rawCode + "--"
              sign = -1
         let a = new AbilityDefinitionIntelligenceBonusPlus1(fourchar2int(raw))
         a.setIntelligenceBonus(1, sign * pow2(n))
         setProperties(a, I2S(sign * pow2(n)), "Intelligence")
     
init
    for int i = Bonus.LIFE castTo int to Bonus.INTELLIGENCE castTo int
         for int j = 0 to (bonusTypes[i].maxBonus - 1)
              preloadAbility(bonusTypes[i].baseRaw + shiftRaw(j))

Changelog:
1.5 - Fixed a few bugs, thanks @Halithor
1.4 - Added GetBonus and AddBonus, abilities are now preloaded, works with latest stdlib
1.3 - Added the possibility to chose between percental and absolute manaregen, added HotDoc-conform documentation.
1.2 - Fixed the code so it works with the latest Wurst StdLib.
1.1 - Added bonus for max hp, max mana
1.0 - Initial release

Credits:
- Crigges
- Earth-Fury
 
Last edited by a moderator:
Level 14
Joined
Jun 27, 2008
Messages
1,325
Do you think it would be beneficial to use ability levels, have abilities with upto four levels?

Setting max mana/hp is essential for any bonus system.

BTW, the Paladin died instantly as it had -300 str applied ;)

I think using levels makes the whole thing slower as adding abilities takes more time the more levels they have. Sure, with more levels you would need less abilities, but still ...
But if you have a source that confirms otherwise im open to it.

Setting max hp/mana is essential, i agree, but dont using a different approach than for other stats. Thats why i will release it either as a independent library or add it in a future version (i wanna test some stuff before i write it).

Negative hp regen also kills the testunits, but whatever - its just a test map :p
 
Level 8
Joined
Jan 23, 2015
Messages
121
Got a lot of errors when I tried to import it. I assume it is outdated.
Yep it's outdated since stdlib got updated and many functions have been renamed, but it's the only problem with this (as far as I'm concerned), so you can fix this manually./
I fixed it by doing this three things:
- Rename all "printWarning" to just "print"
- Rename all "idString2IdInteger" to "fourchar2int"
- Make setRace in manaregen generation took Race.Other instead of raw string (I'm a bit confused they didn't overload it with method that takes string for some reason)

And then everything seems to be OK.
 
Last edited:
Top