Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e573219fd | |||
| 9fb11bc587 | |||
| a11d9be830 | |||
| 038aa37838 | |||
| 25b6e30aa8 | |||
| f445741eda | |||
| d3c24e44d8 | |||
| d2cf0d80b1 | |||
| 47350cb2f1 | |||
| 26e46cf10a | |||
| 4722fdf4d6 | |||
| dc3040df26 | |||
| b1bce86604 | |||
| 6066091d8d | |||
| 287b6d83a7 | |||
| d8efba89a1 | |||
| 936d525503 | |||
| b113f630bf | |||
| 939cfb1e86 | |||
| 5f5e0e2a8c | |||
| c993a9a5b1 | |||
| cc7de0e53c | |||
| 4d41986c12 | |||
| d04731f475 | |||
| 44a641a0ca | |||
| 1ad022d193 | |||
| 1c7cf343b1 | |||
| d4b00e3508 | |||
| adc912e6cd | |||
| 51a457ebf6 | |||
| 2e9c558027 | |||
| bcd0758328 | |||
| 2b680a203f | |||
| e3d7874dce | |||
| ab6a5832c0 | |||
| d83a999974 | |||
| b83890a764 | |||
| 5ad3c165e5 | |||
| 2b3e774cbb | |||
| 96f8d2bceb |
@@ -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 +1,2 @@
|
||||
.history/
|
||||
node_modules
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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+)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
+35
@@ -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;
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
"TYPES": {
|
||||
"Actor": {
|
||||
"personnage": "Personnage",
|
||||
"PNJ": "PNJ"
|
||||
"pnj": "PNJ"
|
||||
},
|
||||
"Item": {
|
||||
"accessoire": "Accessoire",
|
||||
@@ -17,7 +17,8 @@
|
||||
"fee": "Fée",
|
||||
"pouvoir": "Pouvoir",
|
||||
"profil": "Profil",
|
||||
"protection": "Protection"
|
||||
"protection": "Protection",
|
||||
"sort": "Sort"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,725 @@
|
||||
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
|
||||
const createdItems = await this.actor.createEmbeddedDocuments("Item", [itemData])
|
||||
|
||||
for (const createdItem of createdItems) {
|
||||
this.#notifyDroppedItemVisibility(createdItem)
|
||||
}
|
||||
|
||||
return createdItems
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the user when a dropped magic item will not be visible in the Magie tab.
|
||||
* @param {Item} item
|
||||
*/
|
||||
#notifyDroppedItemVisibility(item) {
|
||||
if (!item) return
|
||||
|
||||
const magieActivee = this.actor.system?.biodata?.magie !== false
|
||||
if ((item.type === "sort" || this.#isMagicCompetence(item)) && !magieActivee) {
|
||||
ui.notifications.info("Élément de magie ajouté, mais l'onglet Magie reste masqué tant que « Fiche de Magie ? » n'est pas activé dans la fiche.")
|
||||
}
|
||||
|
||||
if (item.type !== "sort") return
|
||||
|
||||
const competenceName = item.system?.competence?.trim()
|
||||
if (!competenceName) {
|
||||
ui.notifications.warn("Sort ajouté, mais il n'apparaîtra pas dans l'onglet Magie tant que sa compétence de magie n'est pas renseignée.")
|
||||
return
|
||||
}
|
||||
|
||||
const competence = this.actor.items.find(actorItem =>
|
||||
actorItem.type === "competence"
|
||||
&& actorItem.system?.profil === "magie"
|
||||
&& actorItem.name === competenceName)
|
||||
|
||||
if (!competence) {
|
||||
ui.notifications.warn(`Sort ajouté, mais il n'apparaîtra pas dans l'onglet Magie tant que la compétence magique « ${competenceName} » n'existe pas sur l'acteur.`)
|
||||
return
|
||||
}
|
||||
|
||||
if (competenceName === "Magie du Clan" && !["soufflecombat", "soufflemouvement", "souffleesprit"].includes(item.system?.souffle)) {
|
||||
ui.notifications.warn("Sort ajouté, mais il n'apparaîtra pas dans l'onglet Magie tant que son souffle n'est pas renseigné dans sa fiche.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Item} item
|
||||
* @returns {boolean}
|
||||
*/
|
||||
#isMagicCompetence(item) {
|
||||
return item?.type === "competence" && item.system?.profil === "magie"
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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 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,248 +0,0 @@
|
||||
/**
|
||||
* Extend the basic ActorSheet with some very simple modifications
|
||||
* @extends {ActorSheet}
|
||||
*/
|
||||
|
||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class HeritiersActorSheet extends ActorSheet {
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
|
||||
return mergeObject(super.defaultOptions, {
|
||||
classes: ["fvtt-les-heritiers", "sheet", "actor"],
|
||||
template: "systems/fvtt-les-heritiers/templates/actor-sheet.html",
|
||||
width: 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 = 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"),
|
||||
futileSkills :this.actor.organizeFutileSkills(),
|
||||
contacts: this.actor.organizeContacts(),
|
||||
armes: duplicate(this.actor.getWeapons()),
|
||||
monnaies: duplicate(this.actor.getMonnaies()),
|
||||
pouvoirs: duplicate(this.actor.getPouvoirs()),
|
||||
fee: duplicate(this.actor.getFee() || {} ),
|
||||
protections: duplicate(this.actor.getArmors()),
|
||||
combat: this.actor.getCombatValues(),
|
||||
equipements: duplicate(this.actor.getEquipments()),
|
||||
avantages: duplicate(this.actor.getAvantages()),
|
||||
atouts: duplicate(this.actor.getAtouts()),
|
||||
capacites: duplicate(this.actor.getCapacites()),
|
||||
desavantages: duplicate(this.actor.getDesavantages()),
|
||||
profils: 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 TextEditor.enrichHTML(this.object.system.biodata.description, {async: true}),
|
||||
revesetranges: await TextEditor.enrichHTML(this.object.system.biodata.revesetranges, {async: true}),
|
||||
secretsdecouverts: await TextEditor.enrichHTML(this.object.system.biodata.secretsdecouverts, {async: true}),
|
||||
questions: await TextEditor.enrichHTML(this.object.system.biodata.questions, {async: true}),
|
||||
habitat: await TextEditor.enrichHTML(this.object.system.biodata.habitat, {async: true}),
|
||||
playernotes: await TextEditor.enrichHTML(this.object.system.biodata.playernotes, {async: true}),
|
||||
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-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")
|
||||
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)
|
||||
}*/
|
||||
|
||||
}
|
||||
+408
-66
@@ -1,6 +1,6 @@
|
||||
/* -------------------------------------------- */
|
||||
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]
|
||||
@@ -41,7 +41,7 @@ export class HeritiersActor extends Actor {
|
||||
const skills = await HeritiersUtility.loadCompendium("fvtt-les-heritiers.competences")
|
||||
data.items = []
|
||||
for (let skill of skills) {
|
||||
if (skill.system.categorie == "utile") {
|
||||
if (skill.system.categorie == "utile" && skill.system.profil != "magie") {
|
||||
data.items.push(skill.toObject())
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ export class HeritiersActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
prepareArme(arme) {
|
||||
arme = duplicate(arme)
|
||||
arme = foundry.utils.duplicate(arme)
|
||||
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
||||
return arme
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export class HeritiersActor extends Actor {
|
||||
|
||||
/* ----------------------- --------------------- */
|
||||
addMember(actorId) {
|
||||
let members = duplicate(this.system.members)
|
||||
let members = foundry.utils.duplicate(this.system.members)
|
||||
members.push({ id: actorId })
|
||||
this.update({ 'system.members': members })
|
||||
}
|
||||
@@ -138,7 +138,7 @@ export class HeritiersActor extends Actor {
|
||||
let pouvoirs = []
|
||||
for (let item of this.items) {
|
||||
if (item.type == "pouvoir") {
|
||||
let itemObj = duplicate(item)
|
||||
let itemObj = foundry.utils.duplicate(item)
|
||||
itemObj.maxUsage = this.getPouvoirUsageMax(item)
|
||||
pouvoirs.push(itemObj)
|
||||
}
|
||||
@@ -146,12 +146,91 @@ export class HeritiersActor extends Actor {
|
||||
HeritiersUtility.sortArrayObjectsByName(pouvoirs)
|
||||
return pouvoirs
|
||||
}
|
||||
getSorts() {
|
||||
return this.getItemSorted(["sort"])
|
||||
}
|
||||
getCompetencesMagie() {
|
||||
let comp = []
|
||||
for (let item of this.items) {
|
||||
if (item.type == "competence" && item.system.profil == "magie") {
|
||||
let itemObj = foundry.utils.duplicate(item)
|
||||
comp.push(itemObj)
|
||||
}
|
||||
}
|
||||
HeritiersUtility.sortArrayObjectsByName(comp)
|
||||
return comp
|
||||
}
|
||||
|
||||
prepareMagie() {
|
||||
let magieList = []
|
||||
for (let item of this.items) {
|
||||
if (item.type == "competence" && item.system.profil == "magie") {
|
||||
let magie = {}
|
||||
magie.name = item.name
|
||||
magie.competence = foundry.utils.duplicate(item)
|
||||
magie.rang = Math.round(item.system.niveau / 2);
|
||||
magie.rangGenericName = game.system.lesheritiers.config.rangName[magie.rang];
|
||||
console.log("Magie", item.name, item.system.niveau, magie.rang, 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 = {
|
||||
1: { nomNiveau: magie.competence.system.nomniveau["1"], sorts: [] },
|
||||
2: { nomNiveau: magie.competence.system.nomniveau["2"], sorts: [] },
|
||||
3: { nomNiveau: magie.competence.system.nomniveau["3"], sorts: [] },
|
||||
4: { nomNiveau: magie.competence.system.nomniveau["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))
|
||||
magie.sorts[Number(sort.system.niveau)].sorts.push(sortObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
magieList.push(magie)
|
||||
}
|
||||
}
|
||||
return magieList
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getSkills() {
|
||||
let comp = []
|
||||
for (let item of this.items) {
|
||||
item = duplicate(item)
|
||||
item = foundry.utils.duplicate(item)
|
||||
if (item.type == "competence") {
|
||||
comp.push(item)
|
||||
}
|
||||
@@ -172,11 +251,11 @@ export class HeritiersActor extends Actor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
organizeUtileSkills(kind = "mental") {
|
||||
organizeMagicSkills() {
|
||||
let comp = {}
|
||||
for (let key in game.system.lesheritiers.config.competenceProfil) {
|
||||
if (game.system.lesheritiers.config.competenceProfil[key].kind == kind)
|
||||
comp[key] = { skills: [], niveau: this.system.competences[key].niveau }
|
||||
if (game.system.lesheritiers.config.competenceProfil[key].kind == "magical")
|
||||
comp[key] = { skills: [], niveau: 0 }
|
||||
}
|
||||
for (let item of this.items) {
|
||||
if (item.type == "competence") {
|
||||
@@ -192,6 +271,29 @@ export class HeritiersActor extends Actor {
|
||||
}
|
||||
return Object.fromEntries(Object.entries(comp).sort())
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
organizeUtileSkills(kind = "mental") {
|
||||
let comp = {}
|
||||
for (let key in game.system.lesheritiers.config.competenceProfil) {
|
||||
if (game.system.lesheritiers.config.competenceProfil[key].kind == kind)
|
||||
comp[key] = { skills: [], niveau: this.system.competences[key].niveau, rang: this.system.competences[key].rang }
|
||||
}
|
||||
for (let item of this.items) {
|
||||
if (item.type == "competence") {
|
||||
if (item.system.categorie == "utile" && comp[item.system.profil]) {
|
||||
this.prepareUtileSkill(item)
|
||||
comp[item.system.profil].skills.push(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let key in comp) {
|
||||
HeritiersUtility.sortArrayObjectsByName(comp[key].skills)
|
||||
}
|
||||
return Object.fromEntries(Object.entries(comp).sort())
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
organizeContacts() {
|
||||
let contactList = {}
|
||||
@@ -252,19 +354,70 @@ export class HeritiersActor extends Actor {
|
||||
return combat
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
prepareBaseData() {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async prepareData() {
|
||||
super.prepareData();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getMaxRangMagie() {
|
||||
let niv = 0
|
||||
let bestMagie
|
||||
for (let comp of this.items) {
|
||||
if (comp.type == "competence" && comp.system.profil == "magie") {
|
||||
if (comp.system.niveau > niv) {
|
||||
bestMagie = comp
|
||||
niv = comp.system.niveau
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestMagie) {
|
||||
return Math.round(bestMagie.system.niveau / 2)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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') {
|
||||
// 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()
|
||||
@@ -280,7 +433,7 @@ export class HeritiersActor extends Actor {
|
||||
getItemById(id) {
|
||||
let item = this.items.find(item => item.id == id);
|
||||
if (item) {
|
||||
item = duplicate(item)
|
||||
item = foundry.utils.duplicate(item)
|
||||
}
|
||||
return item;
|
||||
}
|
||||
@@ -302,6 +455,8 @@ export class HeritiersActor extends Actor {
|
||||
if (dataType) {
|
||||
if (dataType.toLowerCase() == "number") {
|
||||
value = Number(value)
|
||||
} else if (dataType.toLowerCase() == "boolean") {
|
||||
value = Boolean(value)
|
||||
} else {
|
||||
value = String(value)
|
||||
}
|
||||
@@ -338,7 +493,7 @@ export class HeritiersActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getCarac(attrKey) {
|
||||
return duplicate(this.system.caracteristiques)
|
||||
return foundry.utils.duplicate(this.system.caracteristiques)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -359,13 +514,13 @@ export class HeritiersActor extends Actor {
|
||||
getSubActors() {
|
||||
let subActors = [];
|
||||
for (let id of this.system.subactors) {
|
||||
subActors.push(duplicate(game.actors.get(id)));
|
||||
subActors.push(foundry.utils.duplicate(game.actors.get(id)));
|
||||
}
|
||||
return subActors;
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
async addSubActor(subActorId) {
|
||||
let subActors = duplicate(this.system.subactors);
|
||||
let subActors = foundry.utils.duplicate(this.system.subactors);
|
||||
subActors.push(subActorId);
|
||||
await this.update({ 'system.subactors': subActors });
|
||||
}
|
||||
@@ -387,11 +542,12 @@ export class HeritiersActor extends Actor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async incDecAdversite(adv, incDec = 0) {
|
||||
let adversite = duplicate(this.system.adversite)
|
||||
let adversite = foundry.utils.duplicate(this.system.adversite)
|
||||
adversite[adv] += Number(incDec)
|
||||
adversite[adv] = Math.max(adversite[adv], 0)
|
||||
this.update({ 'system.adversite': adversite })
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async incDecQuantity(objetId, incDec = 0) {
|
||||
let objetQ = this.items.get(objetId)
|
||||
@@ -434,7 +590,7 @@ export class HeritiersActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
async setPredilectionUsed(compId, predIdx) {
|
||||
let comp = this.items.get(compId)
|
||||
let pred = duplicate(comp.system.predilections)
|
||||
let pred = foundry.utils.duplicate(comp.system.predilections)
|
||||
pred[predIdx].used = true
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: compId, 'system.predilections': pred }])
|
||||
}
|
||||
@@ -456,7 +612,7 @@ export class HeritiersActor extends Actor {
|
||||
}
|
||||
if (arme.system.totalDefensif > maxDef) {
|
||||
maxDef = arme.system.totalDefensif
|
||||
bestArme = duplicate(arme)
|
||||
bestArme = foundry.utils.duplicate(arme)
|
||||
}
|
||||
}
|
||||
return bestArme
|
||||
@@ -471,7 +627,7 @@ export class HeritiersActor extends Actor {
|
||||
for (let auto of talent.system.automations) {
|
||||
if (auto.eventtype === "prepare-roll") {
|
||||
if (auto.competence.toLowerCase() == competence.name.toLowerCase()) {
|
||||
talent = duplicate(talent)
|
||||
talent = foundry.utils.duplicate(talent)
|
||||
talent.system.bonus = auto.bonus
|
||||
talent.system.baCost = auto.baCost
|
||||
talents.push(talent)
|
||||
@@ -520,6 +676,14 @@ export class HeritiersActor extends Actor {
|
||||
this.update({ 'system.rang.tricherie': tricherie })
|
||||
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) {
|
||||
let comp1 = this.items.find(it => it.name == compName1)
|
||||
@@ -533,6 +697,7 @@ export class HeritiersActor extends Actor {
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getCommonRollData(compId = undefined, compName = undefined) {
|
||||
let rollData = HeritiersUtility.getBasicRollData()
|
||||
@@ -552,12 +717,12 @@ export class HeritiersActor extends Actor {
|
||||
rollData.rulesMalus.push(this.getPvMalus())
|
||||
|
||||
if (compId) {
|
||||
rollData.competence = duplicate(this.items.get(compId) || {})
|
||||
rollData.competence = foundry.utils.duplicate(this.items.get(compId) || {})
|
||||
this.prepareUtileSkill(rollData.competence)
|
||||
rollData.actionImg = rollData.competence?.img
|
||||
}
|
||||
if (compName) {
|
||||
rollData.competence = duplicate(this.items.find(item => item.name.toLowerCase() == compName.toLowerCase()) || {})
|
||||
rollData.competence = foundry.utils.duplicate(this.items.find(item => item.name.toLowerCase() == compName.toLowerCase()) || {})
|
||||
if (rollData.competence?.name) {
|
||||
this.prepareUtileSkill(rollData.competence)
|
||||
rollData.actionImg = rollData.competence?.img
|
||||
@@ -579,8 +744,7 @@ export class HeritiersActor extends Actor {
|
||||
rollData.caracKey = "per"
|
||||
}
|
||||
rollData.carac = this.system.caracteristiques[rollData.caracKey]
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -589,8 +753,7 @@ export class HeritiersActor extends Actor {
|
||||
rollData.mode = "carac"
|
||||
rollData.carac = this.system.caracteristiques[key]
|
||||
rollData.caracKey = key
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -599,8 +762,7 @@ export class HeritiersActor extends Actor {
|
||||
rollData.mode = "rang"
|
||||
rollData.rang = this.system.rang[key]
|
||||
rollData.rangKey = key
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
async rollRootCompetence(compKey) {
|
||||
@@ -608,8 +770,7 @@ export class HeritiersActor extends Actor {
|
||||
rollData.mode = "competence"
|
||||
rollData.competence = { name: this.system.competences[compKey].label, system: { niveau: this.system.competences[compKey].niveau } }
|
||||
console.log("RollDatra", rollData)
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -617,34 +778,108 @@ export class HeritiersActor extends Actor {
|
||||
let rollData = this.getCommonRollData(compId)
|
||||
rollData.mode = "competence"
|
||||
console.log("RollDatra", rollData)
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
inDecCarac(key, incDec) {
|
||||
let carac = this.system.caracteristiques[key]
|
||||
carac.value += incDec
|
||||
if (carac.value < 0 || carac.value > carac.rang) {
|
||||
ui.notifications.warn("Pas assez de points dans cette caractéristique ou rang max atteint !")
|
||||
return false
|
||||
}
|
||||
carac.value = Math.max(carac.value, 0)
|
||||
carac.value = Math.min(carac.value, carac.rang)
|
||||
this.update({ [`system.caracteristiques.${key}`]: carac })
|
||||
return true
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollSort(sortId) {
|
||||
let sort = this.items.get(sortId)
|
||||
let comp = this.items.find(it => it.type == "competence" && it.name.toLowerCase() == sort.system.competence.toLowerCase())
|
||||
if (!comp) {
|
||||
ui.notifications.warn("Compétence de magie associée non trouvée !")
|
||||
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)
|
||||
rollData.mode = "sort"
|
||||
rollData.sort = foundry.utils.duplicate(sort)
|
||||
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.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) {
|
||||
// Vérifier si au moins 1 point d'Esprit est disponible
|
||||
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: ${rollData.totalEsprit}, disponible: ${this.system.caracteristiques.esp.value})`)
|
||||
return
|
||||
} else {
|
||||
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}).`)
|
||||
ui.notifications.warn(`${rollData.totalEsprit} Points d'Esprit seront utilisés à la place si vous effectuez le lancer.`)
|
||||
}
|
||||
}
|
||||
|
||||
if (sort.system.carac2 != "none") {
|
||||
// get the best carac between carac1 and carac2
|
||||
if (this.system.caracteristiques[sort.system.carac1].value > this.system.caracteristiques[sort.system.carac2].value) {
|
||||
rollData.caracKey = sort.system.carac1
|
||||
} else {
|
||||
rollData.caracKey = sort.system.carac2
|
||||
}
|
||||
rollData.caracMessage = "Ce sort peut être lancé avec " + game.system.lesheritiers.config.caracList[sort.system.carac1] + " ou " + game.system.lesheritiers.config.caracList[sort.system.carac2] + ". La meilleure caractéristique a été selectionnée."
|
||||
} else {
|
||||
rollData.caracKey = sort.system.carac1
|
||||
}
|
||||
console.log("RollData", rollData)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollAttaqueArme(armeId) {
|
||||
let arme = this.items.get(armeId)
|
||||
if (arme) {
|
||||
arme = duplicate(arme)
|
||||
arme = foundry.utils.duplicate(arme)
|
||||
arme.system.isMelee = HeritiersUtility.isArmeMelee(arme)
|
||||
let competenceName = "Tir"
|
||||
let key = "prec"
|
||||
if (arme.system.isMelee) {
|
||||
competenceName = "Mêlée"
|
||||
key = "agi"
|
||||
let key = arme.system.isMelee ? "agi" : "prec"
|
||||
let rollData
|
||||
if (this.type == 'pnj') {
|
||||
rollData = this.getCommonRollData()
|
||||
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.caracKey = key
|
||||
rollData.arme = arme
|
||||
rollData.mode = "arme"
|
||||
rollData.attackType = "Attaque normale"
|
||||
rollData.armes = this.getOtherMeleeWeapons(arme)
|
||||
if (rollData.defenderTokenId && arme.system.isMelee) {
|
||||
rollData.cacheDifficulte = true
|
||||
}
|
||||
console.log(">>>> ARME", rollData)
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,17 +888,22 @@ export class HeritiersActor extends Actor {
|
||||
let arme = this.items.get(armeId)
|
||||
if (arme) {
|
||||
let key = "for"
|
||||
let competenceName = "Mêlée"
|
||||
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.caracKey = key
|
||||
rollData.arme = duplicate(arme)
|
||||
rollData.arme = foundry.utils.duplicate(arme)
|
||||
rollData.mode = "attaquebrutale"
|
||||
rollData.attackType = "Attaque brutale"
|
||||
rollData.armes = this.getOtherMeleeWeapons(arme)
|
||||
rollData.rulesMalus.push({ name: "Attaque brutale", value: -2 })
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
@@ -672,15 +912,21 @@ export class HeritiersActor extends Actor {
|
||||
if (arme) {
|
||||
let key = "agi"
|
||||
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 rollData = this.getCommonRollData(undefined, pireCompetence.name)
|
||||
rollData = this.getCommonRollData(undefined, pireCompetence.name)
|
||||
}
|
||||
rollData.carac = this.system.caracteristiques[key]
|
||||
rollData.caracKey = key
|
||||
rollData.arme = duplicate(arme)
|
||||
rollData.arme = foundry.utils.duplicate(arme)
|
||||
rollData.armes = this.getOtherMeleeWeapons(arme)
|
||||
rollData.mode = "attaquecharge"
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
rollData.attackType = "Attaque en charge"
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,40 +934,136 @@ export class HeritiersActor extends Actor {
|
||||
async rollAssomerArme(armeId) {
|
||||
let arme = this.items.get(armeId)
|
||||
if (arme) {
|
||||
let competenceName = "Mêlée"
|
||||
//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["agi"]
|
||||
rollData.caracKey = "agi"
|
||||
rollData.arme = duplicate(arme)
|
||||
rollData.arme = foundry.utils.duplicate(arme)
|
||||
rollData.mode = "assommer"
|
||||
rollData.attackType = "Assommer"
|
||||
if (rollData.defenderTokenId) {
|
||||
rollData.cacheDifficulte = true
|
||||
}
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
pouvoirPassifDialog(pouvoir) {
|
||||
let rollData = this.getCommonRollData()
|
||||
rollData.pouvoir = pouvoir
|
||||
rollData.mode = "pouvoirpassif"
|
||||
rollData.pouvoirPointsUsage = 0
|
||||
rollData.noRoll = true
|
||||
|
||||
let d = new Dialog({
|
||||
title: "Activer le pouvoir passif " + pouvoir.name,
|
||||
content: "<p>Choisissez le nombre de Points d'Usage</p>",
|
||||
buttons: {
|
||||
one: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "1 Point d'Usage",
|
||||
callback: () => {
|
||||
rollData.pouvoirPointsUsage = 1;
|
||||
HeritiersUtility.rollHeritiers(rollData);
|
||||
}
|
||||
},
|
||||
two: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "2 Points d'Usage",
|
||||
callback: () => {
|
||||
rollData.pouvoirPointsUsage = 2;
|
||||
HeritiersUtility.rollHeritiers(rollData);
|
||||
}
|
||||
},
|
||||
three: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "3 Points d'Usage",
|
||||
callback: () => {
|
||||
rollData.pouvoirPointsUsage = 3;
|
||||
HeritiersUtility.rollHeritiers(rollData);
|
||||
}
|
||||
},
|
||||
four: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "4 Points d'Usage",
|
||||
callback: () => {
|
||||
rollData.pouvoirPointsUsage = 4;
|
||||
HeritiersUtility.rollHeritiers(rollData);
|
||||
}
|
||||
},
|
||||
close: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: "Annuler",
|
||||
callback: () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
default: "one",
|
||||
render: html => console.log("Pouvoir passif"),
|
||||
close: html => console.log("No option")
|
||||
});
|
||||
d.render(true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollPouvoir(pouvoirId) {
|
||||
let pouvoir = this.items.get(pouvoirId)
|
||||
if (pouvoir) {
|
||||
let rollData = this.getCommonRollData(undefined, undefined)
|
||||
if (pouvoir.system.feeriemasque != "autre") {
|
||||
rollData.pouvoirBase = duplicate(this.system.rang[pouvoir.system.feeriemasque.toLowerCase()])
|
||||
rollData.pouvoirBase.label = "Féerie"
|
||||
rollData.carac = duplicate(this.system.caracteristiques[pouvoir.system.carac])
|
||||
rollData.caracKey = pouvoir.system.carac
|
||||
if (pouvoir.system.pouvoirtype == "passif") {
|
||||
this.pouvoirPassifDialog(pouvoir)
|
||||
return
|
||||
}
|
||||
|
||||
let rollData = this.getCommonRollData(undefined, undefined)
|
||||
|
||||
rollData.pouvoirMaxUsage = this.getPouvoirUsageMax(pouvoir)
|
||||
rollData.pouvoir = duplicate(pouvoir)
|
||||
rollData.pouvoir = foundry.utils.duplicate(pouvoir)
|
||||
rollData.mode = "pouvoir"
|
||||
let rollDialog = await HeritiersRollDialog.create(this, rollData)
|
||||
rollDialog.render(true)
|
||||
|
||||
if (pouvoir.system.feeriemasque != "autre") {
|
||||
rollData.pouvoirBase = foundry.utils.duplicate(this.system.rang[pouvoir.system.feeriemasque.toLowerCase()])
|
||||
rollData.pouvoirBase.label = "Féerie"
|
||||
if (pouvoir.system.istest && !pouvoir.system.carac) {
|
||||
ui.notifications.warn("Le pouvoir actif " + pouvoir.name + " n'a pas de caractéristique associée")
|
||||
}
|
||||
if (pouvoir.system.istest) {
|
||||
rollData.carac = foundry.utils.duplicate(this.system.caracteristiques[pouvoir.system.carac])
|
||||
rollData.caracKey = pouvoir.system.carac
|
||||
} else {
|
||||
rollData.noRoll = true
|
||||
HeritiersUtility.rollHeritiers(rollData);
|
||||
return;
|
||||
//this.incDecPointsUsage(pouvoir.id, -rollData.pouvoirPointsUsage)
|
||||
//ui.notifications.warn("Le pouvoir actif " + pouvoir.name + " a été utilisé, dépense de " + pouvoirPointsUsage + " points d'usage")
|
||||
}
|
||||
}
|
||||
await HeritiersRollDialog.create(this, rollData)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
incDecPointsAme(value) {
|
||||
let newValue = this.system.magie.pointsame.value + value
|
||||
newValue = Math.max(newValue, 0)
|
||||
newValue = Math.min(newValue, this.system.magie.pointsame.max)
|
||||
this.update({ 'system.magie.pointsame.value': newValue })
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
incDecPV(value) {
|
||||
let newValue = this.system.pv.value + value
|
||||
newValue = Math.max(newValue, 0)
|
||||
newValue = Math.min(newValue, this.system.pv.max)
|
||||
this.update({ 'system.pv.value': newValue })
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
incDecPointsUsage(pouvoirId, value) {
|
||||
let pouvoir = this.items.get(pouvoirId)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* -------------------------------------------- */
|
||||
|
||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
||||
import { HeritiersRollDialog } from "./heritiers-roll-dialog.js";
|
||||
import { HeritiersRollDialog } from "./applications/heritiers-roll-dialog.mjs";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class HeritiersCommands {
|
||||
|
||||
+170
-39
@@ -12,7 +12,7 @@ export const HERITIERS_CONFIG = {
|
||||
"san": "Sang-Froid"
|
||||
},
|
||||
|
||||
competenceCategorie : {
|
||||
competenceCategorie: {
|
||||
"utile": "Utile",
|
||||
"futile": "Futile"
|
||||
},
|
||||
@@ -24,13 +24,14 @@ export const HERITIERS_CONFIG = {
|
||||
interet: "Personne d'interêt"
|
||||
},
|
||||
|
||||
competenceProfil : {
|
||||
"aventurier": {kind: "physical", name: "Aventurier"},
|
||||
"roublard": {kind: "physical",name: "Roublard"},
|
||||
"combattant": {kind: "physical",name:"Combattant"},
|
||||
"erudit": {kind: "mental",name:"Erudit"},
|
||||
"savant": {kind: "mental",name:"Savant"},
|
||||
"gentleman": {kind: "mental",name:"Gentleman"}
|
||||
competenceProfil: {
|
||||
"aventurier": { kind: "physical", name: "Aventurier" },
|
||||
"roublard": { kind: "physical", name: "Roublard" },
|
||||
"combattant": { kind: "physical", name: "Combattant" },
|
||||
"erudit": { kind: "mental", name: "Erudit" },
|
||||
"savant": { kind: "mental", name: "Savant" },
|
||||
"gentleman": { kind: "mental", name: "Gentleman" },
|
||||
"magie": { kind: "magical", name: "Magie" },
|
||||
},
|
||||
baseTestPouvoir: {
|
||||
"feerie": "Féerie",
|
||||
@@ -52,7 +53,7 @@ export const HERITIERS_CONFIG = {
|
||||
},
|
||||
statutMasque: {
|
||||
"masque": "Masqué",
|
||||
"demasque":"Démasqué"
|
||||
"demasque": "Démasqué"
|
||||
},
|
||||
niveauPouvoir: {
|
||||
"normal": "Normal",
|
||||
@@ -65,32 +66,32 @@ export const HERITIERS_CONFIG = {
|
||||
},
|
||||
seuilsDifficulte: {
|
||||
"-1": "Aucun/Non applicable",
|
||||
"5": "Enfantine",
|
||||
"6": "Triviale",
|
||||
"7": "Moins Triviale",
|
||||
"8": "Aisée",
|
||||
"7": "Moins Aisée",
|
||||
"10": "Normale",
|
||||
"11": "Moins Normale",
|
||||
"12": "Compliquée",
|
||||
"13": "Plus Compliquée",
|
||||
"14": "Difficile",
|
||||
"15": "Plus Difficile",
|
||||
"16": "Très Difficile",
|
||||
"17": "Très Très Difficile",
|
||||
"18": "Critique",
|
||||
"19": "Plus Critique",
|
||||
"20": "Insurmontable",
|
||||
"20": "Très Insurmontable",
|
||||
"22": "Surhumaine",
|
||||
"23": "Très Surhumaine",
|
||||
"24": "Epique",
|
||||
"25": "Plus Epique",
|
||||
"26": "Légendaire",
|
||||
"26": "Très Légendaire",
|
||||
"28": "Mythique",
|
||||
"29": "Plus Mythique",
|
||||
"30": "Divine"
|
||||
"5": "Enfantine (5)",
|
||||
"6": "Triviale (6)",
|
||||
"7": "Moins Triviale (7)",
|
||||
"8": "Aisée (8)",
|
||||
"9": "Moins Aisée (9)",
|
||||
"10": "Normale (10)",
|
||||
"11": "Moins Normale (11)",
|
||||
"12": "Compliquée (12)",
|
||||
"13": "Plus Compliquée (13)",
|
||||
"14": "Difficile (14)",
|
||||
"15": "Plus Difficile (15)",
|
||||
"16": "Très Difficile (16)",
|
||||
"17": "Très Très Difficile (17)",
|
||||
"18": "Critique (18)",
|
||||
"19": "Plus Critique (19)",
|
||||
"20": "Insurmontable (20)",
|
||||
"21": "Très Insurmontable (21)",
|
||||
"22": "Surhumaine (22)",
|
||||
"23": "Très Surhumaine (23)",
|
||||
"24": "Epique (24)",
|
||||
"25": "Plus Epique (25)",
|
||||
"26": "Légendaire (26)",
|
||||
"27": "Très Légendaire (27)",
|
||||
"28": "Mythique (28)",
|
||||
"29": "Plus Mythique (29)",
|
||||
"30": "Divine (30)"
|
||||
},
|
||||
|
||||
attaqueCible: {
|
||||
@@ -100,7 +101,7 @@ export const HERITIERS_CONFIG = {
|
||||
"tete": "Tête/Coeur"
|
||||
},
|
||||
|
||||
categorieArme : {
|
||||
categorieArme: {
|
||||
"trait": "Arme de trait",
|
||||
"poing": "Arme de poing",
|
||||
"epaule": "Arme d'épaule",
|
||||
@@ -130,13 +131,13 @@ export const HERITIERS_CONFIG = {
|
||||
"controlee": "Contrôlée (C)",
|
||||
"prohibee": "Prohibée (P)"
|
||||
},
|
||||
armeDissimulation :{
|
||||
armeDissimulation: {
|
||||
"tresfacile": "Très facile (TF)",
|
||||
"facile": "Facile (F)",
|
||||
"difficile": "Difficile (D)",
|
||||
"impossible": "Impossible (I)"
|
||||
},
|
||||
typeProtection : {
|
||||
typeProtection: {
|
||||
"balle": "Protège ds balles",
|
||||
"melee": "Protège en mélée",
|
||||
"tout": "Tout type de dégats"
|
||||
@@ -145,7 +146,137 @@ export const HERITIERS_CONFIG = {
|
||||
"traditionnelle": "Traditionnelle",
|
||||
"moderne": "Moderne",
|
||||
"orientale": "Orientale"
|
||||
},
|
||||
typeContact: {
|
||||
"contact": "Contact",
|
||||
"allie": "Allié",
|
||||
"ennemi": "Ennemi",
|
||||
"interet": "Personne d'interêt"
|
||||
},
|
||||
niveauContact: {
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
},
|
||||
pointsUsageList: {
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
},
|
||||
attaquePlusieursList: {
|
||||
"0": "0",
|
||||
"1": "+1",
|
||||
"2": "+2",
|
||||
},
|
||||
attaque2ArmesListe: [
|
||||
{ value: "0", label: "Aucun" },
|
||||
{ value: "-4", label: "Deux armes à 1 main" },
|
||||
{ value: "-2", label: "Deux armes naturelles" },
|
||||
{ value: "-2", label: "Avec spécialisation \"Mauvaise Main\"" }
|
||||
],
|
||||
typeProfil: {
|
||||
"mineur": "Mineur",
|
||||
"majeur": "Majeur",
|
||||
},
|
||||
bonusMalusContext: [
|
||||
{ value: "-6", label: "-6" },
|
||||
{ value: "-5", label: "-5" },
|
||||
{ value: "-4", label: "-4" },
|
||||
{ value: "-3", label: "-3" },
|
||||
{ value: "-2", label: "-2" },
|
||||
{ value: "-1", label: "-1" },
|
||||
{ value: "0", label: "0" },
|
||||
{ value: "1", label: "+1" },
|
||||
{ value: "2", label: "+2" },
|
||||
{ value: "3", label: "+3" },
|
||||
{ value: "4", label: "+4" },
|
||||
{ value: "5", label: "+5" },
|
||||
{ value: "6", label: "+6" }
|
||||
],
|
||||
listNiveauSort: {
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
},
|
||||
listRangSort: {
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7"
|
||||
},
|
||||
listNiveau: {
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9",
|
||||
"10": "10"
|
||||
},
|
||||
rangName: [
|
||||
"Novice",
|
||||
"Novice",
|
||||
"Adepte",
|
||||
"Maître",
|
||||
"Grand Maître"
|
||||
],
|
||||
rangNameSpecific: {
|
||||
"Druidisme": {
|
||||
"Novice": "Eubage",
|
||||
"Adepte": "Saronide",
|
||||
"Maître": "Ovate",
|
||||
"Grand Maître": "Archidruide"
|
||||
},
|
||||
"Faëomancie": {
|
||||
"Novice": "Marmiton",
|
||||
"Adepte": "Queux",
|
||||
"Maître": "Chef",
|
||||
"Grand Maître": "Maître-queux"
|
||||
},
|
||||
"Nécromancie": {
|
||||
"Novice": "Inexpertus",
|
||||
"Adepte": "Discipulus",
|
||||
"Maître": "Dominus",
|
||||
"Grand Maître": "Magister"
|
||||
},
|
||||
"Necromancie": {
|
||||
"Novice": "Inexpertus",
|
||||
"Adepte": "Discipulus",
|
||||
"Maître": "Dominus",
|
||||
"Grand Maître": "Magister"
|
||||
},
|
||||
"Magie du Clan": {
|
||||
"Novice": "Apprenti",
|
||||
"Adepte": "Disciple",
|
||||
"Maître": "Maître",
|
||||
"Grand Maître": "Éminence"
|
||||
},
|
||||
"Théurgie": {
|
||||
"Novice": "Frère",
|
||||
"Adepte": "Père",
|
||||
"Maître": "Saint",
|
||||
"Grand Maître": "Apôtre"
|
||||
},
|
||||
"Grand Langage": {
|
||||
"Novice": "Éveillé",
|
||||
"Adepte": "Initié",
|
||||
"Maître": "Sage",
|
||||
"Grand Maître": "Docteur"
|
||||
}
|
||||
},
|
||||
soufflesMagieDuClan: {
|
||||
"soufflecombat": "Souffle du Combat",
|
||||
"soufflemouvement": "Souffle du Mouvement",
|
||||
"souffleesprit": "Souffle de l'Esprit"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Extend the basic ActorSheet with some very simple modifications
|
||||
* @extends {ActorSheet}
|
||||
*/
|
||||
|
||||
import { HeritiersActorSheet } from "./heritiers-actor-sheet.js";
|
||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class HeritiersCreatureSheet extends HeritiersActorSheet {
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
|
||||
return mergeObject(super.defaultOptions, {
|
||||
classes: ["fvtt-les-heritiers", "sheet", "actor"],
|
||||
template: "systems/fvtt-les-heritiers/templates/creature-sheet.html",
|
||||
width: 640,
|
||||
height: 720,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }],
|
||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
||||
editScore: false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
||||
|
||||
/**
|
||||
* Extend the basic ItemSheet with some very simple modifications
|
||||
* @extends {ItemSheet}
|
||||
*/
|
||||
export class HeritiersItemSheet extends ItemSheet {
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
|
||||
return mergeObject(super.defaultOptions, {
|
||||
classes: ["fvtt-les-heritiers", "sheet", "item"],
|
||||
template: "systems/fvtt-les-heritiers/templates/item-sheet.html",
|
||||
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
||||
width: 620,
|
||||
height: 550,
|
||||
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}]
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
_getHeaderButtons() {
|
||||
let buttons = super._getHeaderButtons();
|
||||
// Add "Post to chat" button
|
||||
// We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry!
|
||||
buttons.unshift(
|
||||
{
|
||||
class: "post",
|
||||
icon: "fas fa-comment",
|
||||
onclick: ev => { }
|
||||
})
|
||||
return buttons
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
setPosition(options = {}) {
|
||||
const position = super.setPosition(options);
|
||||
const sheetBody = this.element.find(".sheet-body");
|
||||
const bodyHeight = position.height - 192;
|
||||
sheetBody.css("height", bodyHeight);
|
||||
if (this.item.type.includes('weapon')) {
|
||||
position.width = 640;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async getData() {
|
||||
const objectData = duplicate(this.object)
|
||||
let formData = {
|
||||
title: this.title,
|
||||
id: this.id,
|
||||
type: objectData.type,
|
||||
img: objectData.img,
|
||||
name: objectData.name,
|
||||
editable: this.isEditable,
|
||||
cssClass: this.isEditable ? "editable" : "locked",
|
||||
system: objectData.system,
|
||||
limited: this.object.limited,
|
||||
options: this.options,
|
||||
owner: this.document.isOwner,
|
||||
config: game.system.lesheritiers.config,
|
||||
isArmeMelee: HeritiersUtility.isArmeMelee(this.object),
|
||||
description: await TextEditor.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
|
||||
}
|
||||
}
|
||||
|
||||
//this.options.editable = !(this.object.origin == "embeddedItem");
|
||||
console.log("ITEM DATA", formData, this);
|
||||
return formData;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
_getHeaderButtons() {
|
||||
let buttons = super._getHeaderButtons();
|
||||
buttons.unshift({
|
||||
class: "post",
|
||||
icon: "fas fa-comment",
|
||||
onclick: ev => this.postItem()
|
||||
});
|
||||
return buttons
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
postItem() {
|
||||
let chatData = duplicate(HeritiersUtility.data(this.item));
|
||||
if (this.actor) {
|
||||
chatData.actor = { id: this.actor.id };
|
||||
}
|
||||
// Don't post any image for the item (which would leave a large gap) if the default image is used
|
||||
if (chatData.img.includes("/blank.png")) {
|
||||
chatData.img = null;
|
||||
}
|
||||
// JSON object for easy creation
|
||||
chatData.jsondata = JSON.stringify(
|
||||
{
|
||||
compendium: "postedItem",
|
||||
payload: chatData,
|
||||
});
|
||||
|
||||
renderTemplate('systems/fvtt-Heritiers-rpg/templates/post-item.html', chatData).then(html => {
|
||||
let chatOptions = HeritiersUtility.chatDataSetup(html);
|
||||
ChatMessage.create(chatOptions)
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.options.editable) return;
|
||||
|
||||
|
||||
// Update Inventory Item
|
||||
html.find('.item-edit').click(ev => {
|
||||
const li = $(ev.currentTarget).parents(".item")
|
||||
const item = this.object.options.actor.getOwnedItem(li.data("item-id"))
|
||||
item.sheet.render(true);
|
||||
});
|
||||
|
||||
html.find('.delete-subitem').click(ev => {
|
||||
this.deleteSubitem(ev);
|
||||
})
|
||||
|
||||
html.find('#add-specialite').click(ev => {
|
||||
let spec = duplicate(this.object.system.specialites)
|
||||
spec.push( { name: "Nouvelle Spécialité", id: randomID(16), used: false })
|
||||
this.object.update( { 'system.specialites': spec })
|
||||
})
|
||||
html.find('.delete-specialite').click(ev => {
|
||||
const li = $(ev.currentTarget).parents(".specialite-item")
|
||||
let index = li.data("specialite-index")
|
||||
let spec = duplicate(this.object.system.specialites)
|
||||
spec.splice(index,1)
|
||||
this.object.update( { 'system.specialites': spec })
|
||||
})
|
||||
html.find('.edit-specialite').change(ev => {
|
||||
const li = $(ev.currentTarget).parents(".specialite-item")
|
||||
let index = li.data("specialite-index")
|
||||
let spec = duplicate(this.object.system.specialites)
|
||||
spec[index].name = ev.currentTarget.value
|
||||
spec[index].id = spec[index].id || randomID(16)
|
||||
this.object.update( { 'system.specialites': spec })
|
||||
})
|
||||
html.find('.edit-specialite-description').change(ev => {
|
||||
const li = $(ev.currentTarget).parents(".specialite-item")
|
||||
let index = li.data("specialite-index")
|
||||
let spec = duplicate(this.object.system.specialites)
|
||||
spec[index].description = ev.currentTarget.value
|
||||
spec[index].id = spec[index].id || randomID(16)
|
||||
this.object.update( { 'system.specialites': spec })
|
||||
})
|
||||
|
||||
html.find('#add-automation').click(ev => {
|
||||
let autom = duplicate(this.object.system.automations)
|
||||
autom.push( { eventtype: "on-drop", name: "Automatisation 1", competence: "", minLevel: 0, id: randomID(16) })
|
||||
this.object.update( { 'system.automations': autom })
|
||||
})
|
||||
html.find('.delete-automation').click(ev => {
|
||||
const li = $(ev.currentTarget).parents(".automation-item")
|
||||
let index = li.data("automation-index")
|
||||
let autom = duplicate(this.object.system.automations)
|
||||
autom.splice(index,1)
|
||||
this.object.update( { 'system.automations': autom })
|
||||
})
|
||||
html.find('.automation-edit-field').change(ev => {
|
||||
let index = $(ev.currentTarget).data("automation-index")
|
||||
let field = $(ev.currentTarget).data("automation-field")
|
||||
let auto = duplicate(this.object.system.automations)
|
||||
auto[index][field] = ev.currentTarget.value
|
||||
auto[index].id = auto[index].id || randomID(16)
|
||||
this.object.update( { 'system.automations': auto })
|
||||
})
|
||||
|
||||
// Update Inventory Item
|
||||
html.find('.item-delete').click(ev => {
|
||||
const li = $(ev.currentTarget).parents(".item");
|
||||
let itemId = li.data("item-id");
|
||||
let itemType = li.data("item-type");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
get template() {
|
||||
let type = this.item.type;
|
||||
return `systems/fvtt-les-heritiers/templates/item-${type}-sheet.html`;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
_updateObject(event, formData) {
|
||||
return this.object.update(formData);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export const defaultItemImg = {
|
||||
fee: "systems/fvtt-les-heritiers/assets/icons/faery_type.webp",
|
||||
profil: "systems/fvtt-les-heritiers/assets/icons/profil.webp",
|
||||
equipement: "systems/fvtt-les-heritiers/assets/icons/equipement.webp",
|
||||
sort: "systems/fvtt-les-heritiers/assets/icons/sort.webp",
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,32 @@ export class HeritiersItem extends Item {
|
||||
if (!data.img) {
|
||||
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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+89
-20
@@ -9,15 +9,17 @@
|
||||
/* -------------------------------------------- */
|
||||
// Import Modules
|
||||
import { HeritiersActor } from "./heritiers-actor.js";
|
||||
import { HeritiersItemSheet } from "./heritiers-item-sheet.js";
|
||||
import { HeritiersActorSheet } from "./heritiers-actor-sheet.js";
|
||||
import { HeritiersActorPNJSheet } from "./heritiers-actor-pnj-sheet.js";
|
||||
import { HeritiersCreatureSheet } from "./heritiers-creature-sheet.js";
|
||||
import { HeritiersItem } from "./heritiers-item.js";
|
||||
import { HeritiersUtility } from "./heritiers-utility.js";
|
||||
import { HeritiersCombat } from "./heritiers-combat.js";
|
||||
import { HeritiersItem } from "./heritiers-item.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 */
|
||||
/* -------------------------------------------- */
|
||||
@@ -46,20 +48,62 @@ Hooks.once("init", async function () {
|
||||
// Define custom Entity classes
|
||||
CONFIG.Combat.documentClass = HeritiersCombat
|
||||
CONFIG.Actor.documentClass = HeritiersActor
|
||||
CONFIG.Actor.dataModels = {
|
||||
personnage: models.PersonnageDataModel,
|
||||
pnj: models.PnjDataModel
|
||||
}
|
||||
|
||||
CONFIG.Item.documentClass = HeritiersItem
|
||||
CONFIG.Item.dataModels = {
|
||||
accessoire: models.AccessoireDataModel,
|
||||
arme: models.ArmeDataModel,
|
||||
atoutfeerique: models.AtoutFeeriqueDataModel,
|
||||
avantage: models.AvantageDataModel,
|
||||
capacitenaturelle: models.CapaciteNaturelleDataModel,
|
||||
competence: models.CompetenceDataModel,
|
||||
contact: models.ContactDataModel,
|
||||
desavantage: models.DesavantageDataModel,
|
||||
equipement: models.EquipementDataModel,
|
||||
fee: models.FeeDataModel,
|
||||
pouvoir: models.PouvoirDataModel,
|
||||
profil: models.ProfilDataModel,
|
||||
protection: models.ProtectionDataModel,
|
||||
sort: models.SortDataModel
|
||||
}
|
||||
|
||||
// Create an object of bonus/malus from -6 to +6 signed
|
||||
HERITIERS_CONFIG.bonusMalus = Array.from({ length: 7 }, (v, k) => toString(k - 6))
|
||||
CONFIG.HERITIERS = HERITIERS_CONFIG
|
||||
|
||||
game.system.lesheritiers = {
|
||||
HeritiersUtility,
|
||||
config: HERITIERS_CONFIG
|
||||
config: HERITIERS_CONFIG,
|
||||
models,
|
||||
sheets
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
// Register sheet application classes
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
Actors.registerSheet("fvtt-les-heritiers", HeritiersActorSheet, { types: ["personnage"], makeDefault: true })
|
||||
Actors.registerSheet("fvtt-les-heritiers", HeritiersActorPNJSheet, { types: ["pnj"], makeDefault: true })
|
||||
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
|
||||
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", sheets.HeritiersPersonnageSheet, { types: ["personnage"], makeDefault: true })
|
||||
foundry.documents.collections.Actors.registerSheet("fvtt-les-heritiers", sheets.HeritiersPnjSheet, { types: ["pnj"], makeDefault: true })
|
||||
|
||||
Items.unregisterSheet("core", ItemSheet);
|
||||
Items.registerSheet("fvtt-les-heritiers", HeritiersItemSheet, { makeDefault: true })
|
||||
// Register AppV2 Item Sheets
|
||||
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersAccessoireSheet, { types: ["accessoire"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersArmeSheet, { types: ["arme"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersAtoutFeeriqueSheet, { types: ["atoutfeerique"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersAvantageSheet, { types: ["avantage"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersCapaciteNaturelleSheet, { types: ["capacitenaturelle"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersCompetenceSheet, { types: ["competence"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersContactSheet, { types: ["contact"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersDesavantageSheet, { types: ["desavantage"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersEquipementSheet, { types: ["equipement"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersFeeSheet, { types: ["fee"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersPouvoirSheet, { types: ["pouvoir"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersProfilSheet, { types: ["profil"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersProtectionSheet, { types: ["protection"], makeDefault: true })
|
||||
foundry.documents.collections.Items.registerSheet("fvtt-les-heritiers", sheets.HeritiersSortSheet, { types: ["sort"], makeDefault: true })
|
||||
|
||||
HeritiersUtility.init()
|
||||
|
||||
@@ -70,12 +114,38 @@ function welcomeMessage() {
|
||||
ChatMessage.create({
|
||||
user: game.user.id,
|
||||
whisper: [game.user.id],
|
||||
content: `<div id="welcome-message-heritiers"><span class="rdd-roll-part">
|
||||
<strong>Bienvenue dans Les Heritiers et la Belle Epoque !</strong>
|
||||
<p>Les livres du JDR Les Heritiers sont nécessaires pour jouer : https://www.titam-france.fr</p>
|
||||
<p>Les Heritiers est jeu de rôle publié par Titam France/Sombres projets, tout les droits leur appartiennent.</p>
|
||||
<p>Système développé par LeRatierBretonnien, support sur le <a href="https://discord.gg/pPSDNJk">Discord FR de Foundry</a>.</p>
|
||||
` });
|
||||
content: `
|
||||
<div class="heritiers-chat-card heritiers-welcome-card">
|
||||
<div class="chat-card-header welcome-header">
|
||||
<div class="welcome-icon-wrapper">
|
||||
<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>
|
||||
`
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -106,10 +176,10 @@ Hooks.once("ready", function () {
|
||||
});
|
||||
}
|
||||
|
||||
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter=>{
|
||||
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
|
||||
console.log("ClassCounter loaded", moduleCounter)
|
||||
moduleCounter.ClassCounter.registerUsageCount()
|
||||
}).catch(err=>
|
||||
}).catch(err =>
|
||||
console.log("No stats available, giving up.")
|
||||
)
|
||||
welcomeMessage();
|
||||
@@ -130,4 +200,3 @@ Hooks.on("chatMessage", (html, content, msg) => {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -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 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.competence?.system.niveau > 0) {
|
||||
enableD10 = true
|
||||
}
|
||||
if (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);
|
||||
|
||||
var dialog = this;
|
||||
function onLoad() {
|
||||
}
|
||||
$(function () { onLoad(); });
|
||||
|
||||
html.find('#sdValue').change(async (event) => {
|
||||
this.rollData.sdValue = Number(event.currentTarget.value)
|
||||
})
|
||||
html.find('#caracKey').change(async (event) => {
|
||||
this.rollData.caracKey = String(event.currentTarget.value)
|
||||
})
|
||||
html.find('#bonus-malus-context').change((event) => {
|
||||
this.rollData.bonusMalusContext = Number(event.currentTarget.value)
|
||||
})
|
||||
html.find('#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)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
+142
-53
@@ -26,7 +26,7 @@ const __facesAdjacentes = {
|
||||
10: [8, 4, 3, 7]
|
||||
},
|
||||
"d12": {
|
||||
1:[2, 3, 4, 5, 6],
|
||||
1: [2, 3, 4, 5, 6],
|
||||
2: [1, 6, 8, 12, 3],
|
||||
3: [1, 4, 11, 12, 2],
|
||||
4: [1, 5, 10, 11, 3],
|
||||
@@ -48,7 +48,7 @@ export class HeritiersUtility {
|
||||
/* -------------------------------------------- */
|
||||
static async init() {
|
||||
Hooks.on('renderChatLog', (log, html, data) => HeritiersUtility.chatListeners(html))
|
||||
Hooks.on("getChatLogEntryContext", (html, options) => HeritiersUtility.chatRollMenu(html, options))
|
||||
/* Unused for Heitiers : Hooks.on("getChatMessageContextOptions", (html, options) => HeritiersUtility.chatRollMenu(html, options))*/
|
||||
|
||||
this.rollDataStore = {}
|
||||
this.defenderStore = {}
|
||||
@@ -76,6 +76,10 @@ export class HeritiersUtility {
|
||||
Handlebars.registerHelper('mul', function (a, 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);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -104,6 +108,8 @@ export class HeritiersUtility {
|
||||
const skills = await HeritiersUtility.loadCompendium("fvtt-les-heritiers.competences")
|
||||
this.skills = skills.map(i => i.toObject())
|
||||
|
||||
this.competencesMagie = this.skills.filter(s => s.system.profil == "magie")
|
||||
|
||||
game.settings.register("fvtt-les-heritiers", "heritiers-heritage", {
|
||||
name: "Points d'héritage",
|
||||
hint: "Points d'héritage du groupe",
|
||||
@@ -114,6 +120,32 @@ export class HeritiersUtility {
|
||||
})
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getSDSortValue(niveau) {
|
||||
if (niveau <= 1) return 12;
|
||||
if (niveau == 2) return 14;
|
||||
if (niveau == 3) return 16;
|
||||
if (niveau > 3) return 18;
|
||||
return 18;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getCompetencesMagie() {
|
||||
return this.competencesMagie
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static buildCompetencesMagie() {
|
||||
let competences = foundry.utils.duplicate(this.getCompetencesMagie())
|
||||
for (let comp of competences) {
|
||||
// Calcul du rang
|
||||
let rang = Math.round(comp.system.niveau / 2);
|
||||
competences.system.rang = rang;
|
||||
competences.system.rangGenericName = game.system.lesheritiers.config.rangName[rang];
|
||||
competences.system.rangSpecificName = game.system.lesheritiers.config.rangNameSpecific[comp.Name][competences.system.rangGenericName];
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async loadCompendiumData(compendium) {
|
||||
const pack = game.packs.get(compendium);
|
||||
@@ -133,29 +165,29 @@ export class HeritiersUtility {
|
||||
/* -------------------------------------------- */
|
||||
static async chatListeners(html) {
|
||||
|
||||
html.on("click", '.predilection-reroll', async event => {
|
||||
$(html).on("click", '.predilection-reroll', async event => {
|
||||
let predIdx = $(event.currentTarget).data("predilection-index")
|
||||
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
|
||||
let message = game.messages.get(messageId)
|
||||
let rollData = message.getFlag("world", "heritiers-roll")
|
||||
let actor = this.getActorFromRollData(rollData)
|
||||
await actor.setPredilectionUsed(rollData.competence._id, predIdx)
|
||||
rollData.competence = duplicate(actor.getCompetence(rollData.competence._id))
|
||||
rollData.competence = foundry.utils.duplicate(actor.getCompetence(rollData.competence._id))
|
||||
HeritiersUtility.rollHeritiers(rollData)
|
||||
})
|
||||
|
||||
html.on("click", '.roll-tricherie-2', async event => {
|
||||
$(html).on("click", '.roll-tricherie-2', async event => {
|
||||
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
|
||||
let message = game.messages.get(messageId)
|
||||
let rollData = message.getFlag("world", "heritiers-roll")
|
||||
let actor = this.getActorFromRollData(rollData)
|
||||
if ( await actor.incDecTricherie(-2) ) {
|
||||
if (await actor.incDecTricherie(-2)) {
|
||||
rollData.forcedValue = Number($(event.currentTarget).data("dice-value"))
|
||||
HeritiersUtility.rollHeritiers(rollData)
|
||||
}
|
||||
})
|
||||
|
||||
html.on("click", '.roll-chat-degat', async event => {
|
||||
$(html).on("click", '.roll-chat-degat', async event => {
|
||||
let messageId = HeritiersUtility.findChatMessageId(event.currentTarget)
|
||||
let message = game.messages.get(messageId)
|
||||
let rollData = message.getFlag("world", "heritiers-roll")
|
||||
@@ -168,14 +200,14 @@ export class HeritiersUtility {
|
||||
static async preloadHandlebarsTemplates() {
|
||||
|
||||
const templatePaths = [
|
||||
'systems/fvtt-les-heritiers/templates/editor-notes-gm.html',
|
||||
'systems/fvtt-les-heritiers/templates/partial-item-header.html',
|
||||
'systems/fvtt-les-heritiers/templates/partial-item-description.html',
|
||||
'systems/fvtt-les-heritiers/templates/partial-item-nav.html',
|
||||
'systems/fvtt-les-heritiers/templates/partial-utile-skills.html',
|
||||
'systems/fvtt-les-heritiers/templates/partial-list-niveau.html'
|
||||
'systems/fvtt-les-heritiers/templates/editor-notes-gm.hbs',
|
||||
'systems/fvtt-les-heritiers/templates/partial-item-header.hbs',
|
||||
'systems/fvtt-les-heritiers/templates/partial-item-description.hbs',
|
||||
'systems/fvtt-les-heritiers/templates/partial-item-nav.hbs',
|
||||
'systems/fvtt-les-heritiers/templates/partial-utile-skills.hbs',
|
||||
'systems/fvtt-les-heritiers/templates/partial-actor-equipment.hbs'
|
||||
]
|
||||
return loadTemplates(templatePaths);
|
||||
return foundry.applications.handlebars.loadTemplates(templatePaths);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -252,7 +284,7 @@ export class HeritiersUtility {
|
||||
|
||||
let id = rollData.rollId;
|
||||
let oldRollData = this.rollDataStore[id] || {};
|
||||
let newRollData = mergeObject(oldRollData, rollData);
|
||||
let newRollData = foundry.utils.mergeObject(oldRollData, rollData);
|
||||
this.rollDataStore[id] = newRollData;
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
@@ -390,8 +422,8 @@ export class HeritiersUtility {
|
||||
rollData.finalResult -= 5 + rollValue // Remove also the dice result has it has been added already
|
||||
}
|
||||
}
|
||||
if ( !rollData.forcedValue) {
|
||||
rollData.adjacentFaces = duplicate(__facesAdjacentes[rollData.mainDice][rollData.diceValue])
|
||||
if (!rollData.forcedValue) {
|
||||
rollData.adjacentFaces = foundry.utils.duplicate(__facesAdjacentes[rollData.mainDice][rollData.diceValue])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,7 +483,7 @@ export class HeritiersUtility {
|
||||
this.computeArmeDegats(rollData, actor)
|
||||
}
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await 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")
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
@@ -463,7 +495,7 @@ export class HeritiersUtility {
|
||||
this.computeMarge(rollData, valeurDefense)
|
||||
rollData.dureeAssommer = (rollData.marge) ? rollData.marge * 2 : 1
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await 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")
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
@@ -471,13 +503,14 @@ export class HeritiersUtility {
|
||||
|
||||
let actor = this.getActorFromRollData(rollData)
|
||||
|
||||
if ( rollData.mode == "pouvoir" && actor.getPouvoirUsage(rollData.pouvoir._id) < rollData.pouvoirPointsUsage) {
|
||||
if (rollData.mode == "pouvoir" && actor.getPouvoirUsage(rollData.pouvoir._id) < rollData.pouvoirPointsUsage) {
|
||||
ui.notifications.warn("Pas assez de points d'usage pour ce pouvoir.")
|
||||
return
|
||||
}
|
||||
|
||||
//rollData.actionImg = "systems/fvtt-les-heritiers/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
|
||||
rollData.carac = duplicate(actor.system.caracteristiques[rollData.caracKey])
|
||||
if (rollData.caracKey == "pre") rollData.caracKey = "pres"; // Patch tomanage wrong carac key
|
||||
rollData.carac = foundry.utils.duplicate(actor.system.caracteristiques[rollData.caracKey])
|
||||
|
||||
if (rollData.forcedValue) {
|
||||
rollData.diceFormula = rollData.forcedValue
|
||||
@@ -535,25 +568,41 @@ export class HeritiersUtility {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let myRoll = new Roll(rollData.diceFormula).roll({ async: false })
|
||||
if (!rollData.noRoll) {
|
||||
let myRoll = await new Roll(rollData.diceFormula).roll()
|
||||
await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode"))
|
||||
rollData.roll = duplicate(myRoll)
|
||||
rollData.roll = foundry.utils.duplicate(myRoll)
|
||||
console.log(">>>> ", myRoll)
|
||||
this.computeResult(actor, rollData)
|
||||
this.computeMarge(rollData, rollData.sdValue) // Calcul de la marge si seuil présent
|
||||
// Compute weapon damage for successful attacks
|
||||
if (rollData.arme && rollData.isSuccess) {
|
||||
this.computeArmeDegats(rollData, actor)
|
||||
}
|
||||
}
|
||||
|
||||
if (rollData.mode == "init") {
|
||||
actor.setFlag("world", "last-initiative", rollData.finalResult)
|
||||
}
|
||||
|
||||
// Gestion pouvoir et points d'usage
|
||||
if (rollData.mode == "pouvoir") {
|
||||
if (rollData.mode == "pouvoir" || rollData.mode == "pouvoirpassif") {
|
||||
actor.incDecPointsUsage(rollData.pouvoir._id, -rollData.pouvoirPointsUsage)
|
||||
}
|
||||
// Gestion sort et points d'âme
|
||||
if (rollData.mode == "sort") {
|
||||
if (rollData.spendEsprit) {
|
||||
actor.inDecCarac("esp", -rollData.totalEsprit)
|
||||
} else {
|
||||
actor.incDecPointsAme(-rollData.sortPointsAme)
|
||||
if (rollData.sort.system.competence == "Magie du Clan") {
|
||||
actor.incDecPV(-2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.html`, rollData)
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.hbs`, rollData)
|
||||
}, rollData)
|
||||
|
||||
// Gestion attaque standard
|
||||
@@ -582,16 +631,16 @@ export class HeritiersUtility {
|
||||
static async bonusRollHeritiers(rollData) {
|
||||
rollData.bonusFormula = rollData.addedBonus
|
||||
|
||||
let bonusRoll = new Roll(rollData.bonusFormula).roll({ async: false })
|
||||
let bonusRoll = await new Roll(rollData.bonusFormula).roll()
|
||||
await this.showDiceSoNice(bonusRoll, game.settings.get("core", "rollMode"));
|
||||
rollData.bonusRoll = duplicate(bonusRoll)
|
||||
rollData.bonusRoll = foundry.utils.duplicate(bonusRoll)
|
||||
|
||||
rollData.finalResult += rollData.bonusRoll.total
|
||||
|
||||
this.computeResult(rollData)
|
||||
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.html`, rollData)
|
||||
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-les-heritiers/templates/chat-generic-result.hbs`, rollData)
|
||||
}, rollData)
|
||||
|
||||
}
|
||||
@@ -622,7 +671,7 @@ export class HeritiersUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static blindMessageToGM(chatOptions) {
|
||||
let chatGM = duplicate(chatOptions);
|
||||
let chatGM = foundry.utils.duplicate(chatOptions);
|
||||
chatGM.whisper = this.getUsers(user => user.isGM);
|
||||
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
|
||||
console.log("blindMessageToGM", chatGM);
|
||||
@@ -686,7 +735,7 @@ export class HeritiersUtility {
|
||||
/* -------------------------------------------- */
|
||||
static getBasicRollData() {
|
||||
let rollData = {
|
||||
rollId: randomID(16),
|
||||
rollId: foundry.utils.randomID(16),
|
||||
rollMode: game.settings.get("core", "rollMode"),
|
||||
sdList: game.system.lesheritiers.config.seuilsDifficulte,
|
||||
sdValue: -1,
|
||||
@@ -764,13 +813,13 @@ export class HeritiersUtility {
|
||||
static chatRollMenu(html, options) {
|
||||
let canApply = li => canvas.tokens.controlled.length && li.find(".heritiers-roll").length
|
||||
let canApplyBA = function (li) {
|
||||
let message = game.messages.get(li.attr("data-message-id"))
|
||||
let message = game.messages.get($(li).attr("data-message-id"))
|
||||
let rollData = message.getFlag("world", "heritiers-roll")
|
||||
let actor = this.getActorFromRollData(rollData)
|
||||
return (!rollData.isReroll && actor.getBonneAventure() > 0)
|
||||
}
|
||||
let canApplyPE = function (li) {
|
||||
let message = game.messages.get(li.attr("data-message-id"))
|
||||
let message = game.messages.get($(li).attr("data-message-id"))
|
||||
let rollData = message.getFlag("world", "heritiers-roll")
|
||||
let actor = this.getActorFromRollData(rollData)
|
||||
return (!rollData.isReroll && actor.getEclat() > 0)
|
||||
@@ -804,25 +853,30 @@ export class HeritiersUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async confirmDelete(actorSheet, li) {
|
||||
let itemId = li.data("item-id");
|
||||
let msgTxt = "<p>Are you sure to remove this Item ?";
|
||||
// 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 buttons = {
|
||||
delete: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "Yes, remove it",
|
||||
label: "Oui !",
|
||||
callback: () => {
|
||||
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
|
||||
if (li.slideUp) {
|
||||
li.slideUp(200, () => actorSheet.render(false));
|
||||
} else {
|
||||
actorSheet.render(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
label: "Cancel"
|
||||
label: "Non !"
|
||||
}
|
||||
}
|
||||
msgTxt += "</p>";
|
||||
let d = new Dialog({
|
||||
title: "Confirm removal",
|
||||
title: "Confirmer la suppression",
|
||||
content: msgTxt,
|
||||
buttons: buttons,
|
||||
default: "cancel"
|
||||
@@ -830,21 +884,56 @@ export class HeritiersUtility {
|
||||
d.render(true);
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
static async __create_talents_table() {
|
||||
let compName = "fvtt-les-heritiers.talents-cellule"
|
||||
const compData = await HeritiersUtility.loadCompendium(compName)
|
||||
let talents = compData.map(i => i.toObject())
|
||||
|
||||
let htmlTab = "<table border='1'><tbody>";
|
||||
for (let entryData of talents) {
|
||||
console.log(entryData)
|
||||
htmlTab += `<tr><td>@UUID[Compendium.${compName}.${entryData._id}]{${entryData.name}}</td>`
|
||||
htmlTab += `<td>${entryData.system.description}</td>`;
|
||||
//htmlTab += `<td>${entryData.system.resumebonus}</td>`;
|
||||
htmlTab += "</tr>\n";
|
||||
static loadSort() {
|
||||
// Create afolder in the item directory if it doesn't exist
|
||||
if (!game.folders.getName("Magie du Clan")) {
|
||||
Folder.create({
|
||||
name: "Magie du Clan",
|
||||
type: "Item",
|
||||
color: "#3b1361"
|
||||
});
|
||||
}
|
||||
htmlTab += "</table>";
|
||||
await JournalEntry.create({ name: 'Liste des Talents de Cellule', content: htmlTab });
|
||||
|
||||
// Load the srcdata/sorts-druidisme.json file
|
||||
return fetch("systems/fvtt-les-heritiers/srcdata/sort_magieduclan.json")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log("Sorts Magie du Clan loaded:", data);
|
||||
this.sortDruidisme = data;
|
||||
// Loop through the spell and create the "sort "item based on the JSON content
|
||||
data.forEach(spell => {
|
||||
spell.name = spell.name;
|
||||
spell.type = "sort";
|
||||
spell.system = {
|
||||
niveau: spell.niveau,
|
||||
competence: spell.competence,
|
||||
carac1: spell.carac1,
|
||||
carac2: spell.carac2,
|
||||
description: spell.description,
|
||||
ingredients: spell.ingredients,
|
||||
portee: spell.portee,
|
||||
duree: spell.duree,
|
||||
concentration: spell.concentration,
|
||||
critique: spell.critique,
|
||||
resistance: spell.resistance,
|
||||
coutactivation: spell.coutactivation
|
||||
};
|
||||
spell.img = "systems/fvtt-les-heritiers/assets/icons/sort.webp";
|
||||
spell.folder = game.folders.getName("Magie du Clan").id;
|
||||
|
||||
// Create the item in the world
|
||||
Item.create(spell)
|
||||
.then(item => {
|
||||
console.log("Sort created:", item);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error creating sort item:", error);
|
||||
});
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error loading druidism spells:", error);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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 })
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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: "" })
|
||||
};
|
||||
}
|
||||
}
|
||||
Generated
+4971
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
MANIFEST-000058
|
||||
MANIFEST-000349
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
2024/04/01-17:26:18.400582 7f2f096006c0 Recovering log #56
|
||||
2024/04/01-17:26:18.410531 7f2f096006c0 Delete type=3 #54
|
||||
2024/04/01-17:26:18.410588 7f2f096006c0 Delete type=0 #56
|
||||
2024/04/01-17:27:00.271754 7f2f03a006c0 Level-0 table #61: started
|
||||
2024/04/01-17:27:00.271819 7f2f03a006c0 Level-0 table #61: 0 bytes OK
|
||||
2024/04/01-17:27:00.307464 7f2f03a006c0 Delete type=0 #59
|
||||
2024/04/01-17:27:00.345066 7f2f03a006c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||
2024/04/01-17:27:00.345103 7f2f03a006c0 Manual compaction at level-1 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||
2026/04/01-22:18:07.499964 7f303ffff6c0 Recovering log #346
|
||||
2026/04/01-22:18:07.553355 7f303ffff6c0 Delete type=3 #344
|
||||
2026/04/01-22:18:07.553447 7f303ffff6c0 Delete type=0 #346
|
||||
2026/04/01-22:18:57.557641 7f303effd6c0 Level-0 table #352: started
|
||||
2026/04/01-22:18:57.557674 7f303effd6c0 Level-0 table #352: 0 bytes OK
|
||||
2026/04/01-22:18:57.594048 7f303effd6c0 Delete type=0 #350
|
||||
2026/04/01-22:18:57.719667 7f303effd6c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
2024/03/31-17:51:10.121804 7f2efb8006c0 Recovering log #52
|
||||
2024/03/31-17:51:10.132699 7f2efb8006c0 Delete type=3 #50
|
||||
2024/03/31-17:51:10.132828 7f2efb8006c0 Delete type=0 #52
|
||||
2024/03/31-17:52:39.333208 7f2efae006c0 Level-0 table #57: started
|
||||
2024/03/31-17:52:39.333263 7f2efae006c0 Level-0 table #57: 0 bytes OK
|
||||
2024/03/31-17:52:39.340471 7f2efae006c0 Delete type=0 #55
|
||||
2024/03/31-17:52:39.340751 7f2efae006c0 Manual compaction at level-0 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||
2024/03/31-17:52:39.340782 7f2efae006c0 Manual compaction at level-1 from '!items!1NhJH4IJpxsGmLB8' @ 72057594037927935 : 1 .. '!items!y1yOenfAJTsb3r6e' @ 0 : 0; will stop at (end)
|
||||
2026/04/01-22:04:02.872910 7f30557ee6c0 Delete type=3 #1
|
||||
2026/04/01-22:18:01.730160 7f303effd6c0 Level-0 table #347: started
|
||||
2026/04/01-22:18:01.730193 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||
2026/04/01-22:18:01.758466 7f303effd6c0 Delete type=0 #345
|
||||
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
|
||||
2026/04/01-22:18:01.862841 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||
2026/04/01-22:18:01.881687 7f303effd6c0 Generated table #348@0: 31 keys, 76976 bytes
|
||||
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.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
MANIFEST-000058
|
||||
MANIFEST-000349
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
2024/04/01-17:26:18.436287 7f2f096006c0 Recovering log #56
|
||||
2024/04/01-17:26:18.446918 7f2f096006c0 Delete type=3 #54
|
||||
2024/04/01-17:26:18.447011 7f2f096006c0 Delete type=0 #56
|
||||
2024/04/01-17:27:00.345258 7f2f03a006c0 Level-0 table #61: started
|
||||
2024/04/01-17:27:00.345284 7f2f03a006c0 Level-0 table #61: 0 bytes OK
|
||||
2024/04/01-17:27:00.384063 7f2f03a006c0 Delete type=0 #59
|
||||
2024/04/01-17:27:00.501638 7f2f03a006c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||
2024/04/01-17:27:00.501693 7f2f03a006c0 Manual compaction at level-1 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||
2026/04/01-22:18:07.610829 7f303ffff6c0 Recovering log #346
|
||||
2026/04/01-22:18:07.668851 7f303ffff6c0 Delete type=3 #344
|
||||
2026/04/01-22:18:07.668940 7f303ffff6c0 Delete type=0 #346
|
||||
2026/04/01-22:18:57.757073 7f303effd6c0 Level-0 table #352: started
|
||||
2026/04/01-22:18:57.757102 7f303effd6c0 Level-0 table #352: 0 bytes OK
|
||||
2026/04/01-22:18:57.790436 7f303effd6c0 Delete type=0 #350
|
||||
2026/04/01-22:18:57.859562 7f303effd6c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
2024/03/31-17:51:10.159099 7f2efb8006c0 Recovering log #52
|
||||
2024/03/31-17:51:10.171301 7f2efb8006c0 Delete type=3 #50
|
||||
2024/03/31-17:51:10.171460 7f2efb8006c0 Delete type=0 #52
|
||||
2024/03/31-17:52:39.340903 7f2efae006c0 Level-0 table #57: started
|
||||
2024/03/31-17:52:39.340946 7f2efae006c0 Level-0 table #57: 0 bytes OK
|
||||
2024/03/31-17:52:39.348787 7f2efae006c0 Delete type=0 #55
|
||||
2024/03/31-17:52:39.370825 7f2efae006c0 Manual compaction at level-0 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||
2024/03/31-17:52:39.370890 7f2efae006c0 Manual compaction at level-1 from '!items!1ETVaPBtjDtzelK1' @ 72057594037927935 : 1 .. '!items!zbsVCsWxRzkzzG1N' @ 0 : 0; will stop at (end)
|
||||
2026/04/01-22:04:02.918161 7f30557ee6c0 Delete type=3 #1
|
||||
2026/04/01-22:18:02.037942 7f303effd6c0 Level-0 table #347: started
|
||||
2026/04/01-22:18:02.037978 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||
2026/04/01-22:18:02.069106 7f303effd6c0 Delete type=0 #345
|
||||
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
|
||||
2026/04/01-22:18:02.131798 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||
2026/04/01-22:18:02.144068 7f303effd6c0 Generated table #348@0: 72 keys, 18161 bytes
|
||||
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.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
MANIFEST-000058
|
||||
MANIFEST-000349
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
2024/04/01-17:26:18.385599 7f2f0aa006c0 Recovering log #56
|
||||
2024/04/01-17:26:18.395901 7f2f0aa006c0 Delete type=3 #54
|
||||
2024/04/01-17:26:18.395960 7f2f0aa006c0 Delete type=0 #56
|
||||
2024/04/01-17:27:00.228031 7f2f03a006c0 Level-0 table #61: started
|
||||
2024/04/01-17:27:00.228054 7f2f03a006c0 Level-0 table #61: 0 bytes OK
|
||||
2024/04/01-17:27:00.271487 7f2f03a006c0 Delete type=0 #59
|
||||
2024/04/01-17:27:00.345051 7f2f03a006c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||
2024/04/01-17:27:00.345088 7f2f03a006c0 Manual compaction at level-1 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||
2026/04/01-22:18:07.389011 7f303ffff6c0 Recovering log #346
|
||||
2026/04/01-22:18:07.442566 7f303ffff6c0 Delete type=3 #344
|
||||
2026/04/01-22:18:07.442655 7f303ffff6c0 Delete type=0 #346
|
||||
2026/04/01-22:18:57.645113 7f303effd6c0 Level-0 table #352: started
|
||||
2026/04/01-22:18:57.645145 7f303effd6c0 Level-0 table #352: 0 bytes OK
|
||||
2026/04/01-22:18:57.684527 7f303effd6c0 Delete type=0 #350
|
||||
2026/04/01-22:18:57.719716 7f303effd6c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
2024/03/31-17:51:10.103648 7f2efcc006c0 Recovering log #52
|
||||
2024/03/31-17:51:10.116316 7f2efcc006c0 Delete type=3 #50
|
||||
2024/03/31-17:51:10.116475 7f2efcc006c0 Delete type=0 #52
|
||||
2024/03/31-17:52:39.317590 7f2efae006c0 Level-0 table #57: started
|
||||
2024/03/31-17:52:39.317634 7f2efae006c0 Level-0 table #57: 0 bytes OK
|
||||
2024/03/31-17:52:39.332888 7f2efae006c0 Delete type=0 #55
|
||||
2024/03/31-17:52:39.340690 7f2efae006c0 Manual compaction at level-0 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||
2024/03/31-17:52:39.340767 7f2efae006c0 Manual compaction at level-1 from '!items!0fPXtA5LkLgG8uDj' @ 72057594037927935 : 1 .. '!items!zvtBlG6KCIn0oCVk' @ 0 : 0; will stop at (end)
|
||||
2026/04/01-22:04:02.828271 7f3054fed6c0 Delete type=3 #1
|
||||
2026/04/01-22:18:01.697526 7f303effd6c0 Level-0 table #347: started
|
||||
2026/04/01-22:18:01.697587 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||
2026/04/01-22:18:01.729927 7f303effd6c0 Delete type=0 #345
|
||||
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
|
||||
2026/04/01-22:18:01.823278 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||
2026/04/01-22:18:01.833392 7f303effd6c0 Generated table #348@0: 153 keys, 61883 bytes
|
||||
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.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
MANIFEST-000058
|
||||
MANIFEST-000349
|
||||
|
||||
+7
-8
@@ -1,8 +1,7 @@
|
||||
2024/04/01-17:26:18.342233 7f2f096006c0 Recovering log #56
|
||||
2024/04/01-17:26:18.352564 7f2f096006c0 Delete type=3 #54
|
||||
2024/04/01-17:26:18.352637 7f2f096006c0 Delete type=0 #56
|
||||
2024/04/01-17:27:00.041914 7f2f03a006c0 Level-0 table #61: started
|
||||
2024/04/01-17:27:00.041937 7f2f03a006c0 Level-0 table #61: 0 bytes OK
|
||||
2024/04/01-17:27:00.113315 7f2f03a006c0 Delete type=0 #59
|
||||
2024/04/01-17:27:00.191182 7f2f03a006c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||
2024/04/01-17:27:00.191238 7f2f03a006c0 Manual compaction at level-1 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||
2026/04/01-22:18:07.221290 7f30557ee6c0 Recovering log #346
|
||||
2026/04/01-22:18:07.279192 7f30557ee6c0 Delete type=3 #344
|
||||
2026/04/01-22:18:07.279260 7f30557ee6c0 Delete type=0 #346
|
||||
2026/04/01-22:18:57.441372 7f303effd6c0 Level-0 table #352: started
|
||||
2026/04/01-22:18:57.441403 7f303effd6c0 Level-0 table #352: 0 bytes OK
|
||||
2026/04/01-22:18:57.483170 7f303effd6c0 Delete type=0 #350
|
||||
2026/04/01-22:18:57.557505 7f303effd6c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||
|
||||
+11
-8
@@ -1,8 +1,11 @@
|
||||
2024/03/31-17:51:10.054712 7f2efb8006c0 Recovering log #52
|
||||
2024/03/31-17:51:10.065463 7f2efb8006c0 Delete type=3 #50
|
||||
2024/03/31-17:51:10.065576 7f2efb8006c0 Delete type=0 #52
|
||||
2024/03/31-17:52:39.281908 7f2efae006c0 Level-0 table #57: started
|
||||
2024/03/31-17:52:39.281951 7f2efae006c0 Level-0 table #57: 0 bytes OK
|
||||
2024/03/31-17:52:39.289468 7f2efae006c0 Delete type=0 #55
|
||||
2024/03/31-17:52:39.289750 7f2efae006c0 Manual compaction at level-0 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||
2024/03/31-17:52:39.303742 7f2efae006c0 Manual compaction at level-1 from '!items!0EAAt0qSzcD9VRBH' @ 72057594037927935 : 1 .. '!items!zfpjROW9LDAlXUkN' @ 0 : 0; will stop at (end)
|
||||
2026/04/01-22:04:02.761288 7f3054fed6c0 Delete type=3 #1
|
||||
2026/04/01-22:18:01.450482 7f303effd6c0 Level-0 table #347: started
|
||||
2026/04/01-22:18:01.450548 7f303effd6c0 Level-0 table #347: 0 bytes OK
|
||||
2026/04/01-22:18:01.481608 7f303effd6c0 Delete type=0 #345
|
||||
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
|
||||
2026/04/01-22:18:01.561290 7f303effd6c0 Compacting 1@0 + 0@1 files
|
||||
2026/04/01-22:18:01.574187 7f303effd6c0 Generated table #348@0: 63 keys, 27421 bytes
|
||||
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.
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