Compare commits

...

13 Commits

Author SHA1 Message Date
391b99b18f Ajout release process
All checks were successful
Release Creation / build (release) Successful in 58s
2026-01-09 17:44:47 +01:00
ff9acaab65 Ajout release process 2026-01-09 17:44:38 +01:00
60978a67b2 Appv2 + DataModel migration completed 2026-01-09 17:34:39 +01:00
5894cf043f Appv2 + DataModel migration completed 2026-01-09 17:34:39 +01:00
0fd2130fb1 Appv2 + DataModel migration completed 2026-01-09 17:34:39 +01:00
001f0cffa8 Initial data model migration 2026-01-09 17:34:39 +01:00
05ca6e1763 Proper version 2026-01-06 22:57:53 +01:00
d3f014fcca Rework title font and fix pause logo switching 2026-01-05 22:45:10 +01:00
679b3208cd Rework title font and fix pause logo switching 2026-01-05 22:43:52 +01:00
871a18a0ee Correction sur talent Dragon 2025-11-21 21:21:45 +01:00
264c49eb06 Fix release mess 2025-11-07 00:30:31 +01:00
9349c54966 Enhance CSS+fonts 2025-10-26 21:39:25 +01:00
274efac2b5 Enhance CSS+fonts 2025-10-26 15:39:08 +01:00
176 changed files with 12943 additions and 1391 deletions

View File

@@ -0,0 +1,63 @@
name: Release Creation
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
#- uses: actions/checkout@v3
- uses: RouxAntoine/checkout@v3.5.4
# get part of the tag after the `v`
- name: Extract tag version number
id: get_version
uses: battila7/get-version-action@v2
# Substitute the Manifest and Download URLs in the system.json
- name: Substitute Manifest and Download Links For Versioned Ones
id: sub_manifest_link_version
uses: microsoft/variable-substitution@v1
with:
files: 'system.json'
env:
version: ${{steps.get_version.outputs.version-without-v}}
url: https://www.uberwald.me/gitea/${{gitea.repository}}
manifest: https://www.uberwald.me/gitea/public/fvtt-hawkmoon-cyd/releases/download/latest/system.json
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-hawkmoon-cyd.zip
# Create a zip file with all files required by the module to add to the release
- run: |
apt update -y
apt install -y zip
- run: zip -r ./fvtt-hawkmoon-cyd.zip system.json README.md LICENCE.txt assets/ lang/ modules/ packs/ styles/ templates/
- name: setup go
uses: https://github.com/actions/setup-go@v4
with:
go-version: '>=1.20.1'
- name: Use Go Action
id: use-go-action
uses: https://gitea.com/actions/release-action@main
with:
files: |-
./fvtt-hawkmoon-cyd.zip
system.json
api_key: '${{secrets.ALLOW_PUSH_RELEASE}}'
- name: Publish to Foundry server
uses: https://github.com/djlechuck/foundryvtt-publish-package-action@v1
with:
token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }}
id: 'fvtt-hawkmoon-cyd'
version: ${{github.event.release.tag_name}}
manifest: 'https://www.uberwald.me/gitea/public/fvtt-hawkmoon-cyd/releases/download/latest/system.json'
notes: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/fvtt-hawkmoon-cyd.zip'
compatibility-minimum: '13'
compatibility-verified: '13'

View File

@@ -40,7 +40,7 @@
{{selectOptions combativiteList selected=system.sante.etat valueAttr="value" nameAttr="value" labelAttr="label"}} {{selectOptions combativiteList selected=system.sante.etat valueAttr="value" nameAttr="value" labelAttr="label"}}
</select> </select>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -55,6 +55,7 @@
<a class="item" data-tab="equipement">Equipement</a> <a class="item" data-tab="equipement">Equipement</a>
<a class="item" data-tab="biodata">Bio&Notes</a> <a class="item" data-tab="biodata">Bio&Notes</a>
</nav> </nav>
<hr>
{{!-- Sheet Body --}} {{!-- Sheet Body --}}
<section class="sheet-body"> <section class="sheet-body">
@@ -63,7 +64,7 @@
<div class="tab principal" data-group="primary" data-tab="principal"> <div class="tab principal" data-group="primary" data-tab="principal">
<div class="flexcol"> <div class="flexcol">
<div class="grid grid-2col"> <div class="grid grid-2col">
<div class="sheet-box color-bg-archetype"> <div class="sheet-box color-bg-archetype">
@@ -86,7 +87,7 @@
<input type="text" class="padd-right numeric-input item-field-label-short" name="system.vitesse.value" <input type="text" class="padd-right numeric-input item-field-label-short" name="system.vitesse.value"
value="{{system.vitesse.value}}" data-dtype="Number" /> value="{{system.vitesse.value}}" data-dtype="Number" />
</li> </li>
</ul> </ul>
</div> </div>
@@ -236,17 +237,17 @@
{{#each skills as |skill key|}} {{#each skills as |skill key|}}
<li class="item flexrow " data-item-id="{{skill._id}}" data-item-type="competence"> <li class="item flexrow " data-item-id="{{skill._id}}" data-item-type="competence">
<img class="item-name-img" src="{{skill.img}}" /> <img class="item-name-img" src="{{skill.img}}" />
<div class="flexcol item-name-label"> <div class="flexcol item-name-label">
<span class="item-name-label competence-name"><a class="roll-competence item-field-label-short" <span class="item-name-label competence-name"><a class="roll-competence "
data-attr-key="tochoose">{{skill.name}}</a></span> data-attr-key="tochoose">{{skill.name}}</a></span>
<span class="predilection-text"> <span class="predilection-text">
{{#each skill.system.predilections as |pred key|}} {{#each skill.system.predilections as |pred key|}}
{{#if (and pred.acquise (not pred.used))}} {{#if (and pred.acquise (not pred.used))}}
{{pred.name}}, {{pred.name}},
{{/if}} {{/if}}
{{/each}} {{/each}}
</span> </span>
</div> </div>
@@ -410,21 +411,21 @@
<div class="tab equipement" data-group="primary" data-tab="equipement"> <div class="tab equipement" data-group="primary" data-tab="equipement">
<div class="flexcol"> <div class="flexcol">
<hr> <hr>
<div class="sheet-box color-bg-archetype"> <div class="sheet-box color-bg-archetype">
<h4> <h4>
<label class="argent-total-text"> <label class="argent-total-text">
Argent Total : {{richesse.po}} PO - {{richesse.pa}} PA - {{richesse.sc}} SC (total {{richesse.valueSC}} SC) Argent Total : {{richesse.po}} PO - {{richesse.pa}} PA - {{richesse.sc}} SC (total {{richesse.valueSC}} SC)
</label> </label>
</h4> </h4>
</div> </div>
<div class="sheet-box color-bg-archetype"> <div class="sheet-box color-bg-archetype">
<ul class="item-list alternate-list"> <ul class="item-list alternate-list">
<li class="item flexrow list-item items-title-bg"> <li class="item flexrow list-item items-title-bg">
<span class="item-name-label-header"> <span class="item-name-label-header">
<h3><label class="items-title-text">Richesses et Argent</label></h3> <h3><label class="items-title-text">Richesses et Argent</label></h3>
</span> </span>
<span class="item-field-label-short"> <span class="item-field-label-short">
<label class="short-label">Quantité</label> <label class="short-label">Quantité</label>
@@ -440,7 +441,7 @@
<li class="item flexrow " data-item-id="{{monnaie._id}}" data-item-type="monnaie"> <li class="item flexrow " data-item-id="{{monnaie._id}}" data-item-type="monnaie">
<img class="item-name-img" src="{{monnaie.img}}" /> <img class="item-name-img" src="{{monnaie.img}}" />
<span class="item-name-label competence-name">{{monnaie.name}}</span> <span class="item-name-label competence-name">{{monnaie.name}}</span>
<span class="item-name-label competence-name item-field-label-medium">{{monnaie.system.quantite}} <span class="item-name-label competence-name item-field-label-medium">{{monnaie.system.quantite}}
<a class="quantity-modify plus-minus-button" data-quantite-value="-1">-</a> <a class="quantity-modify plus-minus-button" data-quantite-value="-1">-</a>
<a class="quantity-modify plus-minus-button" data-quantite-value="+1">+</a> <a class="quantity-modify plus-minus-button" data-quantite-value="+1">+</a>
</span> </span>
@@ -460,7 +461,7 @@
<label class="argent-total-text"> <label class="argent-total-text">
Valeur Total Equipement : {{valeurEquipement.po}} PO - {{valeurEquipement.pa}} PA - {{valeurEquipement.sc}} SC (total {{valeurEquipement.valueSC}} SC) Valeur Total Equipement : {{valeurEquipement.po}} PO - {{valeurEquipement.pa}} PA - {{valeurEquipement.sc}} SC (total {{valeurEquipement.valueSC}} SC)
</label> </label>
</h4> </h4>
</div> </div>
<div class="sheet-box color-bg-archetype"> <div class="sheet-box color-bg-archetype">
@@ -627,9 +628,6 @@
<span> <span>
<h3>Description</h3> <h3>Description</h3>
</span> </span>
<div class="medium-editor item-text-long-line">
{{editor description target="system.biodata.description" button=true owner=owner editable=editable}}
</div>
</div> </div>

View File

@@ -0,0 +1,254 @@
<div class="hawkmoon-chat-result">
{{!-- Header avec acteur --}}
<div class="chat-result-header">
{{#if actorImg}}
<img class="actor-icon" src="{{actorImg}}" alt="{{alias}}" />
{{/if}}
<div class="header-info">
<h4 class="actor-name">{{alias}}</h4>
{{#if competence}}
<div class="action-title">
<i class="fas fa-dice-d20"></i>
{{competence.name}}
{{#if arme}}• {{arme.name}}{{/if}}
</div>
{{else if actionImg}}
<div class="action-title">
<i class="fas fa-dice-d20"></i>
{{attr.label}}{{#if attr2}} + {{attr2.label}}{{/if}}
</div>
{{/if}}
</div>
</div>
{{!-- Résultat principal --}}
<div class="result-main">
<div class="result-display">
<div class="dice-result">
<i class="fas fa-dice"></i>
<span class="dice-value">{{diceResult}}</span>
</div>
<div class="total-result">
<span class="total-label">Total</span>
<span class="total-value">{{finalResult}}</span>
</div>
{{#if difficulte}}
<div class="difficulty">
<span class="difficulty-label">SD</span>
<span class="difficulty-value">{{difficulte}}</span>
</div>
{{/if}}
</div>
{{!-- Badge de résultat --}}
{{#if difficulte}}
<div class="result-badge-container">
{{#if isHeroique}}
<div class="result-badge heroique">
<i class="fas fa-star"></i> HÉROÏQUE !
</div>
{{else if isDramatique}}
<div class="result-badge dramatique">
<i class="fas fa-skull"></i> DRAMATIQUE !
</div>
{{else if isSuccess}}
<div class="result-badge success">
<i class="fas fa-check"></i> Succès
</div>
{{else}}
<div class="result-badge failure">
<i class="fas fa-times"></i> Échec
</div>
{{/if}}
</div>
{{/if}}
</div>
{{!-- Détails du jet --}}
<div class="result-details">
<div class="details-section">
<div class="detail-row">
<span class="detail-label">Formule:</span>
<span class="detail-value">{{diceFormula}}</span>
</div>
<div class="detail-row">
<span class="detail-label">{{attr.label}}:</span>
<span class="detail-value">{{attr.value}}</span>
</div>
{{#if attr2}}
<div class="detail-row">
<span class="detail-label">{{attr2.label}}:</span>
<span class="detail-value">{{attr2.value}}</span>
</div>
{{/if}}
{{#if competence}}
<div class="detail-row">
<span class="detail-label">{{competence.name}}:</span>
<span class="detail-value">{{competence.system.niveau}}</span>
</div>
{{/if}}
{{#if selectedMaitrise}}
<div class="detail-row">
<span class="detail-label">Maîtrise:</span>
<span class="detail-value">{{selectedMaitrise.name}}</span>
</div>
{{/if}}
{{#if arme}}
<div class="detail-row">
<span class="detail-label">Arme:</span>
<span class="detail-value">{{arme.name}} (+{{arme.system.bonusmaniementoff}})</span>
</div>
{{/if}}
{{#if bonusRoll}}
<div class="detail-row bonus">
<span class="detail-label">{{textBonus}}:</span>
<span class="detail-value">+{{bonusRoll.total}}</span>
</div>
{{/if}}
</div>
</div>
{{!-- Effets et conséquences --}}
{{#if isSuccess}}
<div class="result-effects">
{{#if attaqueDesarme}}
<div class="effect-item">
<i class="fas fa-hand-sparkles"></i>
{{#if isHeroique}}
Vous récupérez l'arme de votre adversaire dans votre main !
{{else}}
Vous désarmez votre adversaire ! Son arme tombe hors de sa portée.
{{/if}}
</div>
{{/if}}
{{#if immobiliser}}
<div class="effect-item">
<i class="fas fa-lock"></i>
{{#if isHeroique}}
Votre cible est immobilisée, et vous pouvez faire une action complexe.
{{else}}
Votre cible est immobilisée.
{{/if}}
</div>
{{/if}}
{{#if desengager}}
<div class="effect-item">
<i class="fas fa-running"></i>
Vous vous désengagez de votre adversaire.
</div>
{{/if}}
{{#if repousser}}
<div class="effect-item">
<i class="fas fa-hand-rock"></i>
{{#if isHeroique}}
Votre cible est repoussée de 3 mètres et tombe au sol.
{{else}}
Votre cible tombe au sol.
{{/if}}
</div>
{{/if}}
{{#if assomer}}
<div class="effect-item">
<i class="fas fa-dizzy"></i>
{{#if isHeroique}}
Votre cible est assomée pour [[/r 1d10+10]] minutes.
{{else}}
Votre cible est assomée pour [[/r 1d10]] minutes.
{{/if}}
</div>
{{/if}}
{{#if coupBas}}
<div class="effect-item">
<i class="fas fa-shoe-prints"></i>
La cible a reçu 2 adversités bleues et a perdu 1 niveau de combativité.
{{#if isHeroique}}
<br><strong>Et votre cible perd sa prochaine action complexe.</strong>
{{/if}}
</div>
{{/if}}
{{#if arme}}
{{#if contenir}}
<div class="effect-item">
<i class="fas fa-shield-alt"></i>
{{#if isHeroique}}
Aucun dégât, mais tous les adversaires dont le SD + 10 est atteint ne peuvent déclarer d'attaque contre vous lors de leur prochaine action complexe.
{{else}}
Aucun dégât, mais la cible ne peut pas déclarer d'attaque contre vous lors de sa prochaine action complexe.
{{/if}}
</div>
{{else}}
{{#if (eq nbCombativitePerdu "vaincu")}}
<div class="effect-item victory">
<i class="fas fa-trophy"></i>
<strong>Votre adversaire est vaincu !</strong>
</div>
{{else}}
<div class="effect-item">
<i class="fas fa-heart-broken"></i>
Votre adversaire a perdu {{nbCombativitePerdu}} État de Combativité.
</div>
{{/if}}
{{#if (not arme.system.onlevelonly)}}
<div class="damage-buttons">
<button class="chat-card-button roll-chat-degat">
<i class="fas fa-burst"></i> Dégâts de l'arme
</button>
{{#if coupDevastateur}}
<button class="chat-card-button roll-chat-degat-devastateur">
<i class="fas fa-explosion"></i> Dégâts avec Coup Dévastateur
</button>
{{/if}}
</div>
{{/if}}
{{/if}}
{{/if}}
</div>
{{/if}}
{{!-- Avertissements --}}
{{#if attaqueCharge}}
<div class="result-warning">
<i class="fas fa-exclamation-triangle"></i>
Vous avez chargé : vos adversaires bénéficient de +3 pour vous attaquer.
</div>
{{/if}}
{{#if desengager}}
{{#if (not isSuccess)}}
<div class="result-warning">
<i class="fas fa-exclamation-triangle"></i>
Vous ne parvenez pas à vous désengager, votre adversaire a un bonus de +3 pour vous attaquer.
</div>
{{/if}}
{{/if}}
{{#if isInit}}
<div class="result-info">
<i class="fas fa-flag"></i> Initiative stockée !
</div>
{{/if}}
{{!-- Prédilections --}}
{{#each predilections as |pred key|}}
{{#if (and (and pred.acquise (not pred.maitrise)) (not pred.used))}}
<div class="predilection-section">
<button class="chat-card-button predilection-reroll" data-predilection-index="{{key}}">
<i class="fas fa-redo"></i> Prédilection : {{pred.name}}
</button>
</div>
{{/if}}
{{/each}}
</div>

View File

@@ -3,8 +3,8 @@
* @extends {ActorSheet} * @extends {ActorSheet}
*/ */
import { HawkmoonUtility } from "./hawkmoon-utility.js"; import { HawkmoonUtility } from "../modules/hawkmoon-utility.js";
import { HawkmoonAutomation } from "./hawkmoon-automation.js"; import { HawkmoonAutomation } from "../modules/hawkmoon-automation.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class HawkmoonActorSheet extends foundry.appv1.sheets.ActorSheet { export class HawkmoonActorSheet extends foundry.appv1.sheets.ActorSheet {
@@ -36,6 +36,7 @@ export class HawkmoonActorSheet extends foundry.appv1.sheets.ActorSheet {
editable: this.isEditable, editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked", cssClass: this.isEditable ? "editable" : "locked",
system: objectData.system, system: objectData.system,
systemFields: this.document.system.schema.fields,
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)), effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
limited: this.object.limited, limited: this.object.limited,
skills: this.actor.getSkills(), skills: this.actor.getSkills(),
@@ -56,7 +57,7 @@ export class HawkmoonActorSheet extends foundry.appv1.sheets.ActorSheet {
nbCombativite: this.actor.system.sante.nbcombativite, nbCombativite: this.actor.system.sante.nbcombativite,
combativiteList: HawkmoonUtility.getCombativiteList(this.actor.system.sante.nbcombativite), combativiteList: HawkmoonUtility.getCombativiteList(this.actor.system.sante.nbcombativite),
initiative: this.actor.getFlag("world", "last-initiative") || -1, initiative: this.actor.getFlag("world", "last-initiative") || -1,
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.description, {async: true}), enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.description, {async: true}),
habitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.habitat, {async: true}), habitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.object.system.biodata.habitat, {async: true}),
options: this.options, options: this.options,
owner: this.document.isOwner, owner: this.document.isOwner,
@@ -66,7 +67,6 @@ export class HawkmoonActorSheet extends foundry.appv1.sheets.ActorSheet {
} }
this.formData = formData; this.formData = formData;
console.log("PC : ", formData, this.object);
return formData; return formData;
} }

View File

@@ -3,8 +3,8 @@
* @extends {ActorSheet} * @extends {ActorSheet}
*/ */
import { HawkmoonUtility } from "./hawkmoon-utility.js"; import { HawkmoonUtility } from "../modules/hawkmoon-utility.js";
import { HawkmoonAutomation } from "./hawkmoon-automation.js"; import { HawkmoonAutomation } from "../modules/hawkmoon-automation.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
const __ALLOWED_ITEM_CELLULE = { "talent": 1, "ressource": 1, "contact": 1, "equipement": 1, "protection": 1, "artefact": 1, "arme": 1, "monnaie": 1 } const __ALLOWED_ITEM_CELLULE = { "talent": 1, "ressource": 1, "contact": 1, "equipement": 1, "protection": 1, "artefact": 1, "arme": 1, "monnaie": 1 }

View File

@@ -4,8 +4,8 @@
*/ */
import { HawkmoonActorSheet } from "./hawkmoon-actor-sheet.js"; import { HawkmoonActorSheet } from "./hawkmoon-actor-sheet.js";
import { HawkmoonUtility } from "./hawkmoon-utility.js"; import { HawkmoonUtility } from "../modules/hawkmoon-utility.js";
import { HawkmoonAutomation } from "./hawkmoon-automation.js"; import { HawkmoonAutomation } from "../modules/hawkmoon-automation.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class HawkmoonCreatureSheet extends HawkmoonActorSheet { export class HawkmoonCreatureSheet extends HawkmoonActorSheet {

View File

@@ -1,14 +1,14 @@
import { HawkmoonUtility } from "./hawkmoon-utility.js"; import { HawkmoonUtility } from "../modules/hawkmoon-utility.js";
export class HawkmoonRollDialog extends Dialog { export class HawkmoonRollDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async create(actor, rollData ) { static async create(actor, rollData) {
let options = { classes: ["HawkmoonDialog"], width: 320, height: 'fit-content', 'z-index': 99999 }; let options = { classes: ["HawkmoonDialog"], width: 420, height: 'fit-content', 'z-index': 99999 };
let html = await foundry.applications.handlebars.renderTemplate('systems/fvtt-hawkmoon-cyd/templates/roll-dialog-generic.html', rollData); let html = await foundry.applications.handlebars.renderTemplate('systems/fvtt-hawkmoon-cyd/templates/roll-dialog-generic.html', rollData);
return new HawkmoonRollDialog(actor, rollData, html, options ); return new HawkmoonRollDialog(actor, rollData, html, options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -18,20 +18,21 @@ export class HawkmoonRollDialog extends Dialog {
content: html, content: html,
buttons: { buttons: {
rolld10: { rolld10: {
icon: '<i class="fas fa-check"></i>', icon: '<i class="fas fa-check"></i>',
label: "Lancer 1d10", label: "Lancer 1d10",
callback: () => { this.roll("d10") } callback: () => { this.roll("d10") }
}, },
rolld20: { rolld20: {
icon: '<i class="fas fa-check"></i>', icon: '<i class="fas fa-check"></i>',
label: "Lancer 1d20", label: "Lancer 1d20",
callback: () => { this.roll("d20") } callback: () => { this.roll("d20") }
}, },
cancel: { cancel: {
icon: '<i class="fas fa-times"></i>', icon: '<i class="fas fa-times"></i>',
label: "Annuler", label: "Annuler",
callback: () => { this.close() } callback: () => { this.close() }
} }, }
},
close: close close: close
} }
@@ -42,9 +43,9 @@ export class HawkmoonRollDialog extends Dialog {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
roll ( dice) { roll(dice) {
this.rollData.mainDice = dice this.rollData.mainDice = dice
HawkmoonUtility.rollHawkmoon( this.rollData ) HawkmoonUtility.rollHawkmoon(this.rollData)
} }
@@ -57,20 +58,20 @@ export class HawkmoonRollDialog extends Dialog {
} }
$(function () { onLoad(); }); $(function () { onLoad(); });
html.find('#modificateur').change(async (event) => { html.find('#modificateur').change(async (event) => {
this.rollData.modificateur = Number(event.currentTarget.value) this.rollData.modificateur = Number(event.currentTarget.value)
}) })
html.find('#difficulte').change( (event) => { html.find('#difficulte').change((event) => {
console.log("Difficulte: " + event.currentTarget.value) console.log("Difficulte: " + event.currentTarget.value)
this.rollData.difficulte = Number(event.currentTarget.value) this.rollData.difficulte = Number(event.currentTarget.value)
}) })
html.find('#attrKey').change(async (event) => { html.find('#attrKey').change(async (event) => {
this.rollData.attrKey = String(event.currentTarget.value) this.rollData.attrKey = String(event.currentTarget.value)
}) })
html.find('#attrKey2').change(async (event) => { html.find('#attrKey2').change(async (event) => {
this.rollData.attrKey2 = String(event.currentTarget.value) this.rollData.attrKey2 = String(event.currentTarget.value)
}) })
html.find('#select-maitrise').change(async (event) => { html.find('#select-maitrise').change(async (event) => {
this.rollData.maitriseId = String(event.currentTarget.value) this.rollData.maitriseId = String(event.currentTarget.value)
}) })
html.find('#competence-talents').change((event) => { html.find('#competence-talents').change((event) => {

View File

@@ -0,0 +1,36 @@
<li
class="item flexrow list-item list-item-shadow"
data-item-id="{{equip._id}}"
>
<a class="item-edit item-name-img" title="Edit Item"
><img class="sheet-competence-img" src="{{equip.img}}"
/></a>
{{#if (eq level 1)}}
<span class="item-name-label">{{equip.name}}</span>
{{else}}
<span class="item-name-label-level2">{{equip.name}}</span>
{{/if}}
<span class="item-field-label-long"
><label>
{{equip.system.quantity}} (<a class="quantity-minus plus-minus-button">
-</a
>/<a class="quantity-plus plus-minus-button">+</a>)
</label>
</span>
<div class="item-filler">&nbsp;</div>
<div class="item-controls item-controls-fixed">
{{#if (eq level 1)}}
<a class="item-control item-equip" title="Worn"
>{{#if equip.system.equipped}}<i class="fas fa-circle"></i>{{else}}<i
class="fas fa-genderless"
></i
>{{/if}}</a
>
{{/if}}
<a class="item-control item-delete" title="Delete Item"
><i class="fas fa-trash"></i
></a>
</div>
</li>

View File

@@ -0,0 +1,334 @@
<form class="skill-roll-dialog">
<header class="roll-dialog-header">
{{#if img}}
<img class="actor-icon" src="{{img}}" data-edit="img" title="{{name}}" />
{{/if}}
<h1 class="dialog-roll-title roll-dialog-header">{{title}}</h1>
</header>
<div class="flexcol">
<div class="flexrow">
{{#if (eq attrKey "tochoose")}}
<span class="roll-dialog-label"><strong>Attribut</strong></span>
<select
class="status-small-label color-class-common"
id="attrKey"
type="text"
name="attrKey"
value="attrKey"
data-dtype="string"
>
{{selectOptions attributs selected=attrKey}}
</select>
{{else}}
<span class="roll-dialog-label"><strong>{{attr.label}}</strong></span>
<span class="small-label roll-dialog-label">{{attr.value}}</span>
{{/if}}
</div>
{{#if nbAdversites}}
<div class="flexrow">
<span class="roll-dialog-label"><strong>Malus d'adversités</strong></span>
<span class="small-label roll-dialog-label">- {{nbAdversites}}</span>
</div>
{{/if}} {{#if competence}}
<div class="flexrow">
<span class="roll-dialog-label"><strong>{{competence.name}}</strong></span>
<span class="small-label roll-dialog-label"><strong>{{competence.system.niveau}}</strong></span>
</div>
{{#if maitrises}}
<div class="flexrow">
<span class="roll-dialog-label"><strong>Maîtrise</strong></span>
<select
class="status-small-label color-class-common"
id="select-maitrise"
type="text"
name="select-maitrise"
value="maitriseId"
data-dtype="string"
>
{{selectOptions maitrises selected=maitriseId valueAttr="key"
nameAttr="key" labelAttr="label"}}
</select>
</div>
{{/if}} {{else}}
<div class="flexrow">
<span class="roll-dialog-label"><strong>Second Attribut</strong></span>
<select
class="status-small-label color-class-common"
id="attrKey2"
type="text"
name="attrKey2"
value="attrKey2"
data-dtype="string"
>
{{#select attrKey2}}
<option value="none">Aucun</option>
{{#each attributs as |attrLabel attrKey|}}
<option value="{{attrKey}}">{{attrLabel}}</option>
{{/each}} {{/select}}
</select>
</div>
{{/if}} {{#if (count talents)}}
<div class="flexrow">
<span class="roll-dialog-label"><strong></strong>Talents</strong></span>
<select
class="flex1"
name="competence-talents"
id="competence-talents"
data-type="String"
multiple
>
{{#each talents as |talent key|}}
<option value="{{talent._id}}">{{talent.name}}</option>
{{/each}}
</select>
</div>
{{/if}} {{#if conditionsCommunes}}
<div class="flexrow">
<span class="roll-dialog-label">En surplomb, défenseur au sol (+3)?</span>
<input type="checkbox" id="defenseur-au-sol" {{checked defenseurAuSol}} />
</div>
<div class="flexrow">
<span class="roll-dialog-label">Défenseur aveuglé (+10)?</span>
<input
type="checkbox"
id="defenseur-aveugle"
{{checked
defenseurAveugle}}
/>
</div>
<div class="flexrow">
<span class="roll-dialog-label">Défenseur de dos (+5)?</span>
<input type="checkbox" id="defenseur-de-dos" {{checked defenseurDeDos}} />
</div>
<div class="flexrow">
<span class="roll-dialog-label">Défenseur immobilisé (+5)?</span>
<input
type="checkbox"
id="defenseur-immobilise"
{{checked
defenseurImmobilise}}
/>
</div>
{{/if}} {{#if (or immobiliser repousser)}}
<div class="flexrow">
<span class="roll-dialog-label">Cible consciente?</span>
<input type="checkbox" id="cibleconsciente" {{checked cibleconsciente}} />
</div>
{{/if}} {{#if arme}} {{#if arme.system.isMelee}} {{#if bonusArmeNaturelle}}
<div class="flexrow">
<span class="roll-dialog-label">Arme naturelle/fortune en défense</span>
<span class="small-label roll-dialog-label">{{bonusArmeNaturelle}}</span>
</div>
{{/if}}
<div class="flexrow">
<span class="roll-dialog-label">En surplomb, défenseur au sol (+3)?</span>
<input type="checkbox" id="defenseur-au-sol" {{checked defenseurAuSol}} />
</div>
<div class="flexrow">
<span class="roll-dialog-label"
>Attaquants multiples (après le premier) (+3)?</span
>
<input
type="checkbox"
id="attaquants-multiple"
{{checked
attaquantsMultiples}}
/>
</div>
{{#if hasAmbidextre}}
<div class="flexrow">
<span class="roll-dialog-label"
>Première attaque avec deux armes (-3)?</span
>
<input type="checkbox" id="ambidextre-1" {{checked attaqueAmbidextre1}} />
</div>
<div class="flexrow">
<span class="roll-dialog-label"
>Seconde attaque avec deux armes (-6)?</span
>
<input type="checkbox" id="ambidextre-2" {{checked attaqueAmbidextre2}} />
</div>
{{/if}} {{#if hasFeinte}}
<div class="flexrow">
<span class="roll-dialog-label"
>Feinte (<strong>cout : 1 BA</strong>) ?</span
>
<input type="checkbox" id="feinte" {{checked feinte}} />
</div>
{{/if}}
<div class="flexrow">
<span class="roll-dialog-label"><strong></strong>Soutiens</strong></span>
<select
class="status-small-label color-class-common"
name="soutiens"
id="soutiens"
data-type="Number"
>
{{selectOptions config.optionsSoutiens selected=soutiens valueAttr="key"
nameAttr="key" labelAttr="label"}}
</select>
</div>
<div class="flexrow">
<span class="roll-dialog-label">Défenseur aveuglé (+10)?</span>
<input
type="checkbox"
id="defenseur-aveugle"
{{checked
defenseurAveugle}}
/>
</div>
<div class="flexrow">
<span class="roll-dialog-label">Défenseur de dos (+5)?</span>
<input type="checkbox" id="defenseur-de-dos" {{checked defenseurDeDos}} />
</div>
<div class="flexrow">
<span class="roll-dialog-label"
>Défenseur dans espace restreint (+3)?</span
>
<input
type="checkbox"
id="defenseur-restreint"
{{checked
defenseurRestreint}}
/>
</div>
<div class="flexrow">
<span class="roll-dialog-label">Défenseur immobilisé (+5)?</span>
<input
type="checkbox"
id="defenseur-immobilise"
{{checked
defenseurImmobilise}}
/>
</div>
<div class="flexrow">
<span class="roll-dialog-label">Charge ?</span>
<input type="checkbox" id="attaque-charge" {{checked attaqueCharge}} />
</div>
<div class="flexrow">
<span class="roll-dialog-label">Contenir?</span>
<input type="checkbox" id="contenir" {{checked contenir}} />
</div>
<div class="flexrow">
<span class="roll-dialog-label">Désarmer (SD+10)?</span>
<input type="checkbox" id="attaque-desarme" {{checked attaqueDesarme}} />
</div>
{{#if isMonte}}
<div class="flexrow">
<span class="roll-dialog-label">Charge de cavalerie?</span>
<input
type="checkbox"
id="charge-cavalerie"
{{checked
chargeCavalerie}}
/>
</div>
{{/if}} {{else}}
<div class="flexrow">
<span class="roll-dialog-label">Tireur en déplacement ?</span>
<select
class="item-field-label-long"
type="text"
id="tireur-deplacement"
data-dtype="string"
>
{{selectOptions config.optionsTireurDeplacement
selected=tireurDeplacement valueAttr="key" nameAttr="key"
labelAttr="label"}}
</select>
</div>
<div class="flexrow">
<span class="roll-dialog-label">Couvert de la cible ?</span>
<select
class="item-field-label-long"
type="text"
id="cible-couvert"
data-dtype="string"
>
{{selectOptions config.optionsCouvert selected=cibleCouvert
valueAttr="key" nameAttr="key" labelAttr="label"}}
</select>
</div>
<div class="flexrow">
<span class="roll-dialog-label">Cible se déplace vite (SD+3)?</span>
<input
type="checkbox"
id="tireur-cible-deplace"
{{checked
cibleDeplace}}
/>
</div>
<div class="flexrow">
<span class="roll-dialog-label">Cible corps à corps (SD+3)?</span>
<input type="checkbox" id="tireur-cible-cac" {{checked cibleCaC}} />
</div>
<div class="flexrow">
<span class="roll-dialog-label">Taille de la cible ?</span>
<select
class="item-field-label-long"
type="text"
id="taille-cible"
data-dtype="string"
>
{{selectOptions config.optionsTailleCible selected=tailleCible
valueAttr="key" nameAttr="key" labelAttr="label"}}
</select>
</div>
{{/if}} {{/if}}
<div class="flexrow">
<span class="roll-dialog-label">Bonus/Malus </span>
<select
class="roll-dialog-label"
id="bonus-malus-context"
type="text"
value="{{bonusMalusContext}}"
data-dtype="Number"
>
{{selectOptions config.optionsBonusMalus selected=bonusMalusContext
valueAttr="key" nameAttr="key" labelAttr="label"}}
</select>
</div>
{{#if (or armeDefense arme.system.isDistance)}} {{#if
arme.system.isDistance}}
<div class="flexrow">
<span class="roll-dialog-label">SD de distance</span>
<select
class="item-field-label-long"
type="text"
id="distance-tir"
data-dtype="string"
>
{{selectOptions config.optionsDistanceTir selected=distanceTir
valueAttr="key" nameAttr="key" labelAttr="label"}}
</select>
</div>
{{else}}
<div class="flexrow">
{{#if desengager}}
<span class="roll-dialog-label">C. Offensive adversaire </span>
{{else}}
<span class="roll-dialog-label">C. Défensive adversaire</span>
{{/if}}
<span class="roll-dialog-label"><strong>{{difficulte}}</strong> </span>
</div>
{{/if}} {{else}} {{#if isInit}} {{else}}
<div class="flexrow">
<span class="roll-dialog-label">Difficulté : </span>
<select
class="roll-dialog-label"
id="difficulte"
type="text"
name="difficulte"
data-dtype="String"
>
{{selectOptions config.optionsDifficulte selected=difficulte
valueAttr="key" nameAttr="key" labelAttr="label"}}
</select>
</div>
{{/if}} {{/if}}
</div>
</form>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

35
gulpfile.js Normal file
View File

@@ -0,0 +1,35 @@
const gulp = require('gulp');
const less = require('gulp-less');
const sourcemaps = require('gulp-sourcemaps');
// Paths
const paths = {
styles: {
src: 'less/**/*.less',
dest: 'styles/'
}
};
// Compile LESS to CSS
function styles() {
return gulp.src('less/hawkmoon.less')
.pipe(sourcemaps.init())
.pipe(less())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(paths.styles.dest));
}
// Watch files
function watchFiles() {
gulp.watch(paths.styles.src, styles);
}
// Define complex tasks
const build = gulp.series(styles);
const watch = gulp.series(build, watchFiles);
// Export tasks
exports.styles = styles;
exports.build = build;
exports.watch = watch;
exports.default = build;

4
less/hawkmoon.less Normal file
View File

@@ -0,0 +1,4 @@
// Main LESS file for Hawkmoon system
// Temporarily importing the full converted simple.css while we refactor
@import "simple-converted";

2713
less/simple-converted.less Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,177 @@
import { HawkmoonUtility } from "../hawkmoon-utility.js"
import { HAWKMOON_CONFIG } from "../hawkmoon-config.js"
/**
* Dialogue de jet de dé pour Hawkmoon - Version DialogV2
*/
export class HawkmoonRollDialog {
/**
* Create and display the roll dialog
* @param {HawkmoonActor} actor - The actor making the roll
* @param {Object} rollData - Data for the roll
* @returns {Promise<HawkmoonRollDialog>}
*/
static async create(actor, rollData) {
// Préparer le contexte pour le template
const context = {
...rollData,
difficulte: String(rollData.difficulte || 0), // Convertir en string pour matcher les options du select
img: actor.img,
name: actor.name,
config: HAWKMOON_CONFIG,
}
// Si attrKey est "tochoose", préparer la liste des attributs sélectionnables
if (rollData.attrKey === "tochoose") {
context.selectableAttributes = actor.system.attributs
// Ne pas changer attrKey ni attr - l'utilisateur doit choisir
}
// Rendre le template en HTML
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-hawkmoon-cyd/templates/roll-dialog-generic.hbs",
context
)
// Utiliser DialogV2.wait avec le HTML rendu
return foundry.applications.api.DialogV2.wait({
window: { title: "Test de Capacité", icon: "fa-solid fa-dice-d20" },
classes: ["hawkmoon-roll-dialog"],
position: { width: 480 },
modal: false, // Permettre l'interaction avec le canvas pour garder la cible sélectionnée
content,
buttons: [
{
action: "rolld10",
label: "Lancer 1d10",
icon: "fa-solid fa-dice-d10",
default: true,
callback: (event, button, dialog) => {
this._updateRollDataFromForm(rollData, button.form.elements, actor)
rollData.mainDice = "d10"
HawkmoonUtility.rollHawkmoon(rollData)
}
},
{
action: "rolld20",
label: "Lancer 1d20",
icon: "fa-solid fa-dice-d20",
callback: (event, button, dialog) => {
this._updateRollDataFromForm(rollData, button.form.elements, actor)
rollData.mainDice = "d20"
HawkmoonUtility.rollHawkmoon(rollData)
}
},
],
rejectClose: false,
})
}
/**
* Mettre à jour rollData avec les valeurs du formulaire
* @param {Object} rollData - L'objet rollData à mettre à jour
* @param {HTMLFormControlsCollection} formElements - Les éléments du formulaire
* @param {HawkmoonActor} actor - L'acteur pour récupérer les attributs
* @private
*/
static _updateRollDataFromForm(rollData, formElements, actor) {
// Attributs
if (formElements.attrKey) {
rollData.attrKey = formElements.attrKey.value
// Si l'attribut a changé, mettre à jour rollData.attr
if (rollData.attrKey !== "tochoose" && rollData.attrKey !== "none" && actor) {
rollData.attr = foundry.utils.duplicate(actor.system.attributs[rollData.attrKey])
rollData.actionImg = "systems/fvtt-hawkmoon-cyd/assets/icons/" + actor.system.attributs[rollData.attrKey].labelnorm + ".webp"
}
}
if (formElements.attrKey2) {
rollData.attrKey2 = formElements.attrKey2.value
}
// Modificateurs de base
if (formElements.difficulte) {
rollData.difficulte = Number(formElements.difficulte.value)
}
if (formElements.modificateur) {
rollData.modificateur = Number(formElements.modificateur.value)
}
if (formElements.soutiens) {
rollData.soutiens = Number(formElements.soutiens.value)
}
// Compétence
if (formElements.maitrise) {
rollData.maitriseId = formElements.maitrise.value
}
if (formElements.talents) {
// Récupérer toutes les options sélectionnées (select multiple)
const selectedOptions = Array.from(formElements.talents.selectedOptions)
rollData.selectedTalents = selectedOptions.map(opt => opt.value)
}
// Modificateurs de tir
if (formElements.tailleCible) {
rollData.tailleCible = formElements.tailleCible.value
}
if (formElements.tireurDeplacement) {
rollData.tireurDeplacement = formElements.tireurDeplacement.value
}
if (formElements.cibleCouvert) {
rollData.cibleCouvert = formElements.cibleCouvert.value
}
if (formElements.distanceTir) {
rollData.distanceTir = formElements.distanceTir.value
}
if (formElements.cibleDeplace) {
rollData.cibleDeplace = formElements.cibleDeplace.checked
}
if (formElements.cibleCaC) {
rollData.cibleCaC = formElements.cibleCaC.checked
}
// Modificateurs de combat (checkboxes)
if (formElements.defenseurAuSol) {
rollData.defenseurAuSol = formElements.defenseurAuSol.checked
}
if (formElements.ambidextre1) {
rollData.ambidextre1 = formElements.ambidextre1.checked
}
if (formElements.ambidextre2) {
rollData.ambidextre2 = formElements.ambidextre2.checked
}
if (formElements.attaqueMonte) {
rollData.attaqueMonte = formElements.attaqueMonte.checked
}
if (formElements.defenseurAveugle) {
rollData.defenseurAveugle = formElements.defenseurAveugle.checked
}
if (formElements.defenseurDeDos) {
rollData.defenseurDeDos = formElements.defenseurDeDos.checked
}
if (formElements.defenseurRestreint) {
rollData.defenseurRestreint = formElements.defenseurRestreint.checked
}
if (formElements.defenseurImmobilise) {
rollData.defenseurImmobilise = formElements.defenseurImmobilise.checked
}
if (formElements.attaqueCharge) {
rollData.attaqueCharge = formElements.attaqueCharge.checked
}
if (formElements.chargeCavalerie) {
rollData.chargeCavalerie = formElements.chargeCavalerie.checked
}
if (formElements.attaquantsMultiple) {
rollData.attaquantsMultiple = formElements.attaquantsMultiple.checked
}
if (formElements.feinte) {
rollData.feinte = formElements.feinte.checked
}
if (formElements.contenir) {
rollData.contenir = formElements.contenir.checked
}
if (formElements.attaqueDesarme) {
rollData.attaqueDesarme = formElements.attaqueDesarme.checked
}
}
}

View File

@@ -0,0 +1,17 @@
export { default as HawkmoonTalentSheet } from "./talent-sheet.mjs"
export { default as HawkmoonCompetenceSheet } from "./competence-sheet.mjs"
export { default as HawkmoonArmeSheet } from "./arme-sheet.mjs"
export { default as HawkmoonProtectionSheet } from "./protection-sheet.mjs"
export { default as HawkmoonHistoriqueSheet } from "./historique-sheet.mjs"
export { default as HawkmoonProfilSheet } from "./profil-sheet.mjs"
export { default as HawkmoonEquipementSheet } from "./equipement-sheet.mjs"
export { default as HawkmoonMonnaieSheet } from "./monnaie-sheet.mjs"
export { default as HawkmoonArtefactSheet } from "./artefact-sheet.mjs"
export { default as HawkmoonRessourceSheet } from "./ressource-sheet.mjs"
export { default as HawkmoonContactSheet } from "./contact-sheet.mjs"
export { default as HawkmoonMutationSheet } from "./mutation-sheet.mjs"
// Actor sheets
export { default as HawkmoonPersonnageSheet } from "./personnage-sheet.mjs"
export { default as HawkmoonCreatureSheet } from "./creature-sheet.mjs"
export { default as HawkmoonCelluleSheet } from "./cellule-sheet.mjs"

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonArmeSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["arme"],
position: {
width: 640,
},
window: {
contentClasses: ["arme-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-arme-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonArtefactSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["artefact"],
position: {
width: 620,
},
window: {
contentClasses: ["artefact-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-artefact-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,547 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
import { HawkmoonUtility } from "../../hawkmoon-utility.js"
import { HawkmoonAutomation } from "../../hawkmoon-automation.js"
export default class HawkmoonActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
/**
* Different sheet modes.
* @enum {number}
*/
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
this._sheetMode = this.constructor.SHEET_MODES.PLAY // Commencer en mode visualisation
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["fvtt-hawkmoon-cyd", "sheet", "actor"],
position: {
width: 640,
height: 720,
},
window: {
resizable: true,
},
form: {
submitOnChange: true,
closeOnSubmit: false,
},
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: "form" }],
actions: {
editImage: HawkmoonActorSheet.#onEditImage,
toggleSheet: HawkmoonActorSheet.#onToggleSheet,
editItem: HawkmoonActorSheet.#onEditItem,
deleteItem: HawkmoonActorSheet.#onDeleteItem,
createItem: HawkmoonActorSheet.#onCreateItem,
equipItem: HawkmoonActorSheet.#onEquipItem,
modifyQuantity: HawkmoonActorSheet.#onModifyQuantity,
modifyAdversite: HawkmoonActorSheet.#onModifyAdversite,
rollInitiative: HawkmoonActorSheet.#onRollInitiative,
rollAttribut: HawkmoonActorSheet.#onRollAttribut,
rollCompetence: HawkmoonActorSheet.#onRollCompetence,
rollArmeOffensif: HawkmoonActorSheet.#onRollArmeOffensif,
rollArmeDegats: HawkmoonActorSheet.#onRollArmeDegats,
rollAssommer: HawkmoonActorSheet.#onRollAssommer,
rollCoupBas: HawkmoonActorSheet.#onRollCoupBas,
rollImmobiliser: HawkmoonActorSheet.#onRollImmobiliser,
rollRepousser: HawkmoonActorSheet.#onRollRepousser,
rollDesengager: HawkmoonActorSheet.#onRollDesengager,
},
}
/**
* Is the sheet currently in 'Play' mode?
* @type {boolean}
*/
get isPlayMode() {
// Initialize if not set
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
}
/**
* Is the sheet currently in 'Edit' mode?
* @type {boolean}
*/
get isEditMode() {
// Initialize if not set
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
}
/**
* Tab groups state
* @type {object}
*/
tabGroups = { primary: "principal" }
/** @override */
async _prepareContext() {
const actor = this.document
const context = {
actor: actor,
system: actor.system,
source: actor.toObject(),
fields: actor.schema.fields,
systemFields: actor.system.schema.fields,
isEditable: this.isEditable,
isEditMode: this.isEditMode,
isPlayMode: this.isPlayMode,
isGM: game.user.isGM,
config: CONFIG.HAWKMOON,
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true }),
enrichedHabitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.habitat || "", { async: true }),
}
return context
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
// Activate drag & drop handlers
this.#dragDrop.forEach(d => d.bind(this.element))
// Manual tab navigation
const html = this.element
const tabLinks = html.querySelectorAll('a.item[data-tab]')
const tabContents = html.querySelectorAll('.tab[data-tab]')
// Hide all tabs initially
tabContents.forEach(tab => {
tab.classList.remove('active')
tab.style.display = 'none'
})
// Show active tab
const activeTab = this.tabGroups.primary
const activeTabContent = html.querySelector(`.tab[data-tab="${activeTab}"]`)
if (activeTabContent) {
activeTabContent.classList.add('active')
activeTabContent.style.display = 'block'
}
// Activate the corresponding nav link
tabLinks.forEach(link => {
if (link.dataset.tab === activeTab) {
link.classList.add('active')
} else {
link.classList.remove('active')
}
})
// Tab click handler
tabLinks.forEach(link => {
link.addEventListener('click', (event) => {
event.preventDefault()
const tab = link.dataset.tab
// Update state
this.tabGroups.primary = tab
// Hide all tabs
tabContents.forEach(t => {
t.classList.remove('active')
t.style.display = 'none'
})
// Show selected tab
const selectedTab = html.querySelector(`.tab[data-tab="${tab}"]`)
if (selectedTab) {
selectedTab.classList.add('active')
selectedTab.style.display = 'block'
}
// Update nav links
tabLinks.forEach(l => {
if (l.dataset.tab === tab) {
l.classList.add('active')
} else {
l.classList.remove('active')
}
})
})
})
// Inline item editing
html.querySelectorAll('.edit-item-data').forEach(input => {
input.addEventListener('change', (event) => {
const li = event.target.closest('.item')
const itemId = li.dataset.itemId
const itemType = li.dataset.itemType
const itemField = event.target.dataset.itemField
const dataType = event.target.dataset.dtype
const value = event.target.value
this.actor.editItemField(itemId, itemType, itemField, dataType, value)
})
})
}
// #region Drag & Drop
/**
* Create drag-and-drop workflow handlers for this Application
* @returns {DragDrop[]} An array of DragDrop handlers
* @private
*/
#createDragDropHandlers() {
return this.options.dragDrop.map((d) => {
d.permissions = {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this),
}
d.callbacks = {
dragstart: this._onDragStart.bind(this),
drop: this._onDrop.bind(this),
}
return new foundry.applications.ux.DragDrop(d)
})
}
/**
* Define whether a user is able to begin a dragstart workflow for a given drag selector
* @param {string} selector The candidate HTML selector for dragging
* @returns {boolean} Can the current user drag this selector?
* @protected
*/
_canDragStart(selector) {
return this.isEditable
}
/**
* Define whether a user is able to conclude a drag-and-drop workflow for a given drop selector
* @param {string} selector The candidate HTML selector for the drop target
* @returns {boolean} Can the current user drop on this selector?
* @protected
*/
_canDragDrop(selector) {
return this.isEditable
}
/**
* Callback actions which occur at the beginning of a drag start workflow.
* @param {DragEvent} event The originating DragEvent
* @protected
*/
_onDragStart(event) {
const li = event.currentTarget.closest(".item")
if (!li?.dataset.itemId) return
const item = this.actor.items.get(li.dataset.itemId)
if (!item) return
const dragData = item.toDragData()
event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
}
/**
* Callback actions which occur when a dragged element is dropped on a target.
* @param {DragEvent} event The originating DragEvent
* @protected
*/
async _onDrop(event) {
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
const actor = this.actor
// Handle different data types
switch (data.type) {
case "Item":
return this._onDropItem(event, data)
case "Actor":
return this._onDropActor(event, data)
case "ActiveEffect":
return this._onDropActiveEffect(event, data)
}
}
/**
* Handle dropping an Item on the actor sheet
* @param {DragEvent} event
* @param {object} data
* @private
*/
async _onDropItem(event, data) {
if (!this.actor.isOwner) return false
let item = await fromUuid(data.uuid)
if (item.pack) {
item = await HawkmoonUtility.searchItem(item)
}
const autoresult = HawkmoonAutomation.processAutomations("on-drop", item, this.actor)
if (autoresult.isValid) {
// In AppV2, we need to get the item data differently
const itemData = item.toObject ? item.toObject() : item
return this.actor.createEmbeddedDocuments("Item", [itemData])
} else {
ui.notifications.warn(autoresult.warningMessage)
return false
}
}
/**
* Handle dropping an Actor on the sheet
* @param {DragEvent} event
* @param {object} data
* @private
*/
async _onDropActor(event, data) {
// To be implemented by subclasses if needed
return false
}
/**
* Handle dropping an ActiveEffect on the sheet
* @param {DragEvent} event
* @param {object} data
* @private
*/
async _onDropActiveEffect(event, data) {
// To be implemented by subclasses if needed
return false
}
// #endregion
// #region Action Handlers
/**
* Toggle between edit and play mode
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static #onToggleSheet(event, target) {
console.log("Toggle sheet clicked", this)
const wasEditMode = this.isEditMode
console.log("Current mode:", this._sheetMode, "isEditMode:", wasEditMode, "isPlayMode:", this.isPlayMode)
this._sheetMode = wasEditMode ? this.constructor.SHEET_MODES.PLAY : this.constructor.SHEET_MODES.EDIT
console.log("New mode set to:", this._sheetMode, "(", wasEditMode ? "PLAY" : "EDIT", ")")
console.log("After change - isEditMode:", this.isEditMode, "isPlayMode:", this.isPlayMode)
this.render({ force: true })
}
/**
* Edit the actor image
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onEditImage(event, target) {
const fp = new FilePicker({
type: "image",
current: this.actor.img,
callback: (path) => {
this.actor.update({ img: path })
},
})
return fp.browse()
}
/**
* Edit an item
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onEditItem(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
if (!itemId) return
const item = this.actor.items.get(itemId)
if (item) item.sheet.render(true)
}
/**
* Delete an item
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onDeleteItem(event, target) {
const li = target.closest(".item")
await HawkmoonUtility.confirmDelete(this, li)
}
/**
* Create a new item
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onCreateItem(event, target) {
const itemType = target.dataset.type
await this.actor.createEmbeddedDocuments("Item", [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true })
}
/**
* Equip/unequip an item
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onEquipItem(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
if (itemId) {
await this.actor.equipItem(itemId)
this.render()
}
}
/**
* Modify item quantity
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onModifyQuantity(event, target) {
const li = target.closest(".item")
const itemId = li?.dataset.itemId
const value = Number(target.dataset.quantiteValue)
if (itemId) {
await this.actor.incDecQuantity(itemId, value)
}
}
/**
* Modify adversité
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onModifyAdversite(event, target) {
const li = target.closest(".item")
const adv = li?.dataset.adversite
const value = Number(target.dataset.adversiteValue)
if (adv) {
await this.actor.incDecAdversite(adv, value)
}
}
/**
* Roll initiative
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollInitiative(event, target) {
await this.actor.rollAttribut("adr", true)
}
/**
* Roll attribut
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollAttribut(event, target) {
const li = target.closest(".item")
const attrKey = li?.dataset.attrKey
if (attrKey) {
await this.actor.rollAttribut(attrKey, false)
}
}
/**
* Roll competence
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollCompetence(event, target) {
const li = target.closest(".item")
const attrKey = target.dataset.attrKey
const compId = li?.dataset.itemId
if (attrKey && compId) {
await this.actor.rollCompetence(attrKey, compId)
}
}
/**
* Roll arme offensif
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollArmeOffensif(event, target) {
const li = target.closest(".item")
const armeId = li?.dataset.itemId
if (armeId) {
await this.actor.rollArmeOffensif(armeId)
}
}
/**
* Roll arme degats
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollArmeDegats(event, target) {
const li = target.closest(".item")
const armeId = li?.dataset.itemId
if (armeId) {
await this.actor.rollArmeDegats(armeId)
}
}
/**
* Roll assommer
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollAssommer(event, target) {
await this.actor.rollAssommer()
}
/**
* Roll coup bas
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollCoupBas(event, target) {
await this.actor.rollCoupBas()
}
/**
* Roll immobiliser
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollImmobiliser(event, target) {
await this.actor.rollImmobiliser()
}
/**
* Roll repousser
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollRepousser(event, target) {
await this.actor.rollRepousser()
}
/**
* Roll désengager
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onRollDesengager(event, target) {
await this.actor.rollDesengager()
}
// #endregion
}

View File

@@ -0,0 +1,342 @@
const { HandlebarsApplicationMixin } = foundry.applications.api
export default class HawkmoonItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["fvtt-hawkmoon-cyd", "item"],
position: {
width: 620,
height: 600,
},
form: {
submitOnChange: true,
},
window: {
resizable: true,
},
tabs: [
{
navSelector: 'nav[data-group="primary"]',
contentSelector: "section.sheet-body",
initial: "description",
},
],
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
actions: {
editImage: HawkmoonItemSheet.#onEditImage,
postItem: HawkmoonItemSheet.#onPostItem,
addPredilection: HawkmoonItemSheet.#onAddPredilection,
deletePredilection: HawkmoonItemSheet.#onDeletePredilection,
addAutomation: HawkmoonItemSheet.#onAddAutomation,
deleteAutomation: HawkmoonItemSheet.#onDeleteAutomation,
},
}
/**
* Tab groups state
* @type {object}
*/
tabGroups = { primary: "description" }
/**
* Is the sheet currently in 'Play' mode?
* @type {boolean}
*/
/** @override */
async _prepareContext() {
const context = {
fields: this.document.schema.fields,
systemFields: this.document.system.schema.fields,
item: this.document,
system: this.document.system,
source: this.document.toObject(),
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }),
isEditMode: true,
isEditable: this.isEditable,
isGM: game.user.isGM,
config: CONFIG.HAWKMOON,
attributs: this.#getAttributs(),
}
return context
}
/** @override */
_onRender(context, options) {
super._onRender(context, options)
this.#dragDrop.forEach((d) => d.bind(this.element))
// Activate tab navigation manually
const nav = this.element.querySelector('nav.tabs[data-group]')
if (nav) {
const group = nav.dataset.group
// Activate the current tab
const activeTab = this.tabGroups[group] || "description"
nav.querySelectorAll('[data-tab]').forEach(link => {
const tab = link.dataset.tab
link.classList.toggle('active', tab === activeTab)
link.addEventListener('click', (event) => {
event.preventDefault()
this.tabGroups[group] = tab
this.render()
})
})
// Show/hide tab content
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
content.classList.toggle('active', content.dataset.tab === activeTab)
})
}
}
// #region Drag-and-Drop Workflow
/**
* Create drag-and-drop workflow handlers for this Application
* @returns {DragDrop[]} An array of DragDrop handlers
* @private
*/
#createDragDropHandlers() {
return this.options.dragDrop.map((d) => {
d.permissions = {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this),
}
d.callbacks = {
dragstart: this._onDragStart.bind(this),
dragover: this._onDragOver.bind(this),
drop: this._onDrop.bind(this),
}
return new foundry.applications.ux.DragDrop.implementation(d)
})
}
/**
* Can the User start a drag workflow for a given drag selector?
* @param {string} selector The candidate HTML selector for the drag event
* @returns {boolean} Can the current user drag this selector?
* @protected
*/
_canDragStart(selector) {
return this.isEditable
}
/**
* Can the User drop an entry at a given drop selector?
* @param {string} selector The candidate HTML selector for the drop event
* @returns {boolean} Can the current user drop on this selector?
* @protected
*/
_canDragDrop(selector) {
return this.isEditable
}
/**
* Callback for dragstart events.
* @param {DragEvent} event The drag start event
* @protected
*/
_onDragStart(event) {
const target = event.currentTarget
const dragData = { type: "Item", uuid: this.document.uuid }
event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
}
/**
* Callback for dragover events.
* @param {DragEvent} event The drag over event
* @protected
*/
_onDragOver(event) {
// Default behavior is fine
}
/**
* Callback for drop events.
* @param {DragEvent} event The drop event
* @protected
*/
async _onDrop(event) {
const data = TextEditor.getDragEventData(event)
const item = await fromUuid(data.uuid)
if (!item) return
// Handle drop logic here if needed
console.log("Item dropped:", item)
}
// #endregion
// #region Action Handlers
/**
* Edit the item image
* @param {Event} event The triggering event
* @param {HTMLElement} target The target element
* @private
*/
static async #onEditImage(event, target) {
const fp = new foundry.applications.ui.FilePicker({
type: "image",
current: this.document.img,
callback: (path) => {
this.document.update({ img: path })
},
})
return fp.browse()
}
/**
* Post item to chat
* @param {Event} event The triggering event
* @param {HTMLElement} target The target element
* @private
*/
static async #onPostItem(event, target) {
let chatData = foundry.utils.duplicate(this.document)
if (this.document.actor) {
chatData.actor = { id: this.document.actor.id }
}
// Don't post any image for the item if the default image is used
if (chatData.img.includes("/blank.png")) {
chatData.img = null
}
// JSON object for easy creation
chatData.jsondata = JSON.stringify({
compendium: "postedItem",
payload: chatData,
})
const html = await renderTemplate('systems/fvtt-hawkmoon-cyd/templates/post-item.hbs', chatData)
const chatOptions = {
user: game.user.id,
content: html,
}
ChatMessage.create(chatOptions)
}
/**
* Add a predilection
* @param {Event} event The triggering event
* @param {HTMLElement} target The target element
* @private
*/
static async #onAddPredilection(event, target) {
let pred = foundry.utils.duplicate(this.document.system.predilections || [])
pred.push({ name: "Nouvelle prédilection", id: foundry.utils.randomID(16), used: false, acquise: false, maitrise: false, description: "" })
await this.document.update({ 'system.predilections': pred })
}
/**
* Delete a predilection
* @param {Event} event The triggering event
* @param {HTMLElement} target The target element
* @private
*/
static async #onDeletePredilection(event, target) {
const index = parseInt(target.closest("[data-predilection-index]").dataset.predilectionIndex)
let pred = foundry.utils.duplicate(this.document.system.predilections)
pred.splice(index, 1)
await this.document.update({ 'system.predilections': pred })
}
/**
* Add an automation
* @param {Event} event The triggering event
* @param {HTMLElement} target The target element
* @private
*/
static async #onAddAutomation(event, target) {
let autom = foundry.utils.duplicate(this.document.system.automations || [])
autom.push({
eventtype: "on-drop",
name: "Automatisation 1",
bonusname: "vigueur",
bonus: 0,
competence: "",
minLevel: 0,
baCost: 0,
id: foundry.utils.randomID(16)
})
await this.document.update({ 'system.automations': autom })
}
/**
* Delete an automation
* @param {Event} event The triggering event
* @param {HTMLElement} target The target element
* @private
*/
static async #onDeleteAutomation(event, target) {
const index = parseInt(target.closest("[data-automation-index]").dataset.automationIndex)
let autom = foundry.utils.duplicate(this.document.system.automations)
autom.splice(index, 1)
await this.document.update({ 'system.automations': autom })
}
// #endregion
// #region Helper Methods
/**
* Get list of attributs
* @returns {Object}
* @private
*/
#getAttributs() {
return {
"adr": "Adresse",
"pui": "Puissance",
"cla": "Clairvoyance",
"pre": "Présence",
"tre": "Trempe"
}
}
// #endregion
/** @override */
_onChangeForm(formConfig, event) {
// Handle special form changes
const target = event.target
// Handle predilection field changes
if (target.classList.contains('edit-predilection') ||
target.classList.contains('edit-predilection-description') ||
target.classList.contains('predilection-acquise') ||
target.classList.contains('predilection-maitrise') ||
target.classList.contains('predilection-used')) {
const li = target.closest('.prediction-item')
if (li) {
const index = parseInt(li.dataset.predictionIndex)
const field = target.classList.contains('edit-predilection') ? 'name' :
target.classList.contains('edit-predilection-description') ? 'description' :
target.classList.contains('predilection-acquise') ? 'acquise' :
target.classList.contains('predilection-maitrise') ? 'maitrise' : 'used'
let pred = foundry.utils.duplicate(this.document.system.predilections)
if (target.type === 'checkbox') {
pred[index][field] = target.checked
} else {
pred[index][field] = target.value
}
pred[index].id = pred[index].id || foundry.utils.randomID(16)
this.document.update({ 'system.predilections': pred })
return
}
}
// Handle automation field changes
if (target.classList.contains('automation-edit-field')) {
const index = parseInt(target.dataset.automationIndex)
const field = target.dataset.automationField
let auto = foundry.utils.duplicate(this.document.system.automations)
auto[index][field] = target.value
auto[index].id = auto[index].id || foundry.utils.randomID(16)
this.document.update({ 'system.automations': auto })
return
}
super._onChangeForm(formConfig, event)
}
}

View File

@@ -0,0 +1,142 @@
import HawkmoonActorSheet from "./base-actor-sheet.mjs"
const __ALLOWED_ITEM_CELLULE = { talent: 1, ressource: 1, contact: 1, equipement: 1, protection: 1, artefact: 1, arme: 1, monnaie: 1 }
export default class HawkmoonCelluleSheet extends HawkmoonActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
window: {
...super.DEFAULT_OPTIONS.window,
title: "SHEETS.Actor.cellule",
},
actions: {
...super.DEFAULT_OPTIONS.actions,
editActor: HawkmoonCelluleSheet.#onEditActor,
deleteActor: HawkmoonCelluleSheet.#onDeleteActor,
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/fvtt-hawkmoon-cyd/templates/cellule-sheet.hbs",
},
}
/** @override */
tabGroups = { primary: "talents" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
// Add cellule-specific data
context.talents = foundry.utils.duplicate(actor.getTalents() || {})
context.ressources = foundry.utils.duplicate(actor.getRessources ? actor.getRessources() : [])
context.contacts = foundry.utils.duplicate(actor.getContacts ? actor.getContacts() : [])
context.members = this.#getMembers()
context.equipements = foundry.utils.duplicate(actor.getEquipments ? actor.getEquipments() : [])
context.artefacts = foundry.utils.duplicate(actor.getArtefacts ? actor.getArtefacts() : [])
context.armes = foundry.utils.duplicate(actor.getWeapons ? actor.getWeapons() : [])
context.monnaies = foundry.utils.duplicate(actor.getMonnaies ? actor.getMonnaies() : [])
context.protections = foundry.utils.duplicate(actor.getArmors ? actor.getArmors() : [])
context.richesse = actor.computeRichesse ? actor.computeRichesse() : 0
context.valeurEquipement = actor.computeValeurEquipement ? actor.computeValeurEquipement() : 0
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.description || "", { async: true })
return context
}
/**
* Get members of the cellule with full actor data
* @returns {Array}
* @private
*/
#getMembers() {
let membersFull = []
for (let memberId of this.actor.system.members) {
let actor = game.actors.get(memberId)
if (actor) {
membersFull.push({ name: actor.name, id: actor.id, img: actor.img })
}
}
return membersFull
}
/**
* Override _onDropItem to filter allowed item types for cellule
* @override
*/
async _onDropItem(event, data) {
const item = await fromUuid(data.uuid)
// Check if item type is allowed for cellule
if (!__ALLOWED_ITEM_CELLULE[item.type]) {
ui.notifications.warn(`Le type d'item ${item.type} n'est pas autorisé pour une cellule`)
return false
}
return super._onDropItem(event, data)
}
/**
* Override _onDropActor to handle adding members
* @override
*/
async _onDropActor(event, data) {
const droppedActor = await fromUuid(data.uuid)
if (droppedActor.type !== "personnage") {
ui.notifications.warn("Seuls les personnages peuvent être ajoutés à une cellule")
return false
}
// Check if already a member
const isMember = this.actor.system.members.includes(droppedActor.id)
if (isMember) {
ui.notifications.warn("Ce personnage est déjà membre de cette cellule")
return false
}
// Add member ID
const members = [...this.actor.system.members, droppedActor.id]
await this.actor.update({ "system.members": members })
return true
}
// #region Cellule-specific Actions
/**
* Edit an actor (member)
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onEditActor(event, target) {
const li = target.closest(".item")
const actorId = li?.dataset.actorId
if (!actorId) return
const actor = game.actors.get(actorId)
if (actor) actor.sheet.render(true)
}
/**
* Delete an actor (remove member)
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onDeleteActor(event, target) {
const li = target.closest(".item")
const actorId = li?.dataset.actorId
if (actorId) {
const members = this.actor.system.members.filter(id => id !== actorId)
await this.actor.update({ "system.members": members })
}
}
// #endregion
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonCompetenceSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["competence"],
position: {
width: 620,
},
window: {
contentClasses: ["competence-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-competence-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonContactSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["contact"],
position: {
width: 620,
},
window: {
contentClasses: ["contact-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-contact-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,60 @@
import HawkmoonActorSheet from "./base-actor-sheet.mjs"
import { HawkmoonUtility } from "../../hawkmoon-utility.js"
export default class HawkmoonCreatureSheet extends HawkmoonActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
window: {
...super.DEFAULT_OPTIONS.window,
title: "SHEETS.Actor.creature",
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/fvtt-hawkmoon-cyd/templates/creature-sheet.hbs",
},
}
/** @override */
tabGroups = { primary: "principal" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
// Add creature-specific data
context.skills = actor.getSkills ? actor.getSkills() : []
context.armes = foundry.utils.duplicate(actor.getWeapons ? actor.getWeapons() : [])
context.protections = foundry.utils.duplicate(actor.getArmors ? actor.getArmors() : [])
context.combat = actor.getCombatValues ? actor.getCombatValues() : {}
context.equipements = foundry.utils.duplicate(actor.getEquipments ? actor.getEquipments() : [])
context.talents = foundry.utils.duplicate(actor.getTalents ? actor.getTalents() : [])
context.talentsCell = this.#getCelluleTalents()
context.nbCombativite = actor.system.sante?.nbcombativite || 0
context.combativiteList = HawkmoonUtility.getCombativiteList(actor.system.sante?.nbcombativite || 0)
context.initiative = actor.getFlag("world", "last-initiative") || -1
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true })
context.enrichedHabitat = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.habitat || "", { async: true })
return context
}
/**
* Get talents from attached cellule
* @private
*/
#getCelluleTalents() {
const celluleId = this.actor.system?.details?.celluleid
if (!celluleId) return []
const cellule = game.actors.get(celluleId)
if (!cellule) return []
return foundry.utils.duplicate(cellule.getTalents?.() || [])
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonEquipementSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["equipement"],
position: {
width: 620,
},
window: {
contentClasses: ["equipement-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-equipement-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonHistoriqueSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["historique"],
position: {
width: 620,
},
window: {
contentClasses: ["historique-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-historique-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonMonnaieSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["monnaie"],
position: {
width: 620,
},
window: {
contentClasses: ["monnaie-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-monnaie-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonMutationSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["mutation"],
position: {
width: 620,
},
window: {
contentClasses: ["mutation-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-mutation-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,104 @@
import HawkmoonActorSheet from "./base-actor-sheet.mjs"
import { HawkmoonUtility } from "../../hawkmoon-utility.js"
export default class HawkmoonPersonnageSheet extends HawkmoonActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes],
window: {
...super.DEFAULT_OPTIONS.window,
title: "SHEETS.Actor.personnage",
},
actions: {
...super.DEFAULT_OPTIONS.actions,
openCellule: HawkmoonPersonnageSheet.#onOpenCellule,
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/fvtt-hawkmoon-cyd/templates/actor-sheet.hbs",
},
}
/** @override */
tabGroups = { primary: "principal" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
const actor = this.document
// Add personnage-specific data
context.skills = actor.getSkills()
context.armes = foundry.utils.duplicate(actor.getWeapons())
context.monnaies = foundry.utils.duplicate(actor.getMonnaies())
context.protections = foundry.utils.duplicate(actor.getArmors())
context.historiques = foundry.utils.duplicate(actor.getHistoriques() || [])
context.talents = foundry.utils.duplicate(actor.getTalents() || [])
context.mutations = foundry.utils.duplicate(actor.getMutations() || [])
context.talentsCell = this.#getCelluleTalents()
context.celluleId = this.#getCelluleId()
context.profils = foundry.utils.duplicate(actor.getProfils() || [])
context.combat = actor.getCombatValues()
context.equipements = foundry.utils.duplicate(actor.getEquipments())
context.artefacts = foundry.utils.duplicate(actor.getArtefacts())
context.richesse = actor.computeRichesse()
context.coupDevastateur = actor.items.find(it => it.type == "talent" && it.name.toLowerCase() == "coup devastateur" && !it.system.used)
context.valeurEquipement = actor.computeValeurEquipement()
context.nbCombativite = actor.system.sante.nbcombativite
context.combativiteList = HawkmoonUtility.getCombativiteList(actor.system.sante.nbcombativite)
context.initiative = actor.getFlag("world", "last-initiative") || -1
return context
}
/**
* Get talents from cellules this actor is a member of
* @returns {Array}
* @private
*/
#getCelluleTalents() {
let talents = []
for (let cellule of game.actors) {
if (cellule.type == "cellule") {
let found = cellule.system.members.includes(this.actor.id)
if (found) {
talents = talents.concat(cellule.getTalents())
}
}
}
return talents
}
/**
* Get the ID of the cellule this actor is a member of
* @returns {string|null}
* @private
*/
#getCelluleId() {
for (let cellule of game.actors) {
if (cellule.type == "cellule") {
if (cellule.system.members.includes(this.actor.id)) {
return cellule.id
}
}
}
return null
}
/**
* Open cellule sheet
* @param {Event} event
* @param {HTMLElement} target
* @private
*/
static async #onOpenCellule(event, target) {
const celluleId = target.dataset.celluleId
if (!celluleId) return
const cellule = game.actors.get(celluleId)
if (cellule) cellule.sheet.render(true)
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonProfilSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["profil"],
position: {
width: 620,
},
window: {
contentClasses: ["profil-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-profil-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonProtectionSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["protection"],
position: {
width: 620,
},
window: {
contentClasses: ["protection-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-protection-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,21 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonRessourceSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["ressource"],
position: {
width: 620,
},
window: {
contentClasses: ["ressource-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-ressource-sheet.hbs",
},
}
}

View File

@@ -0,0 +1,51 @@
import HawkmoonItemSheet from "./base-item-sheet.mjs"
export default class HawkmoonTalentSheet extends HawkmoonItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["talent"],
position: {
width: 620,
},
window: {
contentClasses: ["talent-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-hawkmoon-cyd/templates/item-talent-sheet.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "Détails" },
description: { id: "description", group: "primary", label: "Description" }
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
return context
}
}

View File

@@ -1,6 +1,6 @@
/* -------------------------------------------- */ /* -------------------------------------------- */
import { HawkmoonUtility } from "./hawkmoon-utility.js"; import { HawkmoonUtility } from "./hawkmoon-utility.js";
import { HawkmoonRollDialog } from "./hawkmoon-roll-dialog.js"; import { HawkmoonRollDialog } from "./applications/hawkmoon-roll-dialog.mjs";
/* -------------------------------------------- */ /* -------------------------------------------- */
const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10] const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10]
@@ -655,8 +655,7 @@ export class HawkmoonActor extends Actor {
let rollData = this.getCommonRollData(attrKey) let rollData = this.getCommonRollData(attrKey)
rollData.multiplier = (isInit) ? 1 : 2 rollData.multiplier = (isInit) ? 1 : 2
rollData.isInit = isInit rollData.isInit = isInit
let rollDialog = await HawkmoonRollDialog.create(this, rollData) await HawkmoonRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -664,22 +663,30 @@ export class HawkmoonActor extends Actor {
let rollData = this.getCommonRollData(attrKey, compId) let rollData = this.getCommonRollData(attrKey, compId)
rollData.multiplier = 1 // Attr multiplier, always 1 in competence mode rollData.multiplier = 1 // Attr multiplier, always 1 in competence mode
console.log("RollDatra", rollData) console.log("RollDatra", rollData)
let rollDialog = await HawkmoonRollDialog.create(this, rollData) await HawkmoonRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollArmeOffensif(armeId) { async rollArmeOffensif(armeId) {
let arme = this.items.get(armeId) let arme = this.items.get(armeId)
if (!arme.system.equipped) {
ui.notifications.warn("Cette arme doit être équipée pour pouvoir attaquer !")
return
}
if (arme.type == "arme") { if (arme.type == "arme") {
arme = this.prepareArme(arme) arme = this.prepareArme(arme)
} }
if (!arme.system.competence) {
ui.notifications.warn("Aucune compétence trouvée pour cette arme !")
return
}
let rollData = this.getCommonRollData(arme.system.attrKey, arme.system.competence._id) let rollData = this.getCommonRollData(arme.system.attrKey, arme.system.competence._id)
rollData.arme = arme rollData.arme = arme
rollData.isCombat = true
rollData.isTir = arme.system.isDistance || false
HawkmoonUtility.updateWithTarget(rollData) HawkmoonUtility.updateWithTarget(rollData)
console.log("ARME!", rollData) console.log("ARME!", rollData)
let rollDialog = await HawkmoonRollDialog.create(this, rollData) await HawkmoonRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollAssommer() { async rollAssommer() {
@@ -687,8 +694,7 @@ export class HawkmoonActor extends Actor {
rollData.assomer = true rollData.assomer = true
rollData.conditionsCommunes = true rollData.conditionsCommunes = true
HawkmoonUtility.updateWithTarget(rollData) HawkmoonUtility.updateWithTarget(rollData)
let rollDialog = await HawkmoonRollDialog.create(this, rollData) await HawkmoonRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollCoupBas() { async rollCoupBas() {
@@ -696,8 +702,7 @@ export class HawkmoonActor extends Actor {
rollData.coupBas = true rollData.coupBas = true
rollData.conditionsCommunes = true rollData.conditionsCommunes = true
HawkmoonUtility.updateWithTarget(rollData) HawkmoonUtility.updateWithTarget(rollData)
let rollDialog = await HawkmoonRollDialog.create(this, rollData) await HawkmoonRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollImmobiliser() { async rollImmobiliser() {
@@ -706,8 +711,7 @@ export class HawkmoonActor extends Actor {
rollData.conditionsCommunes = true rollData.conditionsCommunes = true
rollData.cibleconsciente = true rollData.cibleconsciente = true
HawkmoonUtility.updateWithTarget(rollData) HawkmoonUtility.updateWithTarget(rollData)
let rollDialog = await HawkmoonRollDialog.create(this, rollData) await HawkmoonRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollRepousser() { async rollRepousser() {
@@ -716,8 +720,7 @@ export class HawkmoonActor extends Actor {
rollData.conditionsCommunes = true rollData.conditionsCommunes = true
rollData.cibleconsciente = true rollData.cibleconsciente = true
HawkmoonUtility.updateWithTarget(rollData) HawkmoonUtility.updateWithTarget(rollData)
let rollDialog = await HawkmoonRollDialog.create(this, rollData) await HawkmoonRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollDesengager() { async rollDesengager() {
@@ -725,13 +728,16 @@ export class HawkmoonActor extends Actor {
rollData.desengager = true rollData.desengager = true
rollData.conditionsCommunes = true rollData.conditionsCommunes = true
HawkmoonUtility.updateWithTarget(rollData) HawkmoonUtility.updateWithTarget(rollData)
let rollDialog = await HawkmoonRollDialog.create(this, rollData) await HawkmoonRollDialog.create(this, rollData)
rollDialog.render(true)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollArmeDegats(armeId, targetVigueur = undefined, rollDataInput = undefined) { async rollArmeDegats(armeId, targetVigueur = undefined, rollDataInput = undefined) {
let arme = this.items.get(armeId) let arme = this.items.get(armeId)
if (!arme.system.equipped) {
ui.notifications.warn("Cette arme doit être équipée pour pouvoir infliger des dégâts !")
return
}
if (arme.type == "arme") { if (arme.type == "arme") {
arme = this.prepareArme(arme) arme = this.prepareArme(arme)
} }
@@ -782,7 +788,7 @@ export class HawkmoonActor extends Actor {
nbEtatPerdus: nbEtatPerdus nbEtatPerdus: nbEtatPerdus
} }
HawkmoonUtility.createChatWithRollMode(rollData.alias, { HawkmoonUtility.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-hawkmoon-cyd/templates/chat-degats-result.html`, rollData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-hawkmoon-cyd/templates/chat-degats-result.hbs`, rollData)
}) })
if (rollDataInput?.defenderTokenId && nbEtatPerdus) { if (rollDataInput?.defenderTokenId && nbEtatPerdus) {

View File

@@ -1,7 +1,7 @@
/* -------------------------------------------- */ /* -------------------------------------------- */
import { HawkmoonUtility } from "./hawkmoon-utility.js"; import { HawkmoonUtility } from "./hawkmoon-utility.js";
import { HawkmoonRollDialog } from "./hawkmoon-roll-dialog.js"; import { HawkmoonRollDialog } from "./applications/hawkmoon-roll-dialog.mjs";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class HawkmoonCommands { export class HawkmoonCommands {
@@ -106,18 +106,17 @@ export class HawkmoonCommands {
static _chatAnswer(msg, content) { static _chatAnswer(msg, content) {
msg.whisper = [game.user.id]; msg.whisper = [game.user.id];
msg.content = content; msg.content = content;
ChatMessage.create(msg); ChatMessage.create(msg);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async poolRoll( msg) { async poolRoll( msg) {
let rollData = HawkmoonUtility.getBasicRollData() let rollData = HawkmoonUtility.getBasicRollData()
rollData.alias = "Dice Pool Roll", rollData.alias = "Dice Pool Roll",
rollData.mode = "generic" rollData.mode = "generic"
rollData.title = `Dice Pool Roll`; rollData.title = `Dice Pool Roll`;
let rollDialog = await HawkmoonRollDialog.create( this, rollData); await HawkmoonRollDialog.create( this, rollData);
rollDialog.render( true );
} }
} }

View File

@@ -24,7 +24,7 @@ export class HawkmoonTokenHud {
const controlIconActions = $(html).find('.control-icon[data-action=combat]'); const controlIconActions = $(html).find('.control-icon[data-action=combat]');
// initiative // initiative
await HawkmoonTokenHud._configureSubMenu(controlIconActions, 'systems/fvtt-hawkmoon-cyd/templates/hud-adversites.html', hudData, await HawkmoonTokenHud._configureSubMenu(controlIconActions, 'systems/fvtt-hawkmoon-cyd/templates/hud-adversites.hbs', hudData,
(event) => { (event) => {
let adversite = event.currentTarget.attributes['data-action-index'].value let adversite = event.currentTarget.attributes['data-action-index'].value
let value = Number(event.currentTarget.attributes['data-action-value'].value) let value = Number(event.currentTarget.attributes['data-action-value'].value)

View File

@@ -9,10 +9,7 @@
/* -------------------------------------------- */ /* -------------------------------------------- */
// Import Modules // Import Modules
import { HawkmoonActor } from "./hawkmoon-actor.js"; import { HawkmoonActor } from "./hawkmoon-actor.js";
import { HawkmoonItemSheet } from "./hawkmoon-item-sheet.js";
import { HawkmoonActorSheet } from "./hawkmoon-actor-sheet.js";
import { HawkmoonCreatureSheet } from "./hawkmoon-creature-sheet.js";
import { HawkmoonCelluleSheet } from "./hawkmoon-cellule-sheet.js";
import { HawkmoonUtility } from "./hawkmoon-utility.js"; import { HawkmoonUtility } from "./hawkmoon-utility.js";
import { HawkmoonCombat } from "./hawkmoon-combat.js"; import { HawkmoonCombat } from "./hawkmoon-combat.js";
import { HawkmoonItem } from "./hawkmoon-item.js"; import { HawkmoonItem } from "./hawkmoon-item.js";
@@ -20,6 +17,12 @@ import { HawkmoonAutomation } from "./hawkmoon-automation.js";
import { HawkmoonTokenHud } from "./hawkmoon-hud.js"; import { HawkmoonTokenHud } from "./hawkmoon-hud.js";
import { HAWKMOON_CONFIG } from "./hawkmoon-config.js"; import { HAWKMOON_CONFIG } from "./hawkmoon-config.js";
// Import DataModels
import * as models from "./models/index.mjs";
// Import AppV2 Item Sheets
import * as sheets from "./applications/sheets/_module.mjs";
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Foundry VTT Initialization */ /* Foundry VTT Initialization */
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -48,22 +51,59 @@ Hooks.once("init", async function () {
// Define custom Entity classes // Define custom Entity classes
CONFIG.Combat.documentClass = HawkmoonCombat CONFIG.Combat.documentClass = HawkmoonCombat
CONFIG.Actor.documentClass = HawkmoonActor CONFIG.Actor.documentClass = HawkmoonActor
CONFIG.Actor.dataModels = {
personnage: models.PersonnageDataModel,
cellule: models.CelluleDataModel,
creature: models.CreatureDataModel
}
CONFIG.Item.documentClass = HawkmoonItem CONFIG.Item.documentClass = HawkmoonItem
CONFIG.Item.dataModels = {
talent: models.TalentDataModel,
historique: models.HistoriqueDataModel,
profil: models.ProfilDataModel,
competence: models.CompetenceDataModel,
arme: models.ArmeDataModel,
protection: models.ProtectionDataModel,
monnaie: models.MonnaieDataModel,
equipement: models.EquipementDataModel,
artefact: models.ArtefactDataModel,
ressource: models.RessourceDataModel,
contact: models.ContactDataModel,
mutation: models.MutationDataModel
}
CONFIG.HAWKMOON = HAWKMOON_CONFIG
game.system.hawkmoon = { game.system.hawkmoon = {
HawkmoonUtility, HawkmoonUtility,
HawkmoonAutomation, HawkmoonAutomation,
config : HAWKMOON_CONFIG config: HAWKMOON_CONFIG,
models,
sheets
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
// Regster sheet application classes // Register sheet application classes
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet); foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
foundry.documents.collections.Actors.registerSheet("fvtt-hawkmoon-cyd", HawkmoonActorSheet, { types: ["personnage"], makeDefault: true }) foundry.documents.collections.Actors.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonPersonnageSheet, { types: ["personnage"], makeDefault: true })
foundry.documents.collections.Actors.registerSheet("fvtt-hawkmoon-cyd", HawkmoonCreatureSheet, { types: ["creature"], makeDefault: true }) foundry.documents.collections.Actors.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonCreatureSheet, { types: ["creature"], makeDefault: true })
foundry.documents.collections.Actors.registerSheet("fvtt-hawkmoon-cyd", HawkmoonCelluleSheet, { types: ["cellule"], makeDefault: true }); foundry.documents.collections.Actors.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonCelluleSheet, { types: ["cellule"], makeDefault: true });
// Register AppV2 Item Sheets
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet); foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", HawkmoonItemSheet, { makeDefault: true }) foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonTalentSheet, { types: ["talent"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonCompetenceSheet, { types: ["competence"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonArmeSheet, { types: ["arme"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonProtectionSheet, { types: ["protection"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonHistoriqueSheet, { types: ["historique"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonProfilSheet, { types: ["profil"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonEquipementSheet, { types: ["equipement"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonMonnaieSheet, { types: ["monnaie"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonArtefactSheet, { types: ["artefact"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonRessourceSheet, { types: ["ressource"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonContactSheet, { types: ["contact"], makeDefault: true })
foundry.documents.collections.Items.registerSheet("fvtt-hawkmoon-cyd", sheets.HawkmoonMutationSheet, { types: ["mutation"], makeDefault: true })
HawkmoonUtility.init() HawkmoonUtility.init()
HawkmoonAutomation.init() HawkmoonAutomation.init()
@@ -76,12 +116,39 @@ function welcomeMessage() {
ChatMessage.create({ ChatMessage.create({
user: game.user.id, user: game.user.id,
whisper: [game.user.id], whisper: [game.user.id],
content: `<div id="welcome-message-Hawkmoon"><span class="rdd-roll-part"> content: `
<strong>Bienvenue dans Hawkmoon et le troisième Millénaire !</strong> <div class="hawkmoon-chat-result">
<p>Les livres de Hawkmoon sont nécessaires pour jouer : https://www.titam-france.fr</p> <div class="chat-result-header">
<p>Hawkmoon est jeu de rôle publié par Titam France/Sombres projets, tout les droits leur appartiennent.</p> <img class="actor-icon" src="systems/fvtt-hawkmoon-cyd/assets/logos/hawkmoon_logo.webp" alt="Hawkmoon" />
<p>Système développé par LeRatierBretonnien, support sur le <a href="https://discord.gg/pPSDNJk">Discord FR de Foundry</a>.</p> <div class="header-info">
` }); <h4 class="actor-name">Bienvenue dans Hawkmoon !</h4>
<div class="action-title">
<i class="fas fa-book-open"></i>
Le Troisième Millénaire
</div>
</div>
</div>
<div class="result-details">
<div class="details-section">
<h5 class="section-title"><i class="fas fa-info-circle"></i> Informations importantes</h5>
<div class="description-content">
<p><strong>Les livres de Hawkmoon sont nécessaires pour jouer.</strong></p>
<p>Hawkmoon est un jeu de rôle publié par <strong>Titam France / Sombres projets</strong>, tous les droits leur appartiennent.</p>
</div>
</div>
<div class="details-section">
<h5 class="section-title"><i class="fas fa-users"></i> Support & Communauté</h5>
<div class="description-content">
<p>Système développé par <strong>LeRatierBretonnien</strong></p>
<p>Support disponible sur le <a href="https://discord.gg/pPSDNJk" target="_blank" style="color: #d4af37; text-decoration: underline;">Discord FR de Foundry VTT</a></p>
</div>
</div>
</div>
</div>
`
});
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -113,10 +180,10 @@ Hooks.once("ready", function () {
}); });
} }
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter=>{ import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
console.log("ClassCounter loaded", moduleCounter) console.log("ClassCounter loaded", moduleCounter)
moduleCounter.ClassCounter.registerUsageCount() moduleCounter.ClassCounter.registerUsageCount()
}).catch(err=> }).catch(err =>
console.log("No stats available, giving up.") console.log("No stats available, giving up.")
) )

View File

@@ -104,15 +104,33 @@ export class HawkmoonUtility {
return this.skills return this.skills
} }
/* -------------------------------------------- */
static updatePauseLogo(html) {
let logoPause = "systems/fvtt-hawkmoon-cyd/assets/logos/" + game.settings.get("fvtt-hawkmoon-cyd", "hawkmoon-pause-logo") + ".webp"
console.log("Hawkmoon | Updating pause logo to:", logoPause)
// Supprimer l'ancien style s'il existe
let oldStyle = document.getElementById('hawkmoon-pause-logo-override')
if (oldStyle) {
oldStyle.remove()
}
// Injecter une règle CSS pour override le logo
let styleSheet = document.createElement('style')
styleSheet.id = 'hawkmoon-pause-logo-override'
styleSheet.textContent = `#pause>img { content: url(${logoPause}) !important; }`
document.head.appendChild(styleSheet)
console.log("Hawkmoon | Logo CSS rule injected")
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static async ready() { static async ready() {
const skills = await HawkmoonUtility.loadCompendium("fvtt-hawkmoon-cyd.skills") const skills = await HawkmoonUtility.loadCompendium("fvtt-hawkmoon-cyd.skills")
this.skills = skills.map(i => i.toObject()) this.skills = skills.map(i => i.toObject())
// Setup pause logo // Setup pause logo
let logoPause = "systems/fvtt-hawkmoon-cyd/assets/logos/" + game.settings.get("fvtt-hawkmoon-cyd", "hawkmoon-pause-logo") + ".webp" this.updatePauseLogo()
let logoImg = document.querySelector('#pause').children[0]
logoImg.setAttribute('style', `content: url(${logoPause})`)
game.system.hawkmoon.config.listeNiveauSkill = this.createDirectOptionList(0, 10) game.system.hawkmoon.config.listeNiveauSkill = this.createDirectOptionList(0, 10)
game.system.hawkmoon.config.listeNiveauCreature = this.createDirectOptionList(0, 35) game.system.hawkmoon.config.listeNiveauCreature = this.createDirectOptionList(0, 35)
@@ -187,13 +205,13 @@ export class HawkmoonUtility {
static async preloadHandlebarsTemplates() { static async preloadHandlebarsTemplates() {
const templatePaths = [ const templatePaths = [
'systems/fvtt-hawkmoon-cyd/templates/editor-notes-gm.html', 'systems/fvtt-hawkmoon-cyd/templates/editor-notes-gm.hbs',
'systems/fvtt-hawkmoon-cyd/templates/partial-item-header.html', 'systems/fvtt-hawkmoon-cyd/templates/partial-item-header.hbs',
'systems/fvtt-hawkmoon-cyd/templates/partial-item-description.html', 'systems/fvtt-hawkmoon-cyd/templates/partial-item-description.hbs',
'systems/fvtt-hawkmoon-cyd/templates/partial-item-nav.html', 'systems/fvtt-hawkmoon-cyd/templates/partial-item-nav.hbs',
'systems/fvtt-hawkmoon-cyd/templates/partial-item-prix.html', 'systems/fvtt-hawkmoon-cyd/templates/partial-item-prix.hbs',
'systems/fvtt-hawkmoon-cyd/templates/partial-automation.html', 'systems/fvtt-hawkmoon-cyd/templates/partial-automation.hbs',
'systems/fvtt-hawkmoon-cyd/templates/hud-adversites.html', 'systems/fvtt-hawkmoon-cyd/templates/hud-adversites.hbs',
] ]
return foundry.applications.handlebars.loadTemplates(templatePaths); return foundry.applications.handlebars.loadTemplates(templatePaths);
} }
@@ -247,11 +265,14 @@ export class HawkmoonUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static getTarget() { static getTarget() {
if (game.user.targets && game.user.targets.size == 1) { console.log("getTarget - targets size:", game.user.targets?.size)
if (game.user.targets && game.user.targets.size >= 1) {
for (let target of game.user.targets) { for (let target of game.user.targets) {
return target; console.log("getTarget - Returning target:", target.id)
return target; // Prendre la première cible si plusieurs
} }
} }
console.log("getTarget - No target found")
return undefined; return undefined;
} }
@@ -425,11 +446,10 @@ export class HawkmoonUtility {
} }
} }
} }
rollData.diceFormula += `+${rollData.bonusMalusContext}`
} else if (rollData.attr2) { } else if (rollData.attr2) {
rollData.diceFormula += `+${rollData.attr.value}+${rollData.attr2.value}+${rollData.modificateur}+${rollData.bonusMalusContext}` rollData.diceFormula += `+${rollData.attr.value}+${rollData.attr2.value}+${rollData.modificateur}`
} else { } else {
rollData.diceFormula += `+${rollData.attr.value}*${rollData.multiplier}+${rollData.modificateur}+${rollData.bonusMalusContext}` rollData.diceFormula += `+${rollData.attr.value}*${rollData.multiplier}+${rollData.modificateur}`
} }
// Bonus arme naturelle en défense // Bonus arme naturelle en défense
@@ -508,7 +528,7 @@ export class HawkmoonUtility {
} }
this.createChatWithRollMode(rollData.alias, { this.createChatWithRollMode(rollData.alias, {
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-hawkmoon-cyd/templates/chat-generic-result.html`, rollData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-hawkmoon-cyd/templates/chat-generic-result.hbs`, rollData)
}, rollData) }, rollData)
if ((rollData.coupBas || rollData.arme) && rollData.isSuccess && rollData.defenderTokenId) { if ((rollData.coupBas || rollData.arme) && rollData.isSuccess && rollData.defenderTokenId) {
@@ -550,7 +570,7 @@ export class HawkmoonUtility {
this.computeResultQuality(rollData) this.computeResultQuality(rollData)
this.createChatWithRollMode(rollData.alias, { this.createChatWithRollMode(rollData.alias, {
content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-hawkmoon-cyd/templates/chat-generic-result.html`, rollData) content: await foundry.applications.handlebars.renderTemplate(`systems/fvtt-hawkmoon-cyd/templates/chat-generic-result.hbs`, rollData)
}, rollData) }, rollData)
} }
@@ -633,9 +653,13 @@ export class HawkmoonUtility {
break; break;
} }
chatOptions.alias = chatOptions.alias || name chatOptions.alias = chatOptions.alias || name
// Ajouter le flag directement dans les options pour éviter les problèmes de timing
if (rollData) {
chatOptions.flags = { world: { "hawkmoon-roll": rollData } }
}
let msg = await ChatMessage.create(chatOptions) let msg = await ChatMessage.create(chatOptions)
console.log("=======>", rollData)
msg.setFlag("world", "hawkmoon-roll", rollData)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -646,8 +670,7 @@ export class HawkmoonUtility {
modificateursOptions: this.getModificateurOptions(), modificateursOptions: this.getModificateurOptions(),
pointAmeOptions: this.getPointAmeOptions(), pointAmeOptions: this.getPointAmeOptions(),
difficulte: 0, difficulte: 0,
modificateur: 0, modificateur: "0",
bonusMalusContext: 0,
bonusArmeNaturelle: 0, bonusArmeNaturelle: 0,
defenseurAveugle: false, defenseurAveugle: false,
defenseurDeDos: false, defenseurDeDos: false,
@@ -658,6 +681,8 @@ export class HawkmoonUtility {
tireurDeplacement: "immobile", tireurDeplacement: "immobile",
cibleCouvert: "aucun", cibleCouvert: "aucun",
distanceTir: "porteemoyenne", distanceTir: "porteemoyenne",
cibleDeplace: false,
cibleCaC: false,
attaqueCharge: false, attaqueCharge: false,
attaqueDesarme: false, attaqueDesarme: false,
attaqueAmbidextre1: false, attaqueAmbidextre1: false,
@@ -672,10 +697,18 @@ export class HawkmoonUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static updateWithTarget(rollData) { static updateWithTarget(rollData) {
let target = HawkmoonUtility.getTarget() let target = HawkmoonUtility.getTarget()
console.log("updateWithTarget - Current target:", target)
console.log("updateWithTarget - Existing defenderTokenId:", rollData.defenderTokenId)
if (target) { if (target) {
rollData.defenderTokenId = target.id rollData.defenderTokenId = target.id
console.log("updateWithTarget - Set defenderTokenId to:", rollData.defenderTokenId)
}
// Utiliser la cible déjà enregistrée si aucune cible n'est actuellement sélectionnée
if (rollData.defenderTokenId) {
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
console.log("updateWithTarget - Defender actor:", defender.name)
rollData.armeDefense = defender.getBestDefenseValue() rollData.armeDefense = defender.getBestDefenseValue()
console.log("updateWithTarget - armeDefense:", rollData.armeDefense)
rollData.armeAttaqueDefenseur = defender.getBestAttackValue() rollData.armeAttaqueDefenseur = defender.getBestAttackValue()
rollData.targetVigueur = defender.getVigueur() rollData.targetVigueur = defender.getVigueur()
rollData.protectionDefenseur = defender.getProtection() rollData.protectionDefenseur = defender.getProtection()
@@ -691,14 +724,23 @@ export class HawkmoonUtility {
rollData.difficulte = rollData.armeAttaqueDefenseur?.system?.totalOffensif || 0; rollData.difficulte = rollData.armeAttaqueDefenseur?.system?.totalOffensif || 0;
} else if (rollData.armeDefense) { } else if (rollData.armeDefense) {
rollData.difficulte = rollData.armeDefense.system.totalDefensif rollData.difficulte = rollData.armeDefense.system.totalDefensif
if (!rollData.desengager && !rollData.arme.system.armenaturelle && !rollData.arme.system.armefortune) { console.log("updateWithTarget - Calculated difficulte from armeDefense:", rollData.difficulte)
if (rollData.arme && !rollData.desengager && !rollData.arme.system.armenaturelle && !rollData.arme.system.armefortune) {
if (rollData.armeDefense.system.armenaturelle || rollData.armeDefense.system.armefortune) { if (rollData.armeDefense.system.armenaturelle || rollData.armeDefense.system.armefortune) {
rollData.bonusArmeNaturelle = 3 rollData.bonusArmeNaturelle = 3
} }
} }
} else if (rollData.arme?.system.isDistance) {
// Pour les armes à distance, calculer la difficulté de base (protection + distance par défaut)
const distanceValues = { "porteecourte": 5, "porteemoyenne": 9, "porteelongue": 14 }
rollData.difficulte = rollData.protectionDefenseur + distanceValues[rollData.distanceTir || "porteemoyenne"]
console.log("updateWithTarget - Calculated difficulte for ranged:", rollData.difficulte, "= protection", rollData.protectionDefenseur, "+ distance", distanceValues[rollData.distanceTir || "porteemoyenne"])
} else { } else {
console.log("updateWithTarget - No armeDefense found!")
ui.notifications.warn("Aucune arme de défense équipée, difficulté manuelle à positionner.") ui.notifications.warn("Aucune arme de défense équipée, difficulté manuelle à positionner.")
} }
} else {
console.log("updateWithTarget - No defenderTokenId, skipping target calculations")
} }
} }
@@ -789,7 +831,7 @@ export class HawkmoonUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async confirmDelete(actorSheet, li) { static async confirmDelete(actorSheet, li) {
let itemId = li.data("item-id"); let itemId = li.dataset.itemId;
let msgTxt = "<p>Etes vous certain de vouloir supprimer cet item ?"; let msgTxt = "<p>Etes vous certain de vouloir supprimer cet item ?";
let buttons = { let buttons = {
delete: { delete: {
@@ -797,7 +839,7 @@ export class HawkmoonUtility {
label: "Oui !", label: "Oui !",
callback: () => { callback: () => {
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]); actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
li.slideUp(200, () => actorSheet.render(false)); actorSheet.render(false);
} }
}, },
cancel: { cancel: {

31
modules/models/arme.mjs Normal file
View File

@@ -0,0 +1,31 @@
/**
* Data model pour les armes
*/
export default class ArmeDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false }),
typearme: new fields.StringField({ initial: "" }),
armenaturelle: new fields.BooleanField({ initial: false }),
armefortune: new fields.BooleanField({ initial: false }),
bonusmaniementoff: new fields.NumberField({ initial: 0, integer: true }),
seuildefense: new fields.NumberField({ initial: 0, integer: true }),
onlevelonly: new fields.BooleanField({ initial: false }),
degats: new fields.StringField({ initial: "" }),
deuxmains: new fields.BooleanField({ initial: false }),
percearmure: new fields.BooleanField({ initial: false }),
percearmurevalue: new fields.NumberField({ initial: 0, integer: true }),
courte: new fields.NumberField({ initial: 0, integer: true }),
moyenne: new fields.NumberField({ initial: 0, integer: true }),
longue: new fields.NumberField({ initial: 0, integer: true }),
tr: new fields.NumberField({ initial: 0, integer: true })
};
}
}

View File

@@ -0,0 +1,28 @@
/**
* Data model pour les artefacts
*/
export default class ArtefactDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false }),
complexite: new fields.NumberField({ initial: 0, integer: true }),
branche: new fields.StringField({ initial: "" }),
branche2: new fields.StringField({ initial: "none" }),
dureerealisation: new fields.StringField({ initial: "" }),
tempsroute: new fields.StringField({ initial: "" }),
effetdejeu: new fields.StringField({ initial: "" }),
defautcourant: new fields.StringField({ initial: "" }),
autrescarac: new fields.StringField({ initial: "" }),
avantagespossibles: new fields.StringField({ initial: "" }),
avantages: new fields.StringField({ initial: "" }),
competences: new fields.StringField({ initial: "" })
};
}
}

View File

@@ -0,0 +1,53 @@
/**
* Template de base pour tous les items
*/
export class BaseItemModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" })
};
}
}
/**
* Template pour les équipements de base
*/
export class BaseEquipItemModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false })
};
}
}
/**
* Template pour l'automatisation
*/
export class AutomationItemModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
isautomated: new fields.BooleanField({ initial: false }),
automations: new fields.ArrayField(
new fields.SchemaField({
id: new fields.StringField({ initial: "" }),
eventtype: new fields.StringField({ initial: "on-drop" }),
name: new fields.StringField({ initial: "" }),
bonusname: new fields.StringField({ initial: "vigueur" }),
bonus: new fields.NumberField({ initial: 0, integer: true }),
competence: new fields.StringField({ initial: "" }),
minLevel: new fields.NumberField({ initial: 0, integer: true }),
baCost: new fields.NumberField({ initial: 0, integer: true })
}),
{ initial: [] }
)
};
}
}

View File

@@ -0,0 +1,15 @@
/**
* Data model pour les cellules
*/
export default class CelluleDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
notoriete: new fields.NumberField({ initial: 0, integer: true }),
resistance: new fields.NumberField({ initial: 0, integer: true }),
developpement: new fields.NumberField({ initial: 0, integer: true }),
members: new fields.ArrayField(new fields.StringField(), { initial: [] }),
description: new fields.HTMLField({ initial: "" })
};
}
}

View File

@@ -0,0 +1,27 @@
/**
* Data model pour les compétences
*/
export default class CompetenceDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
niveau: new fields.NumberField({ initial: 0, integer: true }),
attribut1: new fields.StringField({ initial: "" }),
attribut2: new fields.StringField({ initial: "" }),
attribut3: new fields.StringField({ initial: "" }),
doublebonus: new fields.BooleanField({ initial: false }),
predilections: new fields.ArrayField(
new fields.SchemaField({
id: new fields.StringField({ initial: "" }),
name: new fields.StringField({ initial: "" }),
description: new fields.StringField({ initial: "" }),
acquise: new fields.BooleanField({ initial: false }),
maitrise: new fields.BooleanField({ initial: false }),
used: new fields.BooleanField({ initial: false })
}),
{ initial: [] }
)
};
}
}

View File

@@ -0,0 +1,14 @@
/**
* Data model pour les contacts
*/
export default class ContactDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
contacttype: new fields.StringField({ initial: "" }),
niveau: new fields.StringField({ initial: "" }),
pointdev: new fields.NumberField({ initial: 0, integer: true }),
description: new fields.HTMLField({ initial: "" })
};
}
}

View File

@@ -0,0 +1,97 @@
/**
* Data model pour les créatures
*/
export default class CreatureDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
// Template biodata
biodata: new fields.SchemaField({
name: new fields.StringField({ initial: "" }),
age: new fields.NumberField({ initial: 0, integer: true }),
poids: new fields.StringField({ initial: "" }),
taille: new fields.StringField({ initial: "" }),
cheveux: new fields.StringField({ initial: "" }),
sexe: new fields.StringField({ initial: "" }),
yeux: new fields.StringField({ initial: "" }),
description: new fields.HTMLField({ initial: "" }),
habitat: new fields.HTMLField({ initial: "" }),
notes: new fields.HTMLField({ initial: "" }),
statut: new fields.StringField({ initial: "" }),
gmnotes: new fields.HTMLField({ initial: "" }),
statutresistant: new fields.StringField({ initial: "commun" })
}),
// Template core
subactors: new fields.ArrayField(new fields.StringField(), { initial: [] }),
attributs: new fields.SchemaField({
adr: new fields.SchemaField({
label: new fields.StringField({ initial: "Adresse" }),
labelnorm: new fields.StringField({ initial: "adresse" }),
abbrev: new fields.StringField({ initial: "adr" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
pui: new fields.SchemaField({
label: new fields.StringField({ initial: "Puissance" }),
labelnorm: new fields.StringField({ initial: "puissance" }),
abbrev: new fields.StringField({ initial: "pui" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
cla: new fields.SchemaField({
label: new fields.StringField({ initial: "Clairvoyance" }),
labelnorm: new fields.StringField({ initial: "clairvoyance" }),
abbrev: new fields.StringField({ initial: "cla" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
pre: new fields.SchemaField({
label: new fields.StringField({ initial: "Présence" }),
labelnorm: new fields.StringField({ initial: "presence" }),
abbrev: new fields.StringField({ initial: "pre" }),
value: new fields.NumberField({ initial: 0, integer: true })
}),
tre: new fields.SchemaField({
label: new fields.StringField({ initial: "Trempe" }),
labelnorm: new fields.StringField({ initial: "trempe" }),
abbrev: new fields.StringField({ initial: "tre" }),
value: new fields.NumberField({ initial: 0, integer: true })
})
}),
bonneaventure: new fields.SchemaField({
base: new fields.NumberField({ initial: 0, integer: true }),
actuelle: new fields.NumberField({ initial: 0, integer: true })
}),
experience: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
eclat: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
sante: new fields.SchemaField({
vigueur: new fields.NumberField({ initial: 0, integer: true }),
etat: new fields.NumberField({ initial: 0, integer: true }),
vigueurmodifier: new fields.NumberField({ initial: 0, integer: true }),
nbcombativite: new fields.NumberField({ initial: 5, integer: true })
}),
adversite: new fields.SchemaField({
bleue: new fields.NumberField({ initial: 0, integer: true }),
rouge: new fields.NumberField({ initial: 0, integer: true }),
noire: new fields.NumberField({ initial: 0, integer: true })
}),
vitesse: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
combat: new fields.SchemaField({
initbonus: new fields.NumberField({ initial: 0, integer: true }),
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
bonusdegats: new fields.NumberField({ initial: 0, integer: true }),
attaquebonus: new fields.NumberField({ initial: 0, integer: true }),
defensebonus: new fields.NumberField({ initial: 0, integer: true }),
defensetotale: new fields.BooleanField({ initial: false }),
monte: new fields.BooleanField({ initial: false })
}),
// Propriétés spécifiques aux créatures
ressources: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
})
};
}
}

View File

@@ -0,0 +1,17 @@
/**
* Data model pour l'équipement
*/
export default class EquipementDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false })
};
}
}

View File

@@ -0,0 +1,12 @@
/**
* Data model pour les historiques
*/
export default class HistoriqueDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
bonusmalus: new fields.StringField({ initial: "" })
};
}
}

23
modules/models/index.mjs Normal file
View File

@@ -0,0 +1,23 @@
/**
* Index des DataModels pour Hawkmoon CYD
* Ce fichier centralise tous les exports des modèles de données
*/
// Modèles d'items
export { default as TalentDataModel } from './talent.mjs';
export { default as HistoriqueDataModel } from './historique.mjs';
export { default as ProfilDataModel } from './profil.mjs';
export { default as CompetenceDataModel } from './competence.mjs';
export { default as ArmeDataModel } from './arme.mjs';
export { default as ProtectionDataModel } from './protection.mjs';
export { default as MonnaieDataModel } from './monnaie.mjs';
export { default as EquipementDataModel } from './equipement.mjs';
export { default as ArtefactDataModel } from './artefact.mjs';
export { default as RessourceDataModel } from './ressource.mjs';
export { default as ContactDataModel } from './contact.mjs';
export { default as MutationDataModel } from './mutation.mjs';
// Modèles d'acteurs
export { default as PersonnageDataModel } from './personnage.mjs';
export { default as CelluleDataModel } from './cellule.mjs';
export { default as CreatureDataModel } from './creature.mjs';

View File

@@ -0,0 +1,17 @@
/**
* Data model pour les monnaies
*/
export default class MonnaieDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false })
};
}
}

View File

@@ -0,0 +1,14 @@
/**
* Data model pour les mutations
*/
export default class MutationDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
mutationcategorie: new fields.StringField({ initial: "tares_communes" }),
hascomplexite: new fields.BooleanField({ initial: false }),
complexite: new fields.NumberField({ initial: 0, integer: true })
};
}
}

View File

@@ -0,0 +1,93 @@
/**
* Data model pour les personnages
*/
export default class PersonnageDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
// Template biodata
biodata: new fields.SchemaField({
name: new fields.StringField({ initial: "" }),
age: new fields.NumberField({ initial: 0, integer: true }),
poids: new fields.StringField({ initial: "" }),
taille: new fields.StringField({ initial: "" }),
cheveux: new fields.StringField({ initial: "" }),
sexe: new fields.StringField({ initial: "" }),
yeux: new fields.StringField({ initial: "" }),
description: new fields.HTMLField({ initial: "" }),
habitat: new fields.HTMLField({ initial: "" }),
notes: new fields.HTMLField({ initial: "" }),
statut: new fields.StringField({ initial: "" }),
gmnotes: new fields.HTMLField({ initial: "" }),
statutresistant: new fields.StringField({ initial: "commun" })
}),
// Template core
subactors: new fields.ArrayField(new fields.StringField(), { initial: [] }),
attributs: new fields.SchemaField({
adr: new fields.SchemaField({
label: new fields.StringField({ initial: "Adresse" }),
labelnorm: new fields.StringField({ initial: "adresse" }),
abbrev: new fields.StringField({ initial: "adr" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
pui: new fields.SchemaField({
label: new fields.StringField({ initial: "Puissance" }),
labelnorm: new fields.StringField({ initial: "puissance" }),
abbrev: new fields.StringField({ initial: "pui" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
cla: new fields.SchemaField({
label: new fields.StringField({ initial: "Clairvoyance" }),
labelnorm: new fields.StringField({ initial: "clairvoyance" }),
abbrev: new fields.StringField({ initial: "cla" }),
value: new fields.NumberField({ initial: 1, integer: true })
}),
pre: new fields.SchemaField({
label: new fields.StringField({ initial: "Présence" }),
labelnorm: new fields.StringField({ initial: "presence" }),
abbrev: new fields.StringField({ initial: "pre" }),
value: new fields.NumberField({ initial: 0, integer: true })
}),
tre: new fields.SchemaField({
label: new fields.StringField({ initial: "Trempe" }),
labelnorm: new fields.StringField({ initial: "trempe" }),
abbrev: new fields.StringField({ initial: "tre" }),
value: new fields.NumberField({ initial: 0, integer: true })
})
}),
bonneaventure: new fields.SchemaField({
base: new fields.NumberField({ initial: 0, integer: true }),
actuelle: new fields.NumberField({ initial: 0, integer: true })
}),
experience: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
eclat: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
sante: new fields.SchemaField({
vigueur: new fields.NumberField({ initial: 0, integer: true }),
etat: new fields.NumberField({ initial: 0, integer: true }),
vigueurmodifier: new fields.NumberField({ initial: 0, integer: true }),
nbcombativite: new fields.NumberField({ initial: 5, integer: true })
}),
adversite: new fields.SchemaField({
bleue: new fields.NumberField({ initial: 0, integer: true }),
rouge: new fields.NumberField({ initial: 0, integer: true }),
noire: new fields.NumberField({ initial: 0, integer: true })
}),
vitesse: new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true })
}),
combat: new fields.SchemaField({
initbonus: new fields.NumberField({ initial: 0, integer: true }),
vitessebonus: new fields.NumberField({ initial: 0, integer: true }),
bonusdegats: new fields.NumberField({ initial: 0, integer: true }),
attaquebonus: new fields.NumberField({ initial: 0, integer: true }),
defensebonus: new fields.NumberField({ initial: 0, integer: true }),
defensetotale: new fields.BooleanField({ initial: false }),
monte: new fields.BooleanField({ initial: false })
})
};
}
}

23
modules/models/profil.mjs Normal file
View File

@@ -0,0 +1,23 @@
/**
* Data model pour les profils
*/
export default class ProfilDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
exemples: new fields.StringField({ initial: "" }),
attribut1: new fields.StringField({ initial: "" }),
attribut2: new fields.StringField({ initial: "" }),
attribut3: new fields.StringField({ initial: "" }),
competences: new fields.StringField({ initial: "" }),
talentsinitie: new fields.StringField({ initial: "" }),
prerequisaguerri: new fields.StringField({ initial: "" }),
talentsaguerri: new fields.StringField({ initial: "" }),
prerequismaitre: new fields.StringField({ initial: "" }),
talentsmaitre: new fields.StringField({ initial: "" }),
celluleinfo: new fields.StringField({ initial: "" }),
equipement: new fields.StringField({ initial: "" })
};
}
}

View File

@@ -0,0 +1,19 @@
/**
* Data model pour les protections
*/
export default class ProtectionDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
prixpo: new fields.NumberField({ initial: 0, integer: true }),
prixca: new fields.NumberField({ initial: 0, integer: true }),
prixsc: new fields.NumberField({ initial: 0, integer: true }),
rarete: new fields.NumberField({ initial: 0, integer: true }),
quantite: new fields.NumberField({ initial: 1, integer: true }),
equipped: new fields.BooleanField({ initial: false }),
protection: new fields.NumberField({ initial: 0, integer: true }),
adversitepoids: new fields.NumberField({ initial: 0, integer: true })
};
}
}

View File

@@ -0,0 +1,12 @@
/**
* Data model pour les ressources
*/
export default class RessourceDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
pointdev: new fields.NumberField({ initial: 0, integer: true }),
description: new fields.HTMLField({ initial: "" })
};
}
}

30
modules/models/talent.mjs Normal file
View File

@@ -0,0 +1,30 @@
/**
* Data model pour les talents
*/
export default class TalentDataModel extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
description: new fields.HTMLField({ initial: "" }),
isautomated: new fields.BooleanField({ initial: false }),
automations: new fields.ArrayField(
new fields.SchemaField({
id: new fields.StringField({ initial: "" }),
eventtype: new fields.StringField({ initial: "on-drop" }),
name: new fields.StringField({ initial: "" }),
bonusname: new fields.StringField({ initial: "vigueur" }),
bonus: new fields.NumberField({ initial: 0, integer: true }),
competence: new fields.StringField({ initial: "" }),
minLevel: new fields.NumberField({ initial: 0, integer: true }),
baCost: new fields.NumberField({ initial: 0, integer: true })
}),
{ initial: [] }
),
talenttype: new fields.StringField({ initial: "" }),
utilisation: new fields.StringField({ initial: "" }),
prerequis: new fields.StringField({ initial: "" }),
resumebonus: new fields.StringField({ initial: "" }),
used: new fields.BooleanField({ initial: false })
};
}
}

View File

@@ -1 +1 @@
MANIFEST-000317 MANIFEST-000377

View File

@@ -1,8 +1,8 @@
2025/10/17-20:49:09.898677 7f7b753f96c0 Recovering log #315 2026/01/09-17:40:07.997473 7f1c563fe6c0 Recovering log #375
2025/10/17-20:49:09.909170 7f7b753f96c0 Delete type=3 #313 2026/01/09-17:40:08.091717 7f1c563fe6c0 Delete type=0 #375
2025/10/17-20:49:09.909239 7f7b753f96c0 Delete type=0 #315 2026/01/09-17:40:08.091792 7f1c563fe6c0 Delete type=3 #373
2025/10/17-22:35:10.581106 7f7b6f3ff6c0 Level-0 table #320: started 2026/01/09-17:44:28.072757 7f1c54bfb6c0 Level-0 table #380: started
2025/10/17-22:35:10.581135 7f7b6f3ff6c0 Level-0 table #320: 0 bytes OK 2026/01/09-17:44:28.072827 7f1c54bfb6c0 Level-0 table #380: 0 bytes OK
2025/10/17-22:35:10.587115 7f7b6f3ff6c0 Delete type=0 #318 2026/01/09-17:44:28.079432 7f1c54bfb6c0 Delete type=0 #378
2025/10/17-22:35:10.603825 7f7b6f3ff6c0 Manual compaction at level-0 from '!journal!MUbViCE2PkVxlzqe' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.105106 7f1c54bfb6c0 Manual compaction at level-0 from '!journal!MUbViCE2PkVxlzqe' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end)
2025/10/17-22:35:10.603883 7f7b6f3ff6c0 Manual compaction at level-1 from '!journal!MUbViCE2PkVxlzqe' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.117150 7f1c54bfb6c0 Manual compaction at level-1 from '!journal!MUbViCE2PkVxlzqe' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/10/17-20:28:53.005538 7f7b6ffff6c0 Recovering log #311 2026/01/08-08:15:08.713116 7f93ea7fc6c0 Recovering log #371
2025/10/17-20:28:53.015248 7f7b6ffff6c0 Delete type=3 #309 2026/01/08-08:15:08.722804 7f93ea7fc6c0 Delete type=3 #369
2025/10/17-20:28:53.015310 7f7b6ffff6c0 Delete type=0 #311 2026/01/08-08:15:08.722873 7f93ea7fc6c0 Delete type=0 #371
2025/10/17-20:41:09.987430 7f7b6f3ff6c0 Level-0 table #316: started 2026/01/08-08:28:05.068912 7f93e9ffb6c0 Level-0 table #376: started
2025/10/17-20:41:09.987468 7f7b6f3ff6c0 Level-0 table #316: 0 bytes OK 2026/01/08-08:28:05.068957 7f93e9ffb6c0 Level-0 table #376: 0 bytes OK
2025/10/17-20:41:09.993944 7f7b6f3ff6c0 Delete type=0 #314 2026/01/08-08:28:05.106033 7f93e9ffb6c0 Delete type=0 #374
2025/10/17-20:41:10.021275 7f7b6f3ff6c0 Manual compaction at level-0 from '!journal!MUbViCE2PkVxlzqe' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end) 2026/01/08-08:28:05.217659 7f93e9ffb6c0 Manual compaction at level-0 from '!journal!MUbViCE2PkVxlzqe' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end)
2025/10/17-20:41:10.021333 7f7b6f3ff6c0 Manual compaction at level-1 from '!journal!MUbViCE2PkVxlzqe' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end) 2026/01/08-08:28:05.217719 7f93e9ffb6c0 Manual compaction at level-1 from '!journal!MUbViCE2PkVxlzqe' @ 72057594037927935 : 1 .. '!journal.pages!gVybbv17TFY8o3Y4.fQidyqfF1TbsZKHM' @ 0 : 0; will stop at (end)

View File

@@ -1 +1 @@
MANIFEST-000316 MANIFEST-000376

View File

@@ -1,8 +1,8 @@
2025/10/17-20:49:09.793672 7f7b753f96c0 Recovering log #314 2026/01/09-17:40:07.215100 7f1c563fe6c0 Recovering log #374
2025/10/17-20:49:09.803546 7f7b753f96c0 Delete type=3 #312 2026/01/09-17:40:07.308808 7f1c563fe6c0 Delete type=0 #374
2025/10/17-20:49:09.803615 7f7b753f96c0 Delete type=0 #314 2026/01/09-17:40:07.308890 7f1c563fe6c0 Delete type=3 #372
2025/10/17-22:35:10.536969 7f7b6f3ff6c0 Level-0 table #319: started 2026/01/09-17:44:28.036990 7f1c54bfb6c0 Level-0 table #379: started
2025/10/17-22:35:10.537001 7f7b6f3ff6c0 Level-0 table #319: 0 bytes OK 2026/01/09-17:44:28.037073 7f1c54bfb6c0 Level-0 table #379: 0 bytes OK
2025/10/17-22:35:10.542920 7f7b6f3ff6c0 Delete type=0 #317 2026/01/09-17:44:28.043547 7f1c54bfb6c0 Delete type=0 #377
2025/10/17-22:35:10.548820 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!0fit7HelSjaFtXcW' @ 72057594037927935 : 1 .. '!items!wxrzP3NyiHiYnAMJ' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.043784 7f1c54bfb6c0 Manual compaction at level-0 from '!items!0fit7HelSjaFtXcW' @ 72057594037927935 : 1 .. '!items!wxrzP3NyiHiYnAMJ' @ 0 : 0; will stop at (end)
2025/10/17-22:35:10.548874 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!0fit7HelSjaFtXcW' @ 72057594037927935 : 1 .. '!items!wxrzP3NyiHiYnAMJ' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.043823 7f1c54bfb6c0 Manual compaction at level-1 from '!items!0fit7HelSjaFtXcW' @ 72057594037927935 : 1 .. '!items!wxrzP3NyiHiYnAMJ' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/10/17-20:28:52.891518 7f7b74bf86c0 Recovering log #310 2026/01/08-08:15:08.609938 7f93eaffd6c0 Recovering log #370
2025/10/17-20:28:52.901457 7f7b74bf86c0 Delete type=3 #308 2026/01/08-08:15:08.619765 7f93eaffd6c0 Delete type=3 #368
2025/10/17-20:28:52.901513 7f7b74bf86c0 Delete type=0 #310 2026/01/08-08:15:08.619830 7f93eaffd6c0 Delete type=0 #370
2025/10/17-20:41:09.934499 7f7b6f3ff6c0 Level-0 table #315: started 2026/01/08-08:28:04.844266 7f93e9ffb6c0 Level-0 table #375: started
2025/10/17-20:41:09.934522 7f7b6f3ff6c0 Level-0 table #315: 0 bytes OK 2026/01/08-08:28:04.844294 7f93e9ffb6c0 Level-0 table #375: 0 bytes OK
2025/10/17-20:41:09.940458 7f7b6f3ff6c0 Delete type=0 #313 2026/01/08-08:28:04.883113 7f93e9ffb6c0 Delete type=0 #373
2025/10/17-20:41:09.960521 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!0fit7HelSjaFtXcW' @ 72057594037927935 : 1 .. '!items!wxrzP3NyiHiYnAMJ' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.919770 7f93e9ffb6c0 Manual compaction at level-0 from '!items!0fit7HelSjaFtXcW' @ 72057594037927935 : 1 .. '!items!wxrzP3NyiHiYnAMJ' @ 0 : 0; will stop at (end)
2025/10/17-20:41:09.960598 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!0fit7HelSjaFtXcW' @ 72057594037927935 : 1 .. '!items!wxrzP3NyiHiYnAMJ' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.919798 7f93e9ffb6c0 Manual compaction at level-1 from '!items!0fit7HelSjaFtXcW' @ 72057594037927935 : 1 .. '!items!wxrzP3NyiHiYnAMJ' @ 0 : 0; will stop at (end)

View File

@@ -1 +1 @@
MANIFEST-000316 MANIFEST-000376

View File

@@ -1,8 +1,8 @@
2025/10/17-20:49:09.768629 7f7b6ffff6c0 Recovering log #314 2026/01/09-17:40:07.033899 7f1c55bfd6c0 Recovering log #374
2025/10/17-20:49:09.779130 7f7b6ffff6c0 Delete type=3 #312 2026/01/09-17:40:07.115529 7f1c55bfd6c0 Delete type=0 #374
2025/10/17-20:49:09.779215 7f7b6ffff6c0 Delete type=0 #314 2026/01/09-17:40:07.115669 7f1c55bfd6c0 Delete type=3 #372
2025/10/17-22:35:10.498148 7f7b6f3ff6c0 Level-0 table #319: started 2026/01/09-17:44:28.002424 7f1c54bfb6c0 Level-0 table #379: started
2025/10/17-22:35:10.498192 7f7b6f3ff6c0 Level-0 table #319: 0 bytes OK 2026/01/09-17:44:28.002453 7f1c54bfb6c0 Level-0 table #379: 0 bytes OK
2025/10/17-22:35:10.504185 7f7b6f3ff6c0 Delete type=0 #317 2026/01/09-17:44:28.008591 7f1c54bfb6c0 Delete type=0 #377
2025/10/17-22:35:10.523654 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!0nhTxujlIUB63Aqt' @ 72057594037927935 : 1 .. '!items!tFU5yISK6spdNWco' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.016607 7f1c54bfb6c0 Manual compaction at level-0 from '!items!0nhTxujlIUB63Aqt' @ 72057594037927935 : 1 .. '!items!tFU5yISK6spdNWco' @ 0 : 0; will stop at (end)
2025/10/17-22:35:10.523691 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!0nhTxujlIUB63Aqt' @ 72057594037927935 : 1 .. '!items!tFU5yISK6spdNWco' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.016651 7f1c54bfb6c0 Manual compaction at level-1 from '!items!0nhTxujlIUB63Aqt' @ 72057594037927935 : 1 .. '!items!tFU5yISK6spdNWco' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/10/17-20:28:52.864581 7f7b6ffff6c0 Recovering log #310 2026/01/08-08:15:08.584392 7f93eb7fe6c0 Recovering log #370
2025/10/17-20:28:52.874388 7f7b6ffff6c0 Delete type=3 #308 2026/01/08-08:15:08.595037 7f93eb7fe6c0 Delete type=3 #368
2025/10/17-20:28:52.874444 7f7b6ffff6c0 Delete type=0 #310 2026/01/08-08:15:08.595129 7f93eb7fe6c0 Delete type=0 #370
2025/10/17-20:41:09.921911 7f7b6f3ff6c0 Level-0 table #315: started 2026/01/08-08:28:04.658126 7f93e9ffb6c0 Level-0 table #375: started
2025/10/17-20:41:09.921935 7f7b6f3ff6c0 Level-0 table #315: 0 bytes OK 2026/01/08-08:28:04.658154 7f93e9ffb6c0 Level-0 table #375: 0 bytes OK
2025/10/17-20:41:09.928010 7f7b6f3ff6c0 Delete type=0 #313 2026/01/08-08:28:04.696322 7f93e9ffb6c0 Delete type=0 #373
2025/10/17-20:41:09.934388 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!0nhTxujlIUB63Aqt' @ 72057594037927935 : 1 .. '!items!tFU5yISK6spdNWco' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.770719 7f93e9ffb6c0 Manual compaction at level-0 from '!items!0nhTxujlIUB63Aqt' @ 72057594037927935 : 1 .. '!items!tFU5yISK6spdNWco' @ 0 : 0; will stop at (end)
2025/10/17-20:41:09.934420 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!0nhTxujlIUB63Aqt' @ 72057594037927935 : 1 .. '!items!tFU5yISK6spdNWco' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.770775 7f93e9ffb6c0 Manual compaction at level-1 from '!items!0nhTxujlIUB63Aqt' @ 72057594037927935 : 1 .. '!items!tFU5yISK6spdNWco' @ 0 : 0; will stop at (end)

View File

@@ -1 +1 @@
MANIFEST-000316 MANIFEST-000376

View File

@@ -1,8 +1,8 @@
2025/10/17-20:49:09.744321 7f7b75bfa6c0 Recovering log #314 2026/01/09-17:40:06.860640 7f1c553fc6c0 Recovering log #374
2025/10/17-20:49:09.753860 7f7b75bfa6c0 Delete type=3 #312 2026/01/09-17:40:06.945970 7f1c553fc6c0 Delete type=0 #374
2025/10/17-20:49:09.753934 7f7b75bfa6c0 Delete type=0 #314 2026/01/09-17:40:06.946049 7f1c553fc6c0 Delete type=3 #372
2025/10/17-22:35:10.511424 7f7b6f3ff6c0 Level-0 table #319: started 2026/01/09-17:44:27.995662 7f1c54bfb6c0 Level-0 table #379: started
2025/10/17-22:35:10.511449 7f7b6f3ff6c0 Level-0 table #319: 0 bytes OK 2026/01/09-17:44:27.995696 7f1c54bfb6c0 Level-0 table #379: 0 bytes OK
2025/10/17-22:35:10.517304 7f7b6f3ff6c0 Delete type=0 #317 2026/01/09-17:44:28.002283 7f1c54bfb6c0 Delete type=0 #377
2025/10/17-22:35:10.523675 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!15IDGG6JoZnRCQtY' @ 72057594037927935 : 1 .. '!items!yI1zY5k8mAdx9wHK' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.016595 7f1c54bfb6c0 Manual compaction at level-0 from '!items!15IDGG6JoZnRCQtY' @ 72057594037927935 : 1 .. '!items!yI1zY5k8mAdx9wHK' @ 0 : 0; will stop at (end)
2025/10/17-22:35:10.523698 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!15IDGG6JoZnRCQtY' @ 72057594037927935 : 1 .. '!items!yI1zY5k8mAdx9wHK' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.016641 7f1c54bfb6c0 Manual compaction at level-1 from '!items!15IDGG6JoZnRCQtY' @ 72057594037927935 : 1 .. '!items!yI1zY5k8mAdx9wHK' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/10/17-20:28:52.833282 7f7b75bfa6c0 Recovering log #310 2026/01/08-08:15:08.560342 7f93eaffd6c0 Recovering log #370
2025/10/17-20:28:52.844042 7f7b75bfa6c0 Delete type=3 #308 2026/01/08-08:15:08.570307 7f93eaffd6c0 Delete type=3 #368
2025/10/17-20:28:52.844117 7f7b75bfa6c0 Delete type=0 #310 2026/01/08-08:15:08.570398 7f93eaffd6c0 Delete type=0 #370
2025/10/17-20:41:09.915408 7f7b6f3ff6c0 Level-0 table #315: started 2026/01/08-08:28:04.883258 7f93e9ffb6c0 Level-0 table #375: started
2025/10/17-20:41:09.915432 7f7b6f3ff6c0 Level-0 table #315: 0 bytes OK 2026/01/08-08:28:04.883283 7f93e9ffb6c0 Level-0 table #375: 0 bytes OK
2025/10/17-20:41:09.921802 7f7b6f3ff6c0 Delete type=0 #313 2026/01/08-08:28:04.919585 7f93e9ffb6c0 Delete type=0 #373
2025/10/17-20:41:09.934379 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!15IDGG6JoZnRCQtY' @ 72057594037927935 : 1 .. '!items!yI1zY5k8mAdx9wHK' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.919781 7f93e9ffb6c0 Manual compaction at level-0 from '!items!15IDGG6JoZnRCQtY' @ 72057594037927935 : 1 .. '!items!yI1zY5k8mAdx9wHK' @ 0 : 0; will stop at (end)
2025/10/17-20:41:09.934414 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!15IDGG6JoZnRCQtY' @ 72057594037927935 : 1 .. '!items!yI1zY5k8mAdx9wHK' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.919805 7f93e9ffb6c0 Manual compaction at level-1 from '!items!15IDGG6JoZnRCQtY' @ 72057594037927935 : 1 .. '!items!yI1zY5k8mAdx9wHK' @ 0 : 0; will stop at (end)

View File

@@ -1 +1 @@
MANIFEST-000316 MANIFEST-000376

View File

@@ -1,8 +1,8 @@
2025/10/17-20:49:09.817906 7f7b75bfa6c0 Recovering log #314 2026/01/09-17:40:07.398683 7f1c55bfd6c0 Recovering log #374
2025/10/17-20:49:09.828670 7f7b75bfa6c0 Delete type=3 #312 2026/01/09-17:40:07.488253 7f1c55bfd6c0 Delete type=0 #374
2025/10/17-20:49:09.828748 7f7b75bfa6c0 Delete type=0 #314 2026/01/09-17:40:07.488337 7f1c55bfd6c0 Delete type=3 #372
2025/10/17-22:35:10.543003 7f7b6f3ff6c0 Level-0 table #319: started 2026/01/09-17:44:28.016766 7f1c54bfb6c0 Level-0 table #379: started
2025/10/17-22:35:10.543024 7f7b6f3ff6c0 Level-0 table #319: 0 bytes OK 2026/01/09-17:44:28.016805 7f1c54bfb6c0 Level-0 table #379: 0 bytes OK
2025/10/17-22:35:10.548737 7f7b6f3ff6c0 Delete type=0 #317 2026/01/09-17:44:28.023141 7f1c54bfb6c0 Delete type=0 #377
2025/10/17-22:35:10.548828 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!0BopmCu8vGK2923j' @ 72057594037927935 : 1 .. '!items!zYx0Ak2y1LNTcKlO' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.043713 7f1c54bfb6c0 Manual compaction at level-0 from '!items!0BopmCu8vGK2923j' @ 72057594037927935 : 1 .. '!items!zYx0Ak2y1LNTcKlO' @ 0 : 0; will stop at (end)
2025/10/17-22:35:10.548865 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!0BopmCu8vGK2923j' @ 72057594037927935 : 1 .. '!items!zYx0Ak2y1LNTcKlO' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.043764 7f1c54bfb6c0 Manual compaction at level-1 from '!items!0BopmCu8vGK2923j' @ 72057594037927935 : 1 .. '!items!zYx0Ak2y1LNTcKlO' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/10/17-20:28:52.917702 7f7b753f96c0 Recovering log #310 2026/01/08-08:15:08.634608 7f93eb7fe6c0 Recovering log #370
2025/10/17-20:28:52.927558 7f7b753f96c0 Delete type=3 #308 2026/01/08-08:15:08.644771 7f93eb7fe6c0 Delete type=3 #368
2025/10/17-20:28:52.927613 7f7b753f96c0 Delete type=0 #310 2026/01/08-08:15:08.644849 7f93eb7fe6c0 Delete type=0 #370
2025/10/17-20:41:09.940553 7f7b6f3ff6c0 Level-0 table #315: started 2026/01/08-08:28:04.770939 7f93e9ffb6c0 Level-0 table #375: started
2025/10/17-20:41:09.940581 7f7b6f3ff6c0 Level-0 table #315: 0 bytes OK 2026/01/08-08:28:04.770974 7f93e9ffb6c0 Level-0 table #375: 0 bytes OK
2025/10/17-20:41:09.947637 7f7b6f3ff6c0 Delete type=0 #313 2026/01/08-08:28:04.807757 7f93e9ffb6c0 Delete type=0 #373
2025/10/17-20:41:09.960552 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!0BopmCu8vGK2923j' @ 72057594037927935 : 1 .. '!items!zYx0Ak2y1LNTcKlO' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.919742 7f93e9ffb6c0 Manual compaction at level-0 from '!items!0BopmCu8vGK2923j' @ 72057594037927935 : 1 .. '!items!zYx0Ak2y1LNTcKlO' @ 0 : 0; will stop at (end)
2025/10/17-20:41:09.960608 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!0BopmCu8vGK2923j' @ 72057594037927935 : 1 .. '!items!zYx0Ak2y1LNTcKlO' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.919789 7f93e9ffb6c0 Manual compaction at level-1 from '!items!0BopmCu8vGK2923j' @ 72057594037927935 : 1 .. '!items!zYx0Ak2y1LNTcKlO' @ 0 : 0; will stop at (end)

View File

@@ -1 +1 @@
MANIFEST-000316 MANIFEST-000376

View File

@@ -1,8 +1,8 @@
2025/10/17-20:49:09.780990 7f7b74bf86c0 Recovering log #314 2026/01/09-17:40:07.118312 7f1c553fc6c0 Recovering log #374
2025/10/17-20:49:09.790922 7f7b74bf86c0 Delete type=3 #312 2026/01/09-17:40:07.212316 7f1c553fc6c0 Delete type=0 #374
2025/10/17-20:49:09.791046 7f7b74bf86c0 Delete type=0 #314 2026/01/09-17:40:07.212395 7f1c553fc6c0 Delete type=3 #372
2025/10/17-22:35:10.504312 7f7b6f3ff6c0 Level-0 table #319: started 2026/01/09-17:44:28.008871 7f1c54bfb6c0 Level-0 table #379: started
2025/10/17-22:35:10.504337 7f7b6f3ff6c0 Level-0 table #319: 0 bytes OK 2026/01/09-17:44:28.008958 7f1c54bfb6c0 Level-0 table #379: 0 bytes OK
2025/10/17-22:35:10.511317 7f7b6f3ff6c0 Delete type=0 #317 2026/01/09-17:44:28.016332 7f1c54bfb6c0 Delete type=0 #377
2025/10/17-22:35:10.523666 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.016621 7f1c54bfb6c0 Manual compaction at level-0 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end)
2025/10/17-22:35:10.523704 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.016661 7f1c54bfb6c0 Manual compaction at level-1 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/10/17-20:28:52.876807 7f7b75bfa6c0 Recovering log #310 2026/01/08-08:15:08.597802 7f93ea7fc6c0 Recovering log #370
2025/10/17-20:28:52.886763 7f7b75bfa6c0 Delete type=3 #308 2026/01/08-08:15:08.607783 7f93ea7fc6c0 Delete type=3 #368
2025/10/17-20:28:52.886882 7f7b75bfa6c0 Delete type=0 #310 2026/01/08-08:15:08.607847 7f93ea7fc6c0 Delete type=0 #370
2025/10/17-20:41:09.909233 7f7b6f3ff6c0 Level-0 table #315: started 2026/01/08-08:28:04.733376 7f93e9ffb6c0 Level-0 table #375: started
2025/10/17-20:41:09.909293 7f7b6f3ff6c0 Level-0 table #315: 0 bytes OK 2026/01/08-08:28:04.733403 7f93e9ffb6c0 Level-0 table #375: 0 bytes OK
2025/10/17-20:41:09.915307 7f7b6f3ff6c0 Delete type=0 #313 2026/01/08-08:28:04.770489 7f93e9ffb6c0 Delete type=0 #373
2025/10/17-20:41:09.934366 7f7b6f3ff6c0 Manual compaction at level-0 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.770754 7f93e9ffb6c0 Manual compaction at level-0 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end)
2025/10/17-20:41:09.934408 7f7b6f3ff6c0 Manual compaction at level-1 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.770794 7f93e9ffb6c0 Manual compaction at level-1 from '!items!15foLG7y3LUXNzkK' @ 72057594037927935 : 1 .. '!items!z1HtkvazCGHut7cz' @ 0 : 0; will stop at (end)

View File

@@ -1 +1 @@
MANIFEST-000226 MANIFEST-000286

View File

@@ -1,8 +1,8 @@
2025/10/17-20:49:09.755935 7f7b753f96c0 Recovering log #224 2026/01/09-17:40:06.948771 7f1c563fe6c0 Recovering log #284
2025/10/17-20:49:09.766032 7f7b753f96c0 Delete type=3 #222 2026/01/09-17:40:07.030884 7f1c563fe6c0 Delete type=0 #284
2025/10/17-20:49:09.766080 7f7b753f96c0 Delete type=0 #224 2026/01/09-17:40:07.030949 7f1c563fe6c0 Delete type=3 #282
2025/10/17-22:35:10.517436 7f7b6f3ff6c0 Level-0 table #229: started 2026/01/09-17:44:27.988602 7f1c54bfb6c0 Level-0 table #289: started
2025/10/17-22:35:10.517471 7f7b6f3ff6c0 Level-0 table #229: 0 bytes OK 2026/01/09-17:44:27.988662 7f1c54bfb6c0 Level-0 table #289: 0 bytes OK
2025/10/17-22:35:10.523546 7f7b6f3ff6c0 Delete type=0 #227 2026/01/09-17:44:27.995503 7f1c54bfb6c0 Delete type=0 #287
2025/10/17-22:35:10.523683 7f7b6f3ff6c0 Manual compaction at level-0 from '!folders!5d4Zn28TUcPxRyXd' @ 72057594037927935 : 1 .. '!items!zttESycGKltfwCzJ' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.016581 7f1c54bfb6c0 Manual compaction at level-0 from '!folders!5d4Zn28TUcPxRyXd' @ 72057594037927935 : 1 .. '!items!zttESycGKltfwCzJ' @ 0 : 0; will stop at (end)
2025/10/17-22:35:10.523709 7f7b6f3ff6c0 Manual compaction at level-1 from '!folders!5d4Zn28TUcPxRyXd' @ 72057594037927935 : 1 .. '!items!zttESycGKltfwCzJ' @ 0 : 0; will stop at (end) 2026/01/09-17:44:28.016631 7f1c54bfb6c0 Manual compaction at level-1 from '!folders!5d4Zn28TUcPxRyXd' @ 72057594037927935 : 1 .. '!items!zttESycGKltfwCzJ' @ 0 : 0; will stop at (end)

View File

@@ -1,8 +1,8 @@
2025/10/17-20:28:52.850444 7f7b753f96c0 Recovering log #220 2026/01/08-08:15:08.572263 7f93ebfff6c0 Recovering log #280
2025/10/17-20:28:52.860559 7f7b753f96c0 Delete type=3 #218 2026/01/08-08:15:08.581752 7f93ebfff6c0 Delete type=3 #278
2025/10/17-20:28:52.860629 7f7b753f96c0 Delete type=0 #220 2026/01/08-08:15:08.581804 7f93ebfff6c0 Delete type=0 #280
2025/10/17-20:41:09.928209 7f7b6f3ff6c0 Level-0 table #225: started 2026/01/08-08:28:04.623048 7f93e9ffb6c0 Level-0 table #285: started
2025/10/17-20:41:09.928246 7f7b6f3ff6c0 Level-0 table #225: 0 bytes OK 2026/01/08-08:28:04.623109 7f93e9ffb6c0 Level-0 table #285: 0 bytes OK
2025/10/17-20:41:09.934269 7f7b6f3ff6c0 Delete type=0 #223 2026/01/08-08:28:04.657992 7f93e9ffb6c0 Delete type=0 #283
2025/10/17-20:41:09.934398 7f7b6f3ff6c0 Manual compaction at level-0 from '!folders!5d4Zn28TUcPxRyXd' @ 72057594037927935 : 1 .. '!items!zttESycGKltfwCzJ' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.770699 7f93e9ffb6c0 Manual compaction at level-0 from '!folders!5d4Zn28TUcPxRyXd' @ 72057594037927935 : 1 .. '!items!zttESycGKltfwCzJ' @ 0 : 0; will stop at (end)
2025/10/17-20:41:09.934428 7f7b6f3ff6c0 Manual compaction at level-1 from '!folders!5d4Zn28TUcPxRyXd' @ 72057594037927935 : 1 .. '!items!zttESycGKltfwCzJ' @ 0 : 0; will stop at (end) 2026/01/08-08:28:04.770767 7f93e9ffb6c0 Manual compaction at level-1 from '!folders!5d4Zn28TUcPxRyXd' @ 72057594037927935 : 1 .. '!items!zttESycGKltfwCzJ' @ 0 : 0; will stop at (end)

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