I'm using UnitProperties. I've recoded it a bit to work with AutoIndex and it _might_ have gotten broken in the process. I've made several test triggers to check that, but it turned out fine. It seems that it's this trigger.
The problem is that this spell works only on the first unit around (it loses it's 10 armor), the second one loses 14 while it has only 5 before the spell. The target doesn't gain anything.
I've commented the lines which should only tell me what the spell is doing at the moment. However, if I cast the spell with those lines uncommented, it TELLS me the correct values, while it doesn't apply correct ones. For those who want it, here's the new UnitProperties (I've marked my changes):
UnitProperties
The only thing I did was replacing the u:Id with GetUnitId(u) from AutoIndex. AutoIndex remained unchanged.
JASS:
//*******************************************************************************//* 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.lualibraryUnitPropertiesinitializerInitrequiresAutoIndexglobals//*******************************************************************************// 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.//privateconstantintegerMAX = 8privateconstantintegerOFFSET = 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.//privateconstantintegerAGI = 'AG+a'privateconstantintegerINT = 'IN+a'privateconstantintegerSTR = 'ST+a'privateconstantintegerATTACK_SPEED = 'AT+a'privateconstantintegerARMOR = 'AR+a'privateconstantintegerDAMAGE = 'DA+a'privateconstantintegerLIFE_REGEN = 'LI+a'privateconstantintegerMANA_REGEN_PERCENT = 'MR+a'//*******************************************************************************// These globals reference the starting abilities for properties that use the// life/mana bug.//privateconstantintegerMAX_LIFE = 'Lif+'privateconstantintegerMAX_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.//privatetimerTimer = CreateTimer()
privateintegerarrayPow2propertiesarrayUnitPropertiesendglobalsstructproperties// ******************************************************************************// These are the standard properties you would want to manipulate on a given unit//realAgi = 0realInt = 0realStr = 0realLifeRegen = 0realManaRegen = 0realManaRegenPercent = 0realArmor = 0realDamage = 0realAttackSpeed = 0realMoveSpeed// ******************************************************************************// 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.//unitunitrealAttackEvasion = 0realAttackMiss = 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 = 100staticmethodcreatetakesunitureturnspropertieslocalpropertiesthis = properties.allocate()
set.unit = u// You may want to use GetUnitDefaultMoveSpeed here, but I have seen// some bugs with it.set.MoveSpeed = GetUnitMoveSpeed(u)
returnthisendmethodmethodonDestroytakesnothingreturnsnothingsetUnitProperties[GetUnitId(.unit)] = 0set.unit = nullendmethodendstruct// ******************************************************************************// 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// deleted. ~Idontneedaname@// ******************************************************************************// Create or manipulate a set of properties for a given unit.//functionCreateUnitPropertiestakesunitureturnsnothinglocalintegerid = GetUnitId(u)ifUnitProperties[id] == 0thensetUnitProperties[id] = properties.create(u)
endifendfunctionfunctionGetUnitPropertiestakesunitureturnspropertiesreturnUnitProperties[GetUnitId(u)]
endfunctionfunctionDestroyUnitPropertiestakesunitureturnsnothinglocalintegerid = GetUnitId(u)ifUnitProperties[id] != 0thencallproperties.destroy(UnitProperties[id])
endifendfunction//*******************************************************************************// 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.//functionUnitModifyMoveSpeedtakesunitu, realamountreturnsnothinglocalpropertiesp = UnitProperties[GetUnitId(u)]
setp.MoveSpeed = p.MoveSpeed + R2I(amount)
callSetUnitMoveSpeed(u, p.MoveSpeed)
endfunctionfunctionUnitSetMoveSpeedtakesunitu, realamountreturnsnothinglocalpropertiesp = UnitProperties[GetUnitId(u)]
setp.MoveSpeed = R2I(amount)
callSetUnitMoveSpeed(u, p.MoveSpeed)
endfunction//There's already a native for this, but for the sake of completeness:functionUnitGetMoveSpeedtakesunitureturnsrealreturnUnitProperties[GetUnitId(u)].MoveSpeedendfunction// ******************************************************************************// Periodic properties, like static mana regeneration.////! textmacro UnitSetPeriodic takes memberglobalsprivategroup$member$Group = CreateGroup()
endglobalsfunctionUnitModify$member$takesunitu, realamountreturnsnothinglocalpropertiesp = UnitProperties[GetUnitId(u)]
setp.$member$ = p.$member$ + amountifp.$member$ != 0thencallGroupAddUnit($member$Group, u)
elsecallGroupRemoveUnit($member$Group, u)
endifendfunctionfunctionUnitSet$member$takesunitu, realamountreturnsnothinglocalpropertiesp = UnitProperties[GetUnitId(u)]
setp.$member$ = amountifamount != 0thencallGroupAddUnit($member$Group, u)
elsecallGroupRemoveUnit($member$Group, u)
endifendfunctionfunctionUnitGet$member$takesunitureturnsrealreturnUnitProperties[GetUnitId(u)].$member$endfunction//! endtextmacro//! runtextmacro UnitSetPeriodic("ManaRegen")// ******************************************************************************// Triggered properties like Spell Crits and Spell Evasion use these macros.////! textmacro UnitSetProperty takes memberfunctionUnitModify$member$takesunitu, realamountreturnsnothinglocalpropertiesp = UnitProperties[GetUnitId(u)]
setp.$member$ = p.$member$ + R2I(amount)
endfunctionfunctionUnitSet$member$takesunitu, realamountreturnsnothingsetUnitProperties[GetUnitId(u)].$member$ = R2I(amount)
endfunctionfunctionUnitGet$member$takesunitureturnsrealreturnUnitProperties[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 memberfunctionUnitModify$member$takesunitu, realamountreturnsnothinglocalpropertiesp = UnitProperties[GetUnitId(u)]
setp.$member$ = p.$member$ + R2I(amount)
endfunctionfunctionUnitSet$member$takesunitu, realamountreturnsnothingsetUnitProperties[GetUnitId(u)].$member$ = R2I(amount)
endfunctionfunctionUnitGet$member$takesunitureturnsrealreturnUnitProperties[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, MEMBERfunctionUnitModify$member$takesunitu, realamountreturnsnothinglocalpropertiesp = GetUnitProperties(u)
localrealr = p.$member$localintegeri = 1localintegerelocalintegerasetp.$member$ = p.$member$ + R2I(amount)
setamount = R2I(p.$member$)
loopexitwheni > 1ifamount >= 0andr >= 0.thensete = $MEMBER$setr = amountelseifamount <= 0andr <= 0.thensete = $MEMBER$ + OFFSETsetr = -amountelseifamount <= 0andr >= 0.thensete = $MEMBER$setr = 0.seti = 0elseifamount >= 0andr <= 0.thensete = $MEMBER$ + OFFSETsetr = 0.seti = 0endifseta = MAXloopexitwhena < 0ifr >= Pow2[a] thencallUnitAddAbility(u, e + a)
setr = r - Pow2[a]
elsecallUnitRemoveAbility(u, e + a)
endifseta = a - 1endloopseti = i + 1endloopendfunctionfunctionUnitSet$member$takesunitu, realamountreturnsnothinglocalpropertiesp = GetUnitProperties(u)
localrealr = p.$member$localintegeri = 1localintegerelocalintegerasetp.$member$ = R2I(amount)
setamount = R2I(p.$member$)
loopexitwheni > 1ifamount >= 0andr >= 0.thensete = $MEMBER$setr = amountelseifamount <= 0andr <= 0.thensete = $MEMBER$ + OFFSETsetr = -amountelseifamount <= 0andr >= 0.thensete = $MEMBER$setr = 0.seti = 0elseifamount >= 0andr <= 0.thensete = $MEMBER$ + OFFSETsetr = 0.seti = 0endifseta = MAXloopexitwhena < 0ifr >= Pow2[a] thencallUnitAddAbility(u, e + a)
setr = r - Pow2[a]
elsecallUnitRemoveAbility(u, e + a)
endifseta = a - 1endloopseti = i + 1endloopendfunctionfunctionUnitGet$member$takesunitureturnsrealreturnUnitProperties[GetUnitId(u)].$member$endfunctionstruct$member$extendsarraystaticmethodoperator [] takesunitureturnsrealreturnUnitProperties[GetUnitId(u)].$member$endmethodstaticmethodoperator []= takesunitu, realrreturnsnothingcallUnitSet$member$(u, r)
endmethodendstruct//! 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, statefunctionUnitModify$member$takesunitu, realamountreturnsnothinglocalintegercomp = 100localintegeri = 4localintegerabil = $state$ifamount < 0thensetabil = abil + 2setamount = -amountendifsetamount = R2I(amount)
loopexitwheni < 2ifamount >= compthenloopexitwhenamount < compcallUnitAddAbility (u, abil)
callSetUnitAbilityLevel(u, abil, i)
callUnitRemoveAbility (u, abil)
setamount = amount - compendloopendifsetcomp = comp / 10seti = i - 1endloopendfunctionfunctionUnitSet$member$takesunitu, realamountreturnsnothinglocalintegercomp = 100localintegeri = 4localintegerabil = $state$localrealr = GetUnitState(u, UNIT_STATE_$state$)
ifamount > rthensetamount = R2I(amount - r)
elseifamount < rthensetamount = R2I(RAbsBJ(r - amount))
setabil = abil + 2endifloopexitwheni < 2ifamount >= compthenloopexitwhenamount < compcallUnitAddAbility (u, abil)
callSetUnitAbilityLevel(u, abil, i)
callUnitRemoveAbility (u, abil)
setamount = amount - compendloopendifsetcomp = comp / 10seti = i - 1endloopendfunctionfunctionUnitGet$member$takesunitureturnsrealreturnGetUnitState(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.//functionRefreshUnitPropertiestakesunitureturnsnothingcallUnitModifyArmor (u, 0)
callUnitModifyDamage (u, 0)
callUnitModifyAttackSpeed (u, 0)
callUnitModifyMoveSpeed (u, 0)
callUnitModifyAgi (u, 0)
callUnitModifyInt (u, 0)
callUnitModifyStr (u, 0)
callUnitModifyLifeRegen (u, 0)
callUnitModifyManaRegen (u, 0)
callUnitModifyManaRegenPercent(u, 0)
endfunction// ******************************************************************************// Additional periodic properties would be added to the Periodic function and// would require their own ForGroup.//privatefunctionUnitAdjustManatakesnothingreturnsnothinglocalunitu = GetEnumUnit()
setu:Mana = u:Mana + u:ManaRegensetu = nullendfunctionprivatefunctionPeriodictakesnothingreturnsnothingcallForGroup(ManaRegenGroup, functionUnitAdjustMana)
endfunction// ******************************************************************************// Initialization stores powers of 2 up to 30, starts the periodic timer and// preloads all property abilities.//globalsprivateunituendglobalsprivatefunctionPreloadBinarytakesintegerireturnsnothinglocalintegerdone = i + OFFSET + MAXlocalintegerexitloopsetexit = i + MAXloopexitwheni == exitcallUnitAddAbility(u, i)
seti = i + 1endloopexitwheni == doneseti = (i - MAX) + OFFSETendloopendfunctionprivatefunctionInittakesnothingreturnsnothinglocalintegeri = 1setPow2[0] = 1callTimerStart(Timer, 1, true, functionPeriodic)
loopexitwheni > 30// Can't go above 2^30, integer will overflowsetPow2[i] = Pow2[i - 1] * 2seti = i + 1endloopsetu = CreateUnit(Player(14), 'hfoo', 0, 0, 0)
callPreloadBinary (AGI)
callPreloadBinary (INT)
callPreloadBinary (STR)
callPreloadBinary (ATTACK_SPEED)
callPreloadBinary (DAMAGE)
callPreloadBinary (LIFE_REGEN)
callPreloadBinary (ARMOR)
callPreloadBinary (MANA_REGEN_PERCENT)
callUnitAddAbility(u, MAX_LIFE)
callUnitAddAbility(u, MAX_LIFE+2)
callUnitAddAbility(u, MAX_MANA)
callUnitAddAbility(u, MAX_MANA+2)
callUnitAddAbility(u, 'Evas')
callKillUnit (u)
callRemoveUnit (u)
setu = nullendfunction//*******************************************************************************// These wrappers use operator overloading to allow a more intuitive syntax than// UnitSet/Modify/GetProperty()://// set Unit:Property = x// or// set Property[Unit] = x//// To modify a property rather than set it to a specific value://// set Unit:Property = Unit:Property + x// or// set Property[Unit] = Property[Unit] + x////! textmacro UnitPropertyWrapper takes namestruct$name$extendsarraystaticmethodoperator [] takesunitureturnsrealreturnUnitGet$name$(u)
endmethodstaticmethodoperator []= takesunitu, realrreturnsnothingcallUnitSet$name$(u, r)
endmethodendstruct//! endtextmacro//! runtextmacro UnitPropertyWrapper("ManaRegen")//! runtextmacro UnitPropertyWrapper("MoveSpeed")//! runtextmacro UnitPropertyWrapper("AttackEvasion")//! runtextmacro UnitPropertyWrapper("AttackMiss")//! textmacro UnitStateWrapper takes name, typestruct$name$extendsarraystaticmethodoperator [] takesunitureturnsrealreturnGetUnitState(u, UNIT_STATE_$type$)
endmethodstaticmethodoperator []= takesunitu, realrreturnsnothingcallSetUnitState(u, UNIT_STATE_$type$, r)
endmethodendstructstructMax$name$extendsarraystaticmethodoperator [] takesunitureturnsrealreturnGetUnitState(u, UNIT_STATE_MAX_$type$)
endmethodstaticmethodoperator []= takesunitu, realrreturnsnothingcallUnitSetMax$name$(u, r)
endmethodendstruct//! endtextmacro//! runtextmacro UnitStateWrapper("Life", "LIFE")//! runtextmacro UnitStateWrapper("Mana", "MANA")endlibrary
//------------------------------------------------------------------------------// The BonusMod_BeginBonuses macro takes a single boolean type parameter.// If set to true, bonus abilities will be created (or recreated) on save.// If set to false, abilities will not be generated.// // If you modify any of the bonus declaration macros, or add new ones, you must// regenerate abilities.// // Note that if you remove a bonus, the abilities it had created will not be// automatically removed. This is also true of reducing the number of abilities// a bonus uses.// // After you generate abilities, you must close your map and reopen it in the// editor. You can then disable ability generation until the next time you// modify the bonus types.//------------------------------------------------------------------------------//! runtextmacro BonusMod_BeginBonuses("true")