All scripts are expected to be written using Lua language v5.4.1 and should be placed in Scripts folder. Scripts folder itself should be placed in the game folder.
- settings.lua - mss32 proxy dll settings that changes game rules
- doppelganger.lua - logic that computes level of doppelganger transform (category L_DOPPELGANGER)
- transformSelf.lua - computes unit level and determines free attacks to give for transform-self attacks (category L_TRANSFORM_SELF)
- transformOther.lua - computes unit level for transform-other attacks (category L_TRANSFORM_OTHER)
- summon.lua - computes summoned unit level for summon attacks (category L_SUMMON)
- textids.lua - contains interface text mapping for custom functionality
- getAllTargets.lua - contains selection/attack targeting logic for any/all attack reach
- getAdjacentTargets.lua - contains selection/attack targeting logic for adjacent/all-adjacent attack reach
- getSelectedTargetAndAllAdjacentToIt.lua - contains attack targeting logic for selective-cleave attack reach
- getSelectedTargetAndOneAdjacentToIt.lua - contains attack targeting logic for single selective-cleave attack reach
- getSelectedTargetAndOneBehindIt.lua - contains attack targeting logic for pierce attack reach
- getSelectedLineTargets.lua - contains attack targeting logic for wide-cleave attack reach
- getSelectedColumnTargets.lua - contains attack targeting logic for column attack reach
- getSelectedArea2x2Targets.lua - contains attack targeting logic for 2x2 area splash attack reach
- getSelectedTargetAndTwoChainedRandom.lua - contains attack targeting logic for random chain attack reach
- getSelectedTargetAndOneRandom.lua - contains attack targeting logic for additional random target
- getWoundedFemaleGreenskinTargets.lua - contains targeting logic that only allows to reach wounded female greenskins
- Scripts/Modifiers contain custom modifier script examples
Writes message to luaDebug.log file when debugHooks is set to true in settings.lua.
log('Unit current level:' .. unit.impl.level)Returns current scenario. The function only accessible to scripts where scenario access is appropriate:
summon.luadoppelganger.luatransformSelf.luatransformOther.luadrainLevel.lua- custom attack reach scripts
- custom unit modifier script
checkEventCondition has scenario as its argument so getScenario is not bound to it.
getScenario():getUnit(unitId)Race = { Human, Undead, Heretic, Dwarf, Neutral, Elf }Subrace = { Custom, Human, Undead, Heretic, Dwarf, Neutral, NeutralHuman, NeutralElf, NeutralGreenSkin,
NeutralDragon, NeutralMarsh, NeutralWater, NeutralBarbarian, NeutralWolf, Elf }Lord = { Mage, Warrior, Diplomat }Terrain = { Human, Dwarf, Heretic, Undead, Neutral, Elf }Ground = { Plain, Forest, Water, Mountain }Unit = { Soldier, Noble, Leader, Summon, Illusion, Guardian }
Leader = { Fighter, Explorer, Mage, Rod, Noble }
Ability = { Incorruptible, WeaponMaster, WandScrollUse, WeaponArmorUse, BannerUse, JewelryUse,
Rod, OrbUse, TalismanUse, TravelItemUse, CriticalHit }
Attack = { Damage, Drain, Paralyze, Heal, Fear, BoostDamage, Petrify, LowerDamage, LowerInitiative,
Poison, Frostbite, Revive, DrainOverflow, Cure, Summon, DrainLevel, GiveAttack,
Doppelganger, TransformSelf, TransformOther, Blister, BestowWards, Shatter }
Source = { Weapon, Mind, Life, Death, Fire, Water, Earth, Air }
Reach = { All, Any, Adjacent }
Immune = { NotImmune, Once, Always }
Item = { Armor, Jewel, Weapon, Banner, PotionBoost, PotionHeal, PotionRevive,
PotionPermanent, Scroll, Wand, Valuable, Orb, Talisman, TravelItem, Special }
Equipment = { Banner, Tome, Battle1, Battle2, Artifact1, Artifact2, Boots }
DeathAnimation = { Human, Heretic, Dwarf, Undead, Neutral, Dragon, Ghost, Elf }
BattleStatus = {
XpCounted, -- Unit was killed and its experience points were counted
Dead, -- Unit dead
Paralyze, -- Unit paralyzed
Petrify, -- Unit petrified
DisableLong, -- Long disable applied (paralyze, petrify or fear)
BoostDamageLvl1, -- 25% boost
BoostDamageLvl2, -- 50% boost
BoostDamageLvl3, -- 75% boost
BoostDamageLvl4, -- 100% boost
BoostDamageLong, -- Long damage boost (until battle is over or lower damage applied)
LowerDamageLvl1, -- 50% lower damage
LowerDamageLvl2, -- 33% lower damage
LowerDamageLong, -- Long lower damage (until battle is over or removed)
LowerInitiative, -- 50% lower initiative
LowerInitiativeLong, -- Long lower initiative
Poison, -- Poison dot
PoisonLong, -- Long poison applied
Frostbite, -- Frostbite dot
FrostbiteLong, -- Long frostbite applied
Blister, -- Blister dot
BlisterLong, -- Long blister applied
Cured, -- Cure applied
Transform, -- Unit transformed by another unit
TransformLong, -- Long transformation applied by another unit
TransformSelf, -- Unit transfomed himself
TransformDoppelganger, -- Doppelganger transformation
TransformDrainLevel, -- Drain level applied
Summon, -- Unit was summoned during battle
Retreated, -- Unit retreated from battle
Retreat, -- Unit is retreating
Hidden, -- Unit is hidden. For example, while leader dueling a thief
Defend, -- Defend was used in this round
Unsummoned -- Unsummon effect applied
}
Relation = { War, Neutral, Peace }
Represents point in 2D space.
Methods:
-- Creates point with both coordinates set to 0.
Point.new()
-- Creates point with specified coordinates.
-- For example: x = 1, y = 3
Point.new(1, 3)
-- Converts point to string '(x, y)'
tostring(point)
-- Access x coordinate for reading and writing
local x = point.x
point.x = x + 1
-- Access y coordinate for reading and writing
local y = point.y
point.y = y + 1Represents object identifier. Identifiers used to search scenario objects.
Methods:
-- Creates Id from string
Id.new('S143KC0001')
-- Returns empty identifier
Id.emptyId()
-- Converts Id to string
tostring(id)
-- Returns integer representation of id.
-- Can be used as Lua table key for best performance.
id.value
-- Returns identified object index among the same type of scenario objects (units, stacks, items, etc.).
-- Can be used as Lua table key for best performance.
id.typeIndexRepresents unit modifier. Modifiers wrap unit implementation.
Methods:
Returns modifier id. MODIF_ID value from Gmodif.dbf.
modifier.idRepresents game unit that participates in a battle, takes damage and performs attacks. Unit can also be a leader. Leaders are main units in stacks.
Methods:
-- Returns unit's current experience points.
unit.xp
-- Returns unit's current hit points.
unit.hp
-- Returns unit's maximum hit points.
unit.hpMaxReturns unit id. This is different to id of unit implementation. The value is unique for every unit on scenario map.
unit.idReturns unit's current implementation. Current implementation describes unit stats according to its levels and possible transformations applied during battle.
unit.implReturns unit's base implementation. Base implementation is a record in GUnits.dbf that describes unit basic stats.
unit.baseImplReturns unit's leveled (generated) implementation. Leveled implementation is unit's current implementation without modifiers, or base implementation plus upgrades from GDynUpgr.dbf according to unit's level. This does not include leader upgrades from GleaUpg.dbf, because the upgrades are modifiers.
unit.leveledImplReturns original unit dummy that represents unit state before transformation,
or nil if unit is not transformed.
The state does not include any unit modifiers thus contains only leveled implementation.
Unit can be transformed by transform-self, transform-other, drain-level or doppelganger attack.
unit.originalReturns array of original modifiers that were applied to unit before transformation, or empty array if unit is not transformed. Usually, modifiers are reapplied after transformation, but there are cases where some modifiers are incompatible with a new form, thus not getting applied to it.
unit.originalModifiersRepresents preserved state of game unit. Used, for instance, to preserve unit state before transformation, so it can be restored later. Methods are identical to unit, except that there is no original and originalModifiers.
Represents unit template. Records in GUnits.dbf are unit implementations.
How unit implementation works and why it is different to unit
Unit implementation is a unit template that can be used by different individual units on scenario map. It is different to unit, because different unit instances can have the same implementation. For example, you can have 3 Squires of level 1 in your party, each having the same implementation.
There are 3 different stages of unit implementation that build on top of each other:
Global, corresponds to a record fromGUnits.dbf;- Returned by unit.baseImpl or impl.global;
- Its
idcorresponds toUNIT_IDfromGUnits.dbf.
Generated, equalsGlobalplus level upgrades fromGDynUpgr.dbf(if any);- Returned by unit.leveledImpl or impl.generated;
- Its
idis different toidof inheritedGlobalimplementation; - If unit has no level upgrades,
unit.leveledImpl/impl.generatedequalsunit.baseImpl/impl.global.
Modified, equalsGeneratedplus applied modifiers fromGmodif.dbf(if any).- Returned by unit.impl;
- Its
idequals toidof inheritedGeneratedimplementation; - If unit has no modifiers,
unit.implequalsunit.leveledImpl/impl.generated.
Unit implementation changes when unit:
- Gets an upgrade, does not matter if it transforms to higher tier unit or simply gets over-level;
- Gets transformed: by Transform-Self, Transform-Other, Drain-Level, or Doppelganger attack;
- Gets modified: when consuming a potion, affected by a spell, equipping an item, getting a leader upgrade, etc.;
Methods:
-- Returns unit's implementation level. LEVEL value from GUnits.dbf.
impl.level
-- Returns experience points needed for next level. XP_NEXT value from GUnits.dbf.
impl.xpNext
-- Returns experience points granted for killing the unit. XP_KILLED value from GUnits.dbf.
impl.xpKilled
-- Returns unit's hit points. HIT_POINT value from GUnits.dbf.
impl.hp
-- Returns unit's armor. ARMOR value from GUnits.dbf.
impl.armor
-- Returns unit's regen. REGEN value from GUnits.dbf.
impl.regen
-- Returns unit's race. ID value from Lrace.dbf. See Race enumeration for all possible values.
impl.race
-- Returns unit's subrace. ID value from LSubRace.dbf. See Subrace enumeration for all possible values.
impl.subrace
-- Indicates if the unit is small (occupies single slot). SIZE_SMALL value from GUnits.dbf.
impl.small
-- Indicates if the unit is male. SEX_M value from GUnits.dbf.
impl.male
-- Indicates if the unit is water only. WATER_ONLY value from GUnits.dbf.
impl.waterOnly
-- Indicates if the unit attacks twice. ATCK_TWICE value from GUnits.dbf.
impl.attacksTwice
-- Returns level after which dynUpgrade2 rules are applied. DYN_UPG_LV from GUnits.dbf.
impl.dynUpgLvl
-- Returns dynamic upgrade 1.
impl.dynUpg1
-- Returns dynamic upgrade 2.
impl.dynUpg2
-- Returns primary attack or nil if no primary attack used.
impl.attack1
-- Returns secondary attack or nil if no secondary attack used.
impl.attack2
-- Returns alternative attack or nil if no alternative attack used.
impl.altAttack
-- Returns leader maximum movement points (or 0 if unit is not a leader).
impl.movement
-- Returns leader scouting range (or 0 if unit is not a leader).
impl.scout
-- Returns current leadership value (or 0 if unit is not a leader).
impl.leadershipReturns unit implementation id. UNIT_ID value from GUnits.dbf.
impl.idReturns base unit implementation. BASE_UNIT value from GUnits.dbf.
impl.baseReturns unit type.
impl.typeReturns leader type (or -1 if unit is not a leader).
impl.leaderTypeReturns true if leader has specified ability (or false if unit is not a leader).
impl:hasAbility(Ability.TalismanUse)Returns true if leader has movement bonus on specified ground (or false if unit is not a leader).
impl:hasMoveBonus(Ground.Water)Returns global unit implementation - a record from GUnits.dbf.
Same as unit.baseImpl.
impl.globalReturns generated unit implementation.
Equals global plus upgrades from GDynUpgr.dbf according to unit's level.
Same as unit.leveledImpl.
impl.generatedReturns array of applied modifiers.
impl.modifiersReturns true if the implementation has modifier specified by id or id string.
impl:hasModifier("G000UM5021")
impl:hasModifier(Id.new("G000UM5021"))Returns immune type for specified attack type.
impl:getImmuneToAttackClass(Attack.Paralyze)Returns immune type for specified attack source.
impl:getImmuneToAttackSource(Source.Water)Represents one of the twelve unit slots on battlefield. Unit positions on a battlefield are mirrored. Frontline positions are even, backline - odd.
1 0 0 1
3 2 vs 2 3
5 4 4 5
Methods:
-- Returns a unit that occupies the slot.
slot.unit
-- Returns a position of the slot (0-5).
slot.position
-- Returns a line index of the slot: 0 - frontline, 1 - backline.
slot.line
-- Returns a column index of the slot: 0 - top, 1 - middle, 2 - bottom.
slot.column
-- Indicates if the slot is on the frontline.
slot.frontline
-- Indicates if the slot is on the backline.
slot.backline
-- Returns a distance between two slots (used for adjacent slot calculations).
slot.distance(otherSlot)Represents 6 unit slots.
Methods:
Returns group id.
group.idReturns group as array of 6 unit slots.
group.slotsReturns group units.
group.unitsReturns true if group has specified unit or unit id.
group:hasUnit(unit)
group:hasUnit(Id.new('S143UN0001'))Represents player's fog of war.
Methods:
Returns true if specified map position is covered by fog of war. Map position can be specified by pair of coordinates or a point.
local hidden = fog:getFog(3, 7)Represents game player including AI and neutrals.
Methods:
Returns player id. The value is unique for every player on scenario map.
player.idReturns player race.
player.raceReturns player lord.
player.lordReturns player bank.
player.bankReturns true if player is human (not AI).
player.humanReturns true if player is always AI.
player.alwaysAiReturns player's fog of war.
In fully loaded scenario, player objects always have fog of war. During scenario loading this property can return nil.
local fog = player.fog
if fog == nil then
return
endRepresents group of 6 unit slots on a map. One of the units is a leader.
Methods:
Returns stack id. The value is unique for every stack on scenario map.
stack.idReturns stack position as a point.
stack.positionReturns player that owns the stack. Neutral stacks are owned by neutral player.
stack.ownerReturns fort that this stack is visiting or nil if none.
stack.insideReturns stack units as a group.
stack.groupReturns stack leader unit.
stack.leaderReturns stack subrace.
stack.subraceReturns array of inventory items. This includes equipped items.
stack.inventoryReturns equipped item by equipment value.
stack:getEquippedItem(Equipment.Boots)--- Returns stack current movement points.
stack.movement
--- Returns true if stack is invisible.
stack.invisible
--- Returns number of battles won by the stack.
stack.battlesWonRepresents Capital or City on a map. Fort contains a garrison group of 6 unit slots. Note that the garrison group is different to a group of visiting stack.
Methods:
Returns fort id. The value is unique for every fort on scenario map.
fort.idReturns fort position as a point.
fort.positionReturns player that owns the fort. Neutral forts are owned by neutral player.
fort.ownerReturns fort units as a group.
fort.groupReturns visitor stack, or nil if none.
fort.visitorReturns fort subrace.
fort.subraceReturns array of inventory items.
fort.inventoryReturns true if fort is a capital city.
fort.capitalReturns fort tier (level). Tiers are in range [1 : 6]. Tier 6 corresponds to the capital city.
fort.tierRepresents Ruin on a map. Ruin contains a garrison group of 6 unit slots.
Methods:
Returns ruin id. The value is unique for every ruin on scenario map.
ruin.idReturns ruin position as a point.
ruin.positionReturns player that looted the ruin, or nil if none.
ruin.looterReturns ruin units as a group.
ruin.groupReturns item reward for looting the ruin.
ruin.itemReturns cash reward for looting the ruin.
ruin.cashRepresents rod object in scenario. Rods are planted to transform terrain and capture resources.
Methods:
Returns rod id. The value is unique for every rod on scenario map.
rod.idReturns copy of rod position as a point.
rod.positionReturns player that planted the rod.
rod.ownerRepresents rules that applied when unit makes its progress gaining levels. Records in GDynUpgr.dbf are dynamic upgrades.
Methods:
-- Returns number of experience points added with each dynamic upgrade. XP_NEXT value from GDynUpgr.dbf.
dynUpgrade.xpNextRepresents location object in scenario.
Methods:
Returns location identifier.
location.idReturns copy of location position as a point.
location.positionReturns radius of location
location.radiusRepresents scenario variable used by events.
Methods:
-- Returns variable name
variable.name
-- Returns variable value
variable.valueStores scenario variables, allows searching them by name.
Methods:
Searches for ScenarioVariable by its name, reeturns nil if not found.
local variable = variables:getVariable('VAR1')
if (variable == nil) then
return
endRepresents map tile.
Methods:
Returns tile terrain type.
tile.terrainReturns tile ground type.
tile.groundRepresents diplomacy relations between races in scenario.
Methods:
Returns current diplomacy relations value between two races in range [0 : 100].
local current = diplomacy:getCurrentRelation(race1, race2)Returns previous diplomacy relations value between two races in range [0 : 100].
local prev = diplomacy:getPreviousRelation(race1, race2)Returns true if two races are in alliance.
local allies = diplomacy:getAlliance(race1, race2)Returns turn number when two races made an alliance. Returns zero if races are not in alliance.
local turn = diplomacy:getAllianceTurn(race1, race2)Returns true if two races are always at war.
local atWar = diplomacy:getAlwaysAtWar(race1, race2)Returns true if diplomacy relations prohibit AI-controlled races from breaking alliance.
local couldNotBreak = diplomacy:getAiCouldNotBreakAlliance(race1, race2)Returns relation type according to diplomacy relations value.
-- Value to type mapping (D_WAR and D_NEUTRAL can be found in GVars.dbf):
-- 0 D_WAR D_NEUTRAL 100
-- | War | Neutral | Peace |
--
local relation = diplomacy:getRelationType(relationValue)Represents scenario map with all its objects and state.
Methods:
Searches for Location by id string or Id, returns nil if not found.
local location = scenario:getLocation('S143LO0001')
if (location == nil) then
return
endReturns ScenarioVariables. If scenario has no variables defined, returns nil.
local variables = scenario.variables
if (variables == nil) then
return
endSearches for Tile by pair of coordinates or Point, returns nil if not found.
local tile = scenario:getTile(3, 5)
if (tile == nil) then
return
endSearches for stack by:
Returns nil if not found.
local stack = scenario:getStack(10, 15)
if (stack == nil) then
return
endSearches for fort by:
Returns nil if not found.
local fort = scenario:getFort(10, 15)
if (fort == nil) then
return
endSearches for ruin by:
Returns nil if not found.
local ruin = scenario:getRuin(10, 15)
if (ruin == nil) then
return
endSearches for rod by:
Returns nil if not found.
local rod = scenario:getRod(10, 15)
if (rod == nil) then
return
endSearches for player by id string or id, returns nil if not found.
local player = scenario:getPlayer('S143PL0000')
if (player == nil) then
return
endSearches for unit by id string or unit id, returns nil if not found.
local unit = scenario:getUnit('S143UN0001')
if (unit == nil) then
return
endSearches for stack that has specified unit among all the stacks in the whole scenario.
You can also use unit id string or id.
Returns nil if not found.
local stack = scenario:findStackByUnit(unit)
if stack == nil then
return
endNote that this search is heavy in terms of performance, so you probably want to minimize excessive calls and use variables to store its results.
Searches for fort that has specified unit in its garrison among all the forts in the whole scenario.
Only garrison units are counted, visiting stack is ignored.
You can also use unit id string or id.
Returns nil if not found.
local fort = scenario:findFortByUnit(unit)
if fort == nil then
return
endNote that this search is heavy in terms of performance, so you probably want to minimize excessive calls and use variables to store its results.
Searches for ruin that has specified unit among all the ruins in the whole scenario.
You can also use unit id string or id.
Returns nil if not found.
local ruin = scenario:findRuinByUnit(unit)
if ruin == nil then
return
endNote that this search is heavy in terms of performance, so you probably want to minimize excessive calls and use variables to store its results.
Returns number of current day in game.
scenario.dayReturns scenario map size.
scenario.sizeReturns object that holds diplomacy relations between races.
Fully loaded scenario always have diplomacy relations. During scenario loading this property can return nil.
local diplomacy = scenario.diplomacy
if diplomacy == nil then
return
endRepresents attack of Unit implementation.
Methods:
Returns attack id. This is different for every dynamic upgrade unit gets.
attack.idReturns attack type.
attack.typeReturns attack source.
attack.sourceReturns attack reach.
attack.reachReturns array of modifiers applied by bestow wards attack.
attack.wards--- Returns attack initiative value.
attack.initiative
--- Returns attack power (accuracy).
attack.power
--- Returns damage the attack can inflict. Damage depends on attack type.
attack.damage
--- Returns healing the attack can apply. Healing depends on attack type.
attack.heal
--- Returns true if attack has long effect duration. Effect depends on attack type.
attack.infinite
--- Returns true if attack can inflict critical damage.
attack.crit
--- Returns level for boost damage, lower damage and lower initiative attacks.
attack.level
--- Returns true if attack is melee (L_ADJACENT or custom reach marked as MELEE in LAttR.dbf).
attack.melee
--- Returns maximum number of targets (1, 6 or MAX_TARGTS value for custom reach in LAttR.dbf).
attack.maxTargets
--- Returns critical damage percent [0 : 255].
attack.critDamage
--- Returns critical damage chance [0 : 100].
attack.critPower
--- Returns damage ratio [0 : 255] for additional targets.
attack.damageRatio
--- Returns true if damage ratio reapplied for each consecutive target.
attack.damageRatioPerTarget
--- Returns true if damage is split among targets.
attack.damageSplitRepresents game currency, mana and gold united.
Methods:
Creates new currency from existing object.
Currency.new(existing)Returns or sets amount of infernal mana.
currency.infernalManaReturns or sets amount of life mana.
currency.lifeManaReturns or sets amount of death mana.
currency.deathManaReturns or sets amount of runic mana.
currency.runicManaReturns or sets amount of grove mana.
currency.groveManaReturns or sets amount of gold.
currency.goldRepresents base item of any type (described in GItem.dbf).
Methods:
Returns item id. ITEM_ID value from GItem.dbf.
base.idReturns item type.
base.typeReturns item value.
base.valueReturns related Unit implementation. For instance: in case of "Angel Orb", Angel unit implementation is returned.
base.unitImplReturns Attack that this item performs (in case of orb or talisman), or nil if no attack is associated with the item.
For instance: in case of "Orb of Fire", corresponding attack from Gattacks.dbf is returned.
base.attackRepresents item object in the current scenario.
Methods:
Returns item id. This is different to id of Item base. The value is unique for every item on scenario map.
item.idReturns Item base.
item.baseReturns item sell value, it accounts global sell ratio and used talisman charges (if applicable).
item.sellValueRepresents battle information.
Methods:
Returns whether a unit with a specified id has a specified battle status.
if battle:getUnitStatus(unit.id, BattleStatus.Defend) then
-- Do something scary
endReturns current round in battle. Round counting starts from 1, but there is a special round 0 when units with 'Doppelganger' attacks present.
battle.currentRoundReturns true if autobattle mode is turned on.
battle.autoBattleReturns player that started battle.
battle.attackerPlayerReturns player that was attacked.
battle.defenderPlayerReturns stack that started battle. Only stacks can initiate battles.
battle.attackerReturns group that was attacked.
Defender group can represent units of a stack, fort or ruin.
Use group.id to get actual type of a group.
battle.defenderdoppelganger and target have type Unit.
item is Item used to perform the attack.
battle specifies an information about current battle.
function getLevel(doppelganger, target, item, battle)
-- Get current doppelganger implementation
local impl = doppelganger.impl
-- Get target unit implementation
local targImpl = target.impl
-- Get least level value from both
local level = math.min(impl.level, targImpl.level)
-- Make sure doppelganger transform level is not lesser than target's base
local baseImpl = target.baseImpl
if (level < baseImpl.level) then
level = baseImpl.level
end
return level
endunit has type Unit.
transformImpl is Unit implementation.
item is Item used to perform the attack.
battle specifies an information about current battle.
function getLevel(unit, transformImpl, item, battle)
-- Transform into current level or level of resulting unit's template, whichever is bigger.
return math.max(unit.impl.level, transformImpl.level)
endattacker and target have type Unit.
transformImpl is Unit implementation.
item is Item used to perform the attack.
battle specifies an information about current battle.
function getLevel(attacker, target, transformImpl, item, battle)
-- transform using target level with a minimum of transform impl level
return math.max(target.impl.level, transformImpl.level);
endattacker and target have type Unit.
item is Item used to perform the attack.
battle specifies an information about current battle.
function getLevel(attacker, target, item, battle)
-- transform into unit with its level minus 1 and minus attacker over-level
return math.max(1, target.impl.level - 1 - attacker.impl.level + attacker.baseImpl.level);
endsummoner has type Unit.
summonImpl is Unit implementation.
item is Item used to perform the attack.
battle specifies an information about current battle.
function getLevel(summoner, summonImpl, item, battle)
-- Use base level of summon if cheap item is used to summon it
if item and item.base.value.gold < 500 then
return summonImpl.level
end
-- Summon unit with level twice as big as summoner level
-- or with level of summon implementation, whichever is bigger.
local impl = summoner.impl
local summonerLevel = impl.level
local summonLevel = summonImpl.level
return math.max(summonerLevel * 2, summonLevel)
endSee Scripts directory for additional examples.
Targeting scripts are used to specify either selection or attack targets of custom attack reach:
- Selection targets are targets that can be selected (clicked) (specified as
SEL_SCRIPTinLAttR.dbf); - Attack targets are targets that will be affected by attack (specified as
ATT_SCRIPTinLAttR.dbf). For instance, in case of "pierce" attack, you can only click adjacent targets, but the attack will not only affect the selected target but also the one behind it (if any). Thus the "pierce" attack uses getAdjacentTargets.lua as selection script and getSelectedTargetAndOneBehindIt.lua as attack script.
Targeting scripts use uniform getTargets function for both selection and attack scripts with the following arguments:
attackeris the unit slot of the attacker unit;selectedis the unit slot of the unit that was selected (clicked).selected.position == -1andselected.unit == nilif this is selection script (no target is clicked yet);alliesare unit slots of all the allies on the battlefield (excluding the attacker);targetsare unit slots of all the targets on the battlefield on which the attack can be performed. For instance, if targets are allies and the attack is Revive, then it will only include dead allies that can be revived;targetsAreAlliesspecified whether targets are allies;itemspecifies an item (orb or talisman) used to perform the attack, ornilif no item is used;battlespecifies an information about current battle;isMarkingspecified whether the script is being called to mark targets visually on the battlefield. Can be used to provide consistent visual representation for randomized scripts, as soft alternative toMRK_TARGTSflag inLAttR.dbf. Alwaysfalseif this is selection script.
function getTargets(attacker, selected, allies, targets, targetsAreAllies, item, battle, isMarking)
-- Get the selected target and the one behind it (pierce attack)
local result = {selected}
for i = 1, #targets do
local target = targets[i]
if target.backline and target.position == selected.position + 1 then
table.insert(result, target)
break
end
end
return result
end-- You can use lambda functions freely
local forEachTile = function (location, f)
local pos = location.position
-- Use integers for tile coordinates
local halfR = math.floor(location.radius / 2)
local startX = pos.x - halfR
local startY = pos.y - halfR
local endX = pos.x + halfR
local endY = pos.y + halfR
for x = startX,endX,1 do
for y = startY,endY,1 do
f(x, y)
end
end
end
local location = scenario:getLocation('S143LO0000')
if (location == nil) then
return false
end
local tilesTotal = location.radius * location.radius
local count = 0
forEachTile(location, function (x, y)
local tile = scenario:getTile(x, y)
if (tile == nil) then
return false
end
if (tile.terrain == Terrain.Human) then
count = count + 1
end
end)
return tilesTotal == countModifiers in the game stack on top of each other, making ordered modifiers chain.
First modifier takes base values from the unit it modifies, next - values modified by the first modifier and so on.
Custom modifiers allow you to modify every stat of unit and its attacks in a single script file with a number of uniform functions.
Most of the custom modifier functions have the following form:
function getSomething(unit, prev)
if someCondition then
return modifiedValue
end
return prev
endunit has type Unit. The unit is presented in a state before the current modifier is applied.
prev is a previous value of the stat. It is either a base value or a value modified by the previous modifier.
Check Scripts/Modifiers for script examples.
template.lua contains a complete list of available functions.
For example:
- Lets say we have a unit with base of
50initiative; - Then we give it a potion that increases damage by 50% if unit initiative
> 60; - Then we give it another potion that increases initiative to
70.
The damage bonus will not work in this case, even though a final unit initiative is 70 that is > 60.
This is because damage modifier applied earlier than initiative modifier.
If we get unit.impl.attack1.initiative from the damage modifier script it will be 50, because initiative modifier is not applied yet.
The limitation described above can be avoided by getting unit instance via getScenario:getUnit(unit.id).
But you have to be very careful using this approach, as you can easily fall into a deadloop.
Lets see a bad example where we created a modifier that grants bonus armor and regen depending on each other:
function getArmor(unit, prev)
local finalUnit = getScenario():getUnit(unit.id)
return prev + finalUnit.impl.regen / 5
end
function getRegen(unit, prev)
local finalUnit = getScenario():getUnit(unit.id)
return prev + finalUnit.impl.armor / 10
endOr it can be two different modifiers, does not matter:
-- MyBonusArmorMod.lua
function getArmor(unit, prev)
local finalUnit = getScenario():getUnit(unit.id)
return prev + finalUnit.impl.regen / 5
endand
-- MyBonusRegenMod.lua
function getRegen(unit, prev)
local finalUnit = getScenario():getUnit(unit.id)
return prev + finalUnit.impl.armor / 10
endWhen this modifier(s) applied to a unit, we are getting circular dependence here: final armor depends on final regen while final regen depends on final armor.
Imagine what happens when the game tries to get unit armor:
It calls getArmor that calls getRegen that calls getArmor that calls getRegen that calls getArmor that calls getRegen that calls getArmor...and so on until your game hang or crash to desktop.
As a good example, you could refer to a third stat, thus avoiding deadloop condition:
-- MyBonusArmorMod.lua
function getArmor(unit, prev)
local finalUnit = getScenario():getUnit(unit.id)
return prev + finalUnit.impl.regen / 5
endand
-- MyBonusRegenMod.lua
function getRegen(unit, prev)
local finalUnit = getScenario():getUnit(unit.id)
return prev + finalUnit.impl.level / 10
endThis way, regen depends on level, and armor depends on regen and there is no circular dependence in this case.
Remember that this is subject for all modifiers that can potentially happen to be applied to the same unit.