v0.3.0
2026-02-19
Map
- [x] Added a Batch Token Modal, which allows the GM to drop multiple token images onto the map at once, and then quickly step through them to set properties and insert them onto the map. This is a huge QoL improvement for GMs running large encounters with many monsters, allowing them to set up the map much more quickly.
Admin Mode
- [x] Implement an Admin Mode for the UI. This is enabled when running on localhost.
- There is a Users top-level route and nav item, which shows a list of all users in the system, with their email, name, and campaigns they are in.
- [x] There is a delete button to delete any user.
- [x] There is an 'impersonate' button to impersonate any user, which logs you in as that user and takes you to the root route. This is useful for testing multiplayer.
- There is a Users top-level route and nav item, which shows a list of all users in the system, with their email, name, and campaigns they are in.
Player Mode
- [x] Implement a "player mode" for the UI
- When a user is looking at a campaign they own, they are in GM mode. If they are looking at a campaign they don't own, they are in Player mode.
- In Player mode, there are UI changes as such:
- Left Nav: remove "Library" and "Systems", since players don't need to access those.
- Map Menu:
- Campaign: remove
- Map: only "Add Drawing" and "Current Layer" should remain, remove all others
- Library: remove
- System: remove
- Token: remove 'Generate Monster'
- Combat: remove
- Top Bar: add an indicator that the user is in Player Mode
- Root Route: show invited campaigns ahead of owned campaigns. The action for invited campaigns should be "Join as Player".
Campaigns
- [x] Add "aliases" to Campaign Property Definitions. This is an array of 0, 1, or many strings which are alternative names for the property, which can be used in code and in the UI. For example, "Strength" might have alias "str".
- [x] Change data entities (data API, postgres, sqlite).
- [x] Add migration scripts (postgres and sqlite) to add aliases to existing property definitions, defaulting to an empty array.
- [x] Update Campaign Edit Modal to allow editing aliases as a comma-separated list.
- [x] All of this applies to System Property Definitions as well, so do the same for those.
- [ ] On a Campaign detail page, add a section showing which users are invited to the campaign, and add a button to invite new users (functionality to generate a link to share with invitees already exists). Each user should have a delete button.
Scripting
- [x] Establish a Token API for scripts run in Actions.
- The token API is essentially a prototype object with properties/methods defined below, merged with the actual token object (Object.assign({}, tokenPrototype, token)). This allows us to add useful functions and properties to the token object in scripting, such as:
- getDistanceTo(otherToken): number - returns distance to another token, in grid units, using the same distance formula as the map's distance measurement mode (e.g. Euclidean, Manhattan, etc).
- getLineOfSightTo(otherToken): boolean - returns whether this token has line of sight to another token, using the same line of sight algorithm as the map's line of sight mode.
- getProperty(propertyName: string): any - returns the value of a metadata property. Allows case-insensitive access and also checks aliases. For example, if a token has a metadata property "Strength" with alias "str", then token.getProperty("strength") and token.getProperty("str") would both return the value of that property.
- When accessing a property inherited from the system, getProperty will find values for the original property name and any aliases, and return the value if found. For example, token.metadata.system8_str will be found by getProperty('str') or getProperty('Strength')
- setProperty(propertyName: string, value: any): void - sets the value of a metadata property. Allows case-insensitive access and also checks aliases, similar to getProperty. It will find canonical metadata property names and set them, before defaulting to setting a new property with the given name.
- addAction(tbd): creates a new action on this token, with the given parameters (e.g. name, code, etc). TODO: figure out the arguments.
- The token API is essentially a prototype object with properties/methods defined below, merged with the actual token object (Object.assign({}, tokenPrototype, token)). This allows us to add useful functions and properties to the token object in scripting, such as:
- [x] Enhance IntelliSense for scripting Actions and Function:
- [x] When adding/editing an Action, there is an existing token object to query; when editing Campaign Functions, the campaign property definitions are available to query. In both cases, enhance IntelliSense to show these objects and their properties/methods. For example, when typing "token.getProperty(", IntelliSense should show the list of properties available on that token, including campaign properties inherited from the system, and their aliases. This will make it much easier to write scripts without having to constantly refer to documentation.
- [x] When an autocomplete suggestion is highlighted, the Tab key should insert the suggestion, rather than increment the indent of the current line.
- [x] Code Editor colors: most of the colors are too dark in dark mode. Reserved words are purple, variable names are blue, primitives are green, strings are red, comments are orange. All of these colors need more luminosity in dark mode to be easily distinguishable.