19 Commits
Author SHA1 Message Date
uberwald 9c7ebc6df9 Sync compendiums
Release Creation / build (release) Failing after 1m24s
2026-08-03 21:26:03 +02:00
uberwald 05f668acf0 ci: compiler les compendiums lors de la release
Le workflow Gitea zippait packs/ sans recompiler les LevelDB depuis les
sources YAML (gitignorées en CI). Ajoute npm run pullYAMLtoLDB entre le
build CSS et le zip pour embarquer des compendiums à jour dans la release.
2026-08-03 20:27:27 +02:00
uberwald 10b01614e6 chore: regénérer les compendiums depuis le monde
Extraction complète des packs vers src/packs/*.yml après mise à jour des
compendiums dans le monde. Ajoute les nouveaux items et rites, met à
jour le contenu existant, et renomme le pack Historiques en 'Historiques
& Création'.
2026-08-03 18:49:06 +02:00
uberwald 89d9d71395 style: ajuster la mise en page des fiches
Release Creation / build (release) Failing after 1m51s
Réduit la taille des polices d'en-tête et du logo, retravaille la
disposition des totems, et double la hauteur des zones Notes et
Relations dans l'onglet Histoire.
2026-08-03 15:59:57 +02:00
uberwald 1e298ed809 fix: éviter le double affichage des dés 3D
Le message de jet n'attache plus la liste des dés : Dice So Nice
réaffichait le jet via son hook updateChatMessage en plus de l'appel
explicite à showDiceSoNice, provoquant une double animation.
2026-08-03 15:59:51 +02:00
uberwald 4ffaebf52b feat: lancer les armes via la boîte de dialogue avec compétence
Ajoute un champ compétence au modèle arme, un sélecteur dans la fiche
d'arme et ouvre la boîte de dialogue de jet pré-remplie au clic sur
l'icône dé (réservé aux acteurs disposant d'aptitudes/compétences).
2026-08-03 15:59:47 +02:00
uberwald de7691c4dc fix: transmettre le handicap au message de jet (succès requis) 2026-08-01 21:35:48 +02:00
uberwald c87b25fbaf fix: aligner les jets et la boîte de dialogue avec les règles 2026-08-01 21:33:38 +02:00
uberwald 6ce51ed8cf fix: réparer les contrôles du combat tracker et la pause (v14) 2026-08-01 21:33:38 +02:00
uberwald df852f17d1 fix: aligner le JS avec l'API Foundry v14
- effects.mjs: remove e._getSourceName() (removed in v14, now a getter)
- fight.mjs: convert VermineCombatTracker from AppV1 (get template/getData)
  to AppV2 PARTS + _prepareContext/_prepareTurnContext
- fight.mjs: VermineCombat.rollInitiative v14 options-object signature
- hooks.mjs: renderPause -> renderGamePause; getSceneControlButtons now
  uses controls.tokens and tools record (v14 names/structure)
- roll.mjs: ChatMessage.create uses rolls array (v14 removed 'roll' key)
- item.mjs: getRollData returns system when no actor (v14 expects object)
- vermine2047.mjs: correct unregisterSheet API for ActorSheetV2
- base-item-sheet.mjs: call super._onRender
2026-08-01 21:33:38 +02:00
uberwald 8916600bf6 chore: ignorer le répertoire local .swival/ 2026-08-01 21:33:38 +02:00
uberwald 0189f62734 fix: importer foundry.applications.api et utiliser _onRender dans les pickers AppV2
- applications.mjs: import HandlebarsApplicationMixin/ApplicationV2 from
  foundry.applications.api (they are not globals in Foundry v14), fixing
  'ReferenceError: HandlebarsApplicationMixin is not defined'
- Replace activateListeners(html) with _onRender(context, options) and
  this.element — AppV2 does not call activateListeners
- TraitSelector: remove recursive render(true) in _validateTraits
- fight.mjs: VermineCombatTracker same _onRender conversion
2026-08-01 21:33:38 +02:00
uberwald 29b6a98c89 Fix dummy bug
Release Creation / build (release) Failing after 1m46s
2026-07-17 22:09:53 +02:00
uberwald b1d73e6b08 chore: remove CHANGELOG.md, add LICENSE.md (CC BY-NC-SA 4.0) 2026-07-12 22:32:48 +02:00
uberwald 509a5efd9d Actualiser README.md 2026-07-12 22:31:15 +02:00
uberwald fea7c1c271 docs: translate README to French with English summary 2026-07-12 22:30:16 +02:00
uberwald 412573fc4d chore: stop tracking regles/ (keep local) 2026-07-12 22:29:00 +02:00
uberwald 47e2ebe45b docs: rewrite README with system architecture, types, commands 2026-07-12 22:28:16 +02:00
uberwald 90de66d668 fix: Dice So Nice import v14 compat, override Foundry text colors for readability
Release Creation / build (release) Failing after 1m28s
- hooks.mjs: replace static dice-so-nice import with dynamic import
  using game.modules.get('dice-so-nice').id (path removed in v14)
- hooks.mjs: fix permission condition (|| -> &&), jQuery -> vanilla JS
- less/base.less: override --color-text-* and --button-text-color
  for both .themed.theme-dark (AppV2) and body.theme-dark (legacy apps)
- target #settings-config buttons + labels + hints for dark grays
2026-07-12 22:05:45 +02:00
540 changed files with 21174 additions and 25149 deletions
+4 -4
View File
@@ -144,7 +144,7 @@ module.exports = {
'no-lone-blocks': 'warn',
'no-lonely-if': 'warn',
'no-loop-func': 'warn',
'no-magic-numbers': ['warn', { ignore: [0, 1, 2], ignoreEnums: true, ignoreNumericLiteralTypes: true, ignoreArrayIndexes: true }],
'@typescript-eslint/no-magic-numbers': 'off',
'no-multi-assign': 'warn',
'no-multi-str': 'warn',
'no-negated-condition': 'warn',
@@ -208,7 +208,7 @@ module.exports = {
'one-var': ['warn', 'never'],
'one-var-declaration-per-line': ['warn', 'initializations'],
'operator-assignment': ['warn', 'always'],
'prefer-arrow-callback': ['warn', { classPropertiesAllowed: true, disallowTLSClassFields: true }],
'prefer-arrow-callback': 'warn',
'prefer-const': ['error', { destructuring: 'all', ignoreReadBeforeAssign: false }],
'prefer-destructuring': ['warn', { array: false, object: true }],
'prefer-exponentiation-operator': 'warn',
@@ -221,7 +221,7 @@ module.exports = {
'prefer-rest-params': 'warn',
'prefer-spread': 'warn',
'prefer-template': 'warn',
'quote-props': ['warn', 'as-needed', { keywords: true, unnecessaryQuote: false, numbers: true }],
'quote-props': ['warn', 'as-needed'],
'radix': ['error', 'always'],
'require-await': 'warn',
'require-unicode-regexp': 'off',
@@ -313,7 +313,7 @@ module.exports = {
'padded-blocks': ['warn', 'never'],
'padding-line-between-statements': 'off',
'prefer-exponentiation-operator': 'warn',
'quote-props': ['warn', 'as-needed', { keywords: true, unnecessaryQuote: false, numbers: true }],
'quote-props': ['warn', 'as-needed'],
'quotes': ['warn', 'single', { avoidEscape: true, allowTemplateLiterals: true }],
'rest-spread-spacing': ['warn', 'never'],
'semi': ['warn', 'always'],
+4
View File
@@ -36,6 +36,10 @@ jobs:
- name: Build CSS
run: npm install && npm run build
# Recompile les compendiums LevelDB depuis les sources YAML (src/packs/ → packs/)
- name: Compile Compendiums
run: npm run pullYAMLtoLDB
# Crée le zip de release avec tous les fichiers nécessaires au système
- run: |
apt update -y
+16 -7
View File
@@ -2,6 +2,8 @@
## Build & Development
Requires Node.js v16.15.1 (see `.nvmrc`).
```bash
npm run watch # Watch SCSS + templates, proxy Foundry at localhost:30000 (requires Foundry running)
npm run buildStyle # Compile SCSS once to css/vermine2047.css
@@ -10,7 +12,7 @@ npm run pushLDBtoYAML # Extract LevelDB packs to editable YAML in src/packs/
npx eslint module/ # Lint JavaScript (ESLint configured in .eslintrc.js)
```
There are no automated tests. Use `npm run watch` during development to live-reload SCSS and Handlebars templates via browser-sync.
There are no automated tests. Use `npm run watch` during development — browser-sync proxies Foundry over HTTPS and live-reloads SCSS, `.hbs`, and `.html` templates on change. `system.json` declares `hotReload` for `.css`, `.scss`, `.hbs`, and `.json` extensions, so Foundry auto-refreshes those assets client-side when the dev server is running.
## Architecture
@@ -27,6 +29,7 @@ This is a **FoundryVTT game system** (`system.json` → system ID `vermine2047`)
|---|---|
| `config.mjs` | All game constants (`CONFIG.VERMINE`) — abilities, skills, totems, threat/role/pattern levels, traits, damage types |
| `roll.mjs` | `VermineUtils` class — d10 dice pool system with success counting, totem mechanics, rerolls, Dice So Nice integration |
| `dice3d.mjs` | Dice So Nice 3D dice configuration (appearance, colors, themes for totem/standard dice) |
| `fight.mjs` | Confrontation system (`VermineFight`), plus `VermineCombat`, `VermineCombatant`, `VermineCombatTracker` |
| `group-link.mjs` | `GroupLink` — bidirectional sync of members/encounters between group actors and character/NPC actors via Foundry hooks |
| `hooks.mjs` | All Foundry hook registrations (chat messages, hotbar drop, combat, preCreate, Dice So Nice) |
@@ -34,21 +37,27 @@ This is a **FoundryVTT game system** (`system.json` → system ID `vermine2047`)
| `handlebars-manager.mjs` | Template preloading paths + all Handlebars helpers (level config lookups, math, conditionals) |
| `effects.mjs` | Active effect management |
| `dialogs/rollDialog.mjs` | Advanced roll dialog (ability/skill selection, difficulty, totems, specialties, assist/pool bonuses) |
| `applications.mjs` | `TotemPicker` and `TraitSelector` applications |
| `applications.mjs` | `TotemPicker` and `TraitSelector` applications (still AppV1) |
| `applications/sheets/` | ApplicationV2 sheet classes: `base-actor-sheet.mjs`, `base-item-sheet.mjs` + 4 actor + 12 item sheets |
**Data model**: `template.json` defines the Actor/Item schemas (Foundry's template system). Derived data is computed in `prepareDerivedData()` in `module/documents/actor.mjs` and `module/documents/item.mjs`.
**Data model**: Defined via Foundry DataModels (`module/models/`) extending `foundry.abstract.TypeDataModel`. Type-specific derived data is computed in each DataModel's `prepareDerivedData()`. The legacy `template.json` has been removed in favor of `system.json``documentTypes`.
**Sheet inheritance**: `VermineActorSheet` (base) → `VermineCharacterSheet`, `VermineNpcSheet`, `VermineGroupSheet`, `VermineCreatureSheet`. Sheets expose `CONFIG.VERMINE` as `context.config` in template data.
**Sheet inheritance**: ApplicationV2 (`HandlebarsApplicationMixin(ActorSheetV2)`/`ItemSheetV2`) in `module/applications/sheets/`. `VermineBaseActorSheet` `VermineCharacterSheetV2`, etc. Sheets expose `CONFIG.VERMINE` as `context.config` in template data. Each sheet is split into parts via `static PARTS`; tab navigation uses Core's `templates/generic/tab-navigation.hbs`.
**Data preparation**: `VermineActor.prepareDerivedData()` delegates to the type-specific DataModel. Each DataModel implements its own `prepareDerivedData()` with type-specific computation logic.
**Global access**: On `init`, `game.vermine2047` is populated with `{ VermineActor, VermineItem, VermineUtils, VermineCombat, GroupLink }` for easy access in macros and scripts.
**Compendium packs**: Stored in `packs/` as LevelDB databases. Edit source data in `src/packs/` as YAML, then `npm run pullYAMLtoLDB` to build. The CI release workflow runs this automatically.
## Key Conventions
- **All `.mjs` files**: ES module syntax only. Foundry globals (`game`, `Hooks`, `CONFIG`, `Actor`, `Item`, `ChatMessage`, `Roll`, `Handlebars`, `renderTemplate`, `foundry`) are available but declared as readonly globals in `.eslintrc.js`.
- **All `.mjs` files**: ES module syntax only. Foundry globals (`game`, `Hooks`, `CONFIG`, `Actor`, `Item`, `ChatMessage`, `Roll`, `Handlebars`, `renderTemplate`, `foundry`, `ui`, `Canvas`, `Dialog`) are available but declared as readonly globals in `.eslintrc.js`.
- **ESLint conventions**: 2-space indent, single quotes, semicolons required, `===` equality (except `== null`). Constants named with `VERMINE_` prefix are exempt from camelCase. Max 1 class per file, 500 lines per file, 100 lines per function, 5 levels of nesting max. No `console.log` calls — use `ui.notifications`.
- **Code language**: Source code and comments are in French. UI strings in `lang/fr.json` and `lang/en.json`. Use `game.i18n.localize()` for all user-visible text.
- **Template naming**: Actor sheets at `templates/actor/actor-{type}-sheet.hbs`, item sheets at `templates/item/item-{type}-sheet.html` (note: item sheets use `.html`, everything else uses `.hbs`). Chat card templates at `templates/item/chatCards/{type}.hbs`.
- **Template naming**: Actor sheets use ApplicationV2 part templates in `templates/actor/appv2/{type}-{part}.hbs` (e.g. `character-main.hbs`, `npc-characteristics.hbs`). Item sheets at `templates/item/item-{type}-sheet.html`. Chat card templates at `templates/item/chatCards/{type}.hbs`. Legacy templates in `templates/actor/` subdirectories are preserved for dialog/chat use.
- **Dice system**: d10 pools with success counting (result ≥ difficulty). Totem dice (human/adapted) count double on success. Formula syntax: `{N}d10cs>={threshold}[label]`. Totem dice: `(1d10cs>={threshold}[totem_label]*2)`.
- **CSS**: SCSS sources in `scss/` compile to `css/vermine2047.css`. Organized by concern: `_app.scss`, `item-sheet.scss`, `roll.scss`, `dialog.scss`, etc.
- **CSS**: SCSS sources in `scss/` compile to `css/vermine2047.css`. Entry point: `vermine2047.scss` which imports partials: `_app.scss`, `_flex.scss`, `dialog.scss`, `item-sheet.scss`, `itemCards.scss`, `roll.scss`, `special-applications.scss`, `special-inputs.scss`, `style.scss`, `base_work.scss`. Add new SCSS partials as `_component.scss`, import in `vermine2047.scss`.
- **Data tools**: The `pushLDBtoYAML` / `pullYAMLtoLDB` scripts use `@foundryvtt/foundryvtt-cli` to convert between LevelDB packs and editable YAML. When adding pack content, edit YAML in `src/packs/` then rebuild.
- **Actor updates**: Use `actor.update({...})` with dot-notation paths (e.g., `'system.adaptation.totems.human.value'`). The `GroupLink` hooks system automatically syncs group memberships on actor changes.
- **New template partials**: Must be listed in `preloadHandlebarsTemplates()` in `handlebars-manager.mjs` for pre-compilation.
+5 -1
View File
@@ -31,12 +31,16 @@ jobs:
- name: Install Dependencies
run: npm ci
# Build CSS from LESS
- name: Build CSS
run: npm run build:less
# Pull YAML to LDB packs
- name: Build Packs
run: npm run pullYAMLtoLDB
# Create a zip file with all files required by the module to add to the release
- run: zip -r ./system.zip system.json template.json assets/ css/ lang/ module/ templates/ packs/
- run: zip -r ./system.zip system.json assets/ css/ lang/ module/ templates/ packs/
# Create a release for this specific version
- name: Update Release with Files
+4
View File
@@ -19,6 +19,10 @@ foundry
# Ancien dossier SCSS (archivé)
scss_old/
# Outils d'audit local
.swival/
# Fichiers CSS générés
css/vermine2047.*.css
!css/vermine2047.css
regles/
+48
View File
@@ -0,0 +1,48 @@
{
"atom": false,
"borderZero": {
"style": "none"
},
"colorVariables": true,
"comment": false,
"depthLevel": 10,
"duplicateProperty": true,
"emptyRule": true,
"finalNewline": true,
"hexLength": "long",
"hexNotation": "lowercase",
"hexValidation": true,
"idSelector": false,
"important": true,
"importPath": [
"./less"
],
"maxCharPerLine": 120,
"newlineAfterBlock": true,
"propertyOrdering": false,
"propertyUnits": true,
"qualifyingElement": [
"button",
"input",
"select",
"textarea"
],
"selectorNaming": false,
"singleLinePerProperty": true,
"singleLinePerSelector": false,
"spaceAfterComma": true,
"spaceAfterComment": true,
"spaceAfterPropertyColon": "one",
"spaceAfterPropertyValue": true,
"spaceAroundCombinator": true,
"spaceAroundOperator": true,
"spaceBeforeBrace": "one",
"spaceBetweenParens": false,
"stringQuotes": "double",
"trailingSemicolon": true,
"trailingWhitespace": true,
"urlFormat": "relative",
"urlQuotes": true,
"variableNaming": false,
"zeroUnit": false
}
+63
View File
@@ -0,0 +1,63 @@
# AGENTS.md — Vermine2047 FoundryVTT system
## Project
FoundryVTT v14 game system for the French post-apocalyptic TTRPG "Vermine 2047". Bilingual FR/EN.
- **Entrypoint**: `module/vermine2047.mjs` (declared in `system.json` esmodules)
- **CSS**: `css/vermine2047.min.css` (compiled from LESS, **not** SCSS)
- **System ID**: `vermine2047`
## Architecture
| Layer | Location | Notes |
|-------|----------|-------|
| Entry/init | `module/vermine2047.mjs` | `Hooks.once('init',...)` — register DataModels before Document classes |
| DataModels | `module/models/*.mjs` | FoundryV2 `TypeDataModel` — 4 Actor types, 12 Item types |
| Documents | `module/documents/*.mjs` | `VermineActor`, `VermineItem` (thin wrappers) |
| Sheets (AppV2) | `module/applications/sheets/*.mjs` | ApplicationV2 sheets per type |
| System | `module/system/*.mjs` | config, roll, fight, dialogs, hooks, settings, dice3d, group-link |
| Templates (HBS) | `templates/actor/appv2/*.hbs` | ApplicationV2 templates |
| Legacy templates | `templates/actor/*.hbs`, `templates/actor/{character,group,npc,creature}/*.hbs` | Coexisting legacy templates |
### Actor types: character, npc, group, creature
### Item types: item, weapon, defense, vehicle, ability, specialty, background, trauma, evolution, rumor, target, rite
Key objects at runtime:
- `game.vermine2047``{ VermineUtils, VermineCombat, GroupLink }`
- `CONFIG.VERMINE` → game configuration (totems, traits, skills, etc.)
- `CONFIG.ui.combat` → custom `VermineCombatTracker`
Dice system: d10-based, success-counting (`Xd10cs>=N`). Totems give domain bonuses. Initiative formula:
`(@abilities.reflexes.value + @skills.alertness.value)d10cs>=@combatStatus.difficulty`
## Commands
```sh
npm run build:less # LESS → vermine2047.min.css
npm run build:less:dev # LESS → vermine2047.dev.css (unminified)
npm run build:all-css # both
npm run watch # gulp watch + browser-sync proxy to localhost:30000
npm run lint:less # lesshint
npm run pushLDBtoYAML # extract Foundry packs → src/packs/*.yml
npm run pullYAMLtoLDB # compile src/packs/*.yml → packs/ LevelDB
make build / watch / lint # Makefile wrappers for above
```
- Node version required: `v16.15.1` (per `.nvmrc`)
## Gotchas
- **DataModels before Document classes**: In `init` hook, register `CONFIG.Actor.dataModels.*` and `CONFIG.Item.dataModels.*` **before** setting `CONFIG.Actor.documentClass` / `CONFIG.Item.documentClass`.
- **`no-console: 'error'`**: ESLint blocks `console.log/warn/info`. Use `console.error` sparingly or disable the rule inline.
- **ESLint is very verbose**: ~350 rules. Common failures: missing semicolons, trailing commas, single quotes, 2-space indent, `max-len: 120`. Run `npx eslint module/` before committing.
- **Two template systems**: Both legacy (`templates/actor/*.hbs`) and ApplicationV2 (`templates/actor/appv2/*.hbs`) are preloaded. Don't confuse them.
- **Packs in two places**: `src/packs/*.yml` (YAML source, tracked) ↔ `packs/*` (LevelDB binary, gitignored). Edit YAML source, then run `pullYAMLtoLDB` to compile.
- **CSS auto-loaded**: `system.json` declares styles; **do not** inject `<link>` manually (causes MIME type errors).
- **`game-mode` setting**: Default in code is `'e'` (likely a typo for `'1'`). Values: `1`=Survie, `2`=Cauchemar, `3`=Apocalypse.
- **editMode flag disabled**: No auto-set on actor creation (causes ActiveEffect errors). Enable manually via sheet checkbox.
- **GroupLink auto-syncs**: `GroupLink.registerHooks()` runs on init. Actor updates propagate to groups and vice versa.
- **Custom dice**: Dice So Nice integration uses user color (regular/human/adapted color sets) with custom d10 face images.
- **No tests**: `npm test` is a stub (exits 1). No test framework installed.
- **No CI**: `.github/` is gitignored; no workflows configured.
- **HotReload**: `system.json` flags hotReload for css, scss, hbs, json extensions.
-141
View File
@@ -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*
+7
View File
@@ -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.
+124 -49
View File
@@ -1,61 +1,136 @@
# Vermine 2047 System
# Vermine 2047 Système Foundry VTT
![Foundry v14](https://img.shields.io/badge/Foundry-v14-blue)
![Version](https://img.shields.io/badge/version-0.1.13-green)
##TODO
Système non-officiel pour le jeu de rôle **Vermine 2047**, univers post-apo français par **Studio Agate**.
### fiche de perso
► Achetez le jeu : https://www.kickandgo.net/boutique-agate/category.php?cl=UyQcRrFtYcYrAnLbOfDj&idp=20
- [X] création de spécialitées depuis le (+) des compétences
- [X] fixer les cases hexas , comprtement chelou , piste = un click à l'air d'envoyer 2 update, verifier le onChange
- [X] dialog d'edition des min-max
Interface bilingue **FR/EN**.
### fiche de groupe
- [X] pas encore penché dessus
---
#### Members et encounters
- [X] faire une classe GroupLink pour avoir les actors en objets dans les array group.members[], group.encounters[], et character.encounters[],
- [X] faire une fonction sur le Hook.onUpdateActor => update des groupes dans characters, update des encounters et members dans groups
## Types d'Acteurs
### fiche de pnj créature
- [X] à faire,
- [X] lister les gabarit/taille/roles(creatures) et menace/experience/role(pnj)... stocker les modifs dans CONFIG.VERMINE,
| Type | Fiche | Description |
|------|-------|-------------|
| **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 |
## Types d'Objets
### les jets de dés
- [X] redesign de rollDialog => `<details>+<sumary>`=> rendre moins dense
- [X] envoyer les spécialités utilisables au rollDialog
- [X] envoyer les items utilisables au rollDialog
- [X] gérer le fait de choisir quel totem garder : recalcul des réussites
- [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 rerolls depuis chat(cf noc)
- [X] gérer les rerolls après le jet en fonction du score d'effort et de la carac
- [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] ajout des domaines de prédilections
- [X] gérer le dés en +/- selon l'influence du totem adapté ou humain selon les domaines
- [X] gérer les réussites auto
- [X] gérer les seuils auto si compétence non maitrisée
| 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) |
### le combat
- [X] modifier la difficulté en fonction de l'état du combatant /offensif/actif/passif/
## Système de Dés
- **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
### les items
- [X] ajouter apprentissage aux abilities
- [X] passer le type d'arme en select/options
- [X] ajouter handicap de rareté
- [X] ajouter pour items Item "competence nécessaire"
- [X] gérer les rolls d'items dans le chat
- [X] repasser sur les différents itemTypes et sheets
- [X] verifier le selector de traits (trait pratique cf : msg pretre)
- [X] construire une selecteur de traits, traits= CONFIG.VERMINE.traits
traits:[
key:{
name:string,
description:string,
value:number
}
]
- [X] ajouter des dégats sur chaques fiches item
### Initiative
Formule : `(@abilities.reflexes.value + @skills.alertness.value)d10cs>=@combatStatus.difficulty`
---
## Architecture
```
module/
├── vermine2047.mjs # Point d'entrée (Hooks.once init)
├── models/ # DataModels (Foundry v2 TypeDataModel)
│ ├── actor/ # character.mjs, npc.mjs, group.mjs, creature.mjs
│ └── item/ # Les 12 types d'objets
├── documents/ # VermineActor, VermineItem
├── applications/
│ └── sheets/ # Fiches AppV2 par type
├── system/
│ ├── config.mjs # CONFIG.VERMINE (totems, traits, compétences…)
│ ├── roll.mjs # VermineUtils.roll()
│ ├── 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 # Paramètres du système (mode de jeu…)
│ ├── tour.mjs # Visites guidées (stub)
│ └── group-link.mjs # GroupLink : synchro acteurs↔groupes
templates/
├── actor/appv2/ # Templates AppV2 des fiches acteurs
├── actor/ # Templates legacy (coexistence)
└── item/ # Templates des fiches objets
css/
└── vermine2047.min.css # Compilé depuis LESS
less/
└── base.less # Source LESS principale
```
## Objets Clés
- `game.vermine2047``{ VermineUtils, VermineCombat, GroupLink }`
- `CONFIG.VERMINE` → totems, traits, compétences, domaines, états de combat…
- `CONFIG.ui.combat``VermineCombatTracker` personnalisé
## Commandes
```sh
npm run build:less # LESS → css/vermine2047.min.css
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 # 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
```
### Prérequis
- Node.js `v16.15.1` (cf `.nvmrc`)
- Foundry VTT **v14**
## Notes de Développement
- **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`.
---
## 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.
-203
View File
@@ -1,203 +0,0 @@
# Révision Complète des Templates Acteurs Vermine2047
## 📅 Date: 2026-06-04
## 🎯 Objectif
Réviser fichiers par fichier les templates des acteurs pour identifier et corriger les duplications et incohérences, comme demandé par l'utilisateur.
---
## ✅ Corrections Effectuées
### 1. **Corrections de Structure CSS**
-`templates/actor/parts/actor-items.hbs:77` - `grid grid-2``grid grid-2col`
### 2. **Corrections des Balises HTML**
Toutes les balises `<p><a>` mal utilisées dans les listes d'items ont été remplacées par `<div><a class="item-control item-edit">` :
-`templates/actor/parts/actor-weapons.hbs:25-36` - 6 balises `<p>` corrigées
-`templates/actor/parts/actor-defenses.hbs:38-41` - 4 balises `<p>` corrigées
-`templates/actor/group/group-items.hbs:20-21` - 2 balises `<p>` corrigées
-`templates/actor/group/group-vehicles.hbs:22-27` - 3 balises `<p>` corrigées
**Impact**: Meilleure sémantique HTML et cohérence avec le reste du codebase.
### 3. **Corrections de Fautes de Frappe**
-`templates/actor/character/character-totem.hbs:12-16,19-23,26-30` - `smarttlk``smarttl` (3 occurrences)
-`templates/actor/character/character-totem.hbs:98` - `{{compétence}}``"Compétence"` (tooltip)
### 4. **Corrections de Classes CSS Dupliquées**
-`templates/actor/character/character-totem.hbs:59` - `class="item-name" class="flexrow"``class="item-name flexrow"`
### 5. **Corrections de Commentaires HTML**
Tous les commentaires HTML standard `<!-- -->` ont été convertis en commentaires Handlebars `{{!-- --}}` :
-`templates/actor/character/character-features.hbs:1`
-`templates/actor/character/character-header.hbs:1`
-`templates/actor/group/group-header.hbs:1`
-`templates/actor/character/character-totem.hbs:30-34` - Commentaire multi-lignes simplifié
-`templates/actor/character/character-id.hbs:5` - Ajout de commentaire
### 6. **Uniformisation des Localisations**
-`templates/actor/character/character-features.hbs:2` - `Caractéristiques``{{ localize 'VERMINE.abilities' }}`
-`templates/actor/character/character-features.hbs:32` - `Compétences``{{ localize 'VERMINE.skills' }}`
### 7. **Suppression des Balises Orphelines**
-`templates/actor/character/character-id.hbs:112` - Suppression de `{{/if}}` orphelin
### 8. **Optimisation des Structures Dupliquées**
#### a) Création de Partial pour les Catégories de Compétences NPC
-**Nouveau fichier**: `templates/actor/parts/npc-skill-category.hbs`
- Partial réutilisable pour afficher une catégorie de compétences
- Accepte `categoryKey` et `categoryLabel` comme paramètres
-**Modification**: `templates/actor/actor-npc-sheet.hbs:227-297`
- Remplacement de ~150 lignes de code dupliqué par une boucle Handlebars
- Utilisation du nouveau partial pour les 6 catégories (Homme, Animal, Outil, Arme, Survie, Monde)
- **Réduction**: ~145 lignes de code
#### b) Création de Partial Générique pour les Listes d'Items
-**Nouveau fichier**: `templates/actor/parts/item-list.hbs`
- Partial générique et réutilisable pour afficher des listes d'items
- Prend en charge: itemType, items, createType, showSkill
- Peut être utilisé pour standardiser l'affichage des listes dans character-totem.hbs et group-info.hbs
### 9. **Correction d'Erreur JavaScript**
-`module/system/roll.mjs:365-424` - Correction de l'erreur `html.find(...).forEach is not a function`
- Problème: La fonction `chatListenners` recevait un objet jQuery ou un élément DOM, et `html.find()` échouait si `html` était un élément DOM natif
- Solution: Ajout de `const $html = $(html);` au début de la fonction
- Remplacement de toutes les occurrences de `html.` par `$html.` dans la fonction
- **Impact**: La fonction gère maintenant correctement les deux types d'entrée (jQuery object ou DOM element)
---
## 📁 Fichiers Modifiés
### Templates Principaux (4)
1. `templates/actor/actor-character-sheet.hbs` - OK
2. `templates/actor/actor-npc-sheet.hbs` - ✅ Optimisé
3. `templates/actor/actor-creature-sheet.hbs` - OK
4. `templates/actor/actor-group-sheet.hbs` - OK
### Partials Character (6)
1. `templates/actor/character/character-features.hbs` - ✅ Corrigé
2. `templates/actor/character/character-header.hbs` - ✅ Corrigé
3. `templates/actor/character/character-id.hbs` - ✅ Corrigé
4. `templates/actor/character/character-stories.hbs` - OK
5. `templates/actor/character/character-totem.hbs` - ✅ Corrigé (multiples corrections)
6. `templates/actor/character/character-combat.hbs` - OK (à optimiser)
### Partials Parts (7)
1. `templates/actor/parts/actor-items.hbs` - ✅ Corrigé
2. `templates/actor/parts/actor-weapons.hbs` - ✅ Corrigé
3. `templates/actor/parts/actor-defenses.hbs` - ✅ Corrigé
4. `templates/actor/parts/actor-effects.hbs` - OK
5. `templates/actor/parts/npc-skill-item.hbs` - OK
6. `templates/actor/parts/npc-skill-category.hbs` - ✅ **NOUVEAU**
7. `templates/actor/parts/item-list.hbs` - ✅ **NOUVEAU**
### Partials Group (5)
1. `templates/actor/group/group-header.hbs` - ✅ Corrigé
2. `templates/actor/group/group-info.hbs` - ✅ Corrigé
3. `templates/actor/group/group-items.hbs` - ✅ Corrigé
4. `templates/actor/group/group-vehicles.hbs` - ✅ Corrigé
5. `templates/actor/group/group-experience.hbs` - OK
### JavaScript (2)
1. `module/system/roll.mjs` - ✅ Correction de l'erreur html.find().forEach
2. `module/system/hooks.mjs` - OK (pas de modification nécessaire)
### Nouveaux Fichiers Créés (2)
1. `templates/actor/parts/npc-skill-category.hbs`
2. `templates/actor/parts/item-list.hbs`
---
## 📊 Statistiques
- **Fichiers analysés**: 24 templates + 2 fichiers JS
- **Fichiers modifiés**: 16 fichiers
- **Nouveaux fichiers créés**: 3 (2 partials + 1 rapport)
- **Duplications supprimées**: 1 majeure (catégories de compétences NPC)
- **Lignes de code réduites**: ~150+ lignes
- **Problèmes corrigés**: 20+
- **Partials créés**: 2
---
## 🔍 Problèmes Restants à Résoudre
### 1. **Duplication des Sections de Blessures**
Les templates suivants ont des implémentations similaires pour les blessures :
- `templates/actor/character/character-combat.hbs` (lignes 108-199)
- `templates/actor/npc/npc-combat.hbs` (lignes 23-47)
- `templates/actor/creature/creature-combat.hbs` (lignes 34-66)
**Solution recommandée**: Créer un partial `templates/actor/parts/wounds-section.hbs` pour standardiser l'affichage des blessures (minor, major, deadly).
### 2. **Utilisation du Partial item-list.hbs**
Le partial `item-list.hbs` a été créé mais n'est pas encore utilisé. Il pourrait remplacer les duplications dans :
- `templates/actor/character/character-totem.hbs` (5 listes: abilities, specialties, backgrounds, traumas, evolutions)
- `templates/actor/group/group-info.hbs` (5 listes identiques)
- `templates/actor/group/group-experience.hbs` (1 liste)
**Impact potentiel**: Réduction de ~200+ lignes de code dupliqué.
### 3. **Erreurs JavaScript Restantes**
Les erreurs suivantes n'ont pas encore été investiguées :
- `vermine2047.mjs:83` - `Cannot read properties of undefined (reading 'Actor')` - Problème de timing avec `game.system.template.Actor`
- `actor.mjs:89` - `Cannot read properties of undefined (reading 'difficulty')` - Problème dans `prepareCombatStatus`
---
## 📝 Rapport Complet
Un rapport détaillé a été créé : `REVISION_TEMPLATES_RAPPORT.md`
---
## 🎯 Résumé des Actions
### ✅ Terminées
1. Correction de toutes les incohérences de syntaxe HTML/CSS
2. Suppression des duplications évidentes (catégories de compétences NPC)
3. Correction des fautes de frappe et erreurs de syntaxe
4. Uniformisation des commentaires et localisations
5. Correction de l'erreur JavaScript `html.find(...).forEach`
6. Création de 2 nouveaux partials réutilisables
### ⏳ Recommandations pour la Suite
1. Créer un partial pour les blessures (`wounds-section.hbs`)
2. Appliquer le partial `item-list.hbs` dans les templates existants
3. Investiguer et corriger les erreurs JavaScript restantes
4. Tester tous les templates dans FoundryVTT
---
## 💡 Améliorations Apportées
### Maintenabilité
- **Réduction de la duplication**: ~150 lignes supprimées grâce aux partials
- **Meilleure organisation**: 2 nouveaux partials créés pour une meilleure réutilisation
- **Cohérence accrue**: Uniformisation des commentaires et des balises
### Robustesse
- **Correction d'erreurs**: 1 erreur JavaScript critique corrigée
- **Meilleure sémantique HTML**: Remplacement des balises `<p>` inappropriées
- **Suppression de balises orphelines**: Élimination de `{{/if}}` sans correspondant
### Internationalisation
- **Localisations ajoutées**: 2 titres maintenant localisés
- **Préparation pour traduction**: Structure plus propre pour les traductions futures
---
## 📌 Conclusion
Cette révision a permis de :
1. **Corriger** les erreurs de syntaxe et d'incohérence dans les templates
2. **Optimiser** le code en supprimant les duplications évidentes
3. **Améliorer** la maintenabilité avec de nouveaux partials
4. **Stabiliser** le code JavaScript en corrigant une erreur critique
Le travail peut être considéré comme **complet pour la phase 1** (nettoyage et correction). La phase 2 (optimisation avancée) consiste à créer des partials supplémentaires pour les sections de blessures et à appliquer le partial `item-list.hbs` dans les templates existants.
-175
View File
@@ -1,175 +0,0 @@
# Rapport de Révision des Templates Acteurs Vermine2047
## Date: 2026-06-04
## Objectif
Réviser fichiers par fichier les templates des acteurs pour identifier et corriger les duplications et incohérences.
## Problèmes Initiaux Identifiés
### 1. Erreurs JavaScript
Les erreurs initiales reportées incluaient:
- `ENOENT: no such file or directory, open '/.../templates/item/partials/damages.html'` → Fichier existe en `.hbs`
- `Cannot read properties of undefined (reading 'Actor')` → Problème dans vermine2047.mjs:123
- `html.find(...).forEach is not a function` → Problème dans roll.mjs:373
- `Cannot read properties of undefined (reading 'difficulty')` → Problème dans actor.mjs:89
- `ActiveEffect application phase "initial" has already completed` → Problème de cycle de vie
### 2. Duplications dans les Templates
#### a) Duplication des catégories de compétences NPC (actor-npc-sheet.hbs)
**Problème**: 6 catégories de compétences (Homme, Animal, Outil, Arme, Survie, Monde) avec la même structure HTML dupliquée.
**Solution**:
- Créé un nouveau partial: `templates/actor/parts/npc-skill-category.hbs`
- Remplacé la section dupliquée (lignes 227-297) par une boucle Handlebars
- Utilisation: `{{> "systems/vermine2047/templates/actor/parts/npc-skill-category.hbs" categoryKey=key categoryLabel=(concat "VERMINE.skill_category." key)}}`
**Réduction**: ~150 lignes → ~5 lignes
#### b) Duplication de la structure des blessures
**Fichiers concernés**:
- `character-combat.hbs` (lignes 108-199)
- `npc-combat.hbs` (lignes 23-47)
- `creature-combat.hbs` (lignes 34-66)
**Problème**: Chaque template de combat a sa propre implémentation des radio buttons pour les blessures.
**Solution recommandée**: Créer un partial `wounds-section.hbs` (à implémenter)
#### c) Duplication des listes d'items
**Fichiers concernés**:
- `character-totem.hbs` (abilities, specialties, backgrounds, traumas, evolutions)
- `group-info.hbs` (abilities, specialties, backgrounds, traumas, evolutions)
- `group-experience.hbs` (group abilities)
**Solution**:
- Créé un partial générique: `templates/actor/parts/item-list.hbs`
- Peut être utilisé pour standardiser l'affichage des listes d'items
## Corrections Effectuées
### 1. Corrections de Classes CSS
-`actor-items.hbs:77`: `grid grid-2``grid grid-2col`
### 2. Corrections des Balises HTML
-`actor-weapons.hbs:25-36`: Remplacement des `<p><a>` par `<div><a class="item-control item-edit">`
-`actor-defenses.hbs:38-41`: Remplacement des `<p><a>` par `<div><a class="item-control item-edit">`
-`group-items.hbs:20-21`: Remplacement des `<p><a>` par `<div><a class="item-control item-edit">`
-`group-vehicles.hbs:22-27`: Remplacement des `<p><a>` par `<div><a class="item-control item-edit">`
### 3. Corrections de Fautes de Frappe
-`character-totem.hbs:12-16,19-23,26-30`: `smarttlk``smarttl`
-`character-totem.hbs:98`: `{{compétence}}``"Compétence"` (tooltips)
### 4. Corrections de Doubles Classes
-`character-totem.hbs:59`: `class="item-name" class="flexrow"``class="item-name flexrow"`
### 5. Corrections de Commentaires HTML
-`character-features.hbs:1`: `<!-- Character -->``{{!-- Character --}}`
-`character-header.hbs:1`: `<!-- HEADER -->``{{!-- HEADER --}}`
-`group-header.hbs:1`: `<!-- HEADER -->``{{!-- HEADER --}}`
-`character-totem.hbs:30-34`: Commentaire HTML multi-lignes → `{{!-- Abstract Items --}}`
-`character-id.hbs:5`: Ajout de commentaire Handlebars
### 6. Corrections de Localisation
-`character-features.hbs:2`: `Caractéristiques``{{ localize 'VERMINE.abilities' }}`
-`character-features.hbs:32`: `Compétences``{{ localize 'VERMINE.skills' }}`
### 7. Optimisation des Structures Dupliquées
-**Création de `npc-skill-category.hbs`**: Partial pour les catégories de compétences NPC
-**Modification de `actor-npc-sheet.hbs`**: Utilisation du nouveau partial avec boucle
-**Création de `item-list.hbs`**: Partial générique pour les listes d'items
### 8. Correction de Balises Orphelines
-`character-id.hbs:112`: Suppression de `{{/if}}` orphelin
## Fichiers Modifiés
### Templates Principaux
1. `actor-character-sheet.hbs` - Structure de base OK
2. `actor-npc-sheet.hbs` - ✅ Optimisé (duplication des catégories de compétences supprimée)
3. `actor-creature-sheet.hbs` - Structure OK
4. `actor-group-sheet.hbs` - Structure OK
### Partials Character
1. `character/character-features.hbs` - ✅ Commentaires et localisations corrigés
2. `character/character-header.hbs` - ✅ Commentaire corrigé
3. `character/character-id.hbs` - ✅ Commentaire ajouté, balise orpheline supprimée
4. `character/character-stories.hbs` - OK
5. `character/character-totem.hbs` - ✅ Fautes de frappe corrigées, commentaire corrigé, double classe corrigée
6. `character/character-combat.hbs` - À optimiser (duplication avec wounds)
### Partials Parts
1. `parts/actor-items.hbs` - ✅ grid-2 → grid-2col
2. `parts/actor-weapons.hbs` - ✅ Balises <p> corrigées
3. `parts/actor-defenses.hbs` - ✅ Balises <p> corrigées
4. `parts/actor-effects.hbs` - OK
5. `parts/npc-skill-item.hbs` - OK
6. `parts/npc-skill-category.hbs` - ✅ NOUVEAU
7. `parts/item-list.hbs` - ✅ NOUVEAU
### Partials Group
1. `group/group-header.hbs` - ✅ Commentaire corrigé
2. `group/group-info.hbs` - ✅ Commentaire corrigé
3. `group/group-items.hbs` - ✅ Balises <p> corrigées
4. `group/group-vehicles.hbs` - ✅ Balises <p> corrigées
5. `group/group-experience.hbs` - OK
### Templates de Combat
1. `npc/npc-combat.hbs` - À optimiser
2. `creature/creature-combat.hbs` - À optimiser
3. `character/character-combat.hbs` - À optimiser
### Autres
1. `create.hbs` - OK
## Recommandations pour la Suite
### 1. Créer un partial pour les blessures
Créer `templates/actor/parts/wounds-section.hbs` pour standardiser l'affichage des blessures (minor, major, deadly) utilisés dans:
- character-combat.hbs
- npc-combat.hbs
- creature-combat.hbs
### 2. Standardiser les listes d'items
Utiliser le partial `item-list.hbs` pour remplacer les duplications dans:
- character-totem.hbs (5 listes)
- group-info.hbs (5 listes)
- group-experience.hbs (1 liste)
### 3. Vérifier les erreurs JavaScript
Les erreurs initiales doivent être investiguées dans:
- `vermine2047.mjs:123` - `Cannot read properties of undefined (reading 'Actor')`
- `roll.mjs:373` - `html.find(...).forEach is not a function`
- `actor.mjs:89` - `Cannot read properties of undefined (reading 'difficulty')`
### 4. Vérifier les références .html
Bien que aucune référence `.html` n'ait été trouvée dans les templates, l'erreur initiale suggère qu'il y a des références dans le code JavaScript. Rechercher dans:
- Les fichiers `.mjs` pour des références à `damages.html`
- Les appels à `loadTemplates()` ou `renderTemplate()`
## Statistiques
- **Fichiers analysés**: 24 templates
- **Duplications supprimées**: 1 (catégories de compétences NPC)
- **Partials créés**: 2 (npc-skill-category.hbs, item-list.hbs)
- **Fichiers modifiés**: 12
- **Lignes de code réduites**: ~150+ lignes
- **Problèmes corrigés**: 15+
## Prochaines Étapes
1. ✅ Corriger les erreurs de syntaxe HTML/CSS (TERMINÉ)
2. ✅ Supprimer les duplications évidentes (TERMINÉ pour NPC skills)
3. ⏳ Créer des partials pour les sections communes (EN COURS)
4. ⏳ Optimiser les templates de combat
5. ⏳ Vérifier et corriger les erreurs JavaScript
6. ⏳ Tester tous les templates dans FoundryVTT
## Notes
- Tous les templates utilisent maintenant `.hbs` au lieu de `.html`
- Les commentaires sont progressivement uniformisés vers `{{!-- --}}`
- Les structures de grille utilisent `grid-2col` au lieu de `grid-2`
- Les balises `<p>` pour les cellules de tableau ont été remplacées par `<div>`
+34
View File
@@ -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`.
+1
View File
@@ -223,6 +223,7 @@
"world": "World"
},
"CharacterNamePlaceholder": "Character Name",
"ProfilePlaceholder": "Profile",
"none": "None",
"bonuses": "Bonuses",
"choose_ability": "Choose Ability",
+1
View File
@@ -314,6 +314,7 @@
"total_damage": "Dégâts totaux",
"wound_thresholds": "Seuils de blessures",
"CharacterNamePlaceholder": "Nom du personnage",
"ProfilePlaceholder": "Profil",
"sexes": {
"female": "Féminin",
"male": "Masculin"
+41 -12
View File
@@ -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;
@@ -106,6 +106,10 @@
overflow-y: auto;
padding: 8px;
border-right: 1px solid rgba(255, 255, 255, 0.1);
> * {
flex: 0 0 auto;
}
}
> nav.tabs {
@@ -291,7 +295,7 @@
.chooseTotem {
font-family: "DistressBlack", sans-serif;
font-size: 0.9rem;
font-size: 0.8rem;
cursor: pointer;
&:hover {
@@ -331,21 +335,39 @@
}
.padding-with-frieze {
margin-left: 18% !important;
margin-right: 10% !important;
padding: 0.25rem;
li {
max-width: 100%;
}
.major-totem {
position: relative;
.resource-label {
h4 {
position: absolute;
transform: rotate(-8deg);
opacity: 1;
transition: 0.2s;
font-size: 1em;
}
}
.major-totem {
display: flex;
justify-content: center;
margin-bottom: 0.75rem;
padding: 0.6rem;
background-color: rgba(0, 0, 0, 0.12);
border: 2px groove #eeede0;
.flexcol {
gap: 0.3rem;
h4 {
margin: 0;
font-size: 1.1em;
}
.img-totem {
width: 64px;
height: 64px;
filter: drop-shadow(0px 0px 10px rgb(0, 0, 0));
}
}
}
@@ -377,7 +399,7 @@
}
.char-vermine2047 {
font-size: 1.5rem;
font-size: 1.1rem;
}
}
@@ -409,6 +431,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;
+38 -29
View File
@@ -19,17 +19,39 @@
}
div.minor-totems {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
gap: 0.25rem;
min-height: 8.5rem;
padding: 0.5rem;
background-color: rgba(146, 156, 111, 0.5215686275);
h3 {
text-align: center;
margin: 0;
font-size: 1.2rem;
line-height: 1.1;
}
.totem-row {
display: flex;
justify-content: space-between;
align-items: center;
}
h5 {
position: absolute;
top: 0;
margin: 0;
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
font-size: 0.8rem;
img {
max-width: 2rem;
position: absolute;
bottom: -2rem;
max-width: 1.5rem;
height: auto;
}
&.human, &.adapted {
@@ -53,44 +75,31 @@
}
.totem-dice {
display: flex;
justify-content: space-between;
align-items: center;
.human-dice,
.adapted-dice {
display: flex;
flex-direction: row;
margin-left: 2rem;
align-items: center;
}
.human-dice i,
.adapted-dice i {
padding-top: 0.5rem;
color: @totem-human-color;
position: relative;
z-index: 0;
pointer-events: none;
}
.adapted-dice {
justify-content: flex-end;
margin-left: 0;
margin-right: 2rem;
transform: rotate(180deg);
i {
transform: rotate(180deg);
color: @totem-adapted-color;
}
}
.adapted-dice i {
transform: rotate(180deg);
padding-top: 0.5rem;
color: @totem-adapted-color;
}
}
.human {
left: 0;
img { left: 0; }
}
.adapted {
right: 0;
img { right: 0; }
}
}
}
+41
View File
@@ -12,6 +12,47 @@ body.system-vermine2047 {
color: @theme-color-secondary;
}
// Override Foundry Core default CSS custom properties
// for better readability on the dark Vermine2047 theme.
// AppV2 windows (sheets) use .themed.theme-dark on their content
.system-vermine2047 .themed.theme-dark {
--color-text-primary: #888888;
--color-text-emphatic: #999999;
--color-text-secondary: #777777;
--color-text-subtle: #666666;
--color-text-light-highlight: #888888;
--color-text-light-heading: #777777;
--color-text-light-primary: #666666;
}
// Old-style Application windows (settings-config, dialogs) inherit from body.theme-dark
body.system-vermine2047.theme-dark {
--color-text-primary: #888888;
--color-text-emphatic: #999999;
--color-text-secondary: #777777;
--color-text-subtle: #666666;
--color-text-light-highlight: #888888;
--color-text-light-heading: #777777;
--color-text-light-primary: #666666;
}
#settings-config button,
#settings-config a.button {
--button-text-color: #000000;
--button-border-color: #666666;
color: #000000;
}
body.system-vermine2047.theme-dark .form-group > label {
color: #777777;
}
#settings-config p,
#settings-config .hint {
color: #777777;
}
img {
border: none;
}
@@ -114,7 +114,8 @@ export default class VermineBaseActorSheet extends HandlebarsApplicationMixin(fo
isEditable: this.isEditable,
enrichedNotes: await enrich("identity.notes"),
enrichedBiography: await enrich("identity.biography"),
enrichedRelations: await enrich("identity.relations")
enrichedRelations: await enrich("identity.relations"),
enrichedEquipmentNotes: await enrich("equipment.description")
}
}
@@ -230,6 +231,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))
}
+5 -320
View File
@@ -43,295 +43,27 @@ export default class VermineActor extends Actor {
* Prepare Character type specific data
*/
_prepareCharacterData(actorData) {
if (actorData.type !== 'character') return;
this._setAgeType();
this._setCharacterEffort();
this._setCharacterSelfControl();
this._setCharacterThresholds();
// Make modifications to data here. For example:
const systemData = actorData.system;
// Loop through ability scores, and add their modifiers to our sheet output.
for (let [key, ability] of Object.entries(systemData.abilities)) {
// Calculate the modifier using d20 rules.
ability.mod = Math.floor((ability.value - 10) / 2);
}
this.prepareCombatStatus();
// Character derived data is computed by VermineCharacterData.prepareDerivedData()
}
prepareCombatStatus() {
// Ensure combatStatus exists (defined in base template)
if (!this.system.combatStatus) {
this.system.combatStatus = { difficulty: "9", label: "Passif" };
return;
}
// Ensure difficulty exists
if (!this.system.combatStatus.difficulty) {
this.system.combatStatus.difficulty = "9";
}
//combat initiative reaction difficulty
const difficulty = parseInt(this.system.combatStatus.difficulty) || 9;
// Only update if values are different to avoid triggering unnecessary updates
const currentLabel = this.system.combatStatus.label;
let newLabel = "Passif";
switch (difficulty) {
case 5: newLabel = "Offensif"; break;
case 7: newLabel = "Actif"; break;
case 9: newLabel = "Passif"; break;
}
// Only update if label changed
if (currentLabel !== newLabel) {
this.system.combatStatus.label = newLabel;
}
// Only update difficulty if it was undefined or invalid
if (!this.system.combatStatus.difficulty || isNaN(parseInt(this.system.combatStatus.difficulty))) {
this.system.combatStatus.difficulty = "9";
}
}
/**
* Prepare NPC type specific data.
*/
_prepareNpcData(actorData) {
if (actorData.type !== 'npc') return;
// Make modifications to data here. For example:
const systemData = actorData.system;
// Set wound thresholds based on threat level
this._setNpcThresholds();
// Set reserve max values based on role
this._setNpcAttributes();
this.prepareCombatStatus();
// Prepare abilities with labels
for (let [k, v] of Object.entries(systemData.abilities)) {
v.label = game.i18n.localize(CONFIG.VERMINE.abilities[k]) ?? k;
}
}
/**
* Set NPC wound thresholds based on threat level
*/
_setNpcThresholds() {
const health = this.system.abilities?.health?.value || 1;
const threatLevel = this.system.threat?.value || 1;
const threatConfig = CONFIG.VERMINE.npcThreatLevels[threatLevel] || {};
// Use threat-based wounds or fall back to health-based
this.system.minorWound.threshold = threatConfig.minorWound || health;
this.system.majorWound.threshold = threatConfig.majorWound || (health + 3);
this.system.deadlyWound.threshold = threatConfig.deadlyWound || (health + 7 < 11 ? health + 7 : 10);
// Set max wounds based on threat level
this.system.minorWound.max = threatConfig.minorWound || 4;
this.system.majorWound.max = threatConfig.majorWound || 3;
this.system.deadlyWound.max = threatConfig.deadlyWound || 2;
}
/**
* Set NPC attributes from role level
*/
_setNpcAttributes() {
const roleLevel = this.system.role?.value || 1;
const roleConfig = CONFIG.VERMINE.npcRoleLevels[roleLevel] || {};
// Set effort and self_control based on role
this.system.attributes.effort.max = roleConfig.pools || 0;
this.system.attributes.self_control.max = roleConfig.reaction_bonus || 0;
// NPC derived data is computed by VermineNpcData.prepareDerivedData()
}
/**
* Prepare Group type specific data.
*/
_prepareGroupData(actorData) {
if (actorData.type !== 'group') return;
this.prepareCombatStatus();
// Initialize group-specific data if not present
this._initGroupData();
// Calculate reserve max based on group level
this._calculateGroupReserve();
// Update morale level based on dice value
this._updateGroupMorale();
}
/**
* Initialize group data with defaults
*/
_initGroupData() {
if (this.type !== 'group') return;
const system = this.system;
// Initialize objectives if not present
if (!system.objectives) {
system.objectives = { major: [], minor: [] };
}
// Initialize groupAbilities if not present
if (!system.groupAbilities) {
system.groupAbilities = [];
}
// Initialize reserve if not present
if (!system.reserve) {
system.reserve = { value: 0, min: 0, max: 10 };
}
}
/**
* Calculate group reserve max based on level
* Rules: Group level determines reserve size
*/
_calculateGroupReserve() {
if (this.type !== 'group') return;
const level = this.system.level?.value || 1;
// Reserve max is based on group level (simplified: level * 1D for now)
// Can be customized based on specific rules
this.system.reserve.max = Math.min(10, level * 2);
// Ensure value doesn't exceed max
if (this.system.reserve.value > this.system.reserve.max) {
this.system.reserve.value = this.system.reserve.max;
}
}
/**
* Update group morale level based on dice value
* Rules: 7D+ = Haut, 6-3D = Normal, 2D- = Bas, 0D = Crise
*/
_updateGroupMorale() {
if (this.type !== 'group') return;
const moraleValue = this.system.morale?.value || 0;
const moraleLevel = this.system.morale?.level;
// If level is already explicitly set, keep it
if (moraleLevel && moraleLevel !== "high") return;
// Determine morale level based on dice value
if (moraleValue >= 7) {
this.system.morale.level = "high";
} else if (moraleValue >= 3) {
this.system.morale.level = "normal";
} else if (moraleValue >= 1) {
this.system.morale.level = "low";
} else {
this.system.morale.level = "crisis";
}
// Group derived data is computed by VermineGroupData.prepareDerivedData()
}
/**
* Prepare Creature type specific data.
* Calculates computed values from pattern, size, role, and pack.
*/
_prepareCreatureData(actorData) {
if (actorData.type !== 'creature') return;
this.prepareCombatStatus();
// Calculate computed values from pattern, size, role, and pack
this._calculateCreatureComputedValues();
// Set wound thresholds from creature characteristics
this._calculateCreatureWoundThresholds();
}
/**
* Calculate creature computed values from pattern, size, role, and pack.
* Rules: Attack = pattern + size + pack + role.reaction
* Damage = pattern.damage + size.vigor + pack.damage
* Reaction = role.reaction + role.reaction_bonus
*/
_calculateCreatureComputedValues() {
if (this.type !== 'creature') return;
const patternLevel = this.system.pattern?.value || 1;
const sizeLevel = this.system.size?.value || 1;
const roleLevel = this.system.role?.value || 1;
const packLevel = this.system.pack?.value || 0;
// Get config values
const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel] || {};
const sizeConfig = CONFIG.VERMINE.creatureSizeLevels[sizeLevel] || {};
const roleConfig = CONFIG.VERMINE.creatureRoleLevels[roleLevel] || {};
const packConfig = CONFIG.VERMINE.creaturePackLevels[packLevel] || {};
// Calculate computed values
this.system.computed = this.system.computed || {};
// Attack: pattern + size + pack + role.reaction
this.system.computed.attack = (patternConfig.attack || 0) +
(sizeConfig.attack || 0) +
(packConfig.attack || 0) +
(roleConfig.reaction || 0);
// Damage: pattern + size.vigor + pack
this.system.computed.damage = (patternConfig.damage || 0) +
(sizeConfig.vigor || 0) +
(packConfig.damage || 0);
// Vigor: size.vigor + pack.damage
this.system.computed.vigor = (sizeConfig.vigor || 0) + (packConfig.damage || 0);
// Reaction: role.reaction + role.reaction_bonus
this.system.computed.reaction = (roleConfig.reaction || 0) + (roleConfig.reaction_bonus || 0);
this.system.computed.reactionBonus = roleConfig.reaction_bonus || 0;
// Pools (reserves)
this.system.computed.pools = roleConfig.pools || 0;
// Gear and hindrance
this.system.computed.gear = roleConfig.gear || 9;
this.system.computed.gearHindrance = roleConfig.gear_hindrance || 0;
// Protection
this.system.computed.protection = roleConfig.protection || 1;
}
/**
* Calculate creature wound thresholds from pattern, size, and pack.
* Rules: Thresholds are sum of minorWound, majorWound, deadlyWound from all sources
*/
_calculateCreatureWoundThresholds() {
if (this.type !== 'creature') return;
const patternLevel = this.system.pattern?.value || 1;
const sizeLevel = this.system.size?.value || 1;
const packLevel = this.system.pack?.value || 0;
const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel] || {};
const sizeConfig = CONFIG.VERMINE.creatureSizeLevels[sizeLevel] || {};
const packConfig = CONFIG.VERMINE.creaturePackLevels[packLevel] || {};
// Calculate wound thresholds (sum of all sources)
this.system.minorWound.threshold = (patternConfig.minorWound || 0) +
(sizeConfig.minorWound || 0) +
(packConfig.minorWound || 0);
this.system.majorWound.threshold = (patternConfig.majorWound || 0) +
(sizeConfig.majorWound || 0) +
(packConfig.majorWound || 0);
this.system.deadlyWound.threshold = (patternConfig.deadlyWound || 0) +
(sizeConfig.deadlyWound || 0) +
(packConfig.deadlyWound || 0);
// Set max wounds
this.system.minorWound.max = Math.min(5, this.system.minorWound.threshold + 2);
this.system.majorWound.max = Math.min(4, this.system.majorWound.threshold + 1);
this.system.deadlyWound.max = Math.min(2, this.system.deadlyWound.threshold);
// Creature derived data is computed by VermineCreatureData.prepareDerivedData()
}
/**
@@ -376,53 +108,6 @@ export default class VermineActor extends Actor {
// Process additional NPC data here.
}
_setCharacterSelfControl() {
this.system.attributes.self_control.max = 0 + Object.values(this.system.abilities).filter(i => i.category === "mental" || i.category === "social").map((i) => i.value).reduce((acc, curr) => acc + curr, 0) + this.modFromAgeSelfControl;
}
_setCharacterEffort() {
this.system.attributes.effort.max = 0 + Object.values(this.system.abilities).filter(i => i.category === "physical" || i.category === "manual").map((i) => i.value).reduce((acc, curr) => acc + curr, 0) + this.modFromAgeEffort;
}
_setCharacterThresholds() {
const health = this.system.abilities.health.value;
this.system.minorWound.threshold = health;
this.system.majorWound.threshold = health + 3;
this.system.deadlyWound.threshold = (health + 7 < 11) ? health + 7 : 10;
this.system.minorWound.max = 4 + this.modFromAgeWounds.l;
this.system.majorWound.max = 3 + this.modFromAgeWounds.h;
this.system.deadlyWound.max = 2 + this.modFromAgeWounds.d;
}
_setAgeType() {
Object.keys(CONFIG.VERMINE.AgeTypes).forEach((type) => {
if (this.system.identity.age >= parseInt(CONFIG.VERMINE.AgeTypes[type].beginning, 10)) {
this.system.identity.ageType = type;
}
});
}
get ageType() {
return this.system.identity.ageType;
}
get modFromAgeSelfControl() {
return this.ageType == 1 ? -1 : 0;
}
get modFromAgeEffort() {
if (this.ageType == 1) return -1;
if (this.ageType == 3) return -2;
return 0;
}
get modFromAgeWounds() {
if (this.ageType == 1) return { l: 0, h: 0, d: -1 };
if (this.ageType == 3) return { l: -1, h: -1, d: -1 };
return { l: 0, h: 0, d: 0 };
}
// Character derived-data methods removed — handled by VermineCharacterData.prepareDerivedData()
}
+2 -2
View File
@@ -38,7 +38,7 @@ export default class VermineItem extends Item {
}
prepareAbilityData() {
console.log('ability data', this)
// ability data
const actorType = (this.actor !== null) ? this.actor.type : 'character';
if (this.system.type == "") {
@@ -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);
+3 -20
View File
@@ -94,20 +94,10 @@ export default class VermineCharacterData extends foundry.abstract.TypeDataModel
prepareDerivedData() {
super.prepareDerivedData()
// 1. Déterminer la tranche d'âge
this._setAgeType()
// 2. Calculer les modificateurs de caractéristiques
this._setAbilityModifiers()
// 3. Calculer les réserves (sang-froid et effort)
this._setSelfControlMax()
this._setEffortMax()
// 4. Calculer les seuils de blessures
this._setWoundThresholds()
// 5. Mettre à jour le statut de combat
this._updateCombatStatus()
}
@@ -118,22 +108,15 @@ export default class VermineCharacterData extends foundry.abstract.TypeDataModel
_setAgeType() {
const age = this.identity.age
const ageTypes = CONFIG.VERMINE.AgeTypes
for (const [type, cfg] of Object.entries(ageTypes)) {
const entries = Object.entries(ageTypes).reverse()
for (const [type, cfg] of entries) {
if (age >= parseInt(cfg.beginning, 10)) {
this.identity.ageType = parseInt(type, 10)
break
}
}
}
/**
* Calcule les modificateurs de caractéristiques (règle d20).
*/
_setAbilityModifiers() {
for (const ability of Object.values(this.abilities)) {
ability.mod = Math.floor((ability.value - 10) / 2)
}
}
/**
* Calcule le max de sang-froid :
* somme des caractéristiques mentales + sociales + modificateur d'âge.
+6 -6
View File
@@ -114,13 +114,13 @@ export default class VermineNpcData extends foundry.abstract.TypeDataModel {
const threatLevel = this.threat?.value || 1
const threatConfig = CONFIG.VERMINE.npcThreatLevels[threatLevel] || {}
this.minorWound.threshold = threatConfig.minorWound || health
this.majorWound.threshold = threatConfig.majorWound || (health + 3)
this.deadlyWound.threshold = threatConfig.deadlyWound || (health + 7 < 11 ? health + 7 : 10)
this.minorWound.threshold = health
this.majorWound.threshold = health + 3
this.deadlyWound.threshold = Math.min(health + 7, 10)
this.minorWound.max = threatConfig.minorWound || 4
this.majorWound.max = threatConfig.majorWound || 3
this.deadlyWound.max = threatConfig.deadlyWound || 2
this.minorWound.max = threatConfig.minorWound || 1
this.majorWound.max = threatConfig.majorWound || 1
this.deadlyWound.max = threatConfig.deadlyWound || 1
}
/**
+1
View File
@@ -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({
+107 -119
View File
@@ -1,5 +1,6 @@
export class TotemPicker extends Application {
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
export class TotemPicker extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(linkEl, actor) {
super();
@@ -7,45 +8,39 @@ export class TotemPicker extends Application {
this.actor = actor;
}
/* -------------------------------------------- */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
id: "TOTEM_PICKER",
title: game.i18n.localize("VERMINE.totem_picker"),
template: 'systems/vermine2047/templates/applications/choose-totem.hbs',
popOut: true,
resizable: true,
height: "800",
width: "800"
});
}
getData() {
// Send data to the template
static get DEFAULT_OPTIONS() {
return {
config: CONFIG.VERMINE,
}
id: "TOTEM_PICKER",
position: { width: 800, height: 800 },
window: { title: game.i18n.localize("VERMINE.totem_picker"), resizable: true }
};
}
activateListeners(html) {
super.activateListeners(html);
html.find('.totem').click(event => {
const totem = $(event.target).parent('a').data('totem');
if (totem != null) {
this.actor.update({ 'system.identity.totem': totem });
}
this.close();
static PARTS = {
main: {
template: 'systems/vermine2047/templates/applications/choose-totem.hbs'
}
};
_prepareContext() {
return { config: CONFIG.VERMINE };
}
_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;
this.actor.update({ 'system.identity.totem': link.dataset.totem });
this.close();
});
});
}
/*async _updateObject(event, formData) {
// console.log(formData.exampleInput);
}*/
}
export class ActorPicker extends Application {
export class ActorPicker extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(linkEl, actor) {
super();
@@ -53,152 +48,145 @@ export class ActorPicker extends Application {
this.actor = actor;
}
/* -------------------------------------------- */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
static get DEFAULT_OPTIONS() {
return {
id: "ACTOR_PICKER",
title: game.i18n.localize("VERMINE.actor_picker"),
template: 'systems/vermine2047/templates/applications/choose-actor.hbs',
popOut: true,
resizable: true,
height: "350",
width: "600"
});
position: { width: 600, height: 350 },
window: { title: game.i18n.localize("VERMINE.actor_picker"), resizable: true }
};
}
static PARTS = {
main: {
template: 'systems/vermine2047/templates/applications/choose-actor.hbs'
}
};
getData() {
// Send data to the template
_prepareContext() {
const npcs = game.actors.filter(a => a.type == "npc");
const characters = game.actors.filter(a => a.type == "character");
const all = game.actors.filter(a => a.type == "npc" || a.type == 'character');
const type = $(this.linkEl).data('type');
const type = this.linkEl.dataset.type;
let actorsList = [];
if (type == 'members') {
actorsList = characters;
} else if (type == 'relations') {
actorsList = npcs;//[...npcs, ...characters];
actorsList = npcs;
} else {
actorsList = all;
}
return {
config: CONFIG.VERMINE,
actorsList: actorsList
}
};
}
async activateListeners(html) {
super.activateListeners(html);
html.find('.actor').click(async (event) => {
const actorId = $(event.target).parent('div').data('actor-id');
const type = $(this.linkEl).data('type');
if (!actorId) return;
let actorsList = [];
_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;
const type = this.linkEl.dataset.type;
if (!actorId) return;
if (type == 'members') {
actorsList = foundry.utils.duplicate(this.actor.system.members || []);
} else if (type == 'encounters') {
actorsList = foundry.utils.duplicate(this.actor.system.encounters || []);
}
if (!Array.isArray(actorsList)) {
actorsList = [];
}
let actorsList = [];
// Add actor if not already present
if (!actorsList.includes(actorId)) {
actorsList.push(actorId);
}
if (type == 'members') {
actorsList = foundry.utils.duplicate(this.actor.system.members || []);
} else if (type == 'encounters') {
actorsList = foundry.utils.duplicate(this.actor.system.encounters || []);
}
if (type == 'members') {
await this.actor.update({ 'system.members': actorsList });
} else if (type == 'encounters') {
await this.actor.update({ 'system.encounters': actorsList });
}
this.close();
if (!Array.isArray(actorsList)) {
actorsList = [];
}
if (!actorsList.includes(actorId)) {
actorsList.push(actorId);
}
if (type == 'members') {
await this.actor.update({ 'system.members': actorsList });
} else if (type == 'encounters') {
await this.actor.update({ 'system.encounters': actorsList });
}
this.close();
});
});
}
}
export class TraitSelector extends Application {
export class TraitSelector extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(targetItem) {
super();
this.targetItem = targetItem;
this.traits = CONFIG.VERMINE.traits
this.traits = CONFIG.VERMINE.traits;
}
/* -------------------------------------------- */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["vermine2047", "trait-selector"],
title: game.i18n.localize("VERMINE.traits_selector"),
template: 'systems/vermine2047/templates/applications/choose-traits.hbs',
popOut: true,
resizable: true,
height: "500",
width: "500"
});
static get DEFAULT_OPTIONS() {
return {
id: "TRAIT_SELECTOR",
position: { width: 500, height: 500 },
window: { title: game.i18n.localize("VERMINE.traits_selector"), resizable: true },
classes: ["vermine2047", "trait-selector"]
};
}
getData() {
static PARTS = {
main: {
template: 'systems/vermine2047/templates/applications/choose-traits.hbs'
}
};
_prepareContext() {
return {
traits: this.traits,
item: this.targetItem
}
};
}
async activateListeners(html) {
super.activateListeners(html);
this.validateTraits(html);
html.find('input').change(ev => {
this.onChangeInput(ev)
})
_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) {
let checks = html.find("input.trait-selector");
for (let inp of checks) {
_validateTraits(html) {
const checks = html.querySelectorAll("input.trait-selector");
for (const inp of checks) {
if (this.targetItem.system.traits[inp.dataset.trait]) {
if (inp.type == "checkbox") {
inp.checked = true
inp.checked = true;
}
}
}
await this.render(true)
}
async onChangeInput(ev) {
let el = ev.currentTarget;
let traitKey = el.dataset.trait; // Récupère la clé du trait à partir de l'attribut data-trait
let traitValue = parseInt(el.value) || null
let traits = this.targetItem.system.traits || {}; // Récupère les traits actuels, ou un objet vide si aucun trait n'est défini
console.log(traitKey, traitValue, traits)
async _onChangeInput(ev) {
const el = ev.currentTarget;
const traitKey = el.dataset.trait;
const traitValue = parseInt(el.value) || null;
const traits = this.targetItem.system.traits || {};
if (el.classList.contains('trait-selector')) {
if (!traits[traitKey]) {
// Si la case est cochée, ajoute le trait
await this.targetItem.update({ [`system.traits.${traitKey}`]: this.traits[traitKey] });
} else {
// Si la case est décochée, retire le trait
await this.targetItem.update({ [`system.traits.${traitKey}`]: null });
}
}
if (traitValue) {
console.log(el.value)
// Logique pour les valeurs des traits si nécessaire
el.closest("label").querySelector('.trait-selector').checked = true;
} else {
el.closest("label").querySelector('.trait-selector').checked = false;
}
this.render(true)
this.render(true);
}
}
}
+43 -40
View File
@@ -26,46 +26,6 @@ VERMINE.DifficultyLevels = {
3: { "label": "DIFFICULTY_LEVELS.hard", "difficulty": 7 },
4: { "label": "DIFFICULTY_LEVELS.very_hard", "difficulty": 9 },
5: { "label": "DIFFICULTY_LEVELS.impossible", "difficulty": 10 }
},
VERMINE.ThreatLevels = {
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 },
}
VERMINE.ExperienceLevels = {
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" },
}
VERMINE.RoleLevels = {
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.PatternLevels = {
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 },
}
VERMINE.SizeLevels = {
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 }
}
VERMINE.PackLevels = {
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 }
}
/**
@@ -251,6 +211,49 @@ VERMINE.skillCategories = {
}
}
/**
* Skills mapping: each skill belongs to a category.
* Used by templates to render skill-select dropdowns.
*/
VERMINE.skills = {
// man
arts: { category: "man" },
civilization: { category: "man" },
psychology: { category: "man" },
rumors: { category: "man" },
healing: { category: "man" },
// animal
animalism: { category: "animal" },
dissection: { category: "animal" },
wildlife: { category: "animal" },
repulsion: { category: "animal" },
tracks: { category: "animal" },
// tool
crafting: { category: "tool" },
diy: { category: "tool" },
mecanical: { category: "tool" },
piloting: { category: "tool" },
technology: { category: "tool" },
// weapon
firearms: { category: "weapon" },
archery: { category: "weapon" },
armory: { category: "weapon" },
throwing: { category: "weapon" },
melee: { category: "weapon" },
// survival
alertness: { category: "survival" },
atletics: { category: "survival" },
food: { category: "survival" },
stealth: { category: "survival" },
close: { category: "survival" },
// world
environment: { category: "world" },
flora: { category: "world" },
road: { category: "world" },
toxics: { category: "world" },
ruins: { category: "world" }
}
VERMINE.sexes = { "male": "SEXES.male", "female": "SEXES.female" };
VERMINE.totems = {
-37
View File
@@ -1,37 +0,0 @@
import { VermineUtils } from "./roll.mjs";
export class CombatResultDialog extends Dialog {
constructor(dialogData, options) {
/*let options = { classes: ["combat", "result"], ...options };
let conf = {
title: "Résultat de la confrontation",
content: dialogData.content
};
super(conf, options);
this.dialogData = dialogData;*/
}
/* -------------------------------------------- */
activateListeners(html) {
/*super.activateListeners(html);
this.html = html;
this.setEphemere(this.dialogData.signe.system.ephemere);
html.find(".signe-aleatoire").click(event => this.setSigneAleatoire());
html.find("[name='signe.system.ephemere']").change((event) => this.setEphemere(event.currentTarget.checked));
html.find(".signe-xp-sort").change((event) => this.onValeurXpSort(event));
html.find("input.select-actor").change((event) => this.onSelectActor(event));
html.find("input.select-tmr").change((event) => this.onSelectTmr(event));*/
}
async onSelectActor(event) {
/*const actorId = this.html.find(event.currentTarget)?.data("actor-id");
const actor = this.dialogData.actors.find(it => it.id == actorId);
if (actor) {
actor.selected = event.currentTarget.checked;
}*/
}
}
+3 -6
View File
@@ -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;
}
@@ -139,7 +140,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
getHandicapSelect() {
const sel = this.#getHandicap();
return parseInt(sel?.value, 10) || 1;
return Math.max(0, (parseInt(sel?.value, 10) || 1) - 1);
}
getSkillCategory() {
@@ -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;
}
+8 -8
View File
@@ -43,19 +43,19 @@ export async function initUserDice(dice3d, user) {
export function darkenColor(color, percent) {
const num = parseInt(color.replace('#', ''), 16);
const amt = Math.round(2.55 * percent);
const R = (num >> 16) + amt;
const G = ((num >> 8) & 0x00FF) + amt;
const B = (num & 0x0000FF) + amt;
return '#' + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 + (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
const R = (num >> 16) - amt;
const G = ((num >> 8) & 0x00FF) - amt;
const B = (num & 0x0000FF) - amt;
return '#' + (0x1000000 + (R < 0 ? 0 : R > 255 ? 255 : R) * 0x10000 + (G < 0 ? 0 : G > 255 ? 255 : G) * 0x100 + (B < 0 ? 0 : B > 255 ? 255 : B)).toString(16).slice(1);
}
export function lightenColor(color, percent) {
const num = parseInt(color.replace('#', ''), 16);
const amt = Math.round(2.55 * percent);
const R = (num >> 16) - amt;
const G = ((num >> 8) & 0x00FF) - amt;
const B = (num & 0x0000FF) - amt;
return '#' + (0x1000000 + (R < 0 ? 0 : R > 255 ? 255 : R) * 0x10000 + (G < 0 ? 0 : G > 255 ? 255 : G) * 0x100 + (B < 0 ? 0 : B > 255 ? 255 : B)).toString(16).slice(1);
const R = (num >> 16) + amt;
const G = ((num >> 8) & 0x00FF) + amt;
const B = (num & 0x0000FF) + amt;
return '#' + (0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 + (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
}
export function oppositeColor(color) {
const num = parseInt(color.replace('#', ''), 16);
-1
View File
@@ -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);
+83 -235
View File
@@ -1,5 +1,4 @@
import { VERMINE } from "./config.mjs";
import { getActorSkillScore, updateActorSkillScore } from "./functions.mjs";
/**
* Handles combat-related dice rolls for Vermine2047.
@@ -28,7 +27,8 @@ export class VermineFight {
*/
async performTest(enemyAchievement, enemyConservation, skillKey, skill, params, actor) {
// Use d10 as per Vermine2047 official rules
const dicePool = (params.spleen != undefined || params.purpose != undefined) ? '5' : '4';
const basePool = Math.max(skill, 1)
const dicePool = (params.spleen != undefined || params.purpose != undefined) ? String(basePool + 1) : String(basePool)
const difficulty = params.difficulty || 7; // Default difficulty
const r = new Roll(dicePool + `d10`);
let diceString = '';
@@ -167,220 +167,26 @@ export class VermineFight {
// data injected to char data
static previousValues = {
dicePool: 4,
skills: VERMINE.skillsList,
cskills: VERMINE.cskills,
skills: [],
cskills: [],
cephalic: false,
achievementReroll: VERMINE.achievementReroll,
conservationReroll: VERMINE.conservationReroll
achievementReroll: 0,
conservationReroll: 0
};
static rollerTemplate = 'systems/vermine2047/templates/fight.html';
static CombatResultTemplate = 'systems/vermine2047/templates/fight-result.html';
static async chatMessageHandler(message, html, data) {
// console.log("accès au fin du fin", message._id);
// sélection du dé actif
html.on("click", '.confrontation .die.d10', event => {
const diceResult = parseInt($(event.target).html(), 10);
html.find('.confrontation .die.d10').removeClass('active');
$(event.target).addClass('active');
});
// sélection des dés d'accomplissement (achievement = successes)
html.on("click", '.confrontation .add-to-achievement', event => {
const diceResult = parseInt(html.find('.confrontation .die.d10.active').html(), 10);
html.find('.confrontation .die.d10.active').removeClass('min').addClass('max');
});
// sélection des dés de conservation
html.on("click", '.confrontation .add-to-conservation', event => {
const diceResult = parseInt(html.find('.confrontation .die.d10.active').html(), 10);
html.find('.confrontation .die.d10.active').removeClass('max').addClass('min');
});
// reset de la sélection des pools
html.on("click", '.confrontation .reset', event => {
html.find('.confrontation .die.d10')
.removeClass('max')
.removeClass('min');
});
// résolution de la confrontation
// Note: With d10 system, we count successes (dice >= difficulty) instead of summing
html.on("click", '.confrontation .resolve', async event => {
let achievementDice = 0;
let conservationDice = 0;
let achievementBasis = 0;
let conservationBasis = 0;
const difficulty = 7; // Default difficulty for legacy compatibility
// Count successes (dice >= difficulty) for achievement
html.find('.confrontation .die.d10.max').each(function (index) {
const value = parseInt($(this).html(), 10);
if (value >= difficulty) {
achievementDice++;
}
});
// Count successes (dice >= difficulty) for conservation
html.find('.confrontation .die.d10.min').each(function (index) {
const value = parseInt($(this).html(), 10);
if (value >= difficulty) {
conservationDice++;
}
});
// saisie des résultats
achievementBasis = html.find('td.achievement-result').data('achievement-basis');
html.find('td.achievement-result').data('achievement-value', achievementDice);
html.find('td.achievement-result').html(achievementBasis + achievementDice);
conservationBasis = html.find('td.conservation-result').data('conservation-basis');
html.find('td.conservation-result').data('conservation-value', conservationDice);
html.find('td.conservation-result').html(conservationBasis + conservationDice);
// calcul des marges (now based on successes, not sum)
const achievementMargin = achievementBasis + achievementDice - parseInt(html.find('td.adv-achievement-result').html(), 10);
const conservationMargin = conservationBasis + conservationDice - parseInt(html.find('td.adv-conservation-result').html(), 10);
html.find('td.achievement-margin').html(achievementMargin);
html.find('td.conservation-margin').html(conservationMargin);
});
// fin de la résolution de la confrontation
}
static async chatListeners(html) {
// supprime le masquage des résultats du dé
html.off("click", ".dice-roll");
}
/**
* main class function
* @returns
*/
static async ui(externalData = {}) {
let actor = {};
// get the actor
try {
actor = game.user.character;
} catch (e) {
throw ("Aucun personnage défini !");
}
if (actor == null && externalData.speakerId != undefined && externalData.speakerId != null) {
// on récupère le speakerId, et de là l'objet actor
actor = game.actors.get(externalData.speakerId);
VermineFight.previousValues['speakerName'] = actor.name;
VermineFight.previousValues['speakerImg'] = actor.img;
} else {
VermineFight.previousValues['speakerName'] = "Anonyme";
}
// get the data
let charData = (externalData) => {
let o = Object.assign({ _template: VermineFight.rollerTemplate }, { ...VermineFight.previousValues, ...externalData });
return o;
};
let data = charData(externalData);
console.log(data);
// render template
let html = await foundry.applications.handlebars.renderTemplate(data._template, data);
let ui = new Dialog({
title: game.i18n.localize("VERMINE.FightTool"),
content: html,
buttons: {
roll: {
label: game.i18n.localize('VERMINE.Roll4Fight'),
callback: (html) => {
let form = html.find('#dice-pool-form');
if (!form[0].checkValidity()) {
throw "Invalid Data";
}
let enemyAchievement, enemyConservation, skillKey, skill = 5, enemySkill, params = {};
form.serializeArray().forEach(e => {
switch (e.name) {
case "skill":
case "cephalic":
if (e.value !== '') {
skillKey = e.value;
}
break;
case "skill-score":
skill = +e.value;
break;
case "specialization":
params.specialization = true;
break;
case "usure":
params.usure = +e.value;
break;
case "trait":
params.trait = +e.value;
break;
case "purpose":
params.purpose = true;
break;
case "spleen":
params.spleen = true;
break;
case "adv-skill":
enemySkill = +e.value;
break;
case "achievement":
enemyAchievement = +e.value;
break;
case "conservation":
enemyConservation = +e.value;
break;
}
});
// prise en compte de l'usure sur la feuille de perso
if (params.usure != undefined) {
const newSpentScore = getActorSkillScore(actor, skillKey, 'spent') + params.usure;
console.log(newSpentScore);
updateActorSkillScore(actor, skillKey, 'spent', newSpentScore);
}
return VermineFight.get().performTest(enemyAchievement + enemySkill, enemyConservation + enemySkill, skillKey, skill, params, actor);
}
},
cancel: {
label: game.i18n.localize('Close'),
callback: () => { }
}
},
render: function (h) {
h.on("change", 'select[name="skill"]', event => {
const skillLabel = $(event.target).val();
const currentSkillScore = getActorSkillScore(actor, skillLabel) - getActorSkillScore(actor, skillLabel, 'spent');
if (parseInt(currentSkillScore, 10) >= 0) {
h.find('input#skillScore').val(currentSkillScore);
}
});
}
}, { width: 601, height: 'fit-content' });
ui.render(true);
return ui;
}
}
export class VermineCombat extends Combat {
_encounterCheck() {
console.log('encounter combat object', this);
// encounter check
}
async rollInitiative(ids, formula = undefined, messageOptions = {}) {
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, formula, messageOptions);
return super.rollInitiative(ids, formula, messageOptions)
async rollInitiative(ids, {formula=null, updateTurn=true, messageMode, messageOptions={}}={}) {
// roll initiative
return super.rollInitiative(ids, {formula, updateTurn, messageMode, messageOptions});
}
nextRound() {
@@ -417,31 +223,76 @@ export class VermineCombat extends Combat {
export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
get template() {
return "systems/vermine2047/templates/combat-tracker.hbs";
}
/* -------------------------------------------- */
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.find("[data-attitude]").click(this._setStatut.bind(this));
/** @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));
});
}
/**
@@ -465,23 +316,20 @@ export class VermineCombatTracker extends foundry.applications.sidebar.tabs.Comb
}
let actor = await game.actors.get(combatant.actorId)
console.log(actor, combatant)
// combatant attitude set
}
}
export class VermineCombatant extends Combatant {
constructor(data, context) {
super(data, context);
this.setDefaultAttitude();
}
setDefaultAttitude() {
this.attitude = this.token.flags.world?.attitude || "active"
super(data, context)
if (this.token) {
this.attitude = this.token.flags.world?.attitude || "active"
} else {
this.attitude = "active"
}
}
_getInitiativeFormula() {
return String(CONFIG.Combat.initiative.formula || game.system.initiative);
return String(CONFIG.Combat.initiative.formula || game.system.initiative)
}
}
+28 -30
View File
@@ -1,47 +1,45 @@
import { VERMINE } from './config.mjs'
/**
* renvoie le score d'une compétence d'un actor existant
* @param {VermineActor}
* @return {number||null} Data for rendering or null
* Returns the score of a skill for an actor.
* Skills are stored as a flat object in actor.system.skills: { [key]: { value, spent, label, category } }
* @param {VermineActor} actor
* @param {string} skillLabel - The localized label of the skill to find
* @param {"value"|"spent"} [property="value"] - Which property to return
* @returns {number|null} The skill score or null if not found
*/
export function getActorSkillScore(actor, skillLabel, property = "value") {
let returnedValue = null;
const skills = actor.system?.skills;
if (!skills) return null;
for(let i in actor.system.skills){
for(let j in actor.system.skills[i].data){
if (actor.system.skills[i].data[j].label == skillLabel){
returnedValue = actor.system.skills[i].data[j][property];
}
for (const key of Object.keys(skills)) {
if (skills[key].label === skillLabel) {
return skills[key][property] ?? null;
}
}
return returnedValue;
return null;
}
/**
* met à jour le score d'une compétence d'un actor existant
* @param {VermineActor}
* @return {boolean} bool
* Updates the score of a skill for an actor.
* @param {VermineActor} selectedActor
* @param {string} skillLabel - The localized label of the skill to update
* @param {"value"|"spent"} [property="value"] - Which property to update
* @param {number} updatedValue - The new value
* @returns {boolean} Whether the update was successful
*/
export function updateActorSkillScore(selectedActor, skillLabel, property = "value", updatedValue) {
try {
let updated = false;
// on recherche le label parmi les compétences
for (let st in selectedActor.system.skills){
for (let s in selectedActor.system.skills[st].data){
if (selectedActor.system.skills[st].data[s].label == skillLabel){
selectedActor.system.skills[st].data[s][property] = updatedValue; // printing the new value
const systemSkillKey = `system.skills.${st}.data.${s}.${property}`;
selectedActor.update({[systemSkillKey]:updatedValue }); // updating actor's data
updated = true;
}
const skills = selectedActor.system?.skills;
if (!skills) return false;
for (const key of Object.keys(skills)) {
if (skills[key].label === skillLabel) {
skills[key][property] = updatedValue;
selectedActor.update({ [`system.skills.${key}.${property}`]: updatedValue });
return true;
}
}
return updated;
} catch(e){
return false;
} catch (e) {
return false;
}
}
+17 -6
View File
@@ -124,7 +124,7 @@ export const registerHandlebarsHelpers = function () {
Handlebars.registerHelper('threatLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.ThreatLevels[level];
let levelData = CONFIG.VERMINE.npcThreatLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
@@ -136,7 +136,7 @@ export const registerHandlebarsHelpers = function () {
Handlebars.registerHelper('experienceLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.ExperienceLevels[level];
let levelData = CONFIG.VERMINE.npcExperienceLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
@@ -148,7 +148,7 @@ export const registerHandlebarsHelpers = function () {
Handlebars.registerHelper('roleLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.RoleLevels[level];
let levelData = CONFIG.VERMINE.npcRoleLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
@@ -160,7 +160,7 @@ export const registerHandlebarsHelpers = function () {
Handlebars.registerHelper('patternLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.PatternLevels[level];
let levelData = CONFIG.VERMINE.creaturePatternLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
@@ -172,7 +172,7 @@ export const registerHandlebarsHelpers = function () {
Handlebars.registerHelper('sizeLevel', function (property, level, options) {
if (level < 1 || level > 4)
return "";
let levelData = CONFIG.VERMINE.SizeLevels[level];
let levelData = CONFIG.VERMINE.creatureSizeLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
@@ -184,7 +184,7 @@ export const registerHandlebarsHelpers = function () {
Handlebars.registerHelper('packLevel', function (property, level, options) {
if (level < 0 || level > 3)
return "";
let levelData = CONFIG.VERMINE.PackLevels[level];
let levelData = CONFIG.VERMINE.creaturePackLevels[level];
if (property == 'label') {
return (levelData !== undefined) ? game.i18n.localize(levelData[property]) : "";
} else {
@@ -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 "";
});
}
+20 -14
View File
@@ -1,6 +1,5 @@
import RollDialog from "./dialogs/rollDialog.mjs";
import { initUserDice } from "./dice3d.mjs";
import { DiceSystem } from '../../../../modules/dice-so-nice/api.js';
import { VermineUtils } from "./roll.mjs";
import { registerTours } from "./tour.mjs";
@@ -10,6 +9,9 @@ export const registerHooks = function () {
*/
CONFIG.debug.hooks = false;
Hooks.once('diceSoNiceReady', async (dice3d) => {
const dsnModule = game.modules.get('dice-so-nice');
if (!dsnModule?.active) return;
const { DiceSystem } = await import(`/modules/${dsnModule.id}/api.js`);
const vermineSystem = new DiceSystem('Vermine2047', 'Vermine 2047', "preferred", 'totem')
dice3d.addSystem(vermineSystem);
@@ -43,7 +45,7 @@ export const registerHooks = function () {
if (rerollTitle) {
rerollTitle.addEventListener("click", () => { html.querySelector(".reroll").classList.toggle('visible') })
}
if (message.author?._id != game.user._id || !game.user.isGM) {
if (message.author?._id !== game.user._id && !game.user.isGM) {
// désactiver les inputs pour les joueurs non-auteurs du message
html.querySelectorAll("input").forEach(inp => inp.disabled = true);
//cacher le boutton reroll
@@ -61,17 +63,21 @@ export const registerHooks = function () {
}
})
Hooks.once("ready", async () => {
console.info("Vermine 2047 | System Initialized.");
// System initialized
//await registerTours();
});
// changement de la pause
Hooks.on("renderPause", async function () {
if ($("#pause").attr("class") !== "paused") return;
$(".paused img").attr("src", 'systems/vermine2047/assets/images/ui/vermine_pause.webp');
$(".paused img").css({ "opacity": 1 });
$("#pause.paused figcaption").text("Communauté endormie...");
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';
img.style.opacity = 1;
});
const caption = pauseEl.querySelector("figcaption");
if (caption) caption.textContent = "Communauté endormie...";
});
/**
@@ -88,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));
}
});
};
}
});
@@ -107,7 +113,7 @@ export const registerHooks = function () {
/* -------------------------------------------- */
Hooks.on("preCreateActor", function (actor) {
console.log('pre create actor', actor.img);
// pre create actor
if (actor.img == "icons/svg/mystery-man.svg") {
actor.updateSource({ "img": `systems/vermine2047/assets/icons/actors/${actor.type}.webp` });
}
@@ -138,7 +144,7 @@ export const registerHooks = function () {
if (game.user.isGM) {
let combatant = (game.combat.combatant) ? game.combat.combatant.actor : "";
console.log('update combat', game.combat);
// update combat
/*if (combatant.type == "marker" && combatant.system.settings.general.isCounter == true) {
let step = (!combatant.system.settings.general.counting) ? -1 : combatant.system.settings.general.counting;
+58 -128
View File
@@ -28,7 +28,8 @@ export class VermineUtils {
skillCategory = null,
keepTotem = null,
skillLevel = null,
hasSpecialty = false
hasSpecialty = false,
handicap = 0
}) {
// Validate inputs
if (!actor) {
@@ -48,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
@@ -77,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
@@ -100,10 +92,13 @@ export class VermineUtils {
NoD++; // Cancel the decrement for adapted
} else if (keepTotem === 'adapted' && totems.human) {
modFormula = `(1D10cs>=${adjustedDifficulty}[adapted_${safeUserName}]*2)`;
NoD++; // Cancel the decrement for human
NoD++; // Cancel the decrement for human
}
}
// Ensure the dice pool cannot go below zero
NoD = Math.max(0, NoD)
// Build base formula
const baseFormula = `${NoD}d10cs>=${adjustedDifficulty}[regular_${safeUserName}]`;
@@ -122,7 +117,6 @@ export class VermineUtils {
skillCategory: skillCategory,
skillLevel: skillLevel,
hasSpecialty: hasSpecialty,
autoSuccesses: autoSuccesses,
totemBonuses: { ...totemBonus },
baseNoD: NoD,
rerolls: Reroll,
@@ -148,7 +142,8 @@ export class VermineUtils {
skillCategory,
keepTotem,
skillLevel,
hasSpecialty
hasSpecialty,
handicap
});
return roll;
@@ -201,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
@@ -364,67 +302,61 @@ export class VermineUtils {
* @param {HTMLElement} html - The HTML element containing chat events.
*/
static async chatListenners(html) {
// Ensure html is a jQuery object
const $html = $(html);
// Get reroll count
const rerollCountElement = $html.find('#allowed_reroll')[0];
const rerollCountElement = html.querySelector('#allowed_reroll');
const rerollCount = rerollCountElement?.innerText;
// Enable/disable rerolls based on count
if (!rerollCount || parseInt(rerollCount, 10) < 1) {
// Disable rerolls for all dice
$html.find('.die').each(function() {
this.classList.remove("rerollable");
});
} else {
// Enable rerolls for all dice
$html.find('.die').each(function() {
this.classList.add("rerollable");
});
}
const dieClass = !rerollCount || parseInt(rerollCount, 10) < 1 ? 'remove' : 'add';
html.querySelectorAll('.die').forEach(el => el.classList[dieClass]("rerollable"));
// Add click event for rerollable dice
$html.find('.rerollable').click(async (ev) => {
ev.preventDefault();
const msgId = ev.currentTarget.closest("li.message")?.dataset?.messageId;
if (msgId) {
const message = await game.messages.get(msgId);
await VermineUtils.onReroll(message, ev);
}
html.querySelectorAll('.rerollable').forEach(el => {
el.addEventListener('click', async (ev) => {
ev.preventDefault();
const msgId = ev.currentTarget.closest("li.message")?.dataset?.messageId;
if (msgId) {
const message = await game.messages.get(msgId);
await VermineUtils.onReroll(message, ev);
}
});
});
// Update granted reroll label
$html.find("#effort-reroll").change(ev => {
const label = $html.find("#granted-reroll")[0];
if (label) {
label.innerText = ev.currentTarget.value;
}
});
const effortReroll = html.querySelector("#effort-reroll");
if (effortReroll) {
effortReroll.addEventListener('change', ev => {
const label = html.querySelector("#granted-reroll");
if (label) {
label.innerText = ev.currentTarget.value;
}
});
}
// Add click event for granting rerolls
$html.find("button.grant-reroll").click(async (ev) => {
const grantedRerollElement = $html.find('#granted-reroll')[0];
const allowedRerollElement = $html.find("#allowed_reroll")[0];
if (grantedRerollElement && allowedRerollElement) {
allowedRerollElement.innerText = grantedRerollElement.innerText;
}
const mesEl = ev.currentTarget.closest('[data-message-id]');
const messageId = mesEl?.dataset?.messageId;
if (messageId) {
// Hide reroll grant area
ev.currentTarget.closest('.reroll-from-effort').style.display = "none";
const rollMessage = ev.currentTarget.closest(".vermine-roll-message");
if (rollMessage) {
const content = rollMessage.outerHTML;
const message = await game.messages.get(messageId);
await message.update({ content: content });
html.querySelectorAll("button.grant-reroll").forEach(el => {
el.addEventListener('click', async (ev) => {
const grantedRerollElement = html.querySelector('#granted-reroll');
const allowedRerollElement = html.querySelector("#allowed_reroll");
if (grantedRerollElement && allowedRerollElement) {
allowedRerollElement.innerText = grantedRerollElement.innerText;
}
}
const mesEl = ev.currentTarget.closest('[data-message-id]');
const messageId = mesEl?.dataset?.messageId;
if (messageId) {
ev.currentTarget.closest('.reroll-from-effort').style.display = "none";
const rollMessage = ev.currentTarget.closest(".vermine-roll-message");
if (rollMessage) {
const content = rollMessage.outerHTML;
const message = await game.messages.get(messageId);
await message.update({ content: content });
}
}
});
});
}
@@ -448,10 +380,10 @@ export class VermineUtils {
blind = true;
// Falls through
case "gmroll": // GM + rolling player
whisper = this.getUsers(user => user.isGM);
whisper = game.users?.filter(user => user.isGM) || [];
break;
case "roll": // Everybody
whisper = this.getUsers(user => user.active);
whisper = game.users?.filter(user => user.active) || [];
break;
case "selfroll":
whisper = [game.user.id];
@@ -473,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;
}
+1 -1
View File
@@ -10,7 +10,7 @@ export const registerSettings = function () {
"2": "Cauchemar",
"3": "Apocalypse"
},
default: 'e',
default: '1',
onChange: value => {
let el = document.querySelector('.game-mode');
el.id = 'game-mode-' + game.settings.get('vermine2047', 'game-mode')
+6 -4
View File
@@ -139,13 +139,15 @@ class WelcomeTour extends VermineTour {
}
export async function registerTours() {
game.tours.register("vermine2047", "welcome", new WelcomeTour());
$(document).on("click", "#chat-log #vermine-tour-chat-button", (el) => {
const tour = game.tours.get("vermine2047.welcome");
tour == null ? void 0 : tour.start();
document.addEventListener("click", (el) => {
if (el.target.closest("#chat-log #vermine-tour-chat-button")) {
const tour = game.tours.get("vermine2047.welcome");
tour?.start();
}
});
if (game.settings.get("vermine2047", "first-run-tips-shown"))
return;
console.log("Posting first-start messages...");
// Posting first-start messages
const gms = ChatMessage.getWhisperRecipients("GM");
ChatMessage.implementation;
ChatMessage.create({
+1 -1
View File
@@ -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)
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-21
View File
@@ -1,21 +0,0 @@
.app.vermine2047.trait-selector {
.form-group {
box-shadow: 0 0 30px gray;
padding: 0.3rem 0.5rem;
border: 3px solid rgb(142, 144, 16);
&:has(input[type="checkbox"]:checked) {
border: 3px solid green
}
label {
display: inline-flex;
align-items: center;
justify-content: space-around;
width: 100%;
text-align: center;
border-bottom: 2px solid black
}
}
}
-61
View File
@@ -1,61 +0,0 @@
// Flexbox.
.flex-group-center,
.flex-group-left,
.flex-group-right {
justify-content: center;
align-items: center;
text-align: center;
}
.flex-group-left {
justify-content: flex-start;
text-align: left;
}
.flex-group-right {
justify-content: flex-end;
text-align: right;
}
.flexshrink {
flex: 0;
}
.flex-center {
align-items: center;
justify-content: center;
}
.flex-between {
justify-content: space-between;
}
.flex-around {
justify-content: space-around;
}
.flexlarge {
flex: 2;
}
.flexsmall {
flex: 0.5
}
// Alignment styles.
.align-left {
justify-content: flex-start;
text-align: left;
}
.align-right {
justify-content: flex-end;
text-align: right;
}
.align-center {
align-items: center;
justify-content: center;
text-align: center;
}
-772
View File
@@ -1,772 +0,0 @@
@font-face {
font-family: "DistressBlack";
src: url("../assets/fonts/dcc_sharp_distress_black_by_dccanim.otf");
}
.sans-font {
font-family: "DistressBlack", sans-serif;
}
/* Change shadow colors and Foundry general font color!! */
.app {
box-shadow: 0 0 20px #7e7544;
/* Change the color code here for a nice shadow color! */
color: #dfdfdf;
/* Text color for window titles and menu all across Foundry */
}
.vermine2047.sheet.actor {
header.sheet-header,
.windoow-content {
min-height: fit-content
}
}
/* Character and Item Name Titles Text style! */
.sheet .charname input {
color: #191813;
font-family: "DistressBlack", sans-serif;
font-size: 30px;
font-style: normal;
}
img.profile-img {
filter: drop-shadow(0px 0px 20px rgb(110, 133, 27));
height: auto;
width: 100%;
max-width: 10rem;
}
/* custom styles */
body.system-vermine2047 img#logo {
content: url("/systems/vermine2047/assets/images/ui/logo_vermine_foundry.webp");
height: auto;
}
/* Customize the chat history area! */
#chat-log .message {
background: url(/systems/vermine2047/assets/images/ui/box_background.webp) repeat;
/*color: #191813; */
}
img {
border: none;
}
ul.unstyled {
list-style-type: none;
padding: 0;
margin: 0;
}
ul.unstyled li {
padding: 0;
margin: 0;
}
.padding-with-frieze {
margin-left: 18% !important;
margin-right: 10% !important;
}
.padding-with-frieze li {
max-width: 100%;
}
.w-full {
width: 100%;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
}
/* ----------------------------------------- */
/* Sheet Structure */
/* ----------------------------------------- */
.system-vermine2047 .sheet .window-content {
background: url(/systems/vermine2047/assets/images/ui/box_background.webp);
padding: 0;
overflow-y: hidden;
}
.system-vermine2047 .dialog .window-content {
background: url(/systems/vermine2047/assets/images/ui/fond_chat_box.webp);
padding: 0.5rem;
overflow-y: hidden;
}
.window-content .row.smb {
margin-bottom: 0.25rem;
}
.window-content .row.mdb {
margin-bottom: 0.5rem;
}
.window-content .row.lgb {
margin-bottom: 1rem;
}
.actor.sheet form .form {
display: flex;
align-items: flex-start;
height: 100%;
overflow: auto;
}
.actor.sheet .form aside {
min-width: max-content;
background-image: url(/systems/vermine2047/assets/images/ui/barre_laterale.webp);
background-repeat: no-repeat;
background-size: auto 200%;
border-right: 2px black;
height: 100%;
padding: 0 0.3rem;
box-shadow: -20px 0px 100px 15px #000000b5 inset;
.major-totem {
position: relative;
h4 {
position: absolute;
transform: rotate(-8deg);
opacity: 1;
transition: 0.2s;
}
}
}
.actor.sheet .form main {
box-shadow: 10px 0px 100px #000000b5 inset;
padding-left: 1rem;
grid-row: span 1 / span 1;
height: 100%;
}
.actor.sheet .form aside .image-wrapper {
text-align: center;
}
.actor.sheet .form aside .image-wrapper img {
width: 80%;
height: auto;
}
.actor.sheet .form aside .paper {
margin-top: 1rem;
height: 350px;
}
.actor.sheet .form aside .second-paper {
margin-top: 4rem;
height: 150px;
}
.actor.sheet .form h3 {
font-family: "DistressBlack", sans-serif;
text-align: center;
text-transform: uppercase;
color: #4e564c;
font-size: 1.7rem;
border-bottom: none;
margin: 0;
}
.actor.sheet .form h4,
.item.sheet .form h4 {
font-family: "DistressBlack", sans-serif;
font-size: 1.4em;
text-transform: uppercase;
margin: 0 0 0.2rem;
}
.actor.sheet .form .characteristics h4 {
font-size: 1.25rem;
margin-top: 0 0.1rem;
}
.actor.sheet .form .tab.totem h4,
.actor.sheet .form .tab.equipment h4,
.actor.sheet .form .tab.stories h4 {
margin-top: 0.875rem;
}
.system-vermine2047 .char-header {
font-family: "DistressBlack", sans-serif;
}
.system-vermine2047 .char-header section {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
}
.system-vermine2047 .char-header h1.char-name,
.system-vermine2047 .char-vermine2047 {
border-bottom: none;
line-height: 2rem;
}
.system-vermine2047 .char-vermine2047 {
font-size: 1.5rem;
}
.system-vermine2047 .sheet.actor form {
width: 100%;
height: 100%;
overflow: hidden;
}
.system-vermine2047 .sheet.actor form div.hexa {
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
background: radial-gradient(circle, rgba(255, 255, 255, 0.425) 0%, rgba(0, 0, 0, 0.288) 100%);
height: 1.5rem;
max-width: 1.5rem;
color: black;
transform: rotate(90deg);
transition: 0.2s;
margin: 0.2rem;
&:hover {
background: radial-gradient(circle, rgba(255, 255, 255, 0.425) 0%, rgba(0, 0, 0, 0.288) 100%);
}
input {
opacity: 1;
min-width: 100%;
min-height: 100%;
opacity: 0
}
&.checked {
&:hover {
background: radial-gradient(circle, rgb(43, 43, 43) 0%, rgba(0, 0, 0, 0.288) 100%);
}
background: radial-gradient(circle, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0.288) 100%);
}
&.unavailable {
background: radial-gradient(circle, rgba(66, 15, 15, 0.664) 0%, rgba(131, 70, 70, 0.432) 100%);
}
}
.system-vermine2047 .sheet.actor .totem-details {
position: relative;
img.img-totem {
transform-origin: 50% 50%;
filter: grayscale(1);
opacity: 0.15;
position: absolute;
width: 30%;
height: auto;
pointer-events: none;
aspect-ratio: 1/1;
left: 35%;
}
}
.system-vermine2047 .sheet.actor div.minor-totems {
position: relative;
background-color: #929c6f85;
h5 {
position: absolute;
top: 0;
img {
max-width: 2rem;
position: absolute;
bottom: -2rem;
}
}
h5.human,
h5.adapted {
transition: 0.3s;
img.img-totem {
filter: drop-shadow(0px 0px 20px rgb(0, 0, 0));
}
&.major {
transform: scale(1.1);
& img {
filter: drop-shadow(0px 0px 10px red)
}
}
}
.totem-dice {
.human-dice,
.adapted-dice {
display: flex;
flex-direction: row;
margin-left: 2rem;
i {
padding-top: 0.5rem;
color: #064930;
}
}
.adapted-dice {
justify-content: flex-end;
margin-left: 0;
margin-right: 2rem;
transform: rotate(180deg);
i {
transform: rotate(180deg);
padding-top: 0.5rem;
color: rgb(85, 52, 2);
}
}
}
.human {
left: 0;
img {
left: 0;
}
}
.adapted {
right: 0;
img {
right: 0
}
}
}
.system-vermine2047 .sheet.actor form input[type=text],
.system-vermine2047 .sheet.actor form input[type=number] {
width: calc(100% - 2px);
height: calc(100% - 2px);
background: none;
padding: 0;
margin: 1px 0;
color: #333;
border: 1px solid transparent;
&.hexa {
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
background: radial-gradient(circle, rgba(255, 255, 255, 0.425) 0%, rgba(0, 0, 0, 0.288) 100%);
height: 1.5rem;
max-width: 1.5rem;
color: black
}
}
.system-vermine2047 .sheet.actor form input[disabled],
.system-vermine2047 .sheet.actor form select[disabled] {
cursor: not-allowed;
}
.system-vermine2047 .sheet.actor form input[type=text]:hover:not(:disabled),
.system-vermine2047 .sheet.actor form input[type=text]:focus,
.system-vermine2047 .sheet.actor form select:hover:not(:disabled),
.system-vermine2047 .sheet.actor form select:focus,
.system-vermine2047 .sheet.actor form input[type=number]:hover:not(:disabled),
.system-vermine2047 .sheet.actor form input[type=number]:focus,
.system-vermine2047 .sheet.actor form textarea:hover:not(:disabled),
.system-vermine2047 .sheet.actor form textarea:focus {
box-shadow: 0 0 10px #005a3c inset;
}
.system-vermine2047 .sheet.actor form select {
font-size: 0.6rem;
border: none;
appearance: none;
min-width: fit-content;
max-width: fit-content;
padding: 0 0.2rem;
margin: 0 0.2rem;
cursor: help;
}
.system-vermine2047 .sheet.actor form label {
display: block;
}
.system-vermine2047 .sheet.actor form .mce-panel span {
display: inherit;
}
.system-vermine2047 .sheet.actor form.editable .rollable:hover,
.system-vermine2047 .sheet.actor form.editable a:hover {
color: #000;
text-shadow: 0 5px 5px #1fa832;
cursor: pointer;
}
.system-vermine2047 .sheet.actor form .sheet-tabs {
font-weight: 500;
height: 30px;
}
.system-vermine2047 .sheet.actor form .sheet-tabs>.list-row {
line-height: 24px;
padding-top: 3px;
font-size: 2rem;
text-align: center;
}
.system-vermine2047 .sheet.actor form .sheet-tabs>.list-row:last-of-type {
padding-right: 4px;
}
.system-vermine2047 .sheet.actor form .sheet-tabs>.list-row.active {
color: #000;
font-weight: 700;
}
.system-vermine2047 .sheet.actor form .tab {
flex: 1;
overflow: hidden;
}
.system-vermine2047 .sheet.actor form .tag-legacy {
float: left;
margin: 0 2px 2px 0;
padding: 0 3px;
font-size: var(--font-size-10);
line-height: 16px;
border: 1px solid #999;
border-radius: 3px;
white-space: normal;
font-weight: 500;
}
/* ---------------------------------------- */
/* Actor Sheet */
/* ---------------------------------------- */
.system-vermine2047 .sheet.actor,
.system-vermine2047 .sheet.actor .window-content {
min-width: 690px;
}
.system-vermine2047 .sheet.actor .sidebar {
flex: 0.2;
}
.system-vermine2047 .sheet.actor .floatright {
float: right;
}
.system-vermine2047 .sheet.actor .sheet-upper {
height: 268px;
}
.system-vermine2047 .sheet.actor .sheet-upper .sheet-header {
height: 48px;
}
.system-vermine2047 .sheet.actor .sheet-upper .sheet-profile,
.system-vermine2047 .sheet.actor .sheet-upper .sheet-showcase {
height: 220px;
}
.system-vermine2047 .sheet.actor .sheet-content {
padding: 4px;
}
.system-vermine2047 .sheet.actor .sheet-sidebar {
height: calc(100% - 48px);
display: flex;
flex-direction: column;
flex-wrap: nowrap;
overflow-x: hidden;
overflow-y: auto;
}
.system-vermine2047 .sheet.actor .sheet-sidebar>* {
flex: 1;
}
.system-vermine2047 .sheet.actor .sheet-sidebar .sidebar-summary {
overflow-y: hidden;
}
.system-vermine2047 .sheet.actor.npc-sheet .sheet-upper {
height: 220px;
}
.system-vermine2047 .sheet.actor.npc-sheet .sheet-upper .sheet-showcase {
height: 172px;
}
.system-vermine2047 .sheet.actor.npc-sheet .sheet-lower {
height: calc(100% - 220px - 32px);
}
.system-vermine2047 .sheet.actor .sheet-navigation {
border-top: 1px solid var(--secondary-background);
border-bottom: 1px solid var(--primary-background);
}
.system-vermine2047 .sheet.actor .sheet-navigation .sheet-tabs>.list-row {
border-radius: 5px 5px 0 0;
}
.system-vermine2047 .sheet.actor .sheet-navigation .sheet-tabs>.list-row.active {
border: 1px solid #666;
border-bottom: none;
/* box-shadow: 0 0 10px inset #ff6400;
*/
background: var(--primary-background);
color: #fff;
text-shadow: none;
color: #000;
text-shadow: 0 0 10px #00005a;
cursor: pointer;
}
.actor.sheet nav.sheet-navigation {
display: inline-flex;
justify-content: space-around;
align-items: center;
height: 54px;
background: url(../assets/images/ui/barre_haut.webp) no-repeat right top;
background-size: 100% 100%;
width: 100%;
position: relative;
padding-right: 4rem;
font-size: 1.4rem;
}
.actor.sheet nav.sheet-navigation.tabs .item {
height: 2.4rem;
display: inline-block;
z-index: 1;
transition: all 0.1s ease-out;
color: #606060;
box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.404);
}
.actor.sheet nav.sheet-navigation.tabs .item:hover,
.actor.sheet nav.sheet-navigation.tabs .item.active {
color: #000;
text-shadow: 0 5px 5px #1fa832;
cursor: pointer;
}
.actor.sheet nav.sheet-navigation.tabs .item:hover {
text-shadow: 0 5px 5px #1e52259a;
}
.system-vermine2047 .sheet.actor form nav.sheet-navigation.sheet-tabs {
height: 54px;
}
//Query pour les lignes de caract
@container ability-row (max-width: 240px) {
.skill-dots,
span.hexa {
display: none
}
}
.system-vermine2047 .sheet.actor .ability {
padding-right: 0.6rem;
font-size: 0.8rem;
border-bottom: 1px solid rgba(170, 170, 152, 0.664);
box-shadow: 0px 0px 15px rgba(128, 128, 128, 0) inset;
transition: 0.2s;
position: relative;
flex-wrap: nowrap;
min-width: min-content;
container-type: inline-size;
container-name: ability-row;
&:hover {
box-shadow: 0px 0px 15px gray inset;
}
label {
min-width: 40%;
flex: 1.3;
}
span {
max-width: fit-content;
margin: 0 1rem;
flex: 0.5;
}
div.specialties {
position: absolute;
bottom: -0.2rem;
font-size: 0.7rem;
}
.skill-dots {
height: 100%;
align-self: center;
flex: 1.5;
min-width: fit-content;
&>div {
max-width: 0.7rem;
height: 0.7rem;
aspect-ratio: 1/1;
border-radius: 50%;
font-weight: 700;
text-align: center;
padding-bottom: 0.2rem;
font-style: oblique;
align-self: flex-start;
&.dice-pool-dot {
background: radial-gradient(circle, rgba(94, 90, 77, 1) 25%, rgba(0, 0, 0, 1) 100%);
max-width: 0.7rem;
aspect-ratio: 1/1;
border-radius: 50%;
}
&.dice-reroll-dot {
background: radial-gradient(circle, rgb(187, 182, 165) 25%, rgba(0, 0, 0, 1) 100%);
}
}
}
}
.system-vermine2047 .sheet.actor .skill-category {
padding: .3rem;
&.preferred {
h4,
label {
text-shadow: 0px 0px 5px rgba(0, 128, 0, 0.411)
}
box-shadow: 0px 0px 30px rgba(0, 128, 0, 0.306) inset;
}
}
.system-vermine2047 .sheet.actor #edit {
background-color: black;
color: white;
width: 100%
}
.system-vermine2047 .sheet.actor .reserve-grid {
line-height: 0.5rem;
transform-origin: 0% 50%;
max-width: fit-content;
align-items: center;
display: flex;
flex-direction: column;
div.flexrow,
input,
.hexa {
margin: 0;
padding: 0;
min-width: 1rem;
min-height: 1rem;
}
&>.flexrow {
position: relative;
max-width: fit-content;
justify-content: center;
}
}
.hexa {
text-align: center;
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
background: radial-gradient(circle, rgba(255, 255, 255, 0.425) 0%, rgba(0, 0, 0, 0.288) 100%);
max-height: 1.5rem;
max-width: 1.5rem;
min-width: 1.5rem;
aspect-ratio: 1/1;
color: black;
vertical-align: center;
&.checked {
background: radial-gradient(circle, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0.288) 100%);
}
;
input {
width: 1rem
}
input[type="radio"] {
opacity: 0;
&::after,
&::before {
display: none
}
}
}
-62
View File
@@ -1,62 +0,0 @@
.window-app.vermineDialog {
max-width: 50vw;
height: fit-content;
.window-content {
background: url(/systems/vermine2047/assets/images/ui/box_background.webp) repeat;
color: black;
}
details>summary::after {
content: "▶️";
position: relative;
right: 40%;
}
details[open]>summary::after {
content: "🔽"
}
.grid {
justify-content: space-around;
box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.555);
align-items: center;
padding: 0.5rem 0.2rem;
&>* {
margin: 0 0.3rem;
}
}
;
label {
font-family: "DistressBlack",
sans-serif;
font-size: larger;
}
select {
max-width: fit-content;
option {
max-width: fit-content
}
}
.dialog-buttons {
button {
display: block;
flex: 0.3
}
display: flex;
justify-content: space-around;
flex-direction: row;
}
}
-76
View File
@@ -1,76 +0,0 @@
.sheet.item.vermine2047 {
.window-content {
padding: 0 1rem;
.flexrow {
align-items: center;
}
header,
h1,
h2,
h3,
h4,
h5 {
background: 50% 50%/cover no-repeat url(/systems/vermine2047/assets/images/ui/scotch.webp);
text-transform: uppercase;
font-family: "DistressBlack";
margin-top: 1rem;
border-bottom: none;
}
h2,
h3,
h4 {
text-align: center;
}
h5 {
margin-bottom: 0;
}
.resource {
border: none;
border-left: 1px solid gray;
padding: 0.2rem 1rem;
text-align: center;
.flexrow {
min-width: 5rem;
box-shadow: none;
}
}
.damages-row {
margin: 0;
.radios {
margin: 0;
padding: 0.5rem;
}
}
.damage-pannes,
.damage-state,
.damage-effect {
text-align: center;
font-family: "DistressBlack";
}
.traits {
box-shadow: 0px 5px 15px black;
h3 {
margin: 0
}
h4 {
margin: 0
}
}
}
}
-7
View File
@@ -1,7 +0,0 @@
ol#chat-log {
div.item-card {
header img {
max-width: 30%;
}
}
}
-189
View File
@@ -1,189 +0,0 @@
ol#chat-log {
header.message-header {
background-color: black;
padding: 0 1rem;
}
.vermine-roll-message {
overflow: hidden;
box-shadow: 0px 0px 30px white inset;
padding: 0;
position: relative;
.flexrow {
align-items: center;
box-shadow: 0px 5px 10px 0px black;
}
h3,
h4 {
text-transform: uppercase;
font-family: "DistressBlack";
margin-top: 1rem;
border-bottom: none;
font-weight: 900;
background: 50% 0%/cover no-repeat url(/systems/vermine2047/assets/images/ui/scotch.webp);
&+span {
font-family: "DistressBlack";
font-size: large;
text-transform: unset;
padding-left: 2rem;
background: -100% 0%/cover no-repeat url(/systems/vermine2047/assets/images/ui/scotch.webp);
&#allowed_reroll {
font-size: large;
}
}
}
h3 {
background: url(/systems/vermine2047/assets/images/ui/scotch.webp);
background-position: center;
background-size: 200%;
}
h4 {
text-align: center;
}
div.roll-total {
transform: rotate(-3deg) scale(1.2) translateX(2rem) translateY(0.5rem);
background: url(/systems/vermine2047/assets/images/ui/scotch.webp);
background-position: center;
background-size: 200%;
margin-bottom: 2rem;
padding: 0;
z-index: +1;
width: 75%;
}
div.reroll {
button {
text-transform: uppercase;
font-family: "DistressBlack";
padding: 0 1rem;
max-width: fit-content;
box-shadow: 0px 0px 2px black;
background: 50% 0%/cover no-repeat url(/systems/vermine2047/assets/images/ui/scotch.webp);
}
transition:0.3s;
max-height:1px;
overflow: hidden;
justify-content: end;
text-align: center;
align-items: center;
&.visible {
max-height: 15rem;
}
}
ul.roll-results {
list-style: none;
li.die {
position: relative;
max-width: 3rem;
line-height: 3rem;
float: left;
margin: 0.2rem;
background-image: url(/icons/dice/d10black.svg);
background-position: center;
background-repeat: no-repeat;
background-size: contain;
font-weight: 800;
font-size: 1rem;
color: #ffffff;
text-align: center;
transition: 0.3s;
border-bottom: 5px solid rgb(255, 0, 0);
border-radius: 2rem;
&::after {
content: "";
position: absolute;
top: -1rem;
text-wrap: nowrap;
color: white;
font-weight: 100;
font-size: smaller;
text-align: center;
opacity: 0;
text-shadow: 0px 0px 5px black;
}
&:hover::after {
opacity: 1;
color: white;
}
&.human,
&.adapted {
border-top: 5px solid rgb(255, 217, 0);
&::after {
content: "";
}
}
&.rerollable {
cursor: pointer;
&:hover {
transform: translateY(0.5rem)
}
}
&.success {
border-bottom: 5px solid rgb(0, 143, 7);
}
&.adapted {
&::after {
content: "adapté"
}
}
&.human {
&::after {
content: "humain"
}
}
&.rerolled {
transform: translateY(-0rem);
}
span {
text-align: center;
font-size: larger;
text-shadow: 0px 0px 8px black;
}
}
}
}
}
-22
View File
@@ -1,22 +0,0 @@
.app .actor.choose {
div.actor {
position: relative;
img {
border-radius: 50%;
box-shadow: 0px 0px 8px black;
}
span.actor-name {
position: absolute;
text-align: center;
background-color: rgba(255, 255, 255, 0.562);
border: 5px;
width: 100%;
padding: 0 1rem;
border-radius: 5px;
box-shadow: 0px 0px 8px black;
}
}
}
-148
View File
@@ -1,148 +0,0 @@
input[type="range"] {
appearance: none;
background: transparent;
cursor: pointer;
width: 100%;
}
input[type="range"]::-webkit-slider-runnable-track {
background: url(../assets/images/ui/scotch.webp) no-repeat center;
background-size: 100% auto;
height: 0.4rem;
border: none;
box-shadow: 0px 0px 13px rgba(31, 26, 26, 0.979) inset;
}
input[type="range"]::-webkit-slider-thumb {
appearance: none;
margin-top: -0.3rem;
/* Centers thumb on the track */
height: 1rem;
width: 1rem;
border: none;
border-radius: 50%;
background: url(/systems/vermine2047/assets/images/ui/totems/human.webp);
background-size: cover;
filter: contrast(2);
box-shadow: 0px 0px 10px black
}
input[type="range"]:focus::-webkit-slider-thumb {
box-shadow: 0px 0px 10px yellow
}
select {
border: none;
background: url(../assets/images/ui/scotch.webp);
background-size: 100% 100%;
box-shadow: 0px 0px 3px rgba(31, 26, 26, 0.979) inset;
&[disabled] {
color: rgb(0, 0, 0);
text-shadow: 0px 0px 15px black;
}
option {
appearance: none;
border: none;
background: url(../assets/images/ui/scotch.webp);
background-size: 100% 100%;
}
}
input[type="checkbox"],
input[type="radio"] {
-webkit-appearance: none;
appearance: none;
background: transparent;
box-shadow: 0px 0px 3px rgb(133, 133, 78);
cursor: pointer;
width: 1.5rem;
height: 1rem;
border-radius: 0.4rem;
transition: 0.3s;
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
box-shadow: 0px 0px 6px black inset;
background-color: rgba(61, 11, 11, 0.658);
&[disabled="true"] {
filter: grayscale(1)
}
&:after {
content: " ";
background: url(/systems/vermine2047/assets/images/ui/totems/human.webp);
background-size: 50% 150%;
background-repeat: no-repeat;
position: relative;
top: 10%;
left: 0%;
width: 100%;
height: 80%;
display: block;
border-radius: 0%;
padding: 0;
transition: 0.3s;
}
&:checked {
background-color: rgba(26, 107, 12, 0.658);
&:after {
font-weight: 900;
background-color: rgba(26, 1, 1, 0);
left: 50%;
}
}
}
iframe {
min-height: 500px;
.tabs.moods-headings {
max-width: 1px
}
}
input[type="radio"] {
width: 1rem;
height: 1rem;
&:after {
width: 0.8rem;
background-size: 100% 100%;
top: 5%;
left: 5%;
width: 90%;
height: 90%;
background-size: 30% 30%;
background-position: center;
}
&:not([disabled]):hover::after {
background-size: 90% 90%;
}
&:checked::after {
content: "";
background-size: 70% 70%;
top: 5%;
left: 5%;
position: relative;
background-color: rgba(26, 1, 1, 0);
}
}
-546
View File
@@ -1,546 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
/* Global styles */
.window-app {
font-family: "Roboto", sans-serif;
box-shadow: 0px 0px 30px rgb(69, 78, 44);
}
.rollable:hover,
.rollable:focus {
color: #000;
text-shadow: 0 0 10px red;
cursor: pointer;
}
.grid,
.grid-2col {
display: grid;
grid-column: span 2/span 2;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
margin: 5px 0;
padding: 0;
}
.grid-3col {
grid-column: span 3/span 3;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid-4col {
grid-column: span 4/span 4;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.grid-5col {
grid-column: span 5/span 5;
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.grid-6col {
grid-column: span 6/span 6;
grid-template-columns: repeat(6, minmax(0, 1fr));
}
.grid-7col {
grid-column: span 7/span 7;
grid-template-columns: repeat(7, minmax(0, 1fr));
}
.grid-8col {
grid-column: span 8/span 8;
grid-template-columns: repeat(8, minmax(0, 1fr));
}
.grid-9col {
grid-column: span 9/span 9;
grid-template-columns: repeat(9, minmax(0, 1fr));
}
.grid-10col {
grid-column: span 10/span 10;
grid-template-columns: repeat(10, minmax(0, 1fr));
}
.grid-11col {
grid-column: span 11/span 11;
grid-template-columns: repeat(11, minmax(0, 1fr));
}
.grid-12col {
grid-column: span 12/span 12;
grid-template-columns: repeat(12, minmax(0, 1fr));
}
.grid-start-2 {
grid-column-start: 2;
}
.grid-start-3 {
grid-column-start: 3;
}
.grid-start-4 {
grid-column-start: 4;
}
.grid-start-5 {
grid-column-start: 5;
}
.grid-start-6 {
grid-column-start: 6;
}
.grid-start-7 {
grid-column-start: 7;
}
.grid-start-8 {
grid-column-start: 8;
}
.grid-start-9 {
grid-column-start: 9;
}
.grid-start-10 {
grid-column-start: 10;
}
.grid-start-11 {
grid-column-start: 11;
}
.grid-start-12 {
grid-column-start: 12;
}
.grid-span-2 {
grid-column-end: span 2;
}
.grid-span-3 {
grid-column-end: span 3;
}
.grid-span-4 {
grid-column-end: span 4;
}
.grid-span-5 {
grid-column-end: span 5;
}
.grid-span-6 {
grid-column-end: span 6;
}
.grid-span-7 {
grid-column-end: span 7;
}
.grid-span-8 {
grid-column-end: span 8;
}
.grid-span-9 {
grid-column-end: span 9;
}
.grid-span-10 {
grid-column-end: span 10;
}
.grid-span-11 {
grid-column-end: span 11;
}
.grid-span-12 {
grid-column-end: span 12;
}
.flex-group-center,
.flex-group-left,
.flex-group-right {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-align: center;
}
.flex-group-left {
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
text-align: left;
}
.flex-group-right {
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
text-align: right;
}
.flex-align-left {
align-items: flex-start;
}
.flex-align-right {
align-items: flex-end;
}
.gap-xs {
gap: 2px;
}
.gap-sm {
gap: 4px;
}
.gap-md {
gap: 8px;
}
.gap-lg {
gap: 16px;
}
.flexshrink {
-webkit-box-flex: 0;
-ms-flex: 0;
flex: 0;
}
.flex-between {
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
}
.flexlarge {
-webkit-box-flex: 2;
-ms-flex: 2;
flex: 2;
}
.align-left {
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
text-align: left;
}
.align-right {
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
text-align: right;
}
.align-center {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
text-align: center;
}
.system-vermine2047 .item-form {
font-family: "Roboto", sans-serif;
}
.system-vermine2047 .sheet-header {
-webkit-box-flex: 0;
-ms-flex: 0 auto;
flex: 0 auto;
overflow: hidden;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
margin-bottom: 10px;
}
.system-vermine2047 .sheet-header .profile-img {
-webkit-box-flex: 0;
-ms-flex: 0 0 100px;
flex: 0 0 100px;
height: 100px;
margin-right: 10px;
}
.system-vermine2047 .sheet-header .header-fields {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
}
.system-vermine2047 .sheet-header h1.charname {
height: 50px;
padding: 0px;
margin: 5px 0;
border-bottom: 0;
}
.system-vermine2047 .sheet-header h1.charname input {
width: 100%;
height: 100%;
margin: 0;
}
.system-vermine2047 .sheet-tabs {
-webkit-box-flex: 0;
-ms-flex: 0;
flex: 0;
}
.system-vermine2047 .sheet-body .tab,
.editor {
height: 100%;
width: 100%;
}
.editor {
min-height: 75px;
margin-bottom: 1rem;
min-width: 100%;
.editor-content {
min-width: 100%;
min-height: 3rem;
}
}
editor:hover .editor-edit {
display: block;
}
.system-vermine2047 .tox {
min-height: 25vh;
}
.system-vermine2047 .tox .tox-editor-container {
background: #fff;
}
.system-vermine2047 .tox .tox-edit-area {
padding: 0 8px;
}
.system-vermine2047 .resource-label {
font-weight: bold;
}
.system-vermine2047 .items-header {
height: 28px;
margin: 2px 0;
padding: 0;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: rgba(0, 0, 0, 0.05);
border: 2px groove #eeede0;
font-weight: bold;
}
.system-vermine2047 .items-header>* {
font-size: 14px;
text-align: center;
}
.system-vermine2047 .items-header .item-name {
font-weight: bold;
padding-left: 5px;
text-align: left;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.system-vermine2047 .items-list {
list-style: none;
margin: 0;
padding: 0;
overflow-y: auto;
scrollbar-width: thin;
color: #444;
}
.system-vermine2047 .items-list .item-list {
list-style: none;
margin: 0;
padding: 0;
}
.system-vermine2047 .items-list .item-name {
-webkit-box-flex: 2;
-ms-flex: 2;
flex: 2;
margin: 0;
overflow: hidden;
font-size: 13px;
text-align: left;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.system-vermine2047 .items-list .item-name h3,
.system-vermine2047 .items-list .item-name h4 {
margin: 0;
white-space: nowrap;
overflow-x: hidden;
}
.system-vermine2047 .items-list .item-controls {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-flex: 0;
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
}
.system-vermine2047 .items-list .item-controls a {
font-size: 12px;
text-align: center;
margin: 0 6px;
}
.system-vermine2047 .items-list .item {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
padding: 0 2px;
border-bottom: 1px solid #c9c7b8;
}
.system-vermine2047 .items-list .item:last-child {
border-bottom: none;
}
.system-vermine2047 .items-list .item .item-name {
color: #191813;
}
.system-vermine2047 .items-list .item .item-name .item-image {
-webkit-box-flex: 0;
-ms-flex: 0 0 30px;
flex: 0 0 30px;
height: 30px;
background-size: 30px;
border: none;
margin-right: 5px;
}
.system-vermine2047 .items-list .item-prop {
text-align: center;
border-left: 1px solid #c9c7b8;
border-right: 1px solid #c9c7b8;
font-size: 12px;
}
.system-vermine2047 .items-list .items-header {
height: 28px;
margin: 2px 0;
padding: 0;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: rgba(0, 0, 0, 0.05);
border: 2px groove #eeede0;
font-weight: bold;
}
.system-vermine2047 .items-list .items-header>* {
font-size: 12px;
text-align: center;
}
.system-vermine2047 .items-list .items-header .item-name {
padding-left: 5px;
text-align: left;
}
.system-vermine2047 .item-formula {
-webkit-box-flex: 0;
-ms-flex: 0 0 200px;
flex: 0 0 200px;
padding: 0 8px;
}
.system-vermine2047 .effects .item .effect-source,
.system-vermine2047 .effects .item .effect-duration,
.system-vermine2047 .effects .item .effect-controls {
text-align: center;
border-left: 1px solid #c9c7b8;
border-right: 1px solid #c9c7b8;
font-size: 12px;
}
.system-vermine2047 .effects .item .effect-controls {
border: none;
}
.chat-message .message-header {
line-height: 20px;
color: white;
text-shadow: 0px 0px 5px black;
background: #1918135e
}
span.game-mode {
font-family: "DistressBlack",
sans-serif;
position: absolute;
margin-left: auto;
color: transparent;
top: 1rem;
z-index: 900;
width: 55%;
text-align: center;
text-transform: uppercase;
font-weight: 900;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.767) 0%, rgba(0, 0, 0, 0.61) 17%, rgba(0, 0, 0, 0.548) 19%, rgba(222, 255, 221, 0.575) 24%, rgba(255, 255, 255, 0.637) 43%, rgba(0, 0, 0, 0.486) 47%, rgba(254, 255, 254, 0.466) 50%, rgba(0, 0, 0, 0.699) 63%, rgba(134, 160, 137, 0.479) 64%, rgba(213, 248, 210, 0.493) 100%);
background-clip: text;
&#game-mode-1 {
color: rgba(235, 218, 143, 0.8);
}
&#game-mode-2 {
color: rgb(131, 248, 131);
}
&#game-mode-3 {
color: rgba(245, 124, 124, 0.8);
}
}
.shadow {
box-shadow: 0 0.5rem 2rem black;
margin: 2rem 0
}
-117
View File
@@ -1,117 +0,0 @@
@import "./base_work.scss";
@import "./style.scss";
@import "./roll.scss";
@import "./itemCards.scss";
@import "item-sheet.scss";
@import "dialog.scss";
@import "special-inputs.scss";
@import "special-applications.scss";
@import "_flex.scss";
@import "_app.scss";
// overwrites variables
:root {
--color-text-light-highlight: #96d696;
--color-text-light-heading: #9fd8a8;
--color-text-light-primary: #a4b5b3;
--color-text-dark-primary: #131919;
--color-text-dark-secondary: #444b4a;
--color-text-dark-header: #1d2223;
--color-text-dark-inactive: #71797a;
--color-text-hyperlink: #5aaf0a;
--color-text-light-0: #fff;
--color-text-light-1: #e0f0f0;
--color-text-light-2: #c9e0c0;
--color-text-light-3: #90c4a4;
--color-text-light-4: #80c08b;
--color-text-light-5: #60b06b;
--color-text-light-6: #40a05d;
--color-text-light-7: #208028;
--color-text-dark-1: #111;
--color-text-dark-2: #222;
--color-text-dark-3: #444;
--color-text-dark-4: #555;
--color-text-dark-5: #666;
--color-text-dark-6: #777;
--color-border-light-1: #b0d9b0;
--color-border-light-2: #80c0c0;
--color-border-dark-1: #131919;
--color-border-dark-2: #1d2223;
--color-border-dark-3: #2d3333;
--color-border-dark-4: #3d4444;
--color-border-dark-5: #668888;
--color-shadow-primary: #7bb60d;
--color-shadow-highlight: #85cc01d0;
--color-shadow-dark: #000;
--color-underline-inactive: #71797a;
--color-underline-active: #1a1944;
--color-underline-header: #228247;
--color-border-light-highlight: #b0d9b0;
--color-border-light-primary: #a4b5b3;
--color-border-light-secondary: #9fc7d8;
--color-border-light-tertiary: #71797a;
--color-border-dark: #000;
--color-border-dark-primary: #131919;
--color-border-dark-secondary: #1d2223;
--color-border-dark-tertiary: #444b4a;
--color-border-highlight: #85c019;
--color-border-highlight-alt: #70c008;
--color-bg-btn-minor-inactive: #9fc7d8;
--color-bg-btn-minor-active: #a4b5b3;
--color-bg-option: #ccdada;
--color-checkbox-checked: #666;
--color-ownership-none: #00ff55;
--color-ownership-observer: #71797a;
--color-ownership-owner: #a4b5b3;
--z-index-canvas: 0;
--z-index-app: 30;
--z-index-ui: 60;
--z-index-window: 100;
--z-index-tooltip: 9999;
--sidebar-width: 300px;
--sidebar-header-height: 32px;
--sidebar-item-height: 48px;
--hotbar-height: 52px;
--hotbar-width: 578px;
--macro-size: 50px;
--players-width: 200px;
--form-field-height: 26px;
--font-primary: "Signika", sans-serif;
--font-mono: monospace;
--font-awesome: "Font Awesome 6 Pro";
--font-size-11: 0.6875rem;
--font-size-12: 0.75rem;
--font-size-13: 0.8125rem;
--font-size-14: 0.875rem;
--font-size-16: 1rem;
--font-size-18: 1.125rem;
--font-size-20: 1.25rem;
--font-size-24: 1.5rem;
--font-size-28: 1.75rem;
--font-size-32: 2rem;
--font-size-48: 3rem;
--line-height-12: 0.75rem;
--line-height-16: 1rem;
--line-height-20: 1.25rem;
--line-height-30: 1.875rem;
--color-level-info: #b95c87;
--color-level-warning: #04b184;
--color-level-error: #03750;
--color-level-success: #3c266c;
}
/* ----------------------------------------- */
/* Scrollbar
/* ----------------------------------------- */
::-webkit-scrollbar-thumb {
outline: none;
border-radius: 3px;
background: #577822;
border: 1px solid var(--color-border-highlight);
}
::-webkit-scrollbar {
width: 3px;
height: 3px;
}
@@ -0,0 +1,37 @@
folder: WTyHT2h0fsSnUHFT
name: Acuité auditive
type: evolution
_id: vPA7gekUx8kUNOWB
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage est doté dune ouïe extraordinaire. Il perçoit des ondes
sonores très aigües, ce qui lui permet dentendre les ultrasons. Son acuité
est aussi surdéveloppée. Il peut ainsi entendre un son à une distance cinq
fois supérieure aux autres personnages. Enfin, sa sensibilité lui permet de
distinguer les sons entre eux, et ainsi dentendre distinctement un son qui
serait couvert par le vent, par exemple. Son ouïe extraordinaire lui offre
un Bonus de 3D à tous les jets de Perception basés sur laudition.</p>
level:
value: 3
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198965
modifiedTime: 1785775198965
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!vPA7gekUx8kUNOWB'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Adrénaline
type: evolution
_id: DItkLLGSyjAGzruO
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le corps du personnage sest adapté au stress du combat. Quand le
personnage subit des Blessures lors dun combat, il ne subit pas la perte de
Dés de Réserve associée. En revanche, à la fin du combat, il perd 2D
dEffort et 1D de Sang-Froid pour chaque Blessure subie.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198966
modifiedTime: 1785775198966
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!DItkLLGSyjAGzruO'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Analgésie
type: evolution
_id: Cs6ibCrcPxim7QHR
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Pour le personnage, la douleur nest quune information dont il ne subit
pas les conséquences néfastes. Lorsquil subit une Blessure, il ne perd
aucun Dé de Sang- Froid. En outre, le Malus quil subit à la suite de
Blessures est diminué de 1D.</p>
level:
value: 3
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198966
modifiedTime: 1785775198966
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!Cs6ibCrcPxim7QHR'
@@ -0,0 +1,35 @@
folder: WTyHT2h0fsSnUHFT
name: Anticipation
type: evolution
_id: pP9hfvk5JKsoFcnt
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>En décryptant le langage corporel ou peut-être en percevant les
phéromones de son adversaire, le personnage ressent ses intentions. Il
peut utiliser la caractéristique Empathie plutôt que Réflexes pour esquiver
les attaques dun adversaire humain. Sil réussit une telle esquive, il
regagne 1D dans sa Réserve de Sang-Froid.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198967
modifiedTime: 1785775198967
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!pP9hfvk5JKsoFcnt'
@@ -0,0 +1,33 @@
folder: WTyHT2h0fsSnUHFT
name: Au bout de leffort
type: evolution
_id: h3Q6LwnnVuj6MH4Y
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage peut dépasser ses limites. Il peut dépenser, sur un jet, un
nombre de Dés de Réserve égal à la Caractéristique utilisée +1 (contre un
seul normalement).</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198967
modifiedTime: 1785775198967
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!h3Q6LwnnVuj6MH4Y'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Aura doptimisme
type: evolution
_id: JrtHAUqFgA75e6pq
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage dégage une imperceptible aura bienfaisante autour de lui.
La présence réconfortante du personnage réduit de 2D les pertes de Dés de la
Réserve de Groupe à la suite dun événement traumatisant. Le personnage doit
être témoin de l’événement pour que sa Capacité fonctionne.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198967
modifiedTime: 1785775198967
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!JrtHAUqFgA75e6pq'
@@ -0,0 +1,37 @@
folder: WTyHT2h0fsSnUHFT
name: Camouflage
type: evolution
_id: t4p4n73kwwVoeSMe
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage ne dégage aucune odeur et émet des phéromones qui le
rendent insignifiant aux yeux des créatures qui lentourent. Il peut ajouter
son score en Volonté aux jets destinés à camoufler sa présence, son odeur,
ses traces ou un objet lui appartenant. Sil camoufle sa présence et quil
reste immobile, il bénéficie en outre de trois Réussites supplémentaires.
Dès quil se déplace, il doit retenter le jet sans pouvoir bénéficier des
Réussites supplémentaires.</p>
level:
value: 3
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198967
modifiedTime: 1785775198967
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!t4p4n73kwwVoeSMe'
@@ -0,0 +1,33 @@
folder: WTyHT2h0fsSnUHFT
name: Cicatrisation rapide
type: evolution
_id: 2h0xUozy1xqLdKcE
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>La Difficulté des Actions de soin sur le personnage est réduite de 2. Le
coût en Dés de Réserves pour guérir est diminué de 1D pour chaque type de
Blessure.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198968
modifiedTime: 1785775198968
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!2h0xUozy1xqLdKcE'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Défenses immunitaires
type: evolution
_id: oNfjRGGRVFGtfbK0
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>La Virulence dune infection affectant le personnage est réduite de 1
voir Les Règles, « Maladies et infections », page 105. Lorsque le personnage
reçoit des soins pour soigner une infection, le soigneur reçoit une Réussite
supplémentaire en cas de jet réussi.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198968
modifiedTime: 1785775198968
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!oNfjRGGRVFGtfbK0'
@@ -0,0 +1,30 @@
folder: WTyHT2h0fsSnUHFT
name: Dur au mal
type: evolution
_id: vgYLoiEqEOxrGdy6
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: <p>Le Seuil de Blessure Légère du personnage augmente de 1.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198968
modifiedTime: 1785775198968
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!vgYLoiEqEOxrGdy6'
@@ -0,0 +1,33 @@
folder: WTyHT2h0fsSnUHFT
name: Endurance
type: evolution
_id: uGG0FrBn1QtMcxbz
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage a toujours été particulièrement résistant. Toutes les
pertes de Réserve dEffort quil subit sont réduites de 1D, jusqu’à un
minimum de 1D.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198968
modifiedTime: 1785775198968
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!uGG0FrBn1QtMcxbz'
@@ -0,0 +1,37 @@
folder: WTyHT2h0fsSnUHFT
name: Exosquelette
type: evolution
_id: bV6V4i1v4ORWDJSQ
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage a développé un exosquelette fait de chitine. De nombreux
endroits de son corps et de son visage sont recouverts de plaques rigides
qui lui procurent une Protection de 3/5 (L) sans lui infliger de Handicap de
Mobilité voir Les Règles, « Handicap de Mobilité », page 70. Il bénéficie
en outre dun Bonus de 2D aux Actions dont le but est dintimider ou
deffrayer. En revanche, il subit un Malus identique pour toutes les autres
Actions sociales.</p>
level:
value: 4
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198968
modifiedTime: 1785775198968
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!bV6V4i1v4ORWDJSQ'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Force mentale
type: evolution
_id: S8AwTKDxIdez7vHB
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage a des ressources mentales insoupçonnées. Il ne subit pas de
Malus d’Épuisement aux Actions mentales et sociales quand sa Réserve de
Sang-Froid est vide. Si ses deux Réserves sont vides, il subit néanmoins le
Malus normal de 2D à toutes ses Actions.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198969
modifiedTime: 1785775198969
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!S8AwTKDxIdez7vHB'
@@ -0,0 +1,32 @@
folder: WTyHT2h0fsSnUHFT
name: Frappe lourde
type: evolution
_id: RjdZB4Qd2V8mp44x
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Lorsque le personnage attaque à mains nues, ses dommages sont égaux à
Vigueur (C).</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198969
modifiedTime: 1785775198969
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!RjdZB4Qd2V8mp44x'
@@ -0,0 +1,38 @@
folder: WTyHT2h0fsSnUHFT
name: Griffes
type: evolution
_id: kPoioCvlK7H0uJB1
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Les ongles du personnage peuvent pousser en un instant pour devenir des
armes dangereuses. Ces griffes infligent des Dommages de 2 (L) et
bénéficient du Trait Rapide (2). Quand le personnage réussit une attaque et
passe la protection de sa cible, il peut dépenser jusqu’à 3D de sa Réserve
dEffort pour infliger 1 point de Dommages supplémentaire par dé dépensé.
Ces griffes se brisent à la fin du combat et ne peuvent repousser que
lorsque le personnage aura intégralement reconstitué sa Réserve
dEffort.</p>
level:
value: 3
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198969
modifiedTime: 1785775198969
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!kPoioCvlK7H0uJB1'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Habitué à la faim
type: evolution
_id: EqeONvUCK7OhJaNN
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le premier jour de rationnement ou de privation totale de nourriture na
pas deffet sur le personnage. Ainsi, il ne subira aucune perte de Dés de
Réserves avant le deuxième jour de privation totale ou le troisième jour de
rationnement.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198969
modifiedTime: 1785775198969
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!EqeONvUCK7OhJaNN'
@@ -0,0 +1,40 @@
folder: WTyHT2h0fsSnUHFT
name: Hyperosmie
type: evolution
_id: 8ShwJQCWKHsfXLko
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage est doté dun odorat extrêmement développé, proche de celui
dun chien. Cette acuité lui permet de distinguer des odeurs imperceptibles
pour ses semblables. Il peut ainsi reconnaître une personne ou un animal à
son odeur et savoir si cet individu est passé dans un endroit jusqu’à une
semaine après son passage. Cela lui offre un talent incroyable pour suivre
une piste et traquer une proie : il bénéficie dun Bonus de 3D aux jets de
Perception quand lodorat entre en jeu. Cette Mutation saccompagne dune
mémoire olfactive très développée. Une fois quil a senti une odeur, le
personnage ne loublie jamais et peut la distinguer et la reconnaître sans
faillir.</p>
level:
value: 3
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198969
modifiedTime: 1785775198969
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!8ShwJQCWKHsfXLko'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Immunité absolue
type: evolution
_id: ivx8gS0XBEcoqtdl
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage est immunisé à toutes les Infections et toutes les Toxines.
Il ne peut par ailleurs pas développer daddiction. En contrepartie, les
médicaments et autres stimulants sont totalement inefficaces sur lui et
noctroient aucun bénéfice que ce soit.</p>
level:
value: 4
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198970
modifiedTime: 1785775198970
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!ivx8gS0XBEcoqtdl'
@@ -0,0 +1,36 @@
folder: WTyHT2h0fsSnUHFT
name: Indestructible
type: evolution
_id: HfC2oOARrpKnpoQf
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le corps du personnage a gagné en densité, mais son poids augmente de 25
% et ses besoins nutritionnels explosent. Il gagne un cercle dans chaque
type de Blessure, subit (I) Handicap permanent de Mobilité, et a besoin de
deux rations de nourriture plutôt quune. En cas de rationnement de
nourriture, il subit des pertes de Réserves équivalentes à une absence
totale de nourriture.</p>
level:
value: 4
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198970
modifiedTime: 1785775198970
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!HfC2oOARrpKnpoQf'
@@ -0,0 +1,35 @@
folder: WTyHT2h0fsSnUHFT
name: Insensible
type: evolution
_id: Re0aeZzupnz3EL6E
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le corps du personnage sécrète de grandes quantités dhormones en cas de
stress intense, ce qui lui permet dy résister plus facilement. LIntensité
des événements stressants est diminuée dun niveau pour le personnage. Il
na même pas à tenter de jet pour résister à un événement Mineur voir Les
Règles, « Événements », page 92.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198970
modifiedTime: 1785775198970
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!Re0aeZzupnz3EL6E'
@@ -0,0 +1,41 @@
folder: WTyHT2h0fsSnUHFT
name: Instinct
type: evolution
_id: UxNguZd8K62nt1GH
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Un jet dEmpathie + Vigilance contre une Difficulté de 9 permet au
personnage de pressentir un danger ou une menace imminente dorigine
naturelle ou animale. Chaque Réussite obtenue lui permet dobtenir une
information sur le danger pressenti. Si celui-ci tourne au combat, le
personnage sera considéré comme Actif pour son jet de Réaction. Sil a le
temps dinformer ses compagnons du danger, ces derniers seront Actifs
également.</p>
<p>En Modes <strong>Cauchemar </strong>et <strong>Apocalypse</strong>,
chaque Réussite au jet de Vigilance octroie au personnage 1D de Bonus au jet
de Réaction ou au jet permettant d’éviter le danger perçu.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198970
modifiedTime: 1785775198970
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!UxNguZd8K62nt1GH'
@@ -0,0 +1,38 @@
folder: WTyHT2h0fsSnUHFT
name: Musculature surdéveloppée
type: evolution
_id: AAw7U5tUxbUczorz
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage est exceptionnellement fort, ce que reflète sa silhouette
impressionnante. Il bénéficie dun Bonus de 3D aux jets de Vigueur quand une
force musculaire élevée peut laider (soulever ou porter un objet lourd,
enfoncer une porte, abattre un mur, etc.) Lorsquil porte une attaque de
corps à corps ou de mêlée, sa Vigueur est considérée comme augmentée de 3D
pour calculer les Dommages de base de ses attaques. En revanche, sa force
surhumaine naugmente pas sa précision, il ne gagne donc aucun Bonus à
lattaque.</p>
level:
value: 3
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198971
modifiedTime: 1785775198971
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!AAw7U5tUxbUczorz'
@@ -0,0 +1,32 @@
folder: WTyHT2h0fsSnUHFT
name: OEil de lynx
type: evolution
_id: guC1pOfmcmitRGDo
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage bénéficie dun Bonus de 1D sur les Actions impliquant
lacuité visuelle.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198971
modifiedTime: 1785775198971
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!guC1pOfmcmitRGDo'
@@ -0,0 +1,37 @@
folder: WTyHT2h0fsSnUHFT
name: Peau mimétique
type: evolution
_id: kFak0PMoNAuPnSfX
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>La peau du personnage peut prendre la teinte globale de son environnement
pour laider à se camoufler : elle deviendra plus sombre dans lobscurité,
plus verte dans la nature… Pour activer cette Mutation, le personnage doit
dépenser 1D de sa Réserve de Sang-Froid. Dès cet instant, il bénéficie de 4D
Bonus sur tous les jets de Perception visant à rester discret. Sil attaque
sous ce camouflage sans avoir été préalablement repéré, il est considéré
comme Offensif et tous ses adversaires comme Passifs.</p>
level:
value: 4
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198971
modifiedTime: 1785775198971
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!kFak0PMoNAuPnSfX'
@@ -0,0 +1,44 @@
folder: WTyHT2h0fsSnUHFT
name: Phéromones attractives
type: evolution
_id: 4jRJw5vuhZeqoKy9
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage est capable de dégager des phéromones qui attirent la
sympathie des créatures vivantes. Sans autre motivation, les humains et les
animaux sont instinctivement attirés par le personnage et ont un a priori
positif à son encontre. Le personnage peut en outre désigner un individu par
dé de Volonté et par scène. Il bénéficie dun Bonus de 3D à ses Actions
sociales envers ces cibles. Les phéromones peuvent même annihiler toute
intention hostile. Une personne ou un animal qui souhaite agresser le
personnage, verbalement ou physiquement, doit dabord réussir un Jet de
Volonté + Psychologie (ou de sa valeur dAttaque sil na pas de score de
Volonté) contre une Difficulté de 7. Chaque Réussite lui permet dagir
normalement envers le personnage pendant une minute (ou un tour pendant un
combat). Le meneur est laissé seul juge d’éventuelles réactions indésirables
que ces phéromones pourraient produire chez des animaux ou des créatures
rencontrées…</p>
level:
value: 4
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198971
modifiedTime: 1785775198971
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!4jRJw5vuhZeqoKy9'
@@ -0,0 +1,45 @@
folder: WTyHT2h0fsSnUHFT
name: Phéromones répulsives
type: evolution
_id: Y8UeTOYpGkM3nt9R
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage peut dégager des phéromones qui créent le dégoût et la
répulsion chez toutes les créatures vivantes. Lorsquil dégage ces
phéromones, tous les humains et les animaux dans un rayon dune dizaine de
mètres se sentent mal à laise en présence du personnage et, sauf autre
motivation, s’éloignent de lui sans réellement comprendre pourquoi. Le
personnage peut en outre désigner un individu ou une créature par point de
Volonté et par scène. Ces cibles ne peuvent ni lui adresser la parole, ni
sapprocher, ni attaquer le personnage sauf si elles réussissent un Jet de
Volonté + Psychologie (ou de leur valeur dAttaque si elles nont pas de
score de Volonté) contre une Difficulté de 7. Chaque Réussite leur permet
dagir normalement envers le personnage pendant une minute (ou un tour
pendant un combat) mais elles subissent un Malus de 1D à toutes leurs
Actions envers lui. Le meneur est laissé seul juge d’éventuelles réactions
indésirables que ces phéromones pourraient produire chez des animaux ou des
créatures rencontrées…</p>
level:
value: 4
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198972
modifiedTime: 1785775198972
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!Y8UeTOYpGkM3nt9R'
@@ -0,0 +1,32 @@
folder: WTyHT2h0fsSnUHFT
name: Réflexes éclairs
type: evolution
_id: BNZeEd0CpTgYkEou
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage réagit si rapidement au danger quil bénéficie dun Bonus
de 2D à son jet de Réaction.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198972
modifiedTime: 1785775198972
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!BNZeEd0CpTgYkEou'
@@ -0,0 +1,36 @@
folder: WTyHT2h0fsSnUHFT
name: Régénération
type: evolution
_id: wH5OPnv4cenzpgws
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Les plaies du personnage se referment très rapidement. Après une nuit de
repos complet, le personnage peut réduire le niveau dune Blessure ou
effacer une Blessure Légère sans dépenser de dés, il peut ensuite dépenser
des dés pour soigner plusieurs autres Blessures sil possède suffisamment de
Dés de Réserves. Le coût de guérison est réduit de 2D pour chaque type de
Blessure.</p>
level:
value: 3
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198972
modifiedTime: 1785775198972
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!wH5OPnv4cenzpgws'
@@ -0,0 +1,35 @@
folder: WTyHT2h0fsSnUHFT
name: Résistance à la chaleur
type: evolution
_id: rCQOrAzaCgCF0Dri
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage est particulièrement résistant aux températures élevées. Il
ne subit jamais de perte de Dés de Réserves ni de Malus à la récupération à
cause de fortes chaleurs. Il ne subit pas non plus les Dommages cumulatifs
en cas dexposition prolongée<em> voir Les Règles, « Brûlures »</em>, page
99.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198973
modifiedTime: 1785775198973
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!rCQOrAzaCgCF0Dri'
@@ -0,0 +1,35 @@
folder: WTyHT2h0fsSnUHFT
name: Résistance au froid
type: evolution
_id: DHv0PXsIgQ8rPPUY
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage est particulièrement résistant aux grands froids. Il ne
subit jamais de perte de Dés de Réserves ni de Malus à la récupération à
cause du froid extrême. Il ne subit pas non plus les Dommages cumulatifs en
cas dexposition prolongée<em> voir Les Règles, « Froid et chaleur »</em>,
page 99.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198972
modifiedTime: 1785775198972
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!DHv0PXsIgQ8rPPUY'
@@ -0,0 +1,36 @@
folder: WTyHT2h0fsSnUHFT
name: Résistance aux toxines
type: evolution
_id: IMXQRepTNtiHZcaf
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>La Virulence des poisons et toxines qui affectent le personnage est
réduite de 1 voir Les Règles, « Venins et poisons », page 107. Le
personnage peut en outre sélectionner une famille animale ou végétale
(guêpes, araignées, champignons, méduses, batraciens…) en accord avec le
meneur. Les toxines sécrétées par cette famille voient leur Virulence
réduite de 3.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198973
modifiedTime: 1785775198973
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!IMXQRepTNtiHZcaf'
@@ -0,0 +1,33 @@
folder: WTyHT2h0fsSnUHFT
name: Résistance
type: evolution
_id: nxy53EF4ZRSSwCvr
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage ne subit pas de Malus d’Épuisement à ses Actions physiques
et manuelles quand sa Réserve dEffort est vide. Si ses deux Réserves sont
vides, il subit néanmoins le Malus normal de 2D à toutes ses Actions.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198972
modifiedTime: 1785775198972
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!nxy53EF4ZRSSwCvr'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Santé de fer
type: evolution
_id: uneyg1G0Gpbb7l5P
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Lorsque le personnage est blessé, les Malus de récupération à la Réserve
dEffort ne se cumulent pas, seule la Blessure la plus grave est prise en
compte. Le coût en Dés de Réserves pour réduire une Blessure est en outre
réduit de 2D.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198973
modifiedTime: 1785775198973
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!uneyg1G0Gpbb7l5P'
@@ -0,0 +1,33 @@
folder: WTyHT2h0fsSnUHFT
name: Silencieux
type: evolution
_id: mxzf2Od6SMlvhKon
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage anticipe chaque mouvement qui pourrait le faire repérer. Il
bénéficie dun Bonus de 1D sur toutes ses Actions liées à la discrétion
lorsquil est seul.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198973
modifiedTime: 1785775198973
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!mxzf2Od6SMlvhKon'
@@ -0,0 +1,33 @@
folder: WTyHT2h0fsSnUHFT
name: Sommeil réparateur
type: evolution
_id: 71goz10chR7bPQZY
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage na besoin de dormir que quatre heures par nuit. Il
récupère toujours la totalité de ses Réserves sil a pu bénéficier de quatre
heures de repos ininterrompu, même dans des conditions difficiles.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198973
modifiedTime: 1785775198973
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!71goz10chR7bPQZY'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Surhumain
type: evolution
_id: haijrxkf8tAxmWx5
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le joueur sélectionne une de ses Caractéristiques. Il augmente son score
de 1D et peut atteindre 4D. Quelle que soit la Caractéristique choisie, le
personnage dégage une aura étrange, difficilement identifiable, mais à
laquelle personne nest indifférent.</p>
level:
value: 4
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198974
modifiedTime: 1785775198974
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!haijrxkf8tAxmWx5'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Sympathie
type: evolution
_id: qdtjHLeC7pKh57tP
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage dégage des phéromones qui rendent les personnes qui le
côtoient au quotidien plus compatissantes envers lui. Lorsquil souhaite
dépenser des Dés de la Réserve de Groupe, il a le droit de voter avec les
autres membres du Groupe.</p>
level:
value: 2
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198974
modifiedTime: 1785775198974
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!qdtjHLeC7pKh57tP'
@@ -0,0 +1,40 @@
folder: WTyHT2h0fsSnUHFT
name: Toucher empoisonné
type: evolution
_id: VIPHgGAqSklGFhFh
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage sécrète par la paume de ses mains un poison
particulièrement dangereux. Sil entre en contact avec la peau dun humain
ou dun animal (ce qui, en combat, nécessite une attaque au Corps à corps
qui passe la Protection de la cible), il peut dépenser un ou plusieurs Dés
de ses deux Réserves pour sécréter du poison. Ce dernier a une Virulence de
5 + le nombre de Dés dEfforts dépensés voir Les Règles, « Venins et
poisons », page 107. La victime subira alors une Blessure Légère
immédiatement, et une autre par minute pour chaque Dé de Sang-Froid dépensé.
Chaque Réussite au jet de Santé réalisé par la victime pour résister au
poison réduit la durée de lempoisonnement dune minute.</p>
level:
value: 4
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198974
modifiedTime: 1785775198974
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!VIPHgGAqSklGFhFh'
@@ -0,0 +1,34 @@
folder: WTyHT2h0fsSnUHFT
name: Vigilance
type: evolution
_id: ZcvIaqqfjvy67qWx
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage est habitué à ne dormir que dun oeil et à réagir au
moindre bruit ou mouvement suspect. Il nest jamais considéré comme Passif
et gagne une Réussite supplémentaire sur tous ses jets réussis impliquant la
Compétence Vigilance.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198974
modifiedTime: 1785775198974
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!ZcvIaqqfjvy67qWx'
@@ -0,0 +1,39 @@
folder: WTyHT2h0fsSnUHFT
name: Vision nocturne
type: evolution
_id: noIASNejaUkv0Qo3
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Les yeux du personnage se rapprochent de ceux dun félin. Une faible
lumière, comme celle des étoiles, lui suffit à voir très clairement. En
revanche, quand la luminosité est si faible, il ne perçoit que très peu les
nuances de couleur. Tant quune infime source de lumière est disponible, le
manque de luminosité ne lui inflige aucun Malus aux jets de Perception basés
sur la vue. Dans le noir complet, cependant, il est aussi aveugle que
quiconque. Lorsque ses yeux percent les ténèbres, ils reflètent la lumière,
ce qui peut le rendre visible de loin et lui donner une apparence
troublante.</p>
level:
value: 3
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198974
modifiedTime: 1785775198974
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!noIASNejaUkv0Qo3'
@@ -0,0 +1,32 @@
folder: WTyHT2h0fsSnUHFT
name: Volonté de fer
type: evolution
_id: flRHah4HO4JQ4DaM
img: systems/vermine2047/assets/icons/items/evolution.webp
system:
description: >-
<p>Le personnage bénéficie dun Bonus de 1D sur tous ses jets liés au moral,
au stress ou à la détermination.</p>
level:
value: 1
min: 1
max: 4
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198975
modifiedTime: 1785775198975
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!flRHah4HO4JQ4DaM'
@@ -0,0 +1,21 @@
name: Adaptation
sorting: a
folder: null
type: Item
_id: WTyHT2h0fsSnUHFT
description: ''
sort: 100000
color: null
flags: {}
_stats:
compendiumSource: Folder.WTyHT2h0fsSnUHFT
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775198946
modifiedTime: 1785775198993
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!folders!WTyHT2h0fsSnUHFT'
@@ -0,0 +1,39 @@
folder: 965pjqqbhRQqxXE4
name: Adaptabilité
type: ability
_id: zfeNmyLcOyfD4AxI
img: systems/vermine2047/assets/icons/items/ability.webp
system:
description: >-
<p>Lorsque le personnage utilise une Compétence quil ne possède pas, il
ignore son Handicap de Rareté, comme sil sagissait dune Compétence
basique.</p>
type: character
totem: loner
learn:
threshold: 5
hindrance: 0
level:
value: 2
min: 1
max: 3
effects: []
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775498760
modifiedTime: 1785775498760
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!zfeNmyLcOyfD4AxI'
@@ -0,0 +1,47 @@
folder: BJAxv25PiSk29dK0
name: Adaptation
type: ability
_id: CQeFYqMRYMFa6pAa
img: systems/vermine2047/assets/icons/items/ability.webp
system:
description: >-
<p>Le personnage peut se lier avec une espèce animale ou végétale. Il
devient immunisé aux poisons ou venins de cette espèce. Sil sagit
danimaux, il peut apaiser des individus de cette espèce comme sil
possédait la Capacité « Apaisement » voir page 99. Sil la acquise, un
jet réussi permet de les rendre amicaux et il peut être accompagné dune
nuée danimaux de Taille 0 ou dun petit groupe de Taille 1 qui le
défendront de leur mieux. Sil sagit de végétaux, il bénéficie des effets
de la Capacité « Berceau » comme sil la possédait voir page 99. Sil la
acquise, les effets sont doublés (Bonus de 2D et coûts de guérison diminués
de 2D) et le personnage est capable de les faire pousser en quelques
heures.</p>
type: character
totem: symbiote
learn:
threshold: 5
hindrance: 0
level:
value: 3
min: 1
max: 3
effects: []
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775498762
modifiedTime: 1785775498762
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!CQeFYqMRYMFa6pAa'
@@ -0,0 +1,42 @@
folder: K27yLKAXwiIQTzfW
name: Alpha
type: ability
_id: NOBtH3xQJPptBU6I
img: systems/vermine2047/assets/icons/items/ability.webp
system:
description: >-
<p>Le Groupe laisse lun des siens combattre lun des membres dun groupe
adverse. Il désigne un personnage comme son champion (en recourant au vote
au besoin). Ce dernier affronte ladversaire de son choix, pendant que tous
les autres protagonistes doivent assister à ce duel singulier sans
intervenir. Les créatures sans aucun intellect ne sont pas affectées. Cette
Capacité ne fonctionne quen présence dau moins la moitié du Groupe.</p>
type: group
totem: predator
learn:
threshold: 5
hindrance: 0
level:
value: 1
min: 1
max: 3
effects: []
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775496862
modifiedTime: 1785775496862
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!NOBtH3xQJPptBU6I'
@@ -0,0 +1,42 @@
folder: 84EDVIIqk9YkrBgF
name: Analyse
type: ability
_id: Vggeykg6wQGTaR2o
img: systems/vermine2047/assets/icons/items/ability.webp
system:
description: >-
<p>Le personnage étudie une zone ou un objet pour savoir ce qui sy est
passé ou ce à quoi il a servi. Il peut poser une question au meneur, avant
deffectuer un jet de Perception + Armurerie / Artisanat / Mécanique /
Technologie (en fonction de lobjet). Le tableau ci-dessous indique la
Difficulté en fonction de la période à laquelle remontent les événements.
Pour chaque Réussite, le personnage obtient une information.</p>
type: character
totem: builder
learn:
threshold: 5
hindrance: 0
level:
value: 2
min: 1
max: 3
effects: []
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775498746
modifiedTime: 1785775498746
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!Vggeykg6wQGTaR2o'
@@ -0,0 +1,53 @@
folder: BJAxv25PiSk29dK0
name: Apaisement
type: ability
_id: X1RckrwGCbG7IQVR
img: systems/vermine2047/assets/icons/items/ability.webp
system:
description: >-
<p>Le personnage peut apaiser un animal ou une créature solitaire. Il lui
faudra réussir un jet dEmpathie + Animalisme dont la Difficulté dépend du
degré dagressivité de la créature, déterminé par le meneur. La taille est
appliquée en Handicap. Sil sagit dune espèce incapable de communiquer
avec lhomme, le jet aura deux degrés de Handicap supplémentaires. Si le jet
est réussi, l’état desprit de lanimal ou de la créature saméliore dun
cran. Il ne deviendra pas amical dun coup.</p>
<ul>
<li>En Mode <strong>Cauchemar</strong>, le Handicap ne sera que de (I), peu
importe la taille de la créature.</li>
<li>En Mode <strong>Apocalypse</strong>, la Capacité peut également affecter
une meute ou une nuée.</li>
</ul>
type: character
totem: symbiote
learn:
threshold: 5
hindrance: 0
level:
value: 1
min: 1
max: 3
effects: []
effects: []
sort: 0
ownership:
default: 0
GXSW26kNdNoaabl8: 3
kb05teJOwuTQVHwZ: 3
flags: {}
_stats:
compendiumSource: null
duplicateSource: null
coreVersion: '14.365'
systemId: vermine2047
systemVersion: 0.1.13
createdTime: 1785775498763
modifiedTime: 1785775498763
lastModifiedBy: kb05teJOwuTQVHwZ
exportSource: null
_key: '!items!X1RckrwGCbG7IQVR'

Some files were not shown because too many files have changed in this diff Show More