Community guide format
Guide author23 min readUpdated
The contract between a community guide and Polestar: pack.json, the addon the compiler writes, the rules, import strings, versioning.
On this page
The contract between a community addonEvery other pack, a separate addon made and supported by its author, listed under Community guides (GUIDE_PACKS.md). and Polestar, for anyone building
tools of their own. Authors who use Polestar's tools never write any of this
by hand: they write pack.json and .guide files
(GUIDE_FORMAT.md) and polestar compile writes the rest.
The handbook is PACKS.md.
On this page: pack.json ·
The addon · TOC fields ·
Rules Polestar enforces ·
Format 3 · Format 4 · Import strings · Versioning
A community addon is an addon for World of Warcraft: Forever that depends
on Polestar and registers guides with it. It is built from a pack: a folder
with a pack.json and .guide files. In the format, every such addon is a
pack; Polestar's own guides are a pack too (Polestar_Guides, pack id
guides).
pack.json#
The pack's manifest, in the root of the pack's folder. Every tool works on
the pack folder given as its one argument (polestar lint ../joe-routes) or
with --pack <dir>, else on the nearest pack.json in the current folder or
a parent.
{
"id": "joe-routes",
"title": "Joe's Routes",
"author": "Joe",
"version": "1.2.0",
"license": "CC-BY-4.0",
"website": "https://github.com/joe/joe-routes",
"source": "Written from my own play",
"folder": "PolestarPack_JoesRoutes"
}| Key | Required | Value |
|---|---|---|
id | Yes | the pack id: ^[a-z0-9][a-z0-9-]{1,31}$ (2 to 32 lower-case letters, digits and hyphens, starting with a letter or digit), not reserved (below) |
title | Yes | shown to players; at most 64 characters |
author | Yes | shown to players; at most 64 characters |
version | Yes | one word of letters, digits, ., + and -, starting with a letter or digit, at most 32 characters (1.2.0) |
license | Yes | the license of the pack's guides, as an SPDX id where one exists (CC-BY-4.0, CC0-1.0, MIT, GPL-3.0-only); at most 64 characters |
website | No | https:// or http:// address; at most 200 characters |
source | No | where the guides come from, in the author's words; at most 200 characters |
notes | No | one line for the AddOns list, shown when the player points at the pack there; at most 200 characters (default "Community addon for Polestar by <author>") |
folder | No | the addon's folder name: 1 to 64 letters, digits, _ and -, not Polestar and not starting with Polestar_ (in any case); default PolestarPack_ and the title in CamelCase (Joe's Routes → PolestarPack_JoesRoutes), or the id when the title has no letters or digits |
Every value is a string on one line. A missing required field, or a value
that breaks these rules, is a lint error; an unknown key is a warning; a |
in a displayed value is a warning (it prints literally in game).
The pack folder holds pack.json, the .guide files in any layout,
drafts/ (RecorderThe optional Polestar_Recorder addon that records play; record import turns its sessions into data/recordings. drafts, compiled only with --include-drafts), dist/
(build output) and the picture files its image lines name (.tga or
.blp, at any path inside the folder, such as media/). The compiler reads
every .guide file under the folder except in dist/, drafts/,
node_modules/ and folders whose name starts with a dot.
The addon#
polestar compile writes dist/<folder>/ (ours: addon/Polestar_Guides/):
PolestarPack_JoesRoutes/
PolestarPack_JoesRoutes.toc
Pack.lua Polestar.RegisterPack({ ... })
MapFrames.lua Polestar.RegisterMapFrames({ ... }, "joe-routes")
Guides.lua one Polestar.RegisterGuide({ ... }) per guide
LICENSE-DATA.txt the reference data sources the guides draw on
LICENSE.txt the pack folder's LICENSE, copied as is (only when it has one)
media/cave.tga each picture an `image` line names, at its path in the pack folder
PolestarPack_JoesRoutes_deDE/ one texts addon per language the pack translates
PolestarPack_JoesRoutes_deDE.toc
Texts.lua Polestar.RegisterGuideTexts("deDE", "joe-routes", { ... })
LICENSE.txt as aboveTexts addons. A pack whose locales/<locale>/ folder holds translations
(docs/TRANSLATING.md, "GuideOne route, written as a .guide file and compiled to Lua; its id reads like forever/alliance/human/01-elwynn-forest. texts") gets, beside its folder, one
load-on-demand addon per language, <folder>_<locale>. Polestar loads the
client's own once the pack's files are in, and no other: a German client
never reads the French texts. Its TOC carries ## LoadOnDemand: 1,
## Dependencies: Polestar, ## X-Polestar-Texts: <pack id> and
## X-Polestar-Locale: <locale> with the generated-folder marker, and
Texts.lua makes one call:
Polestar.RegisterGuideTexts("deDE", "joe-routes", {
pack = { ["Joe's Routes"] = "Joes Routen" }, -- the pack's title and notes, its guides' folders
guides = { ["joe-routes/elwynn"] = { ["{npc:240} stands outside the inn."] = "{npc:240} steht vor dem Gasthaus." } },
})Each table maps a text's English, name tokens and all, to its translation;
a text without one shows in English. compile, install and dev write
and replace a texts addon folder by the same rule as the pack folder (its
marker and X-Polestar-Texts), and remove the one of a language the pack no
longer translates; pack puts each into the zip as a folder of its own.
The file set is fixed: a new guide changes Guides.lua, never the file list,
so /reload shows every change and only a pack's first install needs a
restart of the game (LICENSE.txt is a text file the game never loads); so
does the first texts addon of a language, below.
Each guide's table is built inside its own function in Guides.lua, so Lua
5.1's constant limit applies per guide.
TOC fields#
## Interface: 16001
## Title: Polestar Community: Joe's Routes
## Notes: Community addon for Polestar by Joe
## Author: Joe
## Version: 1.2.0
## Category: Quests
## Group: Polestar
## Dependencies: Polestar
## X-Polestar-Pack: joe-routes
## X-Polestar-Format: 3
## X-Polestar-Needs: 0.2.5
## X-License: CC-BY-4.0
## X-Website: https://github.com/joe/joe-routes
# Generated by `node tools/polestar.js compile` from pack.json and the .guide files. Do not edit: rebuild with that command.
Pack.lua
MapFrames.lua
Guides.lua| Field | Value |
|---|---|
Interface | copied from addon/Polestar/Polestar.toc at build time |
Title | "Polestar Community: " and pack.json title (the title alone when it already starts so), so the AddOns list reads the addon as a community addon. Community addons built before this prefix carry "Polestar Pack: " until rebuilt; Polestar recognises both. Polestar's own menus show the title without it. Polestar's own pack (guides) gets no prefix |
Author, Version | from pack.json |
Notes | pack.json notes, else "Community addon for Polestar by <author>" |
Category | Quests, Polestar's category in the AddOns list |
Group | Polestar: the AddOns list shows the pack under Polestar's own entry, beside Polestar's own addons. Not written for Polestar's own pack |
Dependencies | Polestar: the client loads Polestar first |
X-Polestar-Pack | the pack id. Polestar lists every addon carrying it on the Community guides page, loaded or not |
X-Polestar-Format | the pack format (below) |
X-Polestar-Needs | the first Polestar version that reads that format, so an older Polestar can say which version the pack needs |
X-License | pack.json license |
X-Website | pack.json website, only when given |
The comment line is the generated-folder marker, and with
X-Polestar-Pack it makes the folder rule: compile (its output folder),
install (dist/<folder>/ and the folder in AddOns, where a symlink is
replaced without the check) and dev (the folder
in AddOns) write a pack folder only when it is absent, empty, or carrying
the generated marker and no other pack's id. Anything else is refused: a
folder without the marker as "not a generated community addon", one generated
for another pack as holding that pack, with the hint to set folder in
pack.json. pack builds a community addon's zip in memory, and play writes
into a new temporary folder, so neither touches an existing pack folder.
Rules Polestar enforces#
Core/Guides.lua applies these at registration; lint reports the same ones
before a pack is built (tests/packs-parity.test.js holds the two together).
- Ids. Pack ids match
^[a-z0-9][a-z0-9-]{1,31}$;guides,forever,lookup,draft,polestarandimportedare reserved. Every guide id starts with<pack id>/, except in Polestar's own packs:guides(the addonPolestar_Guides) ownsforever/anddraft/, Polestar's internallookuppack ownslookup/, andimported(the guides pasted as import strings, below) owns every<author>/that is a valid pack id nobody reserves. - No overrides. A guide id outside its pack's prefix is refused. A pack may register one of its own ids again, which replaces that guide. An id a guide of another pack already holds is refused too ("Guide <id> was refused: <pack> already holds that id."): only an imported guide can share a prefix with a pack.
- Clashes. A pack whose id is already registered is refused whole, and
the problem names both addons; a pack with a reserved id is refused whole
with "Pack id <id> is reserved for Polestar.". Either way the guides and
map frames its files register for that id while it loads are dropped,
never given to the holder, and the dropped guides are counted in the
problem. A pack with a malformed id, no
formator a required field missing is refused too ("Pack id <id> is not valid: …", "The pack is missing <field>."), and its guides are dropped without a problem each. A guide registered before its pack is refused (listed once the pack registers); one for a pack that never registers is ignored, since there is no row to list it on. - Map frames. A pack may add Era map frames for maps Polestar's lack; for a map both cover, Polestar's win whatever the load order (lint warns when the bounds differ). Frames for a pack that is not registered, or was refused, are dropped.
- Format. A pack declaring a format Polestar does not read is refused
whole, with the status
too-newand "Needs Polestar <x-polestar-needs> or newer." ("Needs a newer Polestar." when its TOC has noX-Polestar-Needs). An unknown verb or a malformed field inside a known format refuses only that guide. - Visible problems. Every refusal is recorded against its pack with a
kind:
pack-refused(the pack),guide-refused(one guide, with its id),no-guides(the addon loaded but registered no guides, usually a Lua error in its files). The Community guides page shows them. - Text prints literally. Every string a pack registers is escaped once,
at registration (
|doubled), so colour and link codes do not work. A guide's ids (id,pack,next,nextChoices) are left as written. - Missing guides keep their place. Saved tabs of a guide whose pack is missing or switched off reopen in their old place when it comes back.
- Packs on disk. At login Polestar lists every addon whose TOC carries
X-Polestar-Pack, loaded or not, with a status. A registered pack ison,off(switched off on the Community guides page),refused,too-new(a newer format) orbroken(registered no guides). One that never registered is, in this order of precedence,too-new,disabled(turned off in the AddOns list),not-loaded(the client did not load it: out of date, a dependency missing) orbroken.
Only guides of packs that are on are shown, searched and suggested. Suggestions come from Polestar's guides unless the player picks a pack under Suggest guides from on the Community guides page; only packs with Leveling guides are offered there, and Polestar's guides fill in where the chosen pack has nothing for the character.
Format 3#
The format the compiler writes for a pack whose guides have no when (on a
stepOne stop in a guide: a title, optional conditions and tip, and at least one goal. or a next option), along or label, and whose level goals have no
frac (format 4, below). Format 2 is format 1 with the two group
keys (a guide's and a goal's) and a guide's reward and why, marked
"format 2" below; Polestar 0.2.4 and later read it. Format 3 is format 2
with name tokens in the texts and a guide's names, marked "format 3"
below; Polestar 0.2.5 and later read it. A format 1 or 2 pack is the same
without them and keeps loading.
Name tokens (format 3). The game's names inside the texts a player reads
(a guide's title, why, pitch, pros, cons and reward text; a
step's title and note; a goalOne thing to do in a step, as a verb and its arguments (accept 783, kill, goto, turnin...).'s note and text) are written
{kind:id}: {npc:240}, {object:68}, {item:1528}, {quest:176},
{zone:1429} (a uiMapID), {area:87} (an AreaTable ID), {spell:5487},
{faction:72}, {skill:182} (a profession's skill line). Polestar shows
each as the game names it in the client's language (Polestar.Names, the
client's map and area names, the profession's translation), else as the
guide's names give it, so an English client reads the text as its author
wrote it (a text that spells a name otherwise, "Dendrite" for Dendrite
Starblaze, is in the guide's written with the author's words). The
compiler writes the tokens (GUIDE_FORMAT.md, "Names in
texts"); an older Polestar would show them as written, hence the format.
Format 4#
Format 3 with a step's when: the conditions checked during play
(GUIDE_FORMAT.md, "Conditions checked during play"), a step's along and
label (GUIDE_FORMAT.md, "Along the way"), a level goal's frac:
a point inside the level, and a guide's nextWhen: conditional hand-offs
(GUIDE_FORMAT.md, "Conditional hand-offs"). Polestar 0.2.6 and later read it.
The compiler writes a pack in format 4 only when its guides use one of them;
any other pack stays in format 3, so the Polestar it needs does not change. An
older Polestar would show every conditional step to every character, stop the
route at every along step, count level 12.5 done at level 12 and offer every
next option (it ignores keys it does not know), hence the format.
when is a list of predicates, each a table with its kind k, neg = true
to turn it around, and the kind's keys; a step applies while all of them
hold:
Read the rest of this section535 words
k | Keys | Holds when |
|---|---|---|
"level" | op ("<", "<=", ">", ">="), n; optional frac (0 < frac < 1) | the character's level compares so with n; with frac, the level plus the share of it done (XP / XPMax) compares so with n + frac |
"onquest" | q: a list of quest ids | any of them is in the log |
"complete" | q | the quest is in the log, ready to turn in |
"turnedin" | q | the quest is handed in |
"available" | q; optional minLevel, pre, preAll, closedBy (as on an accept) | the quest is not in the log or done, the level is reached, one of pre and every one of preAll is done, and none of closedBy is in the log or done |
"has" | item; optional n (default 1) | the bags hold n or more |
"zone", "subzone" | name; optional area | the zone (subzone) text reads as the area's name in the client's language, or as name |
"bound" | name; optional area | the bind location reads so |
{ title = "Back to Gornek", when = { { k = "onquest", q = { 788 } }, { k = "level", op = "<", n = 6 } }, goals = { ... } }A when that is not a non-empty list, or a predicate of another kind or
without its keys, refuses the guide. Once the route has moved past a step,
Polestar stores whether its when held (per character, guide and step) and
reads that from then on.
label is a string, the step's name, unique in the guide. along is
true, or the label of a later step: the route passes over the step, and
its open goals are listed under the steps after it until they are done or
the route reaches that labelled step.
{ title = "Boars on the way", along = "camp", goals = { ... } },
{ title = "Goldshire", goals = { ... } },
{ title = "Back at the camp", label = "camp", goals = { ... } },A label that is not a string or names a second step, or an along that
is neither true nor the label of a later step, refuses the guide ("step N:
bad label", "step N: duplicate label <name>", "step N: bad along").
frac on a level goal (format 4) is the share of the level's XP the goal
asks for, a number with 0 < frac < 1: { t = "level", level = 12, frac = 0.5 }
is done at level 12 with XP / XPMax >= 0.5, or at any later level. A goal's
frac outside that refuses the guide ("bad frac"); a level predicate's,
"bad when".
nextWhen maps a next option to its predicates, the same tables as a
step's when: { ["joe-routes/barrens-early"] = { { k = "level", op = "<", n = 12 } } }.
Every key must be one of nextChoices, which the compiler writes whenever
nextWhen is there, even for one option. Polestar offers an option only
while its predicates hold, checked on the character's live state each time
it lists the options; with one option offered it goes on to it without
asking, with none the guide ends there. Nothing is stored. A nextWhen that
is not a table, a key that is not in nextChoices or a malformed predicate
list refuses the guide.
RegisterPack(meta)#
local ADDON = ...
Polestar.RegisterPack({
addon = ADDON, id = "joe-routes", format = 3,
title = "Joe's Routes", author = "Joe", version = "1.2.0", license = "CC-BY-4.0",
website = "https://github.com/joe/joe-routes", source = "Written from my own play",
})| Field | Required | Value |
|---|---|---|
addon | Yes | the addon's folder name (the file's first vararg) |
id | Yes | the pack id (rule 1) |
format | Yes | 3 (1 or 2 for a pack of that format) |
title, author, version, license | Yes | non-empty strings |
website, source, notes | No | strings |
Returns ok, err. Call it once, in the first file the TOC loads, before any
guide.
RegisterGuide(def)#
One table per guide; returns ok, err.
Polestar.RegisterGuide({
id = "joe-routes/elwynn", pack = "joe-routes",
title = "Elwynn Forest", faction = "Alliance", minLevel = 5, maxLevel = 10,
category = "Leveling", zone = 1429, zoneName = "Elwynn Forest",
next = "forever/alliance/02-westfall", author = "Joe", version = 1,
steps = {
{ title = "Goldshire", goals = {
{ t = "accept", quest = 62, title = "The Fargodeep Mine", npc = 240, npcName = "Marshal Dughan", minLevel = 4, qlvl = 7, map = 1429, x = 42.11, y = 65.93, f = "e" },
} },
},
})Read the rest of this section1658 words
Guide keys (the compiler writes them in this order):
| Key | Required | Value |
|---|---|---|
id | Yes | the guide id, under the pack's prefix |
pack | Yes | the registered pack id |
title | Yes | string |
faction | No | "Alliance" or "Horde"; absent: either |
races | No | list of race file names ("Human", "Scourge", "NightElf", …); absent: any |
classes | No | list of class file names ("WARRIOR", …); absent: any |
minLevel, maxLevel | No | numbers |
category | No | "Leveling" (default), "Class", "Professions", "Reputation", "Legacy", "Exploration", "Collectibles", "Miscellaneous", "Dungeons"; the compiler passes another name through with a warning |
folder | No | the guide menuThe hub's Guides section, where a player picks a guide. folder |
keywords | No | list of extra search words |
collects | No | list of item ids the guide obtains |
reward | No | format 2: what the guide's goal gives, shown first in the guide menu's details, in order: { item = 20536, itemName = "Soul Harvester", quality = 3, choice = true } (an item; choice when one of several to choose from; the name shows in the client's quality colour when the item is cached, else the compiled quality's), { spell = 23161, spellName = "Summon Dreadsteed" } or { text = "Shen'dralar reputation" }; an entry with none of item, spell or text refuses the guide |
why | No | format 2: one line on why the goal is worth doing, shown under the reward |
zone, zoneName | No | the default zone's uiMapID and name |
next | No | the id of the guide that follows: one of the pack's or one of Polestar's |
nextChoices | No | list of guide ids offered as a route choice (the first is the default); with nextWhen, every option, even one |
nextWhen | No | format 4: { [<guide id>] = { <predicate>, ... } }, an option of nextChoices offered only while its predicates hold (Format 4, above) |
pitch | No | one line for the route choice card |
pros, cons | No | lists of short lines under the pitch |
author | No | string; the compiler writes the pack's author when the guide names none |
version | No | number |
group | No | true (format 2): a goal of the guide's route that is not optional needs a group; the guide menu shows a "Group" chip |
names | No | format 3: { ["npc:240"] = "Marshal Dughan", ... }, the English of every name token the guide's texts hold (a token it lacks is shown as written) |
written | No | format 3: { ["Back to {npc:3649}"] = "Back to Dendrite", ... }, a text whose names its tokens' English in names would spell otherwise, with the author's words an English client reads |
steps | Yes | list of steps, at least one |
Step keys: title (optional), note (optional, player-facing), image
(optional: { file = "media\\cave", w = 512, h = 256 }, the step's picture:
file its path in the pack's addon folder without the extension, \
between folders, w and h its size in pixels; Polestar shows it under the
step's tip, and an imported guide, which has no addon folder, shows none),
only (optional: { classes = { ... }, races = { ... }, factions = { ... } },
AND across keys, OR within; Polestar also reads an entry "!X" as "not X",
but the compiler writes positive lists only), when (optional, format 4: the
step's conditions checked during play, below) and goals (at least one).
Goal keys common to every verb: t (the verb), text (display text),
note, optional (true: never blocks the step), only (as on a step),
map, x, y (0 to 100, together), z (optional, with them: the height
of the NPC or object the point stands for, below), f ("e": the Era map
frame; absent: the live frame), r (arrival radius in yards), spots
(every place the goal's target spawns, below; with map), group (format
2: the goal needs a group, below).
Verb t | Required | Optional |
|---|---|---|
accept | quest, title | npc/npcName, object/objName, item/itemName, minLevel, qlvl, pre, preAll, closedBy, preTitles |
turnin | quest, title | npc/npcName, object/objName, rewards = { choices = { { item, itemName, quality } }, items = { ... }, xp, money }, direct |
complete | quest, title | — |
kill | target | title, npc, count, quest, oi, spread |
collect | item, itemName | title, count, quest, oi, spread, from, fromAlso, fromId, fromAlsoId, fromKind, fromRank, fromFaction, fromNpcs, npc, npcName, candidates |
goto | map, x, y | text, r, area |
talk | npc, npcName | — |
interact | object, objName | title, quest, oi, spread |
use | item, itemName | title, quest, oi |
sethome | location, or candidates | npc, npcName, area |
hearth | location | area |
getfp | location | npc, npcName, node, area |
fly | location | area |
train | — | npc, npcName, class, skill, skillLine, rank, rankMax, candidates |
buy | item, itemName | count, npc, npcName, candidates |
vendor | — | npc, npcName, candidates |
level | level | frac (format 4) |
note | text | — |
skill | skill, level | skillLine |
learn | spell, spellName | skillLine, npc/npcName, item/itemName, candidates |
craft | spell, spellName | item/itemName, count, untilSkill, skillLine, reagents = { { item, itemName, count } } |
rep | faction, factionName, standing (1 to 8) | — |
explore | map, x, y | text |
achieve | achievement, achievementName | criteria |
The fields:
| Field | Value |
|---|---|
quest, npc, object, item, spell, faction, achievement | numeric ids |
title, npcName, objName, itemName, spellName, factionName, achievementName, target | names |
count | number to collect, kill or buy; on craft, the number of items to make |
oi | 1-based quest objective index (C_QuestLog.GetQuestObjectives) |
minLevel | the level an accept needs; qlvl the quest's level (number or string) |
pre | prerequisite quest ids, any one suffices; preAll every one needed |
closedBy | quest ids that close this one once taken or done |
preTitles | { [questId] = title } for the quests those name |
profLine, profName, profRank | on any goal of a quest only characters with a profession can take: its skill line id, name and the rank it needs; the goal is hidden from characters without it |
location | an inn, flight point or zone name as the game shows it |
class | true on train: the nearest trainer of the player's class |
skill | a profession name; skillLine its skill line id |
rank, rankMax | on train: the profession rank (1 Apprentice to 4 Artisan; Polestar also reads a rank name or a skill cap) and its skill cap (written by the compiler, not read by Polestar) |
level | on level: the level to reach; on skill: the skill to reach |
frac | on level (format 4): the share of the level's XP to reach, 0 < frac < 1 (above) |
untilSkill | on craft: craft until the profession reaches this skill |
reagents | { { item, itemName, count } } |
standing | 1 Hated to 8 Exalted |
criteria | an achievement's criterion index |
z | the height (world z, yards) of the NPC or object the goal's x,y stands for, so the walking route ends on its floor (an Undercity quarter rather than the street above it). Polestar 0.2.4 and later pass it to the walking engine while the arrow leads to that point, and ask again without it when the route found with it fails or ends more than 30 yards above or below it. A spot (spots) and a smart target's candidate (candidates) may carry their own z, which Polestar 0.2.5 and later pass the same way when the arrow leads there (#180). The compiler fills it in (GUIDE_FORMAT.md, "Heights"); a hand-written pack may leave it out. Older Polestar ignores it, so it does not change the format |
area | the AreaTable area ID (C_Map.GetAreaInfo) of the place a sethome, hearth, fly or getfp location or a goto label names. Polestar 0.2.5 and later complete a hearth or fly when the zone or subzone text is that area's name in the client's language, accept a sethome bind location that reads so, count a goto arrived when the player goes indoors in that subzone (a cave or mine past its mouth; never for a zone), and show that name in place of the English one. The compiler fills it in (GUIDE_FORMAT.md, "Places by ID"); without it the goal matches by its English name. Older Polestar ignores it, so it does not change the format |
node | on getfp: the taxi node ID (C_TaxiMap nodeID) of its flight point. Polestar 0.2.5 and later count the goal done once the flight map lists that node as known, instead of comparing names. Filled in like area; older Polestar ignores it |
spots | spawn places { { x, y, z, r, n, alt } }: centre in the goal's map and frame, its height (optional, see z), radius in yards, spawn count; alt = true for another place the target spawns, which the arrow takes only when the player moves on to it (the world map marks it) |
spread | true with spots: an area objective, searched spot by spot |
from, fromAlso, fromKind, fromRank, fromFaction | a collect's main source: name, a second source of the same kind dropping it at 50% or more, "npc" or "object", rank (1 elite, 2 rare elite, 3 boss, 4 rare), faction "A", "H" or "AH" |
fromId, fromAlsoId | the ids of from and fromAlso (of kind fromKind): Polestar names them in the client's language (Polestar.Names), keeping from/fromAlso where it has no name; older Polestar ignores them |
npc, npcName | a collect whose guide names the NPC who hands the item over (npc <id>), for an item nothing drops: the target button targets them first. The farm spot a collect … nearest goal settles on carries the same pair |
fromNpcs | a collect's source creatures where the goal stands: { { npc, npcName } }, the one its point belongs to first, then the other likely sources there, at most 5 (GUIDE_FORMAT.md, "What the compiler fills in"). Newer Polestar gives them to the target button, named in the client's language by their ids, before the item's own droppers from the quest database, whose drop chances rank them over the whole world. Older Polestar ignores them, so it does not change the format |
candidates | smart targets: { { npc, npcName, map, x, y, z, f, faction, races, classes, lvl, rate, spawns } } (z optional, see z); Polestar picks the nearest usable one at runtime |
rewards | { choices = { { item, itemName, quality } }, items = { ... }, xp, money } (money in copper) |
direct | true on a turnin: the quest is handed in straight from the giver's window and never enters the quest log, so the goal needs no accept before it and carries no objectives. Polestar counts it done on the hand-in itself and stores that, so a repeatable stays done after a reload |
escort | true on the accept, complete and objective goals of an escort quest: the guide windowThe small window that shows the current step and its goals while playing (UI/GuideFrame.lua). says under the goal that the NPC may have to respawn (GUIDE_FORMAT.md, "Escort quests"). Older Polestar ignores it |
group | format 2: the goal needs a group, shown under it in the guide window: { kind = "elite", rank = 1 } (rank 1 elite, 2 rare elite, 3 boss: "Elite: group advised"), { kind = "dungeon", name = "Dire Maul" } ("Dungeon: Dire Maul"), { kind = "raid", name = "Molten Core" } ("Raid: Molten Core"), { kind = "group", size = 3 } ("Group of 3 advised"; without size, "Group advised"). The compiler derives it or takes the author's group [N] (GUIDE_FORMAT.md, "Group content"); Polestar shows nothing for a kind it does not know |
Polestar 0.1.0 reads every verb above; 0.2.4 and later read the group
keys; 0.2.4 and later read z; 0.2.5 and later read area, node and
direct. ARCHITECTURE.md ("Guide data") describes when the Engine counts each
goal done.
RegisterMapFrames(frames, packId)#
Polestar.RegisterMapFrames({
era = {
[1429] = { c = 0, left = 1535.417, right = -1935.417, top = -7939.583, bottom = -10254.166 },
},
}, "joe-routes")The Era world bounds of every map the pack's guides place a goal or a
candidate on, where the reference data has them: c the continent,
left/right world Y, top/bottom world X. For a map Polestar already
covers, Polestar's bounds win (rule 4). Call it after RegisterPack: the
frames of a pack that is not registered are dropped.
Import strings#
One compiled guide as one line of text, for a player to paste on Polestar's
Import page instead of installing an addon (GUIDE_PACKS.md, "Import
strings"). polestar export <file.guide> writes it
(tools/lib/pack/importstring.js); addon/Polestar/Core/Import.lua reads
it.
!PS1!<checksum>!<body>Read the rest of this section491 words
| Part | Value |
|---|---|
!PS1! | the string format, version 1. A later format gets another number; Polestar refuses one it does not know with "This import string needs a newer Polestar." |
<checksum> | the Adler-32 of the payload (the JSON below, as UTF-8 bytes), 8 hex digits |
<body> | the payload, compressed with zlib (RFC 1950) and written in base64 (A–Z, a–z, 0–9, +, /, = padding) |
Whitespace anywhere in a pasted string is dropped first (a chat window may wrap it). The payload is JSON:
{ "format": 3, "license": "CC-BY-4.0", "guide": { "id": "yendi/cozy-sleeping-bag", "title": "Cozy Sleeping Bag", "...": "...", "steps": [ ... ] }, "frames": { "1429": { "c": 0, "left": 1535.417, "right": -1935.417, "top": -7939.583, "bottom": -10254.166 } } }| Key | Required | Value |
|---|---|---|
format | Yes | the guide data's format (above): 4 when a step has when, else 3; Polestar refuses a format it does not read |
license | Yes | the license of the guide, from the pack's pack.json |
guide | Yes | one guide exactly as RegisterGuide(def) takes it, without pack: the keys above, numbers rounded as Guides.lua writes them, preTitles with the quest ids as the object's keys |
frames | No | RegisterMapFrames's era table for the maps its goals use, by uiMapID. Polestar takes a frame only for a map the guide's goals or their smart targets stand on, and only where no one (Polestar, a community addon, an earlier import) has registered one |
Polestar reads it as data only. The client's own decoders
(C_EncodingUtil.DecodeBase64 and DecompressString, the ones the walking
maps use) unpack it; Polestar parses the JSON itself (objects, arrays,
strings, numbers, true, false; no null, nested 12 deep at most) and
builds the guide field by field from the keys this page lists, each with its
type. A key it does not know refuses the string, naming the key ("This
import string holds a field Polestar does not read (guide.steps[3].goals[1].onLoad).").
Nothing in a string is ever run: the reader never hands its text to
loadstring or anything like it.
Then the guide meets the rules above, as a community addon's would, plus:
- at most 50,000 characters in the string, 256 KB of payload, 60 steps,
and 4,096 bytes in any one text or key (a longer one is refused, naming
it, before any text is filled in;
exportrefuses it too); - its id is
<author>/<name>, under no reserved id and no id of a community addon the player has installed (registered or only listed); nextand everynextChoicesentry name one of Polestar's guides (forever/,draft/) or a guide already imported;Guides.Validatepasses (the message is the Community guides page's: "Guide <id> was refused: <reason>.").
A refused string saves nothing. An accepted one is kept account-wide in
PolestarDB.imports[<guide id>] as its payload, and registered at every
login through the same checks under the reserved pack imported. A guide's
version (a number) against the stored one's decides Import, Update (higher)
or nothing (equal or lower). Imported guides are listed in the guide menu as
Imported under Community guides, credited "By <author> · Imported ·
<license>", never on the Community guides page, and never suggested.
Versioning#
- The format number changes when a verb, a field or a field's meaning is added or changed. Polestar ignores keys it does not know; an unknown verb refuses the guide.
- Polestar keeps reading every format it has shipped, so a published pack keeps working when Polestar updates.
- The compiler writes the first Polestar version that reads the pack's format
into the TOC as
X-Polestar-Needs. Format 1 is read by Polestar 0.1.0 and later, format 2 (thegroup,rewardandwhykeys) by 0.2.4 and later, format 3 (name tokens) by 0.2.5 and later, format 4 (a step'swhen,alongandlabel, alevelgoal'sfrac, a guide'snextWhen) by 0.2.6 and later. - A pack in a format this Polestar does not read registers nothing; the
Community guides page shows it as
too-newwith "Needs Polestar <version> or newer." (rule 5).