Compare commits
21 Commits
fvtt-les-h
...
14.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fb11bc587 | |||
| a11d9be830 | |||
| 038aa37838 | |||
| 25b6e30aa8 | |||
| f445741eda | |||
| d3c24e44d8 | |||
| d2cf0d80b1 | |||
| 47350cb2f1 | |||
| 26e46cf10a | |||
| 4722fdf4d6 | |||
| dc3040df26 | |||
| b1bce86604 | |||
| 6066091d8d | |||
| 287b6d83a7 | |||
| d8efba89a1 | |||
| 936d525503 | |||
| b113f630bf | |||
| 939cfb1e86 | |||
| 5f5e0e2a8c | |||
| c993a9a5b1 | |||
| cc7de0e53c |
63
.gitea/workflows/release.yaml
Normal file
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: '14'
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
.history/
|
.history/
|
||||||
|
node_modules
|
||||||
|
|||||||
598
DEVELOPER_INSTRUCTIONS.md
Normal file
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
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
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+)
|
||||||
35
gulpfile.js
Normal file
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;
|
||||||
4
less/heritiers.less
Normal file
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
2809
less/simple-converted.less
Normal file
File diff suppressed because it is too large
Load Diff
213
modules/applications/heritiers-roll-dialog.mjs
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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,26 +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 HeritiersActorPNJSheet extends HeritiersActorSheet {
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
|
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
||||||
classes: ["fvtt-les-heritiers", "sheet", "actor"],
|
|
||||||
template: "systems/fvtt-les-heritiers/templates/actor-pnj-sheet.html",
|
|
||||||
width: 780,
|
|
||||||
height: 840,
|
|
||||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
|
|
||||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
|
||||||
editScore: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
/**
|
|
||||||
* Extend the basic ActorSheet with some very simple modifications
|
|
||||||
* @extends {ActorSheet}
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
export class HeritiersActorSheet extends foundry.appv1.sheets.ActorSheet {
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
|
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
||||||
classes: ["fvtt-les-heritiers", "sheet", "actor"],
|
|
||||||
template: "systems/fvtt-les-heritiers/templates/actor-sheet.html",
|
|
||||||
width: 780,
|
|
||||||
height: 840,
|
|
||||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
|
|
||||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
|
||||||
editScore: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
async getData() {
|
|
||||||
const objectData = foundry.utils.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(),
|
|
||||||
utileSkillsMental: this.actor.organizeUtileSkills("mental"),
|
|
||||||
utileSkillsPhysical: this.actor.organizeUtileSkills("physical"),
|
|
||||||
competencesMagie: HeritiersUtility.getCompetencesMagie(),
|
|
||||||
futileSkills: this.actor.organizeFutileSkills(),
|
|
||||||
contacts: this.actor.organizeContacts(),
|
|
||||||
armes: foundry.utils.duplicate(this.actor.getWeapons()),
|
|
||||||
monnaies: foundry.utils.duplicate(this.actor.getMonnaies()),
|
|
||||||
pouvoirs: foundry.utils.duplicate(this.actor.getPouvoirs()),
|
|
||||||
fee: foundry.utils.duplicate(this.actor.getFee() || {}),
|
|
||||||
protections: foundry.utils.duplicate(this.actor.getArmors()),
|
|
||||||
combat: this.actor.getCombatValues(),
|
|
||||||
equipements: foundry.utils.duplicate(this.actor.getEquipments()),
|
|
||||||
avantages: foundry.utils.duplicate(this.actor.getAvantages()),
|
|
||||||
atouts: foundry.utils.duplicate(this.actor.getAtouts()),
|
|
||||||
capacites: foundry.utils.duplicate(this.actor.getCapacites()),
|
|
||||||
desavantages: foundry.utils.duplicate(this.actor.getDesavantages()),
|
|
||||||
profils: foundry.utils.duplicate(this.actor.getProfils()),
|
|
||||||
pvMalus: this.actor.getPvMalus(),
|
|
||||||
heritage: game.settings.get("fvtt-les-heritiers", "heritiers-heritage"),
|
|
||||||
initiative: this.actor.getFlag("world", "last-initiative") || -1,
|
|
||||||
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.description, { async: true }),
|
|
||||||
revesetranges: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.revesetranges, { async: true }),
|
|
||||||
secretsdecouverts: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.secretsdecouverts, { async: true }),
|
|
||||||
questions: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.questions, { async: true }),
|
|
||||||
habitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.habitat, { async: true }),
|
|
||||||
playernotes: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.playernotes, { async: true }),
|
|
||||||
magieList: this.actor.prepareMagie(),
|
|
||||||
options: this.options,
|
|
||||||
owner: this.document.isOwner,
|
|
||||||
editScore: this.options.editScore,
|
|
||||||
config: game.system.lesheritiers.config,
|
|
||||||
isGM: game.user.isGM,
|
|
||||||
isPNJ: (this.actor.type == "pnj")
|
|
||||||
}
|
|
||||||
this.formData = formData;
|
|
||||||
|
|
||||||
console.log("PC : ", formData, this.object);
|
|
||||||
return formData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
dialogRecupUsage() {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
four: {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "4 Points",
|
|
||||||
callback: () => {
|
|
||||||
this.actor.recupUsage(4)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
all: {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Tous les Points",
|
|
||||||
callback: () => {
|
|
||||||
this.actor.recupUsage(100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).render(true)
|
|
||||||
}
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/** @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-rang').click((event) => {
|
|
||||||
const key = $(event.currentTarget).data("rang-key")
|
|
||||||
this.actor.rollRang(key, false)
|
|
||||||
})
|
|
||||||
html.find('.roll-root-competence').click((event) => {
|
|
||||||
const compKey = $(event.currentTarget).data("attr-key")
|
|
||||||
this.actor.rollRootCompetence(compKey)
|
|
||||||
})
|
|
||||||
html.find('.roll-competence').click((event) => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let compId = li.data("item-id")
|
|
||||||
this.actor.rollCompetence(compId)
|
|
||||||
})
|
|
||||||
html.find('.roll-sort').click((event) => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let sortId = li.data("item-id")
|
|
||||||
this.actor.rollSort(sortId)
|
|
||||||
})
|
|
||||||
html.find('.roll-attaque-arme').click((event) => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let armeId = li.data("item-id")
|
|
||||||
this.actor.rollAttaqueArme(armeId)
|
|
||||||
})
|
|
||||||
html.find('.roll-attaque-brutale-arme').click((event) => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let armeId = li.data("item-id")
|
|
||||||
this.actor.rollAttaqueBrutaleArme(armeId)
|
|
||||||
})
|
|
||||||
html.find('.roll-attaque-charge-arme').click((event) => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let armeId = li.data("item-id")
|
|
||||||
this.actor.rollAttaqueChargeArme(armeId)
|
|
||||||
})
|
|
||||||
html.find('.roll-assomer-arme').click((event) => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let armeId = li.data("item-id")
|
|
||||||
this.actor.rollAssomerArme(armeId)
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('.roll-pouvoir').click((event) => {
|
|
||||||
const li = $(event.currentTarget).parents(".item")
|
|
||||||
let pouvoirId = li.data("item-id")
|
|
||||||
this.actor.rollPouvoir(pouvoirId)
|
|
||||||
})
|
|
||||||
html.find('.dialog-recup-usage').click((event) => {
|
|
||||||
this.dialogRecupUsage()
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('.item-add').click((event) => {
|
|
||||||
const itemType = $(event.currentTarget).data("type")
|
|
||||||
if (itemType == "sort") {
|
|
||||||
// Get data-sort-competence
|
|
||||||
let sortCompetence = $(event.currentTarget).data("sort-competence");
|
|
||||||
if (sortCompetence) {
|
|
||||||
this.actor.createEmbeddedDocuments('Item', [{ name: `Nouveau ${itemType} de ${sortCompetence}`, type: itemType, system: { competence: sortCompetence } }], { renderSheet: true })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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,6 +1,6 @@
|
|||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
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";
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10]
|
const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10]
|
||||||
@@ -171,7 +171,41 @@ export class HeritiersActor extends Actor {
|
|||||||
magie.rang = Math.round(item.system.niveau / 2);
|
magie.rang = Math.round(item.system.niveau / 2);
|
||||||
magie.rangGenericName = game.system.lesheritiers.config.rangName[magie.rang];
|
magie.rangGenericName = game.system.lesheritiers.config.rangName[magie.rang];
|
||||||
console.log("Magie", item.name, item.system.niveau, magie.rang, magie.rangGenericName)
|
console.log("Magie", item.name, item.system.niveau, magie.rang, magie.rangGenericName)
|
||||||
magie.rangSpecificName = game.system.lesheritiers.config.rangNameSpecific[item.name][magie.rangGenericName];
|
//magie.rangSpecificName = game.system.lesheritiers.config.rangNameSpecific[item.name][magie.rangGenericName];
|
||||||
|
magie.sorts = {}
|
||||||
|
if (item.name == "Magie du Clan") {
|
||||||
|
magie.sorts = {
|
||||||
|
"soufflecombat": {
|
||||||
|
1: { nomNiveau: magie.competence.system.nomniveausouffle.soufflecombat["1"], sorts: [] },
|
||||||
|
2: { nomNiveau: magie.competence.system.nomniveausouffle.soufflecombat["2"], sorts: [] },
|
||||||
|
3: { nomNiveau: magie.competence.system.nomniveausouffle.soufflecombat["3"], sorts: [] },
|
||||||
|
4: { nomNiveau: magie.competence.system.nomniveausouffle.soufflecombat["4"], sorts: [] }
|
||||||
|
},
|
||||||
|
"soufflemouvement": {
|
||||||
|
1: { nomNiveau: magie.competence.system.nomniveausouffle.soufflemouvement["1"], sorts: [] },
|
||||||
|
2: { nomNiveau: magie.competence.system.nomniveausouffle.soufflemouvement["2"], sorts: [] },
|
||||||
|
3: { nomNiveau: magie.competence.system.nomniveausouffle.soufflemouvement["3"], sorts: [] },
|
||||||
|
4: { nomNiveau: magie.competence.system.nomniveausouffle.soufflemouvement["4"], sorts: [] }
|
||||||
|
},
|
||||||
|
"souffleesprit": {
|
||||||
|
1: { nomNiveau: magie.competence.system.nomniveausouffle.souffleesprit["1"], sorts: [] },
|
||||||
|
2: { nomNiveau: magie.competence.system.nomniveausouffle.souffleesprit["2"], sorts: [] },
|
||||||
|
3: { nomNiveau: magie.competence.system.nomniveausouffle.souffleesprit["3"], sorts: [] },
|
||||||
|
4: { nomNiveau: magie.competence.system.nomniveausouffle.souffleesprit["4"], sorts: [] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let sort of this.items) {
|
||||||
|
if (sort.type == "sort" && sort.system.competence == item.name) {
|
||||||
|
let sortObj = foundry.utils.duplicate(sort)
|
||||||
|
sortObj.sdValue = HeritiersUtility.getSDSortValue(Number(sort.system.niveau))
|
||||||
|
if (!magie.sorts[sort.system?.souffle]) {
|
||||||
|
console.warn("Sort with unknown souffle ", sort.system.souffle, sort)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
magie.sorts[sort.system.souffle][Number(sort.system.niveau)].sorts.push(sortObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
magie.sorts = {
|
magie.sorts = {
|
||||||
1: { nomNiveau: magie.competence.system.nomniveau["1"], sorts: [] },
|
1: { nomNiveau: magie.competence.system.nomniveau["1"], sorts: [] },
|
||||||
2: { nomNiveau: magie.competence.system.nomniveau["2"], sorts: [] },
|
2: { nomNiveau: magie.competence.system.nomniveau["2"], sorts: [] },
|
||||||
@@ -185,6 +219,7 @@ export class HeritiersActor extends Actor {
|
|||||||
magie.sorts[Number(sort.system.niveau)].sorts.push(sortObj)
|
magie.sorts[Number(sort.system.niveau)].sorts.push(sortObj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
magieList.push(magie)
|
magieList.push(magie)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +277,7 @@ export class HeritiersActor extends Actor {
|
|||||||
let comp = {}
|
let comp = {}
|
||||||
for (let key in game.system.lesheritiers.config.competenceProfil) {
|
for (let key in game.system.lesheritiers.config.competenceProfil) {
|
||||||
if (game.system.lesheritiers.config.competenceProfil[key].kind == kind)
|
if (game.system.lesheritiers.config.competenceProfil[key].kind == kind)
|
||||||
comp[key] = { skills: [], niveau: this.system.competences[key].niveau }
|
comp[key] = { skills: [], niveau: this.system.competences[key].niveau, rang: this.system.competences[key].rang }
|
||||||
}
|
}
|
||||||
for (let item of this.items) {
|
for (let item of this.items) {
|
||||||
if (item.type == "competence") {
|
if (item.type == "competence") {
|
||||||
@@ -319,24 +354,8 @@ export class HeritiersActor extends Actor {
|
|||||||
return combat
|
return combat
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
prepareBaseData() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
async prepareData() {
|
async prepareData() {
|
||||||
|
|
||||||
let pvMax = (this.system.caracteristiques.con.rang * 3) + 9 + this.system.pv.mod
|
|
||||||
if (this.system.pv.max != pvMax) {
|
|
||||||
this.update({ 'system.pv.max': pvMax })
|
|
||||||
}
|
|
||||||
if (this.system.biodata.magie || this.type == "pnj") {
|
|
||||||
let pointsAmes = this.system.caracteristiques.esp.rang + this.system.caracteristiques.san.rang + this.getMaxRangMagie()
|
|
||||||
if (this.system.magie.pointsame.max != pointsAmes) {
|
|
||||||
this.update({ 'system.magie.pointsame.max': pointsAmes })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.prepareData();
|
super.prepareData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,8 +379,45 @@ export class HeritiersActor extends Actor {
|
|||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
|
// Calculate derived PV max
|
||||||
|
let pvMax = (this.system.caracteristiques.con.rang * 3) + 9 + this.system.pv.mod
|
||||||
|
this.system.pv.max = pvMax
|
||||||
|
|
||||||
|
// Calculate derived Points d'Âme max
|
||||||
|
if (this.system.biodata.magie || this.type == "pnj") {
|
||||||
|
let pointsAmes = this.system.caracteristiques.esp.rang + this.system.caracteristiques.san.rang + this.getMaxRangMagie()
|
||||||
|
this.system.magie.pointsame.max = pointsAmes
|
||||||
|
}
|
||||||
|
|
||||||
if (this.type == 'personnage') {
|
if (this.type == 'personnage') {
|
||||||
|
// Calculate rang for each competence profile
|
||||||
|
let competencesRangs = {
|
||||||
|
aventurier: 0,
|
||||||
|
combattant: 0,
|
||||||
|
erudit: 0,
|
||||||
|
gentleman: 0,
|
||||||
|
roublard: 0,
|
||||||
|
savant: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum up niveau for each profil
|
||||||
|
for (let item of this.items) {
|
||||||
|
if (item.type == "competence" && item.system.profil && item.system.categorie == "utile") {
|
||||||
|
let profil = item.system.profil.toLowerCase()
|
||||||
|
if (competencesRangs.hasOwnProperty(profil)) {
|
||||||
|
competencesRangs[profil] += item.system.niveau
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate rang (total / 4, rounded down) for each competence
|
||||||
|
for (let profil in competencesRangs) {
|
||||||
|
let total = competencesRangs[profil]
|
||||||
|
let rang = Math.floor(total / 4)
|
||||||
|
if (this.system.competences[profil]) {
|
||||||
|
this.system.competences[profil].rang = rang
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.prepareDerivedData()
|
super.prepareDerivedData()
|
||||||
@@ -399,6 +455,8 @@ export class HeritiersActor extends Actor {
|
|||||||
if (dataType) {
|
if (dataType) {
|
||||||
if (dataType.toLowerCase() == "number") {
|
if (dataType.toLowerCase() == "number") {
|
||||||
value = Number(value)
|
value = Number(value)
|
||||||
|
} else if (dataType.toLowerCase() == "boolean") {
|
||||||
|
value = Boolean(value)
|
||||||
} else {
|
} else {
|
||||||
value = String(value)
|
value = String(value)
|
||||||
}
|
}
|
||||||
@@ -618,6 +676,14 @@ export class HeritiersActor extends Actor {
|
|||||||
this.update({ 'system.rang.tricherie': tricherie })
|
this.update({ 'system.rang.tricherie': tricherie })
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
// Returns the combattant root competence formatted for rollData, used for PNJ attacks
|
||||||
|
_getCombattantCompetence() {
|
||||||
|
const combattant = this.system.competences.combattant
|
||||||
|
return { name: combattant.label, system: { niveau: combattant.niveau } }
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
getPireCompetence(compName1, compName2) {
|
getPireCompetence(compName1, compName2) {
|
||||||
let comp1 = this.items.find(it => it.name == compName1)
|
let comp1 = this.items.find(it => it.name == compName1)
|
||||||
@@ -678,8 +744,7 @@ export class HeritiersActor extends Actor {
|
|||||||
rollData.caracKey = "per"
|
rollData.caracKey = "per"
|
||||||
}
|
}
|
||||||
rollData.carac = this.system.caracteristiques[rollData.caracKey]
|
rollData.carac = this.system.caracteristiques[rollData.caracKey]
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -688,8 +753,7 @@ export class HeritiersActor extends Actor {
|
|||||||
rollData.mode = "carac"
|
rollData.mode = "carac"
|
||||||
rollData.carac = this.system.caracteristiques[key]
|
rollData.carac = this.system.caracteristiques[key]
|
||||||
rollData.caracKey = key
|
rollData.caracKey = key
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -698,8 +762,7 @@ export class HeritiersActor extends Actor {
|
|||||||
rollData.mode = "rang"
|
rollData.mode = "rang"
|
||||||
rollData.rang = this.system.rang[key]
|
rollData.rang = this.system.rang[key]
|
||||||
rollData.rangKey = key
|
rollData.rangKey = key
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
async rollRootCompetence(compKey) {
|
async rollRootCompetence(compKey) {
|
||||||
@@ -707,8 +770,7 @@ export class HeritiersActor extends Actor {
|
|||||||
rollData.mode = "competence"
|
rollData.mode = "competence"
|
||||||
rollData.competence = { name: this.system.competences[compKey].label, system: { niveau: this.system.competences[compKey].niveau } }
|
rollData.competence = { name: this.system.competences[compKey].label, system: { niveau: this.system.competences[compKey].niveau } }
|
||||||
console.log("RollDatra", rollData)
|
console.log("RollDatra", rollData)
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -716,20 +778,19 @@ export class HeritiersActor extends Actor {
|
|||||||
let rollData = this.getCommonRollData(compId)
|
let rollData = this.getCommonRollData(compId)
|
||||||
rollData.mode = "competence"
|
rollData.mode = "competence"
|
||||||
console.log("RollDatra", rollData)
|
console.log("RollDatra", rollData)
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
inDecCarac(key, incDec) {
|
inDecCarac(key, incDec) {
|
||||||
let carac = this.system.caracteristiques[key]
|
let carac = this.system.caracteristiques[key]
|
||||||
carac.value += incDec
|
carac.value += incDec
|
||||||
if (carac.value < 0 || carac.value > carac.max) {
|
if (carac.value < 0 || carac.value > carac.rang) {
|
||||||
ui.notifications.warn("Pas assez de points dans cette caractéristique !")
|
ui.notifications.warn("Pas assez de points dans cette caractéristique ou rang max atteint !")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
carac.value = Math.max(carac.value, 0)
|
carac.value = Math.max(carac.value, 0)
|
||||||
carac.value = Math.min(carac.value, carac.max)
|
carac.value = Math.min(carac.value, carac.rang)
|
||||||
this.update({ [`system.caracteristiques.${key}`]: carac })
|
this.update({ [`system.caracteristiques.${key}`]: carac })
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -742,20 +803,38 @@ export class HeritiersActor extends Actor {
|
|||||||
ui.notifications.warn("Compétence de magie associée non trouvée !")
|
ui.notifications.warn("Compétence de magie associée non trouvée !")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (sort.system.informatif) {
|
||||||
|
if (sort.system.texteinformatif) {
|
||||||
|
let chatData = { user: game.user.id, speaker: { actor: this, alias: this.name }, content: `<div class="heritiers-informatif-sort"><h4>Sort informatif !</h4>${sort.system.texteinformatif}</div>` }
|
||||||
|
ChatMessage.create(chatData)
|
||||||
|
} else {
|
||||||
|
ui.notifications.info("Ce sort est uniquement informatif et ne peut pas être lancé.")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let rollData = this.getCommonRollData(comp.id)
|
let rollData = this.getCommonRollData(comp.id)
|
||||||
rollData.mode = "sort"
|
rollData.mode = "sort"
|
||||||
rollData.sort = foundry.utils.duplicate(sort)
|
rollData.sort = foundry.utils.duplicate(sort)
|
||||||
rollData.sdValue = HeritiersUtility.getSDSortValue(Number(sort.system.niveau))
|
rollData.sdValue = HeritiersUtility.getSDSortValue(Number(sort.system.niveau))
|
||||||
|
if (Number(sort.system.sdspecial) && Number(sort.system.sdspecial) > 0) {
|
||||||
|
rollData.sdValue = Number(sort.system.sdspecial)
|
||||||
|
}
|
||||||
rollData.sortPointsAme = Number(sort.system.niveau)
|
rollData.sortPointsAme = Number(sort.system.niveau)
|
||||||
|
rollData.totalEsprit = 1
|
||||||
|
if (sort.system.competence == "Grand Langage") {
|
||||||
|
rollData.sortPointsAme *= 2
|
||||||
|
rollData.totalEsprit = Math.floor((rollData.sortPointsAme) / 3)
|
||||||
|
}
|
||||||
if (rollData.sortPointsAme > this.system.magie.pointsame.value) {
|
if (rollData.sortPointsAme > this.system.magie.pointsame.value) {
|
||||||
// Vérifier si au moins 1 point d'Esprit est disponible
|
// Vérifier si au moins 1 point d'Esprit est disponible
|
||||||
if (this.system.caracteristiques.esp.value < 1) {
|
if (this.system.caracteristiques.esp.value <= rollData.totalEsprit) {
|
||||||
ui.notifications.warn("Pas assez de Points d'Esprit ni de Points d'Ame pour lancer ce sort (requis: 1, disponible: " + this.system.caracteristiques.esp.value + ")")
|
ui.notifications.warn(`Pas assez de Points d'Esprit ni de Points d'Ame pour lancer ce sort (requis: ${rollData.totalEsprit}, disponible: ${this.system.caracteristiques.esp.value})`)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
rollData.spendEsprit = true
|
rollData.spendEsprit = true
|
||||||
ui.notifications.warn(`Vous n'avez pas assez de Points d'Ame pour lancer ce sort (requis: ${rollData.sortPointsAme}, disponible: ${this.system.magie.pointsame.value}). Un Point d'Esprit sera utilisé à la place si vous effectuez le lancer.`)
|
ui.notifications.warn(`Vous n'avez pas assez de Points d'Ame pour lancer ce sort (requis: ${rollData.sortPointsAme}, disponible: ${this.system.magie.pointsame.value}).`)
|
||||||
|
ui.notifications.warn(`${rollData.totalEsprit} Points d'Esprit seront utilisés à la place si vous effectuez le lancer.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,8 +850,7 @@ export class HeritiersActor extends Actor {
|
|||||||
rollData.caracKey = sort.system.carac1
|
rollData.caracKey = sort.system.carac1
|
||||||
}
|
}
|
||||||
console.log("RollData", rollData)
|
console.log("RollData", rollData)
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -782,24 +860,26 @@ export class HeritiersActor extends Actor {
|
|||||||
if (arme) {
|
if (arme) {
|
||||||
arme = foundry.utils.duplicate(arme)
|
arme = foundry.utils.duplicate(arme)
|
||||||
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
||||||
let competenceName = "Tir"
|
let key = arme.system.isMelee ? "agi" : "prec"
|
||||||
let key = "prec"
|
let rollData
|
||||||
if (arme.system.isMelee) {
|
if (this.type == 'pnj') {
|
||||||
competenceName = "Mêlée"
|
rollData = this.getCommonRollData()
|
||||||
key = "agi"
|
rollData.competence = this._getCombattantCompetence()
|
||||||
|
} else {
|
||||||
|
let competenceName = arme.system.isMelee ? "Mêlée" : "Tir"
|
||||||
|
rollData = this.getCommonRollData(undefined, competenceName)
|
||||||
}
|
}
|
||||||
let rollData = this.getCommonRollData(undefined, competenceName)
|
|
||||||
rollData.carac = this.system.caracteristiques[key]
|
rollData.carac = this.system.caracteristiques[key]
|
||||||
rollData.caracKey = key
|
rollData.caracKey = key
|
||||||
rollData.arme = arme
|
rollData.arme = arme
|
||||||
rollData.mode = "arme"
|
rollData.mode = "arme"
|
||||||
|
rollData.attackType = "Attaque normale"
|
||||||
rollData.armes = this.getOtherMeleeWeapons(arme)
|
rollData.armes = this.getOtherMeleeWeapons(arme)
|
||||||
if (rollData.defenderTokenId && arme.system.isMelee) {
|
if (rollData.defenderTokenId && arme.system.isMelee) {
|
||||||
rollData.cacheDifficulte = true
|
rollData.cacheDifficulte = true
|
||||||
}
|
}
|
||||||
console.log(">>>> ARME", rollData)
|
console.log(">>>> ARME", rollData)
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,17 +888,22 @@ export class HeritiersActor extends Actor {
|
|||||||
let arme = this.items.get(armeId)
|
let arme = this.items.get(armeId)
|
||||||
if (arme) {
|
if (arme) {
|
||||||
let key = "for"
|
let key = "for"
|
||||||
let competenceName = "Mêlée"
|
|
||||||
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
||||||
let rollData = this.getCommonRollData(undefined, competenceName)
|
let rollData
|
||||||
|
if (this.type == 'pnj') {
|
||||||
|
rollData = this.getCommonRollData()
|
||||||
|
rollData.competence = this._getCombattantCompetence()
|
||||||
|
} else {
|
||||||
|
rollData = this.getCommonRollData(undefined, "Mêlée")
|
||||||
|
}
|
||||||
rollData.carac = this.system.caracteristiques[key]
|
rollData.carac = this.system.caracteristiques[key]
|
||||||
rollData.caracKey = key
|
rollData.caracKey = key
|
||||||
rollData.arme = foundry.utils.duplicate(arme)
|
rollData.arme = foundry.utils.duplicate(arme)
|
||||||
rollData.mode = "attaquebrutale"
|
rollData.mode = "attaquebrutale"
|
||||||
|
rollData.attackType = "Attaque brutale"
|
||||||
rollData.armes = this.getOtherMeleeWeapons(arme)
|
rollData.armes = this.getOtherMeleeWeapons(arme)
|
||||||
rollData.rulesMalus.push({ name: "Attaque brutale", value: -2 })
|
rollData.rulesMalus.push({ name: "Attaque brutale", value: -2 })
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -827,15 +912,21 @@ export class HeritiersActor extends Actor {
|
|||||||
if (arme) {
|
if (arme) {
|
||||||
let key = "agi"
|
let key = "agi"
|
||||||
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
||||||
|
let rollData
|
||||||
|
if (this.type == 'pnj') {
|
||||||
|
rollData = this.getCommonRollData()
|
||||||
|
rollData.competence = this._getCombattantCompetence()
|
||||||
|
} else {
|
||||||
let pireCompetence = this.getPireCompetence("Mêlée", "Mouvement")
|
let pireCompetence = this.getPireCompetence("Mêlée", "Mouvement")
|
||||||
let rollData = this.getCommonRollData(undefined, pireCompetence.name)
|
rollData = this.getCommonRollData(undefined, pireCompetence.name)
|
||||||
|
}
|
||||||
rollData.carac = this.system.caracteristiques[key]
|
rollData.carac = this.system.caracteristiques[key]
|
||||||
rollData.caracKey = key
|
rollData.caracKey = key
|
||||||
rollData.arme = foundry.utils.duplicate(arme)
|
rollData.arme = foundry.utils.duplicate(arme)
|
||||||
rollData.armes = this.getOtherMeleeWeapons(arme)
|
rollData.armes = this.getOtherMeleeWeapons(arme)
|
||||||
rollData.mode = "attaquecharge"
|
rollData.mode = "attaquecharge"
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
rollData.attackType = "Attaque en charge"
|
||||||
rollDialog.render(true)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -843,18 +934,22 @@ export class HeritiersActor extends Actor {
|
|||||||
async rollAssomerArme(armeId) {
|
async rollAssomerArme(armeId) {
|
||||||
let arme = this.items.get(armeId)
|
let arme = this.items.get(armeId)
|
||||||
if (arme) {
|
if (arme) {
|
||||||
let competenceName = "Mêlée"
|
let rollData
|
||||||
//arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
if (this.type == 'pnj') {
|
||||||
let rollData = this.getCommonRollData(undefined, competenceName)
|
rollData = this.getCommonRollData()
|
||||||
|
rollData.competence = this._getCombattantCompetence()
|
||||||
|
} else {
|
||||||
|
rollData = this.getCommonRollData(undefined, "Mêlée")
|
||||||
|
}
|
||||||
rollData.carac = this.system.caracteristiques["agi"]
|
rollData.carac = this.system.caracteristiques["agi"]
|
||||||
rollData.caracKey = "agi"
|
rollData.caracKey = "agi"
|
||||||
rollData.arme = foundry.utils.duplicate(arme)
|
rollData.arme = foundry.utils.duplicate(arme)
|
||||||
rollData.mode = "assommer"
|
rollData.mode = "assommer"
|
||||||
|
rollData.attackType = "Assommer"
|
||||||
if (rollData.defenderTokenId) {
|
if (rollData.defenderTokenId) {
|
||||||
rollData.cacheDifficulte = true
|
rollData.cacheDifficulte = true
|
||||||
}
|
}
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -948,8 +1043,7 @@ export class HeritiersActor extends Actor {
|
|||||||
//ui.notifications.warn("Le pouvoir actif " + pouvoir.name + " a été utilisé, dépense de " + pouvoirPointsUsage + " points d'usage")
|
//ui.notifications.warn("Le pouvoir actif " + pouvoir.name + " a été utilisé, dépense de " + pouvoirPointsUsage + " points d'usage")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
await HeritiersRollDialog.create(this, rollData)
|
||||||
rollDialog.render(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -260,24 +260,6 @@ export const HERITIERS_CONFIG = {
|
|||||||
"Maître": "Maître",
|
"Maître": "Maître",
|
||||||
"Grand Maître": "Éminence"
|
"Grand Maître": "Éminence"
|
||||||
},
|
},
|
||||||
"Magie du Clan - Souffle du Combat": {
|
|
||||||
"Novice": "Apprenti",
|
|
||||||
"Adepte": "Disciple",
|
|
||||||
"Maître": "Maître",
|
|
||||||
"Grand Maître": "Éminence"
|
|
||||||
},
|
|
||||||
"Magie du Clan - Souffle du Mouvement": {
|
|
||||||
"Novice": "Apprenti",
|
|
||||||
"Adepte": "Disciple",
|
|
||||||
"Maître": "Maître",
|
|
||||||
"Grand Maître": "Éminence"
|
|
||||||
},
|
|
||||||
"Magie du Clan - Souffle de l'Esprit": {
|
|
||||||
"Novice": "Apprenti",
|
|
||||||
"Adepte": "Disciple",
|
|
||||||
"Maître": "Maître",
|
|
||||||
"Grand Maître": "Éminence"
|
|
||||||
},
|
|
||||||
"Théurgie": {
|
"Théurgie": {
|
||||||
"Novice": "Frère",
|
"Novice": "Frère",
|
||||||
"Adepte": "Père",
|
"Adepte": "Père",
|
||||||
@@ -292,9 +274,9 @@ export const HERITIERS_CONFIG = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
soufflesMagieDuClan: {
|
soufflesMagieDuClan: {
|
||||||
"Souffle du Combat": "Souffle du Combat",
|
"soufflecombat": "Souffle du Combat",
|
||||||
"Souffle du Mouvement": "Souffle du Mouvement",
|
"soufflemouvement": "Souffle du Mouvement",
|
||||||
"Souffle de l'Esprit": "Souffle de l'Esprit"
|
"souffleesprit": "Souffle de l'Esprit"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extend the basic ItemSheet with some very simple modifications
|
|
||||||
* @extends {ItemSheet}
|
|
||||||
*/
|
|
||||||
export class HeritiersItemSheet extends foundry.appv1.sheets.ItemSheet {
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
|
|
||||||
return foundry.utils.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 = foundry.utils.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,
|
|
||||||
isArmeMelee: HeritiersUtility.isArmeMelee(this.object),
|
|
||||||
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.description, {async: true}),
|
|
||||||
mr: (this.object.type == 'specialisation'),
|
|
||||||
isGM: game.user.isGM,
|
|
||||||
usageMax: -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Items specific data
|
|
||||||
if (this.object.type == 'pouvoir' && this.document.isOwner && this.actor) {
|
|
||||||
formData.usageMax = this.actor.getPouvoirUsageMax(this.object)
|
|
||||||
if (this.object.system.pointsusagecourant == -1) {
|
|
||||||
this.object.system.pointsusagecourant = formData.usageMax
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.object.type == 'sort' ) {
|
|
||||||
formData.competencesMagie = HeritiersUtility.getCompetencesMagie()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//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 = foundry.utils.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 = foundry.utils.duplicate(this.object.system.specialites)
|
|
||||||
spec.push( { name: "Nouvelle Spécialité", id: foundry.utils.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 = foundry.utils.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 = foundry.utils.duplicate(this.object.system.specialites)
|
|
||||||
spec[index].name = ev.currentTarget.value
|
|
||||||
spec[index].id = spec[index].id || foundry.utils.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 = foundry.utils.duplicate(this.object.system.specialites)
|
|
||||||
spec[index].description = ev.currentTarget.value
|
|
||||||
spec[index].id = spec[index].id || foundry.utils.randomID(16)
|
|
||||||
this.object.update( { 'system.specialites': spec })
|
|
||||||
})
|
|
||||||
|
|
||||||
html.find('#add-automation').click(ev => {
|
|
||||||
let autom = foundry.utils.duplicate(this.object.system.automations)
|
|
||||||
autom.push( { eventtype: "on-drop", name: "Automatisation 1", competence: "", minLevel: 0, id: foundry.utils.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 = foundry.utils.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 = foundry.utils.duplicate(this.object.system.automations)
|
|
||||||
auto[index][field] = ev.currentTarget.value
|
|
||||||
auto[index].id = auto[index].id || foundry.utils.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,32 @@ export class HeritiersItem extends Item {
|
|||||||
if (!data.img) {
|
if (!data.img) {
|
||||||
data.img = defaultItemImg[data.type];
|
data.img = defaultItemImg[data.type];
|
||||||
}
|
}
|
||||||
|
// Coerce legacy string numeric fields before DataModel validation
|
||||||
|
if (data.system) {
|
||||||
|
for (const key of ["quantite", "rarete", "prix", "degats", "precision",
|
||||||
|
"magasin", "charge", "zone", "points", "malusagilite", "lieu"]) {
|
||||||
|
if (typeof data.system[key] === "string") {
|
||||||
|
const v = parseInt(data.system[key])
|
||||||
|
data.system[key] = Number.isNaN(v) ? 0 : v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
super(data, context);
|
super(data, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Coerce legacy string values for NumberFields in system data (migration from pre-DataModel era)
|
||||||
|
static migrateData(data) {
|
||||||
|
if (data.system) {
|
||||||
|
const numericFields = ["quantite", "rarete", "prix", "degats", "precision",
|
||||||
|
"magasin", "charge", "zone", "points", "malusagilite", "lieu"]
|
||||||
|
for (const key of numericFields) {
|
||||||
|
if (typeof data.system[key] === "string") {
|
||||||
|
const v = parseInt(data.system[key])
|
||||||
|
data.system[key] = Number.isNaN(v) ? 0 : v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.migrateData(data)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { HeritiersActorPNJSheet } from "./heritiers-actor-pnj-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 */
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -45,22 +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
|
// Create an object of bonus/malus from -6 to +6 signed
|
||||||
HERITIERS_CONFIG.bonusMalus = Array.from({ length: 7 }, (v, k) => toString(k - 6))
|
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
|
||||||
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
|
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
|
||||||
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", HeritiersActorSheet, { types: ["personnage"], makeDefault: true })
|
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", sheets.HeritiersPersonnageSheet, { types: ["personnage"], makeDefault: true })
|
||||||
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", HeritiersActorPNJSheet, { types: ["pnj"], makeDefault: true })
|
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", sheets.HeritiersPnjSheet, { types: ["pnj"], makeDefault: true })
|
||||||
|
|
||||||
|
// Register AppV2 Item Sheets
|
||||||
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
|
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
|
||||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", HeritiersItemSheet, { makeDefault: true })
|
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()
|
||||||
|
|
||||||
@@ -71,12 +114,38 @@ 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>
|
||||||
|
`
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
|
||||||
|
|
||||||
export class HeritiersRollDialog extends Dialog {
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
static async create(actor, rollData) {
|
|
||||||
|
|
||||||
let options = { classes: ["HeritiersDialog"], width: 420, height: 'fit-content', 'z-index': 99999 };
|
|
||||||
let html = await foundry.applications.handlebars.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 buttons = {
|
|
||||||
rolld8: {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer 1d8",
|
|
||||||
callback: () => { this.roll("d8") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let enableD10 = false
|
|
||||||
let enableD12 = false
|
|
||||||
if (rollData.mode == "pouvoir" || rollData.competence?.system.niveau > 0) {
|
|
||||||
enableD10 = true
|
|
||||||
}
|
|
||||||
if (rollData.mode == "pouvoir" || rollData.competence?.system.niveau > 1) {
|
|
||||||
enableD12 = true
|
|
||||||
}
|
|
||||||
if (enableD10) {
|
|
||||||
buttons.rolld10 = {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer 1d10",
|
|
||||||
callback: () => { this.roll("d10") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (enableD12) {
|
|
||||||
buttons.rolld12 = {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer 1d12",
|
|
||||||
callback: () => { this.roll("d12") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rollData.tricherie) {
|
|
||||||
buttons["rollTricherie"] = {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer avec 1 Point de Tricherie",
|
|
||||||
callback: () => { this.roll("tricherie") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rollData.heritage) {
|
|
||||||
buttons["rollHeritage"] = {
|
|
||||||
icon: '<i class="fas fa-check"></i>',
|
|
||||||
label: "Lancer avec 1 Point d'Héritage",
|
|
||||||
callback: () => { this.roll("heritage") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buttons["Cancel"] = {
|
|
||||||
icon: '<i class="fas fa-times"></i>',
|
|
||||||
label: "Annuler",
|
|
||||||
callback: () => { this.close() }
|
|
||||||
}
|
|
||||||
let conf = {
|
|
||||||
title: "Test de Capacité",
|
|
||||||
content: html,
|
|
||||||
buttons: buttons,
|
|
||||||
close: close
|
|
||||||
}
|
|
||||||
// Overwrite in case of carac only -> 1d8
|
|
||||||
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) {
|
|
||||||
if (dice == "heritage") {
|
|
||||||
this.rollData.useHeritage = true
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (dice == "tricherie") {
|
|
||||||
this.rollData.useTricherie = true
|
|
||||||
} else {
|
|
||||||
this.rollData.mainDice = dice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HeritiersUtility.rollHeritiers(this.rollData)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
|
|
||||||
function onLoad() {
|
|
||||||
}
|
|
||||||
$(function () { onLoad(); });
|
|
||||||
|
|
||||||
html.find('#sdValue').change(async (event) => {
|
|
||||||
this.rollData.sdValue = Number(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#caracKey').change(async (event) => {
|
|
||||||
//console.log("caracKey", event.currentTarget.value)
|
|
||||||
this.rollData.caracKey = String(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#bonus-malus-context').change((event) => {
|
|
||||||
this.rollData.bonusMalusContext = Number(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#bonus-attaque-plusieurs').change((event) => {
|
|
||||||
this.rollData.bonusAttaquePlusieurs = Number(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#useSpecialite').change((event) => {
|
|
||||||
this.rollData.useSpecialite = event.currentTarget.checked
|
|
||||||
})
|
|
||||||
html.find('#pouvoirPointsUsage').change((event) => {
|
|
||||||
this.rollData.pouvoirPointsUsage = Number(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#attaqueDos').change((event) => {
|
|
||||||
this.rollData.attaqueDos = event.currentTarget.checked
|
|
||||||
})
|
|
||||||
html.find('#bonus-attaque-seconde-arme').change((event) => {
|
|
||||||
this.rollData.secondeArme = String(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
html.find('#attaque-cible').change((event) => {
|
|
||||||
this.rollData.attaqueCible = String(event.currentTarget.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -76,6 +76,10 @@ 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);
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,11 +200,12 @@ 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-utile-skills.html'
|
'systems/fvtt-les-heritiers/templates/partial-utile-skills.hbs',
|
||||||
|
'systems/fvtt-les-heritiers/templates/partial-actor-equipment.hbs'
|
||||||
]
|
]
|
||||||
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
||||||
}
|
}
|
||||||
@@ -478,7 +483,7 @@ export class HeritiersUtility {
|
|||||||
this.computeArmeDegats(rollData, actor)
|
this.computeArmeDegats(rollData, actor)
|
||||||
}
|
}
|
||||||
this.createChatWithRollMode(rollData.alias, {
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-cc-result.html`, rollData)
|
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-cc-result.hbs`, rollData)
|
||||||
}, rollData, "selfroll")
|
}, rollData, "selfroll")
|
||||||
}
|
}
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -490,7 +495,7 @@ export class HeritiersUtility {
|
|||||||
this.computeMarge(rollData, valeurDefense)
|
this.computeMarge(rollData, valeurDefense)
|
||||||
rollData.dureeAssommer = (rollData.marge) ? rollData.marge * 2 : 1
|
rollData.dureeAssommer = (rollData.marge) ? rollData.marge * 2 : 1
|
||||||
this.createChatWithRollMode(rollData.alias, {
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-assommer-result.html`, rollData)
|
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-assommer-result.hbs`, rollData)
|
||||||
}, rollData, "selfroll")
|
}, rollData, "selfroll")
|
||||||
}
|
}
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -570,6 +575,10 @@ export class HeritiersUtility {
|
|||||||
console.log(">>>> ", myRoll)
|
console.log(">>>> ", myRoll)
|
||||||
this.computeResult(actor, rollData)
|
this.computeResult(actor, rollData)
|
||||||
this.computeMarge(rollData, rollData.sdValue) // Calcul de la marge si seuil présent
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rollData.mode == "init") {
|
if (rollData.mode == "init") {
|
||||||
@@ -583,7 +592,7 @@ export class HeritiersUtility {
|
|||||||
// Gestion sort et points d'âme
|
// Gestion sort et points d'âme
|
||||||
if (rollData.mode == "sort") {
|
if (rollData.mode == "sort") {
|
||||||
if (rollData.spendEsprit) {
|
if (rollData.spendEsprit) {
|
||||||
actor.inDecCarac("esp", -1)
|
actor.inDecCarac("esp", -rollData.totalEsprit)
|
||||||
} else {
|
} else {
|
||||||
actor.incDecPointsAme(-rollData.sortPointsAme)
|
actor.incDecPointsAme(-rollData.sortPointsAme)
|
||||||
if (rollData.sort.system.competence == "Magie du Clan") {
|
if (rollData.sort.system.competence == "Magie du Clan") {
|
||||||
@@ -593,7 +602,7 @@ export class HeritiersUtility {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.createChatWithRollMode(rollData.alias, {
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
content: await foundry.applications.handlebars.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
|
// Gestion attaque standard
|
||||||
@@ -631,7 +640,7 @@ export class HeritiersUtility {
|
|||||||
this.computeResult(rollData)
|
this.computeResult(rollData)
|
||||||
|
|
||||||
this.createChatWithRollMode(rollData.alias, {
|
this.createChatWithRollMode(rollData.alias, {
|
||||||
content: await foundry.applications.handlebars.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)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -844,7 +853,8 @@ 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 itemId = li.dataset ? li.dataset.itemId : li.data("item-id");
|
||||||
let msgTxt = "<p>Certain de supprimer cet item ?";
|
let msgTxt = "<p>Certain de supprimer cet item ?";
|
||||||
let buttons = {
|
let buttons = {
|
||||||
delete: {
|
delete: {
|
||||||
@@ -852,7 +862,11 @@ export class HeritiersUtility {
|
|||||||
label: "Oui !",
|
label: "Oui !",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
||||||
|
if (li.slideUp) {
|
||||||
li.slideUp(200, () => actorSheet.render(false));
|
li.slideUp(200, () => actorSheet.render(false));
|
||||||
|
} else {
|
||||||
|
actorSheet.render(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
|
|||||||
26
modules/models/accessoire.mjs
Normal file
26
modules/models/accessoire.mjs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* 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 })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static migrateData(data) {
|
||||||
|
for (const key of ["rarete", "quantite", "prix", "lieu"]) {
|
||||||
|
if (typeof data[key] === "string") {
|
||||||
|
const v = parseInt(data[key]);
|
||||||
|
data[key] = Number.isNaN(v) ? 0 : v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
modules/models/arme.mjs
Normal file
40
modules/models/arme.mjs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static migrateData(data) {
|
||||||
|
for (const key of ["rarete", "quantite", "prix", "degats", "precision", "magasin", "charge", "zone"]) {
|
||||||
|
if (typeof data[key] === "string") {
|
||||||
|
const v = parseInt(data[key]);
|
||||||
|
data[key] = Number.isNaN(v) ? 0 : v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
modules/models/atoutfeerique.mjs
Normal file
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
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
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
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
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
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
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: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
25
modules/models/equipement.mjs
Normal file
25
modules/models/equipement.mjs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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 })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static migrateData(data) {
|
||||||
|
for (const key of ["rarete", "quantite", "prix"]) {
|
||||||
|
if (typeof data[key] === "string") {
|
||||||
|
const v = parseInt(data[key]);
|
||||||
|
data[key] = Number.isNaN(v) ? 0 : v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
modules/models/fee.mjs
Normal file
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
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
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
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
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
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: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
30
modules/models/protection.mjs
Normal file
30
modules/models/protection.mjs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 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: "" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static migrateData(data) {
|
||||||
|
for (const key of ["rarete", "quantite", "prix", "points", "malusagilite"]) {
|
||||||
|
if (typeof data[key] === "string") {
|
||||||
|
const v = parseInt(data[key]);
|
||||||
|
data[key] = Number.isNaN(v) ? 0 : v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
modules/models/sort.mjs
Normal file
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
4971
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
package.json
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
MANIFEST-000240
|
MANIFEST-000349
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
2025/09/13-00:19:21.334396 7fa5077fe6c0 Recovering log #238
|
2026/04/01-22:18:07.499964 7f303ffff6c0 Recovering log #346
|
||||||
2025/09/13-00:19:21.344951 7fa5077fe6c0 Delete type=3 #236
|
2026/04/01-22:18:07.553355 7f303ffff6c0 Delete type=3 #344
|
||||||
2025/09/13-00:19:21.345013 7fa5077fe6c0 Delete type=0 #238
|
2026/04/01-22:18:07.553447 7f303ffff6c0 Delete type=0 #346
|
||||||
2025/09/13-00:23:49.906242 7fa506bff6c0 Level-0 table #243: started
|
|
||||||
2025/09/13-00:23:49.906282 7fa506bff6c0 Level-0 table #243: 0 bytes OK
|
|
||||||
2025/09/13-00:23:49.919621 7fa506bff6c0 Delete type=0 #241
|
|
||||||
2025/09/13-00:23:49.943471 7fa506bff6c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
|
||||||
2025/09/13-00:23:49.943533 7fa506bff6c0 Manual compaction at level-1 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
2025/09/12-22:42:55.465809 7fa507fff6c0 Recovering log #234
|
2026/04/01-22:04:02.872910 7f30557ee6c0 Delete type=3 #1
|
||||||
2025/09/12-22:42:55.475838 7fa507fff6c0 Delete type=3 #232
|
2026/04/01-22:18:01.730160 7f303effd6c0 Level-0 table #347: started
|
||||||
2025/09/12-22:42:55.475940 7fa507fff6c0 Delete type=0 #234
|
2026/04/01-22:18:01.730193 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||||
2025/09/13-00:19:16.157291 7fa506bff6c0 Level-0 table #239: started
|
2026/04/01-22:18:01.758466 7f303effd6c0 Delete type=0 #345
|
||||||
2025/09/13-00:19:16.157318 7fa506bff6c0 Level-0 table #239: 0 bytes OK
|
2026/04/01-22:18:01.862827 7f303effd6c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at '!items!y1yOenfAJTsb3r6e' @ 93 : 1
|
||||||
2025/09/13-00:19:16.169152 7fa506bff6c0 Delete type=0 #237
|
2026/04/01-22:18:01.862841 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||||
2025/09/13-00:19:16.189346 7fa506bff6c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:01.881687 7f303effd6c0 Generated table #348@0: 31 keys, 76976 bytes
|
||||||
2025/09/13-00:19:16.189422 7fa506bff6c0 Manual compaction at level-1 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:01.881720 7f303effd6c0 Compacted 1@0 + 0@1 files => 76976 bytes
|
||||||
|
2026/04/01-22:18:01.902022 7f303effd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
||||||
|
2026/04/01-22:18:01.902219 7f303effd6c0 Delete type=2 #179
|
||||||
|
2026/04/01-22:18:02.006270 7f303effd6c0 Manual compaction at level-0 from '!items!y1yOenfAJTsb3r6e' @ 93 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
BIN
packs/archetypes-fees/MANIFEST-000349
Normal file
BIN
packs/archetypes-fees/MANIFEST-000349
Normal file
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000240
|
MANIFEST-000349
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
2025/09/13-00:19:21.364108 7fa50cbf96c0 Recovering log #238
|
2026/04/01-22:18:07.610829 7f303ffff6c0 Recovering log #346
|
||||||
2025/09/13-00:19:21.373869 7fa50cbf96c0 Delete type=3 #236
|
2026/04/01-22:18:07.668851 7f303ffff6c0 Delete type=3 #344
|
||||||
2025/09/13-00:19:21.373934 7fa50cbf96c0 Delete type=0 #238
|
2026/04/01-22:18:07.668940 7f303ffff6c0 Delete type=0 #346
|
||||||
2025/09/13-00:23:49.943627 7fa506bff6c0 Level-0 table #243: started
|
|
||||||
2025/09/13-00:23:49.943672 7fa506bff6c0 Level-0 table #243: 0 bytes OK
|
|
||||||
2025/09/13-00:23:49.953247 7fa506bff6c0 Delete type=0 #241
|
|
||||||
2025/09/13-00:23:49.997730 7fa506bff6c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
|
||||||
2025/09/13-00:23:49.997793 7fa506bff6c0 Manual compaction at level-1 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
2025/09/12-22:42:55.493084 7fa50d3fa6c0 Recovering log #234
|
2026/04/01-22:04:02.918161 7f30557ee6c0 Delete type=3 #1
|
||||||
2025/09/12-22:42:55.503204 7fa50d3fa6c0 Delete type=3 #232
|
2026/04/01-22:18:02.037942 7f303effd6c0 Level-0 table #347: started
|
||||||
2025/09/12-22:42:55.503264 7fa50d3fa6c0 Delete type=0 #234
|
2026/04/01-22:18:02.037978 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||||
2025/09/13-00:19:16.181338 7fa506bff6c0 Level-0 table #239: started
|
2026/04/01-22:18:02.069106 7f303effd6c0 Delete type=0 #345
|
||||||
2025/09/13-00:19:16.181386 7fa506bff6c0 Level-0 table #239: 0 bytes OK
|
2026/04/01-22:18:02.131787 7f303effd6c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at '!items!zbsVCsWxRzkzzG1N' @ 216 : 1
|
||||||
2025/09/13-00:19:16.189077 7fa506bff6c0 Delete type=0 #237
|
2026/04/01-22:18:02.131798 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||||
2025/09/13-00:19:16.189385 7fa506bff6c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:02.144068 7f303effd6c0 Generated table #348@0: 72 keys, 18161 bytes
|
||||||
2025/09/13-00:19:16.189457 7fa506bff6c0 Manual compaction at level-1 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:02.144102 7f303effd6c0 Compacted 1@0 + 0@1 files => 18161 bytes
|
||||||
|
2026/04/01-22:18:02.168550 7f303effd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
||||||
|
2026/04/01-22:18:02.168665 7f303effd6c0 Delete type=2 #179
|
||||||
|
2026/04/01-22:18:02.307926 7f303effd6c0 Manual compaction at level-0 from '!items!zbsVCsWxRzkzzG1N' @ 216 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
BIN
packs/armes-et-protection/MANIFEST-000349
Normal file
BIN
packs/armes-et-protection/MANIFEST-000349
Normal file
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000240
|
MANIFEST-000349
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
2025/09/13-00:19:21.308006 7fa50d3fa6c0 Recovering log #238
|
2026/04/01-22:18:07.389011 7f303ffff6c0 Recovering log #346
|
||||||
2025/09/13-00:19:21.319049 7fa50d3fa6c0 Delete type=3 #236
|
2026/04/01-22:18:07.442566 7f303ffff6c0 Delete type=3 #344
|
||||||
2025/09/13-00:19:21.319109 7fa50d3fa6c0 Delete type=0 #238
|
2026/04/01-22:18:07.442655 7f303ffff6c0 Delete type=0 #346
|
||||||
2025/09/13-00:23:49.894650 7fa506bff6c0 Level-0 table #243: started
|
|
||||||
2025/09/13-00:23:49.894688 7fa506bff6c0 Level-0 table #243: 0 bytes OK
|
|
||||||
2025/09/13-00:23:49.906014 7fa506bff6c0 Delete type=0 #241
|
|
||||||
2025/09/13-00:23:49.943452 7fa506bff6c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
|
||||||
2025/09/13-00:23:49.943519 7fa506bff6c0 Manual compaction at level-1 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
2025/09/12-22:42:55.438389 7fa5077fe6c0 Recovering log #234
|
2026/04/01-22:04:02.828271 7f3054fed6c0 Delete type=3 #1
|
||||||
2025/09/12-22:42:55.449024 7fa5077fe6c0 Delete type=3 #232
|
2026/04/01-22:18:01.697526 7f303effd6c0 Level-0 table #347: started
|
||||||
2025/09/12-22:42:55.449158 7fa5077fe6c0 Delete type=0 #234
|
2026/04/01-22:18:01.697587 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||||
2025/09/13-00:19:16.107423 7fa506bff6c0 Level-0 table #239: started
|
2026/04/01-22:18:01.729927 7f303effd6c0 Delete type=0 #345
|
||||||
2025/09/13-00:19:16.107448 7fa506bff6c0 Level-0 table #239: 0 bytes OK
|
2026/04/01-22:18:01.823265 7f303effd6c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at '!items!zvtBlG6KCIn0oCVk' @ 459 : 1
|
||||||
2025/09/13-00:19:16.119476 7fa506bff6c0 Delete type=0 #237
|
2026/04/01-22:18:01.823278 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||||
2025/09/13-00:19:16.144823 7fa506bff6c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:01.833392 7f303effd6c0 Generated table #348@0: 153 keys, 61883 bytes
|
||||||
2025/09/13-00:19:16.144857 7fa506bff6c0 Manual compaction at level-1 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:01.833424 7f303effd6c0 Compacted 1@0 + 0@1 files => 61883 bytes
|
||||||
|
2026/04/01-22:18:01.862535 7f303effd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
||||||
|
2026/04/01-22:18:01.862662 7f303effd6c0 Delete type=2 #179
|
||||||
|
2026/04/01-22:18:02.006254 7f303effd6c0 Manual compaction at level-0 from '!items!zvtBlG6KCIn0oCVk' @ 459 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
BIN
packs/atouts-feeriques/MANIFEST-000349
Normal file
BIN
packs/atouts-feeriques/MANIFEST-000349
Normal file
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000240
|
MANIFEST-000349
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
2025/09/13-00:19:21.268491 7fa5077fe6c0 Recovering log #238
|
2026/04/01-22:18:07.221290 7f30557ee6c0 Recovering log #346
|
||||||
2025/09/13-00:19:21.279675 7fa5077fe6c0 Delete type=3 #236
|
2026/04/01-22:18:07.279192 7f30557ee6c0 Delete type=3 #344
|
||||||
2025/09/13-00:19:21.279726 7fa5077fe6c0 Delete type=0 #238
|
2026/04/01-22:18:07.279260 7f30557ee6c0 Delete type=0 #346
|
||||||
2025/09/13-00:23:49.863626 7fa506bff6c0 Level-0 table #243: started
|
|
||||||
2025/09/13-00:23:49.863658 7fa506bff6c0 Level-0 table #243: 0 bytes OK
|
|
||||||
2025/09/13-00:23:49.873805 7fa506bff6c0 Delete type=0 #241
|
|
||||||
2025/09/13-00:23:49.894434 7fa506bff6c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
|
||||||
2025/09/13-00:23:49.894484 7fa506bff6c0 Manual compaction at level-1 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
2025/09/12-22:42:55.400653 7fa50cbf96c0 Recovering log #234
|
2026/04/01-22:04:02.761288 7f3054fed6c0 Delete type=3 #1
|
||||||
2025/09/12-22:42:55.410659 7fa50cbf96c0 Delete type=3 #232
|
2026/04/01-22:18:01.450482 7f303effd6c0 Level-0 table #347: started
|
||||||
2025/09/12-22:42:55.410812 7fa50cbf96c0 Delete type=0 #234
|
2026/04/01-22:18:01.450548 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||||
2025/09/13-00:19:16.132163 7fa506bff6c0 Level-0 table #239: started
|
2026/04/01-22:18:01.481608 7f303effd6c0 Delete type=0 #345
|
||||||
2025/09/13-00:19:16.132192 7fa506bff6c0 Level-0 table #239: 0 bytes OK
|
2026/04/01-22:18:01.561277 7f303effd6c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at '!items!zfpjROW9LDAlXUkN' @ 189 : 1
|
||||||
2025/09/13-00:19:16.144694 7fa506bff6c0 Delete type=0 #237
|
2026/04/01-22:18:01.561290 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||||
2025/09/13-00:19:16.144842 7fa506bff6c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:01.574187 7f303effd6c0 Generated table #348@0: 63 keys, 27421 bytes
|
||||||
2025/09/13-00:19:16.144872 7fa506bff6c0 Manual compaction at level-1 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:01.574228 7f303effd6c0 Compacted 1@0 + 0@1 files => 27421 bytes
|
||||||
|
2026/04/01-22:18:01.591168 7f303effd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
||||||
|
2026/04/01-22:18:01.591346 7f303effd6c0 Delete type=2 #179
|
||||||
|
2026/04/01-22:18:01.697394 7f303effd6c0 Manual compaction at level-0 from '!items!zfpjROW9LDAlXUkN' @ 189 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
BIN
packs/avantages/MANIFEST-000349
Normal file
BIN
packs/avantages/MANIFEST-000349
Normal file
Binary file not shown.
@@ -1 +1 @@
|
|||||||
MANIFEST-000240
|
MANIFEST-000349
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
2025/09/13-00:19:21.296168 7fa507fff6c0 Recovering log #238
|
2026/04/01-22:18:07.332490 7f303f7fe6c0 Recovering log #346
|
||||||
2025/09/13-00:19:21.305712 7fa507fff6c0 Delete type=3 #236
|
2026/04/01-22:18:07.386293 7f303f7fe6c0 Delete type=3 #344
|
||||||
2025/09/13-00:19:21.305760 7fa507fff6c0 Delete type=0 #238
|
2026/04/01-22:18:07.386367 7f303f7fe6c0 Delete type=0 #346
|
||||||
2025/09/13-00:23:49.884597 7fa506bff6c0 Level-0 table #243: started
|
|
||||||
2025/09/13-00:23:49.884644 7fa506bff6c0 Level-0 table #243: 0 bytes OK
|
|
||||||
2025/09/13-00:23:49.894226 7fa506bff6c0 Delete type=0 #241
|
|
||||||
2025/09/13-00:23:49.894466 7fa506bff6c0 Manual compaction at level-0 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)
|
|
||||||
2025/09/13-00:23:49.894528 7fa506bff6c0 Manual compaction at level-1 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
2025/09/12-22:42:55.426015 7fa507fff6c0 Recovering log #234
|
2026/04/01-22:04:02.807869 7f3054fed6c0 Delete type=3 #1
|
||||||
2025/09/12-22:42:55.435789 7fa507fff6c0 Delete type=3 #232
|
2026/04/01-22:18:01.532850 7f303effd6c0 Level-0 table #347: started
|
||||||
2025/09/12-22:42:55.435841 7fa507fff6c0 Delete type=0 #234
|
2026/04/01-22:18:01.532885 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||||
2025/09/13-00:19:16.144975 7fa506bff6c0 Level-0 table #239: started
|
2026/04/01-22:18:01.561124 7f303effd6c0 Delete type=0 #345
|
||||||
2025/09/13-00:19:16.145001 7fa506bff6c0 Level-0 table #239: 0 bytes OK
|
2026/04/01-22:18:01.659218 7f303effd6c0 Manual compaction at level-0 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at '!items!yWDg2KlXEz33TSmZ' @ 108 : 1
|
||||||
2025/09/13-00:19:16.157163 7fa506bff6c0 Delete type=0 #237
|
2026/04/01-22:18:01.659229 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||||
2025/09/13-00:19:16.189315 7fa506bff6c0 Manual compaction at level-0 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:01.673995 7f303effd6c0 Generated table #348@0: 36 keys, 24174 bytes
|
||||||
2025/09/13-00:19:16.189405 7fa506bff6c0 Manual compaction at level-1 from '!items!0cNSRJVPk3GbvxfD' @ 72057594037927935 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)
|
2026/04/01-22:18:01.674032 7f303effd6c0 Compacted 1@0 + 0@1 files => 24174 bytes
|
||||||
|
2026/04/01-22:18:01.697115 7f303effd6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
|
||||||
|
2026/04/01-22:18:01.697239 7f303effd6c0 Delete type=2 #179
|
||||||
|
2026/04/01-22:18:01.697439 7f303effd6c0 Manual compaction at level-0 from '!items!yWDg2KlXEz33TSmZ' @ 108 : 1 .. '!items!yWDg2KlXEz33TSmZ' @ 0 : 0; will stop at (end)
|
||||||
|
|||||||
Binary file not shown.
BIN
packs/capacites/MANIFEST-000349
Normal file
BIN
packs/capacites/MANIFEST-000349
Normal file
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user