Updated for 1.24b compatibility and renamed constants to circumvent a JassHelper bug.
Components:
Unit Properties consists of the core Unit Properties library and an additional Evasion library. These libraries give you dynamic control over the following properties for a unit:
The API is:
To increase a unit's strength by 4, for example, you would call
Installation
Download UnitPropertiesGenerator.txt and rename it to UnitPropertiesGenerator.lua. Place UnitPropertiesGenerator.lua in the same directory as your map. Then paste the UnitProperties library (and the Evasion library, if you want it) into your map and uncomment the line
Credits
Thanks to PitzerMike and Vexorian for their tools, Alexander244 for the original Lua script, and Anitarf for syntax and implementation suggestions.
Components:
Unit Properties consists of the core Unit Properties library and an additional Evasion library. These libraries give you dynamic control over the following properties for a unit:
- Agility
- Intelligence
- Strength
- Current Life
- Max Life
- Life Regeneration
- Current Mana
- Max Mana
- Mana Regeneration
- Mana Regeneration Percent
- Armor
- Damage
- Attack Speed
- Move Speed
- Attack Evasion
- Attack Miss
The API is:
JASS:
function CreateUnitProperties takes unit u returns nothing
function GetUnitProperties takes unit u returns properties
function DestroyUnitProperties takes unit u returns nothing
function UnitModifyAgi takes unit u, real amount returns nothing
function UnitModifyInt takes unit u, real amount returns nothing
function UnitModifyStr takes unit u, real amount returns nothing
function UnitModifyLife takes unit u, real amount returns nothing
function UnitModifyMaxLife takes unit u, real amount returns nothing
function UnitModifyLifeRegen takes unit u, real amount returns nothing
function UnitModifyMana takes unit u, real amount returns nothing
function UnitModifyMaxMana takes unit u, real amount returns nothing
function UnitModifyManaRegen takes unit u, real amount returns nothing
function UnitModifyManaRegenPercent takes unit u, real amount returns nothing
function UnitModifyArmor takes unit u, real amount returns nothing
function UnitModifyDamage takes unit u, real amount returns nothing
function UnitModifyAttackSpeed takes unit u, real amount returns nothing
function UnitModifyMoveSpeed takes unit u, real amount returns nothing
function UnitModifyAttackEvasion takes unit u, real amount returns nothing
function UnitModifyAttackMiss takes unit u, real amount returns nothing
//For each of these functions, there is also a UnitSet and a UnitGet equivalent.
To increase a unit's strength by 4, for example, you would call
UnitModifyStr(unit, 4)
, or UnitModifyStr(unit, -4)
if you wanted to decrease it instead. To set a property to a specific value, you would use UnitSetStr(unit, value)
. In addition to these functions, the following wrapper syntax is also included: set unit:Str = value
, or, if you want to increase or decrease the property by a certain amount, set unit:Str = unit:Str + value
.[jass=Unit Properties]//*******************************************************************************
//* Unit Properties *
//* v.D *
//* *
//* By: Cassiel *
//*******************************************************************************
//*******************************************************************************
// The ObjectMerger command will automatically generate all the necessary
// abilities for you using the external Lua script, which should be placed in the
// same directory as the map you're installing to. It only needs to be run once.
//
////! external ObjectMerger UnitPropertiesGenerator.lua
library UnitProperties initializer Init
globals
//*******************************************************************************
// Max: Specifies the highest power of 2 the property ability sets use. A Max
// of 8 means the system supports values of +/- 511. To change values
// simply reset the Max variable in the Lua script.
//
// Offset: Specifies the difference between the + and - abilities for any
// property.
//
private constant integer MAX = 8
private constant integer OFFSET = 512
//*******************************************************************************
// These globals reference the starting abilities for properties that use binary
// counting. Changing them means changing the rawcodes of all the abilities the
// system uses for that property. This list includes all properties that stack
// linearly and are binary countable.
//
private constant integer AGI = 'AG+a'
private constant integer INT = 'IN+a'
private constant integer STR = 'ST+a'
private constant integer ATTACK_SPEED = 'AT+a'
private constant integer ARMOR = 'AR+a'
private constant integer DAMAGE = 'DA+a'
private constant integer LIFE_REGEN = 'LI+a'
private constant integer MANA_REGEN_PERCENT = 'MR+a'
//*******************************************************************************
// These globals reference the starting abilities for properties that use the
// life/mana bug.
//
private constant integer MAX_LIFE = 'Lif+'
private constant integer MAX_MANA = 'Man+'
// ******************************************************************************
// Timer: Used for any periodic properties that you want/need to
// trigger. Static mana gain (as opposed to percentage mana gain)
// is included as an example.
//
// pow2: Powers of 2 up to 30 are calculated and stored in this array at
// Init.
//
// UnitProperties: This array stores the properties struct for each unit that has
// it. It and the properties struct are public so that advanced
// users have the option of manipulating properties directly
// instead of relying on the UnitGet/Set functions.
//
private timer Timer = CreateTimer()
private integer array Pow2
properties array UnitProperties
endglobals
struct properties
// ******************************************************************************
// These are the standard properties you would want to manipulate on a given unit
//
real Agi = 0
real Int = 0
real Str = 0
real LifeRegen = 0
real ManaRegen = 0
real ManaRegenPercent = 0
real Armor = 0
real Damage = 0
real AttackSpeed = 0
real MoveSpeed
// ******************************************************************************
// These properties are used by the Evasion module, which gives fully dynamic
// control over a unit's chance to evade incoming or miss on outgoing attacks.
//
unit unit
real AttackEvasion = 0
real AttackMiss = 0
// ******************************************************************************
// These are examples of additional properties which would require other systems
// to take advantage of.
//
//real AttackCritical = 0
//real SpellEvasion = 0
//real SpellMiss = 0
//real SpellCritical = 0
// ******************************************************************************
// JASS doesn't handle decimal values very well. 1. + .05 = 1.049999952, and
// 1. + .05 - .05 != 1. Thus it is necessary to store the values without decimals
// and then convert them when they they are retrieved.
//
//real ExpRate = 100
//real BountyRate = 100
static method create takes unit u returns properties
local properties this = properties.allocate()
set .unit = u
// You may want to use GetUnitDefaultMoveSpeed here, but I have seen
// some bugs with it.
set .MoveSpeed = GetUnitMoveSpeed(u)
return this
endmethod
method onDestroy takes nothing returns nothing
set UnitProperties[.unit:Id] = 0
set .unit = null
endmethod
endstruct
// ******************************************************************************
// These functions allow you to retrieve the handle index of a unit. Other
// methods of indexing units, like UnitUserData and HAIL, would also work. This
// method is only viable if you correctly dispose of used handles, so if your
// map suffers from inflated handle indices you should either change
// the "UnitProperties" array above to "UnitProperties[verybignumber]", or use a
// different system. A UnitUserData system is recommended as a replacement
// regardless.
//
// The Id wrapper allows you to retrieve a unit's Id as with:
//
// unit:Id
//
function GetUnitId takes unit u returns integer
return GetHandleId(u)-0x100000
endfunction
struct Id
static method operator [] takes unit u returns integer
return GetUnitId(u)
endmethod
endstruct
// ******************************************************************************
// Create or manipulate a set of properties for a given unit.
//
function CreateUnitProperties takes unit u returns nothing
local integer id = u:Id
if UnitProperties[id] == 0 then
set UnitProperties[id] = properties.create(u)
endif
endfunction
function GetUnitProperties takes unit u returns properties
return UnitProperties[u:Id]
endfunction
function DestroyUnitProperties takes unit u returns nothing
local integer id = u:Id
if UnitProperties[id] != 0 then
call properties.destroy(UnitProperties[id])
endif
endfunction
//*******************************************************************************
// These are the custom functions that allow you to manipulate specific
// properties. For each property, the macros generate a UnitSet, UnitModify and
// UnitGet function.
//
// UnitSetAgi(u, 10): This would set a unit's bonus Agility to 10.
//
// UnitModifyAgi(u, 10): This would add 10 to a unit's bonus Agility. A
// negative number could be used to subtract.
//
// UnitGetAgi(u): This would return a unit's current Agi bonus.
//
// The OperatorWrappers library offers a different interface for those who are
// comfortable with vJASS. More details are available at the top of the library.
//
function UnitModifyMoveSpeed takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.MoveSpeed = p.MoveSpeed + R2I(amount)
call SetUnitMoveSpeed(u, p.MoveSpeed)
endfunction
function UnitSetMoveSpeed takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.MoveSpeed = R2I(amount)
call SetUnitMoveSpeed(u, p.MoveSpeed)
endfunction
//There's already a native for this, but for the sake of completeness:
function UnitGetMoveSpeed takes unit u returns real
return UnitProperties[GetUnitId(u)].MoveSpeed
endfunction
// ******************************************************************************
// Periodic properties, like static mana regeneration.
//
//! textmacro UnitSetPeriodic takes member
globals
private group $member$Group = CreateGroup()
endglobals
function UnitModify$member$ takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.$member$ = p.$member$ + amount
if p.$member$ != 0 then
call GroupAddUnit($member$Group, u)
else
call GroupRemoveUnit($member$Group, u)
endif
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.$member$ = amount
if amount != 0 then
call GroupAddUnit($member$Group, u)
else
call GroupRemoveUnit($member$Group, u)
endif
endfunction
function UnitGet$member$ takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endfunction
//! endtextmacro
//! runtextmacro UnitSetPeriodic("ManaRegen")
// ******************************************************************************
// Triggered properties like Spell Crits and Spell Evasion use these macros.
//
//! textmacro UnitSetProperty takes member
function UnitModify$member$ takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.$member$ = p.$member$ + R2I(amount)
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
set UnitProperties[GetUnitId(u)].$member$ = R2I(amount)
endfunction
function UnitGet$member$ takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endfunction
//! endtextmacro
//! runtextmacro UnitSetProperty("AttackEvasion")
//! runtextmacro UnitSetProperty("AttackMiss")
////! runtextmacro UnitSetProperty("AttackCritical")
////! runtextmacro UnitSetProperty("SpellCritical")
////! runtextmacro UnitSetProperty("SpellEvasion")
////! runtextmacro UnitSetProperty("SpellMiss")
////! runtextmacro UnitSetProperty("SpellResistance")
////! runtextmacro UnitSetProperty("SpellReflection")
//! textmacro UnitSetPercentageProperty takes member
function UnitModify$member$ takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.$member$ = p.$member$ + R2I(amount)
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
set UnitProperties[GetUnitId(u)].$member$ = R2I(amount)
endfunction
function UnitGet$member$ takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endfunction
//! endtextmacro
////! runtextmacro UnitSetPercentageProperty("SpellPower")
////! runtextmacro UnitSetPercentageProperty("ExpRate")
////! runtextmacro UnitSetPercentageProperty("BountyRate")
////! runtextmacro UnitSetPercentageProperty("BloodRate")
// ******************************************************************************
// Properties that are modified by using a binary count system. This includes
// stats, armor, damage etc.
//
//! textmacro UnitSetBinary takes member, MEMBER
function UnitModify$member$ takes unit u, real amount returns nothing
local properties p = GetUnitProperties(u)
local real r = p.$member$
local integer i = 1
local integer e
local integer a
set p.$member$ = p.$member$ + R2I(amount)
set amount = R2I(p.$member$)
loop
exitwhen i > 1
if amount >= 0 and r >= 0. then
set e = $MEMBER$
set r = amount
elseif amount <= 0 and r <= 0. then
set e = $MEMBER$ + OFFSET
set r = -amount
elseif amount <= 0 and r >= 0. then
set e = $MEMBER$
set r = 0.
set i = 0
elseif amount >= 0 and r <= 0. then
set e = $MEMBER$ + OFFSET
set r = 0.
set i = 0
endif
set a = MAX
loop
exitwhen a < 0
if r >= Pow2[a] then
call UnitAddAbility(u, e + a)
set r = r - Pow2[a]
else
call UnitRemoveAbility(u, e + a)
endif
set a = a - 1
endloop
set i = i + 1
endloop
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
local properties p = GetUnitProperties(u)
local real r = p.$member$
local integer i = 1
local integer e
local integer a
set p.$member$ = R2I(amount)
set amount = R2I(p.$member$)
loop
exitwhen i > 1
if amount >= 0 and r >= 0. then
set e = $MEMBER$
set r = amount
elseif amount <= 0 and r <= 0. then
set e = $MEMBER$ + OFFSET
set r = -amount
elseif amount <= 0 and r >= 0. then
set e = $MEMBER$
set r = 0.
set i = 0
elseif amount >= 0 and r <= 0. then
set e = $MEMBER$ + OFFSET
set r = 0.
set i = 0
endif
set a = MAX
loop
exitwhen a < 0
if r >= Pow2[a] then
call UnitAddAbility(u, e + a)
set r = r - Pow2[a]
else
call UnitRemoveAbility(u, e + a)
endif
set a = a - 1
endloop
set i = i + 1
endloop
endfunction
function UnitGet$member$ takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endfunction
struct $member$ extends array
static method operator [] takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endmethod
static method operator []= takes unit u, real r returns nothing
call UnitSet$member$(u, r)
endmethod
endstruct
//! endtextmacro
//! runtextmacro UnitSetBinary("Agi", "AGI")
//! runtextmacro UnitSetBinary("Int", "INT")
//! runtextmacro UnitSetBinary("Str", "STR")
//! runtextmacro UnitSetBinary("Damage", "DAMAGE")
//! runtextmacro UnitSetBinary("Armor", "ARMOR")
//! runtextmacro UnitSetBinary("AttackSpeed", "ATTACK_SPEED")
//! runtextmacro UnitSetBinary("LifeRegen", "LIFE_REGEN")
//! runtextmacro UnitSetBinary("ManaRegenPercent", "MANA_REGEN_PERCENT")
// ******************************************************************************
// Modifying max life and mana are special cases, although they could also be
// done using binary counting.
//
//! textmacro UnitSetMaxState takes member, state
function UnitModify$member$ takes unit u, real amount returns nothing
local integer comp = 100
local integer i = 4
local integer abil = $state$
if amount < 0 then
set abil = abil + 2
set amount = -amount
endif
set amount = R2I(amount)
loop
exitwhen i < 2
if amount >= comp then
loop
exitwhen amount < comp
call UnitAddAbility (u, abil)
call SetUnitAbilityLevel(u, abil, i)
call UnitRemoveAbility (u, abil)
set amount = amount - comp
endloop
endif
set comp = comp / 10
set i = i - 1
endloop
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
local integer comp = 100
local integer i = 4
local integer abil = $state$
local real r = GetUnitState(u, UNIT_STATE_$state$)
if amount > r then
set amount = R2I(amount - r)
elseif amount < r then
set amount = R2I(RAbsBJ(r - amount))
set abil = abil + 2
endif
loop
exitwhen i < 2
if amount >= comp then
loop
exitwhen amount < comp
call UnitAddAbility (u, abil)
call SetUnitAbilityLevel(u, abil, i)
call UnitRemoveAbility (u, abil)
set amount = amount - comp
endloop
endif
set comp = comp / 10
set i = i - 1
endloop
endfunction
function UnitGet$member$ takes unit u returns real
return GetUnitState(u, UNIT_STATE_$state$)
endfunction
//! endtextmacro
//! runtextmacro UnitSetMaxState("MaxLife", "MAX_LIFE")
//! runtextmacro UnitSetMaxState("MaxMana", "MAX_MANA")
// ******************************************************************************
// Refreshes properties for a unit in case a morph ability or something else
// causes them to vanish.
//
function RefreshUnitProperties takes unit u returns nothing
call UnitModifyArmor (u, 0)
call UnitModifyDamage (u, 0)
call UnitModifyAttackSpeed (u, 0)
call UnitModifyMoveSpeed (u, 0)
call UnitModifyAgi (u, 0)
call UnitModifyInt (u, 0)
call UnitModifyStr (u, 0)
call UnitModifyLifeRegen (u, 0)
call UnitModifyManaRegen (u, 0)
call UnitModifyManaRegenPercent(u, 0)
endfunction
// ******************************************************************************
// Additional periodic properties would be added to the Periodic function and
// would require their own ForGroup.
//
private function UnitAdjustMana takes nothing returns nothing
local unit u = GetEnumUnit()
set u:Mana = u:Mana + u:ManaRegen
set u = null
endfunction
private function Periodic takes nothing returns nothing
call ForGroup(ManaRegenGroup, function UnitAdjustMana)
endfunction
// ******************************************************************************
// Initialization stores powers of 2 up to 30, starts the periodic timer and
// preloads all property abilities.
//
globals
private unit u
endglobals
private function PreloadBinary takes integer i returns nothing
local integer done = i + OFFSET + MAX
local integer exit
loop
set exit = i + MAX
loop
exitwhen i == exit
call UnitAddAbility(u, i)
set i = i + 1
endloop
exitwhen i == done
set i = (i - MAX) + OFFSET
endloop
endfunction
private function Init takes nothing returns nothing
local integer i = 1
set Pow2[0] = 1
call TimerStart(Timer, 1, true, function Periodic)
loop
exitwhen i > 30 // Can't go above 2^30, integer will overflow
set Pow2 = Pow2[i - 1] * 2
set i = i + 1
endloop
set u = CreateUnit(Player(14), 'hfoo', 0, 0, 0)
call PreloadBinary (AGI)
call PreloadBinary (INT)
call PreloadBinary (STR)
call PreloadBinary (ATTACK_SPEED)
call PreloadBinary (DAMAGE)
call PreloadBinary (LIFE_REGEN)
call PreloadBinary (ARMOR)
call PreloadBinary (MANA_REGEN_PERCENT)
call UnitAddAbility(u, MAX_LIFE)
call UnitAddAbility(u, MAX_LIFE+2)
call UnitAddAbility(u, MAX_MANA)
call UnitAddAbility(u, MAX_MANA+2)
call UnitAddAbility(u, 'Evas')
call KillUnit (u)
call RemoveUnit (u)
set u = null
endfunction
//*******************************************************************************
// These wrappers use operator overloading to allow a more intuitive syntax than
// UnitSet/Modify/GetProperty():
//
// set Unit
roperty = x
// or
// set Property[Unit] = x
//
// To modify a property rather than set it to a specific value:
//
// set Unit
roperty = Unit
roperty + x
// or
// set Property[Unit] = Property[Unit] + x
//
//! textmacro UnitPropertyWrapper takes name
struct $name$ extends array
static method operator [] takes unit u returns real
return UnitGet$name$(u)
endmethod
static method operator []= takes unit u, real r returns nothing
call UnitSet$name$(u, r)
endmethod
endstruct
//! endtextmacro
//! runtextmacro UnitPropertyWrapper("ManaRegen")
//! runtextmacro UnitPropertyWrapper("MoveSpeed")
//! runtextmacro UnitPropertyWrapper("AttackEvasion")
//! runtextmacro UnitPropertyWrapper("AttackMiss")
//! textmacro UnitStateWrapper takes name, type
struct $name$ extends array
static method operator [] takes unit u returns real
return GetUnitState(u, UNIT_STATE_$type$)
endmethod
static method operator []= takes unit u, real r returns nothing
call SetUnitState(u, UNIT_STATE_$type$, r)
endmethod
endstruct
struct Max$name$ extends array
static method operator [] takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_$type$)
endmethod
static method operator []= takes unit u, real r returns nothing
call UnitSetMax$name$(u, r)
endmethod
endstruct
//! endtextmacro
//! runtextmacro UnitStateWrapper("Life", "LIFE")
//! runtextmacro UnitStateWrapper("Mana", "MANA")
endlibrary[/code]
//* Unit Properties *
//* v.D *
//* *
//* By: Cassiel *
//*******************************************************************************
//*******************************************************************************
// The ObjectMerger command will automatically generate all the necessary
// abilities for you using the external Lua script, which should be placed in the
// same directory as the map you're installing to. It only needs to be run once.
//
////! external ObjectMerger UnitPropertiesGenerator.lua
library UnitProperties initializer Init
globals
//*******************************************************************************
// Max: Specifies the highest power of 2 the property ability sets use. A Max
// of 8 means the system supports values of +/- 511. To change values
// simply reset the Max variable in the Lua script.
//
// Offset: Specifies the difference between the + and - abilities for any
// property.
//
private constant integer MAX = 8
private constant integer OFFSET = 512
//*******************************************************************************
// These globals reference the starting abilities for properties that use binary
// counting. Changing them means changing the rawcodes of all the abilities the
// system uses for that property. This list includes all properties that stack
// linearly and are binary countable.
//
private constant integer AGI = 'AG+a'
private constant integer INT = 'IN+a'
private constant integer STR = 'ST+a'
private constant integer ATTACK_SPEED = 'AT+a'
private constant integer ARMOR = 'AR+a'
private constant integer DAMAGE = 'DA+a'
private constant integer LIFE_REGEN = 'LI+a'
private constant integer MANA_REGEN_PERCENT = 'MR+a'
//*******************************************************************************
// These globals reference the starting abilities for properties that use the
// life/mana bug.
//
private constant integer MAX_LIFE = 'Lif+'
private constant integer MAX_MANA = 'Man+'
// ******************************************************************************
// Timer: Used for any periodic properties that you want/need to
// trigger. Static mana gain (as opposed to percentage mana gain)
// is included as an example.
//
// pow2: Powers of 2 up to 30 are calculated and stored in this array at
// Init.
//
// UnitProperties: This array stores the properties struct for each unit that has
// it. It and the properties struct are public so that advanced
// users have the option of manipulating properties directly
// instead of relying on the UnitGet/Set functions.
//
private timer Timer = CreateTimer()
private integer array Pow2
properties array UnitProperties
endglobals
struct properties
// ******************************************************************************
// These are the standard properties you would want to manipulate on a given unit
//
real Agi = 0
real Int = 0
real Str = 0
real LifeRegen = 0
real ManaRegen = 0
real ManaRegenPercent = 0
real Armor = 0
real Damage = 0
real AttackSpeed = 0
real MoveSpeed
// ******************************************************************************
// These properties are used by the Evasion module, which gives fully dynamic
// control over a unit's chance to evade incoming or miss on outgoing attacks.
//
unit unit
real AttackEvasion = 0
real AttackMiss = 0
// ******************************************************************************
// These are examples of additional properties which would require other systems
// to take advantage of.
//
//real AttackCritical = 0
//real SpellEvasion = 0
//real SpellMiss = 0
//real SpellCritical = 0
// ******************************************************************************
// JASS doesn't handle decimal values very well. 1. + .05 = 1.049999952, and
// 1. + .05 - .05 != 1. Thus it is necessary to store the values without decimals
// and then convert them when they they are retrieved.
//
//real ExpRate = 100
//real BountyRate = 100
static method create takes unit u returns properties
local properties this = properties.allocate()
set .unit = u
// You may want to use GetUnitDefaultMoveSpeed here, but I have seen
// some bugs with it.
set .MoveSpeed = GetUnitMoveSpeed(u)
return this
endmethod
method onDestroy takes nothing returns nothing
set UnitProperties[.unit:Id] = 0
set .unit = null
endmethod
endstruct
// ******************************************************************************
// These functions allow you to retrieve the handle index of a unit. Other
// methods of indexing units, like UnitUserData and HAIL, would also work. This
// method is only viable if you correctly dispose of used handles, so if your
// map suffers from inflated handle indices you should either change
// the "UnitProperties" array above to "UnitProperties[verybignumber]", or use a
// different system. A UnitUserData system is recommended as a replacement
// regardless.
//
// The Id wrapper allows you to retrieve a unit's Id as with:
//
// unit:Id
//
function GetUnitId takes unit u returns integer
return GetHandleId(u)-0x100000
endfunction
struct Id
static method operator [] takes unit u returns integer
return GetUnitId(u)
endmethod
endstruct
// ******************************************************************************
// Create or manipulate a set of properties for a given unit.
//
function CreateUnitProperties takes unit u returns nothing
local integer id = u:Id
if UnitProperties[id] == 0 then
set UnitProperties[id] = properties.create(u)
endif
endfunction
function GetUnitProperties takes unit u returns properties
return UnitProperties[u:Id]
endfunction
function DestroyUnitProperties takes unit u returns nothing
local integer id = u:Id
if UnitProperties[id] != 0 then
call properties.destroy(UnitProperties[id])
endif
endfunction
//*******************************************************************************
// These are the custom functions that allow you to manipulate specific
// properties. For each property, the macros generate a UnitSet, UnitModify and
// UnitGet function.
//
// UnitSetAgi(u, 10): This would set a unit's bonus Agility to 10.
//
// UnitModifyAgi(u, 10): This would add 10 to a unit's bonus Agility. A
// negative number could be used to subtract.
//
// UnitGetAgi(u): This would return a unit's current Agi bonus.
//
// The OperatorWrappers library offers a different interface for those who are
// comfortable with vJASS. More details are available at the top of the library.
//
function UnitModifyMoveSpeed takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.MoveSpeed = p.MoveSpeed + R2I(amount)
call SetUnitMoveSpeed(u, p.MoveSpeed)
endfunction
function UnitSetMoveSpeed takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.MoveSpeed = R2I(amount)
call SetUnitMoveSpeed(u, p.MoveSpeed)
endfunction
//There's already a native for this, but for the sake of completeness:
function UnitGetMoveSpeed takes unit u returns real
return UnitProperties[GetUnitId(u)].MoveSpeed
endfunction
// ******************************************************************************
// Periodic properties, like static mana regeneration.
//
//! textmacro UnitSetPeriodic takes member
globals
private group $member$Group = CreateGroup()
endglobals
function UnitModify$member$ takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.$member$ = p.$member$ + amount
if p.$member$ != 0 then
call GroupAddUnit($member$Group, u)
else
call GroupRemoveUnit($member$Group, u)
endif
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.$member$ = amount
if amount != 0 then
call GroupAddUnit($member$Group, u)
else
call GroupRemoveUnit($member$Group, u)
endif
endfunction
function UnitGet$member$ takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endfunction
//! endtextmacro
//! runtextmacro UnitSetPeriodic("ManaRegen")
// ******************************************************************************
// Triggered properties like Spell Crits and Spell Evasion use these macros.
//
//! textmacro UnitSetProperty takes member
function UnitModify$member$ takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.$member$ = p.$member$ + R2I(amount)
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
set UnitProperties[GetUnitId(u)].$member$ = R2I(amount)
endfunction
function UnitGet$member$ takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endfunction
//! endtextmacro
//! runtextmacro UnitSetProperty("AttackEvasion")
//! runtextmacro UnitSetProperty("AttackMiss")
////! runtextmacro UnitSetProperty("AttackCritical")
////! runtextmacro UnitSetProperty("SpellCritical")
////! runtextmacro UnitSetProperty("SpellEvasion")
////! runtextmacro UnitSetProperty("SpellMiss")
////! runtextmacro UnitSetProperty("SpellResistance")
////! runtextmacro UnitSetProperty("SpellReflection")
//! textmacro UnitSetPercentageProperty takes member
function UnitModify$member$ takes unit u, real amount returns nothing
local properties p = UnitProperties[GetUnitId(u)]
set p.$member$ = p.$member$ + R2I(amount)
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
set UnitProperties[GetUnitId(u)].$member$ = R2I(amount)
endfunction
function UnitGet$member$ takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endfunction
//! endtextmacro
////! runtextmacro UnitSetPercentageProperty("SpellPower")
////! runtextmacro UnitSetPercentageProperty("ExpRate")
////! runtextmacro UnitSetPercentageProperty("BountyRate")
////! runtextmacro UnitSetPercentageProperty("BloodRate")
// ******************************************************************************
// Properties that are modified by using a binary count system. This includes
// stats, armor, damage etc.
//
//! textmacro UnitSetBinary takes member, MEMBER
function UnitModify$member$ takes unit u, real amount returns nothing
local properties p = GetUnitProperties(u)
local real r = p.$member$
local integer i = 1
local integer e
local integer a
set p.$member$ = p.$member$ + R2I(amount)
set amount = R2I(p.$member$)
loop
exitwhen i > 1
if amount >= 0 and r >= 0. then
set e = $MEMBER$
set r = amount
elseif amount <= 0 and r <= 0. then
set e = $MEMBER$ + OFFSET
set r = -amount
elseif amount <= 0 and r >= 0. then
set e = $MEMBER$
set r = 0.
set i = 0
elseif amount >= 0 and r <= 0. then
set e = $MEMBER$ + OFFSET
set r = 0.
set i = 0
endif
set a = MAX
loop
exitwhen a < 0
if r >= Pow2[a] then
call UnitAddAbility(u, e + a)
set r = r - Pow2[a]
else
call UnitRemoveAbility(u, e + a)
endif
set a = a - 1
endloop
set i = i + 1
endloop
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
local properties p = GetUnitProperties(u)
local real r = p.$member$
local integer i = 1
local integer e
local integer a
set p.$member$ = R2I(amount)
set amount = R2I(p.$member$)
loop
exitwhen i > 1
if amount >= 0 and r >= 0. then
set e = $MEMBER$
set r = amount
elseif amount <= 0 and r <= 0. then
set e = $MEMBER$ + OFFSET
set r = -amount
elseif amount <= 0 and r >= 0. then
set e = $MEMBER$
set r = 0.
set i = 0
elseif amount >= 0 and r <= 0. then
set e = $MEMBER$ + OFFSET
set r = 0.
set i = 0
endif
set a = MAX
loop
exitwhen a < 0
if r >= Pow2[a] then
call UnitAddAbility(u, e + a)
set r = r - Pow2[a]
else
call UnitRemoveAbility(u, e + a)
endif
set a = a - 1
endloop
set i = i + 1
endloop
endfunction
function UnitGet$member$ takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endfunction
struct $member$ extends array
static method operator [] takes unit u returns real
return UnitProperties[GetUnitId(u)].$member$
endmethod
static method operator []= takes unit u, real r returns nothing
call UnitSet$member$(u, r)
endmethod
endstruct
//! endtextmacro
//! runtextmacro UnitSetBinary("Agi", "AGI")
//! runtextmacro UnitSetBinary("Int", "INT")
//! runtextmacro UnitSetBinary("Str", "STR")
//! runtextmacro UnitSetBinary("Damage", "DAMAGE")
//! runtextmacro UnitSetBinary("Armor", "ARMOR")
//! runtextmacro UnitSetBinary("AttackSpeed", "ATTACK_SPEED")
//! runtextmacro UnitSetBinary("LifeRegen", "LIFE_REGEN")
//! runtextmacro UnitSetBinary("ManaRegenPercent", "MANA_REGEN_PERCENT")
// ******************************************************************************
// Modifying max life and mana are special cases, although they could also be
// done using binary counting.
//
//! textmacro UnitSetMaxState takes member, state
function UnitModify$member$ takes unit u, real amount returns nothing
local integer comp = 100
local integer i = 4
local integer abil = $state$
if amount < 0 then
set abil = abil + 2
set amount = -amount
endif
set amount = R2I(amount)
loop
exitwhen i < 2
if amount >= comp then
loop
exitwhen amount < comp
call UnitAddAbility (u, abil)
call SetUnitAbilityLevel(u, abil, i)
call UnitRemoveAbility (u, abil)
set amount = amount - comp
endloop
endif
set comp = comp / 10
set i = i - 1
endloop
endfunction
function UnitSet$member$ takes unit u, real amount returns nothing
local integer comp = 100
local integer i = 4
local integer abil = $state$
local real r = GetUnitState(u, UNIT_STATE_$state$)
if amount > r then
set amount = R2I(amount - r)
elseif amount < r then
set amount = R2I(RAbsBJ(r - amount))
set abil = abil + 2
endif
loop
exitwhen i < 2
if amount >= comp then
loop
exitwhen amount < comp
call UnitAddAbility (u, abil)
call SetUnitAbilityLevel(u, abil, i)
call UnitRemoveAbility (u, abil)
set amount = amount - comp
endloop
endif
set comp = comp / 10
set i = i - 1
endloop
endfunction
function UnitGet$member$ takes unit u returns real
return GetUnitState(u, UNIT_STATE_$state$)
endfunction
//! endtextmacro
//! runtextmacro UnitSetMaxState("MaxLife", "MAX_LIFE")
//! runtextmacro UnitSetMaxState("MaxMana", "MAX_MANA")
// ******************************************************************************
// Refreshes properties for a unit in case a morph ability or something else
// causes them to vanish.
//
function RefreshUnitProperties takes unit u returns nothing
call UnitModifyArmor (u, 0)
call UnitModifyDamage (u, 0)
call UnitModifyAttackSpeed (u, 0)
call UnitModifyMoveSpeed (u, 0)
call UnitModifyAgi (u, 0)
call UnitModifyInt (u, 0)
call UnitModifyStr (u, 0)
call UnitModifyLifeRegen (u, 0)
call UnitModifyManaRegen (u, 0)
call UnitModifyManaRegenPercent(u, 0)
endfunction
// ******************************************************************************
// Additional periodic properties would be added to the Periodic function and
// would require their own ForGroup.
//
private function UnitAdjustMana takes nothing returns nothing
local unit u = GetEnumUnit()
set u:Mana = u:Mana + u:ManaRegen
set u = null
endfunction
private function Periodic takes nothing returns nothing
call ForGroup(ManaRegenGroup, function UnitAdjustMana)
endfunction
// ******************************************************************************
// Initialization stores powers of 2 up to 30, starts the periodic timer and
// preloads all property abilities.
//
globals
private unit u
endglobals
private function PreloadBinary takes integer i returns nothing
local integer done = i + OFFSET + MAX
local integer exit
loop
set exit = i + MAX
loop
exitwhen i == exit
call UnitAddAbility(u, i)
set i = i + 1
endloop
exitwhen i == done
set i = (i - MAX) + OFFSET
endloop
endfunction
private function Init takes nothing returns nothing
local integer i = 1
set Pow2[0] = 1
call TimerStart(Timer, 1, true, function Periodic)
loop
exitwhen i > 30 // Can't go above 2^30, integer will overflow
set Pow2 = Pow2[i - 1] * 2
set i = i + 1
endloop
set u = CreateUnit(Player(14), 'hfoo', 0, 0, 0)
call PreloadBinary (AGI)
call PreloadBinary (INT)
call PreloadBinary (STR)
call PreloadBinary (ATTACK_SPEED)
call PreloadBinary (DAMAGE)
call PreloadBinary (LIFE_REGEN)
call PreloadBinary (ARMOR)
call PreloadBinary (MANA_REGEN_PERCENT)
call UnitAddAbility(u, MAX_LIFE)
call UnitAddAbility(u, MAX_LIFE+2)
call UnitAddAbility(u, MAX_MANA)
call UnitAddAbility(u, MAX_MANA+2)
call UnitAddAbility(u, 'Evas')
call KillUnit (u)
call RemoveUnit (u)
set u = null
endfunction
//*******************************************************************************
// These wrappers use operator overloading to allow a more intuitive syntax than
// UnitSet/Modify/GetProperty():
//
// set Unit
// or
// set Property[Unit] = x
//
// To modify a property rather than set it to a specific value:
//
// set Unit
// or
// set Property[Unit] = Property[Unit] + x
//
//! textmacro UnitPropertyWrapper takes name
struct $name$ extends array
static method operator [] takes unit u returns real
return UnitGet$name$(u)
endmethod
static method operator []= takes unit u, real r returns nothing
call UnitSet$name$(u, r)
endmethod
endstruct
//! endtextmacro
//! runtextmacro UnitPropertyWrapper("ManaRegen")
//! runtextmacro UnitPropertyWrapper("MoveSpeed")
//! runtextmacro UnitPropertyWrapper("AttackEvasion")
//! runtextmacro UnitPropertyWrapper("AttackMiss")
//! textmacro UnitStateWrapper takes name, type
struct $name$ extends array
static method operator [] takes unit u returns real
return GetUnitState(u, UNIT_STATE_$type$)
endmethod
static method operator []= takes unit u, real r returns nothing
call SetUnitState(u, UNIT_STATE_$type$, r)
endmethod
endstruct
struct Max$name$ extends array
static method operator [] takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_$type$)
endmethod
static method operator []= takes unit u, real r returns nothing
call UnitSetMax$name$(u, r)
endmethod
endstruct
//! endtextmacro
//! runtextmacro UnitStateWrapper("Life", "LIFE")
//! runtextmacro UnitStateWrapper("Mana", "MANA")
endlibrary[/code]
Installation
Download UnitPropertiesGenerator.txt and rename it to UnitPropertiesGenerator.lua. Place UnitPropertiesGenerator.lua in the same directory as your map. Then paste the UnitProperties library (and the Evasion library, if you want it) into your map and uncomment the line
////! external ObjectMerger UnitPropertiesGenerator.lua
. Save the map, then close it, then open it again. Everything Unit Properties needs to run will have been generated automatically, and you can now comment //! external ObjectMerger UnitPropertiesGenerator.lua
back out.Credits
Thanks to PitzerMike and Vexorian for their tools, Alexander244 for the original Lua script, and Anitarf for syntax and implementation suggestions.
Attachments
Last edited by a moderator: