Brodgar client

session:char: The Character Sheet

One character’s sheet, read through its session: attributes, learning points, carried weight, food and hunger, skills, credos and lore. The curiosities it studies are session:study.

local session = hafen.session():current()                  -- the character on screen
local strength = session and session:char():attr():get("str")
if strength then
  hafen.log():write("strength " .. (strength:base() or 0) .. " (" .. (strength:composite() or 0) .. " buffed)")
end
if session and session:char():skill():find("Alchemy") then hafen.log():write("I know Alchemy") end

Rule Detail
Streams in after login The sheet lives in HUD widgets that build shortly after SessionEnteredWorld: an immediate read answers nil or an empty array. Read on a short timer.
Events FepChanged for food and hunger, StudyChanged for the curiosities, both on the bus. Attributes, learning points, weight, skills, credos and lore change only on an action of yours: read them on demand, after it.
Whose sheet session:char() is the sheet of the character that session plays: hafen.session():get("alt"):char():lp() answers for that character whether or not you look at it.
One object session:char() and each collection below are the same object every call, minted once per session, so a panel reading every frame allocates nothing. session:char():skill():buyable(filter) is a partition, a view: two calls are two objects, and the skills inside them are the identity.
A session the client no longer holds Answers nil-shaped, the shape before entering the world. session:exists() tells the two apart.
Unprotected, never throws The sheet is a display of server state. Every change to it is an action taken elsewhere.

Read

Method Returns Permission Description
session:char():attr() collection Unprotected The base attributes.
session:char():skill() collection Unprotected The skills the character knows.
session:char():credo() collection Unprotected The Credos tab.
session:char():experience() collection Unprotected The Lore tab.
session:char():food() Food \| nil Unprotected FEP and hunger, once the sheet is up.
session:char():lp() number \| nil Unprotected Current learning points.
session:char():weight() number \| nil Unprotected Carried weight, the encumbrance figure.

Attributes

Method Returns Permission Description
session:char():attr():get(name) Attr Unprotected One attribute. Never nil for a real name.
session:char():attr():list(filter) Attr[] Unprotected Every attribute the server has published.
session:char():attr():count(filter) number Unprotected How many match.
session:char():attr():find(filter) Attr \| nil Unprotected The first that matches.
attr:name() string Unprotected Which attribute this is.
attr:base() number \| nil Unprotected The raw base value.
attr:composite() number \| nil Unprotected The computed value: base plus food, gear and buffs.
attr:info() Attr \| nil Unprotected A plain-table snapshot.
Rule Detail
Names str, agi, int, con, prc, csm, dex, wil, psy. The set is closed: a name outside it raises listing them. A misspelt attribute is a typo, never something whose every read is nil.
:get(name) always answers :base() and :composite() are nil until the server has sent it. :list() holds only the published ones.
Identity Interned on session and name: :get("str") == :get("str") on one session, and seen[attr] = true works. The same name on two characters is two objects.

Skills

Method Returns Permission Description
session:char():skill():list(filter) Skill[] Unprotected The skills the character knows.
session:char():skill():buyable(filter) collection Unprotected The skills that can be bought, each with a :cost(). A partition minted per call: :count(), :find() and :list() answer on it. :buyable() == :buyable() is false.
session:char():skill():count(filter) number Unprotected How many known ones match.
session:char():skill():find(filter) Skill \| nil Unprotected The first known skill that matches.
skill:name() string Unprotected The display name.
skill:res() string \| nil Unprotected The icon resource name, once resolved.
skill:cost() number \| nil Unprotected The learning-point price.
skill:known() boolean Unprotected Learnt, rather than buyable.
skill:exists() boolean Unprotected Whether it is still listed.
skill:info() Skill \| nil Unprotected A plain-table snapshot.
Rule Detail
filter A string filter matches the display name and the resource name. :find(name) hands back the skill, which is truthy: if session:char():skill():find("x") then is a membership test that also gives you the thing.
One type for both groups Buying moves a skill from :buyable() to :list() without making it a different skill: a stashed handle goes on reading it and :known() flips.

Credos

Method Returns Permission Description
session:char():credo():list(filter) Credo[] Unprotected Every credo the tab lists, acquired and available.
session:char():credo():pursuing() Credo \| nil Unprotected The one being pursued.
session:char():credo():count(filter) number Unprotected How many match.
session:char():credo():find(filter) Credo \| nil Unprotected The first that matches.
credo:name() string Unprotected The display name.
credo:res() string \| nil Unprotected The icon resource name.
credo:acquired() boolean Unprotected Whether the character has completed it.
credo:rank(), credo:levelTotal() number \| nil Unprotected Pursuit progress. Only on the pursued credo.
credo:questsDone(), credo:questTotal() number \| nil Unprotected Quest progress within the current level, both counts.
credo:questId() number \| nil Unprotected The id of the credo quest, for session:quest.
credo:exists() boolean Unprotected Whether it is still listed.
credo:info() Credo \| nil Unprotected A plain-table snapshot.

The pursued credo is a member of the collection like any other and compares equal to the same credo found in :list(). The progress reads answer nil on every credo but that one.

Lore

Method Returns Permission Description
session:char():experience():list(filter) Experience[] Unprotected The lore the character has seen.
session:char():experience():count(filter) number Unprotected How many match.
session:char():experience():find(filter) Experience \| nil Unprotected The first that matches.
experience:name() string \| nil Unprotected The display name.
experience:res() string Unprotected The lore resource name, its identity.
experience:score() number \| nil Unprotected The experience points it is worth.
experience:modified() number \| nil Unprotected The server’s own time field for this entry.
experience:exists() boolean Unprotected Whether it is still listed.
experience:info() Experience \| nil Unprotected A plain-table snapshot.

A lore entry is addressed by its resource, so it appears in :list() once that resource has resolved, shortly after the tab builds.

Food

session:char():food() holds the client’s absolute numbers about a character: fep():cap() and fep():total() are FEP points, where every bar elsewhere (session:meter) is a fraction. nil until that character’s sheet is up.

Method Returns Permission Description
food:fep() Fep \| nil Unprotected The FEP bar: :cap(), :total(), :entry().
food:fep():entry() collection Unprotected The food events, each answering :res(), :name(), :amount(), :info().
food:fep():info() table \| nil Unprotected Snapshot of the bar: the fep half of Food, entries included.
food:hunger() Hunger \| nil Unprotected The hunger meter: :level(), :label(), :efficacy().
food:hunger():level() number \| nil Unprotected How full it is.
food:hunger():label() string \| nil Unprotected The client’s own word for that level.
food:hunger():efficacy() number \| nil Unprotected The multiplier on what you eat next at this hunger.
food:hunger():info() table \| nil Unprotected Snapshot of the meter: the hunger half of Food.
food:fep():exists(), food:hunger():exists() boolean Unprotected Whether that half is still up. Always answers.
food:exists() boolean Unprotected Whether this is still that character’s live sheet.
food:info() Food \| nil Unprotected Snapshot, the two halves in one table.
Rule Detail
Hunger is not points hunger:level() is the raw fullness figure the client’s tooltip prints in per-mille (level() * 1000). The bar draws only its fractional part, so it is not capped at 1 and a character past full reads above it. hunger:efficacy() is a 0..1 multiplier the client paints as a percentage. Neither carries a client unit.
Snapshots Each half snapshots on its own and inside food:info(). An entry’s :info() is one row of the entries array. Each is nil when its meter is not up.
An entry is data, not a handle The client rebuilds the FEP bar whole on every update, so an entry has no key and no :exists(). It carries the resource, name and amount the bar had when read. Read fep():entry() again rather than keeping one across a meal.
Event FepChanged, whose payload is the Food object.
hafen.event():on("FepChanged", function(food)
  hafen.log():write(("fep %d/%d, %s"):format(food:fep():total(), food:fep():cap(),     -- %d takes the points whole
                                             food:hunger():label() or "?"))
end)

See Also