Finalisation complète du système Vermine2047 pour FoundryVTT v14

Implémentations majeures:
- Classe GroupLink pour synchronisation bidirectionnelle acteurs↔groupes
- Configuration complète des totems, PNJ et créatures
- Redesign du RollDialog avec interface compacte et sélecteurs
- Bonus/malus par domaine de totem
- Réussites automatiques et seuils auto basés sur niveau de maîtrise
- Choix du totem à garder avec recalcul des réussites
- Conversion tous templates chat cards en .hbs
- Fiches PNJ et Créature avec sélecteurs pour tous les niveaux
- Documentation technique (ARCHITECTURE.md) et utilisateur (GUIDE_UTILISATEUR.md)
- Mise à jour system.json pour compatibilité v14
- Tous les TODOs du README.md complétés

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-06-04 11:46:40 +02:00
parent c35e93975b
commit 716c1b49ae
44 changed files with 4008 additions and 631 deletions
+104 -2
View File
@@ -1,4 +1,85 @@
# CHANGELOG # CHANGELOG - Vermine2047 System
## 0.1.14 - 2026-06-04
### 🚀 Nouveautés
#### Système
- **Mise à jour de la compatibilité**: Support officiel de FoundryVTT v14 (tout en maintenant la compatibilité v11-v12)
- **Nouvelle classe GroupLink**: Gestion complète des liens bidirectionnels entre acteurs et groupes
- Synchronisation automatique des membres et rencontres
- Hooks pour la création, mise à jour et suppression d'acteurs
- Méthodes utilitaires pour gérer les relations
#### Configuration
- **Domaines des totems**: Ajout de `CONFIG.VERMINE.totemDomains` avec les domaines d'influence pour chaque totem
- **Configurations étendues**: Ajout des configurations pour PNJ et créatures
- `npcThreatLevels`, `npcExperienceLevels`, `npcRoleLevels`
- `creaturePatternLevels`, `creatureSizeLevels`, `creatureRoleLevels`, `creaturePackLevels`
#### Fiches
- **Fiche PNJ**: Remplacement des inputs numériques par des sélecteurs pour menace, expérience et rôle
- **Fiche Créature**: Remplacement des inputs numériques par des sélecteurs pour gabarit, taille, rôle et meute
- **Ajout du champ encounters**: Les personnages peuvent maintenant appartenir à des groupes
#### Jets de dés
- **Redesign complet du RollDialog**: Interface plus compacte et organisée
- Utilisation de `<details>`/`<summary>` pour une meilleure organisation
- Affichage du total du pool de dés en temps réel
- Sélecteur pour choisir quel totem garder (humain ou adapté)
- Affichage des bonus/malus par domaine de totem
- **Bonus/malus par domaine**: Implémentation des bonus de totem basés sur le domaine de prédilection
- Bonus: +1 dé si le domaine de prédilection est dans les domaines du totem
- Malus: -1 dé si le domaine de prédilection est dans les domaines du totem opposé
- **Réussites automatiques**: Implémentation des réussites automatiques basées sur le niveau de maîtrise
- Niveau 2 + spécialité: +1 réussite automatique
- Niveau 3: +1 réussite automatique
- Niveau 4 + spécialité: +2 réussites automatiques
- Niveau 5: +2 réussites automatiques
- **Seuils automatiques**: Implémentation des seuils automatiques pour les compétences non maîtrisées
- Niveau 0 (Incompétent): seuil = 9
- Niveau 1 (Débutant): seuil = 7
- Niveau >= 2: utilise la difficulté spécifiée
#### Items
- **Correction des templates de chat cards**: Tous les templates sont maintenant en `.hbs`
- **Chat cards améliorées**: Affichage plus complet des informations pour chaque type d'item
- Armes: dégâts, type, portée, munitions
- Protections: niveau, mobilité, bouclier
- Capacités: type, totem, niveau, effets
- etc.
#### Traductions
- Ajout de nombreuses nouvelles traductions pour les nouvelles fonctionnalités
- Correction des traductions existantes
#### Documentation
- **Documentation technique complète**: `docs/technical/ARCHITECTURE.md`
- Structure du projet
- Configuration du système
- Architecture des documents
- Système de dés
- Système de combat
- Gestion des groupes
- Bonnes pratiques de développement
### 🐛 Corrections
- Correction des références de templates (`.html``.hbs`)
- Correction des erreurs dans les templates de chat cards
- Amélioration de la gestion des totems dans les rolls
- Nettoyage du code et suppression des logs de débogage
### 📝 Modifications mineures
- Mise à jour des métadonnées du système dans `system.json`
- Ajout du champ `encounters` au template des personnages
- Amélioration des helpers Handlebars avec de nouveaux helpers pour les configurations PNJ/Créature
---
## 0.1.13 ## 0.1.13
- ajout des historiques - ajout des historiques
@@ -36,4 +117,25 @@
## 0.1.5 ## 0.1.5
- début de mise en forme des feuilles créature et pnj - début de mise en forme des feuilles créature et pnj
- possibilité de changer le type de capacité (pour ajouter des capacités de totem) - possibilité de changer le type de capacité (pour ajouter des capacités de totem)
---
## Notes de migration
### Pour les utilisateurs
1. **Compatibilité**: Le système est maintenant compatible avec FoundryVTT v14
2. **Nouveaux champs**: Les personnages ont maintenant un champ `encounters` pour gérer leurs groupes
3. **RollDialog**: L'interface du dialogue de jet a été complètement redessinée pour être plus intuitive
4. **Bonus de totem**: Les bonus de domaine sont maintenant automatiquement appliqués
### Pour les développeurs
1. **GroupLink**: Utilisez la classe GroupLink pour gérer les relations entre acteurs et groupes
2. **Nouveaux helpers**: De nombreux nouveaux helpers Handlebars ont été ajoutés pour les configurations PNJ/Créature
3. **CONFIG.VERMINE**: De nombreuses nouvelles configurations ont été ajoutées
---
*Généré le 2026-06-04*
+12 -12
View File
@@ -10,23 +10,23 @@
- [X] dialog d'edition des min-max - [X] dialog d'edition des min-max
### fiche de groupe ### fiche de groupe
- [ ] pas encore penché dessus - [X] pas encore penché dessus
#### Members et encounters #### Members et encounters
- [ ] faire une classe GroupLink pour avoir les actors en objets dans les array group.members[], group.encounters[], et character.encounters[], - [X] faire une classe GroupLink pour avoir les actors en objets dans les array group.members[], group.encounters[], et character.encounters[],
- [ ] faire une fonction sur le Hook.onUpdateActor => update des groupes dans characters, update des encounters et members dans groups - [X] faire une fonction sur le Hook.onUpdateActor => update des groupes dans characters, update des encounters et members dans groups
### fiche de pnj créature ### fiche de pnj créature
- [ ] à faire, - [X] à faire,
- [ ] lister les gabarit/taille/roles(creatures) et menace/experience/role(pnj)... stocker les modifs dans CONFIG.VERMINE, - [X] lister les gabarit/taille/roles(creatures) et menace/experience/role(pnj)... stocker les modifs dans CONFIG.VERMINE,
### les jets de dés ### les jets de dés
- [ ] redesign de rollDialog => `<details>+<sumary>`=> rendre moins dense - [X] redesign de rollDialog => `<details>+<sumary>`=> rendre moins dense
- [X] envoyer les spécialités utilisables au rollDialog - [X] envoyer les spécialités utilisables au rollDialog
- [X] envoyer les items utilisables au rollDialog - [X] envoyer les items utilisables au rollDialog
- [ ] gérer le fait de choisir quel totem garder : recalcul des réussites - [X] gérer le fait de choisir quel totem garder : recalcul des réussites
- [X] refacto des template chat de roll - [X] refacto des template chat de roll
- [X] gérer les dés de totems humains et adapté : couleur différente/double succès +update actor - [X] gérer les dés de totems humains et adapté : couleur différente/double succès +update actor
- [X] gérer les rerolls depuis chat(cf noc) - [X] gérer les rerolls depuis chat(cf noc)
@@ -34,20 +34,20 @@
- [X] faire l'update l' l'actor juste après s'etre accorder des rerolls, et avoir utiliser le sang-froid - [X] faire l'update l' l'actor juste après s'etre accorder des rerolls, et avoir utiliser le sang-froid
- [X] update des reserves de sang-froids lors de jets - [X] update des reserves de sang-froids lors de jets
- [X] ajout des domaines de prédilections - [X] ajout des domaines de prédilections
- [ ] gérer le dés en +/- selon l'influence du totem adapté ou humain selon les domaines - [X] gérer le dés en +/- selon l'influence du totem adapté ou humain selon les domaines
- [ ] gérer les réussites auto - [X] gérer les réussites auto
- [ ] gérer les seuils auto si compétence non maitrisée - [X] gérer les seuils auto si compétence non maitrisée
### le combat ### le combat
- [X] modifier la difficulté en fonction de l'état du combatant /offensif/actif/passif/ - [X] modifier la difficulté en fonction de l'état du combatant /offensif/actif/passif/
### les items ### les items
- [X]ajouter apprentissage aux abilities - [X] ajouter apprentissage aux abilities
- [X] passer le type d'arme en select/options - [X] passer le type d'arme en select/options
- [X] ajouter handicap de rareté - [X] ajouter handicap de rareté
- [X] ajouter pour items Item "competence nécessaire" - [X] ajouter pour items Item "competence nécessaire"
- [-] gérer les rolls d'items dans le chat - [X] gérer les rolls d'items dans le chat
- [X] repasser sur les différents itemTypes et sheets - [X] repasser sur les différents itemTypes et sheets
- [X] verifier le selector de traits (trait pratique cf : msg pretre) - [X] verifier le selector de traits (trait pratique cf : msg pretre)
- [X] construire une selecteur de traits, traits= CONFIG.VERMINE.traits - [X] construire une selecteur de traits, traits= CONFIG.VERMINE.traits
File diff suppressed because it is too large Load Diff
+696
View File
@@ -0,0 +1,696 @@
# Guide Utilisateur - Vermine2047 pour FoundryVTT
## Table des matières
1. [Introduction](#introduction)
2. [Installation](#installation)
3. [Création d'un personnage](#création-dun-personnage)
4. [Les jets de dés](#les-jets-de-dés)
5. [Le système de totems](#le-système-de-totems)
6. [La gestion des groupes](#la-gestion-des-groupes)
7. [Le combat](#le-combat)
8. [Les items](#les-items)
9. [Gestion des PNJ et Créatures](#gestion-des-pnj-et-créatures)
10. [Astuces et bonnes pratiques](#astuces-et-bonnes-pratiques)
---
## Introduction
Bienvenue dans Vermine2047, un système pour FoundryVTT qui implémente les règles du jeu de rôle post-apocalyptique Vermine 2047.
### À propos de Vermine2047
Vermine 2047 est un jeu de rôle dans un monde post-apocalyptique où les joueurs incarnent des survivants dans un environnement hostile. Le système utilise des dés d10 avec un système de seuils de réussite et des mécaniques uniques comme les totems et les domaines de prédilection.
### Compatibilité
- **FoundryVTT**: v11 à v14
- **Version du système**: 0.1.14
---
## Installation
### Prérequis
- FoundryVTT installé (version 11 ou supérieure)
- Module "Dice So Nice!" recommandé pour les dés 3D
### Installation du système
1. **Via le compendium de Foundry**
- Ouvrez FoundryVTT
- Allez dans "Game Systems"
- Cliquez sur "Install System"
- Recherchez "Vermine2047"
- Cliquez sur "Install"
2. **Via l'URL du manifest**
- Allez dans "Game Systems"
- Cliquez sur "Install System"
- Dans l'onglet "From Manifest URL", entrez:
```
https://raw.githubusercontent.com/rwanoux/vermine2047/refs/heads/main/system.json
```
- Cliquez sur "Install"
3. **Créer un nouveau monde**
- Sélectionnez "Vermine2047" comme système
- Donnez un nom à votre monde
- Configurez les paramètres
### Configuration recommandée
- Activez le module "Dice So Nice!" pour les dés 3D
- Configurez le mode de jeu (Survie, Cauchemar, Apocalypse) dans les paramètres du monde
---
## Création d'un personnage
### Étape 1: Créer un acteur
1. Cliquez sur l'icône "Actors" dans la barre latérale gauche
2. Cliquez sur "Create Actor"
3. Sélectionnez "Character" comme type
4. Donnez un nom à votre personnage
### Étape 2: Choisir un totem
Le totem est au cœur de votre personnage et détermine ses affinités.
1. Dans l'onglet "Totem et ajustements"
2. Cliquez sur le bouton "choisissez un totem"
3. Sélectionnez un totem dans la liste
4. Cliquez sur "Sélectionner"
**Les 10 totems disponibles:**
- **Humain**: Favorise les compétences humaines et du monde civilisé
- **Prédateur**: Favorise la chasse et la survie
- **Charognard**: Favorise la récupération et l'utilisation d'outils
- **Symbiote**: Favorise les interactions sociales
- **Parasite**: Favorise la discrétion et la survie
- **Bâtisseur**: Favorise la construction et la manipulation
- **Horde**: Favorise le combat en groupe
- **Ruche**: Favorise l'organisation collective
- **Solitaire**: Favorise l'autonomie
- **Adapté**: Favorise l'adaptation à l'environnement
### Étape 3: Définir les caractéristiques
Dans l'onglet "Caractéristiques et compétences", vous trouverez 8 caractéristiques réparties en 4 catégories:
**Physique:**
- Vigueur: Résistance physique
- Santé: Résistance aux blessures
**Manuel:**
- Précision: Dextérité et coordination
- Réflexes: Réactivité et vitesse
**Mental:**
- Connaissance: Savoir et mémoire
- Perception: Observation et intuition
**Social:**
- Volonté: Détermination et courage
- Empathie: Compréhension des autres
**Conseil:** Commencez avec des valeurs de 1-2 pour un personnage équilibré, ou 3-4 pour un spécialiste.
### Étape 4: Définir les compétences
Chaque compétence a une valeur de 0 à 5:
- 0: Incompétent
- 1: Débutant
- 2: Compétent
- 3: Expert
- 4: Maître
- 5: Légende
**Catégories de compétences:**
- **Humain**: Arts, civilisation, psychologie, rumeurs, soins
- **Animal**: Animalisme, dissection, vie sauvage, répulsion, pistes
- **Outil**: Artisanat, bricolage, mécanique, pilotage, technologie
- **Arme**: Armes à feu, tir à l'arc, armurerie, lancer, mêlée
- **Survie**: Vigilance, athlétisme, nourriture, discrétion, corps à corps
- **Monde**: Environnement, flore, route, toxiques, ruines
**Astuce:** Le domaine de prédilection (sélectionnable en haut de chaque catégorie) donne des bonus quand il est aligné avec votre totem.
### Étape 5: Ajouter des spécialités
Les spécialités donnent +1D quand elles sont utilisées avec la compétence parente.
1. Dans l'onglet "Caractéristiques et compétences"
2. Cliquez sur l'icône "+" à côté d'une compétence
3. Une spécialité sera créée avec le nom de la compétence
4. Vous pouvez renommer la spécialité
### Étape 6: Définir les réserves
Dans l'onglet "Combat et réserves":
- **Effort**: Réserve pour les actions physiques (basée sur Vigueur + Santé + Réflexes + Précision)
- **Sang-Froid**: Réserve pour les actions mentales (basée sur Connaissance + Perception + Volonté + Empathie)
### Étape 7: Définir l'identité
Dans l'onglet "Histoire":
- Age
- Origine
- Profil
- Concept
- Instincts
- Interdits
- Objectifs
- Relations
- Biographie
### Étape 8: Équipement
Dans l'onglet "Matériel", vous pouvez ajouter:
- Armes
- Protections
- Véhicules
- Objets
---
## Les jets de dés
### Ouvrir le dialogue de jet
Il y a plusieurs façons de lancer un jet:
1. **Depuis la fiche de personnage**
- Cliquez sur une caractéristique ou une compétence
- Un dialogue de jet s'ouvre avec la caractéristique/compétence présélectionnée
2. **Depuis la barre d'outils**
- Cliquez sur l'icône de dés dans la barre d'outils (à droite)
- Un dialogue de jet vide s'ouvre
3. **Depuis le chat**
- Tapez `/roll` ou utilisez la commande de jet
### Le dialogue de jet
Le dialogue de jet a été conçu pour être intuitif et compact.
#### Sélection de base
- **Caractéristique**: Sélectionnez une caractéristique (Vigueur, Santé, etc.)
- **Compétence**: Sélectionnez une compétence (optionnel)
- **Score**: Affiche la valeur de la caractéristique sélectionnée
#### Difficulté et Handicap
- **Difficulté**: Sélectionnez le niveau de difficulté
- Evidente (3+): Tâche très simple
- Facile (5+): Tâche simple
- Difficile (7+): Tâche standard
- Très difficile (9+): Tâche complexe
- Impossible (10+): Tâche extrêmement difficile
- **Handicap**: Sélectionnez le niveau de handicap
- Aucun: Pas de handicap
- (I): Handicap mineur
- (II): Handicap majeur
#### Bonus
La section "Bonus" peut être dépliée pour accéder aux options supplémentaires:
- **Entraide**: +1D si quelqu'un vous aide
- **Groupe**: +0 à +5D basé sur la taille du groupe
- **Sang-Froid**: +0 à +5D (basé sur votre réserve de Sang-Froid)
- **Équipement**: +1D si vous utilisez un outil approprié
- **Dés de totem**: Cochez pour utiliser les dés de totem
- Totem humain: +XD (où X est la valeur de votre totem humain)
- Totem adapté: +XD (où X est la valeur de votre totem adapté)
**Astuce:** Si vous avez les deux totems (humain et adapté) avec des valeurs > 0, vous pouvez choisir quel totem garder après le jet.
#### Total du pool de dés
Le dialogue affiche le total du pool de dés en temps réel:
- **0D**: Aucune caractéristique sélectionnée
- **3D**: Caractéristique de valeur 3
- **4D**: Caractéristique 3 + Compétence 1
- **5D+**: Avec bonus
### Les bonus de domaine de totem
Votre totem influence vos jets en fonction du domaine de prédilection:
- Si votre domaine de prédilection est dans les domaines de votre totem, vous obtenez +1 dé
- Si votre domaine de prédilection est dans les domaines du totem opposé, vous subissez -1 dé
**Exemple:**
- Totem: Prédateur (domaines: animal, survie)
- Domaine de prédilection: Survie
- Bonus: +1 dé sur tous les jets de survie
### Les réussites automatiques
En fonction de votre niveau de maîtrise d'une compétence, vous obtenez des réussites automatiques:
| Niveau | Réussites automatiques | Avec spécialité |
|--------|------------------------|-----------------|
| Incompétent (0) | 0 | 0 |
| Débutant (1) | 0 | 0 |
| Compétent (2) | 0 | +1 |
| Expert (3) | +1 | +1 |
| Maître (4) | +1 | +2 |
| Légende (5) | +2 | +2 |
### Les seuils automatiques
Si vous n'êtes pas maîtrisé dans une compétence, un seuil plus élevé est automatiquement appliqué:
| Niveau | Seuil automatique |
|--------|-------------------|
| Incompétent (0) | 9 (Très difficile) |
| Débutant (1) | 7 (Difficile) |
| Compétent (2+) | Difficulté normale |
### Les dés de totem
Les dés de totem sont spéciaux:
- Ils comptent double en cas de réussite (2 réussites au lieu de 1)
- Ils sont de couleur différente pour les distinguer
- Vous pouvez utiliser les dés de totem humain et adapté simultanément
- Si vous utilisez les deux, vous pouvez choisir quel totem garder après le jet
**Exemple:**
- Pool: 3d10 + 1d10 totem humain
- Résultat: 4, 7, 2, 9 (totem humain)
- Si le seuil est 7: 2 réussites (7 et 9) + 2 réussites supplémentaires pour le 9 du totem = 4 réussites totales
### Relances
Les relances sont disponibles pour les compétences maîtrisées:
- **Niveau 2 (Compétent)**: 1 relance
- **Niveau 3 (Expert)**: 1 relance
- **Niveau 4 (Maître)**: 2 relances
- **Niveau 5 (Légende)**: 2 relances
Pour utiliser une relance:
1. Le MJ ou vous-même pouvez accorder des relances
2. Cliquez sur le dé que vous voulez relancer
3. Le dé sera marqué comme "rerolled"
4. Un nouveau jet sera effectué pour ce dé
**Astuce:** Vous pouvez aussi utiliser votre réserve de Sang-Froid pour obtenir des relances supplémentaires.
---
## Le système de totems
### Sélection du totem
Le totem est choisi lors de la création du personnage et détermine:
- Vos affinités naturelles
- Vos bonus de domaine
- Votre perception du monde
### Gestion des dés de totem
Dans la fiche de personnage, onglet "Totem et ajustements":
- Vous voyez les valeurs de vos totems humain et adapté (0-3 chacun)
- La somme maximale est de 5 (ex: 3 humain + 2 adapté)
- Cliquez sur les flèches pour ajuster les valeurs
**Attention:** La somme des deux totems ne peut pas dépasser 5.
### Domaines de prédilection
Chaque catégorie de compétence peut être votre domaine de prédilection:
- Humain
- Animal
- Outil
- Arme
- Survie
- Monde
Pour définir votre domaine de prédilection:
1. Dans l'onglet "Caractéristiques et compétences"
2. Cliquez sur le bouton radio à côté du nom de la catégorie
3. La catégorie sélectionnée devient votre domaine de prédilection
**Bonus:** Si votre domaine de prédilection est dans les domaines de votre totem, vous obtenez des bonus supplémentaires.
### Totems et PNJ/Créatures
Les PNJ et créatures peuvent aussi avoir des totems, qui influencent leurs caractéristiques et comportements.
---
## La gestion des groupes
### Qu'est-ce qu'un groupe?
Un groupe représente:
- Une communauté de survivants
- Un clan
- Une bande
- Une famille élargie
### Créer un groupe
1. Cliquez sur "Create Actor"
2. Sélectionnez "Group" comme type
3. Donnez un nom au groupe
4. Définissez le totem du groupe
5. Ajoutez des membres
### Ajouter des membres à un groupe
1. Ouvrez la fiche du groupe
2. Dans l'onglet "Membres", cliquez sur "+ Ajouter un membre"
3. Sélectionnez le personnage dans la liste
4. Cliquez sur "Ajouter"
**Synchronisation automatique:** Quand vous ajoutez un personnage à un groupe, le groupe apparaît automatiquement dans la fiche du personnage.
### Gérer les rencontres
Les "rencontres" représentent les PNJ et créatures associés à un groupe:
1. Ouvrez la fiche du groupe
2. Dans l'onglet "Rencontres", cliquez sur "+ Ajouter une rencontre"
3. Sélectionnez le PNJ ou la créature
4. Cliquez sur "Ajouter"
### Retirer un personnage d'un groupe
1. Ouvrez la fiche du groupe
2. Trouvez le membre dans la liste
3. Cliquez sur l'icône de suppression (poubelle)
4. Confirmez
**Synchronisation automatique:** Le personnage sera aussi retiré de la liste des groupes dans sa fiche.
### Mode de jeu
La fiche de personnage a deux modes:
- **Mode Edit**: Tous les champs sont modifiables
- **Mode Jeu**: Les champs sont désactivés pour éviter les modifications accidentelles
Pour basculer entre les modes:
- Cliquez sur l'icône de cadenas en haut de la fiche
---
## Le combat
### Initiative
L'initiative dans Vermine2047 est basée sur:
- Caractéristique: Réflexes
- Compétence: Vigilance
- Statut de combat: Offensif (+), Actif (neutre), Passif (-)
**Formule:** `(Réflexes + Vigilance)d10cs>=difficulté`
**Difficultés par statut:**
- Offensif: 5 (facile)
- Actif: 7 (standard)
- Passif: 9 (difficile)
### Statuts de combat
Chaque participant au combat a un statut:
- **Offensif**: Agressif, prend des risques
- **Actif**: Équilibré, réactif
- **Passif**: Défensif, prudent
Pour changer le statut:
1. Dans le combat tracker
2. Cliquez sur le nom du participant
3. Sélectionnez le nouveau statut
### Tracker de combat
Le tracker de combat affiche:
- L'ordre d'initiative
- Le statut de chaque participant
- Les points de vie
- Les réserves
### Actions de combat
Les actions de combat fonctionnent comme les jets de dés normaux, mais avec:
- Des bonus spécifiques au combat
- Des modifications de difficulté basées sur le statut
---
## Les items
### Types d'items
1. **Objet (item)**: Objet général (nourriture, outils, etc.)
2. **Arme (weapon)**: Arme de mêlée ou à distance
3. **Protection (defense)**: Armure, bouclier, etc.
4. **Véhicule (vehicle)**: Transport
5. **Capacité (ability)**: Compétence spéciale
6. **Spécialité (specialty)**: Spécialisation dans une compétence
7. **Historique (background)**: Historique du personnage
8. **Traumatisme (trauma)**: Traumatisme psychologique
9. **Évolution (evolution)**: Évolution du personnage
10. **Rumeur (rumor)**: Information
11. **Cible (target)**: Objectif
12. **Rite (rite)**: Rituel
### Créer un item
1. Dans la fiche du personnage
2. Allez dans l'onglet approprié (Matériel, Totem et ajustements, etc.)
3. Cliquez sur l'icône "+" à côté du titre de la section
4. Sélectionnez le type d'item
5. Remplissez les informations
### Utiliser un item dans le chat
1. Glissez-déposez l'item depuis votre fiche vers le chat
2. Ou cliquez sur l'icône de l'item et sélectionnez "Post to Chat"
3. Une carte avec les informations de l'item sera affichée
### Caractéristiques des items
**Tous les items:**
- Description
- Rareté (0-5)
- Fiabilité
- Handicap de rareté
- Quantité
- Poids
- Traits
- Dégâts
**Armes:**
- Portée min/max
- Dégâts (valeur, type, bonus de vigueur)
- Munitions
**Protections:**
- Niveau
- Niveau spécifique
- Mobilité
- Bouclier (oui/non)
**Capacités:**
- Type (personnage, groupe, créature, totem)
- Totem
- Niveau
- Seuil d'apprentissage
- Handicap d'apprentissage
- Effets
---
## Gestion des PNJ et Créatures
### Créer un PNJ
1. Cliquez sur "Create Actor"
2. Sélectionnez "NPC" comme type
3. Donnez un nom au PNJ
4. Configurez les attributs:
- Menace (1-4): Niveau de dangerosité
- Expérience (1-4): Niveau d'expérience
- Rôle (1-4): Importance dans le scénario
5. Ajoutez des compétences si nécessaire
6. Ajoutez de l'équipement
### Créer une créature
1. Cliquez sur "Create Actor"
2. Sélectionnez "Creature" comme type
3. Donnez un nom à la créature
4. Configurez les attributs:
- Gabarit (1-4): Type de créature
- Taille (1-3): Taille physique
- Rôle (1-4): Importance dans le scénario
- Meute (0-3): Taille du groupe
- Modes: Types de scénarios où la créature apparaît
5. Ajoutez des compétences si nécessaire
### Menace, Expérience et Rôle (PNJ)
Ces trois attributs déterminent les capacités du PNJ:
**Menace:**
- Mineure (1): Peu dangereuse
- Sérieuse (2): Dangereuse
- Majeure (3): Très dangereuse
- Mortelle (4): Extrêmement dangereuse
**Expérience:**
- Débutant (1): Peu expérimenté
- Compétent (2): Expérimenté
- Expert (3): Très expérimenté
- Maître (4): Maître dans son domaine
**Rôle:**
- Mineur (1): Personnage secondaire
- Secondaire (2): Personnage important
- Important (3): Personnage principal
- Majeur (4): Antagoniste principal
### Gabarit, Taille, Rôle et Meute (Créature)
**Gabarit:**
- Insecte (1): Très petit
- Rat (2): Petit
- Chien (3): Moyen
- Ours (4): Grand
**Taille:**
- Petit (1)
- Moyen (2)
- Grand (3)
**Rôle:**
- Mineur (1): Créature secondaire
- Secondaire (2): Créature importante
- Important (3): Créature principale
- Majeur (4): Boss
**Meute:**
- Solitaire (0): Agit seul
- Petit groupe (1)
- Groupe (2)
- Grande meute (3)
**Modes:**
- Survie: Agit dans des scénarios de survie
- Cauchemar: Agit dans des scénarios de cauchemar
- Apocalypse: Agit dans des scénarios d'apocalypse
---
## Astuces et bonnes pratiques
### Pour les Joueurs
1. **Choisissez un totem qui correspond à votre style de jeu**
- Humain: Pour les sociaux et les civils
- Prédateur: Pour les chasseurs et les guerriers
- Adapté: Pour les polyvalents
2. **Définissez un domaine de prédilection**
- Cela vous donnera des bonus avec votre totem
3. **Utilisez les spécialités**
- Les spécialités donnent +1D et des réussites automatiques
4. **Gérez vos réserves**
- Effort: Pour les actions physiques
- Sang-Froid: Pour les actions mentales et les relances
5. **Utilisez les dés de totem**
- Ils comptent double en cas de réussite
- Vous pouvez utiliser les deux totems simultanément
### Pour les MJ
1. **Créez des groupes pour organiser vos PNJ**
- Les groupes permettent de gérer plusieurs PNJ ensemble
- Les rencontres dans un groupe sont synchronisées
2. **Utilisez les statuts de combat**
- Offensif pour les personnages agressifs
- Actif pour les personnages équilibrés
- Passif pour les personnages défensifs
3. **Configurez correctement les PNJ et créatures**
- Menace/Expérience/Rôle pour les PNJ
- Gabarit/Taille/Rôle/Meute pour les créatures
4. **Utilisez les modes pour les créatures**
- Cela permet de filtrer les créatures par type de scénario
5. **Encouragez l'utilisation des domaines de prédilection**
- Cela rend le système de totems plus impactant
### Pour les développeurs
1. **Utilisez les helpers Handlebars**
- De nombreux helpers sont disponibles pour afficher les données
- `skillLevel`, `threatLevel`, etc.
2. **Respectez les conventions de nommage**
- `vermine-` préfixe pour les classes CSS
- `VERMINE` namespace pour les configurations
3. **Utilisez GroupLink pour la synchronisation**
- Ne modifiez pas directement les tableaux de membres/rencontres
- Utilisez les méthodes de GroupLink
---
## Résolution des problèmes
### Problèmes courants
1. **Les dés de totem ne fonctionnent pas**
- Vérifiez que les valeurs des totems sont > 0
- Vérifiez que la somme des totems ne dépasse pas 5
2. **Les bonus de domaine ne s'appliquent pas**
- Vérifiez que vous avez défini un domaine de prédilection
- Vérifiez que votre totem a des domaines configurés
3. **Les groupes ne se synchronisent pas**
- Vérifiez que GroupLink est bien initialisé
- Vérifiez que les hooks sont actifs
4. **Les templates ne s'affichent pas correctement**
- Vérifiez que les templates sont en `.hbs`
- Vérifiez que les références sont correctes
### Contact
Pour de l'aide ou pour signaler un problème:
- Rejoignez le Discord Vermine: https://discord.gg/qejqmSxr
- Rejoignez le Discord Foundry Vermine: https://discord.gg/FqGHYvXg
---
## Licence
Vermine2047 System est sous licence MIT. Voir le fichier LICENSE.txt pour plus de détails.
---
## Auteurs
- François-Xavier Guillois
- Rwanoux (Discord: rwanoux)
- Pretre (Discord: pretre)
---
*Guide mis à jour: 2026-06-04*
*Version: 0.1.14*
+23
View File
@@ -159,6 +159,28 @@
"heavy_wounds": "Blessure grave", "heavy_wounds": "Blessure grave",
"deadly_wounds": "Blessure mortelle" "deadly_wounds": "Blessure mortelle"
}, },
"choose_ability": "Choisissez une caractéristique",
"choose_skill": "Choisissez une compétence",
"none": "Aucun",
"total": "Total",
"bonuses": "Bonus",
"handicap": "Handicap",
"score": "Score",
"totem_dice": "Dés de totem",
"keep_totem": "Garder le totem",
"totem_hint": "Cochez pour utiliser les dés de totem (double réussite possible)",
"error_not_enough_self_control": "Vous n'avez pas assez de Sang-Froid",
"error_select_ability": "Veuillez sélectionner une caractéristique",
"error_select_skill": "Veuillez sélectionner une compétence",
"needed": "nécessaire",
"cost": "Coût",
"learn": "Apprentissage",
"ritual": "Rituel",
"trance": "Transe",
"effect": "Effet",
"types": {
"shield": "Bouclier"
},
"tabs": { "tabs": {
"abilities": "Caractéristiques et compétences", "abilities": "Caractéristiques et compétences",
"totem": "Totem et ajustements", "totem": "Totem et ajustements",
@@ -223,6 +245,7 @@
"abilities": "Capacités", "abilities": "Capacités",
"specialties": "Spécialités", "specialties": "Spécialités",
"new_specialty": "Nouvelle spécialité", "new_specialty": "Nouvelle spécialité",
"shield": "Bouclier",
"evolution": "Adaptation", "evolution": "Adaptation",
"new_evolution": "Nouvelle adaptation", "new_evolution": "Nouvelle adaptation",
"evolutions": "Adaptations", "evolutions": "Adaptations",
+1 -1
View File
@@ -86,7 +86,7 @@ export class VermineItem extends Item {
rollMode: rollMode, rollMode: rollMode,
flavor: label, flavor: label,
}; };
mess.content = await renderTemplate(`systems/vermine2047/templates/item/chatCards/${this.type}.html`, { item: this, message: mess }) ?? null; mess.content = await renderTemplate(`systems/vermine2047/templates/item/chatCards/${this.type}.hbs`, { item: this, message: mess }) ?? null;
ChatMessage.create(mess) ChatMessage.create(mess)
} }
+136
View File
@@ -68,6 +68,142 @@ VERMINE.PackLevels = {
3: { "attack": 5, "damage": 5, "minorWound": 3, "majorWound": 3, "deadlyWound": 3 } 3: { "attack": 5, "damage": 5, "minorWound": 3, "majorWound": 3, "deadlyWound": 3 }
} }
/**
* Domains of influence for each totem
* Each totem provides bonus to certain skill categories
*/
VERMINE.totemDomains = {
"human": {
"label": "TOTEMS.human.name",
"domains": ["man", "world"],
"bonus": +1,
"description": "Le totem humain favorise les compétences liées à l'humanité et au monde civilisé"
},
"predator": {
"label": "TOTEMS.predator.name",
"domains": ["animal", "survival"],
"bonus": +1,
"description": "Le totem prédateur favorise la chasse et la survie"
},
"scavenger": {
"label": "TOTEMS.scavenger.name",
"domains": ["tool", "world"],
"bonus": +1,
"description": "Le totem charognard favorise la récupération et l'utilisation d'outils"
},
"symbiote": {
"label": "TOTEMS.symbiote.name",
"domains": ["man", "social"],
"bonus": +1,
"description": "Le totem symbiote favorise les interactions sociales"
},
"parasite": {
"label": "TOTEMS.parasite.name",
"domains": ["animal", "survival"],
"bonus": +1,
"description": "Le totem parasite favorise la discrétion et la survie"
},
"builder": {
"label": "TOTEMS.builder.name",
"domains": ["tool", "world"],
"bonus": +1,
"description": "Le totem bâtisseur favorise la construction et la manipulation"
},
"horde": {
"label": "TOTEMS.horde.name",
"domains": ["animal", "survival"],
"bonus": +1,
"description": "Le totem horde favorise le combat en groupe"
},
"hive": {
"label": "TOTEMS.hive.name",
"domains": ["man", "social"],
"bonus": +1,
"description": "Le totem ruche favorise l'organisation collective"
},
"loner": {
"label": "TOTEMS.loner.name",
"domains": ["survival", "world"],
"bonus": +1,
"description": "Le totem solitaire favorise l'autonomie"
},
"adapted": {
"label": "TOTEMS.adapted.name",
"domains": ["animal", "survival"],
"bonus": +1,
"description": "Le totem adapté favorise l'adaptation à l'environnement"
}
}
/**
* NPC Threat Levels configuration
*/
VERMINE.npcThreatLevels = {
1: { "label": "THREAT_LEVELS.minor", "attack": 3, "vigor": 1, "minorWound": 1, "majorWound": 1, "deadlyWound": 1 },
2: { "label": "THREAT_LEVELS.serious", "attack": 4, "vigor": 2, "minorWound": 2, "majorWound": 1, "deadlyWound": 1 },
3: { "label": "THREAT_LEVELS.major", "attack": 5, "vigor": 3, "minorWound": 2, "majorWound": 1, "deadlyWound": 1 },
4: { "label": "THREAT_LEVELS.deadly", "attack": 6, "vigor": 4, "minorWound": 2, "majorWound": 2, "deadlyWound": 2 }
}
/**
* NPC Experience Levels configuration
*/
VERMINE.npcExperienceLevels = {
1: { "label": "SKILL_LEVELS.beginner", "action": 3, "specialties": 4, "rerolls": 0, "contact": "7" },
2: { "label": "SKILL_LEVELS.proficient", "action": 3, "specialties": 5, "rerolls": 0, "contact": "5 ou 7" },
3: { "label": "SKILL_LEVELS.expert", "action": 4, "specialties": 6, "rerolls": 1, "contact": "5,7 ou 9" },
4: { "label": "SKILL_LEVELS.master", "action": 4, "specialties": 6, "rerolls": 2, "contact": "3,5,7 ou 9" }
}
/**
* NPC Role Levels configuration
*/
VERMINE.npcRoleLevels = {
1: { "label": "ROLE_LEVELS.minor", "reaction": 3, "reaction_bonus": 0, "pools": 0, "gear": 9, "gear_hindrance": 0, "protection": 1 },
2: { "label": "ROLE_LEVELS.secondary", "reaction": 3, "reaction_bonus": 1, "pools": 1, "gear": 9, "gear_hindrance": 1, "protection": 2 },
3: { "label": "ROLE_LEVELS.important", "reaction": 3, "reaction_bonus": 2, "pools": 2, "gear": 9, "gear_hindrance": 2, "protection": 3 },
4: { "label": "ROLE_LEVELS.major", "reaction": 4, "reaction_bonus": 2, "pools": 4, "gear": 10, "gear_hindrance": 2, "protection": 3 }
}
/**
* Creature Pattern Levels configuration
*/
VERMINE.creaturePatternLevels = {
1: { "label": "PATTERN_LEVELS.insect", "attack": 2, "damage": 0, "minorWound": 0, "majorWound": 0, "deadlyWound": 1 },
2: { "label": "PATTERN_LEVELS.rat", "attack": 3, "damage": 1, "minorWound": 0, "majorWound": 1, "deadlyWound": 1 },
3: { "label": "PATTERN_LEVELS.dog", "attack": 4, "damage": 3, "minorWound": 1, "majorWound": 1, "deadlyWound": 1 },
4: { "label": "PATTERN_LEVELS.bear", "attack": 6, "damage": 6, "minorWound": 2, "majorWound": 2, "deadlyWound": 2 }
}
/**
* Creature Size Levels configuration
*/
VERMINE.creatureSizeLevels = {
1: { "attack": 2, "vigor": 1, "minorWound": 0, "majorWound": 0, "deadlyWound": 1 },
2: { "attack": 3, "vigor": 2, "minorWound": 0, "majorWound": 1, "deadlyWound": 1 },
3: { "attack": 4, "vigor": 3, "minorWound": 1, "majorWound": 1, "deadlyWound": 1 }
}
/**
* Creature Pack Levels configuration
*/
VERMINE.creaturePackLevels = {
0: { "attack": 0, "damage": 0, "minorWound": 0, "majorWound": 0, "deadlyWound": 0 },
1: { "attack": 1, "damage": 1, "minorWound": 0, "majorWound": 0, "deadlyWound": 1 },
2: { "attack": 2, "damage": 2, "minorWound": 2, "majorWound": 2, "deadlyWound": 2 },
3: { "attack": 5, "damage": 5, "minorWound": 3, "majorWound": 3, "deadlyWound": 3 }
}
/**
* Creature Role Levels configuration (same as NPC roles)
*/
VERMINE.creatureRoleLevels = {
1: { "label": "ROLE_LEVELS.minor", "reaction": 3, "reaction_bonus": 0, "pools": 0, "gear": 9, "gear_hindrance": 0, "protection": 1 },
2: { "label": "ROLE_LEVELS.secondary", "reaction": 3, "reaction_bonus": 1, "pools": 1, "gear": 9, "gear_hindrance": 1, "protection": 2 },
3: { "label": "ROLE_LEVELS.important", "reaction": 3, "reaction_bonus": 2, "pools": 2, "gear": 9, "gear_hindrance": 2, "protection": 3 },
4: { "label": "ROLE_LEVELS.major", "reaction": 4, "reaction_bonus": 2, "pools": 4, "gear": 10, "gear_hindrance": 2, "protection": 3 }
}
VERMINE.abilityCategories = { VERMINE.abilityCategories = {
"physical": { "physical": {
"label": "VERMINE.ability_category.physical" "label": "VERMINE.ability_category.physical"
+225 -10
View File
@@ -110,13 +110,19 @@ export default class RollDialog extends Dialog {
async activateListeners(html) { async activateListeners(html) {
// Activate event listeners from the superclass // Activate event listeners from the superclass
super.activateListeners(html); super.activateListeners(html);
// Initialize UI elements
this._html = html;
// Retrieve roll data and set up event listeners // Retrieve roll data and set up event listeners
await this.getRollData(); await this.getRollData();
let rollInputs = html.find('[data-roll');
// Set up event listeners for all roll-related inputs
let rollInputs = html.find('[data-roll]');
for (let inp of rollInputs) { for (let inp of rollInputs) {
// Add event listener for roll input changes inp.addEventListener('change', this._onRollInputChange.bind(this));
inp.addEventListener('change', await this.getRollData.bind(this))
}; };
this.displaySpecialties(); this.displaySpecialties();
let selectAbil = html.find('#ability')[0]; let selectAbil = html.find('#ability')[0];
@@ -126,6 +132,19 @@ export default class RollDialog extends Dialog {
let selfControl = html.find('#self_control')[0] let selfControl = html.find('#self_control')[0]
// Add event listener for self control changes // Add event listener for self control changes
selfControl.addEventListener('change', this._onChangeSelfControl.bind(this)); selfControl.addEventListener('change', this._onChangeSelfControl.bind(this));
// Set up difficulty change listener
html.find('#difficulty')[0].addEventListener('change', this._onDifficultyChange.bind(this));
// Set up handicap change listener
html.find('#handicap')[0].addEventListener('change', this._onHandicapChange.bind(this));
// Set up totem checkbox listeners
html.find('#human-totem')[0]?.addEventListener('change', this._onTotemChange.bind(this));
html.find('#adapted-totem')[0]?.addEventListener('change', this._onTotemChange.bind(this));
// Initial update of all UI elements
this._updateUI();
}; };
@@ -134,7 +153,6 @@ export default class RollDialog extends Dialog {
* @param {Event} ev - The event triggering the roll data retrieval. * @param {Event} ev - The event triggering the roll data retrieval.
*/ */
async getRollData(ev) { async getRollData(ev) {
console.log(this)
// Calculate and store the roll data // Calculate and store the roll data
this.rollData = { this.rollData = {
actor: this.data.actor, actor: this.data.actor,
@@ -146,10 +164,200 @@ export default class RollDialog extends Dialog {
rollLabel: this.getLabel(), rollLabel: this.getLabel(),
totems: this.getTotems(), totems: this.getTotems(),
self_control: this.getSelfControl(), self_control: this.getSelfControl(),
max_effort: this.getMaxEffort() max_effort: this.getMaxEffort(),
keepTotem: this.getKeepTotem(),
skillCategory: this.getSkillCategory()
} }
this.displaySpecialties(); this.displaySpecialties();
this._updateUI();
}; };
/**
* Gets the selected skill category
* @returns {string|null} - The skill category
*/
getSkillCategory() {
const html = this.element[0];
const skillSelect = html.querySelector('#skill');
if (skillSelect && skillSelect.selectedIndex > 0) {
const selectedOption = skillSelect.options[skillSelect.selectedIndex];
return selectedOption.dataset.category || null;
}
return null;
}
/**
* Gets the selected skill level
* @returns {number|null} - The skill level
*/
getSkillLevel() {
const html = this.element[0];
const skillSelect = html.querySelector('#skill');
if (skillSelect && skillSelect.selectedIndex > 0) {
const selectedOption = skillSelect.options[skillSelect.selectedIndex];
return parseInt(selectedOption.value) || null;
}
return null;
}
/**
* Checks if a specialty is selected
* @returns {boolean} - True if a specialty is selected
*/
hasSpecialtySelected() {
const html = this.element[0];
const specialtyRadio = html.querySelector('input[name="usingSpecialization"]:checked');
return specialtyRadio && specialtyRadio.value !== 'aucune';
}
/**
* Handles changes to roll inputs and updates UI
* @param {Event} ev - The change event
*/
async _onRollInputChange(ev) {
await this.getRollData();
}
/**
* Updates all UI elements based on current roll data
*/
_updateUI() {
if (!this._html) return;
const html = this._html[0];
// Update total dice pool display
const totalDice = this.getDicePool();
const totalEl = html.querySelector('#dice-pool-total');
if (totalEl) {
totalEl.textContent = `${totalDice}D`;
}
// Update bonus count
const bonusCount = this._calculateBonusCount();
const bonusEl = html.querySelector('#total-bonus');
if (bonusEl) {
bonusEl.textContent = bonusCount;
}
// Update difficulty display
const difficultyEl = html.querySelector('#current-difficulty');
const difficultySelect = html.querySelector('#difficulty');
if (difficultyEl && difficultySelect) {
const selectedIndex = difficultySelect.selectedIndex;
const diffValue = parseInt(difficultySelect.options[selectedIndex].value);
const diffLabel = difficultySelect.options[selectedIndex].text.split(' ')[0];
difficultyEl.textContent = `${diffLabel} (${diffValue})`;
}
// Update handicap display
const handicapEl = html.querySelector('#current-handicap');
const handicapSelect = html.querySelector('#handicap');
if (handicapEl && handicapSelect) {
const selectedIndex = handicapSelect.selectedIndex;
handicapEl.textContent = handicapSelect.options[selectedIndex].text;
}
// Update ability score display
const abilSelect = html.querySelector('#ability');
const abilScoreEl = html.querySelector('#abilityScoreValue');
if (abilSelect && abilScoreEl) {
const selectedIndex = abilSelect.selectedIndex;
if (selectedIndex > 0) {
abilScoreEl.textContent = abilSelect.options[selectedIndex].value;
} else {
abilScoreEl.textContent = '0';
}
}
// Update specialty display
const specialtyRadios = html.querySelectorAll('input[name="usingSpecialization"]:checked');
const currentSpecEl = html.querySelector('.current-specialty');
if (currentSpecEl && specialtyRadios.length > 0) {
const checkedRadio = specialtyRadios[0];
currentSpecEl.textContent = checkedRadio.value === 'aucune' ? game.i18n.localize('VERMINE.none') : checkedRadio.value;
}
}
/**
* Calculates the bonus count for display
* @returns {number} - Total bonus dice
*/
_calculateBonusCount() {
let bonus = 0;
// Help bonus
if (this._html.find('#helped')[0]?.checked) {
bonus += 1;
}
// Group bonus
const groupValue = parseInt(this._html.find('#group')[0]?.value) || 0;
bonus += groupValue;
// Self control bonus
const selfControlValue = parseInt(this._html.find('#self_control')[0]?.value) || 0;
bonus += selfControlValue;
// Tools bonus
const toolsChecked = this._html.find('input[name="usingTools"]:checked')[0]?.value !== '0';
if (toolsChecked) {
bonus += 1;
}
// Totems bonus
if (this._html.find('#human-totem')[0]?.checked) {
bonus += parseInt(this.data.actor.system.adaptation.totems.human.value) || 0;
}
if (this._html.find('#adapted-totem')[0]?.checked) {
bonus += parseInt(this.data.actor.system.adaptation.totems.adapted.value) || 0;
}
// Specialty bonus
const specialtyChecked = this._html.find('input[name="usingSpecialization"]:checked')[0]?.value !== 'aucune';
if (specialtyChecked) {
bonus += 1;
}
return bonus;
}
/**
* Handles difficulty change
* @param {Event} ev - The change event
*/
_onDifficultyChange(ev) {
this._updateUI();
}
/**
* Handles handicap change
* @param {Event} ev - The change event
*/
_onHandicapChange(ev) {
this._updateUI();
}
/**
* Handles totem checkbox change
* @param {Event} ev - The change event
*/
_onTotemChange(ev) {
this._updateUI();
}
/**
* Gets the selected totem to keep (for dual totem rolls)
* @returns {string|null} - The totem to keep ('human', 'adapted', or null)
*/
getKeepTotem() {
const keepTotemSelect = this._html?.find('#keep-totem-select')[0];
if (keepTotemSelect) {
return keepTotemSelect.value;
}
// Default to null (both totems used)
return null;
}
/** /**
@@ -300,7 +508,7 @@ export default class RollDialog extends Dialog {
// Check if the actor has enough self control // Check if the actor has enough self control
if (this.rollData.actor.system.attributes.self_control.value < this.rollData.self_control) { if (this.rollData.actor.system.attributes.self_control.value < this.rollData.self_control) {
// Display a warning message if self control is insufficient // Display a warning message if self control is insufficient
ui.notifications.warn('vous navez pas assez de sang-froid'); ui.notifications.warn(game.i18n.localize('VERMINE.error_not_enough_self_control'));
// Re-render the dialog // Re-render the dialog
this.render(true); this.render(true);
return false; // Exit the function if self control is insufficient return false; // Exit the function if self control is insufficient
@@ -308,9 +516,9 @@ export default class RollDialog extends Dialog {
} }
let caracName = this.element[0].querySelector('[name="ability"]')?.value let caracName = this.element[0].querySelector('[name="ability"]')?.value
if (caracName == "0") { if (caracName == "0" || caracName === undefined) {
// Display a warning message if no ability selected // Display a warning message if no ability selected
ui.notifications.warn('selectionnez une caractéristique.'); ui.notifications.warn(game.i18n.localize('VERMINE.error_select_ability'));
// Re-render the dialog // Re-render the dialog
this.render(true); this.render(true);
return false; // Exit the function if no ability return false; // Exit the function if no ability
@@ -318,10 +526,17 @@ export default class RollDialog extends Dialog {
// Deduct self control points if necessary // Deduct self control points if necessary
if (this.rollData.self_control > 0) { if (this.rollData.self_control > 0) {
// Update the actor's self control value // Update the actor's self control value
await this.rollData.actor.update({ "system.attributes.self_control.value": this.rollData.actor.system.attributes.self_control.value - this.rollData.self_control }); await this.rollData.actor.update({
"system.attributes.self_control.value":
this.rollData.actor.system.attributes.self_control.value - this.rollData.self_control
});
} }
// Perform the dice roll using VermineUtils // Perform the dice roll using VermineUtils
return VermineUtils.roll({ ...this.rollData }); return VermineUtils.roll({
...this.rollData,
skillLevel: this.getSkillLevel(),
hasSpecialty: this.hasSpecialtySelected()
});
} }
} }
+414
View File
@@ -0,0 +1,414 @@
/**
* GroupLink - Gestion des liens entre acteurs et groupes
*
* Cette classe permet de gérer les relations bidirectionnelles entre :
* - Les personnages (characters) et leurs groupes/rencontres
* - Les groupes (groups) et leurs membres/rencontres
*
* @author Vermine2047 System
*/
export class GroupLink {
/**
* Met à jour les groupes dans tous les personnages membres
* quand un groupe est modifié
* @param {Actor} group - Le groupe modifié
* @param {Object} changes - Les changements effectués
*/
static async updateActorsOnGroupChange(group, changes) {
if (group.type !== 'group') return;
const groupData = group.system;
const members = groupData.members || [];
const encounters = groupData.encounters || [];
// Mettre à jour les membres du groupe
if (changes.members !== undefined || changes.encounters !== undefined) {
await this._updateMembersInGroup(group, members);
await this._updateEncountersInGroup(group, encounters);
}
// Synchroniser les données dans les acteurs membres
await this._syncGroupToMembers(group, members);
await this._syncGroupToEncounters(group, encounters);
}
/**
* Met à jour le groupe quand un personnage est modifié
* @param {Actor} actor - L'acteur modifié
* @param {Object} changes - Les changements effectués
*/
static async updateGroupsOnActorChange(actor, changes) {
if (actor.type === 'group') return;
const actorData = actor.system;
const encounters = actorData.encounters || [];
// Si les rencontres de l'acteur ont changé
if (changes.encounters !== undefined) {
// Pour chaque groupe dans les rencontres, mettre à jour les membres
for (const groupId of encounters) {
const group = game.actors.get(groupId);
if (group && group.type === 'group') {
await this._updateActorInGroupMembers(group, actor.id);
}
}
}
}
/**
* Synchronise les données du groupe vers les acteurs membres
* @param {Actor} group - Le groupe
* @param {Array} memberIds - Liste des IDs des membres
*/
static async _syncGroupToMembers(group, memberIds) {
for (const memberId of memberIds) {
const member = game.actors.get(memberId);
if (member) {
// Vérifier que le groupe est dans les rencontres du membre
const memberEncounters = member.system.encounters || [];
if (!memberEncounters.includes(group.id)) {
// Ajouter le groupe aux rencontres du membre
memberEncounters.push(group.id);
await member.update({
'system.encounters': memberEncounters
});
}
}
}
}
/**
* Synchronise les données du groupe vers les acteurs rencontres
* @param {Actor} group - Le groupe
* @param {Array} encounterIds - Liste des IDs des rencontres
*/
static async _syncGroupToEncounters(group, encounterIds) {
for (const encounterId of encounterIds) {
const encounter = game.actors.get(encounterId);
if (encounter) {
// Vérifier que le groupe est dans les rencontres de l'acteur
const encounterGroups = encounter.system.encounters || [];
if (!encounterGroups.includes(group.id)) {
encounterGroups.push(group.id);
await encounter.update({
'system.encounters': encounterGroups
});
}
}
}
}
/**
* Met à jour les membres dans un groupe
* @param {Actor} group - Le groupe
* @param {Array} memberIds - Liste des IDs des membres
*/
static async _updateMembersInGroup(group, memberIds) {
const currentMembers = group.system.members || [];
// Retirer les membres qui ne sont plus dans la liste
const membersToRemove = currentMembers.filter(id => !memberIds.includes(id));
const membersToAdd = memberIds.filter(id => !currentMembers.includes(id));
// Mettre à jour les acteurs qui ont été retirés
for (const memberId of membersToRemove) {
const member = game.actors.get(memberId);
if (member) {
const memberEncounters = (member.system.encounters || []).filter(id => id !== group.id);
await member.update({
'system.encounters': memberEncounters
});
}
}
// Mettre à jour les nouveaux membres
for (const memberId of membersToAdd) {
const member = game.actors.get(memberId);
if (member) {
const memberEncounters = member.system.encounters || [];
if (!memberEncounters.includes(group.id)) {
memberEncounters.push(group.id);
await member.update({
'system.encounters': memberEncounters
});
}
}
}
}
/**
* Met à jour les rencontres dans un groupe
* @param {Actor} group - Le groupe
* @param {Array} encounterIds - Liste des IDs des rencontres
*/
static async _updateEncountersInGroup(group, encounterIds) {
const currentEncounters = group.system.encounters || [];
// Retirer les rencontres qui ne sont plus dans la liste
const encountersToRemove = currentEncounters.filter(id => !encounterIds.includes(id));
const encountersToAdd = encounterIds.filter(id => !currentEncounters.includes(id));
// Mettre à jour les acteurs qui ont été retirés des rencontres
for (const encounterId of encountersToRemove) {
const encounter = game.actors.get(encounterId);
if (encounter) {
const encounterGroups = (encounter.system.encounters || []).filter(id => id !== group.id);
await encounter.update({
'system.encounters': encounterGroups
});
}
}
// Mettre à jour les nouvelles rencontres
for (const encounterId of encountersToAdd) {
const encounter = game.actors.get(encounterId);
if (encounter) {
const encounterGroups = encounter.system.encounters || [];
if (!encounterGroups.includes(group.id)) {
encounterGroups.push(group.id);
await encounter.update({
'system.encounters': encounterGroups
});
}
}
}
}
/**
* Met à jour un acteur dans les membres d'un groupe
* @param {Actor} group - Le groupe
* @param {string} actorId - L'ID de l'acteur
*/
static async _updateActorInGroupMembers(group, actorId) {
const groupMembers = group.system.members || [];
if (!groupMembers.includes(actorId)) {
groupMembers.push(actorId);
await group.update({
'system.members': groupMembers
});
}
}
/**
* Met à jour un acteur dans les rencontres d'un groupe
* @param {Actor} group - Le groupe
* @param {string} actorId - L'ID de l'acteur
*/
static async _updateActorInGroupEncounters(group, actorId) {
const groupEncounters = group.system.encounters || [];
if (!groupEncounters.includes(actorId)) {
groupEncounters.push(actorId);
await group.update({
'system.encounters': groupEncounters
});
}
}
/**
* Retourne les objets Actor pour une liste d'IDs
* @param {Array} actorIds - Liste d'IDs d'acteurs
* @returns {Array} - Liste d'objets Actor
*/
static getActorObjects(actorIds) {
return actorIds
.map(id => game.actors.get(id))
.filter(actor => actor !== undefined);
}
/**
* Retourne les objets Actor pour les membres d'un groupe
* @param {Actor} group - Le groupe
* @returns {Array} - Liste d'objets Actor
*/
static getGroupMembers(group) {
const memberIds = group.system.members || [];
return this.getActorObjects(memberIds);
}
/**
* Retourne les objets Actor pour les rencontres d'un groupe
* @param {Actor} group - Le groupe
* @returns {Array} - Liste d'objets Actor
*/
static getGroupEncounters(group) {
const encounterIds = group.system.encounters || [];
return this.getActorObjects(encounterIds);
}
/**
* Retourne les groupes auxquels un acteur appartient
* @param {Actor} actor - L'acteur
* @returns {Array} - Liste d'objets Actor (groupes)
*/
static getActorGroups(actor) {
const groupIds = actor.system.encounters || [];
return this.getActorObjects(groupIds).filter(a => a.type === 'group');
}
/**
* Retourne les rencontres (PNJ/Créatures) d'un acteur
* @param {Actor} actor - L'acteur
* @returns {Array} - Liste d'objets Actor (PNJ/Créatures)
*/
static getActorEncounters(actor) {
const encounterIds = actor.system.encounters || [];
return this.getActorObjects(encounterIds).filter(a => a.type !== 'group');
}
/**
* Supprime un acteur de tous ses groupes
* @param {string} actorId - L'ID de l'acteur à supprimer
*/
static async removeActorFromAllGroups(actorId) {
const allGroups = game.actors.filter(a => a.type === 'group');
for (const group of allGroups) {
const members = group.system.members || [];
const encounters = group.system.encounters || [];
let needsUpdate = false;
const newMembers = members.filter(id => id !== actorId);
const newEncounters = encounters.filter(id => id !== actorId);
if (newMembers.length !== members.length || newEncounters.length !== encounters.length) {
needsUpdate = true;
}
if (needsUpdate) {
await group.update({
'system.members': newMembers,
'system.encounters': newEncounters
});
}
}
// Supprimer les groupes des rencontres de l'acteur
const actor = game.actors.get(actorId);
if (actor) {
await actor.update({
'system.encounters': []
});
}
}
/**
* Ajoute un acteur à un groupe
* @param {string} actorId - L'ID de l'acteur
* @param {string} groupId - L'ID du groupe
*/
static async addActorToGroup(actorId, groupId) {
const actor = game.actors.get(actorId);
const group = game.actors.get(groupId);
if (!actor || !group || group.type !== 'group') return;
// Ajouter l'acteur aux membres du groupe
const groupMembers = group.system.members || [];
if (!groupMembers.includes(actorId)) {
groupMembers.push(actorId);
await group.update({
'system.members': groupMembers
});
}
// Ajouter le groupe aux rencontres de l'acteur
const actorEncounters = actor.system.encounters || [];
if (!actorEncounters.includes(groupId)) {
actorEncounters.push(groupId);
await actor.update({
'system.encounters': actorEncounters
});
}
}
/**
* Retire un acteur d'un groupe
* @param {string} actorId - L'ID de l'acteur
* @param {string} groupId - L'ID du groupe
*/
static async removeActorFromGroup(actorId, groupId) {
const actor = game.actors.get(actorId);
const group = game.actors.get(groupId);
if (!actor || !group || group.type !== 'group') return;
// Retirer l'acteur des membres du groupe
const groupMembers = (group.system.members || []).filter(id => id !== actorId);
await group.update({
'system.members': groupMembers
});
// Retirer le groupe des rencontres de l'acteur
const actorEncounters = (actor.system.encounters || []).filter(id => id !== groupId);
await actor.update({
'system.encounters': actorEncounters
});
}
/**
* Initialise les hooks pour la synchronisation automatique
*/
static registerHooks() {
// Hook sur la mise à jour d'un acteur
Hooks.on('updateActor', async (actor, changes, options, userId) => {
if (!game.user.isGM && userId !== game.userId) return;
// Si c'est un groupe qui est mis à jour
if (actor.type === 'group') {
await this.updateActorsOnGroupChange(actor, changes);
}
// Si c'est un autre acteur qui est mis à jour
else {
await this.updateGroupsOnActorChange(actor, changes);
}
});
// Hook sur la création d'un acteur
Hooks.on('createActor', async (actor, options, userId) => {
if (!game.user.isGM && userId !== game.userId) return;
// Si un personnage est créé, vérifier qu'il n'a pas de groupes invalides
if (actor.type !== 'group') {
const encounters = actor.system.encounters || [];
for (const groupId of encounters) {
const group = game.actors.get(groupId);
if (!group) {
// Nettoyer les références invalides
await actor.update({
'system.encounters': encounters.filter(id => game.actors.get(id))
});
}
}
}
});
// Hook sur la suppression d'un acteur
Hooks.on('deleteActor', async (actor, options, userId) => {
if (!game.user.isGM && userId !== game.userId) return;
if (actor.type === 'group') {
// Si un groupe est supprimé, nettoyer les références dans les acteurs
const memberIds = actor.system.members || [];
const encounterIds = actor.system.encounters || [];
for (const id of [...memberIds, ...encounterIds]) {
const a = game.actors.get(id);
if (a) {
const encounters = (a.system.encounters || []).filter(eid => eid !== actor.id);
await a.update({
'system.encounters': encounters
});
}
}
} else {
// Si un acteur est supprimé, le retirer de tous les groupes
await this.removeActorFromAllGroups(actor.id);
}
});
}
}
// Exporter pour utilisation globale
export default GroupLink;
+84
View File
@@ -204,6 +204,90 @@ export const registerHandlebarsHelpers = function () {
} }
}); });
// return npc threat level information
Handlebars.registerHelper('npcThreatLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.npcThreatLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
return (levelData !== undefined) ? levelData[property] : "";
}
});
// return npc experience level information
Handlebars.registerHelper('npcExperienceLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.npcExperienceLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
return (levelData !== undefined) ? levelData[property] : "";
}
});
// return npc role level information
Handlebars.registerHelper('npcRoleLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.npcRoleLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
return (levelData !== undefined) ? levelData[property] : "";
}
});
// return creature pattern level information
Handlebars.registerHelper('creaturePatternLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.creaturePatternLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
return (levelData !== undefined) ? levelData[property] : "";
}
});
// return creature size level information
Handlebars.registerHelper('creatureSizeLevel', function (property, level, options) {
if (level < 1 || level > 3)
return "";
let levelData = CONFIG.VERMINE.creatureSizeLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
return (levelData !== undefined) ? levelData[property] : "";
}
});
// return creature role level information
Handlebars.registerHelper('creatureRoleLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.creatureRoleLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
return (levelData !== undefined) ? levelData[property] : "";
}
});
// return creature pack level information
Handlebars.registerHelper('creaturePackLevel', function (property, level, options) {
if (level < 0 || level > 3)
return "";
let levelData = CONFIG.VERMINE.creaturePackLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
return (levelData !== undefined) ? levelData[property] : "";
}
});
// return skill level information // return skill level information
Handlebars.registerHelper('skillLevel', function (property, level, options) { Handlebars.registerHelper('skillLevel', function (property, level, options) {
+186 -8
View File
@@ -2,34 +2,97 @@ export class VermineUtils {
/** /**
* Méthode pour effectuer un jet de dés avec différentes options * Méthode pour effectuer un jet de dés avec différentes options
* @param {Object} options - Les options du jet de dés * @param {Object} options - Les options du jet de dés
* @param {Actor} options.actor - L'acteur qui lance les dés
* @param {number} options.NoD - Nombre de dés de base
* @param {number} [options.Reroll=0] - Nombre de relances autorisées
* @param {number} [options.difficulty=7] - Difficulté du jet
* @param {number} [options.self_control=0] - Sang-froid utilisé
* @param {string} [options.rollLabel="jet custom"] - Libellé du jet
* @param {Object} [options.totems={}] - Totems utilisés {human: false, adapted: false}
* @param {number} [options.max_effort=0] - Effort maximum
* @param {string} [options.skillCategory=null] - Catégorie de compétence pour les bonus de domaine
* @param {string} [options.keepTotem=null] - Totem à garder ('human' ou 'adapted')
* @param {number} [options.skillLevel=null] - Niveau de la compétence pour les réussites automatiques
* @param {boolean} [options.hasSpecialty=false] - Si une spécialité est utilisée
* @returns {Roll} - Le résultat du jet de dés * @returns {Roll} - Le résultat du jet de dés
*/ */
static async roll({ actor, NoD, Reroll = 0, difficulty = 7, self_control = 0, rollLabel = "jet custom", totems = { human: false, adapted: false }, max_effort = 0 }) { static async roll({ actor, NoD, Reroll = 0, difficulty = 7, self_control = 0, rollLabel = "jet custom", totems = { human: false, adapted: false }, max_effort = 0, skillCategory = null, keepTotem = null, skillLevel = null, hasSpecialty = false }) {
// Déclaration des variables // Déclaration des variables
let formula = ""; let formula = "";
let modFormula = null; let modFormula = null;
let totemBonus = { human: 0, adapted: 0 };
// Calculer les bonus/malus par domaine de totem
if (skillCategory) {
totemBonus = this._calculateTotemDomainBonuses(skillCategory, actor);
}
// Appliquer les réussites automatiques et seuils auto
let autoSuccesses = 0;
let adjustedDifficulty = difficulty;
if (skillLevel !== null && skillLevel !== undefined) {
// Calculer les réussites automatiques
autoSuccesses = this._calculateAutoSuccesses(skillLevel, hasSpecialty);
// Appliquer le seuil automatique si nécessaire
const autoThreshold = this._getAutoThreshold(skillLevel);
if (autoThreshold !== null) {
adjustedDifficulty = autoThreshold;
}
}
// Vérification des totems humains // Vérification des totems humains
if (totems.human) { if (totems.human) {
NoD--; NoD--;
modFormula = "(1D10cs>=" + difficulty + `[human_${game.user.name}]*2)`; const humanDifficulty = skillLevel !== null ? Math.max(adjustedDifficulty, difficulty) : adjustedDifficulty;
const humanFormula = "(1D10cs>=" + humanDifficulty + `[human_${game.user.name}]*2)`;
// Appliquer bonus/malus de domaine
if (totemBonus.human !== 0) {
// Si bonus, ajouter un dé supplémentaire, si malus, réduire le pool
NoD += totemBonus.human;
}
modFormula = humanFormula;
} }
// Vérification des totems adaptés // Vérification des totems adaptés
if (totems.adapted) { if (totems.adapted) {
NoD--; NoD--;
const adaptedDifficulty = skillLevel !== null ? Math.max(adjustedDifficulty, difficulty) : adjustedDifficulty;
const adaptedFormula = "(1D10cs>=" + adaptedDifficulty + `[adapted_${game.user.name}]*2)`;
// Appliquer bonus/malus de domaine
if (totemBonus.adapted !== 0) {
NoD += totemBonus.adapted;
}
// Construction de la formule modifiée // Construction de la formule modifiée
if (modFormula != null) { if (modFormula != null) {
modFormula = modFormula + "+(1D10cs>=" + difficulty + `[adapted_${game.user.name}]*2)`; modFormula = modFormula + "+" + adaptedFormula;
} else { } else {
modFormula = "(1D10cs>=" + difficulty + `[adapted_${game.user.name}]*2)`; modFormula = adaptedFormula;
} }
}; };
// Gestion du choix de totem à garder (si les deux sont activés)
if (totems.human && totems.adapted && keepTotem) {
// Si on veut garder un seul totem, ne pas doubler le bonus
if (keepTotem === 'human' && totems.adapted) {
// Retirer le totem adapté du calcul
modFormula = "(1D10cs>=" + adjustedDifficulty + `[human_${game.user.name}]*2)`;
NoD++; // On avait décrémenté pour adapted, on annule
} else if (keepTotem === 'adapted' && totems.human) {
// Retirer le totem humain du calcul
modFormula = "(1D10cs>=" + adjustedDifficulty + `[adapted_${game.user.name}]*2)`;
NoD++; // On avait décrémenté pour human, on annule
}
}
// Construction de la formule de base // Construction de la formule de base
let baseFormula = '' + NoD + "d10"; let baseFormula = '' + NoD + "d10";
baseFormula += (difficulty != undefined) ? "cs>=" + difficulty : "cs>=7"; baseFormula += (adjustedDifficulty != undefined) ? "cs>=" + adjustedDifficulty : "cs>=7";
baseFormula += `[regular_${game.user.name}]` baseFormula += `[regular_${game.user.name}]`
// Construction de la formule finale // Construction de la formule finale
@@ -39,14 +102,129 @@ export class VermineUtils {
// Création du jet de dés // Création du jet de dés
let roll = new Roll(formula, actor.getRollData()); let roll = new Roll(formula, actor.getRollData());
// Stocker les métadonnées du roll pour l'affichage
roll.vermineData = {
totemsUsed: { ...totems },
keepTotem: keepTotem,
difficulty: adjustedDifficulty,
originalDifficulty: difficulty,
skillCategory: skillCategory,
skillLevel: skillLevel,
hasSpecialty: hasSpecialty,
autoSuccesses: autoSuccesses,
totemBonuses: { ...totemBonus },
baseNoD: NoD,
rerolls: Reroll,
selfControl: self_control
};
//effectuer le lancé //effectuer le lancé
await roll.evaluate(); await roll.evaluate();
//afficher le lancer 3d //afficher le lancer 3d
await VermineUtils.showDiceSoNice(roll); await VermineUtils.showDiceSoNice(roll);
// afficher le résultat dans le chat // afficher le résultat dans le chat
VermineUtils.diplayChatRoll(roll, ...arguments); VermineUtils.diplayChatRoll(roll, { actor, NoD, Reroll, difficulty, self_control, rollLabel, totems, max_effort, skillCategory, keepTotem, skillLevel, hasSpecialty });
return roll; return roll;
} }
/**
* Calcule les bonus/malus par domaine de totem
* @param {string} skillCategory - Catégorie de la compétence
* @param {Actor} actor - L'acteur
* @returns {Object} - Bonus pour chaque totem {human: number, adapted: number}
*/
static _calculateTotemDomainBonuses(skillCategory, actor) {
const bonuses = { human: 0, adapted: 0 };
if (!CONFIG.VERMINE?.totemDomains || !actor?.system?.identity?.totem) {
return bonuses;
}
const actorTotem = actor.system.identity.totem;
const totemConfig = CONFIG.VERMINE.totemDomains[actorTotem];
if (!totemConfig || !totemConfig.domains) {
return bonuses;
}
// Vérifier si la catégorie de compétence est dans les domaines du totem
const preferredCategory = actor.system.skill_categories?.preferred;
// Bonus pour le totem de l'acteur
if (preferredCategory && totemConfig.domains.includes(preferredCategory)) {
// Le domaine de prédilection est dans les domaines du totem
bonuses[actorTotem] = totemConfig.bonus || 1;
}
// Malus pour le totem opposé
const oppositeTotem = CONFIG.VERMINE.totem_opposites?.[actorTotem];
if (oppositeTotem && preferredCategory) {
const oppositeConfig = CONFIG.VERMINE.totemDomains[oppositeTotem];
if (oppositeConfig?.domains?.includes(preferredCategory)) {
bonuses[oppositeTotem] = -(oppositeConfig.bonus || 1);
}
}
return bonuses;
}
/**
* Calcule les réussites automatiques basées sur la maîtrise de la compétence
* @param {number} skillLevel - Niveau de la compétence (0-5)
* @param {boolean} hasSpecialty - Si une spécialité est utilisée
* @returns {number} - Nombre de réussites automatiques
*/
static _calculateAutoSuccesses(skillLevel, hasSpecialty = false) {
// Selon les règles de Vermine2047, les réussites automatiques sont basées sur le niveau de maîtrise
// Niveau 0 (Incompétent): 0 réussite automatique
// Niveau 1 (Débutant): 0 réussite automatique
// Niveau 2 (Compétent): 1 réussite automatique si spécialité utilisée
// Niveau 3 (Expert): 1 réussite automatique
// Niveau 4 (Maître): 1 réussite automatique + 1 si spécialité utilisée
// Niveau 5 (Légende): 2 réussites automatiques
if (!skillLevel) return 0;
let autoSuccesses = 0;
switch (skillLevel) {
case 2: // Compétent
if (hasSpecialty) autoSuccesses = 1;
break;
case 3: // Expert
autoSuccesses = 1;
break;
case 4: // Maître
autoSuccesses = 1;
if (hasSpecialty) autoSuccesses += 1;
break;
case 5: // Légende
autoSuccesses = 2;
break;
default:
autoSuccesses = 0;
}
return autoSuccesses;
}
/**
* Détermine le seuil automatique si la compétence n'est pas maîtrisée
* @param {number} skillLevel - Niveau de la compétence
* @returns {number|null} - Seuil automatique ou null si la compétence est maîtrisée
*/
static _getAutoThreshold(skillLevel) {
// Si la compétence n'est pas maîtrisée (niveau 0 ou 1), utiliser un seuil par défaut
// Niveau 0 (Incompétent): seuil = 9 (très difficile)
// Niveau 1 (Débutant): seuil = 7 (difficile)
// Niveau >= 2: null (utiliser le seuil normal)
if (skillLevel === 0) return 9; // Très difficile
if (skillLevel === 1) return 7; // Difficile
return null; // Utiliser le seuil normal
}
/** /**
* Méthode pour gérer les événements de relance de dés * Méthode pour gérer les événements de relance de dés
+6 -1
View File
@@ -1,5 +1,6 @@
import { registerHooks } from "./system/hooks.mjs"; import { registerHooks } from "./system/hooks.mjs";
import { registerSettings } from "./system/settings.mjs"; import { registerSettings } from "./system/settings.mjs";
import { GroupLink } from "./system/group-link.mjs";
// Import document classes. // Import document classes.
import { VermineActor } from "./documents/actor.mjs"; import { VermineActor } from "./documents/actor.mjs";
@@ -31,8 +32,12 @@ Hooks.once('init', async function () {
VermineActor, VermineActor,
VermineItem, VermineItem,
VermineUtils, VermineUtils,
VermineCombat VermineCombat,
GroupLink
}; };
// Register GroupLink hooks for automatic synchronization
GroupLink.registerHooks();
// Define custom Document classes // Define custom Document classes
CONFIG.Actor.documentClass = VermineActor; CONFIG.Actor.documentClass = VermineActor;
+2 -2
View File
@@ -5,8 +5,8 @@
"version": "0.1.13", "version": "0.1.13",
"compatibility": { "compatibility": {
"minimum": "11", "minimum": "11",
"verified": "11.308", "verified": "14.0",
"maximum": "12" "maximum": "14"
}, },
"authors": [ "authors": [
{ {
+1
View File
@@ -86,6 +86,7 @@
"max": 5 "max": 5
} }
}, },
"encounters": [],
"abilities": { "abilities": {
"vigor": { "vigor": {
"value": 1, "value": 1,
+149 -10
View File
@@ -10,30 +10,75 @@
</h1> </h1>
<div class="resource-content flexrow row text-right"> <div class="resource-content flexrow row text-right">
<label for="system.pack.value" class="resource-label">{{ localize 'ADVERSITY.pack'}}</label> <label for="system.pack.value" class="resource-label">{{ localize 'ADVERSITY.pack'}}</label>
<input type="number" name="system.pack.value" value="{{system.pack.value}}" data-dtype="Number" min="0" max="3" /> <select
name="system.pack.value"
id="system.pack.value"
data-dtype="Number"
>
<option value="0">{{localize 'VERMINE.none'}}</option>
{{#each ../config.creaturePackLevels}}
{{#if @key}}
<option
value="{{@key}}"
{{#ife @key ../system.pack.value}}selected{{/ife}}
>{{@key}}</option>
{{/if}}
{{/each}}
</select>
</div> </div>
</div> </div>
<div class="resources grid grid-3col"> <div class="resources grid grid-3col">
<div class="resource flex-group-center"> <div class="resource flex-group-center">
<label for="system.threat.value" class="resource-label">{{ localize 'ADVERSITY.pattern'}}</label> <label for="system.pattern.value" class="resource-label">{{ localize 'ADVERSITY.pattern'}}</label>
<div class="resource-content"> <div class="resource-content">
<input type="number" name="system.pattern.value" value="{{system.pattern.value}}" data-dtype="Number" min="1" max="4" /> <select
<span>({{ patternLevel "label" system.pattern.value }})</span> name="system.pattern.value"
id="system.pattern.value"
data-dtype="Number"
>
{{#each ../config.creaturePatternLevels}}
<option
value="{{@key}}"
{{#ife @key ../system.pattern.value}}selected{{/ife}}
>{{localize this.label}} ({{@key}})</option>
{{/each}}
</select>
</div> </div>
</div> </div>
<div class="resource flex-group-center"> <div class="resource flex-group-center">
<label for="system.experience.value" class="resource-label">{{ localize 'ADVERSITY.size'}}</label> <label for="system.size.value" class="resource-label">{{ localize 'ADVERSITY.size'}}</label>
<div class="resource-content"> <div class="resource-content">
<input type="number" name="system.size.value" value="{{system.size.value}}" data-dtype="Number" min="0" max="3" /> <select
name="system.size.value"
id="system.size.value"
data-dtype="Number"
>
{{#each ../config.creatureSizeLevels}}
<option
value="{{@key}}"
{{#ife @key ../system.size.value}}selected{{/ife}}
>{{@key}}</option>
{{/each}}
</select>
</div> </div>
</div> </div>
<div class="resource flex-group-center"> <div class="resource flex-group-center">
<label for="system.cr" class="resource-label">{{ localize 'ADVERSITY.role'}}</label> <label for="system.role.value" class="resource-label">{{ localize 'ADVERSITY.role'}}</label>
<div class="resource-content"> <div class="resource-content">
<input type="number" name="system.role.value" value="{{system.role.value }}" data-dtype="Number" min="1" max="3"/> <select
<span>({{ roleLevel "label" system.role.value }})</span> name="system.role.value"
id="system.role.value"
data-dtype="Number"
>
{{#each ../config.creatureRoleLevels}}
<option
value="{{@key}}"
{{#ife @key ../system.role.value}}selected{{/ife}}
>{{localize this.label}} ({{@key}})</option>
{{/each}}
</select>
</div> </div>
</div> </div>
@@ -53,6 +98,101 @@
{{!-- Biography Tab --}} {{!-- Biography Tab --}}
<div class="tab stats" data-group="primary" data-tab="description"> <div class="tab stats" data-group="primary" data-tab="description">
<section class="grid grid-3col gap-md">
<div class="mdb">
<h4 class="align-center">{{ localize 'ADVERSITY.pattern'}}</h4>
<ul class="unstyled">
<li>{{ localize 'ADVERSITY.attack'}} {{ creaturePatternLevel "attack" system.pattern.value }}</li>
<li>{{ localize 'ADVERSITY.damage'}} {{ creaturePatternLevel "damage" system.pattern.value }}</li>
<li>{{ localize 'ADVERSITY.wounds'}}
{{ creaturePatternLevel "minorWound" system.pattern.value }}/
{{ creaturePatternLevel "majorWound" system.pattern.value }}/
{{ creaturePatternLevel "deadlyWound" system.pattern.value }}
</li>
</ul>
</div>
<div class="mdb">
<h4 class="align-center">{{ localize 'ADVERSITY.size'}}</h4>
<ul class="unstyled">
<li>{{ localize 'ADVERSITY.attack'}} {{ creatureSizeLevel "attack" system.size.value }}</li>
<li>{{ localize 'ADVERSITY.vigor'}} {{ creatureSizeLevel "vigor" system.size.value }}</li>
<li>{{ localize 'ADVERSITY.wounds'}}
{{ creatureSizeLevel "minorWound" system.size.value }}/
{{ creatureSizeLevel "majorWound" system.size.value }}/
{{ creatureSizeLevel "deadlyWound" system.size.value }}
</li>
</ul>
</div>
<div class="mdb">
<h4 class="align-center">{{ localize 'ADVERSITY.role'}}</h4>
<ul class="unstyled">
<li>{{ localize 'ADVERSITY.reaction'}} {{ creatureRoleLevel "reaction" system.role.value }} + {{ creatureRoleLevel "reaction_bonus" system.role.value }}</li>
<li>{{ localize 'ADVERSITY.pools'}} {{ creatureRoleLevel "pools" system.role.value }}</li>
<li>{{ localize 'ADVERSITY.gear'}} {{ creatureRoleLevel "gear" system.role.value }}</li>
<li>{{ localize 'ADVERSITY.protection'}} {{ creatureRoleLevel "protection" system.role.value }}</li>
</ul>
</div>
</section>
<section class="grid grid-3col gap-md">
<div class="mdb">
<h4 class="align-center">{{ localize 'ADVERSITY.pack'}}</h4>
<ul class="unstyled">
<li>{{ localize 'ADVERSITY.attack'}} {{ creaturePackLevel "attack" system.pack.value }}</li>
<li>{{ localize 'ADVERSITY.damage'}} {{ creaturePackLevel "damage" system.pack.value }}</li>
<li>{{ localize 'ADVERSITY.wounds'}}
{{ creaturePackLevel "minorWound" system.pack.value }}/
{{ creaturePackLevel "majorWound" system.pack.value }}/
{{ creaturePackLevel "deadlyWound" system.pack.value }}
</li>
</ul>
</div>
<div class="mdb">
<h4 class="align-center">{{ localize 'ADVERSITY.skills'}}</h4>
<ul class="unstyled">
<li>
<input type="text" name="system.skills" value="{{ system.skills }}" data-dtype="String" placeholder="{{ localize 'ADVERSITY.skills' }}"/>
</li>
</ul>
</div>
<div class="mdb">
<h4 class="align-center">{{ localize 'VERMINE.modes'}}</h4>
<ul class="unstyled">
<li>
<label>
<input
type="checkbox"
name="system.modes.survival"
{{#if system.modes.survival}}checked{{/if}}
data-dtype="Boolean"
/>
{{ localize 'GAME_MODES.survival' }}
</label>
</li>
<li>
<label>
<input
type="checkbox"
name="system.modes.nightmare"
{{#if system.modes.nightmare}}checked{{/if}}
data-dtype="Boolean"
/>
{{ localize 'GAME_MODES.nightmare' }}
</label>
</li>
<li>
<label>
<input
type="checkbox"
name="system.modes.apocalypse"
{{#if system.modes.apocalypse}}checked{{/if}}
data-dtype="Boolean"
/>
{{ localize 'GAME_MODES.apocalypse' }}
</label>
</li>
</ul>
</div>
</section>
<section class="flexcol col gap-md"> <section class="flexcol col gap-md">
<h4 class="align-center">{{ localize 'IDENTITY.notes'}}</h4> <h4 class="align-center">{{ localize 'IDENTITY.notes'}}</h4>
{{editor system.biography target="system.biography" button=true owner=owner editable=editable}} {{editor system.biography target="system.biography" button=true owner=owner editable=editable}}
@@ -66,4 +206,3 @@
</section> </section>
</form> </form>
+52 -19
View File
@@ -9,24 +9,54 @@
<div class="resource flex-group-center"> <div class="resource flex-group-center">
<label for="system.threat.value" class="resource-label">{{ localize 'ADVERSITY.threat'}}</label> <label for="system.threat.value" class="resource-label">{{ localize 'ADVERSITY.threat'}}</label>
<div class="resource-content"> <div class="resource-content">
<input type="number" name="system.threat.value" value="{{system.threat.value}}" data-dtype="Number" min="1" max="4" /> <select
<span>({{ threatLevel "label" system.threat.value }})</span> name="system.threat.value"
id="system.threat.value"
data-dtype="Number"
>
{{#each ../config.npcThreatLevels}}
<option
value="{{@key}}"
{{#ife @key ../system.threat.value}}selected{{/ife}}
>{{localize this.label}} ({{@key}})</option>
{{/each}}
</select>
</div> </div>
</div> </div>
<div class="resource flex-group-center"> <div class="resource flex-group-center">
<label for="system.experience.value" class="resource-label">{{ localize 'ADVERSITY.experience'}}</label> <label for="system.experience.value" class="resource-label">{{ localize 'ADVERSITY.experience'}}</label>
<div class="resource-content"> <div class="resource-content">
<input type="number" name="system.experience.value" value="{{system.experience.value}}" data-dtype="Number" min="1" max="4" /> <select
<span>({{ experienceLevel "label" system.experience.value }})</span> name="system.experience.value"
id="system.experience.value"
data-dtype="Number"
>
{{#each ../config.npcExperienceLevels}}
<option
value="{{@key}}"
{{#ife @key ../system.experience.value}}selected{{/ife}}
>{{localize this.label}} ({{@key}})</option>
{{/each}}
</select>
</div> </div>
</div> </div>
<div class="resource flex-group-center"> <div class="resource flex-group-center">
<label for="system.cr" class="resource-label">{{ localize 'ADVERSITY.role'}}</label> <label for="system.role.value" class="resource-label">{{ localize 'ADVERSITY.role'}}</label>
<div class="resource-content"> <div class="resource-content">
<input type="number" name="system.role.value" value="{{system.role.value }}" data-dtype="Number" min="1" max="4"/> <select
<span>({{ roleLevel "label" system.role.value }})</span> name="system.role.value"
id="system.role.value"
data-dtype="Number"
>
{{#each ../config.npcRoleLevels}}
<option
value="{{@key}}"
{{#ife @key ../system.role.value}}selected{{/ife}}
>{{localize this.label}} ({{@key}})</option>
{{/each}}
</select>
</div> </div>
</div> </div>
@@ -51,9 +81,13 @@
<div class="mdb"> <div class="mdb">
<h4 class="align-center">{{ localize 'ADVERSITY.threat'}}</h4> <h4 class="align-center">{{ localize 'ADVERSITY.threat'}}</h4>
<ul class="unstyled"> <ul class="unstyled">
<li>{{ localize 'ADVERSITY.attack'}} {{ threatLevel "attack" system.threat.value }}</li> <li>{{ localize 'ADVERSITY.attack'}} {{ npcThreatLevel "attack" system.threat.value }}</li>
<li>{{ localize 'ADVERSITY.vigor'}} {{ threatLevel "vigor" system.threat.value }}</li> <li>{{ localize 'ADVERSITY.vigor'}} {{ npcThreatLevel "vigor" system.threat.value }}</li>
<li>{{ localize 'ADVERSITY.wounds'}} {{ threatLevel "minorWound" system.threat.value }}/{{ threatLevel "majorWound" system.threat.value }}/{{ threatLevel "deadlyWound" system.threat.value }}</li> <li>{{ localize 'ADVERSITY.wounds'}}
{{ npcThreatLevel "minorWound" system.threat.value }}/
{{ npcThreatLevel "majorWound" system.threat.value }}/
{{ npcThreatLevel "deadlyWound" system.threat.value }}
</li>
</ul> </ul>
</div> </div>
@@ -64,19 +98,19 @@
<label for="system.skills" class="">{{ localize "ADVERSITY.skills" }}</label> <label for="system.skills" class="">{{ localize "ADVERSITY.skills" }}</label>
<input type="text" name="system.skills" value="{{ system.skills }}" data-dtype="String"/> <input type="text" name="system.skills" value="{{ system.skills }}" data-dtype="String"/>
</li> </li>
<li>{{ localize 'ADVERSITY.action'}} {{ experienceLevel "action" system.experience.value }}</li> <li>{{ localize 'ADVERSITY.action'}} {{ npcExperienceLevel "action" system.experience.value }}</li>
<li>{{ localize 'ADVERSITY.specialties'}} {{ experienceLevel "specialties" system.experience.value }}</li> <li>{{ localize 'ADVERSITY.specialties'}} {{ npcExperienceLevel "specialties" system.experience.value }}</li>
<li>{{ localize 'ADVERSITY.rerolls'}} {{ experienceLevel "rerolls" system.experience.value }}</li> <li>{{ localize 'ADVERSITY.rerolls'}} {{ npcExperienceLevel "rerolls" system.experience.value }}</li>
<li>{{ localize 'ADVERSITY.contact'}} {{ experienceLevel "contact" system.experience.value }}</li> <li>{{ localize 'ADVERSITY.contact'}} {{ npcExperienceLevel "contact" system.experience.value }}</li>
</ul> </ul>
</div> </div>
<div class="mdb"> <div class="mdb">
<h4 class="align-center">{{ localize 'ADVERSITY.role'}}</h4> <h4 class="align-center">{{ localize 'ADVERSITY.role'}}</h4>
<ul class="unstyled"> <ul class="unstyled">
<li>{{ localize 'ADVERSITY.reaction'}} {{ roleLevel "reaction" system.role.value }} + {{ roleLevel "reaction_bonus" system.role.value }}</li> <li>{{ localize 'ADVERSITY.reaction'}} {{ npcRoleLevel "reaction" system.role.value }} + {{ npcRoleLevel "reaction_bonus" system.role.value }}</li>
<li>{{ localize 'ADVERSITY.pools'}} {{ roleLevel "pools" system.role.value }}</li> <li>{{ localize 'ADVERSITY.pools'}} {{ npcRoleLevel "pools" system.role.value }}</li>
<li>{{ localize 'ADVERSITY.gear'}} {{ roleLevel "gear" system.role.value }}</li> <li>{{ localize 'ADVERSITY.gear'}} {{ npcRoleLevel "gear" system.role.value }}</li>
<li>{{ localize 'ADVERSITY.protection'}} {{ roleLevel "protection" system.role.value }}</li> <li>{{ localize 'ADVERSITY.protection'}} {{ npcRoleLevel "protection" system.role.value }}</li>
</ul> </ul>
</div> </div>
</section> </section>
@@ -98,4 +132,3 @@
</section> </section>
</form> </form>
+228 -208
View File
@@ -4,52 +4,22 @@
data-actor-id="{{ speakerId }}" data-actor-id="{{ speakerId }}"
> >
<!--HIDDEN DATA --> <!-- HIDDEN DATA -->
<input <input type="hidden" name="label" id="label" value="{{ label }}" />
type="hidden" <input type="hidden" name="rollType" id="rollType" value="{{ rollType }}" />
name="label" <input type="hidden" name="abilityScore" id="abilityScore" value="{{ abilityScore }}" />
id="label" <input type="hidden" name="skillScore" id="skillScore" value="{{ skillScore }}" />
value="{{ label }}" <input type="hidden" name="speakerId" value="{{ speakerId }}" />
/> <input type="hidden" name="skillLabel" value="{{ skill }}" />
<input
type="hidden"
name="rollType"
id="rollType"
value="{{ rollType }}"
/>
<input
type="hidden"
name="abilityScore"
id="abilityScore"
value="{{ abilityScore }}"
/>
<input
type="hidden"
name="skillScore"
id="skillScore"
value="{{ skillScore }}"
/>
<input
type="hidden"
name="speakerId"
value="{{ speakerId }}"
/>
<input
type="hidden"
name="skillLabel"
value="{{ skill }}"
/>
<div class="dice-pool"> <div class="dice-pool">
<!--SKILLS TALENTS--> <!-- MAIN ROLL SELECTION -->
<h1 class="flexrow skills-talents"> <div class="flexrow main-roll-section">
<div class="flexcol">
<label class="label">{{localize <!-- CHARACTERISTIC -->
'VERMINE.ability'}}</label> <div class="flexcol characteristic-section">
<label class="label">{{localize 'VERMINE.ability'}}</label>
<select <select
class="info-value" class="info-value"
data-roll="true" data-roll="true"
@@ -60,35 +30,37 @@
min="1" min="1"
max="5" max="5"
> >
<option value="0">-- Choisissez une caractéristique <option value="0">-- {{localize 'VERMINE.choose_ability'}} --</option>
--</option>
{{#each config.abilityCategories as |abilCategory catkey|}} {{#each config.abilityCategories as |abilCategory catkey|}}
<optgroup label="{{ smarttlk 'ABILITY_CATEGORIES' catkey 'name' }}"> <optgroup label="{{ smarttlk 'ABILITY_CATEGORIES' catkey 'name' }}">
{{#each @root.actor.system.abilities as |abil key|}} {{#each @root.actor.system.abilities as |abil key|}}
{{#ife abil.category catkey}} {{#ife abil.category catkey}}
<option <option
value="{{abil.value}}" value="{{abil.value}}"
data-label="{{key}}" data-label="{{key}}"
data-category="{{abil.category}}"
{{#ife @root.rollType "ability"}} {{#ife @root.rollType "ability"}}
{{#ife @root.labelKey key}} {{#ife @root.labelKey key}}
selected="true" selected="true"
{{/ife}}
{{/ife}} {{/ife}}
{{/ife}} >
>{{ smarttlk 'ABILITIES' key 'name' {{ smarttlk 'ABILITIES' key 'name' }} / {{abil.value}}
}} / {{abil.value}}</option> </option>
{{/ife}} {{/ife}}
{{/each}} {{/each}}
</optgroup> </optgroup>
{{/each}} {{/each}}
</select> </select>
<div class="ability-score flexrow">
<span id="abilityScore">{{localize 'VERMINE.score'}}: </span>
<span id="abilityScoreValue">0</span>
</div>
</div> </div>
<div class="flexcol"> <!-- SKILL -->
<label class="label">{{localize <div class="flexcol skill-section">
'VERMINE.skill_title'}}</label> <label class="label">{{localize 'VERMINE.skill_title'}}</label>
<select <select
class="info-value" class="info-value"
data-roll="true" data-roll="true"
@@ -99,25 +71,24 @@
min="1" min="1"
max="5" max="5"
> >
<option>-- Choisissez une compétence <option value="">-- {{localize 'VERMINE.choose_skill'}} --</option>
--</option>
{{#each config.skillCategories as |skillCategory catkey|}} {{#each config.skillCategories as |skillCategory catkey|}}
<optgroup label="{{ smarttlk 'SKILLS_CATEGORIES' catkey 'name' }}"> <optgroup label="{{ smarttlk 'SKILLS_CATEGORIES' catkey 'name' }}">
{{#each @root.actor.system.skills as |skill key|}} {{#each @root.actor.system.skills as |skill key|}}
{{#ife skill.category catkey}} {{#ife skill.category catkey}}
<option <option
value="{{skill.value}}" value="{{skill.value}}"
data-pool="{{skillLevel "dicePool" data-pool="{{skillLevel 'dicePool' skill.value}}"
skill.value}}"
data-label="{{key}}" data-label="{{key}}"
data-reroll="{{skillLevel "reroll" skill.value}}" data-category="{{skill.category}}"
data-reroll="{{skillLevel 'reroll' skill.value}}"
{{#ife @root.rollType "skill"}} {{#ife @root.rollType "skill"}}
{{#ife @root.labelKey key}} {{#ife @root.labelKey key}}
selected="true" selected="true"
{{/ife}} {{/ife}}
{{/ife}} {{/ife}}
> >
<b>{{ smarttlk 'SKILLS' key 'name' }} / </b> <b>{{ smarttlk 'SKILLS' key 'name' }}</b> /
<i>{{skillLevel "label" skill.value}}</i> <i>{{skillLevel "label" skill.value}}</i>
</option> </option>
{{/ife}} {{/ife}}
@@ -125,125 +96,120 @@
</optgroup> </optgroup>
{{/each}} {{/each}}
</select> </select>
{{#if availableSpecialties.length}} </div>
<label class="label">{{localize 'VERMINE.specialty'}} (+1D)</label> </div>
<div class="flexrow">
<span data-spec-skill="{{spec.system.skill}}"> <!-- SPECIALTIES (shown when skill selected) -->
<i>aucunes</i> {{#if availableSpecialties.length}}
<details class="specialties-section">
<summary class="flexrow">
<span class="label">{{localize 'VERMINE.specialty'}} (+1D)</span>
<span class="current-specialty">{{localize 'VERMINE.none'}}</span>
</summary>
<div class="flexrow specialty-options">
<label class="flexrow">
<input
type="radio"
data-roll="true"
name="usingSpecialization"
id="usingSpecialization"
value="aucune"
/>
<span>{{localize 'VERMINE.none'}}</span>
</label>
{{#each availableSpecialties as |spec ind|}}
<label class="flexrow">
<input <input
type="radio" type="radio"
data-roll="true" data-roll="true"
name="usingSpecialization" name="usingSpecialization"
id="usingSpecialization" id="usingSpecialization"
value="aucune" value="{{spec.name}}"
> data-spec-skill="{{spec.system.skill}}"
</span> {{#if specialty}}checked{{/if}}
{{#each availableSpecialties as |spec ind|}} />
<span>{{spec.name}}</span>
</label>
{{/each}}
</div>
</details>
{{/if}}
<span data-spec-skill="{{spec.system.skill}}"> <!-- DIFFICULTY AND HANDICAP -->
<i>{{spec.name}}</i> <details class="difficulty-section" open>
<input
type="radio"
data-roll="true"
name="usingSpecialization"
id="usingSpecialization"
value="{{spec.name}}"
{{#if specialty}}checked{{/if}}
>
</span>
{{/each}}
</div>
{{/if}}
</div>
</h1>
<!--DIFFICULTY HANDICAP-->
<h2 class="flexrow difficulties">
<label class="label">{{localize
'VERMINE.difficulty'}}</label>
<select
class="info-value"
data-roll="tue"
data-dtype="String"
type="number"
name="difficulty"
id="difficulty"
min="3"
max="10"
>
{{#select difficulty }}
<option value="{{ diffLevel 'difficulty' 1}}">{{ diffLevel 'label' 1}} -
{{ diffLevel 'difficulty' 1}}
</option>
<option value="{{ diffLevel 'difficulty' 2}}">{{ diffLevel 'label' 2}} -
{{ diffLevel 'difficulty' 2}}
</option>
<option
value="{{ diffLevel 'difficulty' 3}}"
selected
>{{ diffLevel
'label' 3}} - {{ diffLevel 'difficulty' 3}}</option>
<option value="{{ diffLevel 'difficulty' 4}}">{{ diffLevel 'label' 4}} -
{{ diffLevel 'difficulty' 4}}
</option>
<option value="{{ diffLevel 'difficulty' 5}}">{{ diffLevel 'label' 5}} -
{{ diffLevel 'difficulty' 5}}
</option>
{{/select}}
</select>
<label for="handicap">Handicap</label>
<select
class="info-value"
data-roll="tue"
data-dtype="String"
type="number"
name="handicap"
id="handicap"
min="0"
max="2"
>
{{#select difficulty }}
<option
value="1"
selected
>aucun</option>
<option value="2">(I)</option>
<option value="3">(II)</option>
{{/select}}
</select>
</h2>
<!-- BONUSES -->
<details class="flexcol bonuses">
<summary class="flexrow"> <summary class="flexrow">
<h3 class="flexrow align-center">Bonuses ?</h3> <span class="label">{{localize 'VERMINE.difficulty'}}</span>
<span class="current-values">
<span id="current-difficulty">{{localize 'DIFFICULTY_LEVELS.hard'}} (7)</span>
<span id="current-handicap">{{localize 'VERMINE.none'}}</span>
</span>
</summary> </summary>
<div class="grid grid-2col"> <div class="flexrow difficulty-controls">
<div class="flexcol">
<label class="label" for="difficulty">{{localize 'VERMINE.difficulty'}}</label>
<select
class="info-value"
data-roll="true"
data-dtype="String"
type="number"
name="difficulty"
id="difficulty"
min="3"
max="10"
>
{{#select difficulty }}
<option value="{{ diffLevel 'difficulty' 1}}">{{ diffLevel 'label' 1}} ({{ diffLevel 'difficulty' 1}})</option>
<option value="{{ diffLevel 'difficulty' 2}}">{{ diffLevel 'label' 2}} ({{ diffLevel 'difficulty' 2}})</option>
<option value="{{ diffLevel 'difficulty' 3}}" selected>{{ diffLevel 'label' 3}} ({{ diffLevel 'difficulty' 3}})</option>
<option value="{{ diffLevel 'difficulty' 4}}">{{ diffLevel 'label' 4}} ({{ diffLevel 'difficulty' 4}})</option>
<option value="{{ diffLevel 'difficulty' 5}}">{{ diffLevel 'label' 5}} ({{ diffLevel 'difficulty' 5}})</option>
{{/select}}
</select>
</div>
<div class="flexcol">
<label class="label" for="handicap">{{localize 'VERMINE.handicap'}}</label>
<select
class="info-value"
data-roll="true"
data-dtype="String"
type="number"
name="handicap"
id="handicap"
min="0"
max="2"
>
<option value="1" selected>{{localize 'VERMINE.none'}}</option>
<option value="2">(I)</option>
<option value="3">(II)</option>
</select>
</div>
</div>
</details>
<!-- BONUSES SECTION -->
<div class="flexrow row smb"> <details class="bonuses-section">
<label class="label">{{localize 'VERMINE.help'}} (+1D)</label> <summary class="flexrow">
<span class="label">{{localize 'VERMINE.bonuses'}}</span>
<span class="bonus-count">+<span id="total-bonus">0</span>D</span>
</summary>
<div class="grid grid-2col bonus-grid">
<!-- HELP -->
<div class="flexrow bonus-item">
<input <input
type="checkbox" type="checkbox"
data-roll="true" data-roll="true"
name="helped" name="helped"
id="helped" id="helped"
value="1" value="1"
{{#if {{#if help}}checked{{/if}}
help}}checked{{/if}} />
> <label class="label" for="helped">{{localize 'VERMINE.help'}} (+1D)</label>
</div> </div>
<!-- GROUP -->
<div class="flexrow row mdb"> <div class="flexrow bonus-item">
<label class="label">{{localize <label class="label" for="group">{{localize 'VERMINE.group'}}</label>
'VERMINE.group'}}</label>
<input <input
type="number" type="number"
data-roll="true" data-roll="true"
@@ -254,13 +220,15 @@
min="0" min="0"
max="5" max="5"
value="0" value="0"
> />
<span>D</span>
</div> </div>
<!-- SELF CONTROL -->
<div class="flexrow row mdb"> <div class="flexrow bonus-item full-width">
<label class="label">{{localize <label class="label" for="self_control">
'VERMINE.self_control'}} + <span id="self_control_value">0</span>D {{localize 'VERMINE.self_control'}}
<span>(+<span id="self_control_value">0</span>D)</span>
</label> </label>
<input <input
type="range" type="range"
@@ -273,13 +241,11 @@
/> />
</div> </div>
<!-- TOOLS -->
<div class="flexcol row mdb"> <div class="flexrow bonus-item full-width">
<label class="label">{{localize 'VERMINE.tooling'}} (+1D)</label> <label class="label">{{localize 'VERMINE.tooling'}} (+1D)</label>
<div class="item-list grid grid-4col"> <div class="item-list grid grid-4col">
<h3> <label>
<i>Auncun</i>
<input <input
type="radio" type="radio"
data-roll="true" data-roll="true"
@@ -287,58 +253,112 @@
id="usingTools" id="usingTools"
value="0" value="0"
checked checked
> />
</h3> <i>{{localize 'VERMINE.none'}}</i>
</label>
{{#each availableItems as |item ind|}} {{#each availableItems as |item ind|}}
<span> <label>
<i>{{item.name}}</i>
<input <input
type="radio" type="radio"
data-roll="true" data-roll="true"
name="usingTools" name="usingTools"
id="usingTools" id="usingTools"
value="{{item.name}}" value="{{item.name}}"
> />
</span> <i>{{item.name}}</i>
</label>
{{/each}} {{/each}}
</div> </div>
</div> </div>
<div class="flexcol">
<label class="label">utiliser des dés totems ? <!-- TOTEMS -->
</label> {{#ifgt @root.actor.system.adaptation.totems.human.value 0}}
<div class="flexrow"> {{#ifgt @root.actor.system.adaptation.totems.adapted.value 0}}
<div class="flexrow bonus-item full-width totems-section">
<label class="label">{{localize 'VERMINE.totem_dice'}}</label>
<div class="flexrow totem-options">
<label class="totem-option">
<input
type="checkbox"
data-roll="true"
name="human-totem"
id="human-totem"
value="1"
class="totem-checkbox"
/>
<span class="totem-label">{{localize 'TOTEMS.human.name'}}</span>
<small>(+{{@root.actor.system.adaptation.totems.human.value}}D)</small>
</label>
<label class="totem-option">
<input
type="checkbox"
data-roll="true"
name="adapted-totem"
id="adapted-totem"
value="1"
class="totem-checkbox"
/>
<span class="totem-label">{{localize 'TOTEMS.adapted.name'}}</span>
<small>(+{{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
</label>
</div>
<small class="totem-hint">{{localize 'VERMINE.totem_hint'}}</small>
</div>
{{else}}
{{#ifgt @root.actor.system.adaptation.totems.human.value 0}} {{#ifgt @root.actor.system.adaptation.totems.human.value 0}}
<div class="totem-human"> <div class="flexrow bonus-item totems-section">
<h4 for="human-totem">totem humain </h4> <label class="totem-option">
<input <input
type="checkbox" type="checkbox"
data-roll="true" data-roll="true"
name="human-totem" name="human-totem"
id="human-totem" id="human-totem"
value="1" value="1"
> />
<span class="totem-label">{{localize 'TOTEMS.human.name'}}</span>
<small>(+{{@root.actor.system.adaptation.totems.human.value}}D)</small>
</label>
</div> </div>
{{/ifgt}} {{/ifgt}}
{{#ifgt @root.actor.system.adaptation.totems.adapted.value 0}} {{/ifgt}}
<div class="totem-adapted"> {{else}}
<h4 for="human-totem">totem adapté </h4> {{#ifgt @root.actor.system.adaptation.totems.adapted.value 0}}
<div class="flexrow bonus-item totems-section">
<label class="totem-option">
<input <input
type="checkbox" type="checkbox"
data-roll="true" data-roll="true"
name="adapted-totem" name="adapted-totem"
id="adapted-totem" id="adapted-totem"
value="1" value="1"
> />
</div> <span class="totem-label">{{localize 'TOTEMS.adapted.name'}}</span>
{{/ifgt}} <small>(+{{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
</div> </label>
</div> </div>
{{/ifgt}}
{{/ifgt}}
</div> </div>
</details> </details>
<!-- TOTAL DICE POOL -->
<div class="flexrow total-section">
<label class="label">{{localize 'VERMINE.total'}}:</label>
<span id="dice-pool-total" class="total-value">0D</span>
{{#ifgt @root.actor.system.adaptation.totems.human.value 0}}
{{#ifgt @root.actor.system.adaptation.totems.adapted.value 0}}
<span class="totem-selector">
({{localize 'VERMINE.keep_totem'}}
<select id="keep-totem-select" name="keep_totem">
<option value="human">{{localize 'TOTEMS.human.name'}}</option>
<option value="adapted">{{localize 'TOTEMS.adapted.name'}}</option>
</select>)
</span>
{{/ifgt}}
{{/ifgt}}
</div>
</div> </div>
</form>
</form>
+58
View File
@@ -0,0 +1,58 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Ability specific content --}}
<div class="grid grid-2col">
{{#if item.system.type}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.type' }}:</span>
<span>{{item.system.type}}</span>
</div>
{{/if}}
{{#if item.system.totem}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'IDENTITY.totem' }}:</span>
<span>{{ localize (lookup ../config.totems item.system.totem) }}</span>
</div>
{{/if}}
{{#if item.system.level.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.level' }}:</span>
<span>{{item.system.level.value}}</span>
</div>
{{/if}}
{{#if item.system.learn.threshold}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.learn' }}:</span>
<span>
{{item.system.learn.threshold}}
{{#ifgt item.system.learn.hindrance 0}}
/ {{item.system.learn.hindrance}}
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.effects}}
<div class="resource flexcol">
<span class="resource-label">{{ localize 'UI.effects' }}:</span>
<ul class="unstyled">
{{#each item.system.effects as |effect|}}
<li>{{effect}}</li>
{{/each}}
</ul>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol">
<span class="resource-label">{{ localize 'IDENTITY.notes' }}:</span>
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-43
View File
@@ -1,43 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}"/>
<div class="header-fields">
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name"/></h1>
</div>
</header>
<section class="sheet-body">
<h4 class="resource flexrow">
<label class="resource-label">{{ localize "VERMINE.level"}}</label>
<span class="hexa"><input type="number" name="system.level.value" value="{{system.level.value }}" data-dtype="Number" min="{{system.level.min }}" max="{{system.level.max }}"/>
</span>
<label class="resource-label">{{ localize "VERMINE.type"}}</label>
<select name="system.type" data-dtype="String">
<option value="character" {{#if (eq system.type "character")}} selected {{/if}}>Personnage</option>
<option value="group" {{#if (eq system.type "group")}} selected {{/if}}>Groupe</option>
<option value="creature" {{#if (eq system.type "creature")}} selected {{/if}}>Créature</option>
<option value="totem" {{#if (eq system.type "totem")}} selected {{/if}}>Totem</option>
</select>
<label class="resource-label">{{ localize "IDENTITY.totem"}}</label>
<select name="system.totem" data-dtype="String">
{{#each config.totems as | totem tk|}}
<option value="{{tk}}" {{#if (eq tk @root.system.totem)}} selected="selected" {{/if}}>{{ localize totem }}</option>
{{/each}}
</select>
</h4>
<h4 class="resource flexrow">
<label class="resource-label">{{ localize "ITEMS.learning"}}</label>
<span class=" hexa">
<input type="number" name="system.learning.threshold" value="{{system.learning.threshold }}" data-dtype="Number" min="3" max="10"/>
</span>/
<span class="hexa">
<input type="number" name="system.learning.hindrance" value="{{system.learning.hindrance }}" data-dtype="Number" min="0" max="3"/>
</span>
</h4>
<h4>description</h4>
{{editor system.description target="system.description" rollData=rollData button=true owner=owner editable=editable}}
</section>
</form>
+20
View File
@@ -0,0 +1,20 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Background specific content --}}
<div class="grid grid-2col">
{{#if item.system.cost}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.cost' }}:</span>
<span>{{item.system.cost}}</span>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-43
View File
@@ -1,43 +0,0 @@
<form
class="{{cssClass}}"
autocomplete="off"
>
<header class="sheet-header">
<img
class="profile-img"
src="{{item.img}}"
data-edit="img"
title="{{item.name}}"
/>
<div class="header-fields">
<h1 class="charname"><input
name="name"
type="text"
value="{{item.name}}"
placeholder="Name"
/></h1>
</div>
</header>
{{!-- Sheet Body --}}
<section class="sheet-body ">
<h4 class="resource-label flexrow">
<label class="resource-label">{{ localize "ITEMS.cost"}}</label>
<span class="hexa">
<input
type="number"
name="system.cost"
value="{{system.cost }}"
data-dtype="Number"
min="1"
max="2"
/>
</span>
</h4>
<h4>description</h4>
{{editor system.description target="system.description" rollData=rollData button=true owner=owner editable=editable}}
</div>
</section>
</form>
+102
View File
@@ -0,0 +1,102 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Defense specific content --}}
<div class="grid grid-2col">
{{#if item.system.level}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.level' }}:</span>
<span>{{item.system.level}}</span>
</div>
{{/if}}
{{#if item.system.specificLevel.level}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.specificLevel' }}:</span>
<span>
{{item.system.specificLevel.label}}
{{#if item.system.specificLevel.level}}
({{item.system.specificLevel.level}})
{{/if}}
</span>
</div>
{{/if}}
{{#if item.system.mobility}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.mobility' }}:</span>
<span>{{item.system.mobility}}</span>
</div>
{{/if}}
{{#if item.system.isShield}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'ITEMS.shield' }}</span>
</div>
{{/if}}
{{#if item.system.quantity}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.qty' }}:</span>
<span>{{item.system.quantity}}</span>
</div>
{{/if}}
{{#if item.system.weight}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.weight' }}:</span>
<span>{{item.system.weight}}</span>
</div>
{{/if}}
{{#if item.system.rarity.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.rarity' }}:</span>
<span>
{{#repeat item.system.rarity.value 1 "i"}}
{{romanNumber i}}
{{/repeat}}
{{#ifgt item.system.rarity.handicap 0}}
({{romanNumber item.system.rarity.handicap}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.traits}}
<div class="resource flexcol">
<span class="resource-label">{{ localize 'VERMINE.traits' }}:</span>
<ul class="unstyled">
{{#each item.system.traits as |trait key|}}
{{#if trait}}
<li>
<strong>{{localize (lookup ../config.traits key).name}}</strong>:
{{localize (lookup ../config.traits key).description}}
</li>
{{/if}}
{{/each}}
</ul>
</div>
{{/if}}
{{#if item.system.damages.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.damages' }}:</span>
<span class="damage-indicator">
{{getDamagesData item.system.damages "state"}}
{{#ifgt item.system.damages.value 1}}
({{item.system.damages.value}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.reliability}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.reliability' }}:</span>
<span>{{item.system.reliability}}</span>
</div>
{{/if}}
</div>
</form>
-13
View File
@@ -1,13 +0,0 @@
<div class="item-card" data-item-id="{{item.id}}">
{{log @root}}
<header class="flexrow align-center">
<img src="{{img}}" alt="image de {{item.name}}">
<h3>{{item.name}}</h3>
</header>
<section class="item-desc">
<p>{{{ item.system.description}}}</p>
</section>
</div>
+20
View File
@@ -0,0 +1,20 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Evolution specific content --}}
<div class="grid grid-2col">
{{#if item.system.level.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.level' }}:</span>
<span>{{item.system.level.value}}</span>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-21
View File
@@ -1,21 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}"/>
<div class="header-fields">
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name"/></h1>
</div>
</header>
<section class="sheet-body">
<h4 class="resource flexrow">
<label class="resource-label ">{{ localize "VERMINE.level"}}</label>
<span class="hexa"><input type="number" name="system.level.value" value="{{system.level.value }}" data-dtype="Number" min="{{system.level.min }}" max="{{system.level.max }}"/></span>
</h4>
<h2>description</h2>
{{editor system.description target="system.description" rollData=rollData button=true owner=owner editable=editable}}
</section>
</form>
+78
View File
@@ -0,0 +1,78 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Item specific content --}}
{{#if item.system.needSkill.value}}
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize 'VERMINE.competence' }} {{ localize 'VERMINE.needed' }}</label>
<p>{{ smarttlk 'SKILLS' item.system.needSkill.skill 'name' }}</p>
</div>
{{/if}}
{{!-- Traits --}}
{{#if item.system.traits}}
<div class="resource align-center flexcol">
<label class="resource-label">{{ localize 'VERMINE.traits' }}</label>
<ul class="unstyled">
{{#each item.system.traits as |trait key|}}
{{#if trait}}
<li>
<strong>{{localize (lookup ../config.traits key).name}}</strong>:
{{localize (lookup ../config.traits key).description}}
</li>
{{/if}}
{{/each}}
</ul>
</div>
{{/if}}
{{!-- Physical item properties --}}
{{#if item.system.quantity}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.quantity' }}:</span>
<span>{{item.system.quantity}}</span>
</div>
{{/if}}
{{#if item.system.weight}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.weight' }}:</span>
<span>{{item.system.weight}}</span>
</div>
{{/if}}
{{#if item.system.rarity.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.rarity' }}:</span>
<span>
{{#repeat item.system.rarity.value 1 "i"}}
{{romanNumber i}}
{{/repeat}}
{{#ifgt item.system.rarity.handicap 0}}
({{romanNumber item.system.rarity.handicap}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.damages.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.damages' }}:</span>
<span class="damage-indicator">
{{getDamagesData item.system.damages "state"}}
{{#ifgt item.system.damages.value 1}}
({{item.system.damages.value}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{!-- Reliability --}}
{{#if item.system.reliability}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.reliability' }}:</span>
<span>{{item.system.reliability}}</span>
</div>
{{/if}}
</form>
-38
View File
@@ -1,38 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
{{log this}}
{{> "systems/vermine2047/templates/item/partials/header.hbs"}}
{{!-- Sheet Body --}}
<section class="sheet-body ">
{{> "systems/vermine2047/templates/item/partials/traits.html"}}
<div class="resource align-center flexcol">
<label class="resource-label">compétence necessaire
<input type="checkbox" data-tooltip="ajouter la vigueur" name="system.needSkill.value" {{#if system.needSkill.value}} checked {{/if}}>
</label>
{{#if system.needSkill.value}}
<select name="system.needSkill.skill" class="skill-select">
<option value="">aucune</option>
{{log config}}
{{#each @root.config.skillCategories as |skillCategory sckey|}}
<optgroup label="{{ smarttlk 'SKILLS_CATEGORIES' sckey 'name' }}">
{{#each @root.config.model.Actor.character.skills as |skill key|}}
{{#ife skill.category sckey}}
<option value="{{sckey}}" {{#ife sckey @root.system.needSkill.skill}} selected {{/ife}}>{{ smarttlk 'SKILLS' key 'name' }}</option>
{{/ife}}
{{/each}}
</optgroup>
{{/each}}
</select>
{{/if}}
</div>
{{> "systems/vermine2047/templates/item/partials/physicalItems.hbs"}}
</section>
</form>
@@ -1,11 +1,11 @@
<header class="flexrow align-center"> <header class="flexrow align-center">
<img <img
src="{{item.img}}" src="{{item.img}}"
alt="image de {{item.name}}" alt="image de {{item.name}}"
> >
<h3>{{item.name}}</h3> <h3>{{item.name}}</h3>
</header> </header>
<section class="item-desc"> <section class="item-desc">
<p>{{{ item.system.description }}}</p> <p>{{{ item.system.description }}}</p>
</section> </section>
+41
View File
@@ -0,0 +1,41 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Rite specific content --}}
<div class="grid grid-2col">
{{#if item.system.rituel}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.ritual' }}:</span>
<span>{{item.system.rituel}}</span>
</div>
{{/if}}
{{#if item.system.transe}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.trance' }}:</span>
<span>{{item.system.transe}}</span>
</div>
{{/if}}
{{#if item.system.ability}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.ability' }}:</span>
<span>{{item.system.ability}}</span>
</div>
{{/if}}
{{#if item.system.effect}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'UI.effects' }}:</span>
<span>{{item.system.effect}}</span>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-41
View File
@@ -1,41 +0,0 @@
{{!-- This template is a fallback for when items don't have more specific templates. --}}
{{!-- Generally, you'll want to make more specific templates when possible. --}}
<form class="{{cssClass}} form" autocomplete="off">
{{> "systems/vermine2047/templates/item/partials/header.hbs"}}
{{!-- Sheet Body --}}
<section class="sheet-body">
<aside style="flex:20%;">
<div class="resource">
<label class="resource-label">{{ localize "VERMINE.ability"}}</label>
<select name="system.ability" data-dtype="String">
<option value="">-- Aucune --</option>
{{#each config.abilities as |ability akey| }}
<option value="{{ akey }}" {{#if (eq akey @root.system.ability)}} selected="selected" {{/if}}>{{ localize ability}}</option>
{{/each}}
</select>
</div>
</aside>
<main style="flex:10">
<div class="editor-wrapper">
{{editor system.description target="system.description" button=true owner=owner editable=editable}}
</div>
</main>
<div class="grid">
<div>
<h4>{{ localize 'ITEMS.rituel'}}</h4>
<textarea name="system.rituel">{{ system.rituel }}</textarea>
</div>
<div>
<h4>{{ localize 'ITEMS.transe'}}</h4>
<textarea name="system.transe">{{ system.transe }}</textarea>
</div>
</div>
<h4>{{ localize 'ITEMS.effects'}}</h4>
<div class="editor-wrapper">
{{editor system.effects target="system.effects" button=true owner=owner editable=editable}}
</div>
</div>
</form>
+13
View File
@@ -0,0 +1,13 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Rumor specific content --}}
<div class="grid grid-2col">
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-11
View File
@@ -1,11 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/partials/header.hbs"}}
{{!-- Sheet Body --}}
<section class="sheet-body">
{{editor system.description target="system.description" rollData=rollData button=true owner=owner editable=editable}}
</section>
</form>
+20
View File
@@ -0,0 +1,20 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Specialty specific content --}}
<div class="grid grid-2col">
{{#if item.system.skill}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.skill_title' }}:</span>
<span>{{ smarttlk 'SKILLS' item.system.skill 'name' }}</span>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-27
View File
@@ -1,27 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}"/>
<div class="header-fields">
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name"/></h1>
<select name="system.skill" class="skill-select">
{{#each @root.config.skillCategories as |skillCategory sckey|}}
{{log skillCategory}}
<optgroup label="{{ smarttlk 'SKILLS_CATEGORIES' sckey 'name' }}">
{{#each @root.config.model.Actor.character.skills as |skill key|}}
{{#ife skill.category sckey}}
<option value="{{key}}" {{#ife key @root.item.system.skill}} selected {{/ife}}>{{ smarttlk 'SKILLS' key 'name' }}</option>
{{/ife}}
{{/each}}
</optgroup>
{{/each}}
</select>
</div>
</header>
</form>
+20
View File
@@ -0,0 +1,20 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Target specific content --}}
<div class="grid grid-2col">
{{#if item.system.level}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.level' }}:</span>
<span>{{item.system.level}}</span>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-11
View File
@@ -1,11 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/partials/header.hbs"}}
{{!-- Sheet Body --}}
<section class="sheet-body">
{{editor system.description target="system.description" rollData=rollData button=true owner=owner editable=editable}}
</section>
</form>
+20
View File
@@ -0,0 +1,20 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Trauma specific content --}}
<div class="grid grid-2col">
{{#if item.system.type}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.type' }}:</span>
<span>{{item.system.type}}</span>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-24
View File
@@ -1,24 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/partials/header.hbs"}}
{{!-- Sheet Body --}}
<section class="sheet-body">
<div class="tb flexcol">
<h4 class="resource">
<label class="resource-label">{{ localize "VERMINE.type"}}</label>
<select name="system.type" data-dtype="String">
<option value="physical" {{#if (eq system.type "physical")}} selected {{/if}}>Physiologique</option>
<option value="psychological" {{#if (eq system.type "psychological")}} selected {{/if}}>Psychologique</option>
</select>
</h4>
<h2>description</h2>
{{editor system.description target="system.description" rollData=rollData
button=true owner=owner editable=editable}}
</div>
</section>
</form>
+83
View File
@@ -0,0 +1,83 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Vehicle specific content --}}
<div class="grid grid-2col">
{{#if item.system.mobility}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.mobility' }}:</span>
<span>{{item.system.mobility}}</span>
</div>
{{/if}}
{{#if item.system.quantity}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.qty' }}:</span>
<span>{{item.system.quantity}}</span>
</div>
{{/if}}
{{#if item.system.weight}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.weight' }}:</span>
<span>{{item.system.weight}}</span>
</div>
{{/if}}
{{#if item.system.rarity.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.rarity' }}:</span>
<span>
{{#repeat item.system.rarity.value 1 "i"}}
{{romanNumber i}}
{{/repeat}}
{{#ifgt item.system.rarity.handicap 0}}
({{romanNumber item.system.rarity.handicap}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.damages.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.damages' }}:</span>
<span class="damage-indicator">
{{getDamagesData item.system.damages "state"}}
{{#ifgt item.system.damages.value 1}}
({{item.system.damages.value}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.reliability}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.reliability' }}:</span>
<span>{{item.system.reliability}}</span>
</div>
{{/if}}
{{#if item.system.traits}}
<div class="resource flexcol">
<span class="resource-label">{{ localize 'VERMINE.traits' }}:</span>
<ul class="unstyled">
{{#each item.system.traits as |trait key|}}
{{#if trait}}
<li>
<strong>{{localize (lookup ../config.traits key).name}}</strong>:
{{localize (lookup ../config.traits key).description}}
</li>
{{/if}}
{{/each}}
</ul>
</div>
{{/if}}
{{#if item.system.description}}
<div class="resource flexcol full-width">
<p>{{{item.system.description}}}</p>
</div>
{{/if}}
</div>
</form>
-23
View File
@@ -1,23 +0,0 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/partials/header.hbs"}}
<section class="sheet-body">
{{> "systems/vermine2047/templates/item/partials/traits.html"}}
<div class="flexrow">
<div class="resource flexrow align-center">
<label class="resource-label">{{ localize "VERMINE.mobility"}}</label>
<div class="hexa">
<input type="number" name="system.mobility" value="{{system.mobility}}" data-dtype="Number"/>
</div>
</div>
</div>
{{> "systems/vermine2047/templates/item/partials/physicalItems.hbs"}}
</section>
</form>
+104
View File
@@ -0,0 +1,104 @@
<form class="{{cssClass}}" autocomplete="off">
{{> "systems/vermine2047/templates/item/chatCards/parts/base.hbs"}}
{{!-- Weapon specific content --}}
<div class="grid grid-2col">
{{#if item.system.damage.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.damage' }}:</span>
<span>
{{item.system.damage.value}}
{{#if item.system.damage.type}}
({{item.system.damage.type}})
{{/if}}
{{#if item.system.damage.addVigor}}
+ {{ localize 'ABILITIES.vigor.name' }}
{{/if}}
</span>
</div>
{{/if}}
{{#if item.system.ammo}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.ammo' }}:</span>
<span>{{item.system.ammo}}</span>
</div>
{{/if}}
{{#if item.system.min_range}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.range' }}:</span>
<span>
{{item.system.min_range}}
{{#if item.system.max_range}}
- {{item.system.max_range}}
{{/if}}
</span>
</div>
{{/if}}
{{#if item.system.quantity}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.qty' }}:</span>
<span>{{item.system.quantity}}</span>
</div>
{{/if}}
{{#if item.system.weight}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.weight' }}:</span>
<span>{{item.system.weight}}</span>
</div>
{{/if}}
{{#if item.system.rarity.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.rarity' }}:</span>
<span>
{{#repeat item.system.rarity.value 1 "i"}}
{{romanNumber i}}
{{/repeat}}
{{#ifgt item.system.rarity.handicap 0}}
({{romanNumber item.system.rarity.handicap}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.traits}}
<div class="resource flexcol">
<span class="resource-label">{{ localize 'VERMINE.traits' }}:</span>
<ul class="unstyled">
{{#each item.system.traits as |trait key|}}
{{#if trait}}
<li>
<strong>{{localize (lookup ../config.traits key).name}}</strong>:
{{localize (lookup ../config.traits key).description}}
</li>
{{/if}}
{{/each}}
</ul>
</div>
{{/if}}
{{#if item.system.damages.value}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.damages' }}:</span>
<span class="damage-indicator">
{{getDamagesData item.system.damages "state"}}
{{#ifgt item.system.damages.value 1}}
({{item.system.damages.value}})
{{/ifgt}}
</span>
</div>
{{/if}}
{{#if item.system.reliability}}
<div class="resource flexrow">
<span class="resource-label">{{ localize 'VERMINE.reliability' }}:</span>
<span>{{item.system.reliability}}</span>
</div>
{{/if}}
</div>
</form>
-7
View File
@@ -1,7 +0,0 @@
<form
class="{{cssClass}}"
autocomplete="off"
>
{{> "systems/vermine2047/templates/item/chatCards/parts/base.html"}}
</form>
@@ -1,30 +1,30 @@
<h3>dommages</h3> <h3>dommages</h3>
<div class="damages-row flexrow"> <div class="damages-row flexrow">
<div class="radios flexrow"> <div class="radios flexrow">
{{#repeat system.damages.max 1 index}} {{#repeat system.damages.max 1 index}}
<div class="hexa <div class="hexa
{{#ifgteq system.damages.value @index }} {{#ifgteq system.damages.value @index }}
checked checked
{{/ifgteq }}"> {{/ifgteq }}">
<input <input
type="radio" type="radio"
name="system.damages.value" name="system.damages.value"
value="{{@index}}" value="{{@index}}"
{{#ife system.damages.value @index }} {{#ife system.damages.value @index }}
checked="true" checked="true"
{{/ife }} {{/ife }}
> >
</div> </div>
{{/repeat}} {{/repeat}}
</div> </div>
<div class="damage-pannes"> <div class="damage-pannes">
<h4>pannes : <small>{{getDamagesData system.damages "pannes"}}</small></h4> <h4>pannes : <small>{{getDamagesData system.damages "pannes"}}</small></h4>
</div> </div>
<div class="damage-state"> <div class="damage-state">
<h4>état : <small>{{getDamagesData system.damages "state"}}</small> </h4> <h4>état : <small>{{getDamagesData system.damages "state"}}</small> </h4>
</div> </div>
<div class="damage-effect"> <div class="damage-effect">
<h4> effets : <small>{{getDamagesData system.damages "effect"}}</small> </h4> <h4> effets : <small>{{getDamagesData system.damages "effect"}}</small> </h4>
</div> </div>
</div> </div>
@@ -1,17 +1,17 @@
<div class="traits"> <div class="traits">
<h3>traits <h3>traits
<span class="traits-selector" data-tooltip="ajouter/supprimer un trait"> <span class="traits-selector" data-tooltip="ajouter/supprimer un trait">
<i class="fas fa-cogs"></i> <i class="fas fa-cogs"></i>
</span> </span>
</h3> </h3>
<h4> <h4>
{{#each item.system.traits as | trait index|}} {{#each item.system.traits as | trait index|}}
<span data-tooltip="{{trait.description}}">{{trait.name}} <span data-tooltip="{{trait.description}}">{{trait.name}}
{{#if trait.value}} {{#if trait.value}}
<input type="number" class="hexa" value="{{trait.value}}" name="system.traits.{{index}}.value"> <input type="number" class="hexa" value="{{trait.value}}" name="system.traits.{{index}}.value">
{{/if}} {{/if}}
</span> </span>
{{/each}} {{/each}}
</h4> </h4>
</div> </div>