Workable system, firt step

This commit is contained in:
2026-01-02 20:30:29 +01:00
parent 70d1720176
commit 8a27cef9a4
69 changed files with 6081 additions and 1767 deletions
+396 -4
View File
@@ -8,6 +8,57 @@ export default class HamalronTirageTarot {
*/
static CHAT_TEMPLATE = "systems/fvtt-hamalron/templates/chat-message.hbs"
/**
* Handle Le Mat special effect when distributed to a player
* @param {Actor} actor - The actor who received Le Mat
* @param {HamalronTarotDeckManager} deckManager - The deck manager instance
*/
static async handleLeMatEffect(actor, deckManager) {
// 1. Défausser toutes les cartes du joueur
const allTarotCards = actor.items.filter(i => i.type === "tarot")
if (allTarotCards.length > 0) {
const cardIds = allTarotCards.map(c => c.id)
await actor.deleteEmbeddedDocuments("Item", cardIds)
console.log(`Discarded ${cardIds.length} cards from player`)
// Ajouter toutes les cartes à la défausse
for (const card of allTarotCards) {
deckManager.addToDiscard({
id: card.id,
name: card.name,
img: card.img,
symbole: card.system.symbole,
valeur: card.system.valeur,
type: card.system.type,
description: card.system.description,
})
}
}
// 2. Tirer 7 nouvelles cartes pour le joueur
for (let i = 0; i < 7; i++) {
if (deckManager.deck.length > 0) {
const newCards = deckManager.drawCards(1)
if (newCards.length > 0) {
const newCard = newCards[0]
const originalItem = game.items.get(newCard.id)
if (originalItem) {
await actor.createEmbeddedDocuments("Item", [originalItem.toObject()])
}
}
}
}
// 3. Regrouper la défausse et le deck, puis mélanger
deckManager.deck.push(...deckManager.discard)
deckManager.discard = []
deckManager.shuffleDeck()
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é.`)
}
static async prompt(options = {}) {
let formula = `3D6 + 0D6KH - 0D6KH + ${options?.rollItem?.value}`
@@ -15,6 +66,34 @@ export default class HamalronTirageTarot {
switch (options.rollType) {
case "stat":
break
case "competence":
{
// Récupérer les cartes de tarot du personnage
options.tarotCards = actor.items.filter(i => i.type === "tarot")
// Récupérer le modificateur de compétence depuis NIVEAU_COMPETENCES
const niveauData = SYSTEM.NIVEAU_COMPETENCES[options.rollItem.system.niveau]
options.competenceModifier = niveauData ? niveauData.modificateur : 0
// Marquer les cartes de succès
if (options.tarotCards && actor.system.cartesSucces) {
options.tarotCards = options.tarotCards.map(card => {
const isSuccessCard = card.system.type !== "atout" && Object.keys(actor.system.cartesSucces).some(symbole => {
const successCardData = actor.system.cartesSucces[symbole]
return card.system.symbole === symbole &&
card.system.valeur === successCardData.value.toString()
})
// Créer un objet simple avec toutes les propriétés nécessaires
return {
id: card.id,
name: card.name,
img: card.img,
system: card.system,
isSuccessCard: isSuccessCard
}
})
}
}
break
case "damage":
{
let formula = options.rollItem.system.damage
@@ -56,6 +135,7 @@ export default class HamalronTirageTarot {
let dialogContext = {
actorId: options.actorId,
actorName: options.actorName,
actorImage: options.actorImage,
rollType: options.rollType,
rollItem: foundry.utils.duplicate(options.rollItem), // Object only, no class
weapon: options?.weapon,
@@ -69,23 +149,45 @@ export default class HamalronTirageTarot {
difficulty: "0",
nbAdvantages: "0",
nbDisadvantages: "0",
tarotCards: options.tarotCards || [],
competenceModifier: options.competenceModifier || 0,
difficultyChoices: Object.fromEntries(Object.entries(SYSTEM.DIFFICULTY_CHOICES).map(([key, val]) => [key, val.label])),
}
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-hamalron/templates/roll-dialog.hbs", dialogContext)
const title = HamalronRoll.createTitle(options.rollType, options.rollTarget)
const title = HamalronTirageTarot.createTitle(options.rollType, options.rollTarget)
const label = game.i18n.localize("HAMALRON.Roll.roll")
const cancelLabel = game.i18n.localize("HAMALRON.Roll.cancel")
const rollContext = await foundry.applications.api.DialogV2.wait({
window: { title: title },
classes: ["fvtt-hamalron"],
position: { width: 420 },
content,
buttons: [
{
action: "cancel",
label: cancelLabel,
default: false,
},
{
action: "roll",
label: label,
default: true,
disabled: options.rollType === "competence",
callback: (event, button, dialog) => {
const output = Array.from(button.form.elements).reduce((obj, input) => {
if (input.name) obj[input.name] = input.value
return obj
}, {})
// Ajouter les données de sélection de carte pour les compétences
if (options.rollType === "competence") {
console.log("DEBUG CALLBACK: options.selectedCardId =", options.selectedCardId, "options.selectedCardValue =", options.selectedCardValue)
output.selectedCardId = options.selectedCardId
output.selectedCardValue = options.selectedCardValue
output.selectedDifficulty = options.selectedDifficulty
output.targetScore = options.targetScore
console.log("DEBUG CALLBACK: output =", output)
}
return output
},
},
@@ -96,11 +198,11 @@ export default class HamalronTirageTarot {
render: (event, dialog) => {
$(".roll-stat-advantages").change(event => {
options.nbAdvantages = Number(event.target.value)
HamalronRoll.updateFullFormula(options)
HamalronTirageTarot.updateFullFormula(options)
})
$(".roll-stat-disadvantages").change(event => {
options.nbDisadvantages = Number(event.target.value)
HamalronRoll.updateFullFormula(options)
HamalronTirageTarot.updateFullFormula(options)
})
$(".select-combat-option").change(event => {
let field = $(event.target).data("field")
@@ -110,14 +212,302 @@ export default class HamalronTirageTarot {
} else {
options.numericModifier -= modifier
}
HamalronRoll.updateFullFormula(options)
HamalronTirageTarot.updateFullFormula(options)
})
// Gestion de la sélection de carte pour les compétences
if (options.rollType === "competence") {
let selectedCardId = null
options.selectedDifficulty = "soustension_oppose"
options.targetScore = null
// Calculer et afficher les valeurs pour chaque carte
const competenceSymbole = options.rollItem.system.carteSymbole
$(".tarot-card-select").each(function () {
const cardSymbole = $(this).data("card-symbole")
const cardValeur = $(this).data("card-valeur")
const cardType = $(this).data("card-type")
const valeurData = SYSTEM.TAROT_VALEURS[cardValeur]
const baseValue = valeurData ? valeurData.value : 0
let calculatedValue = 0
if (cardType === "atout") {
calculatedValue = baseValue
} else if (cardSymbole === competenceSymbole) {
calculatedValue = baseValue
} else {
calculatedValue = Math.ceil(baseValue / 2)
}
$(this).find(".card-values").text(`${baseValue}${calculatedValue}`)
})
// Gestion du changement de difficulté
$(".difficulty-select").change(function () {
const difficultyKey = $(this).val()
options.selectedDifficulty = difficultyKey
const difficultyData = SYSTEM.DIFFICULTY_CHOICES[difficultyKey]
if (difficultyData.standard === null) {
// Sous tension ou opposé - pas de score cible
options.targetScore = null
$("#target-score").text("-")
} else {
options.targetScore = difficultyData.standard
$("#target-score").text(options.targetScore)
}
})
$(".tarot-card-select").click(function () {
// Désélectionner toutes les cartes
$(".tarot-card-select").removeClass("selected")
// Sélectionner la carte cliquée
$(this).addClass("selected")
selectedCardId = $(this).data("card-id")
console.log("DEBUG CLICK: selectedCardId =", selectedCardId)
const cardSymbole = $(this).data("card-symbole")
const cardValeur = $(this).data("card-valeur")
const cardType = $(this).data("card-type")
// Calculer la valeur de la carte
const competenceSymbole = options.rollItem.system.carteSymbole
let cardValue = 0
// Récupérer la valeur numérique de la carte depuis TAROT_VALEURS
const valeurData = SYSTEM.TAROT_VALEURS[cardValeur]
const baseValue = valeurData ? valeurData.value : 0
if (cardType === "atout") {
// Si c'est un atout, prendre directement la valeur
cardValue = baseValue
} else if (cardSymbole === competenceSymbole) {
// Si le symbole correspond, prendre la valeur complète
cardValue = baseValue
} else {
// Sinon, diviser par 2 arrondi au supérieur
cardValue = Math.ceil(baseValue / 2)
}
// Stocker la carte sélectionnée et sa valeur calculée
options.selectedCardId = selectedCardId
options.selectedCardValue = cardValue
console.log("DEBUG CLICK END: options.selectedCardId =", options.selectedCardId, "options.selectedCardValue =", options.selectedCardValue)
// Mettre à jour l'affichage
$("#card-value-display").text(`+ ${cardValue}`)
$("#total-value").text(options.competenceModifier + cardValue)
// Activer le bouton de lancement
$("button[data-action='roll']").prop("disabled", false)
})
// Permettre de désélectionner en cliquant à nouveau
$(".tarot-card-select").dblclick(function () {
$(this).removeClass("selected")
options.selectedCardId = null
options.selectedCardValue = 0
$("#card-value-display").text("+ 0")
$("#total-value").text(options.competenceModifier)
// Désactiver le bouton de lancement
$("button[data-action='roll']").prop("disabled", true)
})
}
}
})
// If the user cancels the dialog, exit
if (rollContext === null) return
// Fusionner les données du dialogue dans options
Object.assign(options, rollContext)
// Gestion spéciale pour les compétences (pas de dés)
if (options.rollType === "competence") {
const totalValue = options.competenceModifier + (options.selectedCardValue || 0)
// Récupérer la carte sélectionnée si elle existe
let selectedCard = null
let cardSymboleMatch = false
let isAutomaticSuccess = false
if (options.selectedCardId) {
selectedCard = actor.items.get(options.selectedCardId)
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)
if (actor.system.cartesSucces && selectedCard.system.type !== "atout") {
isAutomaticSuccess = Object.keys(actor.system.cartesSucces).some(symbole => {
const successCardData = actor.system.cartesSucces[symbole]
return selectedCard.system.symbole === symbole &&
selectedCard.system.valeur === successCardData.value.toString()
})
}
}
}
// 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
// Détecter si la carte est un valet
const isValet = selectedCard?.system.valeur === "valet"
// Détecter si la carte est un cavalier
const isCavalier = selectedCard?.system.valeur === "cavalier"
// Détecter si la carte est une reine
const isReine = selectedCard?.system.valeur === "reine"
// Détecter si la carte est un roi
const isRoi = selectedCard?.system.valeur === "roi"
// Détecter si la carte est un atout
const isAtout = selectedCard?.system.type === "atout"
// Si c'est un cavalier et un échec, transformer en égalité
const isEquality = isCavalier && !isSuccess && !isSousTensionOppose
const chatData = {
cssClass: [SYSTEM.id, "competence-roll"].join(" "),
actingCharImg: options.actorImage,
actingCharName: options.actorName,
actorName: options.actorName,
competence: options.rollItem,
competenceModifier: options.competenceModifier,
selectedCard: selectedCard,
cardBonus: options.selectedCardValue || 0,
cardSymboleMatch: cardSymboleMatch,
totalValue: totalValue,
difficulty: difficultyData.label,
targetScore: options.targetScore,
isSuccess: isSuccess,
isAutomaticSuccess: isAutomaticSuccess,
isSousTensionOppose: isSousTensionOppose,
isValet: isValet,
isCavalier: isCavalier,
isReine: isReine,
isRoi: isRoi,
isAtout: isAtout,
isEquality: isEquality,
}
// Créer le message de chat
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-hamalron/templates/chat-competence.hbs",
chatData
)
const messageData = {
user: game.user.id,
speaker: ChatMessage.getSpeaker({ actor }),
content: content,
style: CONST.CHAT_MESSAGE_STYLES.OTHER,
}
// Appliquer le mode de visibilité
ChatMessage.applyRollMode(messageData, rollContext.visibility)
await ChatMessage.create(messageData)
// Gérer la carte utilisée
console.log("DEBUG: selectedCard =", selectedCard, "options.selectedCardId =", options.selectedCardId)
if (selectedCard) {
console.log("Managing used card:", selectedCard.name)
// Supprimer la carte de l'acteur
const cardItem = actor.items.get(selectedCard.id)
if (cardItem) {
await actor.deleteEmbeddedDocuments("Item", [selectedCard.id])
console.log("Card deleted from actor")
}
// Récupérer le deck manager singleton
const module = await import("../applications/tarot-deck-manager.mjs")
const HamalronTarotDeckManager = module.default
let deckManager = HamalronTarotDeckManager._instance
// Si le deck manager n'existe pas, le créer
if (!deckManager) {
console.log("Deck manager not initialized, creating new instance...")
deckManager = new HamalronTarotDeckManager()
await deckManager.resetDeck()
}
console.log("Deck manager instance:", deckManager)
if (deckManager) {
console.log("Deck manager is available")
// Ajouter la carte à la défausse
deckManager.addToDiscard({
id: selectedCard.id,
name: selectedCard.name,
img: selectedCard.img,
symbole: selectedCard.system.symbole,
valeur: selectedCard.system.valeur,
type: selectedCard.system.type,
description: selectedCard.system.description,
})
console.log("Card added to discard")
// Tirer une nouvelle carte automatiquement
console.log("Attempting to draw new card. Deck length:", deckManager.deck.length)
if (deckManager.deck.length > 0) {
const newCards = deckManager.drawCards(1)
console.log("Cards drawn:", newCards)
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)
}
if (originalItem) {
// Cas spécial : Le Mat
if (newCard.valeur === "lemat") {
console.log("Le Mat drawn! Executing special rules...")
await HamalronTirageTarot.handleLeMatEffect(actor, deckManager)
} else {
console.log("Creating embedded document for actor...")
await actor.createEmbeddedDocuments("Item", [originalItem.toObject()])
console.log("New card added to actor")
ui.notifications.info(`${game.i18n.localize("HAMALRON.Notifications.newCardDrawn")}: ${newCard.name}`)
}
} else {
console.warn("Original item not found for card:", newCard)
}
} else {
console.log("No cards were drawn (empty result)")
}
} else {
console.log("Deck is empty, cannot draw new card")
}
deckManager.saveDeckState()
deckManager.render()
ui.notifications.info(`${game.i18n.localize("HAMALRON.Notifications.cardUsed")}: ${selectedCard.name}`)
}
}
return null
}
// Code existant pour les autres types de jets
let rollData = foundry.utils.mergeObject(foundry.utils.duplicate(options), rollContext)
rollData.rollMode = rollContext.visibility
rollData.targetScore = 8
@@ -203,6 +593,8 @@ export default class HamalronTirageTarot {
return `${game.i18n.localize("HAMALRON.Label.titleStat")}`
case "weapon":
return `${game.i18n.localize("HAMALRON.Label.titleWeapon")}`
case "competence":
return `${game.i18n.localize("HAMALRON.Label.titleCompetence")}`
default:
return game.i18n.localize("HAMALRON.Label.titleStandard")
}