- Pack description is in manifest.json, including available Variants.
- Everything a pack provides is done through Lua, starting at scripts/init.lua.
- Data (that is loaded through Lua) is stored as JSON inside the pack.
- Images (that are referenced in JSON) are PNGs, GIFs or JPEGs inside the pack.
- Most strings/identifiers are case-sensitive.
manifest.json in the root of the pack.
The only things of consequence right now are:
{
"name": "<Readable Name>",
"game_name": "<Readable Name>",
"package_uid": "<unique string for saves>",
"package_version": "<unique string for open and saves>",
"platform": "<platform>",
"platform_override": "<optional actual platform>",
"variants": {
"<some_variant_uid>": {
"display_name": "<Readable Name>",
"flags": [
"<some flag>"
]
}
},
"versions_url": "https://example.com/versions.json",
"min_poptracker_version": "0.23.1",
"target_poptracker_version": "0.23.1"
}
Other fields:
{
"author": "<Readbale Names>"
}
Each pack can have multiple variants (e.g. map or compact).
platform_override can be used to set the platform without changing the platform value for compatibility reasons.
If platform is "snes", the snes autotracker is to be enabled.
If platform is "n64", the LuaConnector autotracker is to be enabled.
Currently supported flags:
"ap": pack supports Archipelago autotracking. See AUTOTRACKING.md."apmanual": pack supports sending locations to Archipelago. Usesgame_nameasgame."aphintgame": pack supports scouting locations in Archipelago."uat": pack supports UAT autotracking. See AUTOTRACKING.md."lorom": (SNES) game has LoROM mapping - if not listed in gameinfo.cpp"hirom": (SNES) game has HiROM mapping"exlorom": (SNES) game has ExLoROM mapping"exhirom": (SNES) game has ExHiROM mapping"sa-1": (SNES) game has SA-1 mapping"luaconnector": Connect to (BizHawk) LuaConnector (crowd control version)
versions_url can be used for automatic updates. Information from global packs.json takes precedence.
See https://github.com/black-sliver/PopTracker/tree/packlist for more information.
min_poptracker_version will check if the version is compatible when loading the pack.
It can be written as x, x.y or x.y.z.
target_poptracker_version may influence behavior to not break a pack for breaking API or behavioral changes.
There is no guarantee that everything will have a compatibility layer, but it's best practice to put the latest known
working version number there.
Optional, settings.json in the root of the pack.
Configures behavior of the pack.
{
"smooth_scaling": true|false|null, // configure the image scaling method. null = default = currently crisp
"smooth_map_scaling": true|false|null, // configure the image scaling method for maps. null = default = smooth
"allow_deferred_logic_update": true|false|null, // set initial value of Tracker.AllowDeferredLogicUpdate
"disabled_image_filter": "...", // set the meaning of @disabled in img_mods, defaults to "grey", since 0.33.0
}
NOTE: User overrides for settings are merged with the pack, replacing individual keys, not the whole file.
-
When calling a provided method, use
:instead of. -
When loading files, the search path is ./ as well as ./variant/, so that
you can have global items in
items/items.jsonOR per-variant items in[variant]/items/item.json -
To get proper auto-complete, type hints and warnings for the PopTracker API, you can use LuaLS with our Definition File.
- VSCode/ium extension: search for
sumneko.lua - JetBrains (IntelliJ/PyCharm/CLion/...): SumnekoLua
You have to add a
.luarc.json(example) with the correct path (typically../../api/lua/definition) to your project and restart the Language Server or IDE.
It is recommended to check that file into Git, but exclude it from the final pack when zipping. - VSCode/ium extension: search for
The following interfaces are provided:
string .ActiveVariantUID: variant of the pack that is currently loadedbool :AddItems(jsonfilename): load items from jsonbool :AddMaps(jsonfilename): load maps from jsonbool :AddLocations(jsonfilename): load locations from jsonbool :AddLayouts(jsonfilename): load layouts from jsonbool :AddClasses(jsonfilename): load classes from json, available since 0.34.1int :ProviderCountForCode(code): number of items that provide the code (sum of count for consumables)mixed :FindObjectForCode(string): returns items forcodeor location section for@location/sectionvoid :UiHint(name, value): sends a hint to the Ui, see Ui Hints. Only available in PopTracker, since 0.11.0bool .BulkUpdate: can be set to true from Lua to pause running logic rules. NOTE: Since failing to setBulkUpdateback tofalsewill stop PopTracker from updating, make sure your code between settingBulkUpdateis error resistant (for example by usingpcall).bool .AllowDeferredLogicUpdate: can be set to true from Lua to allow evaluating logic rules fewer times than items are updated.
bool :LoadScript(luafilename): load and execute a lua script from absolute filename inside packrequirecan be used instead (since PopTracker 0.21.0)requirebehaves mostly like Lua require since 0.25.6"foo.baz"will try/scripts/foo/baz.lua,/scripts/foo/baz/init.lua,/foo/baz.luaand/foo/baz/init.lua
...contains mod name for relative require since 0.25.6
ref :AddMemoryWatch(name,addr,len,callback,interval): add a memory watch for auto-tracking, see AUTOTRACKING.mdbool :RemoveMemoryWatch(name): remove memory watch by name, available since 0.11.0ref :AddVariableWatch(name,{variables, ...},callback,interval): add a variable watch for auto-tracking, see AUTOTRACKING.mdbool :RemoveVariableWatch(name): remove variable watch by name, available since 0.16.0ref :AddWatchForCode(name,code,callback): callback(code) will be called whenever an item changed state that canProvide(code). Only available in PopTracker, since 0.11.0, will return a reference (name) to the watch since 0.18.2. Use "*" to trigger for all codes since 0.25.5.bool :RemoveWatchForCode(name): remove watch by nameLuaItem :CreateLuaItem(): create a LuaItem (custom item) instanceref :AddOnFrameHandler(name,callback): callback(elapsed) will be called every frame, available since 0.25.9bool :RemoveOnFrameHandler(name): remove a frame callbackref :AddOnLocationSectionChangedHandler(name, callback): callback (LocationSection) will be called whenever any location section changes, available since 0.26.2bool :RemoveOnLocationSectionChangedHandler(name): removes a previously added LocationSectionChanged callback, available since 0.33.1bool :RemoveOnLocationSectionHandler(name): Old name of RemoveOnLocationSectionChangedHandler, available since 0.26.2ThreadProxy :RunScriptAsync(luaFilename, arg, completeCallback, progressCallback): Load and run script in a separate thread.argis passed as global arg. Most other things are not available in the new context. Usereturnto return a value from the script, that will be passed tocallback(result). (ThreadProxy has no function yet)ThreadProxy :RunStringAsync(script, arg, completeCallback, progressCallback): same as RunScriptAsync, but script is a string instead of a filename.void :AsyncProgress(arg): call progressCallback in main context on next frame. Arg is passed to callback.
- see AUTOTRACKING.md
use ImageRef = string
ImageRef :FromPackRelativePath(filename): for now this will just return filename and path resolution is done later.ImageRef :FromImageReference(original, mod): return ImageRef that is original ImageRef + mod string.
a string in the form of "1.0.0" -- TODO: move to Tracker.PopVersion ?
a table representing an enum with the following constants:
None, Partial, Inspect, SequenceBreak, Normal, Cleared
a table representing an enum with the following constants:
Avoid, None, NoPriority, Unspecified, Priority
DEBUGset to true or an array of strings to get more error or debug outputfalseornil: disable everythingtrue: enable everything{'fps'}: enable FPS output in console{'errors'}: enable more detailed error reporting{'fps', 'errors', ...}: enable multiple
requirefunction, see ScriptHost:LoadScript
use ImageRef = string
ImageRef .Icon: change the icon. UseImageReference:FromPackRelativePath.string .IconMods: icon modifier, see JSON's img_mods. Only available in PopTracker, since 0.11.0string .Name: item's nameobject .ItemState: (any) object to track internal state in Lua. Keys have to be strings for Get and Set below to work.closure(LuaItem) .OnLeftClickFunc: called when left-clickingclosure(LuaItem) .OnRightClickFunc: called when right-clickingclosure(LuaItem) .OnMiddleClickFunc: called when middle-clicking, since 0.25.8- TODO: Forward, Backward or a generalized onClick(button)
closure(LuaItem,string code) .CanProvideCodeFunc: called to determine if item has codeclosure(LuaItem,string code) .ProvidesCodeFunc: called to track progress, closure should return 1 if code is provided (can provide && active)closure(LuaItem,string code) .AdvanceToCodeFunc: called to change item's stage to provide code (not in use yet)closure(LuaItem) .SaveFunc: called when saving, closure should return a lua object that works in LoadFuncclosure(LuaItem,object data) .LoadFunc: called when loading, data as returned bySaveFuncclosure(LuaItem,key,value) .PropertyChangedFunc: called when :Set is called and the value changed:Set(string key,value): write to property store (NOTE: property store == .ItemState):Get(string key): read from property store. not sure what this is good for if we have.ItemState:SetOverlay(string): set overlay text (see JsonItem), only available in PopTracker:SetOverlayColor(string): set overlay text color (see JsonItem), only available in PopTracker, since 0.31.0:SetOverlayBackground(string): set overlay background (see JsonItem), only available in PopTracker, since 0.17.0:SetOverlayFontSize(int): set overlay font size (~pixels), only available in PopTracker, since 0.17.2:SetOverlayAlign(string): set overlay alignment to "left", "center" or "right", only available in PopTracker, since 0.25.9.Owner: returns empty table at the moment for compatibility reasons, since 0.18.2.Type: gets the type of the item as string (always "custom"), since 0.23.0.IgnoreUserInput: disables state/stage changes when clicking the item, since 0.26.2.BadgeText: same as SetOverlay, but a property, since 0.31.0.BadgeTextColor: same as SetOverlay, but a property, since 0.31.0
Probably more to come.
bool .Active: enable/disable itemint .CurrentStage: set/get stage for staged itemsint .AcquiredCount: set/get current amount for consumablesint .MinCount: set/get min amount for consumables (available since 0.21.0)int .MaxCount: set/get max amount for consumables (available since 0.14.0):SetOverlay(string): set overlay text (like count, for non-consumables), only available in PopTracker:SetOverlayColor(string): set text color: "#000000" is (A)RGB, "" is default, only available in PopTracker, since 0.31.0:SetOverlayBackground(string): set background: "#000000" is (A)RGB, "" is transparent, only available in PopTracker, since 0.17.0:SetOverlayFontSize(int): set overlay font size (~pixels), only available in PopTracker, since 0.17.2:SetOverlayAlign(string): set overlay alignment to "left", "center" or "right", only available in PopTracker, since 0.25.9.Owner: returns empty table at the moment for compatibility reasons, since 0.18.2.Increment: sets or gets the increment (left-click) value of consumables.Decrement: sets or gets the decrement (right-click) value of consumables.Type: gets the type of the item as string ("toggle", ...), since 0.23.0.Icon: override displayed image, including mods, until state changes, since 0.26.2. Prefer stages instead..BadgeText: same as SetOverlay, but a property, since 0.31.0.BadgeTextColor: same as SetOverlayColor, but a property, since 0.31.0
Probably more to come.
.Owner: returns empty table at the moment, see Locations.ChestCount: how many chests are in the section.AvailableChestCount: read/write how many chests are NOT checked.AccessibilityLevel: read-only, giving one of the AccessibilityLevel constants.FullID: read-only, full id such as "location/section"
.AccessibilityLevel: read-only, giving one of the AccessibilityLevel constants (will not give CLEARED at the moment), since 0.25.5
[
{
"name": "<Readable Name>",
"type": "<type>",
...
},
...
]
Type:
-
"progressive":-
has any number of states (
"stages") -
adds automatic "off" stage if
"allow_disabled": true(default true) -
adds optional
"loop": falseto determine if the states should loop (last → first) when clicking -
adds optional
"initial_stage_idx": 0 -
adds array of states (
"stages")"stages": [ { "img": "path/to/img.png", "disabled_img": "path/to/disabled.png", // optional, grey img if missing "codes": "check_identifier", "secondary_codes": "state_identifier", // unused at the moment "inherit_codes": false, "img_mods": "@disabled", "disabled_img_mods": "...", "name": "optional name override" // since 0.21.1 }, ... ] -
img_mods filter to be applied on the img
@disabled: as defined insettings.json's "disabled_image_filter"; defaults togreygrey: 0% saturation + 67% brightnessdisable: same asgreyoverlay|path/to/img.png|overlay_filters...: draw a second image over it; overlay_filters are applied to overlay (since 0.25.6)brightness|x: set image brightness by multiplying all colors withx(decimal); since 0.32.2dim: same asbrightness|0.5; since 0.32.2saturation|x|colorspace: set saturation tox, if colorspace isbt601emulate brightness levels, otherwise average r, g, b; since 0.32.2greyscale: same assaturation|0; since 0.32.2- NOTE: order matters, applied left to right
-
inherit_codes: true will make stage3 provide codes for item, stage1, 2 and 3 (default true)
-
-
"toggle":-
only has on/off
-
greyed-out version will be generated by the tracker
-
adds
imgandcodesat the same level{ ... "img": "path/to/img.png", "img_mods": "optional_filter", "codes": "check_identifier" } -
adds
disabled_img: override img when off -
adds
disabled_img_mods: likeimg_modsbut for off. Defaults toimg_mods+"@disabled"). Any value (e.g."none") disables defaults. -
adds
initial_active_state: precollected if true, since 0.25.4
-
"static":- behaves like a toggle that is always on
-
"consumable":- has AcquiredCount (0 = grey/disabled)
- adds
"min_quantity": 0, since 0.21.0 - adds
"max_quantity": 0; if max_quantity < min_quantity, there is no upper limit; works for negative min since 0.32.0 - adds
"increment": 1can be used if an item pickup is always a certain count, since 0.18.3 - adds
"decrement": 1can be used if using an item always uses up multiple, since 0.18.3 - adds optional
"initial_quantity": 0 - adds optional
"overlay_background": ""set text background color with "#000000" (A)RGB notation - adds optional
"overlay_font_size": 0set font size of overlay text - adds optional
"overlay_align": "right"set text alignment of overlay text to "left", "right" or "center", since 0.25.9 - adds alias
badge_font_sizeforoverlay_font_size
-
"progressive_toggle":- like progressive with
allow_disabled: false, but the current stage can be toggled between "on" and "off" - precollected overlay if
initial_active_stateis true, since 0.25.5
- like progressive with
-
"composite_toggle":- is linked to two items left and right that have to be defined before
- lua access is through linked items' .Active only
- adds
"item_left": "code"and"item_right": "code" - adds 4 stages via
"toggle_badged":- display
"img"over"base_item" - toggle visibility of
"img"with right mouse button - change underlying item with left mouse button
- precollected overlay if
initial_active_stateis true, since 0.25.4
- display
{
"name": "Some Item",
"type": "...",
"img": "path/to/item.png",
"codes": "some_item"
},
{
"name": "Combination's name",
"type": "toggle_badged",
"base_item": "some_item",
"img": "path/to/overlay.png",
"codes": "some_overlay"
}Maps are referenced by name in layouts.
[
{
"name": "map_identifier",
"location_size": 24, // size of locations on the map, unit is pixels of img
"location_border_thickness": 2, // border around the locations
"location_shape": "rect", // or "diamond", since 0.26.2, or "trapezoid", since 0.32.0
"img": "path/to/img.png"
},
...
]
TODO: we want to add more stuff, like displaying numbers besides locations
Locations define drops on maps, rules to have them accessible as well as the looks of it
[
{
"name": "Area name",
"short_name": "Area", // shorter version of name. currently unused
"access_rules": [
"<rule1>,<rule2>",
"<rule3>,<rule4>",
"{<checkrule1>, <checkrule2>}",
...
],
"visibility_rules": [
... // like access rules but for visibility of the location or section
],
"chest_unopened_img": "path/to/chest.png", // default if children do not override
"chest_opened_img": "path/to/chest.png",
"overlay_background": "#000000", // set background for number of unopened chests #(AA)RRGGBB
"color": "#ff0000", // color accent/tint of the location tooltip. currently unused
"parent": "parent's name", // read below
"children": [
{
"name": "Location or check name",
"access_rules": [
"<rule on top of parent's>",
...
],
"chest_unopened_img": "path/to/chest.png", // default if section does not override
"chest_opened_img": "path/to/chest.png",
"map_locations": [
{
"map": "map_name",
"x": 123,
"y": 234,
"size": 24, // override map default, since 0.21.1
"border_thickness": 2, // override map default, since 0.21.1
"shape": "rect", // override map default, since 0.26.2
"restrict_visibility_rules": [
... // additional visibility rules for individual map locations, since 0.26
],
"force_invisibility_rules": [
... // additional visibility rules that hide the map location if true, since 0.26
]
}
],
"sections": [
{
"name": "Human readable name",
"clear_as_group": true|false, // one click does all checks in this section
"chest_unopened_img": "path/to/chest.png",
"chest_opened_img": "path/to/chest.png",
"item_count": 1, // number of checks in this section (all use the same image)
"hosted_item": "item", // this item will be checked when cleared
"access_rules": [
"<rule on top of parent's>",
...
],
"visibility_rules": [
"<rule on top of parent's>",
...
]
},
{
"ref": "Path/to/another/section" // links and displays a section from another location here
},
...
]
}
]
}
]
Locations:
Each map_location is a square on the map and shows a popup with individual chests.
Rules:
Rules starting with $ will call the Lua function with that name, @<location>/<section> will use the result of a different access rule, other rules will just look at items' code (runs ProviderCountForCode(rule)).
Rules starting with ^ interpret the value as AccessibilityLevel instead of count. That is ^$func can directly set the AccessibilityLevel, sometimes removing the need for [] and {} (see below). Only available in PopTracker, since 0.25.6.
For $ rules, arguments can be supplied with |. $test|a|b will call test("a","b").
The return value has to be a number (count) or boolean (since v0.20.4).
Rules containing a : will check the count instead of boolean. <rule>:<count> can be used with $ rules and item codes (e.g. consumables) but will have no effect for ^ and @ rules
Rules inside [ ] are optional (i.e. glitches work around this rule).
Rule-groups inside { } are a different set of rules to mark the section as "checkable but not collectible", marked blue on the map.
<rule1>-<rule4> in example above are combined as: (<rule1> AND <rule2>) OR (<rule3> AND <rule4>) have to be met to collect.
{<checkrule1>, <checkrule2>} in example above are combined as: (<checkrule1> AND <checkrule2>) have to be met to check.
Individual rules can be specified as json array instead of string, which allows to use , inside names or arguments. Only available in PopTracker, since 0.19.1.
Rules can be specified as a single string, which is equivalent to [[string]]. Only available in PopTracker, since 0.25.6.
Parent:
With "parent", the location's parent can be overwritten. Since PopTracker v0.19.2.
This is useful to put a location from dungeons.json into a location from overworld.json,
which could also be done with @ access rules.
Sections: The popup is segmented into sections.
A room with magic and 3 chests could have a section for 1 magic and a section for the 3 chests.
Depending on the map it is also possible to have 1 location per chest, but checking that off takes longer.
Name is optional and will be displayed above the section.
Sections can be addressed from Lua with Tracker:FindObjectForCode("@location_name/section_name")
hosted_item is a comma separated list of item codes that are always in this section.
item_count is the number of checks (chests, ...) and defaults to 1 unless hosted_item is used, in which case it defaults to 0.
ref make this section a reference to another section, ignoring all other properties defined here. This can be used to have the same section in detailed and overworld maps.
item_size, item_width and item_height, since 0.32.0, works the same as with Layouts. See below.
inspectable_sequence_break can be set to true on either location or section to restore 0.31.0 behavior of showing sequence-break-inspectable as sequence break.
Tracker Lua Interface:
Tracker:FindObjectForCode('@location_name/section_name')returns a Section or nilTracker:FindObjectForCode('@location_name')returns a Location or nil
Section Lua Interface:
.Owner: probably points to location, but we return an empty table at the moment..Owner.ModifiedByUserisnil..ChestCount: read how many chests are in the section.AvailableChestCount: read/write how many chests are NOT checked.AccessibilityLevel: read-only, giving one of the AccessibilityLevel constants.Highlight: Highlight the location as one of the states in Highlight constants, since 0.32.0
Location Lua Interface:
.AccessibilityLevel: read-only, giving one of the AccessibilityLevel constants, since 0.25.5
Future: We probably want to add a different (additional) interface to Pop for this:
- Give every chest an id
Tracker:setChestState(id,state)- we would then have pop-autotracking.lua and emo-autotracking.lua
- layouts define how the items and maps are placed in the UI
- layout definitions are a map of
"name" => widgetwhere widget can be some container, a reference, an item grid/array, a single item or a (world-)map - the named layout
"tracker_default"is the root object of the tracker view - if
"tracker_default"is missing,"tracker_horizontal"or"tracker_vertical"is used (no way for the user to pick yet) - the named layout
"tracker_broadcast"is the root object of the broadcast view - the named layout
"settings_popup"is the root object of a pack/variant specific settings window - the named layout
"tracker_capture_item"is not supported yet ("item picker popup" for hints) - containers have
"content"which can be an array of other widgets or a json object for a single other widget
the final hierarchy looks something like this: json root -> "tracker_default" -> "content" -> "content" -> ...
Properties:
{
"type": "{container,dock,array,tabbed,group,item,itemgrid,map,layout,recentpins}",
"background": "#000000", // background color #(AA)RRGGBB
"h_alignment": "{left,right,center,stretch}", // defaults to center for item grid
"v_alignment": "{top,bottom,center,stretch}",
"dock": "{top,bottom,left,right}", // if inside a dock, where to dock
"orientation": "{horizontal,vertical}", // how to orient children
"max_height": integer, // maximum height in px
"max_width": integer, // maximum width in px
"margin": "left,top,right,bottom",
"item_margin": "<horizontal>,<vertical>", // margin in px
"item_size": "<horizontal>,<vertical>", // 3=default=32, 4=48, other TBD, 10+=size in pixels
"item_h_alignment": "{left,right,center,stretch}", // align image inside item; PopTracker since 0.19.1
"item_v_alignment": "{top,bottom,center,stretch}", // as above; stretch is not implemented for either
"dropshadow": bool, // enable/disable drop shadow, only partially implemented
"class": "className" // references a layout class to inherit values from before defaults; PopTracker since 0.34.1
}
Type:
"container": has a single child (because that's how containers work IRL)"dock": like array, but uses"dock"to place children"array": like dock, but uses"orientation"instead of"dock"to place children"tabbed": tabs that switch between multiple children, has"tabs": [ {"title":"...","content":{...}} ]"group": has a single child + a header ("header": "text")"item": a single item ("item": "item_code", )"itemgrid": has"rows"as an array of rows, each being an array ofitem_codestrings"map": has optional"maps"which is array of map ids, if missing all maps are added"layout": pastes a different named object here ("key": "name_of_layout")"recentpins": pinned items, uses{"style":"{wrapped,?}","compact":bool}"text": has property"text": "string"to display a string"canvas": has width, height and content - children have width, height, canvas_top and canvas_left; since 0.21.1
Margin:
Margin can alternatively be specified as "x,y" or single int and is an offset of the widget's position and size. Margin defaults to 0 for items and "inner arrays" (array inside array), 5 for everything else.
Using the Tracker::AddClasses() method in init.lua, you can add a file of classes to your pack which contain most the same properties a layout object can.
These allow you to assign common fields and values to a single JSON object that can be applied to other layout types using the class property.
- The only exception to this is
type, which is required on a layout node for verification purposes.
Classes can contain all properties, even if a particular element doesn't use some of them. The order of value precedence is layout -> class -> default.
Example:
{
"grid": {
"item_margin": "0, 0",
"h_alignment": "left",
"item_size": "48, 48"
}
}
And in a layout node:
{
"type": "itemgrid",
"class": "grid",
"item_margin": "2, 2", // this will override the "0, 0" from the class
"content": {
// etc
}
}
Instead of allowing Lua direct access to the UI/Widgets, there is a "Hints" interface. See global Tracker. Hints consist of name and value. The name describes which adjustment to make, value is the value for that adjustment.
The following hint names are defined:
"ActivateTab": value = tab name, activates tab with tab name, since 0.11.0"Zoom <MapName>": value ="<new_zoom_factor>", change zoom for map to new_zoom_factor, since 0.34.0"Zoom <MapName>[<n>]": same as above but for the nth instance starting at 0, since 0.34.0"Pan <MapName>": value ="<center_x>,<center_y>", pan map to put specific pixel in the center, since 0.34.0"Pan <MapName>[<n>]": same as above but for the nth instance starting at 0, since 0.34.0