StatHandler
This package can be used to manage custom stats, assign them to items/buffs/etc. and apply them to units. The way how multiple stat values accumulate can be selected for each stat individually, it is also possible to link stats to give them an absolute and a percental component. By using caches and the appropriate data structures this generic system offers a similar or even higher performance than the usual hardcoded approaches.
This replaces http://www.hiveworkshop.com/forums/lab-715/wurst-stathandler-design-issues-247795/
GitHub: https://github.com/muzzel/WurstDemo/tree/master/wurst/lib/StatHandler
Requires: Rational
Optional: A unit indexing system.
Code:
Config (example):
Defining Stats:
To define a stat you first have to add a new constant to the enum "Stat" in StatHandlerConfig. Then you have to register the stat by calling one of the following functions. These functions determine how multiple stat values are accumulated.
1. Linear - registerStatLin(Stat.ARMOR):
Simply adds the values. This should be used for most absolute values like Damage, Armor, Strength, ...
Example: (20 Armor) and (20 Armor) results in (40 Armor).
2. Exponential - registerStatExp(Stat.SPELLPOWER):
Treats values as percental and multiplies them. This is usually used for percental stats which increase an absolute value.
Example: (50% Spellpower) and (50% Spellpower) result in (125% Spellpower).
Consider your spell deals 100 Damage. If you add a 50% Spellpower increase it does 150 Damage. If you increase those 150 Damage again by 50% you end up at 225 Damage, which is equal to 100 Damage with 125% Bonus damage.
3. Logistic - registerStatLog(Stat.EVASION):
Treats values as percental and multiplies their inverse. This is usually used for chances that rolled after each other.
Example: (10% Evasion) and (10% Evasion) results in (19% Evasion).
Consider you have 10% evasion and you get attacked 100 times. You will evade 10 times and get hit 90 times. Consider you get another chance of 10% to evade if your first try failed. Then you will evade another 9 times (10% of 90) and get hit 81 times. So overall you get hit 81 times and evade 19 times, which is equal to 19% evasion.
A sideeffect of this is that this automatically caps at 100% as you can only get very close to 100%, but never reach it.
In addition to these three types you can use combined types which will add an additional substat to the registered stat. The substat will be considered percental and increase the stat in the respective way. The substat will automatically be taken into account when retrieving the stat!
4. Linear stat, linear substat - registerStatLinLin(Stat.DAMAGE, Stat.DAMAGE_PERCENT)
Both stat and substat accumulate separately in a linear manner, when retrieving the stat the substat is interpreted as percental and multiplied to the stat.
Example: (35 Damage), (65 Damage), (40% Damage), (40% Damage) results in (100 Damage), (80% Damage) which results in (180 Damage).
5. Linear stat, exponential substat - registerStatLinExp(Stat.DAMAGE, Stat.DAMAGE_PERCENT)
Stat values accumulate in a linear manner while substats accumulate exponentially. When retrieving the stat the substat is multiplied to the stat.
Example: (35 Damage), (65 Damage), (40% Damage), (40% Damage) results in (100 Damage), (96% Damage) which results in (196 Damage).
Usage:
1. UnitBaseStatBuffer
To define the base stats for a unittype create a new object of UnitBaseStatBuffer and pass the desired base stats to it. Stats which get no base value assigned will use a default one (zero).
2. UnitStatBuffer
Creating a UnitStatBuffer for a unit allows it to apply stats to it.
The UnitStatBuffer will automatically look for UnitBaseStatBuffers assigned to the unittype of u. It is also possible to pass a UnitBaseStatBuffer directly as second argument (useful if different unittypes are sharing a UnitBaseStatBuffer).
3. StatList
A StatList is a dynamic list which can hold an arbitrary amoint of stats. When a StatList is applied to a UnitStatBuffer all its stats will be added to the UnitStatBuffers unit. StatList are perfect for storing stats of items or buffs.
4. Retrieving stats
To get a units current stats call its UnitStatBuffers getStat function. Getting a stat which has a substat will return the combined value of stat and substat!
Optional UnitIndexer:
If a UnitIndexing system is available the "getUnitIndex(...)" function in StatHandlerConfig can be overwritten to return the correct values. Alternatively the function can be removed and a package which provides the same function can be imported ("import public") to StatHandlerConfig.
Providing a UnitIndexer will enable additional functions, which are basically more comfortable wrappers for the normal functions.
Todo list:
- Left to decide: Make unitindexer a requirement, remove most of the normal functions and only provide those mentioned in "Optional UnitIndexer". -> Smaller API?
- provide a way to reset a units stats?
Changelog:
1.1 Lots of fixes, did intensive testing
1.0 (stable) UnitStatBuffer constructor allows "null" UnitBaseStatBuffer argument and uses a default (empty) one, Added StatChangedEvents,
0.9 Initial unstable release
This package can be used to manage custom stats, assign them to items/buffs/etc. and apply them to units. The way how multiple stat values accumulate can be selected for each stat individually, it is also possible to link stats to give them an absolute and a percental component. By using caches and the appropriate data structures this generic system offers a similar or even higher performance than the usual hardcoded approaches.
This replaces http://www.hiveworkshop.com/forums/lab-715/wurst-stathandler-design-issues-247795/
GitHub: https://github.com/muzzel/WurstDemo/tree/master/wurst/lib/StatHandler
Requires: Rational
Optional: A unit indexing system.
Code:
Wurst:
package StatHandler
import public StatHandlerConfig
import Rational
import HashMap
import LinkedList
// By muzzel - Version 1.1
/** Creates a new RawStatBuffer initialized with default base stats for the specified unitType. */
public function createBaseStatBuffer(int unitTypeId) returns UnitBaseStatBuffer
return new UnitBaseStatBuffer(unitTypeId)
/** Assigns a new UnitStatBuffer to the specified unit. */
public function createUnitStatBuffer(unit u)
new UnitStatBuffer(u)
/** Destroys this units UnitStatBuffer, if any assigned. */
public function destroyUnitStatBuffer(unit u)
let buffer = mapUnitStatBuffers[getUnitIndex(u)]
if buffer castTo int != 0
destroy buffer
/**
* Returns the specified units UnitStatBuffer.
* Returns null if no UnitStatBuffer assigned.
* Requires getUnitIndex(). Returns null if not available.
*/
public function getUnitStatBuffer(unit u) returns UnitStatBuffer
return mapUnitStatBuffers[getUnitIndex(u)]
/**
* Returns a units stat.
* Warning if no UnitStatBuffer assigned.
* Requires getUnitIndex(). Warning if not available.
*/
public function unit.getStat(Stat s) returns int
let buffer = mapUnitStatBuffers[getUnitIndex(this)]
if buffer castTo int == 0
printWarning("StatHandlerConfig: A called function requires a valid implementation of getUnitIndex(unit u).")
return buffer.get(s)
/**
* Returns a units base stat.
* Warning if no UnitStatBuffer assigned.
* Requires getUnitIndex(). Warning if not available.
*/
public function unit.getBaseStat(Stat s) returns int
let buffer = mapUnitStatBuffers[getUnitIndex(this)]
if buffer castTo int == 0
printWarning("StatHandlerConfig: A called function requires a valid implementation of getUnitIndex(unit u).")
return buffer.getBaseStat(s)
/**
* Applies the specified StatList to the unit.
* Warning if no UnitStatBuffer assigned.
* Requires getUnitIndex()! Warning if not available.
*/
public function unit.applyStatList(StatList statList)
let buffer = mapUnitStatBuffers[getUnitIndex(this)]
if buffer castTo int == 0
printWarning("StatHandlerConfig: A called function requires a valid implementation of getUnitIndex(unit u).")
buffer.apply(statList)
/**
* Removes the specified StatList from the unit.
* Warning if no UnitStatBuffer assigned.
* Requires getUnitIndex()! Warning if not available.
*/
public function unit.removeStatList(StatList statList)
let buffer = mapUnitStatBuffers[getUnitIndex(this)]
if buffer castTo int == 0
printWarning("StatHandlerConfig: A called function requires a valid implementation of getUnitIndex(unit u).")
buffer.remove(statList)
/** Registers the specified stat to grow linearly. */
public function registerStatLin(Stat stat)
statType[stat castTo int] = StatType.LINEAR
statSubstat[stat castTo int] = -1
parentStat[stat castTo int] = -1
numStats++
/** Registers the specified stat to grow linearly and adds a StatChangedEvent. */
public function registerStatLin(Stat stat, StatChangedEvent statChangedEvent)
statChangedEvents[stat castTo int] = statChangedEvent
registerStatLin(stat)
/** Registers the specified stat to grow exponentially. */
public function registerStatExp(Stat stat)
statType[stat castTo int] = StatType.EXPONENTIAL
statSubstat[stat castTo int] = -1
parentStat[stat castTo int] = -1
numStats++
/** Registers the specified stat to grow exponentially and adds a StatChangedEvent. */
public function registerStatExp(Stat stat, StatChangedEvent statChangedEvent)
statChangedEvents[stat castTo int] = statChangedEvent
registerStatExp(stat)
/** Registers the specified stat to grow logistically. */
public function registerStatLog(Stat stat)
statType[stat castTo int] = StatType.LOGISTIC
statSubstat[stat castTo int] = -1
parentStat[stat castTo int] = -1
numStats++
/** Registers the specified stat to grow logistically and adds a StatChangedEvent. */
public function registerStatLog(Stat stat, StatChangedEvent statChangedEvent)
statChangedEvents[stat castTo int] = statChangedEvent
registerStatLog(stat)
/** Registers the specified stat to grow linearly and adds a linear substat. */
public function registerStatLinLin(Stat stat, Stat substat)
registerStatLin(stat)
registerStatLin(substat)
statSubstat[stat castTo int] = substat castTo int
parentStat[substat castTo int] = stat castTo int
/** Registers the specified stat to grow linearly, adds a linear substat and a StatChangedEvent. */
public function registerStatLinLin(Stat stat, Stat substat, StatChangedEvent statChangedEvent)
statChangedEvents[stat castTo int] = statChangedEvent
registerStatLinLin(stat, substat)
/** Registers the specified stat to grow linearly and adds an exponential substat. */
public function registerStatLinExp(Stat stat, Stat substat)
registerStatLin(stat)
registerStatExp(substat)
statSubstat[stat castTo int] = substat castTo int
parentStat[substat castTo int] = stat castTo int
/** Registers the specified stat to grow linearly, adds a exponential substat and a StatChangedEvent. */
public function registerStatLinExp(Stat stat, Stat substat, StatChangedEvent statChangedEvent)
statChangedEvents[stat castTo int] = statChangedEvent
registerStatLinExp(stat, substat)
StatType array statType
int array statSubstat // references the stats substat, -1 if none
int array parentStat // references the substats stat, -1 if none
StatChangedEvent array statChangedEvents
int numStats = 0 // number of stats total
HashMap<int, UnitBaseStatBuffer> mapBaseStatBuffers = new HashMap<int, UnitBaseStatBuffer>()
UnitStatBuffer array mapUnitStatBuffers
UnitBaseStatBuffer DEFAULT_BASE_STAT_BUFFER
public interface StatChangedEvent
function statChanged(unit u, Stat s)
enum StatType
LINEAR
EXPONENTIAL
LOGISTIC
class StatEntity
Stat stat
int val
construct(Stat stat, int val)
this.stat = stat
this.val = val
/** A dynamic list which can hold an arbitrary amount of stats. */
public class StatList
protected LinkedList<StatEntity> statList
construct()
statList = new LinkedList<StatEntity>()
/**
* Add a stat with the specified value.
* For exponential/logistic stats use percental values.
*/
function add(Stat s, int value)
switch statType[s castTo int]
case LINEAR
statList.add(new StatEntity(s, value))
case EXPONENTIAL
statList.add(new StatEntity(s, value+100))
case LOGISTIC
statList.add(new StatEntity(s, 100-value))
ondestroy
for StatEntity s in statList
destroy s
destroy statList
/**
* A sized list which buffers a units base stats.
* This is used to set default values for UnitStatBuffers.
*/
public class UnitBaseStatBuffer
private static int array stats
private int arrayStart
/** Creates a new RawStatBuffer initialized with default base stats for the specified unitType. */
construct(int unitType)
mapBaseStatBuffers.put(unitType, this)
arrayStart = 1 + (this castTo int - 1) * numStats
for n = 0 to numStats
if statType[n] == StatType.LINEAR
stats[arrayStart + n] = 0
else // EXPONENTIAL, LOGISTIC
stats[arrayStart + n] = 100
/** Sets the specified stat to the specified value. */
function set(Stat s, int value)
switch statType[s castTo int]
case LINEAR
stats[s2i(s)] = value
case EXPONENTIAL
stats[s2i(s)] = value+100
case LOGISTIC
stats[s2i(s)] = 100-value
/** Returns the array index for the specified stat. */
protected function s2i(Stat s) returns int
return arrayStart + s castTo int
/** Returns the base value (in the internal representation) of the specified stat. */
protected function getStatRaw(Stat s) returns int
return stats[arrayStart + s castTo int]
/**
* A sized list which buffers a units stats.
* Allows adding and removing StatLists and cached retrieval of single stats.
*/
public class UnitStatBuffer
private static int array statsAbs
private static rational array statsPerc
// Cache:
private static int array cache
private static boolean array cacheDirty
// Stat changed events:
private static Stat array eventsToInvoke
private static int eventsToInvokeLast
private static boolean array isEventAdded
private int arrayStart
private unit u
private UnitBaseStatBuffer baseStats
/**
* Creates a new UnitStatBuffer for the specified unit.
* Initializes it with values from the UnitBaseStatBuffer assigned to the units unittype.
*/
construct(unit u)
mapUnitStatBuffers[getUnitIndex(u)] = this
reset(u, mapBaseStatBuffers.get(u.getTypeId()))
/**
* Creates a new UnitStatBuffer for the specified unit.
* Initializes it with values from the specified UnitBaseStatBuffer.
* Uses a default UnitBaseStatBuffer if null.
*/
construct(unit u, UnitBaseStatBuffer baseStats)
mapUnitStatBuffers[getUnitIndex(u)] = this
reset(u, baseStats)
ondestroy
mapUnitStatBuffers[getUnitIndex(u)] = null
private function reset(unit u, UnitBaseStatBuffer baseStatBuffer)
this.u = u
if baseStatBuffer == null
this.baseStats = DEFAULT_BASE_STAT_BUFFER
else
this.baseStats = baseStatBuffer
arrayStart = 1 + (this castTo int - 1) * numStats
for n = 0 to numStats
cacheDirty[arrayStart + n] = true
// Copy UnitBaseStatBuffer:
if statType[n] == StatType.LINEAR
statsAbs[arrayStart + n] = baseStats.getStatRaw(n castTo Stat)
else // EXPONENTIAL, LOGISTIC
statsPerc[arrayStart + n] = rational(baseStats.getStatRaw(n castTo Stat), 100)
/** Retrieves the specified stats value, reads it from cache if available. */
function get(Stat s) returns int
int result = 0
if cacheDirty[arrayStart + s castTo int]
// cache miss
result = getStatRecalc(s castTo int)
cache[arrayStart + s castTo int] = result
cacheDirty[arrayStart + s castTo int] = false
else
// cache hit
result = cache[arrayStart + s castTo int]
return result
/** Retrieves the specified stats base value. */
function getBaseStat(Stat s) returns int
int res = 0
switch statType[s castTo int]
case LINEAR
res = baseStats.getStatRaw(s)
case EXPONENTIAL
res = baseStats.getStatRaw(s)-100
case LOGISTIC
res = 100-baseStats.getStatRaw(s)
return res
/** Calculates the specified stats value. */
private function getStatRecalc(int statId) returns int
int result = 0
switch statType[statId]
case LINEAR
result = statsAbs[arrayStart + statId]
case EXPONENTIAL
result = (statsPerc[arrayStart + statId] * rational(100, 1)).toInt() - 100
case LOGISTIC
result = 100 - (statsPerc[arrayStart + statId] * rational(100, 1)).toInt()
if statSubstat[statId] != -1
// TODO: read substat value from cache, problem: cyclic dependency
// result = (result*(100+getStat(statSubstat[statId] castTo Stat))) div 100
result = (result*(100+getStatRecalc(statSubstat[statId]))) div 100
return result
/** Adds the specified stats event to the event queue, if not added yet. */
private static function eventQueueAdd(Stat s)
if not isEventAdded[s castTo int] and statChangedEvents[s castTo int] != null
isEventAdded[s castTo int] = true
eventsToInvokeLast++
eventsToInvoke[eventsToInvokeLast] = s
/** Resets the event queue. */
private static function eventQueueReset()
eventsToInvokeLast = -1
/** Fires all events in the event queue. */
function eventQueueInvoke()
for n = 0 to eventsToInvokeLast
isEventAdded[eventsToInvoke[n] castTo int] = false
statChangedEvents[eventsToInvoke[n] castTo int].statChanged(u, eventsToInvoke[n])
/** Applies all stat values from the specified StatList to this UnitStatBuffer. */
function apply(StatList l)
eventQueueReset()
for StatEntity s in l.statList
// Mark cache dirty:
cacheDirty[arrayStart + s.stat castTo int] = true
if parentStat[s.stat castTo int] != -1
cacheDirty[arrayStart + parentStat[s.stat castTo int]] = true
eventQueueAdd(parentStat[s.stat castTo int] castTo Stat)
else
eventQueueAdd(s.stat)
// Accumulate values:
if statType[s.stat castTo int] == StatType.LINEAR
statsAbs[arrayStart + s.stat castTo int] += s.val
else // EXPONENTIAL, LOGISTIC
statsPerc[arrayStart + s.stat castTo int] *= rational(s.val, 100)
eventQueueInvoke()
/** Removes all stat values from the specified StatList from this UnitStatBuffer. */
function remove(StatList l)
eventQueueReset()
for StatEntity s in l.statList
// Mark cache dirty:
cacheDirty[arrayStart + s.stat castTo int] = true
if parentStat[s.stat castTo int] != -1
cacheDirty[arrayStart + parentStat[s.stat castTo int]] = true
eventQueueAdd(parentStat[s.stat castTo int] castTo Stat)
else
eventQueueAdd(s.stat)
// Accumulate values:
cacheDirty[arrayStart + s.stat castTo int] = true
if statType[s.stat castTo int] == StatType.LINEAR
statsAbs[arrayStart + s.stat castTo int] -= s.val
else // EXPONENTIAL, LOGISTIC
statsPerc[arrayStart + s.stat castTo int] /= rational(s.val, 100)
eventQueueInvoke()
init
statRegistry()
DEFAULT_BASE_STAT_BUFFER = new UnitBaseStatBuffer(0)
Config (example):
Wurst:
package StatHandlerConfig
import StatHandler
/**
* Make this function return a unique index if you want to use the unit extension functions.
* You can also comment it out and "import public" a package which provides this function.
*/
public function getUnitIndex(unit u) returns int
return 0
/** List your stats here. */
public enum Stat
DAMAGE
DAMAGE_PERC
ARMOR
EVASION
/**
* Register all your stats here. Available functions:
* - Linear stat:
* registerStatLin(Stat stat)
* - Exponential stat:
* registerStatExp(Stat stat)
* - Logistic stat:
* registerStatLog(Stat stat)
* - Linear stat with linear substat:
* registerStatLinLin(Stat stat, Stat substat)
* - Linear stat with exponential substat:
* registerStatLinExp(Stat stat, Stat substat)
*/
public function statRegistry()
registerStatLinLin(Stat.DAMAGE, Stat.DAMAGE_PERC, (unit u, Stat s) -> print("Stat changed: Damage"))
registerStatLin(Stat.ARMOR, (unit u, Stat s) -> print("Stat changed: Armor"))
registerStatLog(Stat.EVASION)
Defining Stats:
To define a stat you first have to add a new constant to the enum "Stat" in StatHandlerConfig. Then you have to register the stat by calling one of the following functions. These functions determine how multiple stat values are accumulated.
1. Linear - registerStatLin(Stat.ARMOR):
Simply adds the values. This should be used for most absolute values like Damage, Armor, Strength, ...
Example: (20 Armor) and (20 Armor) results in (40 Armor).
2. Exponential - registerStatExp(Stat.SPELLPOWER):
Treats values as percental and multiplies them. This is usually used for percental stats which increase an absolute value.
Example: (50% Spellpower) and (50% Spellpower) result in (125% Spellpower).
Consider your spell deals 100 Damage. If you add a 50% Spellpower increase it does 150 Damage. If you increase those 150 Damage again by 50% you end up at 225 Damage, which is equal to 100 Damage with 125% Bonus damage.
3. Logistic - registerStatLog(Stat.EVASION):
Treats values as percental and multiplies their inverse. This is usually used for chances that rolled after each other.
Example: (10% Evasion) and (10% Evasion) results in (19% Evasion).
Consider you have 10% evasion and you get attacked 100 times. You will evade 10 times and get hit 90 times. Consider you get another chance of 10% to evade if your first try failed. Then you will evade another 9 times (10% of 90) and get hit 81 times. So overall you get hit 81 times and evade 19 times, which is equal to 19% evasion.
A sideeffect of this is that this automatically caps at 100% as you can only get very close to 100%, but never reach it.
In addition to these three types you can use combined types which will add an additional substat to the registered stat. The substat will be considered percental and increase the stat in the respective way. The substat will automatically be taken into account when retrieving the stat!
4. Linear stat, linear substat - registerStatLinLin(Stat.DAMAGE, Stat.DAMAGE_PERCENT)
Both stat and substat accumulate separately in a linear manner, when retrieving the stat the substat is interpreted as percental and multiplied to the stat.
Example: (35 Damage), (65 Damage), (40% Damage), (40% Damage) results in (100 Damage), (80% Damage) which results in (180 Damage).
5. Linear stat, exponential substat - registerStatLinExp(Stat.DAMAGE, Stat.DAMAGE_PERCENT)
Stat values accumulate in a linear manner while substats accumulate exponentially. When retrieving the stat the substat is multiplied to the stat.
Example: (35 Damage), (65 Damage), (40% Damage), (40% Damage) results in (100 Damage), (96% Damage) which results in (196 Damage).
Usage:
1. UnitBaseStatBuffer
To define the base stats for a unittype create a new object of UnitBaseStatBuffer and pass the desired base stats to it. Stats which get no base value assigned will use a default one (zero).
Wurst:
let buffer = new UnitBaseStatBuffer('hfoo')
buffer.add(Stat.DAMAGE, 5)
buffer.add(Stat.ARMOR, 2)
2. UnitStatBuffer
Creating a UnitStatBuffer for a unit allows it to apply stats to it.
Wurst:
let u = CreateUnit(Player(0), 'hfoo', 0., 0., 0.)
let buffer = new UnitStatBuffer(u)
The UnitStatBuffer will automatically look for UnitBaseStatBuffers assigned to the unittype of u. It is also possible to pass a UnitBaseStatBuffer directly as second argument (useful if different unittypes are sharing a UnitBaseStatBuffer).
3. StatList
A StatList is a dynamic list which can hold an arbitrary amoint of stats. When a StatList is applied to a UnitStatBuffer all its stats will be added to the UnitStatBuffers unit. StatList are perfect for storing stats of items or buffs.
Wurst:
let u = CreateUnit(Player(0), 'hfoo', 0., 0., 0.)
let buffer = new UnitStatBuffer(u)
// Create a new StatList for item1:
let item1 = new StatList()
item1.add(Stat.ATTACKSPEED, 10)
item1.add(Stat.AGILITY, 6)
// Create a new StatList for item2 (cascade operator syntax):
let item2 = new StatList()..add(Stat.DAMAGE, 4)..add(Stat.DAMAGE_PERCENTAL, 15)..add(Stat.EVASION, 10)
// Apply item1 to the StatBuffer of u:
buffer.apply(item1)
// Apply item2:
buffer.apply(item2)
// Remove item1:
buffer.remove(item1)
4. Retrieving stats
To get a units current stats call its UnitStatBuffers getStat function. Getting a stat which has a substat will return the combined value of stat and substat!
Wurst:
let u = CreateUnit(Player(0), 'hfoo', 0., 0., 0.)
let buffer = new UnitStatBuffer(u)
let item1 = new StatList()..add(Stat.DAMAGE, 8)..add(Stat.DAMAGE_PERCENTAL, 50)
buffer.apply(item1)
int damage = buffer.get(Stat.DAMAGE) // will return 8+50% = 12
Optional UnitIndexer:
If a UnitIndexing system is available the "getUnitIndex(...)" function in StatHandlerConfig can be overwritten to return the correct values. Alternatively the function can be removed and a package which provides the same function can be imported ("import public") to StatHandlerConfig.
Providing a UnitIndexer will enable additional functions, which are basically more comfortable wrappers for the normal functions.
Wurst:
public function createUnitStatBuffer(unit u)
public function destroyUnitStatBuffer(unit u)
public function getUnitStatBuffer(unit u) returns UnitStatBuffer
public function unit.getStat(Stat s) returns int
public function unit.applyStatList(StatList statList)
public function unit.removeStatList(StatList statList)
Todo list:
- Left to decide: Make unitindexer a requirement, remove most of the normal functions and only provide those mentioned in "Optional UnitIndexer". -> Smaller API?
- provide a way to reset a units stats?
Changelog:
1.1 Lots of fixes, did intensive testing
1.0 (stable) UnitStatBuffer constructor allows "null" UnitBaseStatBuffer argument and uses a default (empty) one, Added StatChangedEvents,
0.9 Initial unstable release
Last edited: