Mod API Reference:Client Visual: Difference between revisions
Created page with "'''Client_Visual''' is a client-side mod hook that lets a mod draw its own shapes on the map for the events it creates. Shapes are made of colored triangles. They can be a single static shape, or animated by giving several keyframes that the game tweens between when a player watches the turn. == When it runs == The client calls <code>Client_Visual</code> each time it needs to show a GameOrderEvent that was created by y..." |
No edit summary |
||
| (One intermediate revision by the same user not shown) | |||
| Line 1: | Line 1: | ||
'''Client_Visual''' is a client-side [[Mod Hooks|mod hook]] that lets a mod draw its own shapes on the map for the events it creates. Shapes are made of colored triangles. They can be a single static shape, or animated by giving several keyframes that the game tweens between when a player watches the turn. | '''Client_Visual''' is a client-side [[Mod Hooks|mod hook]] that lets a mod draw its own shapes on the map for the events it creates. Shapes are made of colored triangles. They can be a single static shape, or animated by giving several keyframes that the game tweens between when a player watches the turn. | ||
Note to website users: If you play in 2d mode, this hook will never be called. Custom visuals are not supported in the legacy 2d website mode. It is fully supported in the website's 3d mode, as well as all other clients (iOS, Android, standalone, etc.) | |||
Note this mod hook was added in version 6.06.0. Mods don't need to call "IsVersionOrHigher", since the hook will simply not be called in earlier versions. | |||
== When it runs == | == When it runs == | ||
The client calls <code>Client_Visual</code> each time it needs to show a [[Mod API Reference:GameOrderEvent|GameOrderEvent]] that was created by your mod. Events created inside your mod's server hooks (for example with <code>addNewOrder(WL.GameOrderEvent.Create(...))</code>) are tagged with your mod automatically. The hook is not called for any other order type. | The client calls <code>Client_Visual</code> each time it needs to show a [[Mod API Reference:GameOrderEvent|GameOrderEvent]] that was created by your mod. Events created inside your mod's server hooks (for example with <code>addNewOrder(WL.GameOrderEvent.Create(...))</code>) are tagged with your mod automatically. The hook is currently not called for any other order type, but it may be in the future so you should still check "order" properly to ensure it's the one you are looking for. | ||
The visual appears together with the event's other effects: | The visual appears together with the event's other effects: | ||
Latest revision as of 20:38, 3 October 2026
Client_Visual is a client-side mod hook that lets a mod draw its own shapes on the map for the events it creates. Shapes are made of colored triangles. They can be a single static shape, or animated by giving several keyframes that the game tweens between when a player watches the turn.
Note to website users: If you play in 2d mode, this hook will never be called. Custom visuals are not supported in the legacy 2d website mode. It is fully supported in the website's 3d mode, as well as all other clients (iOS, Android, standalone, etc.)
Note this mod hook was added in version 6.06.0. Mods don't need to call "IsVersionOrHigher", since the hook will simply not be called in earlier versions.
When it runs
The client calls Client_Visual each time it needs to show a GameOrderEvent that was created by your mod. Events created inside your mod's server hooks (for example with addNewOrder(WL.GameOrderEvent.Create(...))) are tagged with your mod automatically. The hook is currently not called for any other order type, but it may be in the future so you should still check "order" properly to ensure it's the one you are looking for.
The visual appears together with the event's other effects:
- Watching the turn: when the event is played back, an animated visual plays from start to finish. The game then moves on to the next order.
- Viewing the event at any other time (clicking it in the orders list, stepping through history, or "Show All"): the visual is drawn in its rest pose (see SetRestTime). When the event is shown faded, the visual is drawn at half opacity.
Because the hook runs every time the event is displayed, it should be quick and should always produce the same result for the same order. Don't rely on state left over from earlier calls.
Visuals are not drawn when the web client is showing the map in 2D mode.
Signature
Put the function in a file named Client_Visual.lua:
function Client_Visual(game, order, visual)
-- describe the visual by calling functions on visual
end
| Argument | Type | Description |
|---|---|---|
game |
ClientGame | The same client game object that other client hooks receive. Use it to read the map, settings, and standings. |
order |
GameOrderEvent | The event being displayed. Read-only. Use its fields (such as Message, TerritoryModifications or JumpToActionSpotOpt) to decide what to draw and where.
|
visual |
table | The object you describe the visual with. See Functions below. |
If your hook doesn't call SetVertices or SetFrames, no visual is drawn and the event displays as it normally would. That's the right thing to do for events you don't want to draw.
Functions
Every function on visual returns visual, so calls can be chained. Call them with a dot (visual.SetDuration(500)), not a colon. A colon passes visual as the first argument, which causes an error.
The order of the calls doesn't matter. The game collects everything after your hook returns, then checks it as a whole.
SetVertices
visual.SetVertices(vertices)
Sets the corner points of a visual that doesn't animate. vertices is a list of points. Each point is {x, y} or {x, y, z}.
- Coordinates are in map units, the same units as the map's SVG and the territory positions. x increases to the right and y increases downward.
- z is optional and defaults to 0. Positive z lifts the point toward the viewer. It only makes a difference when the map is viewed in 3D.
- If an anchor is set (see SetAnchorTerritory and SetAnchorPoint), coordinates are relative to the anchor. Otherwise they are absolute map coordinates.
- At most 1024 vertices. Each coordinate must be between -100000 and 100000.
If you also call SetFrames, SetFrames wins and SetVertices is ignored. A warning is written to the mod log.
SetTriangles
visual.SetTriangles(indices)
Sets which vertices form triangles. indices is a flat list of vertex numbers, three per triangle. Numbering starts at 1, like the rest of Lua. For example, {1,2,3, 1,3,4} makes a rectangle out of four corners.
- The number of indices must be a multiple of 3, and every index must be between 1 and the number of vertices.
- At most 2048 triangles.
- Winding order doesn't matter, because both sides of every triangle are drawn.
- Optional. If you don't call it, every three vertices in a row form one triangle (vertices 1–3, then 4–6, and so on). The vertex count must then be a multiple of 3.
The same triangles are used for every frame of an animation.
SetColors
visual.SetColors(colors)
Sets the colors of the vertices. You can pass either:
- one color, used for every vertex, or
- a list of colors, one per vertex in the same order as the vertices. Colors blend smoothly across each triangle.
A color can be written as:
| Form | Example | Notes |
|---|---|---|
"#RRGGBB" |
"#FF0000" |
Fully opaque. |
"#AARRGGBB" |
"#80FF0000" |
Alpha comes first. 80 is about 50% opacity.
|
{r, g, b} |
{255, 0, 0} |
Each value 0–255. Fully opaque. |
{r, g, b, a} |
{255, 0, 0, 128} |
Each value 0–255. Alpha comes last in this form. |
Optional. Vertices are white if no colors are given. In an animation, SetColors supplies the colors for the first frame if that frame doesn't have its own (see SetFrames).
SetFrames
visual.SetFrames(frames)
Describes an animated visual as a list of keyframes. The game tweens the position and color of every vertex from one keyframe to the next. Each frame is a table with these keys:
| Key | Required | Description |
|---|---|---|
Vertices |
Yes | The vertices for this pose, in the same format as SetVertices. Every frame must have the same number of vertices. Vertex 3 in one frame moves to vertex 3 in the next. |
Colors |
No | Colors for this pose, in the same format as SetColors. If left out, the frame uses the previous frame's colors. The first frame falls back to SetColors, then to white.
|
t |
No | When this pose is reached, as a fraction of the duration from 0 (start) to 1 (end). Each frame's t must not be less than the frame before it. Either give every frame a t or none of them. If none have one, the frames are spread evenly from 0 to 1.
|
Ease |
No | How the tween that ends at this frame is paced. One of Linear (the default), EaseIn (starts slow), EaseOut (ends slow), EaseInOut (slow at both ends) or Step (holds the previous pose, then jumps to this one when its t is reached). Not case-sensitive. The first frame's ease is ignored.
|
- At most 32 frames.
- Before the first frame's
t, the visual holds the first pose. After the last frame'st, it holds the last pose. - A visual with only one frame doesn't animate. It behaves the same as
SetVertices.
SetDuration
visual.SetDuration(milliseconds)
How long the animation lasts when the turn is watched. Defaults to 1000 (one second). Values are limited to 1–10000. Out-of-range values are clamped and a warning is written to the mod log. The player's playback speed setting scales the actual time.
Only matters for animated visuals.
SetRestTime
visual.SetRestTime(t)
Picks which moment of the animation is shown when the visual is not being played: when the event is clicked, viewed in history, or shown with "Show All". It's also the pose the visual returns to after playback finishes or is interrupted. t is from 0 to 1, where 0 is the first pose and 1 is the last. Defaults to 1, the final pose.
For example, an explosion that grows and then fades to nothing should use a rest time in the middle, such as 0.4. Otherwise the event would look empty when it's clicked.
SetAnchorTerritory
visual.SetAnchorTerritory(territoryID)
Makes vertex coordinates relative to the middle of a territory, so {0, 0} is that territory's center. This is the easiest way to place a visual on a territory without knowing its map coordinates.
If the territory doesn't exist on the map, no visual is drawn.
SetAnchorPoint
visual.SetAnchorPoint(x, y)
Makes vertex coordinates relative to a point on the map, given in map units.
SetAnchorTerritory and SetAnchorPoint replace each other: whichever you call last is used. If you call neither, vertex coordinates are absolute map coordinates.
Limits
| Limit | Value |
|---|---|
| Vertices | 1024 |
| Triangles | 2048 |
| Frames | 32 |
| Duration | 1 to 10000 ms |
| Coordinates | -100000 to 100000 |
Errors
There are two kinds of problems:
- Wrong argument types, such as passing a string to
SetVerticesor a malformed color. These raise a normal Lua error when the function is called, the same as any other mod error. - Problems that depend on the whole visual, such as frames with different vertex counts, a triangle index out of range, or an anchor territory that doesn't exist. These don't raise an error, because the hook runs every time the event is shown. Instead, no visual is drawn and a message starting with
Client_Visual:is written to the mod's log. If your visual doesn't appear, check the log first.
Examples
A basic rectangle
A semi-transparent red rectangle, 40 by 20 map units, centered on the first territory the event modifies:
function Client_Visual(game, order, visual)
local terrMods = order.TerritoryModifications
if #terrMods == 0 then
return -- nothing to anchor to, so don't draw anything
end
visual.SetAnchorTerritory(terrMods[1].TerritoryID)
.SetVertices({ {-20, -10}, {20, -10}, {20, 10}, {-20, 10} })
.SetTriangles({ 1,2,3, 1,3,4 })
.SetColors("#B0FF0000")
end
The four vertices are the corners, going clockwise from the top left. SetTriangles splits the rectangle into two triangles. You could leave out SetTriangles and list six vertices instead (two triangles of three), but sharing corners is shorter.
An animated rectangle
A bar that grows out of the territory's center to the right, turns from yellow to red, then fades away:
function Client_Visual(game, order, visual)
local terrMods = order.TerritoryModifications
if #terrMods == 0 then
return
end
-- A rectangle from x=0 to x=len, 10 units tall
local function bar(len)
return { {0, -5}, {len, -5}, {len, 5}, {0, 5} }
end
visual.SetAnchorTerritory(terrMods[1].TerritoryID)
.SetTriangles({ 1,2,3, 1,3,4 })
.SetDuration(1500)
.SetRestTime(0.6) -- when not playing, show it fully grown and red
.SetFrames({
{ t = 0, Vertices = bar(0), Colors = "#FFFFFF00" },
{ t = 0.6, Vertices = bar(80), Colors = "#FFFF0000", Ease = "EaseOut" },
{ t = 1, Vertices = bar(80), Colors = "#00FF0000", Ease = "EaseIn" },
})
end
How it plays:
- From
t = 0to0.6(the first 900 ms), the bar grows from zero to 80 units long and turns from yellow to red.EaseOutmakes it slow down as it reaches full length. - From
0.6to1, it stays the same size while its alpha drops to00, so it fades out.EaseInmakes the fade start gently. SetRestTime(0.6)means a player who clicks the event later sees the full red bar. With the default rest time of 1, they'd see the faded-out final pose, which is invisible.
A gradient triangle at a fixed point
Per-vertex colors and an anchor point instead of a territory. No SetTriangles call is needed, because three vertices are one triangle:
function Client_Visual(game, order, visual)
visual.SetAnchorPoint(500, 300)
.SetVertices({ {0, -30}, {26, 15}, {-26, 15} })
.SetColors({ "#FF0000", "#00FF00", "#0000FF" })
end
Creating the event on the server
Client_Visual only draws events that your mod created. For example, in Server_AdvanceTurn_End.lua:
function Server_AdvanceTurn_End(game, addNewOrder)
local terrID = 1 -- pick whichever territory your mod is affecting
local terrMod = WL.TerritoryModification.Create(terrID)
terrMod.AddArmies = 2
addNewOrder(WL.GameOrderEvent.Create(WL.PlayerID.Neutral, "Reinforcements arrive", nil, { terrMod }))
end
If your mod creates several kinds of events, check order.Message (or another field) in Client_Visual to decide which visual to draw. Return without calling anything for events that shouldn't have one.