User callout: 'Hax' is Kaysser's nickname, drop it from module names. hooks-lib v0.3.0 renamed its module id; this commit follows: - relationships.modules[0].id: hax-hooks-lib -> foundry-hooks-lib - relationships.modules[0].compatibility.minimum: 0.2.0 -> 0.3.0 (we now require the renamed version) - module.json: version 0.1.0 -> 0.1.1, download URL updated - README.md, scripts/main.js, tests/PLAN.md, tests/test-helpers.mjs: branding text updates - .gitignore: un-ignore the new 0.1.1 zip Verification: 75/75 smoke assertions pass, no logic change. Module title is now 'It's Achievable' (dropped the 'Hax's Tools' umbrella prefix per the same callout). 2 module text changes are non-functional; pure branding.
100 lines
3.5 KiB
Markdown
100 lines
3.5 KiB
Markdown
# It's Achievable (`its-achievable`)
|
|
|
|
Foundry VTT module: achievements engine, custom rules, rewards,
|
|
achievement wall, and combat HUD. Consumes the generic Foundry hook
|
|
facade from `foundry-hooks-lib` and the encounter state from `battle-focus`.
|
|
|
|
## Status
|
|
|
|
v0.1.0 — first release as a standalone repo. Sourced from
|
|
`battle-focus/scripts/{achievements.js, achievement-wall.js,
|
|
custom-achievements-app.js, hud.js, achievement-rules.js}` at
|
|
`battle-focus` v0.5.0-alpha.12 (`99cf757` on Gitea).
|
|
|
|
Stage 2 of the Foundry module split. The achievement code is now an
|
|
independent module with its own:
|
|
- Settings namespace (`its-achievable.*`)
|
|
- Module id (`its-achievable`, lowercase kebab)
|
|
- Achievements catalog and rule engine
|
|
- HUD subscription to hooks-lib's v0.2.0 envelope stream
|
|
- Chat-bar popover and form application
|
|
|
|
battle-focus retains its own copies of these files until Stage 3 of
|
|
the split ships (which will delete the copies and add `its-achievable`
|
|
as a soft dependency of battle-focus).
|
|
|
|
## Dependencies
|
|
|
|
- **`foundry-hooks-lib`** (≥0.2.0) — provides the Foundry event stream via
|
|
the generic `{ts, hook, args}` envelope facade. See
|
|
`hooks-lib/docs/HOOK_CONTRACT.md`.
|
|
- **`battle-focus`** (soft) — provides the encounter singleton via
|
|
`game.modules.get("battle-focus").api.getActiveEncounter()`.
|
|
|
|
If either is missing, its-achievable logs a warning and degrades
|
|
gracefully (achievements inactive).
|
|
|
|
## Public API (on `game.modules.get("its-achievable").api`)
|
|
|
|
```js
|
|
const api = game.modules.get("its-achievable").api;
|
|
|
|
// Catalog
|
|
api.getAchievementCatalog();
|
|
api.getActorAchievements(actorKey);
|
|
|
|
// Rule engine
|
|
api.evaluateRulesForEvent(event, encounter, actor, targetActor);
|
|
api.evaluateRulesForEncounterEnd(encounter);
|
|
api.evaluateRulesForCareerUpdate(career);
|
|
|
|
// Awarding
|
|
api.processEventForAchievements(event, encounter);
|
|
api.evaluateCombatAchievements(stats);
|
|
api.evaluateCareerAchievements(pcName, career, encounterId);
|
|
api.awardAchievement(actorKey, achievementId, encounterId);
|
|
|
|
// Wall + HUD
|
|
api.renderAchievementWall(actorId, actorName, opts);
|
|
api.getAchievementWallProgress(actorId, actorName);
|
|
api.renderAchievementPopover(unlocks, viewerName);
|
|
api.buildHudUpdatePayload(encounter, event);
|
|
api.openCustomAchievementsApp();
|
|
```
|
|
|
|
## Settings namespace
|
|
|
|
| Old (`battle-focus.*`) | New (`its-achievable.*`) |
|
|
|---|---|
|
|
| `achievementsByActor` | `achievementsByActor` |
|
|
| `customAchievementRules` | `customAchievementRules` |
|
|
| `enableRewards` | `enableRewards` |
|
|
|
|
**No automatic migration.** Per the Stage 2 decision, users with
|
|
existing worlds will need to re-create their custom rules and
|
|
re-trigger any past achievement awards. Documented in CHANGELOG.
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
npm test # smoke test, no Foundry needed
|
|
```
|
|
|
|
`tests/PLAN.md` describes what we test and what we don't.
|
|
Real-Foundry integration testing happens when battle-focus migrates
|
|
in Stage 3 and the existing E2E suite exercises the moved code.
|
|
|
|
## Architecture notes
|
|
|
|
- **Hooks-lib first.** its-achievable subscribes to
|
|
`foundry-hooks-lib`'s envelope stream. Combat lifecycle, document CRUD,
|
|
dnd5e rolls — all via `hooksLib.api.subscribeMany({...})`. No direct
|
|
`Hooks.on(...)` calls.
|
|
- **Encounter via battle-focus's API.** its-achievable does not import
|
|
battle-focus's `encounter.js` directly; it calls
|
|
`battle-focus.api.getActiveEncounter()` to resolve the singleton.
|
|
- **HUD derives its payload locally.** `buildHudUpdatePayload` lives in
|
|
its-achievable (moved from battle-focus). It derives the HUD state
|
|
from hooks-lib envelopes, not from a battle-focus broadcast.
|
|
|
|
## Maintained by Kaysser Taylor + Hermes |