session:meter: The HUD Meter Bars
The bars in one character’s HUD meter slot (health, stamina, energy, whatever else the server puts there), read through its session. session:meter() is that character’s meter slot.
local session = hafen.session():current() -- the character on screen
local health_meter = session and session:meter():find("hp")
local fill = health_meter and health_meter:segment():list()[1]
if fill and ((fill:value() or 1) < 0.3) then hafen.log():write("low health!") end
| Method |
Returns |
Permission |
Description |
session:meter():list(filter) |
Meter[] |
Unprotected |
Every bar in that HUD, a 1-based array in layout order. |
session:meter():count(filter) |
number |
Unprotected |
How many match. |
session:meter():find(filter) |
Meter \| nil |
Unprotected |
The first meter that matches. |
| Rule |
Detail |
filter |
A string filter is a substring match against the resource name, not trimmed. A miss is nil. |
No :get |
A meter has no key, only a server-published resource name several bars can share. session:meter():get("hp") raises: session:meter() has no verb 'get' — a meter has no key, only a server-published background resource name several bars can share: session:meter():find(needle) is the search and session:meter():list()[n] takes a position. |
| Whose bars |
hafen.session():get("alt"):meter():find("hp") answers for that character while you watch another. |
| One object |
session:meter() is the same object every call, minted once per session: a draw callback reading it at 60 fps allocates nothing. A session the client no longer holds answers an empty array. |
| Interned per addon |
session:meter():find("hp") == session:meter():list()[1] and seen[meter] = true work. meter:segment() and the segments in it are not: both are views off the bar (a segment). |
| Live |
A Meter wraps the meter widget and re-reads it on every call (snapshots vs handles). It carries its own character, so meter:exists() and meter:index() are about the slot it stands in. |
| No write side |
Meters are server-pushed presentation. Swallowing their updates through an IMeter message filter freezes the bars client-side. The server still knows the real values. |
No fixed hp, stamina and energy triple
The meter slot takes any number of bars, and a bar is identified by the resource its background is drawn from, which the server publishes. "hp" is a substring that identifies a bar on this server, not a key the API knows. Read the real names with :res():
for _, meter in ipairs(hafen.session():current():meter():list()) do hafen.log():write(tostring(meter:res())) end
:res() |
What it is |
When present |
gfx/hud/meter/hp |
Health |
Always. |
gfx/hud/meter/stam |
Stamina |
Always. |
gfx/hud/meter/nrj |
Energy |
Always. |
gfx/hud/meter/häst |
The horse’s own bar |
While mounted. |
gfx/hud/meter/mount |
The mount bar |
While mounted. |
The table is observed on this server, not a contract. Re-derive it with :res(). One name is non-ASCII, so use an ASCII needle ("st", "mount", "hp") or compare :res() yourself rather than typing an accented literal.
Read
| Method |
Returns |
Permission |
Description |
meter:res() |
string \| nil |
Unprotected |
The background resource name, the identity. |
meter:index() |
number \| nil |
Unprotected |
Its 1-based position in its HUD. nil once the meter is gone. |
meter:segment() |
collection |
Unprotected |
The bar as Segment objects in draw order. Never nil, may be empty. |
meter:widget() |
Widget \| nil |
Unprotected |
The widget that draws it: the crossing back into the tree. |
meter:exists() |
boolean |
Unprotected |
Whether this meter is still in its HUD slot. Always answers. |
meter:info() |
Meter \| nil |
Unprotected |
A plain-table snapshot, for logging and serialising. |
| Rule |
Detail |
| Guarded reads |
Every read may answer nil: a new meter is nameless at first and its segments stream in after it appears. Only :exists() always answers. Nothing throws once you hold a Meter. |
| Multi-segment bars |
A whole-bar reading is the first segment, meter:segment():list()[1]. The vital bars use one segment each. A bar with more shows them all in :segment():list(). |
| Compacted |
An empty slot the server has published nothing into is not a segment. :segment():list() and the segments array in meter:info() are gap-free, so ipairs walks every entry. A segment’s place is its place in that list. |
A segment
| Method |
Returns |
Permission |
Description |
segment:index() |
number |
Unprotected |
Its 1-based place in meter:segment():list(): list()[n]:index() is n. |
segment:value() |
number \| nil |
Unprotected |
Its fill fraction, 0..1. |
segment:color() |
colour \| nil |
Unprotected |
Its colour, {r=, g=, b=, a=}. |
segment:info() |
MeterSegment \| nil |
Unprotected |
A plain-table snapshot. nil once the band is gone. |
| Rule |
Detail |
| The bar’s fill |
meter:segment():list()[1]:value(), and it says which segment it is. There is no meter:value(): a whole-bar name over segment one is wrong the first time a server publishes a split bar. |
No == |
A band carries no key: it is a place in a bar the server rewrites whole. So meter:segment() mints a fresh collection and fresh Segments per call, and meter:segment() ~= meter:segment(). The handle is live and re-reads its band. segment:index() identifies a band, and the Meter above it is the interned thing to key a table by. |
| Fractions only |
There are no absolute hp, stamina or energy numbers and no hunger figure in the client. The one absolute reading is FEP: session:char():food(). |
Events
| Event |
Payload |
Fires |
MeterAdded |
Meter |
A bar appears in the HUD slot. |
MeterRemoved |
Meter |
A bar goes away. The object still reads and :exists() is false. |
MeterChanged |
Meter |
A bar’s value or colour changes. |
| Rule |
Detail |
| Startup |
The meters stream in shortly after SessionEnteredWorld: :list() is empty for a moment and the bars arrive as a burst of MeterAdded. Mounting adds two bars mid-session and dismounting removes them. |
MeterChanged |
Only on a real change: the whole segment array is compared, so a value push and a pure recolour both count. Standing still is silent. |
A MeterAdded payload can be younger than its resource |
The bar that appears when you mount fires while its background is loading: inside the handler :res() is nil and tostring(meter) reads Meter(?). The same object answers once the resource has loaded. Name-match on a later tick or in MeterChanged, never inside MeterAdded. |
| A removed meter keeps answering |
:exists() is false and :index() is nil, while :res() and :segment() read the values it had. :exists() is the predicate :list() filters on. |
hafen.event():on("MeterChanged", function(meter)
if meter:res() == "gfx/hud/meter/hp" then
local segment = meter:segment():list()[1]
hafen.log():write(("hp %d%%"):format(((segment and segment:value()) or 0) * 100)) -- %d takes the fraction whole
end
end)
See Also
Meter — the snapshot shape :info() returns.
session:char — :food(), the one absolute reading about your character.
session:buff — the other keyless status collection.
- Events — the meter events.