Mob script structure
Mob scripts live in the mobs/ subdirectory of a zone folder. A mob script file returns a local entity table with handler functions and configuration tables.
-- scripts/zones/East_Ronfaure/mobs/Bigmouth_Billy.lua
local ID = zones[xi.zone.EAST_RONFAURE]
---@type TMobEntity
local entity = {}
-- Lottery/placeholder list: { [phId] = nmId, ... }
entity.phList =
{
[ID.mob.BIGMOUTH_BILLY - 2] = ID.mob.BIGMOUTH_BILLY,
[ID.mob.BIGMOUTH_BILLY - 1] = ID.mob.BIGMOUTH_BILLY,
}
-- Possible NM spawn positions
entity.spawnPoints =
{
{ x = 453.625, y = -18.436, z = -127.048 },
{ x = 403.967, y = -36.822, z = -16.285 },
-- ...
}
-- Called when the mob dies
entity.onMobDeath = function(mob, player, optParams)
xi.hunts.checkHunt(mob, player, 151)
end
return entity
Mob handler reference
| Handler | Signature | When called |
|---|
onMobSpawn | (mob) | When the mob spawns |
onMobDeath | (mob, player, optParams) | When the mob dies; player is the killer or last claimant |
onMobDespawn | (mob) | When the mob despawns (after death timer) |
onMobEngaged | (mob, target) | When the mob first enters combat |
onMobDisengage | (mob) | When the mob loses its target |
onMobFight | (mob, target) | Called repeatedly while the mob is in combat |
onMobRoam | (mob) | Called while the mob is roaming |
onMobRoamAction | (mob) | Called for roaming mob actions |
onAdditionalEffect | (attacker, target, attack, damage) | Additional hit effect proc |
onAdditionalEffectMiss | (attacker, target, attack) | Additional effect on a miss |
Placeholder (PH) and lottery NM system
The PH system uses entity.phList and xi.mob.phOnDespawn to spawn lottery NMs when a placeholder mob dies.
-- Maps each placeholder mob ID to the NM it can spawn
entity.phList =
{
[phMobId_1] = nmMobId,
[phMobId_2] = nmMobId,
}
Hooking phOnDespawn
In the placeholder mob’s own script or in Zone.lua, call xi.mob.phOnDespawn from the placeholder’s onDespawn:
entity.onMobDespawn = function(mob)
xi.mob.phOnDespawn(mob, ID.mob.SOME_NM, 10, 120, {
-- chance = 10 (10% pop chance)
-- cooldown = 120 (120-second NM respawn cooldown)
-- immediate = false (wait for next PH pop time)
-- dayOnly = false
-- nightOnly = false
})
end
NM spawn point randomization
When entity.spawnPoints is defined, the framework calls xi.mob.updateNMSpawnPoint automatically on the NM’s pop to choose a random position from the table.
You can also call it manually or supply an override table:
-- Use spawn points defined in the NM's entity.spawnPoints
xi.mob.updateNMSpawnPoint(mob)
-- Supply spawn points directly
xi.mob.updateNMSpawnPoint(mob, {
{ x = 100.0, y = -10.0, z = 200.0 },
{ x = 110.0, y = -10.0, z = 210.0 },
})
Global mob death hook
scripts/globals/mobs.lua defines xi.mob.onMobDeathEx, which is called by the core for every mob death:
xi.mob.onMobDeathEx = function(mob, player, isKiller, isWeaponSkillKill)
-- extend here for server-wide death handling
end
Mob entity API
The mob object is a CBaseEntity (same as players and NPCs) with additional mob-specific methods:
mob:getID() -- entity ID
mob:getName() -- mob name string
mob:getZoneID() -- zone ID
mob:getZoneName() -- zone name string
mob:isSpawned() -- true if currently alive/spawned
mob:isAlive() -- true if HP > 0
mob:spawn() -- force-spawn the mob
mob:getRespawnTime() -- time until next respawn
mob:setSpawn(x, y, z, rot) -- set spawn position
mob:getXPos() / :getYPos() / :getZPos()
mob:addEnmity(target, ce, ve) -- add enmity toward target
mob:updateClaim(player) -- claim mob to player
mob:setLocalVar(name, value) -- per-mob local variable
mob:getLocalVar(name) -- get per-mob local variable
mob:addListener(event, id, fn) -- attach event listener
mob:removeListener(id) -- remove event listener
mob:getMod(modId) -- get a modifier value
mob:setMod(modId, value) -- set a modifier value
mob:hasStatusEffect(effectId) -- check for status effect
mob:lookAt(pos) -- face toward a position
mob:getHP() / :getHPP() -- HP / HP percentage
mob:takeDamage(dmg, source, atkType, dmgType)
Mob skills
Mob skill scripts live in scripts/actions/mobskills/. They are linked to mobs through the SQL mob_skill_lists table rather than in the Lua file itself.
-- scripts/actions/mobskills/someskill.lua
require('scripts/globals/mobskills')
local mobskillObject = {}
mobskillObject.id = 456
mobskillObject.name = 'Some Skill'
-- Return 0 to allow the skill, non-zero to cancel
mobskillObject.onMobSkillCheck = function(target, mob, skill)
return 0
end
-- Execute the skill
mobskillObject.onMobWeaponskill = function(target, mob, skill)
local numhits = 1
local dmgmod = 1.5
local info = xi.mobskills.mobPhysicalMove(
mob, target, skill, numhits, dmgmod,
xi.mobskills.physicalTpBonus.NO_EFFECT
)
local dmg = xi.mobskills.mobFinalAdjustments(
info.dmg, mob, skill, target,
xi.attackType.PHYSICAL, xi.damageType.SLASHING,
xi.mobskills.shadowBehavior.NUMSHADOWS_1
)
target:takeDamage(dmg, mob, xi.attackType.PHYSICAL, xi.damageType.SLASHING)
return dmg
end
return mobskillObject
SQL relationship
Mob behavior is driven by two key SQL tables:
| Table | Purpose |
|---|
mob_pools | Defines mob stats, drop rates, skill lists, and elemental resistances |
mob_spawn_points | Defines where and when mobs spawn in each zone |
mob_skill_lists | Maps mob pool IDs to the set of TP moves they can use |
mob_droplist | Drop table entries per mob pool |
The Lua entity table in mobs/ supplements and overrides these SQL values for named mobs and NMs.
For placeholder NMs, the NM’s mob ID is always the last entry in the phList value (not a key). Placeholder IDs are the keys.