22 KiB
FoundryVTT Les Héritiers System - Developer Instructions
Executive Summary
Les Héritiers is an unofficial FoundryVTT system for the French RPG "Les Héritiers" (published by Titam France/Sombres Projets). The system implements a complete actor and item management framework with support for characters (personnage), NPCs (pnj), and 13 item types. Recent development includes a migration to Foundry v13+ with DataModels and AppV2 architecture.
Repository: https://www.uberwald.me/gitea/public/fvtt-les-heritiers
Current Version: 13.0.7
Compatibility: Foundry 13+
Language: French (single language localization)
1. BUILD & DEVELOPMENT COMMANDS
Build System
- Build Tool: Gulp 4 with LESS compilation
- Package Manager: npm
Scripts (package.json)
"build": "gulp build" # Compile LESS to CSS (primary task)
"watch": "gulp watch" # Watch LESS files and rebuild on changes
Gulp Pipeline (gulpfile.js)
The gulpfile defines three main tasks:
| Task | Source | Output | Features |
|---|---|---|---|
styles |
less/heritiers.less |
styles/heritiers.css |
LESS→CSS compilation with source maps |
watchFiles |
less/**/*.less |
Watches recursively | Auto-rebuild on LESS file changes |
build |
- | - | Runs styles (default export) |
watch |
- | - | Runs build then watchFiles |
Development Workflow:
npm install # Install dependencies (gulp, gulp-less, gulp-sourcemaps)
npm run build # One-time CSS compilation
npm run watch # Development: continuous LESS watching
Key Files:
/package.json- Minimal deps (only gulp toolchain)/gulpfile.js- 36 lines defining LESS tasks/less/heritiers.less- Entry point (importssimple-converted.less)/styles/heritiers.css- Generated output + source maps
2. SYSTEM ARCHITECTURE
2.1 System Metadata (system.json)
| Field | Value |
|---|---|
| id | fvtt-les-heritiers |
| title | Les Héritiers |
| version | 13.0.7 |
| description | Les Héritiers pour FoundryVTT (French) |
| authors | Uberwald/LeRatierBretonnien, Prêtre |
| compatibility | min: "13", verified: "13" |
| manifest | Hosted on Uberwald Gitea |
| languages | French only (fr.json) |
| license | LICENCE.txt (proprietary) |
| grid | 5m squares |
Key Configuration Flags:
hotReload.extensions:css,html,hbs,jsonhotReload.paths:styles,./,templates,lang/fr.jsonprimaryTokenAttribute:sante.vigueur(health/vigor)secondaryTokenAttribute:bonneaventure.actuelle(current good fortune)- Socket Support: Enabled (
socket: true)
2.2 Data Model (template.json.backup)
The system defines a complex hierarchical data model with 2 Actor types and 14 Item types:
Actor Types
1. personnage (Player Character)
- Templates:
biodata,core - Key characteristics (8 attributes):
- Physical: Agilité (agi), Constitution (con), Force (for), Précision (prec)
- Mental: Esprit (esp), Perception (per), Prestance (pres), Sang-Froid (san)
- Fields include biodata (name, appearance variants masked/unmasked, age, personality traits), ranks (Tricherie, Féerie, Masque, Héritage with scenario tracking), PV (health), combat skills
- Profile-based competences (6 profiles + magic)
2. pnj (NPC)
- Templates:
biodata,core - Identical structure to
personnagewith simpler NPC-specific metadata
Core Actor Data (template.core):
caracteristiques: {
agi, con, for, prec, esp, per, pres, san
└─ label, labelnorm, abbrev, kind(physical|mental), value, rang, max
}
rang: {
tricherie, feerie, masque, heritage (with scenarios subfield)
}
pv: { value, max, mod }
competences: { aventurier, combattant, erudit, gentleman, roublard, savant }
magie: { pointsame (value, max) }
experience: { value, pourtricher }
combat: {
esquive, parade, resistancephysique, resistancepsychique, protection,
effetssecondaires, dissimulation, initiative, corpsacorps, tir
}
Item Types (14 total)
| Item Type | Key Fields | Use Case |
|---|---|---|
arme (Weapon) |
degats, precision, cadence, portee, dissimulation, armetype, rarete, prix | Combat equipment |
protection |
points, malusagilite, protectiontype, effetsecondaire | Armor/shields |
equipement |
rarete, quantite, prix | General gear |
accessoire |
lieu (location code) | Small items |
competence |
categorie, profil, niveau, specialites[], ismagie, nomniveau | Skills (6 profiles) |
profil |
profiltype (majeur | mineur) |
contact |
contacttype (contact, allie, ennemi, interet) | NPC contacts |
avantage |
description | Advantages (benefits) |
desavantage |
description | Disadvantages (flaws) |
capacitenaturelle |
pouvoirtype, activation, effet, portee, resistance, cibles, virulence | Natural abilities |
pouvoir |
pouvoirtype, masquetype, niveau, istest, feeriemasque, carac, zoneffet, pointsusagecourant | Powers/spells |
atoutfeerique |
description | Fairy/magical perks |
fee |
feetype, avantages, desavantages, pouvoirsfeeriques*, atoutsfeeriques, competences, capacitenaturelles | Fairy character sheet |
sort (Spell) |
niveau, rang, competence (Druidisme), carac1, carac2, duree, portee, ingredients, resistance | Magic spells |
Item Templates:
base: description (HTMLField)basequip: rarete, quantite, prix, equipped (for equipable items)
2.3 Directory Structure
modules/ # ES6 source code (12,273 LOC total)
├── heritiers-main.js # Entry point: init, hook setup, document class config
├── heritiers-actor.js # HeritiersActor class with creation hooks
├── heritiers-item.js # HeritiersItem class
├── heritiers-config.js # HERITIERS_CONFIG object (enums, constants)
├── heritiers-commands.js # Chat command system
├── heritiers-utility.js # Static utility class (dice, rolls, templates, socket)
├── heritiers-combat.js # HeritiersCombat class
├── xregexp-all.js # RegExp library
├── models/
│ ├── index.mjs # Exports all DataModels
│ ├── personnage.mjs # PersonnageDataModel (complex, 12K+ lines)
│ ├── pnj.mjs # PnjDataModel
│ ├── arme.mjs # ArmeDataModel (updated Jan 21)
│ ├── competence.mjs # CompetenceDataModel (updated Jan 21)
│ ├── capacitenaturelle.mjs # Natural ability model (updated Jan 21)
│ ├── pouvoir.mjs # PouvoirDataModel (updated Jan 21)
│ ├── protection.mjs # ProtectionDataModel (updated Jan 21)
│ ├── [11 more models] # accessoire, atoutfeerique, avantage, contact,
│ │ # desavantage, equipement, fee, profil, sort
│ └── base-item.mjs # Base item template
├── applications/
│ ├── sheets/
│ │ ├── _module.mjs # Exports all sheet classes
│ │ ├── base-actor-sheet.mjs # HeritiersActorSheet (HandlebarsApplicationMixin, AppV2)
│ │ ├── base-item-sheet.mjs # HeritiersItemSheet base
│ │ ├── personnage-sheet.mjs # Character sheet (specific)
│ │ ├── pnj-sheet.mjs # NPC sheet (specific)
│ │ └── [11 item sheets] # Type-specific sheets: arme, protection, competence, etc.
│ └── heritiers-roll-dialog.mjs # Dialog for roll resolution
Key Module Details:
heritiers-main.js (100+ lines):
- Hooks.once("init") - Registers templates, combat, actor/item classes, DataModels
- Registers AppV2 sheets for all actor and item types
- Creates
game.system.lesheritiersnamespace with utilities, config, models, sheets - Socket message handling
heritiers-config.js:
HERITIERS_CONFIGobject with ~40 enum dictionaries:caracList: 8 characteristics with labelscompetenceProfil: 6 profiles (aventurier, roublard, combattant, erudit, savant, gentleman) + magicbaseTestPouvoir,resistancePouvoir,typePouvoirseuilsDifficulte: 30-level difficulty scale (5=Enfantine to 30=Divine)attaqueCible: target localization (body part damage tracking)- Game-specific constants
heritiers-utility.js (60+ custom Handlebars helpers):
rollDataStore,defenderStorefor roll tracking- Handlebars helpers: count, includes, upper, lower, upperFirst, notEmpty, mul, and, or, eq, isTest, etc.
preloadHandlebarsTemplates()- loads 27 template fileschatListeners()- click handlers for chatloadCompendium()- loads packs dynamicallyonSocketMesssage()- inter-client communication
heritiers-actor.js:
- Custom creation logic: auto-adds useful skills from compendium for personnage type
- Extends Foundry Actor class
heritiers-item.js:
- Custom Item class with item-type-specific business logic
2.4 Templates Directory (27 Handlebars files)
Organized by purpose:
Actor Sheets (2 files):
actor-sheet.hbs- Personnage character sheet (complex form with tabs)actor-pnj-sheet.hbs- NPC sheet (simplified)
Item Sheets (14 files):
item-[type]-sheet.hbsfor each item type- E.g.,
item-arme-sheet.hbs,item-competence-sheet.hbs,item-sort-sheet.hbs
Partials (5 files):
partial-actor-equipment.hbs- Equipment list renderingpartial-item-header.hbs- Item sheet header (common)partial-item-nav.hbs- Tabbed navigationpartial-item-description.hbs- Description editorpartial-utile-skills.hbs- Skill list filteringpost-item.hbs- Chat card for item descriptioneditor-notes-gm.hbs- GM notes editor
Chat Results (3 files):
chat-generic-result.hbs- Generic roll resultchat-cc-result.hbs- Close combat resultchat-assommer-result.hbs- Stun attack result
Dialogs (1 file):
roll-dialog-generic.hbs- Roll dialog with modifiers
Design Notes:
- Heavy use of Handlebars conditionals ({{#if}}, {{#each}})
- Data-action attributes for action routing
- Flexbox-based layout (
flexrow,flexcolCSS classes) - Partial templates for reusability
2.5 Localization (lang/fr.json)
Minimal localization - only 24 lines:
{
"TYPES": {
"Actor": {
"personnage": "Personnage",
"pnj": "PNJ"
},
"Item": {
"accessoire": "Accessoire",
"arme": "Arme",
[12 more item types...]
}
}
}
Localization Strategy:
- Labels hardcoded in code/templates in French
- No string keys for UI text outside TYPES
- Template labels defined in DataModel schema (initial values)
- Seeded localization keys in heritiers-config.js (no i18n lookup)
2.6 Styles (less/ and styles/)
LESS Structure (2 files):
heritiers.less(4 lines) - Entry point, imports converted templatesimple-converted.less- Full stylesheet (converted from Bootstrap/template.css)
Output:
- Compiled to
/styles/heritiers.css(referenced in system.json) - Source maps generated (
.mapfiles)
Styling Approach:
- Utility classes:
flexrow,flexcol,padd-right,status-small-label - Color classes:
color-class-common, etc. - Section classes:
background-sheet-header - Responsive design via flexbox
2.7 Compendium Packs (packs/)
11 packs defined in system.json, stored as .db (LevelDB format):
| Pack Name | Type | Label | Folder | Contents |
|---|---|---|---|---|
competences |
Item | Compétences | Creation | Skills |
avantages |
Item | Avantages | Creation | Advantages |
desavantages |
Item | Désavantages | Creation | Disadvantages |
capacites |
Item | Capacités Naturelles | Creation | Natural abilities |
atouts-feeriques |
Item | Atouts Féériques | Creation | Fairy perks |
magie-sorts |
Item | Sorts | Creation | Spells (5 spell schools) |
archetypes-fees |
Item | Fées | Creation | Fairy archetypes |
pouvoirs |
Item | Pouvoirs | Creation | Powers |
profils |
Item | Profils | Creation | Character profiles |
armes-et-protection |
Item | Armes et Protections | Equipment | Weapons & armor |
scenes |
Scene | Scènes | Root | Sample scenes |
journaux |
JournalEntry | Journaux | Root | Game journal entries |
Ownership Model (PLAYER: OBSERVER, ASSISTANT: OWNER):
- Players can observe (read) items in packs
- Assistants have full control
Source Data (srcdata/):
- 5 JSON files for spell normalization:
sort_druidisme.json,sort_faeomancie.json,sort_magieduclan.json,sort_necromancie.json,sort_theurgie.jsonnormalize.py- Python script to transform spell data to compendium format
3. KEY CONVENTIONS
3.1 Module/Class Structure
Pattern: ES6 Classes extending Foundry base classes
- Naming:
Heritiers[Type](e.g.,HeritiersActor,HeritiersItem,HeritiersCombat) - Exports: Named exports via
export class/export default
Example (heritiers-actor.js):
export class HeritiersActor extends Actor {
static async create(data, options) { ... }
// Custom methods and hooks
}
Example (models/personnage.mjs):
export default class PersonnageDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
biodata: new fields.SchemaField({ ... }),
// nested SchemaFields with detailed field definitions
}
}
}
Sheet Pattern (AppV2 - Foundry v13+):
const { HandlebarsApplicationMixin } = foundry.applications.api
export default class HeritiersPersonnageSheet extends HandlebarsApplicationMixin(
foundry.applications.sheets.ActorSheetV2
) {
static DEFAULT_OPTIONS = {
classes: ["fvtt-les-heritiers", "sheet", "actor"],
position: { width: 780, height: 840 },
form: { submitOnChange: true, closeOnSubmit: false },
actions: {
editImage, toggleSheet, editItem, deleteItem,
createItem, equipItem, rollCarac, ...
}
}
// Static action handlers (#onEditImage, etc.)
}
3.2 Data Model Conventions
Field Types (foundry.data.fields.*):
StringField- Text values, default initial valuesHTMLField- Rich-text fields (description, notes, etc.)NumberField- Integers (integer: true), floatsBooleanField- Boolean flagsArrayField- Lists (with element type)SchemaField- Nested objects
Naming Conventions:
- Snake_case for internal field names:
biodata,caracteristiques,pointsame - Abbreviations for characteristics:
agi,con,for,prec,esp,per,pres,san - French names for labels and UI: "Agilité", "Constitution", etc.
Special Fields:
labelnorm- Normalized label for lookups (lowercase, no accents)kind- Classification (physical, mental, magical)initial- Default value in schema definition
3.3 Handlebars Template Conventions
File Naming:
actor-sheet.hbs- Main sheet template (not item-specific)actor-[type]-sheet.hbs- Type-specific actor sheetitem-[type]-sheet.hbs- Type-specific item sheetpartial-[purpose].hbs- Reusable componentchat-[result-type].hbs- Chat message templateeditor-[context].hbs- Editor component
Data Binding:
- Actor data:
{{system.field.subfield}},{{actor.name}} - Item data:
{{system.field}} - Loops:
{{#each system.array as |item key|}} - Conditionals:
{{#if condition}}...{{/if}}
Custom Helpers (60+):
- Comparison:
eq,ne,lt,gt - Array:
includes,count,notEmpty - String:
upper,lower,upperFirst - Math:
mul,add,sub,and,or - Game:
isTest(checks test type)
CSS Classes:
- Layout:
flexrow,flexcol,item,item-list - Styling:
status-small-label,color-class-common,background-sheet-header - Action:
data-action="actionName"for click routing
3.4 Localization Key Naming
Pattern: TYPES.Actor.[type], TYPES.Item.[type]
Current localization (lang/fr.json):
"TYPES": {
"Actor": {
"personnage": "Personnage",
"pnj": "PNJ"
},
"Item": {
"accessoire": "Accessoire",
"arme": "Arme",
"atoutfeerique": "Atout féerique",
...
}
}
Strategy:
- Type labels: Centralized in TYPES
- Field labels: Defined in DataModel schema as
initialvalues - Config labels: In heritiers-config.js (caracList, competenceProfil, etc.)
- Template text: Hardcoded in .hbs files (all French)
No i18n System: System is French-only; no game.i18n lookups used in code
3.5 Dice & Roll System
Initiative Formula (heritiers-config.js):
CONFIG.Combat.initiative = {
formula: "1d10",
decimals: 1
};
Roll Data Storage (heritiers-utility.js):
rollDataStore = {}- Tracks active rolls by IDdefenderStore = {}- Tracks defenders for opposed rolls- Socket-based roll updates for multi-client scenarios
Bonus/Malus:
- Range: -6 to +6 (signed integers)
- Generated as:
Array.from({ length: 7 }, (v, k) => toString(k - 6))
Dice Mechanics:
- D8, D10, D12 support with face adjacency tables (unused in some contexts)
- Example:
__facesAdjacentesobject maps die faces to adjacent faces
3.6 Socket Message Handling
Game Socket (configured in system.json):
- Enabled via
"socket": true - Message channel:
"system.fvtt-les-heritiers"
Message Flow (heritiers-utility.js):
game.socket.on("system.fvtt-les-heritiers", data => {
HeritiersUtility.onSocketMesssage(data)
})
Used for:
- Inter-client roll notifications
- Multiplayer combat updates
3.7 Recent Migration Notes (v13.0.7)
Latest Changes (from git log):
- Migrated from legacy v1 sheets to AppV2 (HandlebarsApplicationMixin + ActorSheetV2)
- Migrated to DataModels (foundry.abstract.TypeDataModel)
- All sheets converted to
.mjs(ES6 modules) - Specification & competence fixes (Jan 21)
- Armor (protection) and weapon (arme) model refinements (Jan 21)
Key Commit: 4722fdf - Migration vers DataModels et appv2 (major architecture update)
4. EXISTING AI & DEVELOPER CONFIGURATIONS
No Pre-Existing Configs Found
The following configuration files do NOT exist in the repository:
.github/copilot-instructions.md❌CLAUDE.md❌.cursorrules❌AGENTS.md❌.windsurfrules❌CONVENTIONS.md❌CONTRIBUTING.md❌
Only README.md exists (29 lines):
- Credits Titam France/Sombres Projets
- Lists contributors: LeRatierBretonnien (dev), Prêtre (testing/data entry)
- Notes official authorization
- Links to books at titam-france.fr
5. DEVELOPMENT WORKFLOW
5.1 Setup
cd /home/morr/work/foundryvtt/fvtt-les-heritiers
npm install
npm run watch
5.2 File Organization When Adding Features
| Purpose | Location | Pattern |
|---|---|---|
| New item type logic | modules/heritiers-item.js |
Extend HeritiersItem |
| New item data model | modules/models/[type].mjs |
Extend TypeDataModel |
| New item sheet | modules/applications/sheets/[type]-sheet.mjs |
Extend HeritiersItemSheet |
| New item template | templates/item-[type]-sheet.hbs |
Handlebars + data-action |
| New actor type | modules/models/[type].mjs |
Extend TypeDataModel |
| New actor sheet | modules/applications/sheets/[type]-sheet.mjs |
Extend HeritiersActorSheet |
| New chat message | templates/chat-[result]-result.hbs |
HTML template |
| Style updates | less/heritiers.less or new import |
LESS → npm run build |
| New config constants | modules/heritiers-config.js |
Add to HERITIERS_CONFIG |
| Game mechanics | modules/heritiers-utility.js or -commands.js |
Static utilities or commands |
5.3 Registration Steps for New Item Type
- Update template.json.backup - Add schema definition
- Create DataModel -
modules/models/[type].mjs - Update heritiers-config.js - Add enums if needed
- Create Sheet -
modules/applications/sheets/[type]-sheet.mjs - Create Template -
templates/item-[type]-sheet.hbs - Update heritiers-main.js - Register sheet class
- Add to models/index.mjs - Export DataModel
- Add to applications/sheets/_module.mjs - Export Sheet class
- Update localization - Add type to lang/fr.json TYPES.Item
- Update system.json - If exposing in UI
5.4 Debugging
- Check browser console for JS errors
- Use Chrome DevTools on http://localhost:30000 (Foundry dev port)
- Hot reload enabled for:
.css,.html,.hbs,.jsonfiles (from system.json flags) - Git history available via
git logorgit show <commit>
6. KEY STATISTICS
| Metric | Value |
|---|---|
| System ID | fvtt-les-heritiers |
| Version | 13.0.7 |
| Total Source LOC | 12,273 lines |
| Actor Types | 2 (personnage, pnj) |
| Item Types | 14 (arme, protection, competence, etc.) |
| Compendium Packs | 11 |
| Template Files | 27 (.hbs) |
| Module Files | 28 (.js/.mjs) |
| Model Files | 16 (DataModels) |
| Sheet Files | 14 (+ 1 base) |
| LESS Files | 2 |
| Localization Files | 1 (fr.json, 24 lines) |
| Dev Dependencies | 3 (gulp, gulp-less, gulp-sourcemaps) |
| Characteristics | 8 (agi, con, for, prec, esp, per, pres, san) |
| Competence Profiles | 6 + magic (aventurier, roublard, combattant, erudit, savant, gentleman) |
| Difficulty Scale | 30 levels (-1 to 30) |
| Last Commit | "Correction sur calcul du rang" (recent) |
7. EXTERNAL RESOURCES
- Official Publisher: Titam France / Sombres Projets
- System Repository: https://www.uberwald.me/gitea/public/fvtt-les-heritiers
- Foundry Documentation: https://foundryvtt.com/articles/
- Foundry v13 API: DataModels, AppV2 sheets (major changes in this version)
8. RECOMMENDATIONS FOR DEVELOPERS
- Use AppV2 Sheet API for all new sheets (do not use legacy v1)
- Define DataModels for type data rather than inline templates
- Keep French localization in code/templates (minimal i18n setup)
- Test with
npm run watchduring development - Use socket messages for multiplayer features
- Follow naming conventions (Heritiers[Type], snake_case for fields, French labels)
- Document dice mechanics when adding new rolls
- Keep configuration constants in heritiers-config.js
- Use Handlebars helpers rather than inline logic in templates
- Register sheets in heritiers-main.js and export from _module.mjs files