Compare commits
13
Commits
412573fc4d
..
14.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89d9d71395 | ||
|
|
1e298ed809 | ||
|
|
4ffaebf52b | ||
|
|
de7691c4dc | ||
|
|
c87b25fbaf | ||
|
|
6ce51ed8cf | ||
|
|
df852f17d1 | ||
|
|
8916600bf6 | ||
|
|
0189f62734 | ||
|
|
29b6a98c89 | ||
|
|
b1d73e6b08 | ||
|
|
509a5efd9d | ||
|
|
fea7c1c271 |
@@ -19,6 +19,9 @@ foundry
|
||||
# Ancien dossier SCSS (archivé)
|
||||
scss_old/
|
||||
|
||||
# Outils d'audit local
|
||||
.swival/
|
||||
|
||||
# Fichiers CSS générés
|
||||
css/vermine2047.*.css
|
||||
!css/vermine2047.css
|
||||
|
||||
-141
@@ -1,141 +0,0 @@
|
||||
# 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
|
||||
- ajout des historiques
|
||||
- ajout des rites
|
||||
|
||||
## 0.1.12
|
||||
- passage des packs en LevelDB
|
||||
|
||||
## 0.1.11
|
||||
- passage à une compatibilité v11
|
||||
|
||||
## 0.1.10
|
||||
- révision du template évolution
|
||||
- révision du template traumatisme
|
||||
- ajout du type rite
|
||||
- test import de pack
|
||||
|
||||
## 0.1.9
|
||||
- versions condensées des méthodes (@kristov)
|
||||
- renommage des templates
|
||||
- image actors par défaut
|
||||
- image des créatures
|
||||
- ajout du totem et de l'apprentissage dans les capacités
|
||||
|
||||
## 0.1.8
|
||||
- refactoring sheet vers actor
|
||||
|
||||
## 0.1.7
|
||||
- sang-froid, effort, et santé
|
||||
|
||||
## 0.1.6
|
||||
- finition de l'onglet totem
|
||||
- ajout du logo de totem dans la sidebar
|
||||
- ajout de traductions manquantes
|
||||
|
||||
## 0.1.5
|
||||
- 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)
|
||||
|
||||
---
|
||||
|
||||
## 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*
|
||||
@@ -0,0 +1,7 @@
|
||||
Code Author :
|
||||
- Developed by LeRatierBretonnien / www.uberwald.me
|
||||
|
||||
Code LICENCE :
|
||||
C BY-NC-SA 4.0
|
||||
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||
This license requires that reusers give credit to the creator. It allows reusers to distribute, remix, adapt, and build upon the material in any medium or format, for noncommercial purposes only. If others modify or adapt the material, they must license the modified material under identical terms.
|
||||
@@ -1,51 +1,53 @@
|
||||
# Vermine 2047 — Foundry VTT System
|
||||
# Vermine 2047 — Système Foundry VTT
|
||||
|
||||

|
||||

|
||||
|
||||
Unofficial game system for **Vermine 2047**, a French post-apocalyptic tabletop RPG by **Éditions John Doe** (https://www.kickandgo.net/boutique-agate/category.php?cl=UyQcRrFtYcYrAnLbOfDj&idp=20).
|
||||
Système non-officiel pour le jeu de rôle **Vermine 2047**, univers post-apo français par **Studio Agate**.
|
||||
|
||||
Bilingual **FR/EN** interface.
|
||||
► Achetez le jeu : https://www.kickandgo.net/boutique-agate/category.php?cl=UyQcRrFtYcYrAnLbOfDj&idp=20
|
||||
|
||||
Interface bilingue **FR/EN**.
|
||||
|
||||
---
|
||||
|
||||
## Actor Types
|
||||
## Types d'Acteurs
|
||||
|
||||
| Type | Sheet | Description |
|
||||
| Type | Fiche | Description |
|
||||
|------|-------|-------------|
|
||||
| **character** | AppV2 | Player character with skills, abilities, totem, equipment |
|
||||
| **npc** | AppV2 | Non-player character with role/threat, wound thresholds |
|
||||
| **group** | AppV2 | Group with members, encounters, link sync |
|
||||
| **creature** | AppV2 | Creature with template/size/role, custom attack stats |
|
||||
| **character** | AppV2 | Personnage joueur — compétences, capacités, totem, équipement |
|
||||
| **npc** | AppV2 | PNJ avec rôle/menace, seuils de blessure |
|
||||
| **group** | AppV2 | Groupe avec membres, rencontres, synchro liée |
|
||||
| **creature** | AppV2 | Créature avec gabarit/taille/rôle, attaques custom |
|
||||
|
||||
## Item Types
|
||||
## Types d'Objets
|
||||
|
||||
| Type | Use |
|
||||
|------|-----|
|
||||
| **item** | Generic items (gear, resources) |
|
||||
| **weapon** | Weapons with damage, range, reliability, rarity |
|
||||
| **defense** | Armor/shields with protection, coverage |
|
||||
| **vehicle** | Vehicles with speed, structure, weapons |
|
||||
| **ability** | Special abilities (apprenticeship, domain) |
|
||||
| **specialty** | Skill specializations (attached to skills) |
|
||||
| **background** | Character backgrounds |
|
||||
| **trauma** | Mental/physical trauma |
|
||||
| **evolution** | Character evolutions |
|
||||
| **rumor** | Rumors (group) |
|
||||
| **target** | Targets (group) |
|
||||
| **rite** | Rites (magic/rituals) |
|
||||
| Type | Utilisation |
|
||||
|------|-------------|
|
||||
| **item** | Objets génériques (matériel, ressources) |
|
||||
| **weapon** | Armes — dégâts, portée, fiabilité, rareté |
|
||||
| **defense** | Armures/boucliers — protection, couverture |
|
||||
| **vehicle** | Véhicules — vitesse, structure, armement |
|
||||
| **ability** | Capacités spéciales (apprentissage, domaine) |
|
||||
| **specialty** | Spécialités de compétence |
|
||||
| **background** | Historiques de personnage |
|
||||
| **trauma** | Traumatismes physiques/mentaux |
|
||||
| **evolution** | Évolutions du personnage |
|
||||
| **rumor** | Rumeurs (groupe) |
|
||||
| **target** | Cibles (groupe) |
|
||||
| **rite** | Rites (magie/rituels) |
|
||||
|
||||
## Dice System
|
||||
## Système de Dés
|
||||
|
||||
- **d10-based success counting**: `Xd10cs>=N`
|
||||
- Difficulty = number of successes required
|
||||
- Totems give domain bonuses (human/adapted dice)
|
||||
- Rerolls via Effort and Sang-froid (Composure)
|
||||
- Automatic successes and failure thresholds
|
||||
- **Dice So Nice!** 3D dice integration with custom d10 face images
|
||||
- **Comptage de succès sur d10** : `Xd10cs>=N`
|
||||
- Difficulté = nombre de succès requis
|
||||
- Les totems donnent des bonus de domaine (dés humains/adaptés)
|
||||
- Relances via l'Effort et le Sang-froid
|
||||
- Succès automatiques et seuils d'échec
|
||||
- Intégration **Dice So Nice!** avec faces de d10 personnalisées
|
||||
|
||||
### Initiative
|
||||
Formula: `(@abilities.reflexes.value + @skills.alertness.value)d10cs>=@combatStatus.difficulty`
|
||||
Formule : `(@abilities.reflexes.value + @skills.alertness.value)d10cs>=@combatStatus.difficulty`
|
||||
|
||||
---
|
||||
|
||||
@@ -53,68 +55,82 @@ Formula: `(@abilities.reflexes.value + @skills.alertness.value)d10cs>=@combatSta
|
||||
|
||||
```
|
||||
module/
|
||||
├── vermine2047.mjs # Entry point (Hooks.once init)
|
||||
├── vermine2047.mjs # Point d'entrée (Hooks.once init)
|
||||
├── models/ # DataModels (Foundry v2 TypeDataModel)
|
||||
│ ├── actor/ # character.mjs, npc.mjs, group.mjs, creature.mjs
|
||||
│ └── item/ # All 12 item types
|
||||
│ └── item/ # Les 12 types d'objets
|
||||
├── documents/ # VermineActor, VermineItem
|
||||
├── applications/
|
||||
│ └── sheets/ # AppV2 sheets per type
|
||||
│ └── sheets/ # Fiches AppV2 par type
|
||||
├── system/
|
||||
│ ├── config.mjs # CONFIG.VERMINE (totems, traits, skills…)
|
||||
│ ├── config.mjs # CONFIG.VERMINE (totems, traits, compétences…)
|
||||
│ ├── roll.mjs # VermineUtils.roll()
|
||||
│ ├── fight.mjs # Combat system, VermineCombat, VermineCombatant
|
||||
│ ├── functions.mjs # Shared helpers (skill scores, etc.)
|
||||
│ ├── hooks.mjs # Hook registrations (DiceSoNice, chat, pause…)
|
||||
│ ├── dice3d.mjs # Dice So Nice colors, initUserDice
|
||||
│ ├── fight.mjs # Système de combat, VermineCombat, VermineCombatant
|
||||
│ ├── functions.mjs # Fonctions partagées
|
||||
│ ├── hooks.mjs # Hooks (DiceSoNice, chat, pause…)
|
||||
│ ├── dice3d.mjs # Couleurs Dice So Nice, initUserDice
|
||||
│ ├── dialogs/rollDialog.mjs
|
||||
│ ├── settings.mjs # System settings (game-mode, etc.)
|
||||
│ ├── tour.mjs # Tours (stub)
|
||||
│ └── group-link.mjs # GroupLink: auto-sync actors↔groups
|
||||
│ ├── settings.mjs # Paramètres du système (mode de jeu…)
|
||||
│ ├── tour.mjs # Visites guidées (stub)
|
||||
│ └── group-link.mjs # GroupLink : synchro acteurs↔groupes
|
||||
templates/
|
||||
├── actor/appv2/ # AppV2 actor sheet templates
|
||||
├── actor/ # Legacy templates (coexisting)
|
||||
└── item/ # Item sheet templates
|
||||
├── actor/appv2/ # Templates AppV2 des fiches acteurs
|
||||
├── actor/ # Templates legacy (coexistence)
|
||||
└── item/ # Templates des fiches objets
|
||||
css/
|
||||
└── vermine2047.min.css # Compiled from LESS
|
||||
└── vermine2047.min.css # Compilé depuis LESS
|
||||
less/
|
||||
└── base.less # Main stylesheet (LESS source)
|
||||
└── base.less # Source LESS principale
|
||||
```
|
||||
|
||||
## Key Objects
|
||||
## Objets Clés
|
||||
|
||||
- `game.vermine2047` → `{ VermineUtils, VermineCombat, GroupLink }`
|
||||
- `CONFIG.VERMINE` → totems, traits, skills, domains, combat statuses…
|
||||
- `CONFIG.ui.combat` → custom `VermineCombatTracker`
|
||||
- `CONFIG.VERMINE` → totems, traits, compétences, domaines, états de combat…
|
||||
- `CONFIG.ui.combat` → `VermineCombatTracker` personnalisé
|
||||
|
||||
## Commands
|
||||
## Commandes
|
||||
|
||||
```sh
|
||||
npm run build:less # LESS → css/vermine2047.min.css
|
||||
npm run build:less:dev # LESS → css/vermine2047.dev.css (unminified)
|
||||
npm run build:all-css # Both
|
||||
npm run build:less:dev # LESS → css/vermine2047.dev.css (non minifié)
|
||||
npm run build:all-css # Les deux
|
||||
npm run watch # Gulp watch + browser-sync → localhost:30000
|
||||
npm run lint:less # LESS lint
|
||||
npm run pushLDBtoYAML # Extract packs → src/packs/*.yml
|
||||
npm run pullYAMLtoLDB # Compile packs → packs/* (LevelDB)
|
||||
make build|watch|lint # Makefile wrappers
|
||||
npm run lint:less # Vérification LESS
|
||||
npm run pushLDBtoYAML # Extraire les packs → src/packs/*.yml
|
||||
npm run pullYAMLtoLDB # Compiler les packs → packs/* (LevelDB)
|
||||
make build|watch|lint # Raccourcis Makefile
|
||||
```
|
||||
|
||||
### Requirements
|
||||
- Node.js `v16.15.1` (see `.nvmrc`)
|
||||
### Prérequis
|
||||
- Node.js `v16.15.1` (cf `.nvmrc`)
|
||||
- Foundry VTT **v14**
|
||||
|
||||
## Development Notes
|
||||
## Notes de Développement
|
||||
|
||||
- **DataModels before Document classes**: In `init` hook, register `CONFIG.Actor.dataModels.*` and `CONFIG.Item.dataModels.*` **before** setting `CONFIG.Actor.documentClass` / `CONFIG.Item.documentClass`.
|
||||
- **Two template systems**: AppV2 (`templates/actor/appv2/`) and legacy (`templates/actor/*.hbs`) coexist — don't confuse them.
|
||||
- **Packs**: Edit YAML source in `src/packs/*.yml`, then run `pullYAMLtoLDB` to compile to LevelDB.
|
||||
- **CSS**: Declared in `system.json` styles — do **not** inject `<link>` manually.
|
||||
- **ESLint**: ~350 rules. Run `npx eslint module/` before committing. No `console.log` (use `console.error` sparingly).
|
||||
- **editMode**: Not auto-set on actor creation (causes ActiveEffect errors). Enable manually via sheet checkbox.
|
||||
- **GroupLink**: Auto-syncs on actor update. Changes propagate bidirectionally.
|
||||
- **HotReload**: Enabled for css, scss, hbs, json in `system.json`.
|
||||
- **DataModels avant Document classes** : Dans le hook `init`, enregistrer `CONFIG.Actor.dataModels.*` et `CONFIG.Item.dataModels.*` **avant** de définir `CONFIG.Actor.documentClass` / `CONFIG.Item.documentClass`.
|
||||
- **Deux systèmes de templates** : AppV2 (`templates/actor/appv2/`) et legacy (`templates/actor/*.hbs`) coexistent.
|
||||
- **Packs** : Éditer les sources YAML dans `src/packs/*.yml`, puis lancer `pullYAMLtoLDB`.
|
||||
- **CSS** : Déclaré dans `system.json` — ne **pas** injecter de `<link>` manuellement.
|
||||
- **ESLint** : ~350 règles. Lancer `npx eslint module/` avant chaque commit. Pas de `console.log`.
|
||||
- **editMode** : Ne se met plus automatiquement à la création d'acteur (cause des erreurs ActiveEffect). Activer manuellement via la case à cocher.
|
||||
- **GroupLink** : Synchronisation automatique bidirectionnelle acteurs↔groupes.
|
||||
- **HotReload** : Activé pour css, scss, hbs, json dans `system.json`.
|
||||
|
||||
## License
|
||||
---
|
||||
|
||||
This system is an unofficial fan project. The Vermine 2047 setting and trademarks belong to Éditions John Doe.
|
||||
## English Summary
|
||||
|
||||
**Vermine 2047** is a French post-apocalyptic TTRPG. This is an unofficial Foundry VTT system (v14).
|
||||
|
||||
- **4 actor types**: character, npc, group, creature
|
||||
- **12 item types**: item, weapon, defense, vehicle, ability, specialty, background, trauma, evolution, rumor, target, rite
|
||||
- **Dice**: d10 success-counting with totem domains, Effort rerolls, Dice So Nice! 3D integration
|
||||
- **Sheets**: AppV2 with tab navigation, edit/play mode toggle
|
||||
- **Bilingual**: FR/EN interface
|
||||
|
||||
---
|
||||
|
||||
## Licence
|
||||
|
||||
Projet fan non-officiel. L'univers et les marques Vermine 2047 appartiennent aux Éditions John Doe.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# vermine2047 Audit Findings
|
||||
|
||||
Security audit of `vermine2047` at commit `b1d73e6b08bf`. Each finding links to a detailed write-up (`NNN-*.md`) and a proposed patch (`NNN-*.patch`).
|
||||
|
||||
## Summary
|
||||
|
||||
**Total findings: 0**
|
||||
|
||||
## Findings
|
||||
|
||||
## Dropped in adjudication
|
||||
|
||||
These findings reproduced in Phase 4 but the Phase 4.5 gate judged them false positives or out of a realistic threat model.
|
||||
|
||||
- **Unpinned mutable CI action enabling supply chain hijacking** (high, .gitea/workflows/release.yaml): Malformed URL in `uses` breaks workflow parsing; step is unreachable and prevents execution entirely.; Relies on improbable upstream compromise; standard configuration gap rather than immediate vulnerability.
|
||||
|
||||
## Run details
|
||||
|
||||
- run id: `aa4595a1`
|
||||
- commit: `b1d73e6b08bf7ed0157a03137cc3bb50c8d3a26c`
|
||||
- branch: `main`
|
||||
- scope: 214 file(s)
|
||||
- files triaged: 214
|
||||
- files escalated: 14
|
||||
- files deep-reviewed: 14
|
||||
- findings proposed: 1
|
||||
- findings verified: 0
|
||||
- findings discarded: 0
|
||||
- findings dropped in adjudication: 1
|
||||
- artifacts: 0 written, 0 failed, 0 pending
|
||||
|
||||
## How to read this directory
|
||||
|
||||
Each finding has a paired `NNN-<slug>.md` (the human report) and `NNN-<slug>.patch` (the proposed fix as a unified diff). The report carries the narrative; the patch is the suggested change. Regenerate a single finding's artifacts with `/audit --regen --finding N`.
|
||||
+10
-3
@@ -31,7 +31,7 @@
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.char-name-value {
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
input:not([type="radio"]):not([type="checkbox"]) {
|
||||
font-size: 1rem;
|
||||
@@ -291,7 +291,7 @@
|
||||
|
||||
.chooseTotem {
|
||||
font-family: "DistressBlack", sans-serif;
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
@@ -377,7 +377,7 @@
|
||||
}
|
||||
|
||||
.char-vermine2047 {
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,6 +409,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ── Onglet Histoire : doubler la hauteur des zones Notes et Relations ──
|
||||
.tab.stories {
|
||||
prose-mirror.editor {
|
||||
min-height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
nav.tabs[data-group="sheet"] {
|
||||
display: inline-flex;
|
||||
justify-content: space-around;
|
||||
|
||||
+18
-3
@@ -20,16 +20,25 @@
|
||||
|
||||
div.minor-totems {
|
||||
position: relative;
|
||||
height: 8.5rem;
|
||||
background-color: rgba(146, 156, 111, 0.5215686275);
|
||||
|
||||
h3 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
h5 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: 2.6rem;
|
||||
margin: 0;
|
||||
z-index: 1;
|
||||
|
||||
img {
|
||||
max-width: 2rem;
|
||||
position: absolute;
|
||||
bottom: -2rem;
|
||||
top: 1.4rem;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
&.human, &.adapted {
|
||||
@@ -53,6 +62,12 @@
|
||||
}
|
||||
|
||||
.totem-dice {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
|
||||
.human-dice,
|
||||
.adapted-dice {
|
||||
display: flex;
|
||||
@@ -90,7 +105,7 @@
|
||||
|
||||
.adapted {
|
||||
right: 0;
|
||||
img { right: 0; }
|
||||
img { right: 0; left: auto; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +230,17 @@ export default class VermineBaseActorSheet extends HandlebarsApplicationMixin(fo
|
||||
const id = target.closest("[data-item-id]")?.dataset?.itemId
|
||||
if (!id) return
|
||||
const item = this.document.items.get(id)
|
||||
if (!item) return
|
||||
if (item.type === "weapon" && this.document.system.abilities && this.document.system.skills) {
|
||||
const { default: RollDialog } = await import("../../system/dialogs/rollDialog.mjs")
|
||||
const dialog = await RollDialog.create({
|
||||
actorId: this.document.id,
|
||||
rolltype: "skill",
|
||||
label: item.system.skill
|
||||
})
|
||||
if (dialog) dialog.render(true)
|
||||
return
|
||||
}
|
||||
item?.roll()
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ export class VermineBaseItemSheet extends HandlebarsApplicationMixin(foundry.app
|
||||
// ── Rendu ───────────────────────────────────────────────────────────
|
||||
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
this.#dragDrop.forEach(d => d.bind(this.element))
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class VermineItem extends Item {
|
||||
*/
|
||||
getRollData() {
|
||||
// If present, return the actor's roll data.
|
||||
if (!this.actor) return null;
|
||||
if (!this.actor) return this.system;
|
||||
const rollData = this.actor.getRollData();
|
||||
// Grab the item's system data as well.
|
||||
rollData.item = foundry.utils.deepClone(this.system);
|
||||
|
||||
@@ -14,6 +14,7 @@ export default class VermineWeaponData extends foundry.abstract.TypeDataModel {
|
||||
const reqInt = { required: true, nullable: false, integer: true }
|
||||
return {
|
||||
...baseItemSchema(),
|
||||
skill: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||||
min_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||
max_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||
damage: new fields.SchemaField({
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
|
||||
|
||||
export class TotemPicker extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
|
||||
constructor(linkEl, actor) {
|
||||
@@ -24,9 +26,9 @@ export class TotemPicker extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
return { config: CONFIG.VERMINE };
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
html.querySelectorAll('.totem').forEach(el => {
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
this.element.querySelectorAll('.totem').forEach(el => {
|
||||
el.addEventListener('click', event => {
|
||||
const link = event.target.closest('a');
|
||||
if (!link?.dataset.totem) return;
|
||||
@@ -80,9 +82,9 @@ export class ActorPicker extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
};
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
html.querySelectorAll('.actor').forEach(el => {
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
this.element.querySelectorAll('.actor').forEach(el => {
|
||||
el.addEventListener('click', async event => {
|
||||
const div = event.target.closest('div');
|
||||
const actorId = div?.dataset.actorId;
|
||||
@@ -148,15 +150,15 @@ export class TraitSelector extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
};
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
this._validateTraits(html);
|
||||
html.querySelectorAll('input').forEach(el => {
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
this._validateTraits(this.element);
|
||||
this.element.querySelectorAll('input').forEach(el => {
|
||||
el.addEventListener('change', ev => this._onChangeInput(ev));
|
||||
});
|
||||
}
|
||||
|
||||
async _validateTraits(html) {
|
||||
_validateTraits(html) {
|
||||
const checks = html.querySelectorAll("input.trait-selector");
|
||||
for (const inp of checks) {
|
||||
if (this.targetItem.system.traits[inp.dataset.trait]) {
|
||||
@@ -165,7 +167,6 @@ export class TraitSelector extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.render(true);
|
||||
}
|
||||
|
||||
async _onChangeInput(ev) {
|
||||
|
||||
@@ -121,7 +121,8 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
||||
const specChecked = this.#el.querySelector("#usingSpecialization")?.checked;
|
||||
const helped = this.#el.querySelector("#helped")?.checked;
|
||||
const tools = this.#el.querySelector("input[name='usingTools']:checked")?.value !== "0";
|
||||
const bonuses = (specChecked ? 1 : 0) + (helped ? 1 : 0) + (tools ? 1 : 0);
|
||||
const group = parseInt(this.#el.querySelector("#group")?.value, 10) || 0;
|
||||
const bonuses = (specChecked ? 1 : 0) + (helped ? 1 : 0) + (tools ? 1 : 0) + group;
|
||||
return (abilVal + sc + skillPool + bonuses) || 0;
|
||||
}
|
||||
|
||||
@@ -212,10 +213,6 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
||||
b += parseInt(this.#getSelfCtrl()?.value, 10) || 0;
|
||||
const tools = this.#el.querySelector("input[name='usingTools']:checked");
|
||||
if (tools && tools.value !== "0") b += 1;
|
||||
const human = this.#el.querySelector("#human-totem");
|
||||
if (human?.checked) b += parseInt(this.#actor?.system?.adaptation?.totems?.human?.value, 10) || 0;
|
||||
const adapted = this.#el.querySelector("#adapted-totem");
|
||||
if (adapted?.checked) b += parseInt(this.#actor?.system?.adaptation?.totems?.adapted?.value, 10) || 0;
|
||||
if (this.hasSpecialtySelected()) b += 1;
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ export function prepareActiveEffectCategories(effects) {
|
||||
|
||||
// Iterate over active effects, classifying them into categories
|
||||
for ( let e of effects ) {
|
||||
e._getSourceName(); // Trigger a lookup for the source name
|
||||
if ( e.disabled ) categories.inactive.effects.push(e);
|
||||
else if ( e.isTemporary ) categories.temporary.effects.push(e);
|
||||
else categories.passive.effects.push(e);
|
||||
|
||||
+64
-15
@@ -184,10 +184,9 @@ export class VermineCombat extends Combat {
|
||||
// encounter check
|
||||
}
|
||||
|
||||
async rollInitiative(ids, formula = undefined, messageOptions = {}) {
|
||||
async rollInitiative(ids, {formula=null, updateTurn=true, messageMode, messageOptions={}}={}) {
|
||||
// roll initiative
|
||||
return super.rollInitiative(ids, formula, messageOptions)
|
||||
|
||||
return super.rollInitiative(ids, {formula, updateTurn, messageMode, messageOptions});
|
||||
}
|
||||
|
||||
nextRound() {
|
||||
@@ -224,24 +223,74 @@ export class VermineCombat extends Combat {
|
||||
|
||||
export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||
|
||||
get template() {
|
||||
return "systems/vermine2047/templates/combat-tracker.hbs";
|
||||
}
|
||||
|
||||
async getData(options) {
|
||||
const context = await super.getData(options);
|
||||
|
||||
if (!context.hasCombat) {
|
||||
return context;
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/vermine2047/templates/combat-tracker.hbs",
|
||||
root: true
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
async _prepareContext(options) {
|
||||
const context = await super._prepareContext(options);
|
||||
context.user = game.user;
|
||||
|
||||
const combat = this.viewed;
|
||||
const hasCombat = combat !== null;
|
||||
const combats = this.combats;
|
||||
const currentIdx = combats.indexOf(combat);
|
||||
const isPlayerTurn = combat?.combatant?.players?.includes(game.user);
|
||||
const canControl = combat?.turn && combat.turn.between(1, combat.turns.length - 2)
|
||||
? combat.canUserModify(game.user, "update", { turn: 0 })
|
||||
: combat?.canUserModify(game.user, "update", { round: 0 });
|
||||
|
||||
Object.assign(context, {
|
||||
combat,
|
||||
hasCombat,
|
||||
combatCount: combats.length,
|
||||
currentIndex: currentIdx + 1,
|
||||
previousId: combats[currentIdx - 1]?.id,
|
||||
nextId: combats[currentIdx + 1]?.id,
|
||||
round: combat?.round,
|
||||
control: isPlayerTurn && canControl,
|
||||
linked: combat?.scene !== null,
|
||||
cssClass: combats.length > 7 ? "cycle" : combats.length ? "tabbed" : "",
|
||||
cssId: "combat",
|
||||
tabName: "combat",
|
||||
labels: {
|
||||
scope: game.i18n.localize(`COMBAT.${combat?.scene ? "Linked" : "Unlinked"}`)
|
||||
},
|
||||
turns: []
|
||||
});
|
||||
|
||||
if ( combat ) {
|
||||
for ( const [i, combatant] of combat.turns.entries() ) {
|
||||
if ( !combatant.visible ) continue;
|
||||
context.turns.push(await this._prepareTurnContext(combat, combatant, i));
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
html.querySelectorAll("[data-attitude]").forEach(el => {
|
||||
/** @override */
|
||||
async _prepareTurnContext(combat, combatant, index) {
|
||||
const turn = await super._prepareTurnContext(combat, combatant, index);
|
||||
const actor = combatant.actor;
|
||||
turn.attitude = combatant.getFlag("world", "attitude") || "active";
|
||||
turn.isPlayer = actor?.hasPlayerOwner ?? false;
|
||||
turn.isNpc = !!actor && !actor.hasPlayerOwner;
|
||||
turn.owner = combatant.isOwner;
|
||||
turn.defeated = combatant.isDefeated;
|
||||
turn.hasRolled = combatant.initiative !== null;
|
||||
turn.hasResource = combatant.resource !== null && combatant.resource !== undefined;
|
||||
turn.effects = (turn.effects?.icons ?? []).map(e => e.img);
|
||||
return turn;
|
||||
}
|
||||
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
this.element.querySelectorAll("[data-attitude]").forEach(el => {
|
||||
el.addEventListener("click", this._setStatut.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -388,4 +388,15 @@ export const registerHandlebarsHelpers = function () {
|
||||
Handlebars.registerHelper("setVar", function (varName, varValue, options) {
|
||||
options.data.root[varName] = varValue;
|
||||
});
|
||||
|
||||
// assign local variables in the current context (used by roll-message.hbs)
|
||||
Handlebars.registerHelper("set", function (options) {
|
||||
const ctx = (this && typeof this === "object") ? this : options.contexts?.[0];
|
||||
if ( ctx && typeof ctx === "object" ) {
|
||||
for ( const [key, value] of Object.entries(options.hash) ) {
|
||||
ctx[key] = value;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
});
|
||||
}
|
||||
@@ -69,8 +69,8 @@ export const registerHooks = function () {
|
||||
});
|
||||
|
||||
// changement de la pause
|
||||
Hooks.on("renderPause", async function () {
|
||||
const pauseEl = document.getElementById("pause");
|
||||
Hooks.on("renderGamePause", async function (app, element) {
|
||||
const pauseEl = element ?? document.getElementById("pause");
|
||||
if (!pauseEl || pauseEl.className !== "paused") return;
|
||||
pauseEl.querySelectorAll("img").forEach(img => {
|
||||
img.src = 'systems/vermine2047/assets/images/ui/vermine_pause.webp';
|
||||
@@ -94,17 +94,17 @@ export const registerHooks = function () {
|
||||
});
|
||||
|
||||
Hooks.on('getSceneControlButtons', async (controls) => {
|
||||
const tokenControls = controls.token;
|
||||
const tokenControls = controls.tokens;
|
||||
if (tokenControls && tokenControls.tools) {
|
||||
tokenControls.tools.push({
|
||||
tokenControls.tools['dice-roller'] = {
|
||||
name: 'Dice Roller',
|
||||
title: game.i18n.localize("VERMINE.RollTool"),
|
||||
icon: 'fas fa-dice-d10',
|
||||
button: true,
|
||||
onClick() {
|
||||
onChange() {
|
||||
RollDialog.create().then(d => d.render(true));
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+8
-76
@@ -49,22 +49,13 @@ export class VermineUtils {
|
||||
totemBonus = this._calculateTotemDomainBonuses(skillCategory, actor);
|
||||
}
|
||||
|
||||
// Apply auto-successes and auto-thresholds
|
||||
let autoSuccesses = 0;
|
||||
// Apply automatic successes from skill mastery
|
||||
let adjustedDifficulty = difficulty;
|
||||
|
||||
if (skillLevel !== null && skillLevel !== undefined) {
|
||||
autoSuccesses = this._calculateAutoSuccesses(skillLevel, hasSpecialty);
|
||||
const autoThreshold = this._getAutoThreshold(skillLevel);
|
||||
if (autoThreshold !== null) {
|
||||
adjustedDifficulty = autoThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle human totem
|
||||
if (totems.human) {
|
||||
NoD--;
|
||||
const humanDifficulty = skillLevel !== null ? Math.max(adjustedDifficulty, difficulty) : adjustedDifficulty;
|
||||
const humanDifficulty = adjustedDifficulty;
|
||||
const humanFormula = `(1D10cs>=${humanDifficulty}[human_${safeUserName}]*2)`;
|
||||
|
||||
// Apply domain bonus/malus
|
||||
@@ -78,7 +69,7 @@ export class VermineUtils {
|
||||
// Handle adapted totem
|
||||
if (totems.adapted) {
|
||||
NoD--;
|
||||
const adaptedDifficulty = skillLevel !== null ? Math.max(adjustedDifficulty, difficulty) : adjustedDifficulty;
|
||||
const adaptedDifficulty = adjustedDifficulty;
|
||||
const adaptedFormula = `(1D10cs>=${adaptedDifficulty}[adapted_${safeUserName}]*2)`;
|
||||
|
||||
// Apply domain bonus/malus
|
||||
@@ -105,8 +96,8 @@ export class VermineUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply handicap (wounds/maluses reduce dice pool)
|
||||
NoD = Math.max(1, NoD - handicap)
|
||||
// Ensure the dice pool cannot go below zero
|
||||
NoD = Math.max(0, NoD)
|
||||
|
||||
// Build base formula
|
||||
const baseFormula = `${NoD}d10cs>=${adjustedDifficulty}[regular_${safeUserName}]`;
|
||||
@@ -126,7 +117,6 @@ export class VermineUtils {
|
||||
skillCategory: skillCategory,
|
||||
skillLevel: skillLevel,
|
||||
hasSpecialty: hasSpecialty,
|
||||
autoSuccesses: autoSuccesses,
|
||||
totemBonuses: { ...totemBonus },
|
||||
baseNoD: NoD,
|
||||
rerolls: Reroll,
|
||||
@@ -152,7 +142,8 @@ export class VermineUtils {
|
||||
skillCategory,
|
||||
keepTotem,
|
||||
skillLevel,
|
||||
hasSpecialty
|
||||
hasSpecialty,
|
||||
handicap
|
||||
});
|
||||
|
||||
return roll;
|
||||
@@ -205,63 +196,6 @@ export class VermineUtils {
|
||||
return bonuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates automatic successes based on skill mastery level.
|
||||
* @param {number} skillLevel - Skill level (0-5)
|
||||
* @param {boolean} [hasSpecialty=false] - Whether a specialty is used
|
||||
* @returns {number} Number of automatic successes
|
||||
*/
|
||||
static _calculateAutoSuccesses(skillLevel, hasSpecialty = false) {
|
||||
// According to Vermine2047 rules, automatic successes are based on mastery level:
|
||||
// Level 0 (Incompetent): 0 automatic successes
|
||||
// Level 1 (Beginner): 0 automatic successes
|
||||
// Level 2 (Proficient): 1 automatic success if specialty is used
|
||||
// Level 3 (Expert): 1 automatic success
|
||||
// Level 4 (Master): 1 automatic success + 1 if specialty is used
|
||||
// Level 5 (Legend): 2 automatic successes
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the automatic threshold if the skill is not mastered.
|
||||
* @param {number} skillLevel - Skill level
|
||||
* @returns {number|null} Automatic threshold or null if skill is mastered
|
||||
*/
|
||||
static _getAutoThreshold(skillLevel) {
|
||||
// If the skill is not mastered (level 0 or 1), use a default threshold
|
||||
// Level 0 (Incompetent): threshold = 9 (very hard)
|
||||
// Level 1 (Beginner): threshold = 7 (hard)
|
||||
// Level >= 2: null (use normal threshold)
|
||||
|
||||
if (skillLevel === 0) return 9; // Very hard
|
||||
if (skillLevel === 1) return 7; // Hard
|
||||
|
||||
return null; // Utiliser le seuil normal
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles reroll events on dice in chat messages.
|
||||
* @param {Object} message - The chat message containing the reroll event
|
||||
@@ -471,11 +405,9 @@ export class VermineUtils {
|
||||
const chatData = {
|
||||
user: game.user?._id,
|
||||
speaker: ChatMessage.getSpeaker(),
|
||||
content: content,
|
||||
roll: roll
|
||||
content: content
|
||||
};
|
||||
const msg = await ChatMessage.create(chatData);
|
||||
await msg.setFlag('world', 'roll', roll);
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ Hooks.once('init', async function () {
|
||||
|
||||
// Register sheet application classes (ApplicationV2)
|
||||
// Unregister core sheets
|
||||
foundry.applications.sheets.ActorSheetV2?.unregisterSheet?.("core", "Actor", {
|
||||
foundry.documents.collections.Actors.unregisterSheet("core", foundry.applications.sheets.ActorSheetV2, {
|
||||
types: ["character", "npc", "group", "creature"]
|
||||
})
|
||||
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1?.sheets?.ItemSheet)
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "matériel",
|
||||
"name": "materiel",
|
||||
"label": "Matériel",
|
||||
"system": "vermine2047",
|
||||
"path": "packs/materiel",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
{{!-- Logo --}}
|
||||
<img class="logo mx-auto" src="systems/vermine2047/assets/images/ui/logo.webp"
|
||||
width="200" alt="logo Vermine" />
|
||||
width="150" alt="logo Vermine" />
|
||||
|
||||
{{!-- Sidebar: Profile Image + Totem Dice --}}
|
||||
<div class="image-wrapper">
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</a>
|
||||
{{/if}}
|
||||
<a class="combat-button combat-control" data-tooltip="COMBAT.Delete"
|
||||
data-control="endCombat" {{#unless
|
||||
data-action="endCombat" {{#unless
|
||||
combatCount}}disabled{{/unless}}>
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
@@ -33,12 +33,12 @@
|
||||
<div class="encounter-controls flexrow {{#if hasCombat}}combat{{/if}}">
|
||||
{{#if user.isGM}}
|
||||
<a class="combat-button combat-control"
|
||||
data-tooltip="COMBAT.RollAll" data-control="rollAll" {{#unless
|
||||
data-tooltip="COMBAT.RollAll" data-action="rollAll" {{#unless
|
||||
turns}}disabled{{/unless}}>
|
||||
<i class="fas fa-users"></i>
|
||||
</a>
|
||||
<a class="combat-button combat-control"
|
||||
data-tooltip="COMBAT.RollNPC" data-control="rollNPC" {{#unless
|
||||
data-tooltip="COMBAT.RollNPC" data-action="rollNPC" {{#unless
|
||||
turns}}disabled{{/unless}}>
|
||||
<i class="fas fa-users-cog"></i>
|
||||
</a>
|
||||
@@ -58,18 +58,18 @@
|
||||
|
||||
{{#if user.isGM}}
|
||||
<a class="combat-button combat-control"
|
||||
data-tooltip="COMBAT.InitiativeReset" data-control="resetAll"
|
||||
data-tooltip="COMBAT.InitiativeReset" data-action="resetAll"
|
||||
{{#unless hasCombat}}disabled{{/unless}}>
|
||||
<i class="fas fa-undo"></i>
|
||||
</a>
|
||||
<a class="combat-button combat-control"
|
||||
data-tooltip="{{labels.scope}}"
|
||||
data-control="toggleSceneLink" {{#unless
|
||||
data-action="toggleSceneLink" {{#unless
|
||||
hasCombat}}disabled{{/unless}}>
|
||||
<i class="fas fa-{{#unless linked}}un{{/unless}}link"></i>
|
||||
</a>
|
||||
<a class="combat-button combat-settings"
|
||||
data-tooltip="COMBAT.Settings" data-control="trackerSettings">
|
||||
data-tooltip="COMBAT.Settings" data-action="trackerSettings">
|
||||
<i class="fas fa-cog"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
@@ -90,20 +90,20 @@
|
||||
<a
|
||||
class="combatant-control {{#if turn.hidden}}active{{/if}}"
|
||||
data-tooltip="COMBAT.ToggleVis"
|
||||
data-control="toggleHidden">
|
||||
data-action="toggleHidden">
|
||||
<i class="fas fa-eye-slash"></i>
|
||||
</a>
|
||||
<a
|
||||
class="combatant-control {{#if turn.defeated}}active{{/if}}"
|
||||
data-tooltip="COMBAT.ToggleDead"
|
||||
data-control="toggleDefeated">
|
||||
data-action="toggleDefeated">
|
||||
<i class="fas fa-skull"></i>
|
||||
</a>
|
||||
|
||||
{{#if turn.canPing}}
|
||||
<a class="combatant-control"
|
||||
data-tooltip="COMBAT.PingCombatant"
|
||||
data-control="alertPingCombatant">
|
||||
data-action="pingCombatant">
|
||||
<i class="fa-solid fa-bullseye-arrow"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
@@ -158,7 +158,7 @@
|
||||
{{else if this.owner}}
|
||||
<a class="combatant-control roll"
|
||||
data-tooltip="COMBAT.InitiativeRoll"
|
||||
data-control="rollInitiative"></a>
|
||||
data-action="rollInitiative"></a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
@@ -171,30 +171,30 @@
|
||||
{{#if user.isGM}}
|
||||
{{#if round}}
|
||||
<a class="combat-control" data-tooltip="COMBAT.RoundPrev"
|
||||
data-control="previousRound"><i
|
||||
data-action="previousRound"><i
|
||||
class="fas fa-step-backward"></i></a>
|
||||
<a class="combat-control" data-tooltip="COMBAT.TurnPrev"
|
||||
data-control="previousTurn"><i
|
||||
data-action="previousTurn"><i
|
||||
class="fas fa-arrow-left"></i></a>
|
||||
<a class="combat-control center" data-control="endCombat">{{localize
|
||||
<a class="combat-control center" data-action="endCombat">{{localize
|
||||
'COMBAT.End'}}</a>
|
||||
<a class="combat-control" data-tooltip="COMBAT.TurnNext"
|
||||
data-control="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
||||
data-action="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
||||
<a class="combat-control" data-tooltip="COMBAT.RoundNext"
|
||||
data-control="nextRound"><i class="fas fa-step-forward"></i></a>
|
||||
data-action="nextRound"><i class="fas fa-step-forward"></i></a>
|
||||
{{else}}
|
||||
<a class="combat-control center"
|
||||
data-control="startCombat">{{localize
|
||||
data-action="startCombat">{{localize
|
||||
'COMBAT.Begin'}}</a>
|
||||
{{/if}}
|
||||
{{else if control}}
|
||||
<a class="combat-control" data-tooltip="COMBAT.TurnPrev"
|
||||
data-control="previousTurn"><i
|
||||
data-action="previousTurn"><i
|
||||
class="fas fa-arrow-left"></i></a>
|
||||
<a class="combat-control center" data-control="nextTurn">{{localize
|
||||
<a class="combat-control center" data-action="nextTurn">{{localize
|
||||
'COMBAT.TurnEnd'}}</a>
|
||||
<a class="combat-control" data-tooltip="COMBAT.TurnNext"
|
||||
data-control="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
||||
data-action="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</nav>
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
class="totem-checkbox"
|
||||
/>
|
||||
<span class="totem-label">{{localize 'TOTEMS.human.name'}}</span>
|
||||
<small>(+{{@root.actor.system.adaptation.totems.human.value}}D)</small>
|
||||
<small>({{@root.actor.system.adaptation.totems.human.value}}D)</small>
|
||||
</label>
|
||||
<label class="totem-option">
|
||||
<input
|
||||
@@ -292,7 +292,7 @@
|
||||
class="totem-checkbox"
|
||||
/>
|
||||
<span class="totem-label">{{localize 'TOTEMS.adapted.name'}}</span>
|
||||
<small>(+{{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
|
||||
<small>({{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
|
||||
</label>
|
||||
</div>
|
||||
<small class="totem-hint">{{localize 'VERMINE.totem_hint'}}</small>
|
||||
@@ -309,7 +309,7 @@
|
||||
value="1"
|
||||
/>
|
||||
<span class="totem-label">{{localize 'TOTEMS.human.name'}}</span>
|
||||
<small>(+{{@root.actor.system.adaptation.totems.human.value}}D)</small>
|
||||
<small>({{@root.actor.system.adaptation.totems.human.value}}D)</small>
|
||||
</label>
|
||||
</div>
|
||||
{{/ifgt}}
|
||||
@@ -326,7 +326,7 @@
|
||||
value="1"
|
||||
/>
|
||||
<span class="totem-label">{{localize 'TOTEMS.adapted.name'}}</span>
|
||||
<small>(+{{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
|
||||
<small>({{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
|
||||
</label>
|
||||
</div>
|
||||
{{/ifgt}}
|
||||
|
||||
@@ -3,6 +3,29 @@
|
||||
|
||||
|
||||
<section class="sheet-body">
|
||||
<div class="flexrow">
|
||||
<label class="resource-label" for="system.skill">{{ localize 'VERMINE.skill_title'}}</label>
|
||||
<select
|
||||
name="system.skill"
|
||||
class="skill-select"
|
||||
>
|
||||
{{#each @root.config.skillCategories as |skillCategory sckey|}}
|
||||
<optgroup label="{{ smarttlk 'SKILLS_CATEGORIES' sckey 'name' }}">
|
||||
{{#each @root.config.skills as |skill key|}}
|
||||
{{#ife skill.category sckey}}
|
||||
<option
|
||||
value="{{key}}"
|
||||
{{#ife key @root.system.skill}}
|
||||
selected
|
||||
{{/ife}}
|
||||
>{{ smarttlk 'SKILLS' key 'name' }}</option>
|
||||
{{/ife}}
|
||||
{{/each}}
|
||||
</optgroup>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{> "systems/vermine2047/templates/item/partials/traits.hbs"}}
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</div>
|
||||
<div class="flexcol">
|
||||
<h4>{{localize "VERMINE.success_required"}}:</h4>
|
||||
<span id="required">{{param.handicap}}</span>
|
||||
<span id="required">{{math_add 1 param.handicap}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user