Repository navigation
CustomMonsters Skills
A custom monster can use a player skill. The server decides when and whom it hits; the client draws part of it by itself, and a script draws the rest.
In the server's MonsterList.xml, AttackType = 100 + the skill number. The monster
then casts that skill on a random one attack in five - the same chance for every monster -
and attacks normally the rest of the time. The Pharaoh (2499) has AttackType="2123",
Raining Arrow.
The cast hits the players within 6 tiles of the monster, never other monsters.
In a script, m.skill is the skill number while the monster casts it, and 0 on its
ordinary attacks.
A skill is drawn in two halves:
- On the targets - the client's own, no script needed. A custom monster's cast goes out with the packet a player's multi-target skill sends, and the client draws these from it: 241-243, 246, 247, 283, 288, 293, 294, 727, 1501, 2004-2006, 2023, 2028-2030, 2040, 2041, 2044-2046, 2059, 2060, 2069-2084, 2091-2096, 2101-2104, 2106, 2107, 2109. A strengthened skill counts as its base one. Any other skill shows nothing on its targets.
- Round the caster - always the script's. The client draws that half for players only.
| Skill | Round the caster (script) | On the targets (client) |
|---|---|---|
| 14 Inferno | ring of fire, the client's effect found by its file - Helpers.ClientModel(R, "inferno")
|
nothing |
| 2023 Raining Arrow | the client's own rain on its target, its id worked out from neighbouring files | the blasts |
SkillEffects.lua ships with the sample scripts and holds the caster half, one entry
per skill in SkillEffects.Caster. Call it from OnAttack; it draws once an attack, on
the attack's first frame (m.attackTime == 1), the only frame every attack reaches:
local SkillEffects = require("SkillEffects")
Monster.OnAttack(function(o, m)
SkillEffects.OnAttack(o, m)
end)An entry added to SkillEffects.Caster is drawn by every monster script that calls
SkillEffects.OnAttack:
-- Inferno: the ring of fire round the caster - the client's own effect, found by its file.
[14] = function(o, m)
local inferno = Helpers.ClientModel(R, "inferno")
if inferno then
local x, y, z = o:GetPosition()
o:SpawnEffect(inferno, 0, 0, x, y, z)
end
end,m:SpawnSkillEffect(type, subType, scale, x, y, z) creates an effect the way the client does
for a skill's caster: owned by the monster and carrying its key (subType 0, scale 1 and the
monster's position when left out). An effect that looks its caster up by that key shows nothing
when spawned any other way.
m:TargetPosition() is where the monster's current target stands - the one the client itself
uses for attack effects - or nothing while it has none. SkillEffects lays Raining Arrow's ring
there and starts its rain on it, so the arrows fall on the target rather than on the monster.
Most of the client's effects can be named by their model file with Helpers.ClientModel. A few
are built in its code and load no file at all - Raining Arrow's rain is one - so nothing
identifies them but a number, and that number moves with every client version. Scripts use none
of them: what the client has no file for, they draw themselves out of the pieces that are
files.
Raining Arrow is the worked example. The rain itself is code-built, but the ring it lays on the
ground is six ordinary models and what falls is Data\Skill\Arrow.bmd, so SkillEffects lays
the ring by file and shoots the arrows one by one:
local RAIN =
{
arrows = 11, spread = 200, height = 200,
arrow = "arrow",
rings =
{
{ model = "elfRing05", subType = 0, scale = 0.5 },
{ model = "elfRing04", subType = 0, scale = 0.25 },
-- the other four pieces
},
}m:ShootArrow(model, skill, x, y, z, r, g, b, angleX, angleY, angleZ) shoots one arrow from a
spot: model is what falls and skill decides what the client draws where it lands. Colour and
angles are yours, so a single call covers a rain, a volley or one shot; left out, the colour is
the monster's own light and the angles are the drop (90, a scatter of -10..10, 180).
angleX is worth knowing about: 90 drops the arrow, while 270 sends it climbing until its life
runs out, so it never lands and never leaves a mark. The client's own rain does put 270 on the
source object it hands to CreateArrow, which works the direction out for itself - an angle on
the effect is not the same thing.
An arrow needs somebody to fly at or it dies in the air instead of landing, and it takes that
from the server: the packet that tells the client who a multi-target skill hit is kept per
monster, and m:ShootArrow gives the arrow the character out of it. So an arrow lands on
somebody the server really damaged, which a guess of your own - everybody within so many units -
would get wrong. A skill the server sends no such packet for still shoots, but its arrows have
nothing to fall towards.
SkillEffects builds Raining Arrow this way: one arrow a 40 ms step, 11 of them, from 200 above
the ground, the spot wandering by up to 200 with each.
The client's own arrow skills - the ones it draws without a script - take their arrows from
the bow or crossbow in the caster's right hand and shoot none without one. m:ShootArrow does
not, since the script names the model. Give an archer monster its bow anyway in
CustomMonster.xml - <Weapon Slot="0" Cat="4" Index="0" /> is the Short Bow and its plain
arrow; the Pharaoh has that one.
-
Monster scripts - the handlers,
OnAttackamong them - Adding a monster - config, models, sounds
-
Objects and the sandbox -
o:SpawnEffectand the shared files
MuLua Scripting Plugin | Home
🖼️ Lua UI
- File map
- Creating windows
- Client and server
- Designing a window
- Protocol
- Scrolling and scrollbars
- Controls
- Custom text
- Text input
- Debug console
- Bitmap Slicer
- Encrypting scripts
Maps
Monsters
Shared
Version 1.14 | 2026-09-09