refacto: harmonisation du système avec les règles Hamalron JDR

Corrections majeures :
- HamalronTirageTarot extends Roll (évaluation des jets fonctionnelle)
- Correction du nom de classe Langue (HamalronFaction -> HamalronLangue)
- Création du template tab-navigation.hbs manquant
- Type acteur 'character' -> 'personnage' dans _preCreate

Types d'items corrigés (anglais -> français) :
- weapon -> arme, armor -> armure, equipment -> equipement
- Suppression des types inexistants (deal, malefica, ritual, perk)

Modèles :
- cost -> cout (armure, equipement)
- Tarot : default -> initial
- Sortilege : LOCALIZATION_PREFIXES corrigé
- Constantes CHOICE_ADVANTAGES_DISADVANTAGES et ATTACK_MODIFIERS ajoutées
- ATTACK_MODIFIERS supprimé (plus utilisé)

Templates alignés sur les modèles réels :
- enemy-trait : suppression champs manquants (trauma, stats, domain...)
- personnage-equipement : champs alignés sur arme/armure/equipement
- personnage-sortileges : suppression domain/level/rituals
- enemy-main : suppression stats et flavorText
- base-actor : suppression mortality, toChat limité aux tarots

Jets de compétence :
- Égalité = réussite (>= au lieu de >, conforme aux règles p.155)
- Comparaison robuste des cartes de succès (via String())

Le Mat :
- Les autres joueurs peuvent renouveler leur main (règle p.151)

findTarotItem : recherche dans game.items puis compendiums
await manquants ajoutés sur saveDeckState()
AGENTS.md créé
This commit is contained in:
2026-07-10 18:52:20 +02:00
parent 4bc381f2e6
commit 68b3079da8
25 changed files with 257 additions and 570 deletions
@@ -33,7 +33,6 @@ export default class HamalronActorSheet extends HandlebarsApplicationMixin(found
toggleSheet: HamalronActorSheet.#onToggleSheet,
edit: HamalronActorSheet.#onItemEdit,
delete: HamalronActorSheet.#onItemDelete,
updateCheckboxArray: HamalronActorSheet.#onUpdateCheckboxArray,
toChat: HamalronActorSheet.#toChat,
},
@@ -69,7 +68,6 @@ export default class HamalronActorSheet extends HandlebarsApplicationMixin(found
actor: this.document,
system: this.document.system,
source: this.document.toObject(),
enrichedBackstory: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.backstory, { async: true }),
isEditMode: this.isEditMode,
isPlayMode: this.isPlayMode,
isEditable: this.isEditable,
@@ -187,44 +185,19 @@ export default class HamalronActorSheet extends HandlebarsApplicationMixin(found
return fp.browse()
}
static #onUpdateCheckboxArray(event, target) {
console.log("Update checkbox array", event, target)
let arrayName = target.dataset.name
let arrayIdx = Number(target.dataset.index)
let dataPath = `system.mortality.${arrayName}`
let tab = foundry.utils.duplicate(this.document.system.mortality[arrayName])
tab[arrayIdx] = target.checked
this.actor.update({ [dataPath]: tab })
}
static async #toChat(event, target) {
const itemUuid = target.getAttribute("data-item-uuid")
const item = fromUuidSync(itemUuid)
if (!item) return
let content = ""
if (item.type === "perk") {
content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-hamalron/templates/chat-perk.hbs", item.toObject())
}
if (item.type === "malefica") {
content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-hamalron/templates/chat-malefica.hbs", item.toObject())
}
if (item.type === "ritual") {
content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-hamalron/templates/chat-ritual.hbs", item.toObject())
}
if (item.type === "species-trait") {
content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-hamalron/templates/chat-trait.hbs", item.toObject())
}
if (item.type === "tarot") {
content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-hamalron/templates/chat-tarot.hbs", item.toObject())
}
const chatData = {
if (item.type !== "tarot") return
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-hamalron/templates/chat-tarot.hbs", item.toObject())
const actor = this.actor || this.document?.parent
ChatMessage.create({
user: game.user.id,
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
content: content,
speaker: ChatMessage.getSpeaker({ actor }),
content,
type: CONST.CHAT_MESSAGE_TYPES.OTHER,
}
ChatMessage.create(chatData, { renderSheet: false })
}, { renderSheet: false })
}
/**
@@ -127,12 +127,13 @@ export default class HamalronItemSheet extends HandlebarsApplicationMixin(foundr
const el = event.currentTarget
if ("link" in event.target.dataset) return
// Extract the data you need
let dragData = null
const itemId = el.dataset.itemId
if (!itemId) return
if (!dragData) return
const item = this.document.items.get(itemId)
if (!item) return
// Set data transfer
const dragData = item.toDragData()
event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
}
+14 -24
View File
@@ -12,10 +12,9 @@ export default class HamalronEnemySheet extends HamalronActorSheet {
contentClasses: ["enemy-content"],
},
actions: {
createTrait: HamalronEnemySheet.#onCreateTrait,
createEquipment: HamalronEnemySheet.#onCreateEquipment,
createWeapon: HamalronEnemySheet.#onCreateWeapon,
createMalefica: HamalronEnemySheet.#onCreateMalefica,
createSortilege: HamalronEnemySheet.#onCreateSortilege,
}
}
@@ -74,13 +73,13 @@ export default class HamalronEnemySheet extends HamalronActorSheet {
break
case "traits":
context.tab = context.tabs.traits
context.traits = doc.itemTypes["species-trait"]
context.traits = doc.itemTypes.competence || []
context.traits.sort((a, b) => a.name.localeCompare(b.name))
context.weapons = doc.itemTypes.weapon
context.weapons = doc.itemTypes.arme || []
context.weapons.sort((a, b) => a.name.localeCompare(b.name))
context.maleficas = doc.itemTypes.malefica
context.maleficas.sort((a, b) => a.name.localeCompare(b.name))
context.equipments = doc.itemTypes.equipment
context.sortileges = doc.itemTypes.sortilege || []
context.sortileges.sort((a, b) => a.name.localeCompare(b.name))
context.equipments = doc.itemTypes.equipement || []
context.equipments.sort((a, b) => a.name.localeCompare(b.name))
break
case "biography":
@@ -92,25 +91,16 @@ export default class HamalronEnemySheet extends HamalronActorSheet {
return context
}
/**
* Creates a new attack item directly from the sheet and embeds it into the document.
* @param {Event} event The initiating click event.
* @param {HTMLElement} target The current target of the event listener.
*/
static #onCreateTrait(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newTrait"), type: "species-trait" }])
}
static #onCreateEquipment(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newEquipment"), type: "equipment" }])
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newEquipment"), type: "equipement" }])
}
static #onCreateMalefica(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newMalefica"), type: "malefica" }])
static #onCreateSortilege(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newSortilege"), type: "sortilege" }])
}
static #onCreateWeapon(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newWeapon"), type: "weapon" }])
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newWeapon"), type: "arme" }])
}
@@ -135,8 +125,8 @@ export default class HamalronEnemySheet extends HamalronActorSheet {
case "stat":
{
let statId = $(event.currentTarget).data("stat-id");
item = this.actor.system.stats[statId];
await this.document.system.roll(rollType, item)
item = this.actor.system?.stats?.[statId];
if (item) await this.document.system.roll(rollType, item)
}
break
case "weapon":
@@ -144,7 +134,7 @@ export default class HamalronEnemySheet extends HamalronActorSheet {
{
let li = $(event.currentTarget).parents(".item");
item = this.actor.items.get(li.data("item-id"));
await this.document.system.roll(rollType, item)
if (item) await this.document.system.roll(rollType, item)
}
break
default:
@@ -154,7 +144,7 @@ export default class HamalronEnemySheet extends HamalronActorSheet {
async _onDrop(event) {
if (!this.isEditable || !this.isEditMode) return
const data = TextEditor.getDragEventData(event)
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
// Handle different data types
switch (data.type) {
+21 -80
View File
@@ -1,5 +1,6 @@
import HamalronActorSheet from "./base-actor-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs"
import HamalronUtils from "../../utils.mjs"
export default class HamalronPersonnageSheet extends HamalronActorSheet {
/** @override */
@@ -16,11 +17,7 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
createEquipment: HamalronPersonnageSheet.#onCreateEquipment,
createArmor: HamalronPersonnageSheet.#onCreateArmor,
createWeapon: HamalronPersonnageSheet.#onCreateWeapon,
createDeal: HamalronPersonnageSheet.#onCreateDeal,
createMalefica: HamalronPersonnageSheet.#onCreateMalefica,
createRitual: HamalronPersonnageSheet.#onCreateRitual,
createPerk: HamalronPersonnageSheet.#onCreatePerk,
modifyAmmo: HamalronPersonnageSheet.#onModifyAmmo,
createSortilege: HamalronPersonnageSheet.#onCreateSortilege,
modifyResistance: HamalronPersonnageSheet.#onModifyResistance,
discardCard: HamalronPersonnageSheet.#onDiscardCard,
rollCompetence: HamalronPersonnageSheet.#onRollCompetence,
@@ -118,21 +115,15 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
case "sortileges":
context.tab = context.tabs.sortileges
context.sortileges = doc.itemTypes.sortilege || []
// Sort the sortileges by system.domain and then by the system.level
context.sortileges.sort((a, b) => {
if (a.system.domain === b.system.domain) {
return a.system.level.localeCompare(b.system.level)
}
return a.system.domain.localeCompare(b.system.domain)
})
context.sortileges.sort((a, b) => a.name.localeCompare(b.name))
break
case "equipment":
context.tab = context.tabs.equipment
context.weapons = doc.itemTypes.weapon || []
context.weapons = doc.itemTypes.arme || []
context.weapons.sort((a, b) => a.name.localeCompare(b.name))
context.armors = doc.itemTypes.armor || []
context.armors = doc.itemTypes.armure || []
context.armors.sort((a, b) => a.name.localeCompare(b.name))
context.equipments = doc.itemTypes.equipment || []
context.equipments = doc.itemTypes.equipement || []
context.equipments.sort((a, b) => a.name.localeCompare(b.name))
break
case "tarot":
@@ -142,16 +133,14 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
context.handLimit = 7
context.handSize = context.tarotCards.length
context.isOverLimit = context.handSize > context.handLimit
context.canAddCard = context.handSize < 8
// Marquer les cartes de succès et enrichir les descriptions
context.canAddCard = context.handSize < context.handLimit
for (const card of context.tarotCards) {
card.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(card.system.description, { async: true })
// Vérifier si c'est une carte de succès
if (doc.system.cartesSucces && card.system.type !== "atout") {
card.isSuccessCard = Object.keys(doc.system.cartesSucces).some(symbole => {
const successCardData = doc.system.cartesSucces[symbole]
return card.system.symbole === symbole &&
card.system.valeur === successCardData.value.toString()
String(card.system.valeur) === String(successCardData.value)
})
} else {
card.isSuccessCard = false
@@ -160,19 +149,7 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
break
case "biography":
context.tab = context.tabs.biography
context.deals = doc.itemTypes.deal || []
context.enrichedBackstory = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.backstory, { async: true })
context.enrichedAppearance = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.appearance, { async: true })
context.enrichedScars = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.scars, { async: true })
context.enrichedLikes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.likes, { async: true })
context.enrichedDislikes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.dislikes, { async: true })
context.enrichedFears = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.fears, { async: true })
context.enrichedVices = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.vices, { async: true })
context.enrichedGoals = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.goals, { async: true })
context.enrichedAmbitions = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.ambitions, { async: true })
context.enrichedValues = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.values, { async: true })
context.enrichedBonds = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.bonds, { async: true })
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true })
context.enrichedHistorial = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.historial, { async: true })
break
}
return context
@@ -180,44 +157,19 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
static #onCreateEquipment(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newEquipment"), type: "equipment" }])
}
static #onCreateDeal(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newDeal"), type: "deal" }])
}
static #onCreateMalefica(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newMalefica"), type: "malefica" }])
}
static #onCreateRitual(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newRitual"), type: "ritual" }])
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newEquipment"), type: "equipement" }])
}
static #onCreateWeapon(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newWeapon"), type: "weapon" }])
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newWeapon"), type: "arme" }])
}
static #onCreateArmor(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newArmor"), type: "armor" }])
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newArmor"), type: "armure" }])
}
static #onCreatePerk(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newPerk"), type: "perk" }])
}
static async #onModifyAmmo(event, target) {
const quantity = parseInt($(event.target).data("quantity"))
const itemId = $(event.target).data("item-id")
const item = this.document.items.get(itemId)
if (!item) {
console.warn(`HamalronCharacterSheet: Item with ID ${itemId} not found`)
return
}
const currentAmmo = item.system.ammoQuantity || 0
const newAmmo = Math.max(0, currentAmmo + quantity) // Ensure ammo doesn't go below 0
await item.update({ "system.ammoQuantity": newAmmo })
static #onCreateSortilege(event, target) {
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newSortilege"), type: "sortilege" }])
}
static async #onModifyResistance(event, target) {
@@ -326,7 +278,7 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
const { default: HamalronTirageTarot } = await import("../../documents/tirage-tarot.mjs")
await HamalronTirageTarot.handleLeMatEffect(this.document, deckManager)
} else {
const originalItem = game.items.get(newCard.id)
const originalItem = await HamalronUtils.findTarotItem(newCard)
if (originalItem) {
await this.document.createEmbeddedDocuments("Item", [originalItem.toObject()])
ui.notifications.info(game.i18n.localize("HAMALRON.Notifications.cardDiscardedAndDrawn") + newCard.name)
@@ -337,7 +289,7 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
ui.notifications.info(game.i18n.localize("HAMALRON.Notifications.cardDiscardedNoNew"))
}
deckManager.saveDeckState()
await deckManager.saveDeckState()
deckManager.render()
}
@@ -382,14 +334,13 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
const { default: HamalronTirageTarot } = await import("../../documents/tirage-tarot.mjs")
await HamalronTirageTarot.handleLeMatEffect(this.document, deckManager)
} else {
// Trouver l'item original pour le dupliquer
const originalItem = game.items.get(newCard.id)
const originalItem = await HamalronUtils.findTarotItem(newCard)
if (originalItem) {
await this.document.createEmbeddedDocuments("Item", [originalItem.toObject()])
ui.notifications.info(game.i18n.localize("HAMALRON.Notifications.newCardDrawn") + newCard.name)
}
}
deckManager.saveDeckState()
await deckManager.saveDeckState()
deckManager.render()
}
}
@@ -426,7 +377,7 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
switch (rollType) {
case "stat":
statKey = $(event.currentTarget).data("stat-id");
item = this.actor.system.stats[statKey];
item = this.actor.system?.stats?.[statKey];
break
case "weapon":
case "damage":
@@ -436,7 +387,7 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
default:
throw new Error(`Unknown roll type ${rollType}`)
}
await this.document.system.roll(rollType, item)
if (item) await this.document.system.roll(rollType, item)
}
async _onDrop(event) {
@@ -456,21 +407,11 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
const currentTarotCount = this.document.items.filter(i => i.type === "tarot").length
// Check if we already have 8 cards (hard limit)
if (currentTarotCount >= 8) {
if (currentTarotCount >= 7) {
ui.notifications.warn(game.i18n.localize("HAMALRON.Notifications.tarotHandFull"))
return
}
}
if (item.type === "species-trait") {
// Check if the item is already in the actor
const existingTrait = this.document.items.find(i => i.type === "species-trait" && i.name === item.name)
if (existingTrait) {
await existingTrait.delete()
// Display info message
ui.notifications.info(game.i18n.localize("HAMALRON.Notifications.speciesTraitDeleted") + existingTrait.name)
}
}
return super._onDropItem(item)
}
@@ -3,12 +3,12 @@ import HamalronItemSheet from "./base-item-sheet.mjs"
export default class HamalronSortilegeSheet extends HamalronItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["ritual"],
classes: ["sortilege"],
position: {
width: 600,
},
window: {
contentClasses: ["ritual-content"],
contentClasses: ["sortilege-content"],
},
}
+8
View File
@@ -120,6 +120,13 @@ export const NIVEAU_COMPETENCES = {
}
}
export const CHOICE_ADVANTAGES_DISADVANTAGES = {
"0": { id: "0", label: "Aucun" },
"1": { id: "1", label: "1" },
"2": { id: "2", label: "2" },
"3": { id: "3", label: "3" },
}
/**
* Include all constant definitions within the SYSTEM global export
* @type {Object}
@@ -135,5 +142,6 @@ export const SYSTEM = {
ENEMY_TYPES: ENEMY_TYPES,
SORTILEGE_CATEGORIES: SORTILEGE_CATEGORIES,
NIVEAU_COMPETENCES: NIVEAU_COMPETENCES,
CHOICE_ADVANTAGES_DISADVANTAGES,
ASCII
}
+10 -36
View File
@@ -1,43 +1,17 @@
import HamalronUtils from "../utils.mjs"
export default class HamalronActor extends Actor {
static async create(data, options) {
// Case of compendium global import
if (data instanceof Array) {
return super.create(data, options);
}
// If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic
if (data.items) {
let actor = super.create(data, options);
return actor;
}
if (data.type === 'character') {
}
return super.create(data, options);
}
_onUpdate(changed, options, userId) {
return super._onUpdate(changed, options, userId)
}
async _preCreate(data, options, user) {
await super._preCreate(data, options, user)
await super._preCreate(data, options, user)
// Configure prototype token settings
const prototypeToken = {}
if (this.type === "character") {
Object.assign(prototypeToken, {
sight: { enabled: true },
actorLink: true,
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY,
})
this.updateSource({ prototypeToken })
if (this.type === "personnage") {
this.updateSource({
prototypeToken: {
sight: { enabled: true },
actorLink: true,
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY,
}
})
}
}
}
}
+2 -3
View File
@@ -1,9 +1,8 @@
import HamalronTirageTarot from "./tirage-tarot.mjs"
export default class HamalronChatMessage extends ChatMessage {
async _renderRollContent(messageData) {
const data = messageData.message
if (this.rolls[0] instanceof HamalronRoll) {
const { default: HamalronTirageTarot } = await import("./tirage-tarot.mjs")
if (this.rolls[0] instanceof HamalronTirageTarot) {
const isPrivate = !this.isContentVisible
// _renderRollHTML va appeler render sur tous les rolls
const rollHTML = await this._renderRollHTML(isPrivate)
+53 -60
View File
@@ -1,7 +1,8 @@
import { SYSTEM } from "../config/system.mjs"
import HamalronUtils from "../utils.mjs"
export default class HamalronTirageTarot {
export default class HamalronTirageTarot extends Roll {
/**
* The HTML template path used to render dice checks of this type
* @type {string}
@@ -41,7 +42,7 @@ export default class HamalronTirageTarot {
const newCards = deckManager.drawCards(1)
if (newCards.length > 0) {
const newCard = newCards[0]
const originalItem = game.items.get(newCard.id)
const originalItem = await HamalronUtils.findTarotItem(newCard)
if (originalItem) {
await actor.createEmbeddedDocuments("Item", [originalItem.toObject()])
}
@@ -50,12 +51,42 @@ export default class HamalronTirageTarot {
}
// 3. Regrouper la défausse et le deck, puis mélanger
deckManager.deck.push(...deckManager.discard)
deckManager.discard = []
deckManager.shuffleDeck()
await deckManager.saveDeckState()
await deckManager.resetDeck()
deckManager.render()
// 4. Proposer aux autres joueurs de renouveler leur main
if (game.user.isGM) {
const refreshOthers = await foundry.applications.api.DialogV2.confirm({
window: { title: "Le Mat - Renouvellement" },
content: `<p>Le Mat a été tiré ! Les autres joueurs peuvent renouveler leur main s'ils le désirent.</p>`,
rejectClose: false,
modal: true,
})
if (refreshOthers) {
for (const otherActor of game.actors.filter(a => a.type === "personnage")) {
if (otherActor.id === actor.id) continue
const otherCards = otherActor.items.filter(i => i.type === "tarot")
if (otherCards.length > 0) {
const otherCardIds = otherCards.map(c => c.id)
await otherActor.deleteEmbeddedDocuments("Item", otherCardIds)
for (let i = 0; i < 7; i++) {
if (deckManager.deck.length > 0) {
const newCards = deckManager.drawCards(1)
if (newCards.length > 0) {
const originalItem = await HamalronUtils.findTarotItem(newCards[0])
if (originalItem) {
await otherActor.createEmbeddedDocuments("Item", [originalItem.toObject()])
}
}
}
}
}
}
await deckManager.saveDeckState()
deckManager.render()
}
}
ui.notifications.info(`Le Mat ! Toutes vos cartes ont été défaussées et vous recevez 7 nouvelles cartes. Le deck a été mélangé.`)
}
@@ -78,14 +109,10 @@ export default class HamalronTirageTarot {
if (options.tarotCards && actor.system.cartesSucces) {
const competenceSymbole = options.rollItem.system.carteSymbole
options.tarotCards = options.tarotCards.map(card => {
// Pour qu'une carte soit une carte de succès :
// 1. Elle ne doit pas être un atout
// 2. Son symbole doit correspondre à celui de la compétence
// 3. Sa valeur doit correspondre à celle d'une carte de succès pour ce symbole
const isSuccessCard = card.system.type !== "atout" &&
card.system.symbole === competenceSymbole &&
actor.system.cartesSucces[competenceSymbole] &&
card.system.valeur === actor.system.cartesSucces[competenceSymbole].value.toString()
String(card.system.valeur) === String(actor.system.cartesSucces[competenceSymbole].value)
// Créer un objet simple avec toutes les propriétés nécessaires
return {
id: card.id,
@@ -103,8 +130,10 @@ export default class HamalronTirageTarot {
let formula = options.rollItem.system.damage
if (options.rollItem?.system?.damageStat && options.rollItem.system.damageStat !== "none" && options.rollItem.system.damageStat !== "") {
let statKey = options.rollItem.system?.damageStat.toLowerCase()
let statValue = actor.system.stats[statKey].value
formula = `${formula} + ${statValue}`
let statValue = actor.system?.stats?.[statKey]?.value
if (statValue != null) {
formula = `${formula} + ${statValue}`
}
}
let damageRoll = new Roll(formula)
await damageRoll.evaluate()
@@ -117,8 +146,10 @@ export default class HamalronTirageTarot {
case "weapon":
{
options.weapon = foundry.utils.duplicate(options.rollItem)
let statKey = options.weapon.system.stat.toLowerCase()
options.rollItem = actor.system.stats[statKey]
let statKey = options.weapon.system?.stat?.toLowerCase()
if (statKey && actor.system?.stats?.[statKey]) {
options.rollItem = actor.system.stats[statKey]
}
}
break
default:
@@ -200,25 +231,6 @@ export default class HamalronTirageTarot {
},
rejectClose: false, // Click on Close button will not launch an error
render: (event, dialog) => {
$(".roll-stat-advantages").change(event => {
options.nbAdvantages = Number(event.target.value)
HamalronTirageTarot.updateFullFormula(options)
})
$(".roll-stat-disadvantages").change(event => {
options.nbDisadvantages = Number(event.target.value)
HamalronTirageTarot.updateFullFormula(options)
})
$(".select-combat-option").change(event => {
let field = $(event.target).data("field")
let modifier = SYSTEM.ATTACK_MODIFIERS[field]
if (event.target.checked) {
options.numericModifier += modifier
} else {
options.numericModifier -= modifier
}
HamalronTirageTarot.updateFullFormula(options)
})
// Gestion de la sélection de carte pour les compétences
if (options.rollType === "competence") {
let selectedCardId = null
@@ -342,16 +354,11 @@ export default class HamalronTirageTarot {
if (selectedCard) {
cardSymboleMatch = selectedCard.system.symbole === options.rollItem.system.carteSymbole
// Vérifier si c'est une carte de succès (les atouts ne peuvent jamais être des cartes de succès)
// Pour qu'une carte soit une carte de succès :
// 1. Elle ne doit pas être un atout
// 2. Son symbole doit correspondre à celui de la compétence
// 3. Sa valeur doit correspondre à celle d'une carte de succès pour ce symbole
if (actor.system.cartesSucces && selectedCard.system.type !== "atout") {
const competenceSymbole = options.rollItem.system.carteSymbole
isAutomaticSuccess = selectedCard.system.symbole === competenceSymbole &&
actor.system.cartesSucces[competenceSymbole] &&
selectedCard.system.valeur === actor.system.cartesSucces[competenceSymbole].value.toString()
String(selectedCard.system.valeur) === String(actor.system.cartesSucces[competenceSymbole].value)
}
}
}
@@ -359,7 +366,7 @@ export default class HamalronTirageTarot {
// Préparer les données pour le chat
const difficultyData = SYSTEM.DIFFICULTY_CHOICES[options.selectedDifficulty || "moyenne"]
const isSousTensionOppose = options.selectedDifficulty === "soustension_oppose"
const isSuccess = isAutomaticSuccess || totalValue > options.targetScore
const isSuccess = isAutomaticSuccess || totalValue >= options.targetScore
// Détecter si la carte est un valet
const isValet = selectedCard?.system.valeur === "valet"
@@ -468,21 +475,7 @@ export default class HamalronTirageTarot {
if (newCards.length > 0) {
const newCard = newCards[0]
console.log("New card to add:", newCard)
// Trouver l'item tarot dans game.items
let originalItem = game.items.get(newCard.id)
console.log("Original item found by ID:", originalItem)
// Si pas trouvé dans game.items, chercher dans tous les items disponibles
if (!originalItem) {
console.log("Searching for card in game.items by properties...")
originalItem = game.items.find(i =>
i.type === "tarot" &&
i.name === newCard.name &&
i.system.symbole === newCard.symbole &&
i.system.valeur === newCard.valeur
)
console.log("Original item found by search:", originalItem)
}
const originalItem = await HamalronUtils.findTarotItem(newCard)
if (originalItem) {
// Cas spécial : Le Mat
@@ -505,7 +498,7 @@ export default class HamalronTirageTarot {
console.log("Deck is empty, cannot draw new card")
}
deckManager.saveDeckState()
await deckManager.saveDeckState()
deckManager.render()
ui.notifications.info(`${game.i18n.localize("HAMALRON.Notifications.cardUsed")}: ${selectedCard.name}`)
}
@@ -673,7 +666,7 @@ export default class HamalronTirageTarot {
* @returns {Promise} - A promise that resolves when the message is created.
*/
async toMessage(messageData = {}, { rollMode, create = true } = {}) {
super.toMessage(
return super.toMessage(
{
isFailure: this.resultType === "failure",
actingCharName: this.actorName,
@@ -682,7 +675,7 @@ export default class HamalronTirageTarot {
realDamage: this.realDamage,
...messageData,
},
{ rollMode: rollMode },
{ rollMode, create },
)
}
-1
View File
@@ -19,7 +19,6 @@ export default class HamalronArme extends foundry.abstract.TypeDataModel {
prepareDerivedData() {
super.prepareDerivedData();
let actor = this.parent?.actor;
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ export default class HamalronArmure extends foundry.abstract.TypeDataModel {
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.protection = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cout = new fields.NumberField({ required: true, initial: 0, min: 0 })
return schema
}
+1 -1
View File
@@ -9,7 +9,7 @@ export default class HamalronEquipement extends foundry.abstract.TypeDataModel {
schema.description = new fields.HTMLField({ required: true, textSearch: true })
schema.quantity = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true })
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0 })
schema.cout = new fields.NumberField({ required: true, initial: 0, min: 0 })
return schema
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { SYSTEM } from "../config/system.mjs";
export default class HamalronFaction extends foundry.abstract.TypeDataModel {
export default class HamalronLangue extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
const schema = {};
@@ -12,5 +12,5 @@ export default class HamalronFaction extends foundry.abstract.TypeDataModel {
}
/** @override */
static LOCALIZATION_PREFIXES = ["HAMALRON.Faction"];
static LOCALIZATION_PREFIXES = ["HAMALRON.Langue"];
}
+1 -5
View File
@@ -66,17 +66,13 @@ export default class HamalronPersonnage extends foundry.abstract.TypeDataModel {
* @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled.
*/
async roll(rollType, rollItem) {
let opponentTarget
const hasTarget = opponentTarget !== undefined
let roll = await HamalronTirageTarot.prompt({
rollType,
rollItem,
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
hasTarget,
target: opponentTarget
hasTarget: false,
})
if (!roll) return null
+1 -6
View File
@@ -24,18 +24,13 @@ export default class HamalronPNJ extends foundry.abstract.TypeDataModel {
static LOCALIZATION_PREFIXES = ["HAMALRON.PNJ"]
async roll(rollType, rollItem) {
let opponentTarget
const hasTarget = opponentTarget !== undefined
let roll = await HamalronTirageTarot.prompt({
rollType,
rollItem,
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
traits: this.parent.items.filter(i => i.type === "trait"),
hasTarget,
target: opponentTarget
hasTarget: false,
})
if (!roll) return null
+1 -1
View File
@@ -13,6 +13,6 @@ export default class HamalronSortilege extends foundry.abstract.TypeDataModel {
}
/** @override */
static LOCALIZATION_PREFIXES = ["HAMALRON.Ritual"]
static LOCALIZATION_PREFIXES = ["HAMALRON.Sortilege"]
}
+1 -1
View File
@@ -15,7 +15,7 @@ export default class HamalronTarot extends foundry.abstract.TypeDataModel {
schema.image = new fields.FilePathField({
required: false,
categories: ["IMAGE"],
default: "icons/svg/treasure.svg",
initial: "icons/svg/treasure.svg",
})
return schema;
+25 -13
View File
@@ -1,19 +1,6 @@
import { SYSTEM } from "./config/system.mjs"
export default class HamalronUtils {
static registerSettings() {
game.settings.register("fvtt-hamalron", "settings-era", {
name: game.i18n.localize("HAMALRON.Settings.era"),
hint: game.i18n.localize("HAMALRON.Settings.eraHint"),
default: "jazz",
scope: "world",
type: String,
choices: SYSTEM.AVAILABLE_SETTINGS,
config: true,
onChange: _ => window.location.reload()
});
}
static async loadCompendiumData(compendium) {
@@ -26,6 +13,31 @@ export default class HamalronUtils {
return compendiumData.filter(filter)
}
static async findTarotItem(cardData) {
if (!cardData) return null
let item = game.items.get(cardData.id)
if (item) return item
item = game.items.find(i =>
i.type === "tarot" &&
i.name === cardData.name &&
i.system.symbole === cardData.symbole &&
String(i.system.valeur) === String(cardData.valeur)
)
if (item) return item
for (const pack of game.packs) {
if (pack.documentName !== "Item") continue
const index = pack.index.find(e =>
e.type === "tarot" &&
e.name === cardData.name
)
if (index) {
item = await pack.getDocument(index._id)
if (item) return item
}
}
return null
}
static registerHandlebarsHelpers() {
Handlebars.registerHelper('isNull', function (val) {