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.lua
doppelganger.lua
transformSelf.lua
transformOther.lua
drainLevel.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 + 1
Represents 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.typeIndex
Represents unit modifier. Modifiers wrap unit implementation.
Methods:
Returns modifier id. MODIF_ID
value from Gmodif.dbf
.
modifier.id
Represents 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.hpMax
Returns unit id. This is different to id of unit implementation. The value is unique for every unit on scenario map.
unit.id
Returns unit's current implementation. Current implementation describes unit stats according to its levels and possible transformations applied during battle.
unit.impl
Returns unit's base implementation. Base implementation is a record in GUnits.dbf that describes unit basic stats.
unit.baseImpl
Returns 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.leveledImpl
Returns 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.original
Returns 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.originalModifiers
Represents 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
id
corresponds toUNIT_ID
fromGUnits.dbf
.
Generated
, equalsGlobal
plus level upgrades fromGDynUpgr.dbf
(if any);- Returned by unit.leveledImpl or impl.generated;
- Its
id
is different toid
of inheritedGlobal
implementation; - If unit has no level upgrades,
unit.leveledImpl
/impl.generated
equalsunit.baseImpl
/impl.global
.
Modified
, equalsGenerated
plus applied modifiers fromGmodif.dbf
(if any).- Returned by unit.impl;
- Its
id
equals toid
of inheritedGenerated
implementation; - If unit has no modifiers,
unit.impl
equalsunit.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.leadership
Returns unit implementation id. UNIT_ID
value from GUnits.dbf
.
impl.id
Returns base unit implementation. BASE_UNIT
value from GUnits.dbf
.
impl.base
Returns unit type.
impl.type
Returns leader type (or -1 if unit is not a leader).
impl.leaderType
Returns 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.global
Returns generated unit implementation.
Equals global
plus upgrades from GDynUpgr.dbf
according to unit's level.
Same as unit.leveledImpl
.
impl.generated
Returns array of applied modifiers.
impl.modifiers
Returns 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.id
Returns group as array of 6 unit slots.
group.slots
Returns group units.
group.units
Returns 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.id
Returns player race.
player.race
Returns player lord.
player.lord
Returns player bank.
player.bank
Returns true if player is human (not AI).
player.human
Returns true if player is always AI.
player.alwaysAi
Returns 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
end
Represents 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.id
Returns stack position as a point.
stack.position
Returns player that owns the stack. Neutral stacks are owned by neutral player.
stack.owner
Returns fort that this stack is visiting or nil
if none.
stack.inside
Returns stack units as a group.
stack.group
Returns stack leader unit.
stack.leader
Returns stack subrace.
stack.subrace
Returns array of inventory items. This includes equipped items.
stack.inventory
Returns 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.battlesWon
Represents 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.id
Returns fort position as a point.
fort.position
Returns player that owns the fort. Neutral forts are owned by neutral player.
fort.owner
Returns fort units as a group.
fort.group
Returns visitor stack, or nil
if none.
fort.visitor
Returns fort subrace.
fort.subrace
Returns array of inventory items.
fort.inventory
Returns true if fort is a capital city.
fort.capital
Returns fort tier (level). Tiers are in range [1 : 6]. Tier 6 corresponds to the capital city.
fort.tier
Represents 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.id
Returns ruin position as a point.
ruin.position
Returns player that looted the ruin, or nil
if none.
ruin.looter
Returns ruin units as a group.
ruin.group
Returns item reward for looting the ruin.
ruin.item
Returns cash reward for looting the ruin.
ruin.cash
Represents 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.id
Returns copy of rod position as a point.
rod.position
Returns player that planted the rod.
rod.owner
Represents 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.xpNext
Represents location object in scenario.
Methods:
Returns location identifier.
location.id
Returns copy of location position as a point.
location.position
Returns radius of location
location.radius
Represents scenario variable used by events.
Methods:
-- Returns variable name
variable.name
-- Returns variable value
variable.value
Stores 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
end
Represents map tile.
Methods:
Returns tile terrain type.
tile.terrain
Returns tile ground type.
tile.ground
Represents 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
end
Returns ScenarioVariables. If scenario has no variables defined, returns nil
.
local variables = scenario.variables
if (variables == nil) then
return
end
Searches for Tile by pair of coordinates or Point, returns nil
if not found.
local tile = scenario:getTile(3, 5)
if (tile == nil) then
return
end
Searches for stack by:
Returns nil
if not found.
local stack = scenario:getStack(10, 15)
if (stack == nil) then
return
end
Searches for fort by:
Returns nil
if not found.
local fort = scenario:getFort(10, 15)
if (fort == nil) then
return
end
Searches for ruin by:
Returns nil
if not found.
local ruin = scenario:getRuin(10, 15)
if (ruin == nil) then
return
end
Searches for rod by:
Returns nil
if not found.
local rod = scenario:getRod(10, 15)
if (rod == nil) then
return
end
Searches for player by id string or id, returns nil
if not found.
local player = scenario:getPlayer('S143PL0000')
if (player == nil) then
return
end
Searches for unit by id string or unit id, returns nil
if not found.
local unit = scenario:getUnit('S143UN0001')
if (unit == nil) then
return
end
Searches 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
end
Note 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
end
Note 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
end
Note 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.day
Returns scenario map size.
scenario.size
Returns 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
end
Represents attack of Unit implementation.
Methods:
Returns attack id. This is different for every dynamic upgrade unit gets.
attack.id
Returns attack type.
attack.type
Returns attack source.
attack.source
Returns attack reach.
attack.reach
Returns 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.damageSplit
Represents game currency, mana and gold united.
Methods:
Creates new currency from existing object.
Currency.new(existing)
Returns or sets amount of infernal mana.
currency.infernalMana
Returns or sets amount of life mana.
currency.lifeMana
Returns or sets amount of death mana.
currency.deathMana
Returns or sets amount of runic mana.
currency.runicMana
Returns or sets amount of grove mana.
currency.groveMana
Returns or sets amount of gold.
currency.gold
Represents base item of any type (described in GItem.dbf).
Methods:
Returns item id. ITEM_ID
value from GItem.dbf
.
base.id
Returns item type.
base.type
Returns item value.
base.value
Returns related Unit implementation. For instance: in case of "Angel Orb", Angel unit implementation is returned.
base.unitImpl
Returns 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.attack
Represents 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.id
Returns Item base.
item.base
Returns item sell value, it accounts global sell ratio and used talisman charges (if applicable).
item.sellValue
Represents 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
end
Returns current round in battle. Round counting starts from 1, but there is a special round 0 when units with 'Doppelganger' attacks present.
battle.currentRound
Returns true if autobattle mode is turned on.
battle.autoBattle
Returns player that started battle.
battle.attackerPlayer
Returns player that was attacked.
battle.defenderPlayer
Returns stack that started battle. Only stacks can initiate battles.
battle.attacker
Returns 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.defender
doppelganger
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
end
unit
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)
end
attacker
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);
end
attacker
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);
end
summoner
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)
end
See 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_SCRIPT
inLAttR.dbf
); - Attack targets are targets that will be affected by attack (specified as
ATT_SCRIPT
inLAttR.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:
attacker
is the unit slot of the attacker unit;selected
is the unit slot of the unit that was selected (clicked).selected.position == -1
andselected.unit == nil
if this is selection script (no target is clicked yet);allies
are unit slots of all the allies on the battlefield (excluding the attacker);targets
are 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;targetsAreAllies
specified whether targets are allies;item
specifies an item (orb or talisman) used to perform the attack, ornil
if no item is used;battle
specifies an information about current battle;isMarking
specified 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_TARGTS
flag inLAttR.dbf
. Alwaysfalse
if 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 == count
Modifiers 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
end
unit
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
50
initiative; - 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
end
Or 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
end
and
-- MyBonusRegenMod.lua
function getRegen(unit, prev)
local finalUnit = getScenario():getUnit(unit.id)
return prev + finalUnit.impl.armor / 10
end
When 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
end
and
-- MyBonusRegenMod.lua
function getRegen(unit, prev)
local finalUnit = getScenario():getUnit(unit.id)
return prev + finalUnit.impl.level / 10
end
This 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.