Compare commits
79 Commits
fvtt-les-h
...
13.1.7
| Author | SHA1 | Date | |
|---|---|---|---|
| 038aa37838 | |||
| 25b6e30aa8 | |||
| f445741eda | |||
| d3c24e44d8 | |||
| d2cf0d80b1 | |||
| 47350cb2f1 | |||
| 26e46cf10a | |||
| 4722fdf4d6 | |||
| dc3040df26 | |||
| b1bce86604 | |||
| 6066091d8d | |||
| 287b6d83a7 | |||
| d8efba89a1 | |||
| 936d525503 | |||
| b113f630bf | |||
| 939cfb1e86 | |||
| 5f5e0e2a8c | |||
| c993a9a5b1 | |||
| cc7de0e53c | |||
| 4d41986c12 | |||
| d04731f475 | |||
| 44a641a0ca | |||
| 1ad022d193 | |||
| 1c7cf343b1 | |||
| d4b00e3508 | |||
| adc912e6cd | |||
| 51a457ebf6 | |||
| 2e9c558027 | |||
| bcd0758328 | |||
| 2b680a203f | |||
| e3d7874dce | |||
| ab6a5832c0 | |||
| d83a999974 | |||
| b83890a764 | |||
| 5ad3c165e5 | |||
| 2b3e774cbb | |||
| 96f8d2bceb | |||
| e288c90ee4 | |||
| 8916de8613 | |||
| 8598df5a57 | |||
| 8781462c8d | |||
| 8c38aead3e | |||
| 67bf71e6c0 | |||
| 63d15e82bb | |||
| 62c3787cea | |||
| df61abac19 | |||
| a7d1a14c52 | |||
| b0dc6f36e4 | |||
| 5109d2aa91 | |||
| 51c162ecbb | |||
| 44d02b0cd1 | |||
| 9b1600304a | |||
| 2dff59c829 | |||
| 55a2a8e3c3 | |||
| 2da1f56a91 | |||
| 66bd9dd2c8 | |||
| 15427f3747 | |||
| 577eccbbd3 | |||
| 05026d454b | |||
| 6497369d7f | |||
| 5e5ddd1c3b | |||
| a72108db5b | |||
| 6a46faadc2 | |||
| e95f7de0c5 | |||
| 9d3ef8cbeb | |||
| c6ec1b74a2 | |||
| 1b12dc44c9 | |||
| f26cd7670c | |||
| 02f8207fb7 | |||
| 439797e71e | |||
| 1d82a6aa60 | |||
| 11b0f22aa7 | |||
| b0a3cb08cb | |||
| 2f3a8e91bd | |||
| f00825ea91 | |||
| 3fa80b6e57 | |||
| fac6618b74 | |||
| d554656925 | |||
| d8e59e8b90 |
63
.gitea/workflows/release.yaml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
name: Release Creation
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
|
||||||
|
|
||||||
|
#- uses: actions/checkout@v3
|
||||||
|
- uses: RouxAntoine/checkout@v3.5.4
|
||||||
|
|
||||||
|
# get part of the tag after the `v`
|
||||||
|
- name: Extract tag version number
|
||||||
|
id: get_version
|
||||||
|
uses: battila7/get-version-action@v2
|
||||||
|
|
||||||
|
# Substitute the Manifest and Download URLs in the system.json
|
||||||
|
- name: Substitute Manifest and Download Links For Versioned Ones
|
||||||
|
id: sub_manifest_link_version
|
||||||
|
uses: microsoft/variable-substitution@v1
|
||||||
|
with:
|
||||||
|
files: 'system.json'
|
||||||
|
env:
|
||||||
|
version: ${{steps.get_version.outputs.version-without-v}}
|
||||||
|
url: https://www.uberwald.me/gitea/${{gitea.repository}}
|
||||||
|
manifest: https://www.uberwald.me/gitea/public/fvtt-les-heritiers/releases/download/latest/system.json
|
||||||
|
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-les-heritiers.zip
|
||||||
|
|
||||||
|
# Create a zip file with all files required by the module to add to the release
|
||||||
|
- run: |
|
||||||
|
apt update -y
|
||||||
|
apt install -y zip
|
||||||
|
|
||||||
|
- run: zip -r ./fvtt-les-heritiers.zip system.json README.md LICENCE.txt assets/ lang/ modules/ packs/ styles/ templates/
|
||||||
|
|
||||||
|
- name: setup go
|
||||||
|
uses: https://github.com/actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: '>=1.20.1'
|
||||||
|
|
||||||
|
- name: Use Go Action
|
||||||
|
id: use-go-action
|
||||||
|
uses: https://gitea.com/actions/release-action@main
|
||||||
|
with:
|
||||||
|
files: |-
|
||||||
|
./fvtt-les-heritiers.zip
|
||||||
|
system.json
|
||||||
|
api_key: '${{secrets.ALLOW_PUSH_RELEASE}}'
|
||||||
|
|
||||||
|
- name: Publish to Foundry server
|
||||||
|
uses: https://github.com/djlechuck/foundryvtt-publish-package-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }}
|
||||||
|
id: 'fvtt-les-heritiers'
|
||||||
|
version: ${{github.event.release.tag_name}}
|
||||||
|
manifest: 'https://www.uberwald.me/gitea/public/fvtt-les-heritiers/releases/download/latest/system.json'
|
||||||
|
notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-les-heritiers.zip'
|
||||||
|
compatibility-minimum: '13'
|
||||||
|
compatibility-verified: '13'
|
||||||
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.history/
|
||||||
|
node_modules
|
||||||
598
DEVELOPER_INSTRUCTIONS.md
Normal file
@@ -0,0 +1,598 @@
|
|||||||
|
# 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)
|
||||||
|
```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**:
|
||||||
|
```bash
|
||||||
|
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 (imports `simple-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`, `json`
|
||||||
|
- `hotReload.paths`: `styles`, `./`, `templates`, `lang/fr.json`
|
||||||
|
- `primaryTokenAttribute`: `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 `personnage` with 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) | Character archetypes |
|
||||||
|
| `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.lesheritiers` namespace with utilities, config, models, sheets
|
||||||
|
- Socket message handling
|
||||||
|
|
||||||
|
**heritiers-config.js**:
|
||||||
|
- `HERITIERS_CONFIG` object with ~40 enum dictionaries:
|
||||||
|
- `caracList`: 8 characteristics with labels
|
||||||
|
- `competenceProfil`: 6 profiles (aventurier, roublard, combattant, erudit, savant, gentleman) + magic
|
||||||
|
- `baseTestPouvoir`, `resistancePouvoir`, `typePouvoir`
|
||||||
|
- `seuilsDifficulte`: 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`, `defenderStore` for roll tracking
|
||||||
|
- Handlebars helpers: count, includes, upper, lower, upperFirst, notEmpty, mul, and, or, eq, isTest, etc.
|
||||||
|
- `preloadHandlebarsTemplates()` - loads 27 template files
|
||||||
|
- `chatListeners()` - click handlers for chat
|
||||||
|
- `loadCompendium()` - loads packs dynamically
|
||||||
|
- `onSocketMesssage()` - 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.hbs` for 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 rendering
|
||||||
|
- `partial-item-header.hbs` - Item sheet header (common)
|
||||||
|
- `partial-item-nav.hbs` - Tabbed navigation
|
||||||
|
- `partial-item-description.hbs` - Description editor
|
||||||
|
- `partial-utile-skills.hbs` - Skill list filtering
|
||||||
|
- `post-item.hbs` - Chat card for item description
|
||||||
|
- `editor-notes-gm.hbs` - GM notes editor
|
||||||
|
|
||||||
|
**Chat Results** (3 files):
|
||||||
|
- `chat-generic-result.hbs` - Generic roll result
|
||||||
|
- `chat-cc-result.hbs` - Close combat result
|
||||||
|
- `chat-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`, `flexcol` CSS classes)
|
||||||
|
- Partial templates for reusability
|
||||||
|
|
||||||
|
### 2.5 Localization (lang/fr.json)
|
||||||
|
|
||||||
|
**Minimal localization** - only 24 lines:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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 template
|
||||||
|
- `simple-converted.less` - Full stylesheet (converted from Bootstrap/template.css)
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
- Compiled to `/styles/heritiers.css` (referenced in system.json)
|
||||||
|
- Source maps generated (`.map` files)
|
||||||
|
|
||||||
|
**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.json`
|
||||||
|
- `normalize.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)**:
|
||||||
|
```javascript
|
||||||
|
export class HeritiersActor extends Actor {
|
||||||
|
static async create(data, options) { ... }
|
||||||
|
// Custom methods and hooks
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example (models/personnage.mjs)**:
|
||||||
|
```javascript
|
||||||
|
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+)**:
|
||||||
|
```javascript
|
||||||
|
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 values
|
||||||
|
- `HTMLField` - Rich-text fields (description, notes, etc.)
|
||||||
|
- `NumberField` - Integers (integer: true), floats
|
||||||
|
- `BooleanField` - Boolean flags
|
||||||
|
- `ArrayField` - 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 sheet
|
||||||
|
- `item-[type]-sheet.hbs` - Type-specific item sheet
|
||||||
|
- `partial-[purpose].hbs` - Reusable component
|
||||||
|
- `chat-[result-type].hbs` - Chat message template
|
||||||
|
- `editor-[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):
|
||||||
|
```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 `initial` values
|
||||||
|
- **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):
|
||||||
|
```javascript
|
||||||
|
CONFIG.Combat.initiative = {
|
||||||
|
formula: "1d10",
|
||||||
|
decimals: 1
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Roll Data Storage** (heritiers-utility.js):
|
||||||
|
- `rollDataStore = {}` - Tracks active rolls by ID
|
||||||
|
- `defenderStore = {}` - 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: `__facesAdjacentes` object 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):
|
||||||
|
```javascript
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
1. **Update template.json.backup** - Add schema definition
|
||||||
|
2. **Create DataModel** - `modules/models/[type].mjs`
|
||||||
|
3. **Update heritiers-config.js** - Add enums if needed
|
||||||
|
4. **Create Sheet** - `modules/applications/sheets/[type]-sheet.mjs`
|
||||||
|
5. **Create Template** - `templates/item-[type]-sheet.hbs`
|
||||||
|
6. **Update heritiers-main.js** - Register sheet class
|
||||||
|
7. **Add to models/index.mjs** - Export DataModel
|
||||||
|
8. **Add to applications/sheets/_module.mjs** - Export Sheet class
|
||||||
|
9. **Update localization** - Add type to lang/fr.json TYPES.Item
|
||||||
|
10. **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`, `.json` files (from system.json flags)
|
||||||
|
- Git history available via `git log` or `git 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](http://www.titam-france.fr)
|
||||||
|
- **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
|
||||||
|
|
||||||
|
1. **Use AppV2 Sheet API** for all new sheets (do not use legacy v1)
|
||||||
|
2. **Define DataModels** for type data rather than inline templates
|
||||||
|
3. **Keep French localization** in code/templates (minimal i18n setup)
|
||||||
|
4. **Test with `npm run watch`** during development
|
||||||
|
5. **Use socket messages** for multiplayer features
|
||||||
|
6. **Follow naming conventions** (Heritiers[Type], snake_case for fields, French labels)
|
||||||
|
7. **Document dice mechanics** when adding new rolls
|
||||||
|
8. **Keep configuration constants** in heritiers-config.js
|
||||||
|
9. **Use Handlebars helpers** rather than inline logic in templates
|
||||||
|
10. **Register sheets in heritiers-main.js** and export from _module.mjs files
|
||||||
|
|
||||||
239
DOCUMENTATION_INDEX.md
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# Les Héritiers FoundryVTT System - Documentation Index
|
||||||
|
|
||||||
|
## 📖 Available Documentation
|
||||||
|
|
||||||
|
### 1. **QUICK_REFERENCE.md** ⭐ START HERE
|
||||||
|
- **Length**: 250 lines
|
||||||
|
- **Purpose**: Fast lookup and common task guide
|
||||||
|
- **Best for**: Developers new to the system
|
||||||
|
- **Contains**:
|
||||||
|
- Quick start setup commands
|
||||||
|
- System overview & statistics
|
||||||
|
- Project structure diagram
|
||||||
|
- Key classes and exports
|
||||||
|
- Step-by-step: Adding a new item type
|
||||||
|
- Common edits (characteristics, packs, rolls, UI)
|
||||||
|
- Testing and git workflow
|
||||||
|
- Useful commands
|
||||||
|
|
||||||
|
### 2. **DEVELOPER_INSTRUCTIONS.md** 📚 COMPREHENSIVE GUIDE
|
||||||
|
- **Length**: 598 lines
|
||||||
|
- **Purpose**: Complete architectural reference
|
||||||
|
- **Best for**: Deep understanding and long-term development
|
||||||
|
- **Contains**:
|
||||||
|
- Executive summary
|
||||||
|
- Build/dev commands (package.json, gulpfile.js)
|
||||||
|
- System architecture (metadata, data model, directories)
|
||||||
|
- Directory structure with file descriptions
|
||||||
|
- Template organization & conventions
|
||||||
|
- Localization strategy
|
||||||
|
- Styling (LESS, CSS)
|
||||||
|
- Compendium packs reference
|
||||||
|
- Detailed conventions:
|
||||||
|
- Module/class structure
|
||||||
|
- Data model conventions
|
||||||
|
- Handlebars template patterns
|
||||||
|
- Localization key naming
|
||||||
|
- Dice & roll system
|
||||||
|
- Socket message handling
|
||||||
|
- Recent migration notes (v13)
|
||||||
|
- Development workflow (setup, file organization, registration steps)
|
||||||
|
- Debugging tips
|
||||||
|
- Key statistics
|
||||||
|
- External resources
|
||||||
|
- Developer recommendations
|
||||||
|
|
||||||
|
### 3. **README.md** (ORIGINAL)
|
||||||
|
- **Length**: 29 lines
|
||||||
|
- **Purpose**: Project overview and credits
|
||||||
|
- **Contains**:
|
||||||
|
- Project description (English & French)
|
||||||
|
- Copyright & authorization notes
|
||||||
|
- Developer credits
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 How to Use This Documentation
|
||||||
|
|
||||||
|
### For First-Time Setup (5-10 minutes)
|
||||||
|
1. Read **QUICK_REFERENCE.md** → "Quick Start" section
|
||||||
|
2. Run `npm install` and `npm run watch`
|
||||||
|
3. Skim the "Project Structure" section
|
||||||
|
|
||||||
|
### For Adding a New Feature (30 minutes)
|
||||||
|
1. Open **QUICK_REFERENCE.md** → "Adding a New Item Type" section
|
||||||
|
2. Follow the numbered steps
|
||||||
|
3. Reference **DEVELOPER_INSTRUCTIONS.md** for deeper understanding of each step
|
||||||
|
4. Test with `npm run watch`
|
||||||
|
|
||||||
|
### For Understanding the System (1-2 hours)
|
||||||
|
1. Start with **DEVELOPER_INSTRUCTIONS.md** → "Executive Summary"
|
||||||
|
2. Read section 2: "System Architecture"
|
||||||
|
3. Read section 3: "Key Conventions"
|
||||||
|
4. Review section 5: "Development Workflow"
|
||||||
|
|
||||||
|
### For Debugging a Bug (varies)
|
||||||
|
1. Check **QUICK_REFERENCE.md** → "Useful Commands" for search/analysis tools
|
||||||
|
2. Reference **DEVELOPER_INSTRUCTIONS.md** → Section 3 for patterns
|
||||||
|
3. Look at git history: `git log --oneline | head -20`
|
||||||
|
4. Check browser console (DevTools) at `http://localhost:30000`
|
||||||
|
|
||||||
|
### For Making Common Edits (5-15 minutes each)
|
||||||
|
Reference **QUICK_REFERENCE.md** → "Common Edits" section:
|
||||||
|
- Adding a new characteristic
|
||||||
|
- Adding a new compendium pack
|
||||||
|
- Adding a new roll type
|
||||||
|
- Changing UI layout
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Quick System Overview
|
||||||
|
|
||||||
|
| Aspect | Detail |
|
||||||
|
|--------|--------|
|
||||||
|
| **System ID** | `fvtt-les-heritiers` |
|
||||||
|
| **Version** | 13.0.7 |
|
||||||
|
| **Foundry** | v13+ (AppV2, DataModels) |
|
||||||
|
| **Language** | French (monolingual) |
|
||||||
|
| **Build Tool** | Gulp 4 + LESS |
|
||||||
|
| **Code** | 12,273 LOC total |
|
||||||
|
| **Actor Types** | 2 (personnage, pnj) |
|
||||||
|
| **Item Types** | 14 types |
|
||||||
|
| **Compendium Packs** | 11 LevelDB packs |
|
||||||
|
| **Sheets** | 16 AppV2 sheets |
|
||||||
|
| **Architecture** | ES6 modules, TypeDataModel |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗂️ Repository Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
fvtt-les-heritiers/
|
||||||
|
├── README.md # Original project readme
|
||||||
|
├── QUICK_REFERENCE.md # ⭐ Start here (250 lines)
|
||||||
|
├── DEVELOPER_INSTRUCTIONS.md # 📚 Deep dive (598 lines)
|
||||||
|
├── DOCUMENTATION_INDEX.md # This file
|
||||||
|
├── package.json # npm config (build commands)
|
||||||
|
├── gulpfile.js # Gulp tasks (LESS → CSS)
|
||||||
|
├── system.json # FoundryVTT manifest
|
||||||
|
├── template.json.backup # Data schema reference
|
||||||
|
├── LICENCE.txt # License
|
||||||
|
├── modules/ # Source code (12,273 LOC)
|
||||||
|
│ ├── heritiers-main.js # Foundry init & registration
|
||||||
|
│ ├── heritiers-actor.js # HeritiersActor class
|
||||||
|
│ ├── heritiers-item.js # HeritiersItem class
|
||||||
|
│ ├── heritiers-config.js # Game constants (HERITIERS_CONFIG)
|
||||||
|
│ ├── heritiers-utility.js # Static utilities & helpers
|
||||||
|
│ ├── heritiers-commands.js # Chat commands
|
||||||
|
│ ├── heritiers-combat.js # Combat handling
|
||||||
|
│ ├── models/ # 16 TypeDataModels
|
||||||
|
│ │ └── personnage.mjs, arme.mjs, competence.mjs, ...
|
||||||
|
│ └── applications/sheets/ # 15 AppV2 Sheet classes
|
||||||
|
│ └── personnage-sheet.mjs, arme-sheet.mjs, ...
|
||||||
|
├── templates/ # 27 Handlebars templates
|
||||||
|
│ ├── actor-sheet.hbs # Character sheet
|
||||||
|
│ ├── actor-pnj-sheet.hbs # NPC sheet
|
||||||
|
│ ├── item-[type]-sheet.hbs # Item sheets (14)
|
||||||
|
│ ├── partial-*.hbs # Reusable components
|
||||||
|
│ ├── chat-*-result.hbs # Chat templates
|
||||||
|
│ └── editor-*.hbs # Editor components
|
||||||
|
├── lang/
|
||||||
|
│ └── fr.json # Localization (24 lines, minimal)
|
||||||
|
├── less/
|
||||||
|
│ ├── heritiers.less # Entry point
|
||||||
|
│ └── simple-converted.less # Imported styles
|
||||||
|
├── styles/
|
||||||
|
│ ├── heritiers.css # Compiled output (generated)
|
||||||
|
│ └── heritiers.css.map # Source map (generated)
|
||||||
|
├── packs/ # 11 Compendium packs (LevelDB)
|
||||||
|
├── srcdata/ # Source data for compilation
|
||||||
|
├── assets/ # UI assets (images, etc.)
|
||||||
|
└── .git/ # Git history available
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Common Tasks Quick Links
|
||||||
|
|
||||||
|
| Task | Guide | Time |
|
||||||
|
|------|-------|------|
|
||||||
|
| Initial setup | QUICK_REFERENCE.md → Quick Start | 5 min |
|
||||||
|
| Add item type | QUICK_REFERENCE.md → Adding a New Item Type | 30 min |
|
||||||
|
| Fix a bug | DEVELOPER_INSTRUCTIONS.md → Section 5 | 15-60 min |
|
||||||
|
| Understand architecture | DEVELOPER_INSTRUCTIONS.md → Section 2 | 30 min |
|
||||||
|
| Change UI layout | QUICK_REFERENCE.md → Change UI layout | 10 min |
|
||||||
|
| Add characteristic | QUICK_REFERENCE.md → Add a new characteristic | 20 min |
|
||||||
|
| Add new roll type | QUICK_REFERENCE.md → Add a new roll type | 25 min |
|
||||||
|
| Debug an issue | QUICK_REFERENCE.md → Testing Changes | 10-30 min |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Documentation Standards
|
||||||
|
|
||||||
|
All documentation follows these principles:
|
||||||
|
|
||||||
|
1. **Clarity**: Written for developers unfamiliar with the system
|
||||||
|
2. **Completeness**: All information needed to perform tasks included
|
||||||
|
3. **Accuracy**: Based on actual code analysis, not assumptions
|
||||||
|
4. **Brevity**: Concise sections with clear headings
|
||||||
|
5. **Examples**: Code snippets and patterns provided
|
||||||
|
6. **Cross-references**: Links between guides for deeper understanding
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Keeping Documentation Updated
|
||||||
|
|
||||||
|
When making significant changes:
|
||||||
|
|
||||||
|
1. **Update QUICK_REFERENCE.md** if:
|
||||||
|
- Adding/removing item types
|
||||||
|
- Changing build commands
|
||||||
|
- Adding/removing features
|
||||||
|
- Changing conventions
|
||||||
|
|
||||||
|
2. **Update DEVELOPER_INSTRUCTIONS.md** if:
|
||||||
|
- Major architecture changes
|
||||||
|
- New development patterns established
|
||||||
|
- New statistics to report
|
||||||
|
- New sections needed
|
||||||
|
|
||||||
|
3. **Update git history** (automatic):
|
||||||
|
- Commit messages explain changes
|
||||||
|
- Reference these in DEVELOPER_INSTRUCTIONS.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Questions?
|
||||||
|
|
||||||
|
Refer to:
|
||||||
|
- **FoundryVTT Docs**: https://foundryvtt.com/articles/
|
||||||
|
- **Game Rules**: https://titam-france.fr
|
||||||
|
- **Git History**: `git log --oneline` (see recent commits)
|
||||||
|
- **Code Comments**: Most functions have JSDoc-style comments
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: March 16, 2025
|
||||||
|
**Documentation Version**: 1.0
|
||||||
|
**System Version**: 13.0.7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎓 Learning Path
|
||||||
|
|
||||||
|
**Day 1**: Quick understanding
|
||||||
|
1. Read QUICK_REFERENCE.md (20 min)
|
||||||
|
2. Run setup commands (5 min)
|
||||||
|
3. Explore directory structure (10 min)
|
||||||
|
|
||||||
|
**Week 1**: Basic development
|
||||||
|
1. Read DEVELOPER_INSTRUCTIONS.md sections 1-3 (45 min)
|
||||||
|
2. Add a simple new item type (1-2 hours)
|
||||||
|
3. Fix a small bug (30 min - 1 hour)
|
||||||
|
|
||||||
|
**Week 2+**: Deep expertise
|
||||||
|
1. Read full DEVELOPER_INSTRUCTIONS.md (1-2 hours)
|
||||||
|
2. Add complex feature (3-6 hours)
|
||||||
|
3. Understand DataModels & AppV2 sheets intimately
|
||||||
|
4. Review git history for patterns (1-2 hours)
|
||||||
|
|
||||||
250
QUICK_REFERENCE.md
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
# Les Héritiers FoundryVTT System - Quick Reference
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install # Install once
|
||||||
|
npm run watch # Development watch (LESS → CSS)
|
||||||
|
npm run build # Build once (production)
|
||||||
|
```
|
||||||
|
|
||||||
|
## System at a Glance
|
||||||
|
|
||||||
|
| Item | Details |
|
||||||
|
|------|---------|
|
||||||
|
| **ID** | `fvtt-les-heritiers` |
|
||||||
|
| **Version** | 13.0.7 |
|
||||||
|
| **Foundry** | v13+ (AppV2, DataModels) |
|
||||||
|
| **Language** | French only |
|
||||||
|
| **Architecture** | ES6 modules, Foundry TypeDataModel |
|
||||||
|
|
||||||
|
## Core Actors (2 types)
|
||||||
|
|
||||||
|
- **`personnage`** - Player Character (full biodata + core mechanics)
|
||||||
|
- **`pnj`** - NPC (same structure, simplified UI)
|
||||||
|
|
||||||
|
### Actor Data Structure
|
||||||
|
- **Characteristics** (8): agi, con, for, prec, esp, per, pres, san
|
||||||
|
- **Ranks** (4): tricherie, feerie, masque, heritage
|
||||||
|
- **Combat**: esquive, parade, initiative, corpsacorps, tir, resistances
|
||||||
|
- **Profiles** (6): aventurier, roublard, combattant, erudit, savant, gentleman + magic
|
||||||
|
|
||||||
|
## Core Items (14 types)
|
||||||
|
|
||||||
|
**Combat**: arme, protection
|
||||||
|
**Equipment**: equipement, accessoire
|
||||||
|
**Character Building**: competence, profil, contact, avantage, desavantage
|
||||||
|
**Powers**: pouvoir, capacitenaturelle, atoutfeerique, fee
|
||||||
|
**Magic**: sort
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
modules/
|
||||||
|
├─ heritiers-main.js # Foundry init, hook setup
|
||||||
|
├─ heritiers-actor.js # Actor class
|
||||||
|
├─ heritiers-item.js # Item class
|
||||||
|
├─ heritiers-config.js # HERITIERS_CONFIG (enums)
|
||||||
|
├─ heritiers-utility.js # Static utilities, Handlebars helpers
|
||||||
|
├─ heritiers-commands.js # Chat commands
|
||||||
|
├─ heritiers-combat.js # Combat class
|
||||||
|
├─ models/ # DataModels for each type
|
||||||
|
│ └─ personnage.mjs, pnj.mjs, arme.mjs, ...
|
||||||
|
└─ applications/sheets/ # AppV2 Sheets
|
||||||
|
└─ personnage-sheet.mjs, arme-sheet.mjs, ...
|
||||||
|
templates/ # 27 Handlebars templates
|
||||||
|
lang/fr.json # Localization (minimal, type names only)
|
||||||
|
less/heritiers.less # LESS compilation entry
|
||||||
|
styles/heritiers.css # Compiled CSS (generated)
|
||||||
|
packs/ # 11 LevelDB compendium packs
|
||||||
|
system.json # Manifest
|
||||||
|
template.json.backup # Data schema reference
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Classes & Exports
|
||||||
|
|
||||||
|
### Actor Classes
|
||||||
|
- **`HeritiersActor`** extends `Actor`
|
||||||
|
- Custom creation: auto-adds utility skills for `personnage`
|
||||||
|
- **PersonnageDataModel**, **PnjDataModel** extend `foundry.abstract.TypeDataModel`
|
||||||
|
|
||||||
|
### Item Classes
|
||||||
|
- **`HeritiersItem`** extends `Item`
|
||||||
|
- **14 DataModels**: ArmeDataModel, CompetenceDataModel, etc.
|
||||||
|
|
||||||
|
### Sheets (AppV2 - Foundry v13+)
|
||||||
|
- **`HeritiersPersonnageSheet`**, **`HeritiersPnjSheet`** extend ActorSheetV2
|
||||||
|
- **`HeritiersArmeSheet`**, **`HeritiersCompetenceSheet`**, etc. (14 item sheets)
|
||||||
|
- All use `HandlebarsApplicationMixin`
|
||||||
|
- Registered in `heritiers-main.js` with `foundry.documents.collections.[Actors|Items].registerSheet()`
|
||||||
|
|
||||||
|
## Adding a New Item Type
|
||||||
|
|
||||||
|
1. Add schema to `template.json.backup`
|
||||||
|
2. Create `modules/models/[typename].mjs` (DataModel)
|
||||||
|
3. Create `modules/applications/sheets/[typename]-sheet.mjs` (AppV2 Sheet)
|
||||||
|
4. Create `templates/item-[typename]-sheet.hbs` (Handlebars template)
|
||||||
|
5. Export from `modules/models/index.mjs`
|
||||||
|
6. Export from `modules/applications/sheets/_module.mjs`
|
||||||
|
7. Register in `heritiers-main.js` (+ other registrations)
|
||||||
|
8. Add to `lang/fr.json` TYPES.Item
|
||||||
|
9. Optionally: add config constants to `heritiers-config.js`
|
||||||
|
|
||||||
|
## Key Constants (heritiers-config.js)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
HERITIERS_CONFIG = {
|
||||||
|
caracList, // 8 characteristics
|
||||||
|
competenceProfil, // 6 profiles + magic
|
||||||
|
seuilsDifficulte, // 30-level difficulty scale
|
||||||
|
baseTestPouvoir, // feerie, masque, autre
|
||||||
|
resistancePouvoir, // physical/psychic active/passive
|
||||||
|
typePouvoir, // actif, passif, metamorphose
|
||||||
|
// ... 30+ more enums
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Handlebars Template Patterns
|
||||||
|
|
||||||
|
**File Naming**:
|
||||||
|
- `actor-sheet.hbs` → Personnage sheet
|
||||||
|
- `actor-pnj-sheet.hbs` → NPC sheet
|
||||||
|
- `item-[type]-sheet.hbs` → Item sheet
|
||||||
|
- `partial-*.hbs` → Reusable components
|
||||||
|
- `chat-*-result.hbs` → Chat results
|
||||||
|
- `editor-*.hbs` → Editor components
|
||||||
|
|
||||||
|
**Data Access**:
|
||||||
|
```handlebars
|
||||||
|
{{actor.name}} <!-- Actor name -->
|
||||||
|
{{system.caracteristiques.agi.value}} <!-- Nested field -->
|
||||||
|
{{#each system.items as |item key|}} <!-- Loops -->
|
||||||
|
{{#if system.magie}} ... {{/if}} <!-- Conditionals -->
|
||||||
|
{{eq kind "physical"}} <!-- Custom helper -->
|
||||||
|
```
|
||||||
|
|
||||||
|
**Custom Helpers** (60+):
|
||||||
|
- Comparison: `eq`, `ne`, `lt`, `gt`, `gte`, `lte`
|
||||||
|
- String: `upper`, `lower`, `upperFirst`
|
||||||
|
- Array: `includes`, `count`, `notEmpty`
|
||||||
|
- Math: `mul`, `add`, `sub`, `and`, `or`
|
||||||
|
|
||||||
|
## Localization
|
||||||
|
|
||||||
|
**Single File**: `lang/fr.json`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"TYPES": {
|
||||||
|
"Actor": { "personnage": "Personnage", "pnj": "PNJ" },
|
||||||
|
"Item": { "arme": "Arme", "competence": "Compétence", ... }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Strategy**: Minimal i18n
|
||||||
|
- Type labels in TYPES
|
||||||
|
- Field labels in DataModel schema (French initial values)
|
||||||
|
- Config constants in heritiers-config.js (French)
|
||||||
|
- Template text hardcoded (French)
|
||||||
|
|
||||||
|
**No game.i18n calls** - all text is French-only
|
||||||
|
|
||||||
|
## Dice & Combat
|
||||||
|
|
||||||
|
- **Initiative**: 1d10 (decimal)
|
||||||
|
- **Bonus/Malus**: -6 to +6
|
||||||
|
- **Roll Storage**: `HeritiersUtility.rollDataStore` (socket-aware)
|
||||||
|
- **Defender Tracking**: `HeritiersUtility.defenderStore`
|
||||||
|
|
||||||
|
## Socket Communication
|
||||||
|
|
||||||
|
**Channel**: `"system.fvtt-les-heritiers"`
|
||||||
|
**Handler**: `HeritiersUtility.onSocketMesssage(data)`
|
||||||
|
**Use Cases**: Roll updates, multi-client sync
|
||||||
|
|
||||||
|
## Recent Changes (v13.0.7)
|
||||||
|
|
||||||
|
- ✅ AppV2 sheet migration (from legacy v1)
|
||||||
|
- ✅ DataModels adoption
|
||||||
|
- ✅ ES6 modules (.mjs) throughout
|
||||||
|
- ✅ Jan 21: arme, competence, capacitenaturelle, pouvoir, protection updates
|
||||||
|
- ✅ Rank calculation fixes
|
||||||
|
|
||||||
|
## Common Edits
|
||||||
|
|
||||||
|
### Add a new characteristic?
|
||||||
|
1. Update `template.json.backup` → `caracteristiques`
|
||||||
|
2. Update `PersonnageDataModel` → `caracteristiques` SchemaField
|
||||||
|
3. Update `HERITIERS_CONFIG.caracList`
|
||||||
|
4. Update templates that reference characteristics
|
||||||
|
|
||||||
|
### Add a new compendium pack?
|
||||||
|
1. Add to `system.json` → `packs` array
|
||||||
|
2. Create directory in `packs/[packname]/`
|
||||||
|
3. Add `.db` file via Foundry UI or scripts
|
||||||
|
|
||||||
|
### Add a new roll type?
|
||||||
|
1. Add dialog template: `templates/roll-dialog-*.hbs`
|
||||||
|
2. Add result template: `templates/chat-*-result.hbs`
|
||||||
|
3. Add handler in `heritiers-utility.js` or `heritiers-commands.js`
|
||||||
|
4. Emit via socket if multiplayer: `game.socket.emit("system.fvtt-les-heritiers", data)`
|
||||||
|
|
||||||
|
### Change UI layout?
|
||||||
|
1. Edit `templates/actor-sheet.hbs` or `item-*.hbs`
|
||||||
|
2. Update LESS: edit `less/heritiers.less`
|
||||||
|
3. Run `npm run build` or watch with `npm run watch`
|
||||||
|
4. CSS reloads automatically via hotReload
|
||||||
|
|
||||||
|
## Testing Changes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run watch
|
||||||
|
# Open FoundryVTT on http://localhost:30000
|
||||||
|
# Open system's world
|
||||||
|
# Make edits (files auto-reload)
|
||||||
|
# Check browser console for errors
|
||||||
|
# Test in UI
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git Workflow (Development)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status
|
||||||
|
git add -A
|
||||||
|
git commit -m "Description of change"
|
||||||
|
git push
|
||||||
|
# Check recent commits: git log --oneline
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Count lines of code
|
||||||
|
wc -l modules/*.js modules/**/*.mjs
|
||||||
|
|
||||||
|
# Search for string in code
|
||||||
|
grep -r "searchterm" modules/
|
||||||
|
|
||||||
|
# List all item types
|
||||||
|
grep '"type":' packs/*/[file].db | head -20
|
||||||
|
|
||||||
|
# Check template syntax
|
||||||
|
npm run build 2>&1 | grep error
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- **DEVELOPER_INSTRUCTIONS.md** - Comprehensive guide (this repo)
|
||||||
|
- **README.md** - Project overview & credits
|
||||||
|
- **system.json** - Manifest (metadata, packs, compatibility)
|
||||||
|
- **template.json.backup** - Data schema reference
|
||||||
|
- [FoundryVTT Docs](https://foundryvtt.com/articles/)
|
||||||
|
- [Les Héritiers Books](https://titam-france.fr) - Game rules
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- All code is French (variable names in French, labels in French)
|
||||||
|
- No English strings in UI
|
||||||
|
- Single language system (French)
|
||||||
|
- Heavy use of camelCase following Foundry conventions
|
||||||
|
- AppV2 is the only supported sheet architecture (v13+)
|
||||||
10
README.md
@@ -1,8 +1,8 @@
|
|||||||
# Système Foundry pour Hawkmoon (French RPG, Titam France/Sombres Projets)
|
# Système Foundry pour Les Héritiers (French RPG, Titam France/Sombres Projets)
|
||||||
|
|
||||||
## EN
|
## EN
|
||||||
|
|
||||||
Unofficial system for Hawkmoon (French version from Titam France).
|
Unofficial system for Les Heritiers (from Titam France).
|
||||||
|
|
||||||
This system has been approved by Département des Sombres Projets ( http://www.titam-france.fr/ ), thanks !
|
This system has been approved by Département des Sombres Projets ( http://www.titam-france.fr/ ), thanks !
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ Books are mandatory to play and are available at : http://www.titam-france.fr
|
|||||||
|
|
||||||
## FR
|
## FR
|
||||||
|
|
||||||
Système non-officiel pour le JDR Hawkmoon (Titam France/Sombres Projets).
|
Système non-officiel pour le JDR Les Héritiers (Titam France/Sombres Projets).
|
||||||
|
|
||||||
Ce système a été autorisé par le Département des Sombres Projets ( http://www.titam-france.fr/ ), merci à eux !
|
Ce système a été autorisé par le Département des Sombres Projets ( http://www.titam-france.fr/ ), merci à eux !
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Les livres du jeu sont nécessaires pour jouer, et sont disponibles ici : http:/
|
|||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
Hawkmoon, le jeu de rôle du Troisième Millénaire, is a property of Titam France/Sombres Projets.
|
Les Héritiers, is a property of Titam France/Sombres Projets.
|
||||||
|
|
||||||
# Developmement
|
# Developmement
|
||||||
|
|
||||||
@@ -26,4 +26,4 @@ LeRatierBretonnien
|
|||||||
|
|
||||||
# Tests, icones et saisie des données
|
# Tests, icones et saisie des données
|
||||||
|
|
||||||
Prêtre, Blondin, Zechrub/Chris, Kyllian, Lightbringer
|
Prêtre, Carter
|
||||||
|
|||||||
BIN
assets/icons/aventurier.webp
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
assets/icons/combattant.webp
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
assets/icons/equipement.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/icons/erudit.webp
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
assets/icons/gentleman.webp
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
assets/icons/profil.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/icons/roublard.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
assets/icons/sort.webp
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
assets/scenes/8DjkNeeujp2qff1N-thumb.webp
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/scenes/ZjIQTg8S4hLZ4kXN-thumb.webp
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
assets/scenes/aanMTXv8znDyE6qb-thumb.webp
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/scenes/ypDutqjqZcr7lx6I-thumb.webp
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/ui/heritiers_background_01.webp
Normal file
|
After Width: | Height: | Size: 192 KiB |
35
gulpfile.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const gulp = require('gulp');
|
||||||
|
const less = require('gulp-less');
|
||||||
|
const sourcemaps = require('gulp-sourcemaps');
|
||||||
|
|
||||||
|
// Paths
|
||||||
|
const paths = {
|
||||||
|
styles: {
|
||||||
|
src: 'less/**/*.less',
|
||||||
|
dest: 'styles/'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compile LESS to CSS
|
||||||
|
function styles() {
|
||||||
|
return gulp.src('less/heritiers.less')
|
||||||
|
.pipe(sourcemaps.init())
|
||||||
|
.pipe(less())
|
||||||
|
.pipe(sourcemaps.write('.'))
|
||||||
|
.pipe(gulp.dest(paths.styles.dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch files
|
||||||
|
function watchFiles() {
|
||||||
|
gulp.watch(paths.styles.src, styles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define complex tasks
|
||||||
|
const build = gulp.series(styles);
|
||||||
|
const watch = gulp.series(build, watchFiles);
|
||||||
|
|
||||||
|
// Export tasks
|
||||||
|
exports.styles = styles;
|
||||||
|
exports.build = build;
|
||||||
|
exports.watch = watch;
|
||||||
|
exports.default = build;
|
||||||
47
lang/fr.json
@@ -1,31 +1,24 @@
|
|||||||
{
|
{
|
||||||
"ACTOR": {
|
"TYPES": {
|
||||||
"TypePersonnage": "Personnage",
|
"Actor": {
|
||||||
"TypeCellule": "Cellule",
|
"personnage": "Personnage",
|
||||||
"TypeCreature": "Créature"
|
"pnj": "PNJ"
|
||||||
},
|
},
|
||||||
|
"Item": {
|
||||||
"ITEM": {
|
"accessoire": "Accessoire",
|
||||||
"TypeArtefact": "Artefact",
|
"arme": "Arme",
|
||||||
"TypeArme": "Arme",
|
"atoutfeerique": "Atout féerique",
|
||||||
"TypeTalent": "Talent",
|
"avantage": "Avantage",
|
||||||
"TypeHistorique": "Historique",
|
"capacitenaturelle": "Capacité naturelle",
|
||||||
"TypeProfil": "Profil",
|
"competence": "Compétence",
|
||||||
"TypeCompetence": "Compétence",
|
"contact": "Contact",
|
||||||
"TypeProtection": "Protection",
|
"desavantage": "Désavantage",
|
||||||
"TypeMonnaie": "Monnaie",
|
"equipement": "Equipement",
|
||||||
"TypeEquipement": "Equipement",
|
"fee": "Fée",
|
||||||
"TypeRessource": "Ressource",
|
"pouvoir": "Pouvoir",
|
||||||
"TypeContact": "Contact"
|
"profil": "Profil",
|
||||||
|
"protection": "Protection",
|
||||||
},
|
"sort": "Sort"
|
||||||
|
|
||||||
"HAWKMOON": {
|
|
||||||
"ui": {
|
|
||||||
"editContact": "Modifier le contact",
|
|
||||||
"deleteContact": "Supprimer le contact",
|
|
||||||
"editTrait": "Modifier le trait",
|
|
||||||
"deleteTrait": "Supprimer le trait"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
4
less/heritiers.less
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Main LESS file for Les Héritiers system
|
||||||
|
// Temporarily importing the full converted simple.css while we refactor
|
||||||
|
|
||||||
|
@import "simple-converted";
|
||||||
2809
less/simple-converted.less
Normal file
213
modules/applications/heritiers-roll-dialog.mjs
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import { HeritiersUtility } from "../heritiers-utility.js"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialogue de jet de dé pour Les Héritiers - Version AppV2
|
||||||
|
*/
|
||||||
|
export class HeritiersRollDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and display the roll dialog
|
||||||
|
* @param {HeritiersActor} actor - The actor making the roll
|
||||||
|
* @param {Object} rollData - Data for the roll
|
||||||
|
* @returns {Promise<Object>} - Returns a dialog-like object for compatibility
|
||||||
|
*/
|
||||||
|
static async create(actor, rollData) {
|
||||||
|
// Préparer le contexte pour le template
|
||||||
|
const context = {
|
||||||
|
...rollData,
|
||||||
|
img: actor.img,
|
||||||
|
name: actor.name,
|
||||||
|
config: game.system.lesheritiers.config,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rendre le template en HTML
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
"systems/fvtt-les-heritiers/templates/roll-dialog-generic.hbs",
|
||||||
|
context
|
||||||
|
)
|
||||||
|
|
||||||
|
// Préparer les boutons selon le mode et le niveau
|
||||||
|
const buttons = this._prepareButtons(rollData)
|
||||||
|
|
||||||
|
// Utiliser DialogV2.wait avec le HTML rendu
|
||||||
|
return foundry.applications.api.DialogV2.wait({
|
||||||
|
window: { title: "Test de Capacité", icon: "fa-solid fa-dice" },
|
||||||
|
classes: ["heritiers-roll-dialog"],
|
||||||
|
position: { width: 420 },
|
||||||
|
modal: false,
|
||||||
|
content,
|
||||||
|
buttons,
|
||||||
|
rejectClose: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Préparer les boutons selon le mode et le niveau de compétence
|
||||||
|
* @param {Object} rollData - Data for the roll
|
||||||
|
* @returns {Array} - Array of button configurations
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static _prepareButtons(rollData) {
|
||||||
|
const buttons = []
|
||||||
|
|
||||||
|
// Bouton d8 toujours disponible
|
||||||
|
buttons.push({
|
||||||
|
action: "rolld8",
|
||||||
|
label: "1d8",
|
||||||
|
icon: "fa-solid fa-dice-d8",
|
||||||
|
default: true,
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
this._updateRollDataFromForm(rollData, button.form.elements)
|
||||||
|
this._executeRoll(rollData, "d8")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Bouton d10 si niveau > 0 ou pouvoir
|
||||||
|
const enableD10 = rollData.mode === "pouvoir" || rollData.competence?.system.niveau > 0
|
||||||
|
if (enableD10) {
|
||||||
|
buttons.push({
|
||||||
|
action: "rolld10",
|
||||||
|
label: "1d10",
|
||||||
|
icon: "fa-solid fa-dice-d10",
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
this._updateRollDataFromForm(rollData, button.form.elements)
|
||||||
|
this._executeRoll(rollData, "d10")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bouton d12 si niveau > 1 ou pouvoir
|
||||||
|
const enableD12 = rollData.mode === "pouvoir" || rollData.competence?.system.niveau > 1
|
||||||
|
if (enableD12) {
|
||||||
|
buttons.push({
|
||||||
|
action: "rolld12",
|
||||||
|
label: "1d12",
|
||||||
|
icon: "fa-solid fa-dice-d12",
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
this._updateRollDataFromForm(rollData, button.form.elements)
|
||||||
|
this._executeRoll(rollData, "d12")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bouton Tricherie si disponible
|
||||||
|
if (rollData.tricherie) {
|
||||||
|
buttons.push({
|
||||||
|
action: "rollTricherie",
|
||||||
|
label: "Lancer 1 Tricherie",
|
||||||
|
icon: "fa-solid fa-mask",
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
this._updateRollDataFromForm(rollData, button.form.elements)
|
||||||
|
this._executeRoll(rollData, "tricherie")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bouton Héritage si disponible
|
||||||
|
if (rollData.heritage) {
|
||||||
|
buttons.push({
|
||||||
|
action: "rollHeritage",
|
||||||
|
label: "Lancer 1 Héritage",
|
||||||
|
icon: "fa-solid fa-crown",
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
this._updateRollDataFromForm(rollData, button.form.elements)
|
||||||
|
this._executeRoll(rollData, "heritage")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si mode carac uniquement, on ne garde que d8
|
||||||
|
if (rollData.mode === "carac") {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
action: "rolld8",
|
||||||
|
label: "Lancer 1d8",
|
||||||
|
icon: "fa-solid fa-dice-d8",
|
||||||
|
default: true,
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
this._updateRollDataFromForm(rollData, button.form.elements)
|
||||||
|
this._executeRoll(rollData, "d8")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mettre à jour rollData avec les valeurs du formulaire
|
||||||
|
* @param {Object} rollData - L'objet rollData à mettre à jour
|
||||||
|
* @param {HTMLFormControlsCollection} formElements - Les éléments du formulaire
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static _updateRollDataFromForm(rollData, formElements) {
|
||||||
|
// Seuil de Difficulté
|
||||||
|
if (formElements.sdValue) {
|
||||||
|
rollData.sdValue = Number(formElements.sdValue.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caractéristique
|
||||||
|
if (formElements.caracKey) {
|
||||||
|
rollData.caracKey = String(formElements.caracKey.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bonus/Malus contextuel
|
||||||
|
if (formElements['bonus-malus-context']) {
|
||||||
|
rollData.bonusMalusContext = Number(formElements['bonus-malus-context'].value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attaque à plusieurs
|
||||||
|
if (formElements['bonus-attaque-plusieurs']) {
|
||||||
|
rollData.bonusAttaquePlusieurs = Number(formElements['bonus-attaque-plusieurs'].value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spécialité
|
||||||
|
if (formElements.useSpecialite !== undefined) {
|
||||||
|
rollData.useSpecialite = formElements.useSpecialite.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
// Points d'usage du pouvoir
|
||||||
|
if (formElements.pouvoirPointsUsage) {
|
||||||
|
rollData.pouvoirPointsUsage = Number(formElements.pouvoirPointsUsage.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attaque dans le dos
|
||||||
|
if (formElements.attaqueDos !== undefined) {
|
||||||
|
rollData.attaqueDos = formElements.attaqueDos.checked
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seconde arme
|
||||||
|
if (formElements['bonus-attaque-seconde-arme']) {
|
||||||
|
rollData.secondeArme = String(formElements['bonus-attaque-seconde-arme'].value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attaque ciblée
|
||||||
|
if (formElements['attaque-cible']) {
|
||||||
|
rollData.attaqueCible = String(formElements['attaque-cible'].value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attaque à deux armes
|
||||||
|
if (formElements['bonus-attaque-deux-armes']) {
|
||||||
|
rollData.attaqueDeuxArmes = Number(formElements['bonus-attaque-deux-armes'].value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exécuter le jet de dés
|
||||||
|
* @param {Object} rollData - Data for the roll
|
||||||
|
* @param {String} dice - Type de dé (d8, d10, d12, tricherie, heritage)
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static _executeRoll(rollData, dice) {
|
||||||
|
if (dice === "heritage") {
|
||||||
|
rollData.useHeritage = true
|
||||||
|
} else if (dice === "tricherie") {
|
||||||
|
rollData.useTricherie = true
|
||||||
|
} else {
|
||||||
|
rollData.mainDice = dice
|
||||||
|
}
|
||||||
|
|
||||||
|
HeritiersUtility.rollHeritiers(rollData)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
modules/applications/sheets/_module.mjs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Export all application sheets for Les Héritiers
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Actor Sheets
|
||||||
|
export { default as HeritiersPersonnageSheet } from './personnage-sheet.mjs';
|
||||||
|
export { default as HeritiersPnjSheet } from './pnj-sheet.mjs';
|
||||||
|
|
||||||
|
// Item Sheets
|
||||||
|
export { default as HeritiersAccessoireSheet } from './accessoire-sheet.mjs';
|
||||||
|
export { default as HeritiersArmeSheet } from './arme-sheet.mjs';
|
||||||
|
export { default as HeritiersAtoutFeeriqueSheet } from './atoutfeerique-sheet.mjs';
|
||||||
|
export { default as HeritiersAvantageSheet } from './avantage-sheet.mjs';
|
||||||
|
export { default as HeritiersCapaciteNaturelleSheet } from './capacitenaturelle-sheet.mjs';
|
||||||
|
export { default as HeritiersCompetenceSheet } from './competence-sheet.mjs';
|
||||||
|
export { default as HeritiersContactSheet } from './contact-sheet.mjs';
|
||||||
|
export { default as HeritiersDesavantageSheet } from './desavantage-sheet.mjs';
|
||||||
|
export { default as HeritiersEquipementSheet } from './equipement-sheet.mjs';
|
||||||
|
export { default as HeritiersFeeSheet } from './fee-sheet.mjs';
|
||||||
|
export { default as HeritiersPouvoirSheet } from './pouvoir-sheet.mjs';
|
||||||
|
export { default as HeritiersProfilSheet } from './profil-sheet.mjs';
|
||||||
|
export { default as HeritiersProtectionSheet } from './protection-sheet.mjs';
|
||||||
|
export { default as HeritiersSortSheet } from './sort-sheet.mjs';
|
||||||
19
modules/applications/sheets/accessoire-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersAccessoireSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.accessoire",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-accessoire-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/arme-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersArmeSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.arme",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-arme-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/atoutfeerique-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersAtoutFeeriqueSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.atoutfeerique",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-atoutfeerique-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/avantage-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersAvantageSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.avantage",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-avantage-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
676
modules/applications/sheets/base-actor-sheet.mjs
Normal file
@@ -0,0 +1,676 @@
|
|||||||
|
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||||
|
|
||||||
|
import { HeritiersUtility } from "../../heritiers-utility.js"
|
||||||
|
|
||||||
|
export default class HeritiersActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||||
|
/**
|
||||||
|
* Different sheet modes.
|
||||||
|
* @enum {number}
|
||||||
|
*/
|
||||||
|
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
|
||||||
|
|
||||||
|
constructor(options = {}) {
|
||||||
|
super(options)
|
||||||
|
this.#dragDrop = this.#createDragDropHandlers()
|
||||||
|
this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||||
|
}
|
||||||
|
|
||||||
|
#dragDrop
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["fvtt-les-heritiers", "sheet", "actor"],
|
||||||
|
position: {
|
||||||
|
width: 780,
|
||||||
|
height: 840,
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
resizable: true,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false,
|
||||||
|
},
|
||||||
|
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: "form" }],
|
||||||
|
actions: {
|
||||||
|
editImage: HeritiersActorSheet.#onEditImage,
|
||||||
|
toggleSheet: HeritiersActorSheet.#onToggleSheet,
|
||||||
|
editItem: HeritiersActorSheet.#onEditItem,
|
||||||
|
deleteItem: HeritiersActorSheet.#onDeleteItem,
|
||||||
|
createItem: HeritiersActorSheet.#onCreateItem,
|
||||||
|
equipItem: HeritiersActorSheet.#onEquipItem,
|
||||||
|
modifyQuantity: HeritiersActorSheet.#onModifyQuantity,
|
||||||
|
quantityIncrease: HeritiersActorSheet.#onQuantityIncrease,
|
||||||
|
quantityDecrease: HeritiersActorSheet.#onQuantityDecrease,
|
||||||
|
pvIncrease: HeritiersActorSheet.#onPvIncrease,
|
||||||
|
pvDecrease: HeritiersActorSheet.#onPvDecrease,
|
||||||
|
rollInitiative: HeritiersActorSheet.#onRollInitiative,
|
||||||
|
rollCarac: HeritiersActorSheet.#onRollCarac,
|
||||||
|
rollRang: HeritiersActorSheet.#onRollRang,
|
||||||
|
rollRootCompetence: HeritiersActorSheet.#onRollRootCompetence,
|
||||||
|
rollCompetence: HeritiersActorSheet.#onRollCompetence,
|
||||||
|
rollSort: HeritiersActorSheet.#onRollSort,
|
||||||
|
rollAttaqueArme: HeritiersActorSheet.#onRollAttaqueArme,
|
||||||
|
rollAttaqueBrutaleArme: HeritiersActorSheet.#onRollAttaqueBrutaleArme,
|
||||||
|
rollAttaqueChargeArme: HeritiersActorSheet.#onRollAttaqueChargeArme,
|
||||||
|
rollAssomerArme: HeritiersActorSheet.#onRollAssomerArme,
|
||||||
|
rollPouvoir: HeritiersActorSheet.#onRollPouvoir,
|
||||||
|
toggleMasque: HeritiersActorSheet.#onToggleMasque,
|
||||||
|
dialogRecupUsage: HeritiersActorSheet.#onDialogRecupUsage,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the sheet currently in 'Play' mode?
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get isPlayMode() {
|
||||||
|
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||||
|
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the sheet currently in 'Edit' mode?
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get isEditMode() {
|
||||||
|
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||||
|
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab groups state
|
||||||
|
* @type {object}
|
||||||
|
*/
|
||||||
|
tabGroups = { primary: "competences" }
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const actor = this.document
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
actor: actor,
|
||||||
|
system: actor.system,
|
||||||
|
source: actor.toObject(),
|
||||||
|
fields: actor.schema.fields,
|
||||||
|
systemFields: actor.system.schema.fields,
|
||||||
|
isEditable: this.isEditable,
|
||||||
|
isEditMode: this.isEditMode,
|
||||||
|
isPlayMode: this.isPlayMode,
|
||||||
|
isGM: game.user.isGM,
|
||||||
|
config: CONFIG.HERITIERS,
|
||||||
|
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true }),
|
||||||
|
enrichedHabitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.habitat || "", { async: true }),
|
||||||
|
enrichedRevesetranges: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.revesetranges || "", { async: true }),
|
||||||
|
enrichedSecretsdecouverts: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.secretsdecouverts || "", { async: true }),
|
||||||
|
enrichedQuestions: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.questions || "", { async: true }),
|
||||||
|
enrichedPlayernotes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.playernotes || "", { async: true }),
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_onRender(context, options) {
|
||||||
|
super._onRender(context, options)
|
||||||
|
|
||||||
|
// Activate drag & drop handlers
|
||||||
|
this.#dragDrop.forEach(d => d.bind(this.element))
|
||||||
|
|
||||||
|
// Manual tab navigation
|
||||||
|
const html = this.element
|
||||||
|
const tabLinks = html.querySelectorAll('.sheet-tabs a.item[data-tab]')
|
||||||
|
const tabContents = html.querySelectorAll('.sheet-body .tab[data-group="primary"]')
|
||||||
|
|
||||||
|
// Hide all tabs initially
|
||||||
|
tabContents.forEach(tab => {
|
||||||
|
tab.classList.remove('active')
|
||||||
|
tab.style.display = 'none'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Show active tab
|
||||||
|
const activeTab = this.tabGroups.primary
|
||||||
|
const activeTabContent = html.querySelector(`.tab[data-group="primary"][data-tab="${activeTab}"]`)
|
||||||
|
if (activeTabContent) {
|
||||||
|
activeTabContent.classList.add('active')
|
||||||
|
activeTabContent.style.display = 'block'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activate the corresponding nav link
|
||||||
|
tabLinks.forEach(link => {
|
||||||
|
if (link.dataset.tab === activeTab) {
|
||||||
|
link.classList.add('active')
|
||||||
|
} else {
|
||||||
|
link.classList.remove('active')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Tab click handler
|
||||||
|
tabLinks.forEach(link => {
|
||||||
|
link.addEventListener('click', (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
const tab = link.dataset.tab
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
this.tabGroups.primary = tab
|
||||||
|
|
||||||
|
// Hide all tabs
|
||||||
|
tabContents.forEach(t => {
|
||||||
|
t.classList.remove('active')
|
||||||
|
t.style.display = 'none'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Show selected tab
|
||||||
|
const selectedTab = html.querySelector(`.tab[data-group="primary"][data-tab="${tab}"]`)
|
||||||
|
if (selectedTab) {
|
||||||
|
selectedTab.classList.add('active')
|
||||||
|
selectedTab.style.display = 'block'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update nav links
|
||||||
|
tabLinks.forEach(l => {
|
||||||
|
if (l.dataset.tab === tab) {
|
||||||
|
l.classList.add('active')
|
||||||
|
} else {
|
||||||
|
l.classList.remove('active')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Inline item editing
|
||||||
|
html.querySelectorAll('.edit-item-data').forEach(input => {
|
||||||
|
input.addEventListener('change', (event) => {
|
||||||
|
const li = event.target.closest('.item')
|
||||||
|
const itemId = li?.dataset.itemId
|
||||||
|
const itemType = li?.dataset.itemType
|
||||||
|
const itemField = event.target.dataset.itemField
|
||||||
|
const dataType = event.target.dataset.dtype
|
||||||
|
// Pour les checkboxes, utiliser checked au lieu de value
|
||||||
|
const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value
|
||||||
|
if (itemId && itemType && itemField) {
|
||||||
|
this.actor.editItemField(itemId, itemType, itemField, dataType, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Drag & Drop
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create drag-and-drop workflow handlers for this Application
|
||||||
|
* @returns {DragDrop[]} An array of DragDrop handlers
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#createDragDropHandlers() {
|
||||||
|
return this.options.dragDrop.map((d) => {
|
||||||
|
d.permissions = {
|
||||||
|
dragstart: this._canDragStart.bind(this),
|
||||||
|
drop: this._canDragDrop.bind(this),
|
||||||
|
}
|
||||||
|
d.callbacks = {
|
||||||
|
dragstart: this._onDragStart.bind(this),
|
||||||
|
drop: this._onDrop.bind(this),
|
||||||
|
}
|
||||||
|
return new foundry.applications.ux.DragDrop(d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define whether a user is able to begin a dragstart workflow for a given drag selector
|
||||||
|
* @param {string} selector The candidate HTML selector for dragging
|
||||||
|
* @returns {boolean} Can the current user drag this selector?
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_canDragStart(selector) {
|
||||||
|
return this.isEditable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define whether a user is able to conclude a drag-and-drop workflow for a given drop selector
|
||||||
|
* @param {string} selector The candidate HTML selector for the drop target
|
||||||
|
* @returns {boolean} Can the current user drop on this selector?
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_canDragDrop(selector) {
|
||||||
|
return this.isEditable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions which occur at the beginning of a drag start workflow.
|
||||||
|
* @param {DragEvent} event The originating DragEvent
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDragStart(event) {
|
||||||
|
const li = event.currentTarget.closest(".item")
|
||||||
|
if (!li?.dataset.itemId) return
|
||||||
|
const item = this.actor.items.get(li.dataset.itemId)
|
||||||
|
if (!item) return
|
||||||
|
|
||||||
|
const dragData = item.toDragData()
|
||||||
|
event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions which occur when a dragged element is dropped on a target.
|
||||||
|
* @param {DragEvent} event The originating DragEvent
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
async _onDrop(event) {
|
||||||
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
const actor = this.actor
|
||||||
|
|
||||||
|
// Handle different data types
|
||||||
|
switch (data.type) {
|
||||||
|
case "Item":
|
||||||
|
return this._onDropItem(event, data)
|
||||||
|
case "Actor":
|
||||||
|
return this._onDropActor(event, data)
|
||||||
|
case "ActiveEffect":
|
||||||
|
return this._onDropActiveEffect(event, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle dropping an Item on the actor sheet
|
||||||
|
* @param {DragEvent} event
|
||||||
|
* @param {object} data
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _onDropItem(event, data) {
|
||||||
|
if (!this.actor.isOwner) return false
|
||||||
|
|
||||||
|
let item = await fromUuid(data.uuid)
|
||||||
|
if (item.pack) {
|
||||||
|
item = await HeritiersUtility.searchItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemData = item.toObject ? item.toObject() : item
|
||||||
|
return this.actor.createEmbeddedDocuments("Item", [itemData])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle dropping an Actor on the sheet
|
||||||
|
* @param {DragEvent} event
|
||||||
|
* @param {object} data
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _onDropActor(event, data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle dropping an ActiveEffect on the sheet
|
||||||
|
* @param {DragEvent} event
|
||||||
|
* @param {object} data
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _onDropActiveEffect(event, data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Action Handlers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle between edit and play mode
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #onToggleSheet(event, target) {
|
||||||
|
const wasEditMode = this.isEditMode
|
||||||
|
this._sheetMode = wasEditMode ? this.constructor.SHEET_MODES.PLAY : this.constructor.SHEET_MODES.EDIT
|
||||||
|
this.render({ force: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit the actor image
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditImage(event, target) {
|
||||||
|
const fp = new FilePicker({
|
||||||
|
type: "image",
|
||||||
|
current: this.actor.img,
|
||||||
|
callback: (path) => {
|
||||||
|
this.actor.update({ img: path })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return fp.browse()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit an item
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditItem(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const itemId = li?.dataset.itemId
|
||||||
|
if (!itemId) return
|
||||||
|
const item = this.actor.items.get(itemId)
|
||||||
|
if (item) item.sheet.render(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an item
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onDeleteItem(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
await HeritiersUtility.confirmDelete(this, li)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new item
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onCreateItem(event, target) {
|
||||||
|
const itemType = target.dataset.type
|
||||||
|
|
||||||
|
// Cas spécial pour les sorts avec une compétence spécifique
|
||||||
|
if (itemType === "sort" && target.dataset.sortCompetence) {
|
||||||
|
const sortCompetence = target.dataset.sortCompetence
|
||||||
|
await this.actor.createEmbeddedDocuments('Item', [{
|
||||||
|
name: `Nouveau ${itemType} de ${sortCompetence}`,
|
||||||
|
type: itemType,
|
||||||
|
system: { competence: sortCompetence }
|
||||||
|
}], { renderSheet: true })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.actor.createEmbeddedDocuments("Item", [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equip/unequip an item
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEquipItem(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const itemId = li?.dataset.itemId
|
||||||
|
if (itemId) {
|
||||||
|
await this.actor.equipItem(itemId)
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify item quantity
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onModifyQuantity(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const itemId = li?.dataset.itemId
|
||||||
|
const value = Number(target.dataset.quantiteValue)
|
||||||
|
if (itemId) {
|
||||||
|
await this.actor.incDecQuantity(itemId, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase item quantity
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onQuantityIncrease(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const itemId = li?.dataset.itemId
|
||||||
|
if (itemId) {
|
||||||
|
await this.actor.incDecQuantity(itemId, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrease item quantity
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onQuantityDecrease(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const itemId = li?.dataset.itemId
|
||||||
|
if (itemId) {
|
||||||
|
await this.actor.incDecQuantity(itemId, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase PV
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onPvIncrease(event, target) {
|
||||||
|
const currentPv = this.actor.system.pv.value || 0
|
||||||
|
const maxPv = this.actor.system.pv.max || 0
|
||||||
|
const newPv = Math.min(currentPv + 1, maxPv)
|
||||||
|
await this.actor.update({ 'system.pv.value': newPv })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrease PV
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onPvDecrease(event, target) {
|
||||||
|
const currentPv = this.actor.system.pv.value || 0
|
||||||
|
const newPv = Math.max(currentPv - 1, 0)
|
||||||
|
await this.actor.update({ 'system.pv.value': newPv })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll initiative
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollInitiative(event, target) {
|
||||||
|
await this.actor.rollInitiative()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll caractéristique
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollCarac(event, target) {
|
||||||
|
const key = target.dataset.key
|
||||||
|
if (key) {
|
||||||
|
await this.actor.rollCarac(key, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll rang
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollRang(event, target) {
|
||||||
|
const key = target.dataset.rangKey
|
||||||
|
if (key) {
|
||||||
|
await this.actor.rollRang(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll root competence
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollRootCompetence(event, target) {
|
||||||
|
const compKey = target.dataset.attrKey
|
||||||
|
if (compKey) {
|
||||||
|
await this.actor.rollRootCompetence(compKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll competence
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollCompetence(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const compId = li?.dataset.itemId
|
||||||
|
if (compId) {
|
||||||
|
await this.actor.rollCompetence(compId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll sort
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollSort(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const sortId = li?.dataset.itemId
|
||||||
|
if (sortId) {
|
||||||
|
await this.actor.rollSort(sortId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll attaque arme
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollAttaqueArme(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const armeId = li?.dataset.itemId
|
||||||
|
if (armeId) {
|
||||||
|
await this.actor.rollAttaqueArme(armeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll attaque brutale arme
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollAttaqueBrutaleArme(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const armeId = li?.dataset.itemId
|
||||||
|
if (armeId) {
|
||||||
|
await this.actor.rollAttaqueBrutaleArme(armeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll attaque charge arme
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollAttaqueChargeArme(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const armeId = li?.dataset.itemId
|
||||||
|
if (armeId) {
|
||||||
|
await this.actor.rollAttaqueChargeArme(armeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll assomer arme
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollAssomerArme(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const armeId = li?.dataset.itemId
|
||||||
|
if (armeId) {
|
||||||
|
await this.actor.rollAssomerArme(armeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll pouvoir
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onRollPouvoir(event, target) {
|
||||||
|
const li = target.closest(".item")
|
||||||
|
const pouvoirId = li?.dataset.itemId
|
||||||
|
if (pouvoirId) {
|
||||||
|
await this.actor.rollPouvoir(pouvoirId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle masque
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onToggleMasque(event, target) {
|
||||||
|
await this.actor.toggleMasqueStatut()
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog récupération usage
|
||||||
|
* @param {Event} event
|
||||||
|
* @param {HTMLElement} target
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onDialogRecupUsage(event, target) {
|
||||||
|
new Dialog({
|
||||||
|
title: "Récupération des Points d'Usage",
|
||||||
|
content: "<p>Combien de Points d'Usage souhaitez-vous récupérer ?</p>",
|
||||||
|
buttons: {
|
||||||
|
one: {
|
||||||
|
icon: '<i class="fas fa-check"></i>',
|
||||||
|
label: "1 Point",
|
||||||
|
callback: () => {
|
||||||
|
this.actor.recupUsage(1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
two: {
|
||||||
|
icon: '<i class="fas fa-check"></i>',
|
||||||
|
label: "2 Points",
|
||||||
|
callback: () => {
|
||||||
|
this.actor.recupUsage(2)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
three: {
|
||||||
|
icon: '<i class="fas fa-check"></i>',
|
||||||
|
label: "3 Points",
|
||||||
|
callback: () => {
|
||||||
|
this.actor.recupUsage(3)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
icon: '<i class="fas fa-times"></i>',
|
||||||
|
label: "Annuler"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
default: "one"
|
||||||
|
}).render(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
}
|
||||||
212
modules/applications/sheets/base-item-sheet.mjs
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||||
|
|
||||||
|
export default class HeritiersItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
||||||
|
constructor(options = {}) {
|
||||||
|
super(options)
|
||||||
|
this.#dragDrop = this.#createDragDropHandlers()
|
||||||
|
}
|
||||||
|
|
||||||
|
#dragDrop
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["fvtt-les-heritiers", "item"],
|
||||||
|
position: {
|
||||||
|
width: 620,
|
||||||
|
height: 600,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
submitOnChange: true,
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
resizable: true,
|
||||||
|
},
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
navSelector: 'nav[data-group="primary"]',
|
||||||
|
contentSelector: "section.sheet-body",
|
||||||
|
initial: "description",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
|
||||||
|
actions: {
|
||||||
|
editImage: HeritiersItemSheet.#onEditImage,
|
||||||
|
postItem: HeritiersItemSheet.#onPostItem,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab groups state
|
||||||
|
* @type {object}
|
||||||
|
*/
|
||||||
|
tabGroups = { primary: "description" }
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = {
|
||||||
|
fields: this.document.schema.fields,
|
||||||
|
systemFields: this.document.system.schema.fields,
|
||||||
|
item: this.document,
|
||||||
|
system: this.document.system,
|
||||||
|
source: this.document.toObject(),
|
||||||
|
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }),
|
||||||
|
isEditMode: true,
|
||||||
|
isEditable: this.isEditable,
|
||||||
|
isGM: game.user.isGM,
|
||||||
|
config: CONFIG.HERITIERS,
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_onRender(context, options) {
|
||||||
|
super._onRender(context, options)
|
||||||
|
this.#dragDrop.forEach((d) => d.bind(this.element))
|
||||||
|
|
||||||
|
// Activate tab navigation manually
|
||||||
|
const nav = this.element.querySelector('nav.tabs[data-group]')
|
||||||
|
if (nav) {
|
||||||
|
const group = nav.dataset.group
|
||||||
|
// Activate the current tab
|
||||||
|
const activeTab = this.tabGroups[group] || "description"
|
||||||
|
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||||
|
const tab = link.dataset.tab
|
||||||
|
link.classList.toggle('active', tab === activeTab)
|
||||||
|
link.addEventListener('click', (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
this.tabGroups[group] = tab
|
||||||
|
this.render()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Show/hide tab content
|
||||||
|
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
|
||||||
|
content.classList.toggle('active', content.dataset.tab === activeTab)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Drag-and-Drop Workflow
|
||||||
|
/**
|
||||||
|
* Create drag-and-drop workflow handlers for this Application
|
||||||
|
* @returns {DragDrop[]} An array of DragDrop handlers
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#createDragDropHandlers() {
|
||||||
|
return this.options.dragDrop.map((d) => {
|
||||||
|
d.permissions = {
|
||||||
|
dragstart: this._canDragStart.bind(this),
|
||||||
|
drop: this._canDragDrop.bind(this),
|
||||||
|
}
|
||||||
|
d.callbacks = {
|
||||||
|
dragstart: this._onDragStart.bind(this),
|
||||||
|
dragover: this._onDragOver.bind(this),
|
||||||
|
drop: this._onDrop.bind(this),
|
||||||
|
}
|
||||||
|
return new foundry.applications.ux.DragDrop(d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the User start a drag workflow for a given drag selector?
|
||||||
|
* @param {string} selector The candidate HTML selector for the drag event
|
||||||
|
* @returns {boolean} Can the current user drag this selector?
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_canDragStart(selector) {
|
||||||
|
return this.isEditable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the User drop an entry at a given drop selector?
|
||||||
|
* @param {string} selector The candidate HTML selector for the drop event
|
||||||
|
* @returns {boolean} Can the current user drop on this selector?
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_canDragDrop(selector) {
|
||||||
|
return this.isEditable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for dragstart events.
|
||||||
|
* @param {DragEvent} event The drag start event
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDragStart(event) {
|
||||||
|
const target = event.currentTarget
|
||||||
|
const dragData = { type: "Item", uuid: this.document.uuid }
|
||||||
|
event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for dragover events.
|
||||||
|
* @param {DragEvent} event The drag over event
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDragOver(event) {
|
||||||
|
// Default behavior is fine
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for drop events.
|
||||||
|
* @param {DragEvent} event The drop event
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
async _onDrop(event) {
|
||||||
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
const item = await fromUuid(data.uuid)
|
||||||
|
if (!item) return
|
||||||
|
|
||||||
|
console.log("Item dropped:", item)
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Action Handlers
|
||||||
|
/**
|
||||||
|
* Edit the item image
|
||||||
|
* @param {Event} event The triggering event
|
||||||
|
* @param {HTMLElement} target The target element
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditImage(event, target) {
|
||||||
|
const fp = new FilePicker({
|
||||||
|
type: "image",
|
||||||
|
current: this.document.img,
|
||||||
|
callback: (path) => {
|
||||||
|
this.document.update({ img: path })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return fp.browse()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post item to chat
|
||||||
|
* @param {Event} event The triggering event
|
||||||
|
* @param {HTMLElement} target The target element
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onPostItem(event, target) {
|
||||||
|
let chatData = foundry.utils.duplicate(this.document)
|
||||||
|
if (this.document.actor) {
|
||||||
|
chatData.actor = { id: this.document.actor.id }
|
||||||
|
}
|
||||||
|
// Don't post any image for the item if the default image is used
|
||||||
|
if (chatData.img.includes("/blank.png") || chatData.img.includes("/mystery-man")) {
|
||||||
|
chatData.img = null
|
||||||
|
}
|
||||||
|
// JSON object for easy creation
|
||||||
|
chatData.jsondata = JSON.stringify({
|
||||||
|
compendium: "postedItem",
|
||||||
|
payload: chatData,
|
||||||
|
})
|
||||||
|
|
||||||
|
const html = await renderTemplate('systems/fvtt-les-heritiers/templates/post-item.html', chatData)
|
||||||
|
const chatOptions = {
|
||||||
|
user: game.user.id,
|
||||||
|
content: html,
|
||||||
|
}
|
||||||
|
ChatMessage.create(chatOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
}
|
||||||
19
modules/applications/sheets/capacitenaturelle-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersCapaciteNaturelleSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.capacitenaturelle",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-capacitenaturelle-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
75
modules/applications/sheets/competence-sheet.mjs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersCompetenceSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.competence",
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
addSpecialite: HeritiersCompetenceSheet.#onAddSpecialite,
|
||||||
|
deleteSpecialite: HeritiersCompetenceSheet.#onDeleteSpecialite,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-competence-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_onRender(context, options) {
|
||||||
|
super._onRender(context, options)
|
||||||
|
|
||||||
|
// Attacher les écouteurs pour l'édition des spécialités
|
||||||
|
this.element.querySelectorAll('.edit-specialite').forEach(input => {
|
||||||
|
input.addEventListener('change', async (event) => {
|
||||||
|
const li = event.target.closest('.specialite-item')
|
||||||
|
const index = Number.parseInt(li?.dataset.specialiteIndex)
|
||||||
|
if (index !== undefined && !Number.isNaN(index)) {
|
||||||
|
const spec = foundry.utils.duplicate(this.item.system.specialites) || []
|
||||||
|
if (spec[index]) {
|
||||||
|
spec[index].name = event.target.value
|
||||||
|
await this.item.update({ 'system.specialites': spec })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.element.querySelectorAll('.edit-specialite-description').forEach(textarea => {
|
||||||
|
textarea.addEventListener('change', async (event) => {
|
||||||
|
const li = event.target.closest('.specialite-item')
|
||||||
|
const index = Number.parseInt(li?.dataset.specialiteIndex)
|
||||||
|
if (index !== undefined && !Number.isNaN(index)) {
|
||||||
|
const spec = foundry.utils.duplicate(this.item.system.specialites) || []
|
||||||
|
if (spec[index]) {
|
||||||
|
spec[index].description = event.target.value
|
||||||
|
await this.item.update({ 'system.specialites': spec })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Event Handlers */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
static async #onAddSpecialite(event, target) {
|
||||||
|
let spec = foundry.utils.duplicate(this.item.system.specialites) || []
|
||||||
|
spec.push({ name: "Nouvelle Spécialité", description: "", used: false })
|
||||||
|
await this.item.update({ 'system.specialites': spec })
|
||||||
|
}
|
||||||
|
|
||||||
|
static async #onDeleteSpecialite(event, target) {
|
||||||
|
const li = target.closest(".specialite-item")
|
||||||
|
let index = Number.parseInt(li.dataset.specialiteIndex)
|
||||||
|
let spec = foundry.utils.duplicate(this.item.system.specialites) || []
|
||||||
|
spec.splice(index, 1)
|
||||||
|
await this.item.update({ 'system.specialites': spec })
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/contact-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersContactSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.contact",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-contact-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/desavantage-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersDesavantageSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.desavantage",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-desavantage-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/equipement-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersEquipementSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.equipement",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-equipement-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/fee-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersFeeSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.fee",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-fee-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
59
modules/applications/sheets/personnage-sheet.mjs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import HeritiersActorSheet from "./base-actor-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersPersonnageSheet extends HeritiersActorSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
classes: [...super.DEFAULT_OPTIONS.classes],
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Actor.personnage",
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
...super.DEFAULT_OPTIONS.actions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/actor-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
tabGroups = { primary: "competences" }
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext()
|
||||||
|
const actor = this.document
|
||||||
|
|
||||||
|
// Add personnage-specific data
|
||||||
|
context.skills = actor.getSkills()
|
||||||
|
context.utileSkillsMental = actor.organizeUtileSkills("mental")
|
||||||
|
context.utileSkillsPhysical = actor.organizeUtileSkills("physical")
|
||||||
|
context.competencesMagie = game.system.lesheritiers.config.competencesMagie || []
|
||||||
|
context.futileSkills = actor.organizeFutileSkills()
|
||||||
|
context.contacts = actor.organizeContacts()
|
||||||
|
context.armes = foundry.utils.duplicate(actor.getWeapons())
|
||||||
|
context.monnaies = foundry.utils.duplicate(actor.getMonnaies())
|
||||||
|
context.pouvoirs = foundry.utils.duplicate(actor.getPouvoirs())
|
||||||
|
context.fee = foundry.utils.duplicate(actor.getFee() || {})
|
||||||
|
context.protections = foundry.utils.duplicate(actor.getArmors())
|
||||||
|
context.combat = actor.getCombatValues()
|
||||||
|
context.equipements = foundry.utils.duplicate(actor.getEquipments())
|
||||||
|
context.avantages = foundry.utils.duplicate(actor.getAvantages())
|
||||||
|
context.atouts = foundry.utils.duplicate(actor.getAtouts())
|
||||||
|
context.capacites = foundry.utils.duplicate(actor.getCapacites())
|
||||||
|
context.desavantages = foundry.utils.duplicate(actor.getDesavantages())
|
||||||
|
context.profils = foundry.utils.duplicate(actor.getProfils())
|
||||||
|
context.pvMalus = actor.getPvMalus()
|
||||||
|
context.heritage = game.settings.get("fvtt-les-heritiers", "heritiers-heritage")
|
||||||
|
context.initiative = actor.getFlag("world", "last-initiative") || -1
|
||||||
|
context.magieList = actor.prepareMagie()
|
||||||
|
context.isPNJ = false
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
}
|
||||||
59
modules/applications/sheets/pnj-sheet.mjs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import HeritiersActorSheet from "./base-actor-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersPnjSheet extends HeritiersActorSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
classes: [...super.DEFAULT_OPTIONS.classes],
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Actor.pnj",
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
...super.DEFAULT_OPTIONS.actions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/actor-pnj-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
tabGroups = { primary: "competences" }
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext()
|
||||||
|
const actor = this.document
|
||||||
|
|
||||||
|
// Add PNJ-specific data
|
||||||
|
context.skills = actor.getSkills()
|
||||||
|
context.utileSkillsMental = actor.organizeUtileSkills("mental")
|
||||||
|
context.utileSkillsPhysical = actor.organizeUtileSkills("physical")
|
||||||
|
context.competencesMagie = game.system.lesheritiers.config.competencesMagie || []
|
||||||
|
context.futileSkills = actor.organizeFutileSkills()
|
||||||
|
context.contacts = actor.organizeContacts()
|
||||||
|
context.armes = foundry.utils.duplicate(actor.getWeapons())
|
||||||
|
context.monnaies = foundry.utils.duplicate(actor.getMonnaies())
|
||||||
|
context.pouvoirs = foundry.utils.duplicate(actor.getPouvoirs())
|
||||||
|
context.fee = foundry.utils.duplicate(actor.getFee() || {})
|
||||||
|
context.protections = foundry.utils.duplicate(actor.getArmors())
|
||||||
|
context.combat = actor.getCombatValues()
|
||||||
|
context.equipements = foundry.utils.duplicate(actor.getEquipments())
|
||||||
|
context.avantages = foundry.utils.duplicate(actor.getAvantages())
|
||||||
|
context.atouts = foundry.utils.duplicate(actor.getAtouts())
|
||||||
|
context.capacites = foundry.utils.duplicate(actor.getCapacites())
|
||||||
|
context.desavantages = foundry.utils.duplicate(actor.getDesavantages())
|
||||||
|
context.profils = foundry.utils.duplicate(actor.getProfils())
|
||||||
|
context.pvMalus = actor.getPvMalus()
|
||||||
|
context.heritage = game.settings.get("fvtt-les-heritiers", "heritiers-heritage")
|
||||||
|
context.initiative = actor.getFlag("world", "last-initiative") || -1
|
||||||
|
context.magieList = actor.prepareMagie()
|
||||||
|
context.isPNJ = true
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/pouvoir-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersPouvoirSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.pouvoir",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-pouvoir-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/profil-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersProfilSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.profil",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-profil-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/applications/sheets/protection-sheet.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class HeritiersProtectionSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.protection",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-protection-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
27
modules/applications/sheets/sort-sheet.mjs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import HeritiersItemSheet from "./base-item-sheet.mjs"
|
||||||
|
import { HeritiersUtility } from "../../heritiers-utility.js"
|
||||||
|
|
||||||
|
export default class HeritiersSortSheet extends HeritiersItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
...super.DEFAULT_OPTIONS,
|
||||||
|
window: {
|
||||||
|
...super.DEFAULT_OPTIONS.window,
|
||||||
|
title: "SHEETS.Item.sort",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
sheet: {
|
||||||
|
template: "systems/fvtt-les-heritiers/templates/item-sort-sheet.hbs",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext()
|
||||||
|
context.competencesMagie = HeritiersUtility.getCompetencesMagie() || []
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
/**
|
|
||||||
* Extend the basic ActorSheet with some very simple modifications
|
|
||||||
* @extends {ActorSheet}
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
export class HeritiersActorSheet extends ActorSheet {
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
|
|
||||||
return mergeObject(super.defaultOptions, {
|
|
||||||
classes: ["fvtt-les-heritiers", "sheet", "actor"],
|
|
||||||
template: "systems/fvtt-les-heritiers/templates/actor-sheet.html",
|
|
||||||
width: 640,
|
|
||||||
height: 720,
|
|
||||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
|
|
||||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
|
||||||
editScore: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
async getData() {
|
|
||||||
const objectData = duplicate(this.object)
|
|
||||||
|
|
||||||
let formData = {
|
|
||||||
title: this.title,
|
|
||||||
id: objectData.id,
|
|
||||||
type: objectData.type,
|
|
||||||
img: objectData.img,
|
|
||||||
name: objectData.name,
|
|
||||||
editable: this.isEditable,
|
|
||||||
cssClass: this.isEditable ? "editable" : "locked",
|
|
||||||
system: objectData.system,
|
|
||||||
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
|
|
||||||
limited: this.object.limited,
|
|
||||||
skills: this.actor.getSkills(),
|
|
||||||
utileSkills :this.actor.organizeUtileSkills(),
|
|
||||||
futileSkills :this.actor.organizeFutileSkills(),
|
|
||||||
armes: duplicate(this.actor.getWeapons()),
|
|
||||||
monnaies: duplicate(this.actor.getMonnaies()),
|
|
||||||
fee: duplicate(this.actor.getFee() || {} ),
|
|
||||||
protections: duplicate(this.actor.getArmors()),
|
|
||||||
combat: this.actor.getCombatValues(),
|
|
||||||
equipements: duplicate(this.actor.getEquipments()),
|
|
||||||
avantages: duplicate(this.actor.getAvantages()),
|
|
||||||
atouts: duplicate(this.actor.getAtouts()),
|
|
||||||
capacites: duplicate(this.actor.getCapacites()),
|
|
||||||
desavantages: duplicate(this.actor.getDesavantages()),
|
|
||||||
pvMalus: this.actor.getPvMalus(),
|
|
||||||
initiative: this.actor.getFlag("world", "last-initiative") || -1,
|
|
||||||
description: await TextEditor.enrichHTML(this.object.system.biodata.description, {async: true}),
|
|
||||||
habitat: await TextEditor.enrichHTML(this.object.system.biodata.habitat, {async: true}),
|
|
||||||
options: this.options,
|
|
||||||
owner: this.document.isOwner,
|
|
||||||
editScore: this.options.editScore,
|
|
||||||
isGM: game.user.isGM
|
|
||||||
}
|
|
||||||
this.formData = formData;
|
|
||||||
|
|
||||||
console.log("PC : ", formData, this.object);
|
|
||||||
return formData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
getCelluleTalents( ) {
|
|
||||||
let talents = []
|
|
||||||
for(let cellule of game.actors) {
|
|
||||||
if (cellule.type == "cellule") {
|
|
||||||
let found = cellule.system.members.find( it => it.id == this.actor.id)
|
|
||||||
if (found) {
|
|
||||||
talents = talents.concat( cellule.getTalents() )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return talents
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/** @override */
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
|
|
||||||
// Everything below here is only needed if the sheet is editable
|
|
||||||
if (!this.options.editable) return;
|
|
||||||
|
|
||||||
// Update Inventory Item
|
|
||||||
html.find('.item-edit').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".item")
|
|
||||||
let itemId = li.data("item-id")
|
|
||||||
const item = this.actor.items.get( itemId )
|
|
||||||
item.sheet.render(true)
|
|
||||||
})
|
|
||||||
// Delete Inventory Item
|
|
||||||
html.find('.item-delete').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".item");
|
|
||||||
HeritiersUtility.confirmDelete(this, li);
|
|
||||||
})
|
|
||||||
html.find('.edit-item-data').change(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".item")
|
|
||||||
let itemId = li.data("item-id")
|
|
||||||
let itemType = li.data("item-type")
|
|
||||||
let itemField = $(ev.currentTarget).data("item-field")
|
|
||||||
let dataType = $(ev.currentTarget).data("dtype")
|
|
||||||
let value = ev.currentTarget.value
|
|
||||||
this.actor.editItemField(itemId, itemType, itemField, dataType, value)
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('.adversite-modify').click(event => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let adv = li.data("adversite")
|
|
||||||
let value = Number($(event.currentTarget).data("adversite-value"))
|
|
||||||
this.actor.incDecAdversite(adv, value)
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('.quantity-modify').click(event => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
const value = Number($(event.currentTarget).data("quantite-value"))
|
|
||||||
this.actor.incDecQuantity( li.data("item-id"), value );
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('.roll-initiative').click((event) => {
|
|
||||||
this.actor.rollInitiative()
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('.roll-carac').click((event) => {
|
|
||||||
const key = $(event.currentTarget).data("key")
|
|
||||||
this.actor.rollCarac(key, false)
|
|
||||||
})
|
|
||||||
html.find('.roll-competence').click((event) => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let compId = li.data("item-id")
|
|
||||||
this.actor.rollCompetence(compId)
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('.item-add').click((event) => {
|
|
||||||
const itemType = $(event.currentTarget).data("type")
|
|
||||||
this.actor.createEmbeddedDocuments('Item', [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('.lock-unlock-sheet').click((event) => {
|
|
||||||
this.options.editScore = !this.options.editScore;
|
|
||||||
this.render(true);
|
|
||||||
});
|
|
||||||
html.find('.item-equip').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".item");
|
|
||||||
this.actor.equipItem( li.data("item-id") );
|
|
||||||
this.render(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/** @override */
|
|
||||||
setPosition(options = {}) {
|
|
||||||
const position = super.setPosition(options);
|
|
||||||
const sheetBody = this.element.find(".sheet-body");
|
|
||||||
const bodyHeight = position.height - 192;
|
|
||||||
sheetBody.css("height", bodyHeight);
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/*async _onDropItem(event, dragData) {
|
|
||||||
let data = event.dataTransfer.getData('text/plain')
|
|
||||||
let dataItem = JSON.parse( data)
|
|
||||||
let item = fromUuidSync(dataItem.uuid)
|
|
||||||
if (item.pack) {
|
|
||||||
item = await HeritiersUtility.searchItem(item)
|
|
||||||
}
|
|
||||||
super._onDropItem(event, dragData)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
import { HeritiersUtility } from "./heritiers-utility.js";
|
||||||
import { HeritiersRollDialog } from "./heritiers-roll-dialog.js";
|
import { HeritiersRollDialog } from "./applications/heritiers-roll-dialog.mjs";
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
export class HeritiersCommands {
|
export class HeritiersCommands {
|
||||||
|
|||||||
@@ -8,26 +8,34 @@ export const HERITIERS_CONFIG = {
|
|||||||
"prec": "Précision",
|
"prec": "Précision",
|
||||||
"esp": "Esprit",
|
"esp": "Esprit",
|
||||||
"per": "Perception",
|
"per": "Perception",
|
||||||
"pres": "Présence",
|
"pres": "Prestance",
|
||||||
"san": "Sang-Froid"
|
"san": "Sang-Froid"
|
||||||
},
|
},
|
||||||
|
|
||||||
competenceCategorie : {
|
competenceCategorie: {
|
||||||
"utile": "Utile",
|
"utile": "Utile",
|
||||||
"futile": "Futile"
|
"futile": "Futile"
|
||||||
},
|
},
|
||||||
|
|
||||||
competenceProfil : {
|
contactType: {
|
||||||
"aventurier": "Aventurier",
|
contact: "Contact",
|
||||||
"roublard": "Roublard",
|
allie: "Allié",
|
||||||
"combattant": "Combattant",
|
ennemi: "Ennemi",
|
||||||
"erudit": "Erudit",
|
interet: "Personne d'interêt"
|
||||||
"savant": "Savant",
|
},
|
||||||
"gentleman": "Gentleman"
|
|
||||||
|
competenceProfil: {
|
||||||
|
"aventurier": { kind: "physical", name: "Aventurier" },
|
||||||
|
"roublard": { kind: "physical", name: "Roublard" },
|
||||||
|
"combattant": { kind: "physical", name: "Combattant" },
|
||||||
|
"erudit": { kind: "mental", name: "Erudit" },
|
||||||
|
"savant": { kind: "mental", name: "Savant" },
|
||||||
|
"gentleman": { kind: "mental", name: "Gentleman" },
|
||||||
|
"magie": { kind: "magical", name: "Magie" },
|
||||||
},
|
},
|
||||||
baseTestPouvoir: {
|
baseTestPouvoir: {
|
||||||
"feerie": "Féerie",
|
"feerie": "Féerie",
|
||||||
"Masque": "Masque",
|
"masque": "Masque",
|
||||||
"autre": "Autre"
|
"autre": "Autre"
|
||||||
},
|
},
|
||||||
resistancePouvoir: {
|
resistancePouvoir: {
|
||||||
@@ -43,6 +51,10 @@ export const HERITIERS_CONFIG = {
|
|||||||
"passif": "Passif",
|
"passif": "Passif",
|
||||||
"metamorphose": "Métamorphose"
|
"metamorphose": "Métamorphose"
|
||||||
},
|
},
|
||||||
|
statutMasque: {
|
||||||
|
"masque": "Masqué",
|
||||||
|
"demasque": "Démasqué"
|
||||||
|
},
|
||||||
niveauPouvoir: {
|
niveauPouvoir: {
|
||||||
"normal": "Normal",
|
"normal": "Normal",
|
||||||
"profond": "Profond",
|
"profond": "Profond",
|
||||||
@@ -53,24 +65,43 @@ export const HERITIERS_CONFIG = {
|
|||||||
"demasque": "Démasqué"
|
"demasque": "Démasqué"
|
||||||
},
|
},
|
||||||
seuilsDifficulte: {
|
seuilsDifficulte: {
|
||||||
"0": "Aucun/Non applicable",
|
"-1": "Aucun/Non applicable",
|
||||||
"5": "Enfantine",
|
"5": "Enfantine (5)",
|
||||||
"6": "Triviale",
|
"6": "Triviale (6)",
|
||||||
"8": "Aisée",
|
"7": "Moins Triviale (7)",
|
||||||
"10": "Normale",
|
"8": "Aisée (8)",
|
||||||
"12": "Compliquée",
|
"9": "Moins Aisée (9)",
|
||||||
"14": "Difficile",
|
"10": "Normale (10)",
|
||||||
"16": "Très Difficile",
|
"11": "Moins Normale (11)",
|
||||||
"18": "Critique",
|
"12": "Compliquée (12)",
|
||||||
"20": "Insurmontable",
|
"13": "Plus Compliquée (13)",
|
||||||
"22": "Surhumaine",
|
"14": "Difficile (14)",
|
||||||
"24": "Epique",
|
"15": "Plus Difficile (15)",
|
||||||
"26": "Légendaire",
|
"16": "Très Difficile (16)",
|
||||||
"28": "Mythique",
|
"17": "Très Très Difficile (17)",
|
||||||
"30": "Divine"
|
"18": "Critique (18)",
|
||||||
|
"19": "Plus Critique (19)",
|
||||||
|
"20": "Insurmontable (20)",
|
||||||
|
"21": "Très Insurmontable (21)",
|
||||||
|
"22": "Surhumaine (22)",
|
||||||
|
"23": "Très Surhumaine (23)",
|
||||||
|
"24": "Epique (24)",
|
||||||
|
"25": "Plus Epique (25)",
|
||||||
|
"26": "Légendaire (26)",
|
||||||
|
"27": "Très Légendaire (27)",
|
||||||
|
"28": "Mythique (28)",
|
||||||
|
"29": "Plus Mythique (29)",
|
||||||
|
"30": "Divine (30)"
|
||||||
},
|
},
|
||||||
|
|
||||||
categorieArme : {
|
attaqueCible: {
|
||||||
|
"none": "Aucune",
|
||||||
|
"membre": "Membre",
|
||||||
|
"main": "Main",
|
||||||
|
"tete": "Tête/Coeur"
|
||||||
|
},
|
||||||
|
|
||||||
|
categorieArme: {
|
||||||
"trait": "Arme de trait",
|
"trait": "Arme de trait",
|
||||||
"poing": "Arme de poing",
|
"poing": "Arme de poing",
|
||||||
"epaule": "Arme d'épaule",
|
"epaule": "Arme d'épaule",
|
||||||
@@ -78,7 +109,7 @@ export const HERITIERS_CONFIG = {
|
|||||||
"blanche": "Arme blanche",
|
"blanche": "Arme blanche",
|
||||||
"improvise": "Arme improvisée",
|
"improvise": "Arme improvisée",
|
||||||
"explosif": "Explosif"
|
"explosif": "Explosif"
|
||||||
},
|
},
|
||||||
typeArme: {
|
typeArme: {
|
||||||
"naturelle": "Arme naturelle",
|
"naturelle": "Arme naturelle",
|
||||||
"trait": "Trait",
|
"trait": "Trait",
|
||||||
@@ -100,13 +131,13 @@ export const HERITIERS_CONFIG = {
|
|||||||
"controlee": "Contrôlée (C)",
|
"controlee": "Contrôlée (C)",
|
||||||
"prohibee": "Prohibée (P)"
|
"prohibee": "Prohibée (P)"
|
||||||
},
|
},
|
||||||
armeDissimulation :{
|
armeDissimulation: {
|
||||||
"tresfacile": "Très facile (TF)",
|
"tresfacile": "Très facile (TF)",
|
||||||
"facile": "Facile (F)",
|
"facile": "Facile (F)",
|
||||||
"difficile": "Difficile (D)",
|
"difficile": "Difficile (D)",
|
||||||
"impossible": "Impossible (I)"
|
"impossible": "Impossible (I)"
|
||||||
},
|
},
|
||||||
typeProtection : {
|
typeProtection: {
|
||||||
"balle": "Protège ds balles",
|
"balle": "Protège ds balles",
|
||||||
"melee": "Protège en mélée",
|
"melee": "Protège en mélée",
|
||||||
"tout": "Tout type de dégats"
|
"tout": "Tout type de dégats"
|
||||||
@@ -115,7 +146,137 @@ export const HERITIERS_CONFIG = {
|
|||||||
"traditionnelle": "Traditionnelle",
|
"traditionnelle": "Traditionnelle",
|
||||||
"moderne": "Moderne",
|
"moderne": "Moderne",
|
||||||
"orientale": "Orientale"
|
"orientale": "Orientale"
|
||||||
|
},
|
||||||
|
typeContact: {
|
||||||
|
"contact": "Contact",
|
||||||
|
"allie": "Allié",
|
||||||
|
"ennemi": "Ennemi",
|
||||||
|
"interet": "Personne d'interêt"
|
||||||
|
},
|
||||||
|
niveauContact: {
|
||||||
|
"1": "1",
|
||||||
|
"2": "2",
|
||||||
|
"3": "3",
|
||||||
|
},
|
||||||
|
pointsUsageList: {
|
||||||
|
"1": "1",
|
||||||
|
"2": "2",
|
||||||
|
"3": "3",
|
||||||
|
"4": "4",
|
||||||
|
},
|
||||||
|
attaquePlusieursList: {
|
||||||
|
"0": "0",
|
||||||
|
"1": "+1",
|
||||||
|
"2": "+2",
|
||||||
|
},
|
||||||
|
attaque2ArmesListe: [
|
||||||
|
{ value: "0", label: "Aucun" },
|
||||||
|
{ value: "-4", label: "Deux armes à 1 main" },
|
||||||
|
{ value: "-2", label: "Deux armes naturelles" },
|
||||||
|
{ value: "-2", label: "Avec spécialisation \"Mauvaise Main\"" }
|
||||||
|
],
|
||||||
|
typeProfil: {
|
||||||
|
"mineur": "Mineur",
|
||||||
|
"majeur": "Majeur",
|
||||||
|
},
|
||||||
|
bonusMalusContext: [
|
||||||
|
{ value: "-6", label: "-6" },
|
||||||
|
{ value: "-5", label: "-5" },
|
||||||
|
{ value: "-4", label: "-4" },
|
||||||
|
{ value: "-3", label: "-3" },
|
||||||
|
{ value: "-2", label: "-2" },
|
||||||
|
{ value: "-1", label: "-1" },
|
||||||
|
{ value: "0", label: "0" },
|
||||||
|
{ value: "1", label: "+1" },
|
||||||
|
{ value: "2", label: "+2" },
|
||||||
|
{ value: "3", label: "+3" },
|
||||||
|
{ value: "4", label: "+4" },
|
||||||
|
{ value: "5", label: "+5" },
|
||||||
|
{ value: "6", label: "+6" }
|
||||||
|
],
|
||||||
|
listNiveauSort: {
|
||||||
|
"1": "1",
|
||||||
|
"2": "2",
|
||||||
|
"3": "3",
|
||||||
|
"4": "4"
|
||||||
|
},
|
||||||
|
listRangSort: {
|
||||||
|
"1": "1",
|
||||||
|
"2": "2",
|
||||||
|
"3": "3",
|
||||||
|
"4": "4",
|
||||||
|
"5": "5",
|
||||||
|
"6": "6",
|
||||||
|
"7": "7"
|
||||||
|
},
|
||||||
|
listNiveau: {
|
||||||
|
"0": "0",
|
||||||
|
"1": "1",
|
||||||
|
"2": "2",
|
||||||
|
"3": "3",
|
||||||
|
"4": "4",
|
||||||
|
"5": "5",
|
||||||
|
"6": "6",
|
||||||
|
"7": "7",
|
||||||
|
"8": "8",
|
||||||
|
"9": "9",
|
||||||
|
"10": "10"
|
||||||
|
},
|
||||||
|
rangName: [
|
||||||
|
"Novice",
|
||||||
|
"Novice",
|
||||||
|
"Adepte",
|
||||||
|
"Maître",
|
||||||
|
"Grand Maître"
|
||||||
|
],
|
||||||
|
rangNameSpecific: {
|
||||||
|
"Druidisme": {
|
||||||
|
"Novice": "Eubage",
|
||||||
|
"Adepte": "Saronide",
|
||||||
|
"Maître": "Ovate",
|
||||||
|
"Grand Maître": "Archidruide"
|
||||||
|
},
|
||||||
|
"Faëomancie": {
|
||||||
|
"Novice": "Marmiton",
|
||||||
|
"Adepte": "Queux",
|
||||||
|
"Maître": "Chef",
|
||||||
|
"Grand Maître": "Maître-queux"
|
||||||
|
},
|
||||||
|
"Nécromancie": {
|
||||||
|
"Novice": "Inexpertus",
|
||||||
|
"Adepte": "Discipulus",
|
||||||
|
"Maître": "Dominus",
|
||||||
|
"Grand Maître": "Magister"
|
||||||
|
},
|
||||||
|
"Necromancie": {
|
||||||
|
"Novice": "Inexpertus",
|
||||||
|
"Adepte": "Discipulus",
|
||||||
|
"Maître": "Dominus",
|
||||||
|
"Grand Maître": "Magister"
|
||||||
|
},
|
||||||
|
"Magie du Clan": {
|
||||||
|
"Novice": "Apprenti",
|
||||||
|
"Adepte": "Disciple",
|
||||||
|
"Maître": "Maître",
|
||||||
|
"Grand Maître": "Éminence"
|
||||||
|
},
|
||||||
|
"Théurgie": {
|
||||||
|
"Novice": "Frère",
|
||||||
|
"Adepte": "Père",
|
||||||
|
"Maître": "Saint",
|
||||||
|
"Grand Maître": "Apôtre"
|
||||||
|
},
|
||||||
|
"Grand Langage": {
|
||||||
|
"Novice": "Éveillé",
|
||||||
|
"Adepte": "Initié",
|
||||||
|
"Maître": "Sage",
|
||||||
|
"Grand Maître": "Docteur"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
soufflesMagieDuClan: {
|
||||||
|
"soufflecombat": "Souffle du Combat",
|
||||||
|
"soufflemouvement": "Souffle du Mouvement",
|
||||||
|
"souffleesprit": "Souffle de l'Esprit"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* Extend the basic ActorSheet with some very simple modifications
|
|
||||||
* @extends {ActorSheet}
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { HeritiersActorSheet } from "./heritiers-actor-sheet.js";
|
|
||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
export class HeritiersCreatureSheet extends HeritiersActorSheet {
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
|
|
||||||
return mergeObject(super.defaultOptions, {
|
|
||||||
classes: ["fvtt-les-heritiers", "sheet", "actor"],
|
|
||||||
template: "systems/fvtt-les-heritiers/templates/creature-sheet.html",
|
|
||||||
width: 640,
|
|
||||||
height: 720,
|
|
||||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
|
|
||||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
|
||||||
editScore: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extend the basic ItemSheet with some very simple modifications
|
|
||||||
* @extends {ItemSheet}
|
|
||||||
*/
|
|
||||||
export class HeritiersItemSheet extends ItemSheet {
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
|
|
||||||
return mergeObject(super.defaultOptions, {
|
|
||||||
classes: ["fvtt-les-heritiers", "sheet", "item"],
|
|
||||||
template: "systems/fvtt-les-heritiers/templates/item-sheet.html",
|
|
||||||
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
|
||||||
width: 620,
|
|
||||||
height: 550,
|
|
||||||
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
_getHeaderButtons() {
|
|
||||||
let buttons = super._getHeaderButtons();
|
|
||||||
// Add "Post to chat" button
|
|
||||||
// We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry!
|
|
||||||
buttons.unshift(
|
|
||||||
{
|
|
||||||
class: "post",
|
|
||||||
icon: "fas fa-comment",
|
|
||||||
onclick: ev => { }
|
|
||||||
})
|
|
||||||
return buttons
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/** @override */
|
|
||||||
setPosition(options = {}) {
|
|
||||||
const position = super.setPosition(options);
|
|
||||||
const sheetBody = this.element.find(".sheet-body");
|
|
||||||
const bodyHeight = position.height - 192;
|
|
||||||
sheetBody.css("height", bodyHeight);
|
|
||||||
if (this.item.type.includes('weapon')) {
|
|
||||||
position.width = 640;
|
|
||||||
}
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
async getData() {
|
|
||||||
const objectData = duplicate(this.object)
|
|
||||||
let formData = {
|
|
||||||
title: this.title,
|
|
||||||
id: this.id,
|
|
||||||
type: objectData.type,
|
|
||||||
img: objectData.img,
|
|
||||||
name: objectData.name,
|
|
||||||
editable: this.isEditable,
|
|
||||||
cssClass: this.isEditable ? "editable" : "locked",
|
|
||||||
system: objectData.system,
|
|
||||||
limited: this.object.limited,
|
|
||||||
options: this.options,
|
|
||||||
owner: this.document.isOwner,
|
|
||||||
config: game.system.lesheritiers.config,
|
|
||||||
description: await TextEditor.enrichHTML(this.object.system.description, {async: true}),
|
|
||||||
mr: (this.object.type == 'specialisation'),
|
|
||||||
isGM: game.user.isGM
|
|
||||||
}
|
|
||||||
|
|
||||||
//this.options.editable = !(this.object.origin == "embeddedItem");
|
|
||||||
console.log("ITEM DATA", formData, this);
|
|
||||||
return formData;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
_getHeaderButtons() {
|
|
||||||
let buttons = super._getHeaderButtons();
|
|
||||||
buttons.unshift({
|
|
||||||
class: "post",
|
|
||||||
icon: "fas fa-comment",
|
|
||||||
onclick: ev => this.postItem()
|
|
||||||
});
|
|
||||||
return buttons
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
postItem() {
|
|
||||||
let chatData = duplicate(HeritiersUtility.data(this.item));
|
|
||||||
if (this.actor) {
|
|
||||||
chatData.actor = { id: this.actor.id };
|
|
||||||
}
|
|
||||||
// Don't post any image for the item (which would leave a large gap) if the default image is used
|
|
||||||
if (chatData.img.includes("/blank.png")) {
|
|
||||||
chatData.img = null;
|
|
||||||
}
|
|
||||||
// JSON object for easy creation
|
|
||||||
chatData.jsondata = JSON.stringify(
|
|
||||||
{
|
|
||||||
compendium: "postedItem",
|
|
||||||
payload: chatData,
|
|
||||||
});
|
|
||||||
|
|
||||||
renderTemplate('systems/fvtt-Heritiers-rpg/templates/post-item.html', chatData).then(html => {
|
|
||||||
let chatOptions = HeritiersUtility.chatDataSetup(html);
|
|
||||||
ChatMessage.create(chatOptions)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/** @override */
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
|
|
||||||
// Everything below here is only needed if the sheet is editable
|
|
||||||
if (!this.options.editable) return;
|
|
||||||
|
|
||||||
|
|
||||||
// Update Inventory Item
|
|
||||||
html.find('.item-edit').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".item")
|
|
||||||
const item = this.object.options.actor.getOwnedItem(li.data("item-id"))
|
|
||||||
item.sheet.render(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
html.find('.delete-subitem').click(ev => {
|
|
||||||
this.deleteSubitem(ev);
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('#add-specialite').click(ev => {
|
|
||||||
let spec = duplicate(this.object.system.specialites)
|
|
||||||
spec.push( { name: "Nouvelle Spécialité", id: randomID(16), used: false })
|
|
||||||
this.object.update( { 'system.specialites': spec })
|
|
||||||
})
|
|
||||||
html.find('.delete-specialite').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".specialite-item")
|
|
||||||
let index = li.data("specialite-index")
|
|
||||||
let spec = duplicate(this.object.system.specialites)
|
|
||||||
spec.splice(index,1)
|
|
||||||
this.object.update( { 'system.specialites': spec })
|
|
||||||
})
|
|
||||||
html.find('.edit-specialite').change(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".specialite-item")
|
|
||||||
let index = li.data("specialite-index")
|
|
||||||
let spec = duplicate(this.object.system.specialites)
|
|
||||||
spec[index].name = ev.currentTarget.value
|
|
||||||
spec[index].id = spec[index].id || randomID(16)
|
|
||||||
this.object.update( { 'system.specialites': spec })
|
|
||||||
})
|
|
||||||
html.find('.edit-specialite-description').change(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".specialite-item")
|
|
||||||
let index = li.data("specialite-index")
|
|
||||||
let spec = duplicate(this.object.system.specialites)
|
|
||||||
spec[index].description = ev.currentTarget.value
|
|
||||||
spec[index].id = spec[index].id || randomID(16)
|
|
||||||
this.object.update( { 'system.specialites': spec })
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('#add-automation').click(ev => {
|
|
||||||
let autom = duplicate(this.object.system.automations)
|
|
||||||
autom.push( { eventtype: "on-drop", name: "Automatisation 1", competence: "", minLevel: 0, id: randomID(16) })
|
|
||||||
this.object.update( { 'system.automations': autom })
|
|
||||||
})
|
|
||||||
html.find('.delete-automation').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".automation-item")
|
|
||||||
let index = li.data("automation-index")
|
|
||||||
let autom = duplicate(this.object.system.automations)
|
|
||||||
autom.splice(index,1)
|
|
||||||
this.object.update( { 'system.automations': autom })
|
|
||||||
})
|
|
||||||
html.find('.automation-edit-field').change(ev => {
|
|
||||||
let index = $(ev.currentTarget).data("automation-index")
|
|
||||||
let field = $(ev.currentTarget).data("automation-field")
|
|
||||||
let auto = duplicate(this.object.system.automations)
|
|
||||||
auto[index][field] = ev.currentTarget.value
|
|
||||||
auto[index].id = auto[index].id || randomID(16)
|
|
||||||
this.object.update( { 'system.automations': auto })
|
|
||||||
})
|
|
||||||
|
|
||||||
// Update Inventory Item
|
|
||||||
html.find('.item-delete').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".item");
|
|
||||||
let itemId = li.data("item-id");
|
|
||||||
let itemType = li.data("item-type");
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
get template() {
|
|
||||||
let type = this.item.type;
|
|
||||||
return `systems/fvtt-les-heritiers/templates/item-${type}-sheet.html`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/** @override */
|
|
||||||
_updateObject(event, formData) {
|
|
||||||
return this.object.update(formData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,8 +15,10 @@ export const defaultItemImg = {
|
|||||||
arme: "systems/fvtt-les-heritiers/assets/icons/weapon.webp",
|
arme: "systems/fvtt-les-heritiers/assets/icons/weapon.webp",
|
||||||
accessoire: "systems/fvtt-les-heritiers/assets/icons/item.webp",
|
accessoire: "systems/fvtt-les-heritiers/assets/icons/item.webp",
|
||||||
protection: "systems/fvtt-les-heritiers/assets/icons/armor.webp",
|
protection: "systems/fvtt-les-heritiers/assets/icons/armor.webp",
|
||||||
fee: "systems/fvtt-les-heritiers/assets/icons/faery_type.webp"
|
fee: "systems/fvtt-les-heritiers/assets/icons/faery_type.webp",
|
||||||
|
profil: "systems/fvtt-les-heritiers/assets/icons/profil.webp",
|
||||||
|
equipement: "systems/fvtt-les-heritiers/assets/icons/equipement.webp",
|
||||||
|
sort: "systems/fvtt-les-heritiers/assets/icons/sort.webp",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,14 +9,17 @@
|
|||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
// Import Modules
|
// Import Modules
|
||||||
import { HeritiersActor } from "./heritiers-actor.js";
|
import { HeritiersActor } from "./heritiers-actor.js";
|
||||||
import { HeritiersItemSheet } from "./heritiers-item-sheet.js";
|
import { HeritiersItem } from "./heritiers-item.js";
|
||||||
import { HeritiersActorSheet } from "./heritiers-actor-sheet.js";
|
|
||||||
import { HeritiersCreatureSheet } from "./heritiers-creature-sheet.js";
|
|
||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
import { HeritiersUtility } from "./heritiers-utility.js";
|
||||||
import { HeritiersCombat } from "./heritiers-combat.js";
|
import { HeritiersCombat } from "./heritiers-combat.js";
|
||||||
import { HeritiersItem } from "./heritiers-item.js";
|
|
||||||
import { HERITIERS_CONFIG } from "./heritiers-config.js";
|
import { HERITIERS_CONFIG } from "./heritiers-config.js";
|
||||||
|
|
||||||
|
// Import DataModels
|
||||||
|
import * as models from "./models/index.mjs";
|
||||||
|
|
||||||
|
// Import AppV2 Sheets
|
||||||
|
import * as sheets from "./applications/sheets/_module.mjs";
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Foundry VTT Initialization */
|
/* Foundry VTT Initialization */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -30,7 +33,7 @@ Hooks.once("init", async function () {
|
|||||||
HeritiersUtility.preloadHandlebarsTemplates()
|
HeritiersUtility.preloadHandlebarsTemplates()
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
// Set an initiative formula for the system
|
// Set an initiative formula for the system
|
||||||
CONFIG.Combat.initiative = {
|
CONFIG.Combat.initiative = {
|
||||||
formula: "1d10",
|
formula: "1d10",
|
||||||
decimals: 1
|
decimals: 1
|
||||||
@@ -45,20 +48,62 @@ Hooks.once("init", async function () {
|
|||||||
// Define custom Entity classes
|
// Define custom Entity classes
|
||||||
CONFIG.Combat.documentClass = HeritiersCombat
|
CONFIG.Combat.documentClass = HeritiersCombat
|
||||||
CONFIG.Actor.documentClass = HeritiersActor
|
CONFIG.Actor.documentClass = HeritiersActor
|
||||||
|
CONFIG.Actor.dataModels = {
|
||||||
|
personnage: models.PersonnageDataModel,
|
||||||
|
pnj: models.PnjDataModel
|
||||||
|
}
|
||||||
|
|
||||||
CONFIG.Item.documentClass = HeritiersItem
|
CONFIG.Item.documentClass = HeritiersItem
|
||||||
|
CONFIG.Item.dataModels = {
|
||||||
|
accessoire: models.AccessoireDataModel,
|
||||||
|
arme: models.ArmeDataModel,
|
||||||
|
atoutfeerique: models.AtoutFeeriqueDataModel,
|
||||||
|
avantage: models.AvantageDataModel,
|
||||||
|
capacitenaturelle: models.CapaciteNaturelleDataModel,
|
||||||
|
competence: models.CompetenceDataModel,
|
||||||
|
contact: models.ContactDataModel,
|
||||||
|
desavantage: models.DesavantageDataModel,
|
||||||
|
equipement: models.EquipementDataModel,
|
||||||
|
fee: models.FeeDataModel,
|
||||||
|
pouvoir: models.PouvoirDataModel,
|
||||||
|
profil: models.ProfilDataModel,
|
||||||
|
protection: models.ProtectionDataModel,
|
||||||
|
sort: models.SortDataModel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an object of bonus/malus from -6 to +6 signed
|
||||||
|
HERITIERS_CONFIG.bonusMalus = Array.from({ length: 7 }, (v, k) => toString(k - 6))
|
||||||
|
CONFIG.HERITIERS = HERITIERS_CONFIG
|
||||||
|
|
||||||
game.system.lesheritiers = {
|
game.system.lesheritiers = {
|
||||||
HeritiersUtility,
|
HeritiersUtility,
|
||||||
config: HERITIERS_CONFIG
|
config: HERITIERS_CONFIG,
|
||||||
|
models,
|
||||||
|
sheets
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
// Register sheet application classes
|
// Register sheet application classes
|
||||||
Actors.unregisterSheet("core", ActorSheet);
|
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
|
||||||
Actors.registerSheet("fvtt-les-heritiers", HeritiersActorSheet, { types: ["personnage"], makeDefault: true })
|
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", sheets.HeritiersPersonnageSheet, { types: ["personnage"], makeDefault: true })
|
||||||
Actors.registerSheet("fvtt-les-heritiers", HeritiersCreatureSheet, { types: ["creature"], makeDefault: true })
|
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", sheets.HeritiersPnjSheet, { types: ["pnj"], makeDefault: true })
|
||||||
|
|
||||||
Items.unregisterSheet("core", ItemSheet);
|
// Register AppV2 Item Sheets
|
||||||
Items.registerSheet("fvtt-les-heritiers", HeritiersItemSheet, { makeDefault: true })
|
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersAccessoireSheet, { types: ["accessoire"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersArmeSheet, { types: ["arme"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersAtoutFeeriqueSheet, { types: ["atoutfeerique"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersAvantageSheet, { types: ["avantage"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersCapaciteNaturelleSheet, { types: ["capacitenaturelle"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersCompetenceSheet, { types: ["competence"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersContactSheet, { types: ["contact"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersDesavantageSheet, { types: ["desavantage"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersEquipementSheet, { types: ["equipement"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersFeeSheet, { types: ["fee"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersPouvoirSheet, { types: ["pouvoir"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersProfilSheet, { types: ["profil"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersProtectionSheet, { types: ["protection"], makeDefault: true })
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersSortSheet, { types: ["sort"], makeDefault: true })
|
||||||
|
|
||||||
HeritiersUtility.init()
|
HeritiersUtility.init()
|
||||||
|
|
||||||
@@ -69,40 +114,52 @@ function welcomeMessage() {
|
|||||||
ChatMessage.create({
|
ChatMessage.create({
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
whisper: [game.user.id],
|
whisper: [game.user.id],
|
||||||
content: `<div id="welcome-message-heritiers"><span class="rdd-roll-part">
|
content: `
|
||||||
<strong>Bienvenue dans Les Heritiers et la Belle Epoque !</strong>
|
<div class="heritiers-chat-card heritiers-welcome-card">
|
||||||
<p>Les livres du JDR Les Heritiers sont nécessaires pour jouer : https://www.titam-france.fr</p>
|
<div class="chat-card-header welcome-header">
|
||||||
<p>Les Heritiers est jeu de rôle publié par Titam France/Sombres projets, tout les droits leur appartiennent.</p>
|
<div class="welcome-icon-wrapper">
|
||||||
<p>Système développé par LeRatierBretonnien, support sur le <a href="https://discord.gg/pPSDNJk">Discord FR de Foundry</a>.</p>
|
<i class="fas fa-book-open welcome-icon"></i>
|
||||||
` });
|
</div>
|
||||||
|
<div class="chat-actor-info">
|
||||||
|
<h3 class="chat-actor-name">Bienvenue dans Les Héritiers !</h3>
|
||||||
|
<div class="chat-action-name">et la Belle Époque</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chat-card-content welcome-content">
|
||||||
|
<div class="welcome-section">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
<p>Les livres du JDR <strong>Les Héritiers</strong> sont nécessaires pour jouer.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="welcome-section">
|
||||||
|
<i class="fas fa-copyright"></i>
|
||||||
|
<p><em>Les Héritiers</em> est un jeu de rôle publié par <strong>Titam France / Sombres Projets</strong>. Tous les droits leur appartiennent.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="welcome-section">
|
||||||
|
<i class="fas fa-code"></i>
|
||||||
|
<p>Système développé par <strong>LeRatierBretonnien</strong></p>
|
||||||
|
<p>Support sur le <a href="https://discord.gg/pPSDNJk" target="_blank"><i class="fab fa-discord"></i> Discord FR de Foundry</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
// Register world usage statistics
|
async function importDefaultScene() {
|
||||||
function registerUsageCount(registerKey) {
|
let exists = game.scenes.find(j => j.name == "Accueil");
|
||||||
if (game.user.isGM) {
|
if (!exists) {
|
||||||
game.settings.register(registerKey, "world-key", {
|
const scenes = await HeritiersUtility.loadCompendium("fvtt-les-heritiers.scenes")
|
||||||
name: "Unique world key",
|
let newDocuments = scenes.filter(i => i.name == "Accueil");
|
||||||
scope: "world",
|
await game.scenes.documentClass.create(newDocuments);
|
||||||
config: false,
|
game.scenes.find(i => i.name == "Accueil").activate();
|
||||||
default: "",
|
|
||||||
type: String
|
|
||||||
});
|
|
||||||
|
|
||||||
let worldKey = game.settings.get(registerKey, "world-key")
|
|
||||||
if (worldKey == undefined || worldKey == "") {
|
|
||||||
worldKey = randomID(32)
|
|
||||||
game.settings.set(registerKey, "world-key", worldKey)
|
|
||||||
}
|
|
||||||
// Simple API counter
|
|
||||||
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
|
|
||||||
//$.ajaxSetup({
|
|
||||||
//headers: { 'Access-Control-Allow-Origin': '*' }
|
|
||||||
//})
|
|
||||||
$.ajax(regURL)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Foundry VTT Initialization */
|
/* Foundry VTT Initialization */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -119,14 +176,15 @@ Hooks.once("ready", function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerUsageCount('fvtt-les-heritiers')
|
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
|
||||||
welcomeMessage()
|
console.log("ClassCounter loaded", moduleCounter)
|
||||||
|
moduleCounter.ClassCounter.registerUsageCount()
|
||||||
|
}).catch(err =>
|
||||||
|
console.log("No stats available, giving up.")
|
||||||
|
)
|
||||||
|
welcomeMessage();
|
||||||
|
importDefaultScene();
|
||||||
|
|
||||||
// CSS patch for v9
|
|
||||||
if (game.version) {
|
|
||||||
let sidebar = document.getElementById("sidebar");
|
|
||||||
sidebar.style.width = "min-content";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -136,10 +194,9 @@ Hooks.on("chatMessage", (html, content, msg) => {
|
|||||||
if (content[0] == '/') {
|
if (content[0] == '/') {
|
||||||
let regExp = /(\S+)/g;
|
let regExp = /(\S+)/g;
|
||||||
let commands = content.match(regExp);
|
let commands = content.match(regExp);
|
||||||
if (game.system.mournblade.commands.processChatCommand(commands, content, msg)) {
|
if (game.system.lesheritiers.commands.processChatCommand(commands, content, msg)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
|
||||||
|
|
||||||
export class HeritiersRollDialog extends Dialog {
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
static async create(actor, rollData) {
|
|
||||||
|
|
||||||
let options = { classes: ["HeritiersDialog"], width: 320, height: 'fit-content', 'z-index': 99999 };
|
|
||||||
let html = await renderTemplate('systems/fvtt-les-heritiers/templates/roll-dialog-generic.html', rollData);
|
|
||||||
|
|
||||||
return new HeritiersRollDialog(actor, rollData, html, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
constructor(actor, rollData, html, options, close = undefined) {
|
|
||||||
let conf = {
|
|
||||||
title: "Test de Capacité",
|
|
||||||
content: html,
|
|
||||||
buttons:
|
|
||||||
{
|
|
||||||
rolld8: {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer 1d8",
|
|
||||||
callback: () => { this.roll("d8") }
|
|
||||||
},
|
|
||||||
rolld10: {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer 1d10",
|
|
||||||
callback: () => { this.roll("d10") }
|
|
||||||
},
|
|
||||||
rolld12: {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer 1d12",
|
|
||||||
callback: () => { this.roll("d12") }
|
|
||||||
},
|
|
||||||
cancel: {
|
|
||||||
icon: '<i class="fas fa-times"></i>',
|
|
||||||
label: "Annuler",
|
|
||||||
callback: () => { this.close() }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
close: close
|
|
||||||
}
|
|
||||||
// Overwrite in case of carac only -> 1d10
|
|
||||||
if (rollData.mode == "carac") {
|
|
||||||
conf.buttons = {
|
|
||||||
rolld8: {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer 1d8",
|
|
||||||
callback: () => { this.roll("d8") }
|
|
||||||
},
|
|
||||||
cancel: {
|
|
||||||
icon: '<i class="fas fa-times"></i>',
|
|
||||||
label: "Annuler",
|
|
||||||
callback: () => { this.close() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super(conf, options);
|
|
||||||
|
|
||||||
this.actor = actor
|
|
||||||
this.rollData = rollData
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
roll(dice) {
|
|
||||||
this.rollData.mainDice = dice
|
|
||||||
HeritiersUtility.rollHeritiers(this.rollData)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
|
|
||||||
var dialog = this;
|
|
||||||
function onLoad() {
|
|
||||||
}
|
|
||||||
$(function () { onLoad(); });
|
|
||||||
|
|
||||||
html.find('#sdValue').change(async (event) => {
|
|
||||||
this.rollData.sdValue = Number(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#caracKey').change(async (event) => {
|
|
||||||
this.rollData.caracKey = String(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#bonus-malus-context').change((event) => {
|
|
||||||
this.rollData.bonusMalusContext = Number(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#useTricherie').change((event) => {
|
|
||||||
this.rollData.useTricherie = event.currentTarget.checked
|
|
||||||
})
|
|
||||||
html.find('#useHeritage').change((event) => {
|
|
||||||
this.rollData.useHeritage = event.currentTarget.checked
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,45 @@
|
|||||||
import { HeritiersCombat } from "./heritiers-combat.js";
|
import { HeritiersCombat } from "./heritiers-combat.js";
|
||||||
import { HeritiersCommands } from "./heritiers-commands.js";
|
import { HeritiersCommands } from "./heritiers-commands.js";
|
||||||
|
|
||||||
|
const __facesAdjacentes = {
|
||||||
|
"d8": {
|
||||||
|
1: [4, 8, 6],
|
||||||
|
2: [7, 5, 3],
|
||||||
|
3: [2, 8, 6],
|
||||||
|
4: [1, 5, 7],
|
||||||
|
5: [2, 4, 8],
|
||||||
|
6: [1, 7, 3],
|
||||||
|
7: [2, 4, 6],
|
||||||
|
8: [1, 3, 5]
|
||||||
|
},
|
||||||
|
"d10": {
|
||||||
|
1: [4, 6, 9, 7],
|
||||||
|
2: [6, 8, 5, 9],
|
||||||
|
3: [7, 5, 8, 10],
|
||||||
|
4: [10, 6, 7, 1],
|
||||||
|
5: [3, 9, 2, 8],
|
||||||
|
6: [1, 4, 2, 9],
|
||||||
|
7: [1, 3, 4, 10],
|
||||||
|
8: [2, 10, 5, 3],
|
||||||
|
9: [1, 5, 6, 2],
|
||||||
|
10: [8, 4, 3, 7]
|
||||||
|
},
|
||||||
|
"d12": {
|
||||||
|
1: [2, 3, 4, 5, 6],
|
||||||
|
2: [1, 6, 8, 12, 3],
|
||||||
|
3: [1, 4, 11, 12, 2],
|
||||||
|
4: [1, 5, 10, 11, 3],
|
||||||
|
5: [1, 6, 9, 10, 4],
|
||||||
|
6: [1, 2, 8, 9, 5],
|
||||||
|
7: [8, 9, 10, 11, 12],
|
||||||
|
8: [2, 6, 9, 7, 12],
|
||||||
|
9: [5, 10, 7, 8, 6],
|
||||||
|
10: [4, 11, 7, 9, 5],
|
||||||
|
11: [7, 10, 4, 3, 12],
|
||||||
|
12: [2, 8, 7, 11, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
export class HeritiersUtility {
|
export class HeritiersUtility {
|
||||||
|
|
||||||
@@ -9,7 +48,7 @@ export class HeritiersUtility {
|
|||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static async init() {
|
static async init() {
|
||||||
Hooks.on('renderChatLog', (log, html, data) => HeritiersUtility.chatListeners(html))
|
Hooks.on('renderChatLog', (log, html, data) => HeritiersUtility.chatListeners(html))
|
||||||
Hooks.on("getChatLogEntryContext", (html, options) => HeritiersUtility.chatRollMenu(html, options))
|
/* Unused for Heitiers : Hooks.on("getChatMessageContextOptions", (html, options) => HeritiersUtility.chatRollMenu(html, options))*/
|
||||||
|
|
||||||
this.rollDataStore = {}
|
this.rollDataStore = {}
|
||||||
this.defenderStore = {}
|
this.defenderStore = {}
|
||||||
@@ -37,22 +76,25 @@ export class HeritiersUtility {
|
|||||||
Handlebars.registerHelper('mul', function (a, b) {
|
Handlebars.registerHelper('mul', function (a, b) {
|
||||||
return parseInt(a) * parseInt(b);
|
return parseInt(a) * parseInt(b);
|
||||||
})
|
})
|
||||||
|
Handlebars.registerHelper('and', function (...args) {
|
||||||
|
// Last argument is Handlebars options object, ignore it
|
||||||
|
return args.slice(0, -1).every(Boolean);
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static sortByName(table) {
|
static sortByName(table) {
|
||||||
return table.sort(function (a, b) {
|
return table.sort(function (a, b) {
|
||||||
let fa = a.name.toLowerCase(),
|
return a.name.localeCompare(b.name);
|
||||||
fb = b.name.toLowerCase();
|
})
|
||||||
if (fa < fb) {
|
}
|
||||||
return -1;
|
|
||||||
}
|
/* -------------------------------------------- */
|
||||||
if (fa > fb) {
|
static sortArrayObjectsByName(myArray) {
|
||||||
return 1;
|
myArray.sort((a, b) => {
|
||||||
}
|
return a.name.localeCompare(b.name);
|
||||||
return 0;
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +108,42 @@ export class HeritiersUtility {
|
|||||||
const skills = await HeritiersUtility.loadCompendium("fvtt-les-heritiers.competences")
|
const skills = await HeritiersUtility.loadCompendium("fvtt-les-heritiers.competences")
|
||||||
this.skills = skills.map(i => i.toObject())
|
this.skills = skills.map(i => i.toObject())
|
||||||
|
|
||||||
|
this.competencesMagie = this.skills.filter(s => s.system.profil == "magie")
|
||||||
|
|
||||||
|
game.settings.register("fvtt-les-heritiers", "heritiers-heritage", {
|
||||||
|
name: "Points d'héritage",
|
||||||
|
hint: "Points d'héritage du groupe",
|
||||||
|
scope: "world",
|
||||||
|
config: true,
|
||||||
|
default: 0,
|
||||||
|
type: Number
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getSDSortValue(niveau) {
|
||||||
|
if (niveau <= 1) return 12;
|
||||||
|
if (niveau == 2) return 14;
|
||||||
|
if (niveau == 3) return 16;
|
||||||
|
if (niveau > 3) return 18;
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static getCompetencesMagie() {
|
||||||
|
return this.competencesMagie
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static buildCompetencesMagie() {
|
||||||
|
let competences = foundry.utils.duplicate(this.getCompetencesMagie())
|
||||||
|
for (let comp of competences) {
|
||||||
|
// Calcul du rang
|
||||||
|
let rang = Math.round(comp.system.niveau / 2);
|
||||||
|
competences.system.rang = rang;
|
||||||
|
competences.system.rangGenericName = game.system.lesheritiers.config.rangName[rang];
|
||||||
|
competences.system.rangSpecificName = game.system.lesheritiers.config.rangNameSpecific[comp.Name][competences.system.rangGenericName];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -87,18 +165,29 @@ export class HeritiersUtility {
|
|||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static async chatListeners(html) {
|
static async chatListeners(html) {
|
||||||
|
|
||||||
html.on("click", '.predilection-reroll', async event => {
|
$(html).on("click", '.predilection-reroll', async event => {
|
||||||
let predIdx = $(event.currentTarget).data("predilection-index")
|
let predIdx = $(event.currentTarget).data("predilection-index")
|
||||||
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
|
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
|
||||||
let message = game.messages.get(messageId)
|
let message = game.messages.get(messageId)
|
||||||
let rollData = message.getFlag("world", "heritiers-roll")
|
let rollData = message.getFlag("world", "heritiers-roll")
|
||||||
let actor = this.getActorFromRollData(rollData)
|
let actor = this.getActorFromRollData(rollData)
|
||||||
await actor.setPredilectionUsed(rollData.competence._id, predIdx)
|
await actor.setPredilectionUsed(rollData.competence._id, predIdx)
|
||||||
rollData.competence = duplicate(actor.getCompetence(rollData.competence._id))
|
rollData.competence = foundry.utils.duplicate(actor.getCompetence(rollData.competence._id))
|
||||||
HeritiersUtility.rollHeritiers(rollData)
|
HeritiersUtility.rollHeritiers(rollData)
|
||||||
})
|
})
|
||||||
|
|
||||||
html.on("click", '.roll-chat-degat', async event => {
|
$(html).on("click", '.roll-tricherie-2', async event => {
|
||||||
|
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
|
||||||
|
let message = game.messages.get(messageId)
|
||||||
|
let rollData = message.getFlag("world", "heritiers-roll")
|
||||||
|
let actor = this.getActorFromRollData(rollData)
|
||||||
|
if (await actor.incDecTricherie(-2)) {
|
||||||
|
rollData.forcedValue = Number($(event.currentTarget).data("dice-value"))
|
||||||
|
HeritiersUtility.rollHeritiers(rollData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$(html).on("click", '.roll-chat-degat', async event => {
|
||||||
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
|
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
|
||||||
let message = game.messages.get(messageId)
|
let message = game.messages.get(messageId)
|
||||||
let rollData = message.getFlag("world", "heritiers-roll")
|
let rollData = message.getFlag("world", "heritiers-roll")
|
||||||
@@ -111,13 +200,14 @@ export class HeritiersUtility {
|
|||||||
static async preloadHandlebarsTemplates() {
|
static async preloadHandlebarsTemplates() {
|
||||||
|
|
||||||
const templatePaths = [
|
const templatePaths = [
|
||||||
'systems/fvtt-les-heritiers/templates/editor-notes-gm.html',
|
'systems/fvtt-les-heritiers/templates/editor-notes-gm.hbs',
|
||||||
'systems/fvtt-les-heritiers/templates/partial-item-header.html',
|
'systems/fvtt-les-heritiers/templates/partial-item-header.hbs',
|
||||||
'systems/fvtt-les-heritiers/templates/partial-item-description.html',
|
'systems/fvtt-les-heritiers/templates/partial-item-description.hbs',
|
||||||
'systems/fvtt-les-heritiers/templates/partial-item-nav.html',
|
'systems/fvtt-les-heritiers/templates/partial-item-nav.hbs',
|
||||||
'systems/fvtt-les-heritiers/templates/partial-list-niveau.html'
|
'systems/fvtt-les-heritiers/templates/partial-utile-skills.hbs',
|
||||||
|
'systems/fvtt-les-heritiers/templates/partial-actor-equipment.hbs'
|
||||||
]
|
]
|
||||||
return loadTemplates(templatePaths);
|
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -194,14 +284,14 @@ export class HeritiersUtility {
|
|||||||
|
|
||||||
let id = rollData.rollId;
|
let id = rollData.rollId;
|
||||||
let oldRollData = this.rollDataStore[id] || {};
|
let oldRollData = this.rollDataStore[id] || {};
|
||||||
let newRollData = mergeObject(oldRollData, rollData);
|
let newRollData = foundry.utils.mergeObject(oldRollData, rollData);
|
||||||
this.rollDataStore[id] = newRollData;
|
this.rollDataStore[id] = newRollData;
|
||||||
}
|
}
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static saveRollData(rollData) {
|
static saveRollData(rollData) {
|
||||||
game.socket.emit("system.fvtt-les-heritiers", {
|
game.socket.emit("system.fvtt-les-heritiers", {
|
||||||
name: "msg_update_roll", data: rollData
|
name: "msg_update_roll", data: rollData
|
||||||
}); // Notify all other clients of the roll
|
}); // Notify all other clients of the roll
|
||||||
this.updateRollData(rollData);
|
this.updateRollData(rollData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +302,6 @@ export class HeritiersUtility {
|
|||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static onSocketMesssage(msg) {
|
static onSocketMesssage(msg) {
|
||||||
//console.log("SOCKET MESSAGE", msg.name, game.user.character.id, msg.data.defenderId);
|
|
||||||
if (msg.name == "msg_update_defense_state") {
|
if (msg.name == "msg_update_defense_state") {
|
||||||
this.updateDefenseState(msg.data.defenderId, msg.data.rollId);
|
this.updateDefenseState(msg.data.defenderId, msg.data.rollId);
|
||||||
}
|
}
|
||||||
@@ -269,123 +358,289 @@ export class HeritiersUtility {
|
|||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static computeMonnaieDetails(valueSC) {
|
static computeMonnaieDetails(valueSC) {
|
||||||
let po = Math.floor(valueSC / 400)
|
let po = Math.floor(valueSC / 400)
|
||||||
let pa = Math.floor((valueSC - (po*400)) / 20)
|
let pa = Math.floor((valueSC - (po * 400)) / 20)
|
||||||
let sc = valueSC - (po*400) - (pa*20)
|
let sc = valueSC - (po * 400) - (pa * 20)
|
||||||
return {
|
return {
|
||||||
po: po, pa: pa, sc: sc, valueSC: valueSC
|
po: po, pa: pa, sc: sc, valueSC: valueSC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static incDecHeritage() {
|
static incDecHeritage() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static computeResult(actor, rollData) {
|
static computeResult(actor, rollData) {
|
||||||
rollData.diceResult = -1
|
let isTricherieHeritage = rollData.useHeritage || rollData.useTricherie
|
||||||
let resTab = []
|
|
||||||
for ( let res of rollData.roll.terms[0].results) {
|
|
||||||
rollData.diceResult = Math.max(res.result, rollData.diceResult)
|
|
||||||
resTab.push(res.result)
|
|
||||||
}
|
|
||||||
let isFailure = false
|
|
||||||
if (rollData.mainDice.includes("d10")) {
|
|
||||||
if ( rollData.diceResult == 1) {
|
|
||||||
rollData.finalResult -= 3
|
|
||||||
isFailure = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rollData.mainDice.includes("d12")) {
|
|
||||||
if ( rollData.diceResult == 1 || rollData.diceResult == 2) {
|
|
||||||
rollData.finalResult -= 5
|
|
||||||
isFailure = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Heritage/Tricherie management
|
|
||||||
rollData.marge = 0
|
rollData.marge = 0
|
||||||
if (!isFailure && (rollData.useHeritage || rollData.useTricherie)) {
|
|
||||||
|
if (isTricherieHeritage) {
|
||||||
|
let resTab = [rollData.roll.terms[0].results[0].result, rollData.roll.terms[0].results[1].result, rollData.roll.terms[0].results[2].result]
|
||||||
|
rollData.diceResult = resTab[0] + "," + resTab[1] + "," + resTab[2]
|
||||||
|
let foundryTotal = resTab[0] + resTab[1] + resTab[2]
|
||||||
|
if (resTab[1] == 1) { resTab[1] -= 4 }
|
||||||
|
if (resTab[2] == 1) { resTab[2] -= 6 }
|
||||||
|
if (resTab[2] == 2) { resTab[2] -= 7 }
|
||||||
|
rollData.diceValue = Math.max(Math.max(resTab[0], resTab[1]), resTab[2])
|
||||||
|
rollData.finalResult = rollData.roll.total - foundryTotal + rollData.diceValue
|
||||||
|
|
||||||
|
// Gestion des résultats spéciaux
|
||||||
resTab = resTab.sort()
|
resTab = resTab.sort()
|
||||||
if ( (resTab[0] == resTab[1]) && (resTab[1] == resTab[2])) {
|
if ((resTab[0] == resTab[1]) && (resTab[1] == resTab[2])) {
|
||||||
rollData.marge = 7
|
rollData.marge = 7
|
||||||
rollData.isSuccess = true
|
rollData.isSuccess = true
|
||||||
rollData.isCriticalSuccess = true
|
rollData.isCriticalSuccess = true
|
||||||
|
rollData.isBrelan = true
|
||||||
}
|
}
|
||||||
if ((resTab[0]+1 == resTab[1]) && (resTab[1]+1 == resTab[2]) ) {
|
if ((resTab[0] + 1 == resTab[1]) && (resTab[1] + 1 == resTab[2])) {
|
||||||
rollData.marge = 7
|
rollData.marge = 7
|
||||||
rollData.isSuccess = true
|
rollData.isSuccess = true
|
||||||
rollData.isCriticalSuccess = true
|
rollData.isCriticalSuccess = true
|
||||||
}
|
rollData.isSuite = true
|
||||||
if ( rollData.useTricherie) {
|
}
|
||||||
|
if (rollData.useTricherie) {
|
||||||
actor.incDecTricherie(-1)
|
actor.incDecTricherie(-1)
|
||||||
}
|
}
|
||||||
if ( rollData.useHeritage) {
|
if (rollData.useHeritage) {
|
||||||
this.incDecHeritage()
|
this.incDecHeritage()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
//rollData.finalResult = Math.max(rollData.finalResult, 0)
|
} else {
|
||||||
//console.log("Result : ", rollData)
|
rollData.finalResult = rollData.roll.total
|
||||||
if (rollData.marge == 0 && rollData.sdValue > 0 ) {
|
let rollValue = rollData.forcedValue || rollData.roll.terms[0].results[0].result
|
||||||
rollData.marge = rollData.finalResult - rollData.sdValue
|
rollData.diceResult = rollValue
|
||||||
rollData.isSuccess = (rollData.finalResult >= rollData.sdValue)
|
rollData.diceValue = rollValue
|
||||||
rollData.isCriticalSuccess = ((rollData.finalResult - rollData.sdValue) >= 7)
|
if (rollData.mainDice.includes("d10")) {
|
||||||
rollData.isCriticalFailure = ((rollData.finalResult - rollData.sdValue) <= -7)
|
if (rollValue == 1) {
|
||||||
|
rollData.finalResult -= 3 + rollValue // substract 3 and the 1 value that has been added
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rollData.mainDice.includes("d12")) {
|
||||||
|
if (rollValue == 1 || rollValue == 2) {
|
||||||
|
rollData.finalResult -= 5 + rollValue // Remove also the dice result has it has been added already
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!rollData.forcedValue) {
|
||||||
|
rollData.adjacentFaces = foundry.utils.duplicate(__facesAdjacentes[rollData.mainDice][rollData.diceValue])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static computeArmeDegats(rollData, actor) {
|
||||||
|
rollData.degatsArme = rollData.arme.system.degats + rollData.marge
|
||||||
|
if (rollData.attaqueDeuxArmes != 0 && rollData.secondeArme) {
|
||||||
|
let secondeArme = actor.items.get(secondeArme)
|
||||||
|
if (secondeArme) {
|
||||||
|
rollData.degatsArme += secondeArme.system.degats
|
||||||
|
rollData.degatsArme += actor.system.caracteristiques.for.value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (rollData.arme.system.categorie == "lourde") {
|
||||||
|
rollData.degatsArme += actor.system.caracteristiques.for.value
|
||||||
|
}
|
||||||
|
if (rollData.arme.system.categorie == "blanche" || rollData.arme.system.categorie == "improvise") {
|
||||||
|
rollData.degatsArme += Math.max(0, actor.system.caracteristiques.for.value - 2)
|
||||||
|
}
|
||||||
|
if (rollData.mode == "attaquecharge") {
|
||||||
|
rollData.degatsArme += 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rollData.attaqueCible == "membre") {
|
||||||
|
rollData.degatsArme -= 2
|
||||||
|
}
|
||||||
|
if (rollData.attaqueCible == "main") {
|
||||||
|
rollData.degatsArme -= 3
|
||||||
|
}
|
||||||
|
if (rollData.attaqueCible == "tete") {
|
||||||
|
rollData.degatsArme *= 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static computeMarge(rollData, seuil) {
|
||||||
|
if (rollData.marge == 0 && seuil >= 0) {
|
||||||
|
rollData.marge = rollData.finalResult - seuil
|
||||||
|
rollData.isSuccess = (rollData.finalResult >= seuil)
|
||||||
|
rollData.isCriticalSuccess = ((rollData.finalResult - seuil) >= 7)
|
||||||
|
rollData.isCriticalFailure = ((rollData.finalResult - seuil) <= -7)
|
||||||
|
// Si compétence > 0 et d8 -> echec critique impossible
|
||||||
|
if (rollData?.competence?.system.niveau > 0 && rollData?.mainDice == "d8") {
|
||||||
|
rollData.isCriticalFailure = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async displayUneDefense(rollData, actor, nomDefense, valeurDefense) {
|
||||||
|
rollData.defenderMode = nomDefense
|
||||||
|
rollData.defenderValue = valeurDefense
|
||||||
|
rollData.marge = 0
|
||||||
|
this.computeMarge(rollData, valeurDefense)
|
||||||
|
if (rollData.isSuccess) {
|
||||||
|
this.computeArmeDegats(rollData, actor)
|
||||||
|
}
|
||||||
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-cc-result.hbs`, rollData)
|
||||||
|
}, rollData, "selfroll")
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static async displayAsssomer(rollData, actor, nomAttaque, etatAssomer, valeurDefense) {
|
||||||
|
rollData.defenderMode = nomAttaque
|
||||||
|
rollData.etatAssommer = etatAssomer
|
||||||
|
rollData.defenderValue = valeurDefense
|
||||||
|
rollData.marge = 0
|
||||||
|
this.computeMarge(rollData, valeurDefense)
|
||||||
|
rollData.dureeAssommer = (rollData.marge) ? rollData.marge * 2 : 1
|
||||||
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-assommer-result.hbs`, rollData)
|
||||||
|
}, rollData, "selfroll")
|
||||||
|
}
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static async rollHeritiers(rollData) {
|
static async rollHeritiers(rollData) {
|
||||||
|
|
||||||
let actor = this.getActorFromRollData(rollData)
|
let actor = this.getActorFromRollData(rollData)
|
||||||
|
|
||||||
|
if (rollData.mode == "pouvoir" && actor.getPouvoirUsage(rollData.pouvoir._id) < rollData.pouvoirPointsUsage) {
|
||||||
|
ui.notifications.warn("Pas assez de points d'usage pour ce pouvoir.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//rollData.actionImg = "systems/fvtt-les-heritiers/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
|
//rollData.actionImg = "systems/fvtt-les-heritiers/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
|
||||||
rollData.carac = duplicate(actor.system.caracteristiques[rollData.caracKey])
|
if (rollData.caracKey == "pre") rollData.caracKey = "pres"; // Patch tomanage wrong carac key
|
||||||
|
rollData.carac = foundry.utils.duplicate(actor.system.caracteristiques[rollData.caracKey])
|
||||||
|
|
||||||
rollData.nbDice = (rollData.useTricherie || rollData.useHeritage) ? 3 : 1
|
if (rollData.forcedValue) {
|
||||||
rollData.diceFormula = rollData.nbDice + rollData.mainDice + "kh1"
|
rollData.diceFormula = rollData.forcedValue
|
||||||
//console.log("BEFORE COMP", rollData)
|
|
||||||
if (rollData.competence) {
|
|
||||||
let compmod = (rollData.competence.system.niveau == 0) ? -3 : 0
|
|
||||||
rollData.diceFormula += `+${rollData.carac.value}+${rollData.competence.system.niveau}+${rollData.bonusMalusContext}+${compmod}`
|
|
||||||
} else {
|
} else {
|
||||||
rollData.diceFormula += `+${rollData.carac.value}+${rollData.bonusMalusContext}`
|
if (rollData.useTricherie || rollData.useHeritage) {
|
||||||
}
|
rollData.diceFormula = "{1d8, 1d10, 1d12}"
|
||||||
rollData.diceFormula += `+${rollData.pvMalus}`
|
} else {
|
||||||
|
rollData.diceFormula = "1" + rollData.mainDice + "kh1"
|
||||||
if (rollData.arme && rollData.arme.type == "arme") {
|
}
|
||||||
rollData.diceFormula += `+${rollData.arme.system.bonusmaniementoff}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let myRoll = new Roll(rollData.diceFormula).roll({ async: false })
|
let rangValue = 0
|
||||||
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
|
if (rollData.rang) {
|
||||||
rollData.roll = myRoll
|
rangValue = rollData.rang.value
|
||||||
console.log(">>>> ", myRoll)
|
}
|
||||||
|
if (rollData.competence) {
|
||||||
|
let compmod = 0 // Bonus de compétence à 0 dans Les Heritiers
|
||||||
|
let specBonus = (rollData.useSpecialite) ? 1 : 0
|
||||||
|
rollData.diceFormula += `+${rollData.carac.value}+${rangValue}+${rollData.competence.system.niveau}+${specBonus}+${rollData.bonusMalusContext}+${compmod}`
|
||||||
|
} else if (rollData.pouvoirBase) {
|
||||||
|
rollData.diceFormula += `+${rollData.carac.value}+${rollData.pouvoirBase.value}+${rangValue}+${rollData.bonusMalusContext}`
|
||||||
|
} else {
|
||||||
|
rollData.diceFormula += `+${rollData.carac.value}+${rangValue}+${rollData.bonusMalusContext}`
|
||||||
|
}
|
||||||
|
let ruleMalus = 0
|
||||||
|
for (let malus of rollData.rulesMalus) {
|
||||||
|
ruleMalus += malus.value
|
||||||
|
}
|
||||||
|
rollData.diceFormula += `+${ruleMalus}`
|
||||||
|
|
||||||
|
// Gestion bonus attaque à plusieurs
|
||||||
|
let bonusAttaque = rollData.bonusAttaquePlusieurs
|
||||||
|
if (rollData.attaqueDos) {
|
||||||
|
bonusAttaque = 2
|
||||||
|
if (rollData.bonusAttaquePlusieurs) {
|
||||||
|
bonusAttaque = 3 // Valeur max, cf règle page 197
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rollData.diceFormula += `+${bonusAttaque}`
|
||||||
|
|
||||||
|
// Gestion attaque avec 2 armes
|
||||||
|
if (rollData.attaqueDeuxArmes != 0) {
|
||||||
|
rollData.diceFormula += `+${rollData.attaqueDeuxArmes}`
|
||||||
|
}
|
||||||
|
// Gestion des attaques ciblées
|
||||||
|
if (rollData.attaqueCible != "none") {
|
||||||
|
if (rollData.attaqueCible == "membre") {
|
||||||
|
rollData.diceFormula += `-2`
|
||||||
|
}
|
||||||
|
if (rollData.attaqueCible == "main") {
|
||||||
|
rollData.diceFormula += `-3`
|
||||||
|
}
|
||||||
|
if (rollData.attaqueCible == "tete") {
|
||||||
|
rollData.diceFormula += `-6`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rollData.noRoll) {
|
||||||
|
let myRoll = await new Roll(rollData.diceFormula).roll()
|
||||||
|
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
|
||||||
|
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||||
|
console.log(">>>> ", myRoll)
|
||||||
|
this.computeResult(actor, rollData)
|
||||||
|
this.computeMarge(rollData, rollData.sdValue) // Calcul de la marge si seuil présent
|
||||||
|
// Compute weapon damage for successful attacks
|
||||||
|
if (rollData.arme && rollData.isSuccess) {
|
||||||
|
this.computeArmeDegats(rollData, actor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rollData.finalResult = myRoll.total
|
|
||||||
this.computeResult(actor, rollData)
|
|
||||||
if (rollData.mode == "init") {
|
if (rollData.mode == "init") {
|
||||||
actor.setFlag("world", "last-initiative", rollData.finalResult)
|
actor.setFlag("world", "last-initiative", rollData.finalResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gestion pouvoir et points d'usage
|
||||||
|
if (rollData.mode == "pouvoir" || rollData.mode == "pouvoirpassif") {
|
||||||
|
actor.incDecPointsUsage(rollData.pouvoir._id, -rollData.pouvoirPointsUsage)
|
||||||
|
}
|
||||||
|
// Gestion sort et points d'âme
|
||||||
|
if (rollData.mode == "sort") {
|
||||||
|
if (rollData.spendEsprit) {
|
||||||
|
actor.inDecCarac("esp", -rollData.totalEsprit)
|
||||||
|
} else {
|
||||||
|
actor.incDecPointsAme(-rollData.sortPointsAme)
|
||||||
|
if (rollData.sort.system.competence == "Magie du Clan") {
|
||||||
|
actor.incDecPV(-2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.createChatWithRollMode(rollData.alias, {
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
content: await renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.html`, rollData)
|
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.hbs`, rollData)
|
||||||
}, rollData)
|
}, rollData)
|
||||||
|
|
||||||
|
// Gestion attaque standard
|
||||||
|
if ((rollData.mode == "arme" || rollData.mode == "attaquebrutale" || rollData.mode == "attaquecharge") &&
|
||||||
|
rollData.defenderTokenId && rollData.arme) {
|
||||||
|
if (rollData.arme.system.categorie != "trait" && rollData.arme.system.categorie != "poing" && rollData.arme.system.categorie != "epaule") {
|
||||||
|
await this.displayUneDefense(rollData, actor, "Parade", rollData.defenderParade)
|
||||||
|
await this.displayUneDefense(rollData, actor, "Esquive", rollData.defenderEsquive)
|
||||||
|
} else if (rollData.sdValue) {
|
||||||
|
this.displayUneDefense(rollData, actor, "A Distance", rollData.sdValue)
|
||||||
|
} else {
|
||||||
|
ui.notifications.warn("Pas de difficulté positionnée pour l'attaque à distance.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Gestion assomer
|
||||||
|
if (rollData.mode == "assommer" && rollData.defenderTokenId && rollData.arme) {
|
||||||
|
await this.displayAsssomer(rollData, actor, "Assommer", "Surprise", rollData.defenderResistancePhysique)
|
||||||
|
await this.displayAsssomer(rollData, actor, "Assommer", "Conscient, Résistance+6", rollData.defenderResistancePhysique + 6)
|
||||||
|
await this.displayAsssomer(rollData, actor, "Assommer", "Conscient, Parade", rollData.defenderParade)
|
||||||
|
await this.displayAsssomer(rollData, actor, "Assommer", "Conscient, Esquive", rollData.defenderEsquive + 6)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static async bonusRollHeritiers(rollData) {
|
static async bonusRollHeritiers(rollData) {
|
||||||
rollData.bonusFormula = rollData.addedBonus
|
rollData.bonusFormula = rollData.addedBonus
|
||||||
|
|
||||||
let bonusRoll = new Roll(rollData.bonusFormula).roll({ async: false })
|
let bonusRoll = await new Roll(rollData.bonusFormula).roll()
|
||||||
await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"));
|
await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"));
|
||||||
rollData.bonusRoll = bonusRoll
|
rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)
|
||||||
|
|
||||||
rollData.finalResult += rollData.bonusRoll.total
|
rollData.finalResult += rollData.bonusRoll.total
|
||||||
|
|
||||||
this.computeResult(rollData)
|
this.computeResult(rollData)
|
||||||
|
|
||||||
this.createChatWithRollMode(rollData.alias, {
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
content: await renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.html`, rollData)
|
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.hbs`, rollData)
|
||||||
}, rollData)
|
}, rollData)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -395,6 +650,10 @@ export class HeritiersUtility {
|
|||||||
return game.users.filter(filter).map(user => user._id);
|
return game.users.filter(filter).map(user => user._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
static isArmeMelee(arme) {
|
||||||
|
return (arme.type == "arme" && (arme.system.categorie == "lourde" || arme.system.categorie == "blanche" || arme.system.categorie == "improvise"))
|
||||||
|
}
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static getWhisperRecipients(rollMode, name) {
|
static getWhisperRecipients(rollMode, name) {
|
||||||
switch (rollMode) {
|
switch (rollMode) {
|
||||||
@@ -412,7 +671,7 @@ export class HeritiersUtility {
|
|||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static blindMessageToGM(chatOptions) {
|
static blindMessageToGM(chatOptions) {
|
||||||
let chatGM = duplicate(chatOptions);
|
let chatGM = foundry.utils.duplicate(chatOptions);
|
||||||
chatGM.whisper = this.getUsers(user => user.isGM);
|
chatGM.whisper = this.getUsers(user => user.isGM);
|
||||||
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
|
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
|
||||||
console.log("blindMessageToGM", chatGM);
|
console.log("blindMessageToGM", chatGM);
|
||||||
@@ -476,11 +735,17 @@ export class HeritiersUtility {
|
|||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static getBasicRollData() {
|
static getBasicRollData() {
|
||||||
let rollData = {
|
let rollData = {
|
||||||
rollId: randomID(16),
|
rollId: foundry.utils.randomID(16),
|
||||||
rollMode: game.settings.get("core", "rollMode"),
|
rollMode: game.settings.get("core", "rollMode"),
|
||||||
sdList: game.system.lesheritiers.config.seuilsDifficulte,
|
sdList: game.system.lesheritiers.config.seuilsDifficulte,
|
||||||
sdValue: 0,
|
sdValue: -1,
|
||||||
bonusMalusContext: 0
|
bonusAttaquePlusieurs: 0,
|
||||||
|
attaqueDeuxArmes: 0,
|
||||||
|
attaqueDos: false,
|
||||||
|
bonusMalusContext: 0,
|
||||||
|
attaqueCible: "none",
|
||||||
|
config: game.system.lesheritiers.config,
|
||||||
|
rulesMalus: []
|
||||||
}
|
}
|
||||||
return rollData
|
return rollData
|
||||||
}
|
}
|
||||||
@@ -491,18 +756,18 @@ export class HeritiersUtility {
|
|||||||
if (target) {
|
if (target) {
|
||||||
rollData.defenderTokenId = target.id
|
rollData.defenderTokenId = target.id
|
||||||
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
|
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
|
||||||
rollData.armeDefense = defender.getBestDefenseValue()
|
rollData.defenderName = defender.name
|
||||||
rollData.targetVigueur = defender.getVigueur()
|
rollData.defenderParade = defender.getCurrentParade()
|
||||||
if (rollData.armeDefense) {
|
rollData.defenderEsquive = defender.getCurrentEsquive()
|
||||||
rollData.difficulte = rollData.armeDefense.system.totalDefensif
|
rollData.defenderResistancePhysique = defender.getCurrentResistancePhysique()
|
||||||
} else {
|
|
||||||
ui.notifications.warn("Aucune arme de défense équipée, difficulté manuelle à positionner.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static createChatWithRollMode(name, chatOptions, rollData = undefined) {
|
static createChatWithRollMode(name, chatOptions, rollData = undefined, rollMode = undefined) {
|
||||||
|
if (rollMode == undefined) {
|
||||||
|
rollMode = game.settings.get("core", "rollMode")
|
||||||
|
}
|
||||||
this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions, rollData)
|
this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions, rollData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,13 +813,13 @@ export class HeritiersUtility {
|
|||||||
static chatRollMenu(html, options) {
|
static chatRollMenu(html, options) {
|
||||||
let canApply = li => canvas.tokens.controlled.length && li.find(".heritiers-roll").length
|
let canApply = li => canvas.tokens.controlled.length && li.find(".heritiers-roll").length
|
||||||
let canApplyBA = function (li) {
|
let canApplyBA = function (li) {
|
||||||
let message = game.messages.get(li.attr("data-message-id"))
|
let message = game.messages.get($(li).attr("data-message-id"))
|
||||||
let rollData = message.getFlag("world", "heritiers-roll")
|
let rollData = message.getFlag("world", "heritiers-roll")
|
||||||
let actor = this.getActorFromRollData(rollData)
|
let actor = this.getActorFromRollData(rollData)
|
||||||
return (!rollData.isReroll && actor.getBonneAventure() > 0)
|
return (!rollData.isReroll && actor.getBonneAventure() > 0)
|
||||||
}
|
}
|
||||||
let canApplyPE = function (li) {
|
let canApplyPE = function (li) {
|
||||||
let message = game.messages.get(li.attr("data-message-id"))
|
let message = game.messages.get($(li).attr("data-message-id"))
|
||||||
let rollData = message.getFlag("world", "heritiers-roll")
|
let rollData = message.getFlag("world", "heritiers-roll")
|
||||||
let actor = this.getActorFromRollData(rollData)
|
let actor = this.getActorFromRollData(rollData)
|
||||||
return (!rollData.isReroll && actor.getEclat() > 0)
|
return (!rollData.isReroll && actor.getEclat() > 0)
|
||||||
@@ -588,25 +853,30 @@ export class HeritiersUtility {
|
|||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
static async confirmDelete(actorSheet, li) {
|
static async confirmDelete(actorSheet, li) {
|
||||||
let itemId = li.data("item-id");
|
// Support both jQuery and native elements
|
||||||
let msgTxt = "<p>Are you sure to remove this Item ?";
|
let itemId = li.dataset ? li.dataset.itemId : li.data("item-id");
|
||||||
|
let msgTxt = "<p>Certain de supprimer cet item ?";
|
||||||
let buttons = {
|
let buttons = {
|
||||||
delete: {
|
delete: {
|
||||||
icon: '<i class="fas fa-check"></i>',
|
icon: '<i class="fas fa-check"></i>',
|
||||||
label: "Yes, remove it",
|
label: "Oui !",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
||||||
li.slideUp(200, () => actorSheet.render(false));
|
if (li.slideUp) {
|
||||||
|
li.slideUp(200, () => actorSheet.render(false));
|
||||||
|
} else {
|
||||||
|
actorSheet.render(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
icon: '<i class="fas fa-times"></i>',
|
icon: '<i class="fas fa-times"></i>',
|
||||||
label: "Cancel"
|
label: "Non !"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msgTxt += "</p>";
|
msgTxt += "</p>";
|
||||||
let d = new Dialog({
|
let d = new Dialog({
|
||||||
title: "Confirm removal",
|
title: "Confirmer la suppression",
|
||||||
content: msgTxt,
|
content: msgTxt,
|
||||||
buttons: buttons,
|
buttons: buttons,
|
||||||
default: "cancel"
|
default: "cancel"
|
||||||
@@ -614,21 +884,56 @@ export class HeritiersUtility {
|
|||||||
d.render(true);
|
d.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************************/
|
static loadSort() {
|
||||||
static async __create_talents_table() {
|
// Create afolder in the item directory if it doesn't exist
|
||||||
let compName = "fvtt-les-heritiers.talents-cellule"
|
if (!game.folders.getName("Magie du Clan")) {
|
||||||
const compData = await HeritiersUtility.loadCompendium(compName)
|
Folder.create({
|
||||||
let talents = compData.map(i => i.toObject())
|
name: "Magie du Clan",
|
||||||
|
type: "Item",
|
||||||
let htmlTab = "<table border='1'><tbody>";
|
color: "#3b1361"
|
||||||
for (let entryData of talents) {
|
});
|
||||||
console.log(entryData)
|
|
||||||
htmlTab += `<tr><td>@UUID[Compendium.${compName}.${entryData._id}]{${entryData.name}}</td>`
|
|
||||||
htmlTab += `<td>${entryData.system.description}</td>`;
|
|
||||||
//htmlTab += `<td>${entryData.system.resumebonus}</td>`;
|
|
||||||
htmlTab += "</tr>\n";
|
|
||||||
}
|
}
|
||||||
htmlTab += "</table>";
|
|
||||||
await JournalEntry.create({ name: 'Liste des Talents de Cellule', content: htmlTab });
|
// Load the srcdata/sorts-druidisme.json file
|
||||||
|
return fetch("systems/fvtt-les-heritiers/srcdata/sort_magieduclan.json")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log("Sorts Magie du Clan loaded:", data);
|
||||||
|
this.sortDruidisme = data;
|
||||||
|
// Loop through the spell and create the "sort "item based on the JSON content
|
||||||
|
data.forEach(spell => {
|
||||||
|
spell.name = spell.name;
|
||||||
|
spell.type = "sort";
|
||||||
|
spell.system = {
|
||||||
|
niveau: spell.niveau,
|
||||||
|
competence: spell.competence,
|
||||||
|
carac1: spell.carac1,
|
||||||
|
carac2: spell.carac2,
|
||||||
|
description: spell.description,
|
||||||
|
ingredients: spell.ingredients,
|
||||||
|
portee: spell.portee,
|
||||||
|
duree: spell.duree,
|
||||||
|
concentration: spell.concentration,
|
||||||
|
critique: spell.critique,
|
||||||
|
resistance: spell.resistance,
|
||||||
|
coutactivation: spell.coutactivation
|
||||||
|
};
|
||||||
|
spell.img = "systems/fvtt-les-heritiers/assets/icons/sort.webp";
|
||||||
|
spell.folder = game.folders.getName("Magie du Clan").id;
|
||||||
|
|
||||||
|
// Create the item in the world
|
||||||
|
Item.create(spell)
|
||||||
|
.then(item => {
|
||||||
|
console.log("Sort created:", item);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Error creating sort item:", error);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Error loading druidism spells:", error);
|
||||||
|
return [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
modules/models/accessoire.mjs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les accessoires
|
||||||
|
*/
|
||||||
|
export default class AccessoireDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
quantite: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
equipped: new fields.BooleanField({ initial: false }),
|
||||||
|
lieu: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
30
modules/models/arme.mjs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les armes
|
||||||
|
*/
|
||||||
|
export default class ArmeDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
quantite: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
equipped: new fields.BooleanField({ initial: false }),
|
||||||
|
categorie: new fields.StringField({ initial: "trait" }),
|
||||||
|
armetype: new fields.StringField({ initial: "trait" }),
|
||||||
|
degats: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
precision: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
cadence: new fields.StringField({ initial: "" }),
|
||||||
|
enraiement: new fields.StringField({ initial: "" }),
|
||||||
|
magasin: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
charge: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
portee: new fields.StringField({ initial: "" }),
|
||||||
|
legalite: new fields.StringField({ initial: "libre" }),
|
||||||
|
dissimulation: new fields.StringField({ initial: "tresfacile" }),
|
||||||
|
zone: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
temps: new fields.StringField({ initial: "" }),
|
||||||
|
allumage: new fields.StringField({ initial: "" }),
|
||||||
|
special: new fields.StringField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
11
modules/models/atoutfeerique.mjs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les atouts féériques
|
||||||
|
*/
|
||||||
|
export default class AtoutFeeriqueDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
11
modules/models/avantage.mjs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les avantages
|
||||||
|
*/
|
||||||
|
export default class AvantageDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
11
modules/models/base-item.mjs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Base data model pour les items
|
||||||
|
*/
|
||||||
|
export default class BaseItemDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
21
modules/models/capacitenaturelle.mjs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les capacités naturelles
|
||||||
|
*/
|
||||||
|
export default class CapaciteNaturelleDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
pouvoirtype: new fields.StringField({ initial: "actif" }),
|
||||||
|
activation: new fields.StringField({ initial: "" }),
|
||||||
|
cibles: new fields.StringField({ initial: "" }),
|
||||||
|
effet: new fields.StringField({ initial: "" }),
|
||||||
|
duree: new fields.StringField({ initial: "" }),
|
||||||
|
portee: new fields.StringField({ initial: "" }),
|
||||||
|
resistance: new fields.StringField({ initial: "aucune" }),
|
||||||
|
resistanceautre: new fields.StringField({ initial: "" }),
|
||||||
|
isvirulence: new fields.BooleanField({ initial: false }),
|
||||||
|
virulence: new fields.StringField({ initial: "" }),
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
43
modules/models/competence.mjs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les compétences
|
||||||
|
*/
|
||||||
|
export default class CompetenceDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
categorie: new fields.StringField({ initial: "utile" }),
|
||||||
|
profil: new fields.StringField({ initial: "aventurier" }),
|
||||||
|
niveau: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
nomniveau: new fields.SchemaField({
|
||||||
|
1: new fields.StringField({ initial: "" }),
|
||||||
|
2: new fields.StringField({ initial: "" }),
|
||||||
|
3: new fields.StringField({ initial: "" }),
|
||||||
|
4: new fields.StringField({ initial: "" })
|
||||||
|
}),
|
||||||
|
nomniveausouffle: new fields.SchemaField({
|
||||||
|
soufflecombat: new fields.SchemaField({
|
||||||
|
1: new fields.StringField({ initial: "" }),
|
||||||
|
2: new fields.StringField({ initial: "" }),
|
||||||
|
3: new fields.StringField({ initial: "" }),
|
||||||
|
4: new fields.StringField({ initial: "" })
|
||||||
|
}),
|
||||||
|
soufflemouvement: new fields.SchemaField({
|
||||||
|
1: new fields.StringField({ initial: "" }),
|
||||||
|
2: new fields.StringField({ initial: "" }),
|
||||||
|
3: new fields.StringField({ initial: "" }),
|
||||||
|
4: new fields.StringField({ initial: "" })
|
||||||
|
}),
|
||||||
|
souffleesprit: new fields.SchemaField({
|
||||||
|
1: new fields.StringField({ initial: "" }),
|
||||||
|
2: new fields.StringField({ initial: "" }),
|
||||||
|
3: new fields.StringField({ initial: "" }),
|
||||||
|
4: new fields.StringField({ initial: "" })
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
predilection: new fields.BooleanField({ initial: false }),
|
||||||
|
specialites: new fields.ArrayField(new fields.ObjectField(), { initial: [] }),
|
||||||
|
ismagie: new fields.BooleanField({ initial: false }),
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
12
modules/models/contact.mjs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les contacts
|
||||||
|
*/
|
||||||
|
export default class ContactDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
contacttype: new fields.StringField({ initial: "contact" }),
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
11
modules/models/desavantage.mjs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les désavantages
|
||||||
|
*/
|
||||||
|
export default class DesavantageDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
15
modules/models/equipement.mjs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les équipements
|
||||||
|
*/
|
||||||
|
export default class EquipementDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
quantite: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
equipped: new fields.BooleanField({ initial: false })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/models/fee.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les fées
|
||||||
|
*/
|
||||||
|
export default class FeeDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
feetype: new fields.StringField({ initial: "traditionnelle" }),
|
||||||
|
avantages: new fields.StringField({ initial: "" }),
|
||||||
|
desavantages: new fields.StringField({ initial: "" }),
|
||||||
|
pouvoirsfeeriquesmasque: new fields.StringField({ initial: "" }),
|
||||||
|
pouvoirsfeeriquesdemasque: new fields.StringField({ initial: "" }),
|
||||||
|
atoutsfeeriques: new fields.StringField({ initial: "" }),
|
||||||
|
competences: new fields.StringField({ initial: "" }),
|
||||||
|
capacitenaturelles: new fields.StringField({ initial: "" }),
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
24
modules/models/index.mjs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Index des DataModels pour Les Héritiers
|
||||||
|
* Ce fichier centralise tous les exports des modèles de données
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Modèles d'acteurs
|
||||||
|
export { default as PersonnageDataModel } from './personnage.mjs';
|
||||||
|
export { default as PnjDataModel } from './pnj.mjs';
|
||||||
|
|
||||||
|
// Modèles d'items
|
||||||
|
export { default as AccessoireDataModel } from './accessoire.mjs';
|
||||||
|
export { default as ArmeDataModel } from './arme.mjs';
|
||||||
|
export { default as AtoutFeeriqueDataModel } from './atoutfeerique.mjs';
|
||||||
|
export { default as AvantageDataModel } from './avantage.mjs';
|
||||||
|
export { default as CapaciteNaturelleDataModel } from './capacitenaturelle.mjs';
|
||||||
|
export { default as CompetenceDataModel } from './competence.mjs';
|
||||||
|
export { default as ContactDataModel } from './contact.mjs';
|
||||||
|
export { default as DesavantageDataModel } from './desavantage.mjs';
|
||||||
|
export { default as EquipementDataModel } from './equipement.mjs';
|
||||||
|
export { default as FeeDataModel } from './fee.mjs';
|
||||||
|
export { default as PouvoirDataModel } from './pouvoir.mjs';
|
||||||
|
export { default as ProfilDataModel } from './profil.mjs';
|
||||||
|
export { default as ProtectionDataModel } from './protection.mjs';
|
||||||
|
export { default as SortDataModel } from './sort.mjs';
|
||||||
234
modules/models/personnage.mjs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les personnages
|
||||||
|
*/
|
||||||
|
export default class PersonnageDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
// Template biodata
|
||||||
|
biodata: new fields.SchemaField({
|
||||||
|
name: new fields.StringField({ initial: "" }),
|
||||||
|
activite: new fields.StringField({ initial: "" }),
|
||||||
|
nomhumain: new fields.StringField({ initial: "" }),
|
||||||
|
activites: new fields.StringField({ initial: "" }),
|
||||||
|
fortune: new fields.StringField({ initial: "0" }),
|
||||||
|
traitscaracteres: new fields.StringField({ initial: "" }),
|
||||||
|
tailledemasquee: new fields.StringField({ initial: "" }),
|
||||||
|
taillemasquee: new fields.StringField({ initial: "" }),
|
||||||
|
poidsmasquee: new fields.StringField({ initial: "" }),
|
||||||
|
poidsdemasquee: new fields.StringField({ initial: "" }),
|
||||||
|
apparencemasquee: new fields.StringField({ initial: "" }),
|
||||||
|
apparencedemasquee: new fields.StringField({ initial: "" }),
|
||||||
|
titrefamille: new fields.StringField({ initial: "" }),
|
||||||
|
langues: new fields.StringField({ initial: "" }),
|
||||||
|
factionfeerique: new fields.StringField({ initial: "" }),
|
||||||
|
typetaille: new fields.StringField({ initial: "" }),
|
||||||
|
age: new fields.StringField({ initial: "0" }),
|
||||||
|
poids: new fields.StringField({ initial: "" }),
|
||||||
|
taille: new fields.StringField({ initial: "" }),
|
||||||
|
cheveux: new fields.StringField({ initial: "" }),
|
||||||
|
sexe: new fields.StringField({ initial: "" }),
|
||||||
|
yeux: new fields.StringField({ initial: "" }),
|
||||||
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
revesetranges: new fields.HTMLField({ initial: "" }),
|
||||||
|
secretsdecouverts: new fields.HTMLField({ initial: "" }),
|
||||||
|
questions: new fields.HTMLField({ initial: "" }),
|
||||||
|
habitat: new fields.HTMLField({ initial: "" }),
|
||||||
|
notes: new fields.HTMLField({ initial: "" }),
|
||||||
|
statut: new fields.StringField({ initial: "" }),
|
||||||
|
playernotes: new fields.HTMLField({ initial: "" }),
|
||||||
|
gmnotes: new fields.HTMLField({ initial: "" }),
|
||||||
|
magie: new fields.BooleanField({ initial: false })
|
||||||
|
}),
|
||||||
|
// Template core
|
||||||
|
subactors: new fields.ArrayField(new fields.StringField(), { initial: [] }),
|
||||||
|
caracteristiques: new fields.SchemaField({
|
||||||
|
agi: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Agilité" }),
|
||||||
|
labelnorm: new fields.StringField({ initial: "agilite" }),
|
||||||
|
abbrev: new fields.StringField({ initial: "agi" }),
|
||||||
|
kind: new fields.StringField({ initial: "physical" }),
|
||||||
|
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 1, integer: true })
|
||||||
|
}),
|
||||||
|
con: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Constitution" }),
|
||||||
|
labelnorm: new fields.StringField({ initial: "constitution" }),
|
||||||
|
abbrev: new fields.StringField({ initial: "con" }),
|
||||||
|
kind: new fields.StringField({ initial: "physical" }),
|
||||||
|
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 1, integer: true })
|
||||||
|
}),
|
||||||
|
for: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Force" }),
|
||||||
|
labelnorm: new fields.StringField({ initial: "force" }),
|
||||||
|
abbrev: new fields.StringField({ initial: "for" }),
|
||||||
|
kind: new fields.StringField({ initial: "physical" }),
|
||||||
|
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 1, integer: true })
|
||||||
|
}),
|
||||||
|
prec: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Précision" }),
|
||||||
|
labelnorm: new fields.StringField({ initial: "precision" }),
|
||||||
|
abbrev: new fields.StringField({ initial: "prec" }),
|
||||||
|
kind: new fields.StringField({ initial: "physical" }),
|
||||||
|
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 1, integer: true })
|
||||||
|
}),
|
||||||
|
esp: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Esprit" }),
|
||||||
|
labelnorm: new fields.StringField({ initial: "esprit" }),
|
||||||
|
abbrev: new fields.StringField({ initial: "esp" }),
|
||||||
|
kind: new fields.StringField({ initial: "mental" }),
|
||||||
|
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 1, integer: true })
|
||||||
|
}),
|
||||||
|
per: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Perception" }),
|
||||||
|
labelnorm: new fields.StringField({ initial: "perception" }),
|
||||||
|
abbrev: new fields.StringField({ initial: "per" }),
|
||||||
|
kind: new fields.StringField({ initial: "mental" }),
|
||||||
|
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 1, integer: true })
|
||||||
|
}),
|
||||||
|
pres: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Prestance" }),
|
||||||
|
labelnorm: new fields.StringField({ initial: "pres" }),
|
||||||
|
abbrev: new fields.StringField({ initial: "pres" }),
|
||||||
|
kind: new fields.StringField({ initial: "mental" }),
|
||||||
|
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 1, integer: true })
|
||||||
|
}),
|
||||||
|
san: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Sang-Froid" }),
|
||||||
|
labelnorm: new fields.StringField({ initial: "sangfroid" }),
|
||||||
|
abbrev: new fields.StringField({ initial: "san" }),
|
||||||
|
kind: new fields.StringField({ initial: "mental" }),
|
||||||
|
value: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 1, integer: true })
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
statutmasque: new fields.StringField({ initial: "masque" }),
|
||||||
|
rang: new fields.SchemaField({
|
||||||
|
tricherie: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Tricherie" }),
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
feerie: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Féerie" }),
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
masque: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Masque" }),
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
heritage: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Héritage" }),
|
||||||
|
value: new fields.StringField({ initial: "0" }),
|
||||||
|
max: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
scenarios: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
pv: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
mod: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
competences: new fields.SchemaField({
|
||||||
|
aventurier: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Aventurier" }),
|
||||||
|
niveau: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
pp: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
combattant: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Combattant" }),
|
||||||
|
niveau: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
pp: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
erudit: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Erudit" }),
|
||||||
|
niveau: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
pp: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
gentleman: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Gentleman" }),
|
||||||
|
niveau: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
pp: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
roublard: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Roublard" }),
|
||||||
|
niveau: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
pp: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
savant: new fields.SchemaField({
|
||||||
|
label: new fields.StringField({ initial: "Savant" }),
|
||||||
|
niveau: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
rang: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
pp: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
magie: new fields.SchemaField({
|
||||||
|
pointsame: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
experience: new fields.SchemaField({
|
||||||
|
value: new fields.StringField({ initial: "0" }),
|
||||||
|
pourtricher: new fields.StringField({ initial: "0" })
|
||||||
|
}),
|
||||||
|
combat: new fields.SchemaField({
|
||||||
|
esquive: new fields.SchemaField({
|
||||||
|
masquee: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
demasquee: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
parade: new fields.SchemaField({
|
||||||
|
masquee: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
demasquee: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
resistancephysique: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
resistancepsychique: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
protection: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
effetssecondaires: new fields.StringField({ initial: "" }),
|
||||||
|
dissimulation: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
initiative: new fields.SchemaField({
|
||||||
|
masquee: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
demasquee: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
corpsacorps: new fields.SchemaField({
|
||||||
|
masquee: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
demasquee: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
tir: new fields.SchemaField({
|
||||||
|
masquee: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
demasquee: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
10
modules/models/pnj.mjs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les PNJ
|
||||||
|
* Utilise le même schéma que les personnages
|
||||||
|
*/
|
||||||
|
import PersonnageDataModel from './personnage.mjs';
|
||||||
|
|
||||||
|
export default class PnjDataModel extends PersonnageDataModel {
|
||||||
|
// Les PNJ utilisent exactement le même schéma que les personnages
|
||||||
|
// On hérite simplement de PersonnageDataModel
|
||||||
|
}
|
||||||
29
modules/models/pouvoir.mjs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les pouvoirs
|
||||||
|
*/
|
||||||
|
export default class PouvoirDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
pouvoirtype: new fields.StringField({ initial: "actif" }),
|
||||||
|
masquetype: new fields.StringField({ initial: "masque" }),
|
||||||
|
niveau: new fields.StringField({ initial: "normal" }),
|
||||||
|
activation: new fields.StringField({ initial: "" }),
|
||||||
|
istest: new fields.BooleanField({ initial: false }),
|
||||||
|
feeriemasque: new fields.StringField({ initial: "feerie" }),
|
||||||
|
zoneffet: new fields.StringField({ initial: "" }),
|
||||||
|
testautre: new fields.StringField({ initial: "" }),
|
||||||
|
carac: new fields.StringField({ initial: "pre" }),
|
||||||
|
duree: new fields.StringField({ initial: "" }),
|
||||||
|
cibles: new fields.StringField({ initial: "" }),
|
||||||
|
effet: new fields.StringField({ initial: "" }),
|
||||||
|
portee: new fields.StringField({ initial: "" }),
|
||||||
|
resistance: new fields.StringField({ initial: "aucune" }),
|
||||||
|
resistanceautre: new fields.StringField({ initial: "" }),
|
||||||
|
pointsusagecourant: new fields.NumberField({ initial: -1, integer: true }),
|
||||||
|
isvirulence: new fields.BooleanField({ initial: false }),
|
||||||
|
virulence: new fields.StringField({ initial: "" }),
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
12
modules/models/profil.mjs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les profils
|
||||||
|
*/
|
||||||
|
export default class ProfilDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
profiltype: new fields.StringField({ initial: "majeur" }),
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
20
modules/models/protection.mjs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les protections
|
||||||
|
*/
|
||||||
|
export default class ProtectionDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
description: new fields.HTMLField({ initial: "" }),
|
||||||
|
rarete: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
quantite: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
prix: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
equipped: new fields.BooleanField({ initial: false }),
|
||||||
|
points: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
protectiontype: new fields.StringField({ initial: "balle" }),
|
||||||
|
effetsecondaire: new fields.StringField({ initial: "" }),
|
||||||
|
malusagilite: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
dissimulation: new fields.StringField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
27
modules/models/sort.mjs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Data model pour les sorts
|
||||||
|
*/
|
||||||
|
export default class SortDataModel extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
niveau: new fields.StringField({ initial: "1" }),
|
||||||
|
rang: new fields.StringField({ initial: "1" }),
|
||||||
|
competence: new fields.StringField({ initial: "Druidisme" }),
|
||||||
|
carac1: new fields.StringField({ initial: "esp" }),
|
||||||
|
carac2: new fields.StringField({ initial: "none" }),
|
||||||
|
sdspecial: new fields.StringField({ initial: "" }),
|
||||||
|
duree: new fields.StringField({ initial: "" }),
|
||||||
|
portee: new fields.StringField({ initial: "" }),
|
||||||
|
concentration: new fields.StringField({ initial: "" }),
|
||||||
|
informatif: new fields.BooleanField({ initial: false }),
|
||||||
|
texteinformatif: new fields.StringField({ initial: "" }),
|
||||||
|
critique: new fields.StringField({ initial: "" }),
|
||||||
|
ingredients: new fields.StringField({ initial: "" }),
|
||||||
|
resistance: new fields.StringField({ initial: "" }),
|
||||||
|
coutactivation: new fields.StringField({ initial: "" }),
|
||||||
|
souffle: new fields.StringField({ initial: "" }),
|
||||||
|
description: new fields.HTMLField({ initial: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
4971
package-lock.json
generated
Normal file
16
package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "fvtt-les-heritiers",
|
||||||
|
"version": "13.0.7",
|
||||||
|
"description": "Les Héritiers RPG for FoundryVTT (French)",
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp build",
|
||||||
|
"watch": "gulp watch"
|
||||||
|
},
|
||||||
|
"author": "Uberwald/LeRatierBretonnien",
|
||||||
|
"license": "SEE LICENSE IN LICENCE.txt",
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp": "^4.0.2",
|
||||||
|
"gulp-less": "^5.0.0",
|
||||||
|
"gulp-sourcemaps": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
packs/archetypes-fees/000179.ldb
Normal file
0
packs/archetypes-fees/000342.log
Normal file
1
packs/archetypes-fees/CURRENT
Normal file
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000336
|
||||||
0
packs/archetypes-fees/LOCK
Normal file
8
packs/archetypes-fees/LOG
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/01/21-10:53:45.991086 7f152a3fc6c0 Recovering log #334
|
||||||
|
2026/01/21-10:53:46.000764 7f152a3fc6c0 Delete type=3 #332
|
||||||
|
2026/01/21-10:53:46.000821 7f152a3fc6c0 Delete type=0 #334
|
||||||
|
2026/01/21-10:54:50.937646 7f1529bfb6c0 Level-0 table #339: started
|
||||||
|
2026/01/21-10:54:50.937686 7f1529bfb6c0 Level-0 table #339: 0 bytes OK
|
||||||
|
2026/01/21-10:54:50.968467 7f1529bfb6c0 Delete type=0 #337
|
||||||
|
2026/01/21-10:54:51.044557 7f1529bfb6c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||||
|
2026/01/21-10:54:51.044593 7f1529bfb6c0 Manual compaction at level-1 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||||
8
packs/archetypes-fees/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/01/21-10:43:50.240377 7f152b3fe6c0 Recovering log #330
|
||||||
|
2026/01/21-10:43:50.250901 7f152b3fe6c0 Delete type=3 #328
|
||||||
|
2026/01/21-10:43:50.250970 7f152b3fe6c0 Delete type=0 #330
|
||||||
|
2026/01/21-10:46:58.350230 7f1529bfb6c0 Level-0 table #335: started
|
||||||
|
2026/01/21-10:46:58.350259 7f1529bfb6c0 Level-0 table #335: 0 bytes OK
|
||||||
|
2026/01/21-10:46:58.357573 7f1529bfb6c0 Delete type=0 #333
|
||||||
|
2026/01/21-10:46:58.364032 7f1529bfb6c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||||
|
2026/01/21-10:46:58.364070 7f1529bfb6c0 Manual compaction at level-1 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||||
BIN
packs/armes-et-protection/000179.ldb
Normal file
0
packs/armes-et-protection/000342.log
Normal file
1
packs/armes-et-protection/CURRENT
Normal file
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000336
|
||||||
0
packs/armes-et-protection/LOCK
Normal file
8
packs/armes-et-protection/LOG
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/01/21-10:53:46.017018 7f152b3fe6c0 Recovering log #334
|
||||||
|
2026/01/21-10:53:46.027168 7f152b3fe6c0 Delete type=3 #332
|
||||||
|
2026/01/21-10:53:46.027239 7f152b3fe6c0 Delete type=0 #334
|
||||||
|
2026/01/21-10:54:51.075256 7f1529bfb6c0 Level-0 table #339: started
|
||||||
|
2026/01/21-10:54:51.075290 7f1529bfb6c0 Level-0 table #339: 0 bytes OK
|
||||||
|
2026/01/21-10:54:51.119572 7f1529bfb6c0 Delete type=0 #337
|
||||||
|
2026/01/21-10:54:51.190089 7f1529bfb6c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||||
|
2026/01/21-10:54:51.190275 7f1529bfb6c0 Manual compaction at level-1 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||||
8
packs/armes-et-protection/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/01/21-10:43:50.267294 7f152abfd6c0 Recovering log #330
|
||||||
|
2026/01/21-10:43:50.276983 7f152abfd6c0 Delete type=3 #328
|
||||||
|
2026/01/21-10:43:50.277035 7f152abfd6c0 Delete type=0 #330
|
||||||
|
2026/01/21-10:46:58.364131 7f1529bfb6c0 Level-0 table #335: started
|
||||||
|
2026/01/21-10:46:58.364205 7f1529bfb6c0 Level-0 table #335: 0 bytes OK
|
||||||
|
2026/01/21-10:46:58.370782 7f1529bfb6c0 Delete type=0 #333
|
||||||
|
2026/01/21-10:46:58.391376 7f1529bfb6c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||||
|
2026/01/21-10:46:58.391426 7f1529bfb6c0 Manual compaction at level-1 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||||
BIN
packs/atouts-feeriques/000179.ldb
Normal file
0
packs/atouts-feeriques/000342.log
Normal file
1
packs/atouts-feeriques/CURRENT
Normal file
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000336
|
||||||
0
packs/atouts-feeriques/LOCK
Normal file
8
packs/atouts-feeriques/LOG
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/01/21-10:53:45.963781 7f152abfd6c0 Recovering log #334
|
||||||
|
2026/01/21-10:53:45.974011 7f152abfd6c0 Delete type=3 #332
|
||||||
|
2026/01/21-10:53:45.974068 7f152abfd6c0 Delete type=0 #334
|
||||||
|
2026/01/21-10:54:50.901280 7f1529bfb6c0 Level-0 table #339: started
|
||||||
|
2026/01/21-10:54:50.901351 7f1529bfb6c0 Level-0 table #339: 0 bytes OK
|
||||||
|
2026/01/21-10:54:50.937470 7f1529bfb6c0 Delete type=0 #337
|
||||||
|
2026/01/21-10:54:51.044539 7f1529bfb6c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||||
|
2026/01/21-10:54:51.044584 7f1529bfb6c0 Manual compaction at level-1 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||||
8
packs/atouts-feeriques/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/01/21-10:43:50.215403 7f152a3fc6c0 Recovering log #330
|
||||||
|
2026/01/21-10:43:50.225230 7f152a3fc6c0 Delete type=3 #328
|
||||||
|
2026/01/21-10:43:50.225283 7f152a3fc6c0 Delete type=0 #330
|
||||||
|
2026/01/21-10:46:58.336769 7f1529bfb6c0 Level-0 table #335: started
|
||||||
|
2026/01/21-10:46:58.336818 7f1529bfb6c0 Level-0 table #335: 0 bytes OK
|
||||||
|
2026/01/21-10:46:58.343699 7f1529bfb6c0 Delete type=0 #333
|
||||||
|
2026/01/21-10:46:58.364008 7f1529bfb6c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||||
|
2026/01/21-10:46:58.364042 7f1529bfb6c0 Manual compaction at level-1 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||||
BIN
packs/avantages/000179.ldb
Normal file
0
packs/avantages/000342.log
Normal file
1
packs/avantages/CURRENT
Normal file
@@ -0,0 +1 @@
|
|||||||
|
MANIFEST-000336
|
||||||
0
packs/avantages/LOCK
Normal file
8
packs/avantages/LOG
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/01/21-10:53:45.925373 7f152bbff6c0 Recovering log #334
|
||||||
|
2026/01/21-10:53:45.935373 7f152bbff6c0 Delete type=3 #332
|
||||||
|
2026/01/21-10:53:45.935424 7f152bbff6c0 Delete type=0 #334
|
||||||
|
2026/01/21-10:54:50.778002 7f1529bfb6c0 Level-0 table #339: started
|
||||||
|
2026/01/21-10:54:50.778030 7f1529bfb6c0 Level-0 table #339: 0 bytes OK
|
||||||
|
2026/01/21-10:54:50.828753 7f1529bfb6c0 Delete type=0 #337
|
||||||
|
2026/01/21-10:54:50.901010 7f1529bfb6c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||||
|
2026/01/21-10:54:50.901050 7f1529bfb6c0 Manual compaction at level-1 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||||
8
packs/avantages/LOG.old
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2026/01/21-10:43:50.178287 7f152abfd6c0 Recovering log #330
|
||||||
|
2026/01/21-10:43:50.188475 7f152abfd6c0 Delete type=3 #328
|
||||||
|
2026/01/21-10:43:50.188562 7f152abfd6c0 Delete type=0 #330
|
||||||
|
2026/01/21-10:46:58.315382 7f1529bfb6c0 Level-0 table #335: started
|
||||||
|
2026/01/21-10:46:58.315409 7f1529bfb6c0 Level-0 table #335: 0 bytes OK
|
||||||
|
2026/01/21-10:46:58.323079 7f1529bfb6c0 Delete type=0 #333
|
||||||
|
2026/01/21-10:46:58.336644 7f1529bfb6c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||||
|
2026/01/21-10:46:58.336671 7f1529bfb6c0 Manual compaction at level-1 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||||