Nouveau roll-dialog: mode attaque / defense

This commit is contained in:
2025-09-21 00:15:21 +02:00
parent 607eedc6d2
commit 0730bdf240
52 changed files with 940 additions and 415 deletions

View File

@@ -12,8 +12,8 @@ Pseudo : LeRatierBretonnien
Mainteneur/Développeur : LeRatierBretonnien Mainteneur/Développeur : LeRatierBretonnien
Développeur : VincentVk Développeur : VincentVk
Tests, Compendiums, Données: Fred, Fab, Grendel Tests, Compendiums, Données: Fred, Fab, Grendel, LeRatierBretonnien, VincentVk
Styles/CSS : Mandar Styles/CSS : Mandar, VincentVk
# Mentions Légales # Mentions Légales
@@ -23,6 +23,6 @@ La carte des Terres Médianes du Rêve est une illustration de **Jidus**, utilis
Les silhouettes des créatures, humanoïdes et entités sont des illustrations de **Roland Barthélémy**, et sont utilisés dans le cadre de ce projet avec son aimable autorisation. Les silhouettes des créatures, humanoïdes et entités sont des illustrations de **Roland Barthélémy**, et sont utilisés dans le cadre de ce projet avec son aimable autorisation.
Merci à eux !! Merci à eux !!
Toute la propriété intellectuelle leur appartient, ce système est une adpatation destinée à fonctionner sous FoundryVTT. Toute la propriété intellectuelle leur appartient, ce système est une adaptation destinée à fonctionner sous FoundryVTT.
L'ensemble du code est sous licence Creative Commons. L'ensemble du code est sous licence Creative Commons.

66
assets/actions/weak.svg Normal file
View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 323.9906 329.83231"
version="1.1"
id="svg6"
sodipodi:docname="weak.svg"
width="323.9906"
height="329.83231"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2230"
inkscape:window-height="1388"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.4355469"
inkscape:cx="157.6092"
inkscape:cy="192.4342"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<g
class=""
transform="translate(-98.3908,-91.5658)"
id="g4">
<path
d="M 200.094,21.094 213.969,164.814 83.72,58.343 156.344,198.249 78.624,168.779 112.906,230.905 30.844,228.843 135.874,297 31.75,327.03 141.188,348.188 c 8.39,-48.802 49.597,-85.194 97.75,-105.344 28.916,-12.1 60.67,-18.762 90.75,-18.594 19.237,0.108 37.776,3.024 54.437,9.063 l 48,-119.375 L 350,196.5 369.22,34.72 327.344,130.688 313.47,92.03 l -32.69,83.5 z m 255.78,190.687 c -17.883,-0.093 -38.852,9.04 -55.937,26.126 a 100.103,100.103 0 0 0 -13.562,16.875 C 357.123,237.155 314,237.977 273.095,250.877 c -9.17,2.484 -18.214,5.537 -26.94,9.188 -43.676,18.277 -78.503,49.837 -86.218,89.625 -6.61,30.108 5.37,63.223 47.438,94.843 H 88.062 l -26.437,47.75 H 318.78 l -88.467,-103.25 c 24.27,-26.707 67.457,-43.703 97,-45.06 13.792,45.096 36.233,113.496 71.718,148.31 h 60.876 c -43.07,-46.546 -76.57,-109.087 -81.97,-179.842 a 33.579,33.579 0 0 0 6.314,8.78 c 18.664,18.664 55.945,11.618 83.28,-15.718 27.337,-27.336 34.384,-64.618 15.72,-83.28 -7,-7 -16.645,-10.382 -27.375,-10.44 z"
fill="#ffffff"
fill-opacity="1"
transform="matrix(0.7,0,0,0.7,76.8,76.8)"
id="path2" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -3,6 +3,19 @@
## 13.0.9 - Le combat d'Illysis ## 13.0.9 - Le combat d'Illysis
- Fix - Fix
- La montée en TMR fonctionne - La montée en TMR fonctionne
- Nouvelle fenêtre de jets de dés
- avancement du mode attaque
- choix de tactique
- choix des dommages mortel/non-mortel, affichage
- affichage du statut de surprise de l'attaquant
- affichage du statut de surprise du défenseur
- prise en compte des significatives (force insuffisante, demi-surprises)
- avancement du mode défense
- sélection esquive/parade
- affichage du statut de surprise du défenseur
- prise en compte des significatives (demi-surprises, armes disparates,
force insuffisante, particulière en finesse)
## 13.0.8 - Le renouveau d'Illysis ## 13.0.8 - Le renouveau d'Illysis

View File

@@ -526,6 +526,7 @@ select,
grid-area: selection; grid-area: selection;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin: 0.1rem 0;
} }
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-img { .system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-img {
display: flex; display: flex;
@@ -549,11 +550,20 @@ select,
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline { .system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin: 0.1rem 0;
} }
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline div.poesie-extrait { .system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline div.poesie-extrait {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline span.status-surprise {
display: flex;
flex-direction: row;
flex-flow: wrap;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline span.status-surprise img {
filter: invert(0.8);
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-section selected-numeric-value { .system-foundryvtt-reve-de-dragon .roll-dialog roll-section selected-numeric-value {
display: flow; display: flow;
width: 2.5rem; width: 2.5rem;
@@ -608,6 +618,11 @@ select,
margin: 0 0.2rem; margin: 0 0.2rem;
padding: 0; padding: 0;
} }
.system-foundryvtt-reve-de-dragon .roll-dialog :is(roll-choix, roll-conditions, roll-carac, roll-comp) img.button-effect-img {
max-width: 1rem;
max-height: 1rem;
margin: 0 0.1rem;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-carac select[name="select-carac"] { .system-foundryvtt-reve-de-dragon .roll-dialog roll-carac select[name="select-carac"] {
max-width: 6rem; max-width: 6rem;
} }

View File

@@ -66,6 +66,7 @@
"StatusRestrained": "Immobilisé", "StatusRestrained": "Immobilisé",
"StatusComma": "Comma", "StatusComma": "Comma",
"StatusDead": "Mort", "StatusDead": "Mort",
"StatusDemiReve": "Demi-rêve" "StatusDemiReve": "Demi-rêve",
"StatusForceWeak": "Force insuffisante"
} }
} }

View File

@@ -83,11 +83,14 @@
gap: 0.2rem; gap: 0.2rem;
align-items: start; align-items: start;
subline { subline {
grid-area: selection; grid-area: selection;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin: 0.1rem 0;
} }
roll-part-img { roll-part-img {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -110,10 +113,19 @@
subline { subline {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin: 0.1rem 0;
div.poesie-extrait{ div.poesie-extrait{
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
span.status-surprise{
display: flex;
flex-direction: row;
flex-flow: wrap;
img {
filter: invert(0.8);
}
}
} }
} }
} }
@@ -178,6 +190,11 @@
margin: 0 0.2rem; margin: 0 0.2rem;
padding: 0; padding: 0;
} }
img.button-effect-img {
max-width: 1rem;
max-height: 1rem;
margin: 0 0.1rem;
}
} }
roll-carac select[name="select-carac"] { roll-carac select[name="select-carac"] {

View File

@@ -49,7 +49,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
}); });
foundry.utils.mergeObject(formData.calc, { foundry.utils.mergeObject(formData.calc, {
surenc: this.actor.computeMalusSurEncombrement(), surenc: this.actor.computeMalusSurEncombrement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr, surprise: RdDBonus.find(this.actor.getSurprise(false)).label,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures), resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute), caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.isSurenc() ? "Sur-Encombrement!" : "", surEncombrementMessage: this.actor.isSurenc() ? "Sur-Encombrement!" : "",

View File

@@ -31,7 +31,7 @@ export class ActorToken {
constructor(token) { constructor(token) {
this.name = token.name ?? token.actor.name this.name = token.name ?? token.actor.name
this.img = token.texture.src ?? token.actor.img this.img = token.actor.isToken && token.texture.src ? token.texture.src : token.actor.img
this.actor = token.actor this.actor = token.actor
this.id = token.actor?.id this.id = token.actor?.id
this.token = token this.token = token

View File

@@ -14,7 +14,7 @@ import { STATUSES } from "./settings/status-effects.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"; import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.js"; import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { Draconique } from "./tmr/draconique.js"; import { Draconique } from "./tmr/draconique.js";
import { LIST_CARAC_PERSONNAGE, RdDCarac } from "./rdd-carac.js"; import { CARACS, LIST_CARAC_PERSONNAGE, RdDCarac } from "./rdd-carac.js";
import { DialogConsommer } from "./dialog-item-consommer.js"; import { DialogConsommer } from "./dialog-item-consommer.js";
import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js"; import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js";
import { RollDataAjustements } from "./rolldata-ajustements.js"; import { RollDataAjustements } from "./rolldata-ajustements.js";
@@ -31,9 +31,8 @@ import { ITEM_TYPES } from "./constants.js";
import { RdDBaseActorSang } from "./actor/base-actor-sang.js"; import { RdDBaseActorSang } from "./actor/base-actor-sang.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js"; import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { DialogChoixXpCarac } from "./dialog-choix-xp-carac.js"; import { DialogChoixXpCarac } from "./dialog-choix-xp-carac.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDItemArme } from "./item/arme.js"; import { ATTAQUE_TYPE, RdDItemArme } from "./item/arme.js";
import { RdDItemBlessure } from "./item/blessure.js"; import { RdDItemBlessure } from "./item/blessure.js";
import { RdDItemTete } from "./item/tete.js"; import { RdDItemTete } from "./item/tete.js";
import { RdDItemSort } from "./item-sort.js"; import { RdDItemSort } from "./item-sort.js";
@@ -167,63 +166,63 @@ export class RdDActor extends RdDBaseActorSang {
.find(it => true) .find(it => true)
} }
/* -------------------------------------------- */ isForceInsuffisante(forceRequise) {
/** Retourne une liste triée d'actions d'armes avec le split arme1 main / arme 2 main / lancer */ const force = parseInt(this.system.carac.force.value)
listActionsAttaque() { return forceRequise > force
let actions = [
this.$prepareAttaqueArme(RdDItemArme.empoignade(this)),
this.$prepareAttaqueArme(RdDItemArme.corpsACorps(this)),
]
const armes = this.itemTypes[ITEM_TYPES.arme]
.filter(it => RdDItemArme.isAttaque(it))
.sort(Misc.ascending(it => it.name));
for (const arme of armes) {
if (arme.system.unemain && arme.system.competence) {
actions.push(this.$prepareAttaqueArme(arme, '(1 main)'))
}
if (arme.system.deuxmains && arme.system.competence) {
actions.push(this.$prepareAttaqueArme(arme, '(2 mains)'))
}
if (arme.system.lancer) {
actions.push(this.$prepareAttaqueArme(arme, '(lancer)'))
}
if (arme.system.tir) {
actions.push(this.$prepareAttaqueArme(arme, '(tir)'))
}
}
return actions;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
$prepareAttaqueArme(arme, main) { /** Retourne une liste triée d'actions d'armes avec le split arme1 main / arme 2 main / lancer */
const comp = this.getCompetence(RdDActor.$getCompetenceAction(arme, main)) listActionsAttaque() {
const caracCode = RdDActor.$getCaracAction(comp, main) const actions = []
const caracValue = this.system.carac[caracCode].value const uniques = []
const dommages = arme.system.dommages.toString()
// TODO: déplacer sur RdDItemArme const addAttaque = (arme, main) => {
if (arme.system.unemain && arme.system.deuxmains && !dommages.includes("/")) { const dommagesArme = RdDItemArme.valeurMain(arme.system.dommages, main)
ui.notifications.info(`Les dommages de l'arme à 1/2 mains ${arme.name} ne sont pas corrects (ie sous la forme X/Y)`) const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
} const ecaillesEfficacite = arme.system.magique ? arme.system.ecaille_efficacite : 0;
const tableauDommages = dommages.includes("/") ? dommages.split("/") : [dommages, dommages]
const dmg = main == '(2 mains)' ? tableauDommages[1] : tableauDommages[0]
const niveau = comp?.system.niveau ?? (['(lancer)', '(tir)'].includes(main) ? -8 : -6)
const ajustement = (arme.parent?.getEtatGeneral() ?? 0) + (arme.system.magique) ? arme.system.ecaille_efficacite : 0
return { const comp = this.getCompetence(RdDActor.$getCompetenceAction(arme, main))
name: arme.name + (main ? ' ' + main : ''), const unique = [comp.id, arme.name, dommagesArme, forceRequise, ecaillesEfficacite].join('|');
action: 'attaque', if (uniques.includes(unique)) {
initOnly: false, return
arme: arme, }
comp: comp, uniques.push(unique);
main: main,
carac: { key: caracCode, value: caracValue }, const caracCode = RdDActor.$getCaracAction(comp, main)
equipe: arme.system.equipe, const caracValue = this.system.carac[caracCode].value
dmg: dmg,
initiative: RdDInitiative.calculInitiative(niveau, caracValue, ajustement) const niveau = comp?.system.niveau ?? (['(lancer)', '(tir)'].includes(main) ? -8 : -6)
const ajustement = (arme.parent?.getEtatGeneral() ?? 0) + ecaillesEfficacite
actions.push({
name: arme.name + (main ? ' ' + main : ''),
action: 'attaque',
initOnly: false,
arme: arme,
comp: comp,
main: main,
carac: { key: caracCode, value: caracValue },
equipe: arme.system.equipe,
dommagesArme: dommagesArme,
forceRequise: forceRequise,
initiative: RdDInitiative.calculInitiative(niveau, caracValue, ajustement)
})
} }
addAttaque(RdDItemArme.empoignade(this))
addAttaque(RdDItemArme.corpsACorps(this))
this.itemTypes[ITEM_TYPES.arme]
.filter(it => it.isAttaque())
.sort(Misc.ascending(it => it.name))
.forEach(arme => {
if (arme.system.unemain && arme.system.competence) { addAttaque(arme, ATTAQUE_TYPE.UNE_MAIN) }
if (arme.system.deuxmains && arme.system.competence) { addAttaque(arme, ATTAQUE_TYPE.DEUX_MAINS) }
if (arme.system.lancer) { addAttaque(arme, ATTAQUE_TYPE.LANCER) }
if (arme.system.tir) { addAttaque(arme, ATTAQUE_TYPE.TIR) }
})
return actions
} }
static $getCaracAction(comp, main) { static $getCaracAction(comp, main) {
@@ -239,10 +238,10 @@ export class RdDActor extends RdDBaseActorSang {
static $getCompetenceAction(arme, main) { static $getCompetenceAction(arme, main) {
switch (main) { switch (main) {
case '(1 main)': return arme.competence1Mains() case ATTAQUE_TYPE.UNE_MAIN: return arme.competence1Mains()
case '(2 mains)': return arme.competence2Mains() case ATTAQUE_TYPE.DEUX_MAINS: return arme.competence2Mains()
case '(lancer)': return arme.system.lancer case ATTAQUE_TYPE.LANCER: return arme.system.lancer
case '(tir)': return arme.system.tir case ATTAQUE_TYPE.TIR: return arme.system.tir
default: return arme.system.competence default: return arme.system.competence
} }
} }
@@ -2278,13 +2277,12 @@ export class RdDActor extends RdDBaseActorSang {
static _getComposantsCaracDerivee(caracName) { static _getComposantsCaracDerivee(caracName) {
switch (Grammar.toLowerCaseNoAccent(caracName)) { switch (Grammar.toLowerCaseNoAccent(caracName)) {
case 'reve-actuel': case 'reve actuel': return ['reve'] case CARACS.REVE_ACTUEL: case 'reve actuel': return [CARACS.REVE]
case 'chance-actuelle': case 'chance actuelle': return ['chance'] case CARACS.CHANCE_ACTUELLE: case 'chance actuelle': return [CARACS.CHANCE]
case 'vie': return ['constitution'] case CARACS.TIR: return [CARACS.DEXTERITE, CARACS.VUE]
case 'tir': return ['vue', 'dexterite'] case CARACS.LANCER: return [CARACS.FORCE, CARACS.DEXTERITE, CARACS.VUE]
case 'lancer': return ['force', 'dexterite', 'vue'] case CARACS.MELEE: return [CARACS.FORCE, CARACS.AGILITE]
case 'melee': return ['force', 'agilite'] case CARACS.DEROBEE: return [CARACS.AGILITE]
case 'derobee': return ['agilite']
} }
return [] return []
} }
@@ -3249,3 +3247,4 @@ export class RdDActor extends RdDBaseActorSang {
} }
} }

View File

@@ -14,9 +14,9 @@ import { RdDConfirm } from "../rdd-confirm.js";
import { RdDCarac } from "../rdd-carac.js"; import { RdDCarac } from "../rdd-carac.js";
import { RdDRollResult } from "../rdd-roll-result.js"; import { RdDRollResult } from "../rdd-roll-result.js";
import { RdDItemArme } from "../item/arme.js";
import { RdDItemCompetence } from "../item-competence.js"; import { RdDItemCompetence } from "../item-competence.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js"; import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDItemArme } from "../item/arme.js";
import { ChatUtility } from "../chat-utility.js"; import { ChatUtility } from "../chat-utility.js";
import { DialogValidationEncaissement } from "../dialog-validation-encaissement.js"; import { DialogValidationEncaissement } from "../dialog-validation-encaissement.js";
@@ -80,7 +80,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
getBonusDegat() { return RdDCarac.getCaracDerivee(this.getEncombrementMax()).plusdom } getBonusDegat() { return RdDCarac.getCaracDerivee(this.getEncombrementMax()).plusdom }
getMoralTotal() { return 0 } getMoralTotal() { return 0 }
listeAmoureux() {return []} listeAmoureux() { return [] }
getProtectionNaturelle() { return Number(this.system.attributs?.protection?.value ?? 0) } getProtectionNaturelle() { return Number(this.system.attributs?.protection?.value ?? 0) }
getSConst() { return 0 } getSConst() { return 0 }
@@ -113,7 +113,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
listActions({ isAttaque = false, isEquipe = false }) { listActions({ isAttaque = false, isEquipe = false }) {
return this.itemTypes[ITEM_TYPES.competencecreature] return this.itemTypes[ITEM_TYPES.competencecreature]
.filter(it => RdDItemCompetenceCreature.isAttaque(it)) .filter(it => it.isAttaque())
.map(it => RdDItemCompetenceCreature.attaqueCreature(it)) .map(it => RdDItemCompetenceCreature.attaqueCreature(it))
.filter(it => it != undefined); .filter(it => it != undefined);
} }
@@ -191,6 +191,10 @@ export class RdDBaseActorReve extends RdDBaseActor {
return RdDItemArme.getArme(armeParadeId ? this.getEmbeddedDocument('Item', armeParadeId) : undefined) return RdDItemArme.getArme(armeParadeId ? this.getEmbeddedDocument('Item', armeParadeId) : undefined)
} }
isForceInsuffisante(forceRequise) {
return false
}
getDraconicOuPossession() { return POSSESSION_SANS_DRACONIC } getDraconicOuPossession() { return POSSESSION_SANS_DRACONIC }
getPossession(possessionId) { getPossession(possessionId) {
@@ -222,8 +226,13 @@ export class RdDBaseActorReve extends RdDBaseActor {
/* -------------------------------------------- */ /* -------------------------------------------- */
isEffectAllowed(effectId) { return false } isEffectAllowed(effectId) { return false }
getEffects(filter = e => true) { getEffects(filter = e => true, forceRequise = undefined) {
return this.getEmbeddedCollection("ActiveEffect").filter(filter); const effects = this.getEmbeddedCollection("ActiveEffect").filter(filter)
if (forceRequise && this.isForceInsuffisante(forceRequise)) {
/// TODO
effects.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
}
return effects
} }
getEffectByStatus(statusId) { getEffectByStatus(statusId) {
@@ -259,11 +268,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
} }
getSurprise(isCombat = undefined) { getSurprise(isCombat = undefined) {
return StatusEffects.typeSurprise( return StatusEffects.getSurprise(this.getEffects(), isCombat)
this.getEffects()
.map(it => StatusEffects.niveauSurprise(it, isCombat))
.reduce(Misc.sum(), 0)
)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@@ -5,7 +5,6 @@ import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { Monnaie } from "../item-monnaie.js"; import { Monnaie } from "../item-monnaie.js";
import { ITEM_TYPES } from "../constants.js"; import { ITEM_TYPES } from "../constants.js";
import { RdDItem } from "../item.js"; import { RdDItem } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js"; import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js";
import { ItemAction } from "../item/item-actions.js"; import { ItemAction } from "../item/item-actions.js";
@@ -56,7 +55,7 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
this._appliquerRechercheObjets(formData.conteneurs, formData.inventaires); this._appliquerRechercheObjets(formData.conteneurs, formData.inventaires);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs); formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.competences.filter(it => it.type == ITEM_TYPES.competencecreature) formData.competences.filter(it => it.type == ITEM_TYPES.competencecreature)
.forEach(it => it.isdommages = RdDItemCompetenceCreature.isDommages(it)) .forEach(it => it.isdommages = it.isDommages())
return formData; return formData;
} }

View File

@@ -1,126 +1,78 @@
import { ITEM_TYPES } from "./constants.js"; import { ITEM_TYPES } from "./constants.js";
import { Grammar } from "./grammar.js";
import { RdDInitiative } from "./initiative.mjs";
import { RdDItem } from "./item.js"; import { RdDItem } from "./item.js";
import { RdDInitiative } from "./initiative.mjs";
export const CATEGORIES_COMPETENCES_CREATURES = {
"generale": { base: 0, label: "Générale" },
"naturelle": { base: 0, label: "Arme naturelle" },
"melee": { base: 0, label: "Mêlée" },
"parade": { base: 0, label: "Parade" },
"tir": { base: 0, label: "Tir" },
"lancer": { base: 0, label: "Lancer" },
"possession": { base: 0, label: "Possession" },
}
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDItemCompetenceCreature extends Item { export class RdDItemCompetenceCreature extends RdDItem {
/* -------------------------------------------- */ static get ITEM_TYPE() { return ITEM_TYPES.competencecreature }
static setRollDataCreature(rollData) {
const code = Grammar.toLowerCaseNoAccentNoSpace(rollData.competence.name);
const selectedCarac = { code: code, label: rollData.competence.name, value: rollData.competence.system.carac_value };
rollData.carac = { [code]: selectedCarac }
rollData.competence.system.defaut_carac = code
rollData.selectedCarac = selectedCarac
rollData.arme = RdDItemCompetenceCreature.armeCreature(rollData.competence);
}
/* -------------------------------------------- */ static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" }
static armeCreature(item) {
const categorieAttaque = RdDItemCompetenceCreature.getCategorieAttaque(item) isParade() { return this.system.iscombat && (this.system.categorie_parade ?? '') != '' }
isBouclier() { return this.system.categorie_parade.includes('bouclier') }
static attaqueCreature(comp) {
const categorieAttaque = comp.getCategorieAttaque()
if (categorieAttaque != undefined) { if (categorieAttaque != undefined) {
// cloner pour ne pas modifier la compétence const initative = RdDInitiative.calculInitiative(comp.system.niveau, comp.system.carac_value);
return foundry.utils.mergeObject(item, { const armeComp = new RdDItem({
action: item.isCompetencePossession() ? 'possession' : 'attaque', name: comp.name,
type: ITEM_TYPES.arme,
img: comp.img,
system: { system: {
competence: item.name, competence: comp.name,
cac: categorieAttaque == "naturelle" ? "naturelle" : "", cac: categorieAttaque == "naturelle" ? "naturelle" : "",
niveau: item.system.niveau, niveau: comp.system.niveau,
initiative: RdDInitiative.calculInitiative(item.system.niveau, item.system.carac_value), initiative: initative,
mortalite: comp.system.mortalite,
dommages: comp.system.dommages,
equipe: true, equipe: true,
resistance: 100, resistance: 100,
dommagesReels: item.system.dommages,
penetration: 0, penetration: 0,
force: 0, force: 0,
rapide: true, rapide: true,
} }
}, { inplace: false, }); });
} const attaque = {
return undefined;
}
static attaqueCreature(comp) {
const categorieAttaque = RdDItemCompetenceCreature.getCategorieAttaque(comp)
if (categorieAttaque != undefined) {
const initative = RdDInitiative.calculInitiative(comp.system.niveau, comp.system.carac_value);
return {
name: comp.name, name: comp.name,
action: comp.isCompetencePossession() ? 'possession' : 'attaque', action: comp.isCompetencePossession() ? 'possession' : 'attaque',
initOnly: false, initOnly: false,
arme: new RdDItem({ arme: armeComp,
name: comp.name,
type: ITEM_TYPES.arme,
img: comp.img,
system: {
competence: comp.name,
cac: categorieAttaque == "naturelle" ? "naturelle" : "",
niveau: comp.system.niveau,
initiative: initative,
equipe: true,
resistance: 100,
dommagesReels: comp.system.dommages,
penetration: 0,
force: 0,
rapide: true,
}
}),
comp: comp, comp: comp,
// main: '',
carac: { key: comp.name, value: comp.system.carac_value }, carac: { key: comp.name, value: comp.system.carac_value },
equipe: true, equipe: true,
mortalite: comp.system.mortalite,
dmg: comp.system.dommages, dmg: comp.system.dommages,
initiative: initative initiative: initative
} };
return attaque
} }
return undefined; return undefined;
} }
/* -------------------------------------------- */ isAttaque() {
static isAttaque(item) { return this.getCategorieAttaque() != undefined
return RdDItemCompetenceCreature.getCategorieAttaque(item) != undefined
} }
static getCategorieAttaque(item) { getCategorieAttaque() {
if (item.type == ITEM_TYPES.competencecreature) { switch (this.system.categorie) {
switch (item.system.categorie) { case "melee":
case "melee": case "tir":
case "tir": case "lancer":
case "lancer": case "naturelle":
case "naturelle": case "possession":
case "possession": return this.system.categorie
return item.system.categorie
}
} }
return undefined
} }
static isDommages(item) { isDommages() {
if (item.type == ITEM_TYPES.competencecreature) { switch (this.system.categorie) {
switch (item.system.categorie) { case "melee":
case "melee": case "tir":
case "tir": case "lancer":
case "lancer": case "naturelle":
case "naturelle": return true
return true
}
}
return false
}
static isParade(item) {
if (item.type == ITEM_TYPES.competencecreature) {
return item.system.categorie_parade || item.system.isparade
} }
return false return false
} }

View File

@@ -9,7 +9,6 @@ import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js"; import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js"; import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { RdDItem } from "./item.js"; import { RdDItem } from "./item.js";
import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js"; import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js"; import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
@@ -106,8 +105,8 @@ export class RdDItemSheetV1 extends foundry.appv1.sheets.ItemSheet {
} }
if (this.item.type == ITEM_TYPES.competencecreature) { if (this.item.type == ITEM_TYPES.competencecreature) {
formData.isparade = RdDItemCompetenceCreature.isParade(this.item) formData.isparade = this.item.isParade()
formData.isdommages = RdDItemCompetenceCreature.isDommages(this.item) formData.isdommages = this.item.isDommages()
} }
if (this.item.type == ITEM_TYPES.tache || if (this.item.type == ITEM_TYPES.tache ||
this.item.type == ITEM_TYPES.livre || this.item.type == ITEM_TYPES.livre ||

View File

@@ -1,4 +1,7 @@
import { ITEM_TYPES } from "./constants.js"; import { ITEM_TYPES } from "./constants.js";
import { CATEGORIES_COMPETENCES } from "./item-competence.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES_CREATURES } from "./item/base-items.js";
import { ITEM_ACTIONS, DEFAULT_ACTIONS, COMMON_ACTIONS } from "./item/item-actions.js";
import { Grammar } from "./grammar.js"; import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
@@ -7,10 +10,6 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "./rdd-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js"; import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDRaretes } from "./item/raretes.js"; import { RdDRaretes } from "./item/raretes.js";
import { CATEGORIES_COMPETENCES } from "./item-competence.js";
import { CATEGORIES_COMPETENCES_CREATURES } from "./item-competencecreature.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE } from "./item/base-items.js";
import { ITEM_ACTIONS, DEFAULT_ACTIONS, COMMON_ACTIONS } from "./item/item-actions.js";
const typesInventaireMateriel = [ const typesInventaireMateriel = [
ITEM_TYPES.arme, ITEM_TYPES.arme,
@@ -186,7 +185,10 @@ export class RdDItem extends Item {
isMonnaie() { return this.type == ITEM_TYPES.monnaie; } isMonnaie() { return this.type == ITEM_TYPES.monnaie; }
isNourritureBoisson() { return this.type == ITEM_TYPES.nourritureboisson; } isNourritureBoisson() { return this.type == ITEM_TYPES.nourritureboisson; }
isService() { return this.type == ITEM_TYPES.service; } isService() { return this.type == ITEM_TYPES.service; }
isAttaque() { return false }
isParade() { return false }
isBouclier() { return false }
getCategorieAttaque() { return undefined }
isCompetence() { return typesObjetsCompetence.includes(this.type) } isCompetence() { return typesObjetsCompetence.includes(this.type) }
isEsquive() { isEsquive() {
return (this.isCompetence() return (this.isCompetence()

View File

@@ -1,10 +1,9 @@
import { ITEM_TYPES } from "../constants.js"; import { ITEM_TYPES } from "../constants.js";
import { RdDItem } from "../item.js"; import { RdDItem } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js"
import { BASE_CORPS_A_CORPS } from "./base-items.js"; import { BASE_CORPS_A_CORPS } from "./base-items.js";
import { Grammar } from "../grammar.js"; import { Grammar } from "../grammar.js";
import { RdDInitiative } from "../initiative.mjs"; import { RdDInitiative } from "../initiative.mjs";
import { MappingCreatureArme } from "./mapping-creature-arme.mjs";
const nomCategorieParade = { const nomCategorieParade = {
"sans-armes": "Sans arme", "sans-armes": "Sans arme",
@@ -19,24 +18,48 @@ const nomCategorieParade = {
"haches": "Haches", "haches": "Haches",
"lances": "Lances", "lances": "Lances",
} }
export const ATTAQUE_TYPE = {
UNE_MAIN: '(1 main)',
DEUX_MAINS: '(2 mains)',
COMPETENCE: 'competence',
TIR: '(tir)',
LANCER: '(lancer)'
}
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDItemArme extends RdDItem { export class RdDItemArme extends RdDItem {
static get ITEM_TYPE() { return ITEM_TYPES.arme } static get ITEM_TYPE() { return ITEM_TYPES.arme }
static get defaultIcon() { static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/armes_armures/epee_gnome.webp" }
return defaultItemImgArme
//return "systems/foundryvtt-reve-de-dragon/icons/armes_armure/epee_sord.webp"; penetration() { return parseInt(this.system.penetration ?? 0) }
}
isParade() { return this.system.resistance > 0 && this.system.categorie_parade }
isBouclier() { return RdDItemArme.getCategorieParade(this).includes('bouclier') }
/* -------------------------------------------- */ /* -------------------------------------------- */
static valeurMain(valeurs, main) {
valeurs = valeurs?.toString() ?? ""
const table = valeurs.includes("/") ? valeurs.split("/") : [valeurs, valeurs]
return parseInt(main == ATTAQUE_TYPE.DEUX_MAINS ? table[1] : table[0])
}
static getMainAttaque(competence) {
switch (competence.system.categorie) {
case 'tir': return ATTAQUE_TYPE.TIR
case 'lancer': return ATTAQUE_TYPE.LANCER
}
if (competence.name.includes('2 main')) {
return ATTAQUE_TYPE.DEUX_MAINS
}
return ATTAQUE_TYPE.UNE_MAIN
}
static getArme(arme) { static getArme(arme) {
switch (arme ? arme.type : '') { switch (arme ? arme.type : '') {
case ITEM_TYPES.arme: return arme; case ITEM_TYPES.arme: return arme;
case ITEM_TYPES.competencecreature: case ITEM_TYPES.competencecreature:
return RdDItemCompetenceCreature.armeCreature(arme); return MappingCreatureArme.armeCreature(arme);
} }
return RdDItemArme.corpsACorps(); return RdDItemArme.corpsACorps();
} }
@@ -47,11 +70,11 @@ export class RdDItemArme extends RdDItem {
return arme.name return arme.name
case ITEM_TYPES.arme: case ITEM_TYPES.arme:
switch (maniement) { switch (maniement) {
case 'competence': return arme.system.competence; case ATTAQUE_TYPE.COMPETENCE: return arme.system.competence;
case '(1 main)': return arme.competence1Mains() case ATTAQUE_TYPE.UNE_MAIN: return arme.competence1Mains()
case '(2 mains)': return arme.competence2Mains() case ATTAQUE_TYPE.DEUX_MAINS: return arme.competence2Mains()
case '(tir)': case 'tir': return arme.system.tir case ATTAQUE_TYPE.TIR: case 'tir': return arme.system.tir
case '(lancer)': case 'lancer': return arme.system.lancer; case ATTAQUE_TYPE.LANCER: case 'lancer': return arme.system.lancer;
} }
} }
return undefined return undefined
@@ -88,16 +111,13 @@ export class RdDItemArme extends RdDItem {
/* -------------------------------------------- */ /* -------------------------------------------- */
static getCategorieParade(armeData) { static getCategorieParade(armeData) {
if (![ITEM_TYPES.arme, ITEM_TYPES.competencecreature].includes(armeData.type)) {
return ''
}
if (armeData.system.categorie_parade) { if (armeData.system.categorie_parade) {
return armeData.system.categorie_parade return armeData.system.categorie_parade
} }
// pour compatibilité avec des personnages existants // pour compatibilité avec des personnages existants
if (armeData.type == ITEM_TYPES.competencecreature || armeData.system.categorie == 'creature') {
return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '')
}
if (!armeData.type.match(/arme|competencecreature/)) {
return ''
}
if (armeData.system.competence == undefined) { if (armeData.system.competence == undefined) {
return ITEM_TYPES.competencecreature; return ITEM_TYPES.competencecreature;
} }
@@ -192,7 +212,7 @@ export class RdDItemArme extends RdDItem {
return Number(arme.system.dommages) return Number(arme.system.dommages)
} }
const tableauDegats = arme.system.dommages.split("/"); const tableauDegats = arme.system.dommages.split("/");
return Number(tableauDegats[maniement == '(1 main)' ? 0 : 1]) return Number(tableauDegats[maniement == ATTAQUE_TYPE.UNE_MAIN ? 0 : 1])
} }
return Number(arme.system.dommages); return Number(arme.system.dommages);
} }
@@ -201,7 +221,7 @@ export class RdDItemArme extends RdDItem {
static armeUneOuDeuxMains(arme, aUneMain) { static armeUneOuDeuxMains(arme, aUneMain) {
if (arme && !arme.system.cac) { if (arme && !arme.system.cac) {
arme = foundry.utils.duplicate(arme); arme = foundry.utils.duplicate(arme);
arme.system.dommagesReels = RdDItemArme.dommagesReels(arme, aUneMain ? '(1 main)' : '(2 mains)') arme.system.dommagesReels = RdDItemArme.dommagesReels(arme, aUneMain ? ATTAQUE_TYPE.UNE_MAIN : ATTAQUE_TYPE.DEUX_MAINS)
} }
return arme; return arme;
} }
@@ -222,25 +242,10 @@ export class RdDItemArme extends RdDItem {
return false return false
} }
static isAttaque(arme) { isAttaque() {
switch (arme.type) { return this.system.resistance > 0 || this.system.portee_courte > 0
case ITEM_TYPES.arme: return arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0)
case ITEM_TYPES.competencecreature: return arme.system.iscombat && RdDItemCompetenceCreature.isAttaque(item)
}
return false
} }
static isParade(arme) {
switch (arme.type) {
case ITEM_TYPES.arme:
return arme.system.resistance > 0 && true/* TODO: regarder la categorie d'arme?*/
case ITEM_TYPES.competencecreature:
return arme.system.iscombat && RdDItemCompetenceCreature.isParade(arme)
}
return false
}
static corpsACorps(actor) { static corpsACorps(actor) {
let competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS let competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS
let melee = actor ? actor.system.carac['melee'].value : 0 let melee = actor ? actor.system.carac['melee'].value : 0

View File

@@ -18,3 +18,12 @@ export const SANS_COMPETENCE = {
img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp" img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp"
} }
export const CATEGORIES_COMPETENCES_CREATURES = {
"generale": { base: 0, label: "Générale" },
"naturelle": { base: 0, label: "Arme naturelle" },
"melee": { base: 0, label: "Mêlée" },
"parade": { base: 0, label: "Parade" },
"tir": { base: 0, label: "Tir" },
"lancer": { base: 0, label: "Lancer" },
"possession": { base: 0, label: "Possession" },
}

View File

@@ -0,0 +1,42 @@
import { Grammar } from "../grammar.js";
import { RdDInitiative } from "../initiative.mjs";
export class MappingCreatureArme {
/* -------------------------------------------- */
static setRollDataCreature(rollData) {
const code = Grammar.toLowerCaseNoAccentNoSpace(rollData.competence.name);
const selectedCarac = { code: code, label: rollData.competence.name, value: rollData.competence.system.carac_value };
rollData.carac = { [code]: selectedCarac }
rollData.competence.system.defaut_carac = code
rollData.selectedCarac = selectedCarac
rollData.arme = MappingCreatureArme.armeCreature(rollData.competence);
}
/* -------------------------------------------- */
static armeCreature(item) {
const categorieAttaque = item.getCategorieAttaque()
if (categorieAttaque != undefined) {
// cloner pour ne pas modifier la compétence
return foundry.utils.mergeObject(item, {
action: item.isCompetencePossession() ? 'possession' : 'attaque',
system: {
competence: item.name,
cac: categorieAttaque == "naturelle" ? "naturelle" : "",
niveau: item.system.niveau,
initiative: RdDInitiative.calculInitiative(item.system.niveau, item.system.carac_value),
equipe: true,
resistance: 100,
dommagesReels: item.system.dommages,
penetration: 0,
force: 0,
rapide: true,
}
}, { inplace: false, });
}
return undefined;
}
}

View File

@@ -1,21 +1,24 @@
import { RdDCarac } from "./rdd-carac.js"; import { RdDItemArme } from "./item/arme.js";
import { RdDPossession } from "./rdd-possession.js"; import { RdDPossession } from "./rdd-possession.js";
const conditionsTactiques = [ const conditionsTactiques = [
{ type: '', descr: '', dmg: 0, attaque: 0, parade: 0, esquive: true }, { key: '', label: '', dmg: 0, attaque: 0, parade: 0, esquive: true, isTactique: false },
{ type: 'charge', descr: 'Charge', dmg: 2, attaque: 4, parade: -4, esquive: false }, { key: 'normale', label: 'Attaque normale', dmg: 0, attaque: 0, parade: 0, esquive: true, isTactique: true },
{ type: 'feinte', descr: 'Feinte', dmg: 1, attaque: 1, parade: 0, esquive: true }, { key: 'charge', label: 'Charge', dmg: 2, attaque: 4, parade: -4, esquive: false, isTactique: true },
{ type: 'pret', descr: 'prêt', dmg: 0, attaque: 0, parade: 0, esquive: true }, { key: 'feinte', label: 'Feinte', dmg: 1, attaque: 1, parade: 0, esquive: true, isTactique: true },
{ type: 'demi', descr: 'Demi-surprise', dmg: 1, attaque: 0, parade: 0, esquive: true }, { key: 'pret', label: 'prêt', dmg: 0, attaque: 0, parade: 0, esquive: true, isTactique: false },
{ type: 'totale', descr: 'Surprise totale', dmg: 10, attaque: 6, parade: 0, esquive: true }, { key: 'demi', label: 'Demi-surprise', dmg: 1, attaque: 0, parade: 0, esquive: true, isTactique: false },
{ key: 'totale', label: 'Surprise totale', dmg: 10, attaque: 6, parade: 0, esquive: true, isTactique: false },
]; ];
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDBonus { export class RdDBonus {
static get tactiques() {
return conditionsTactiques.filter(it => it.isTactique)
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static find(condition) { static find(condition) {
return conditionsTactiques.find(e => e.type == condition) || conditionsTactiques.find(e => e.type == 'pret'); return conditionsTactiques.find(e => e.key == condition) || conditionsTactiques[0];
} }
@@ -32,7 +35,8 @@ export class RdDBonus {
/* -------------------------------------------- */ /* -------------------------------------------- */
static dmg(rollData, actor, isEntiteIncarnee = false) { static dmg(rollData, actor, isEntiteIncarnee = false) {
const dmgArme = RdDBonus.dmgArme(rollData.arme) const dmgArme = RdDBonus.dmgArme(rollData.arme, rollData.arme?.system.dommagesReels)
const forceRequise = RdDItemArme.valeurMain(rollData.arme?.system.force ?? 0, RdDItemArme.getMainAttaque(rollData.competence))
let dmg = { let dmg = {
total: 0, total: 0,
dmgArme: dmgArme, dmgArme: dmgArme,
@@ -41,39 +45,44 @@ export class RdDBonus {
dmgParticuliere: RdDBonus._dmgParticuliere(rollData), dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used), dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used),
mortalite: RdDBonus._calculMortalite(rollData, isEntiteIncarnee), mortalite: RdDBonus._calculMortalite(rollData, isEntiteIncarnee),
dmgActor: RdDBonus.bonusDmg(actor, rollData.selectedCarac?.label.toLowerCase(), dmgArme) dmgActor: RdDBonus.bonusDmg(actor, rollData.selectedCarac?.label.toLowerCase(), dmgArme),
dmgForceInsuffisante: Math.min(0, actor.getForce() - forceRequise)
} }
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere; dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante
return dmg; return dmg;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static description(condition) { static description(condition) {
return RdDBonus.find(condition).descr; return RdDBonus.find(condition).label
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static dmgBonus(condition) { static dmgBonus(condition) {
return RdDBonus.find(condition).dmg; return RdDBonus.find(condition).dmg
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static bonusAttaque(condition) { static bonusAttaque(condition) {
return RdDBonus.find(condition).attaque; return RdDBonus.find(condition).attaque
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static _calculMortalite(rollData, isEntiteIncarnee) { static _calculMortalite(rollData, isEntiteIncarnee) {
return RdDBonus.mortalite(rollData.dmg?.mortalite, rollData.arme?.system.mortalite, isEntiteIncarnee)
}
static mortalite(mortaliteSelect, mortaliteArme, isEntiteIncarnee) {
return isEntiteIncarnee ? "entiteincarnee" return isEntiteIncarnee ? "entiteincarnee"
: rollData.dmg?.mortalite : mortaliteSelect
?? rollData.arme?.system.mortalite ?? mortaliteArme
?? "mortel"; ?? "mortel";
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static dmgArme(arme) { static dmgArme(arme, dommagesMain) {
if (arme) { if (arme) {
let dmgBase = arme.system.dommagesReels ?? Number(arme.system.dommages ?? 0); let dmgBase = dommagesMain ?? Number(arme.system.dommages ?? 0);
//Le bonus dégats magiques ne peut pas faire dépasser le bonus de l'arme (cf p.278) //Le bonus dégats magiques ne peut pas faire dépasser le bonus de l'arme (cf p.278)
return dmgBase + Math.min(dmgBase, arme.system.magique ? arme.system.ecaille_efficacite : 0); return dmgBase + Math.min(dmgBase, arme.system.magique ? arme.system.ecaille_efficacite : 0);
} }
@@ -87,13 +96,13 @@ export class RdDBonus {
/* -------------------------------------------- */ /* -------------------------------------------- */
static bonusDmg(actor, categorie, dmgArme) { static bonusDmg(actor, categorie, dmgArme) {
const dmgActor = actor.getBonusDegat()
if (categorie == undefined) { if (categorie == undefined) {
return 0 return 0
} }
const dmgActor = actor.getBonusDegat()
switch (categorie) { switch (categorie) {
case "tir": return 0; case "(tir)": case "tir": return 0
case "lancer": return Math.max(0, Math.min(dmgArme, dmgActor)); case "(lancer)": case "lancer": return Math.max(0, Math.min(dmgArme, dmgActor));
} }
return dmgActor; return dmgActor;
} }

View File

@@ -52,6 +52,15 @@ export const CARACS = {
EMPATHIE: 'empathie', EMPATHIE: 'empathie',
REVE: 'reve', REVE: 'reve',
CHANCE: 'chance', CHANCE: 'chance',
PROTECTION: 'protection',
BEAUTE: 'beaute',
PERCEPTION: 'perception',
MELEE: 'melee',
TIR: 'tir',
LANCER: 'lancer',
DEROBEE: 'derobee',
CHANCE_ACTUELLE: 'chance-actuelle',
REVE_ACTUEL: 'reve-actuel',
} }
export const LIST_CARAC_PERSONNAGE = { export const LIST_CARAC_PERSONNAGE = {
[CARACS.TAILLE]: { code: CARACS.TAILLE, label: 'Taille', isCarac: true, path: 'system.carac.taille.value' }, [CARACS.TAILLE]: { code: CARACS.TAILLE, label: 'Taille', isCarac: true, path: 'system.carac.taille.value' },
@@ -68,21 +77,21 @@ export const LIST_CARAC_PERSONNAGE = {
[CARACS.EMPATHIE]: { code: CARACS.EMPATHIE, label: 'Empathie', isCarac: true, path: 'system.carac.empathie.value' }, [CARACS.EMPATHIE]: { code: CARACS.EMPATHIE, label: 'Empathie', isCarac: true, path: 'system.carac.empathie.value' },
[CARACS.REVE]: { code: CARACS.REVE, label: 'Rêve', isCarac: true, path: 'system.carac.reve.value' }, [CARACS.REVE]: { code: CARACS.REVE, label: 'Rêve', isCarac: true, path: 'system.carac.reve.value' },
[CARACS.CHANCE]: { code: CARACS.CHANCE, label: 'Chance', isCarac: true, path: 'system.carac.chance.value' }, [CARACS.CHANCE]: { code: CARACS.CHANCE, label: 'Chance', isCarac: true, path: 'system.carac.chance.value' },
'protection': { code: 'protection', label: 'Protection naturelle', isCarac: false, path: 'system.attributs.protection.value' }, [CARACS.PROTECTION]: { code: CARACS.PROTECTION, label: 'Protection naturelle', isCarac: false, path: 'system.attributs.protection.value' },
'beaute': { code: 'beaute', label: 'Beauté', isCarac: false, path: 'system.background.beaute.value' } [CARACS.BEAUTE]: { code: CARACS.BEAUTE, label: 'Beauté', isCarac: false, path: 'system.background.beaute.value' }
} }
export const LIST_CARAC_AUTRES = { export const LIST_CARAC_AUTRES = {
'perception': { code: 'perception', label: 'Perception', path: 'system.carac.perception.value' }, [CARACS.PERCEPTION]: { code: 'perception', label: 'Perception', path: 'system.carac.perception.value' },
} }
const LIST_CARAC_DERIVEE = { const LIST_CARAC_DERIVEE = {
'melee': { code: "melee", label: 'Mêlée', path: 'system.carac.melee.value' }, [CARACS.MELEE]: { code: CARACS.MELEE, label: 'Mêlée', path: 'system.carac.melee.value' },
'tir': { code: "tir", label: 'Tir', path: 'system.carac.tir.value' }, [CARACS.TIR]: { code: CARACS.TIR, label: 'Tir', path: 'system.carac.tir.value' },
'lancer': { code: "lancer", label: 'Lancer', path: 'system.carac.lancer.value' }, [CARACS.LANCER]: { code: CARACS.LANCER, label: 'Lancer', path: 'system.carac.lancer.value' },
'derobee': { code: "derobee", label: 'Dérobée', path: 'system.carac.derobee.value' }, [CARACS.DEROBEE]: { code: CARACS.DEROBEE, label: 'Dérobée', path: 'system.carac.derobee.value' },
'chance-actuelle': { code: "chance-actuelle", label: 'Chance actuelle', path: 'system.carac.lancer.value' }, [CARACS.CHANCE_ACTUELLE]: { code: CARACS.CHANCE_ACTUELLE, label: 'Chance actuelle', path: 'system.carac.lancer.value' },
'reve-actuel': { code: "reve-actuel", label: 'Rêve actuel', path: 'system.reve.reve.value' }, [CARACS.REVE_ACTUEL]: { code: CARACS.REVE_ACTUEL, label: 'Rêve actuel', path: 'system.reve.reve.value' },
} }
export const LIST_CARAC_ROLL = Object.values(LIST_CARAC_PERSONNAGE).filter(it => it.isCarac && it.code != 'taille') export const LIST_CARAC_ROLL = Object.values(LIST_CARAC_PERSONNAGE).filter(it => it.isCarac && it.code != 'taille')
@@ -125,11 +134,11 @@ export class RdDCarac {
} }
static isIntellect(caracLabel) { static isIntellect(caracLabel) {
return Grammar.toLowerCaseNoAccent(caracLabel) == 'intellect'; return Grammar.toLowerCaseNoAccent(caracLabel) == CARACS.INTELLECT
} }
static isVolonte(caracLabel) { static isVolonte(caracLabel) {
return Grammar.toLowerCaseNoAccent(caracLabel) == 'volonte'; return Grammar.toLowerCaseNoAccent(caracLabel) == CARACS.VOLONTE
} }
static isChance(caracLabel) { static isChance(caracLabel) {
return Grammar.toLowerCaseNoAccent(caracLabel)?.match(/chance(( |-)?actuelle)?/); return Grammar.toLowerCaseNoAccent(caracLabel)?.match(/chance(( |-)?actuelle)?/);

View File

@@ -15,6 +15,10 @@ import { RdDItemArme } from "./item/arme.js";
import { RdDItemCompetence } from "./item-competence.js"; import { RdDItemCompetence } from "./item-competence.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"; import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { RdDInitiative } from "./initiative.mjs"; import { RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs";
import { PART_DEFENSE } from "./roll/roll-part-defense.mjs";
import { PART_ATTAQUE } from "./roll/roll-part-attaque.mjs";
import { RollDialogAdapter } from "./roll/roll-dialog-adapter.mjs";
/* -------------------------------------------- */ /* -------------------------------------------- */
const premierRoundInit = [ const premierRoundInit = [
@@ -398,6 +402,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
static registerChatCallbacks(html) { static registerChatCallbacks(html) {
for (let button of [ for (let button of [
'.defense-button',
'.parer-button', '.parer-button',
'.esquiver-button', '.esquiver-button',
'.particuliere-attaque', '.particuliere-attaque',
@@ -466,6 +471,8 @@ export class RdDCombat {
switch (button) { switch (button) {
case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value); case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '.defense-button': return this.defenseV2(attackerRoll);
case '.parer-button': return this.parade(attackerRoll, armeParadeId); case '.parer-button': return this.parade(attackerRoll, armeParadeId);
case '.esquiver-button': return this.esquive(attackerRoll, compId, competence); case '.esquiver-button': return this.esquive(attackerRoll, compId, competence);
case '.encaisser-button': return this.encaisser(attackerRoll, defenderRoll); case '.encaisser-button': return this.encaisser(attackerRoll, defenderRoll);
@@ -713,9 +720,6 @@ export class RdDCombat {
if (RdDCombat.isReussite(attackerRoll)) { if (RdDCombat.isReussite(attackerRoll)) {
return await this._onAttaqueNormale(attackerRoll) return await this._onAttaqueNormale(attackerRoll)
} }
// if (RdDCombat.isParticuliere(attackerRoll) && attackerRoll.particuliere == undefined) {
// return
// }
if (RdDCombat.isEchecTotal(attackerRoll)) { if (RdDCombat.isEchecTotal(attackerRoll)) {
return await this._onAttaqueEchecTotal(attackerRoll) return await this._onAttaqueEchecTotal(attackerRoll)
} }
@@ -857,7 +861,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
_filterArmesParade(defender, competence, armeAttaque) { _filterArmesParade(defender, competence, armeAttaque) {
let defenses = defender.items.filter(it => RdDItemArme.isParade(it)) let defenses = defender.items.filter(it => it.isParade())
defenses = foundry.utils.duplicate(defenses) defenses = foundry.utils.duplicate(defenses)
defenses.forEach(armeDefense => { defenses.forEach(armeDefense => {
// Ajout du # d'utilisation ce round // Ajout du # d'utilisation ce round
@@ -946,6 +950,41 @@ export class RdDCombat {
dialog.render(true); dialog.render(true);
} }
async defenseV2(attackerRoll) {
// this._prepareParade(attackerRoll, arme, competence);
const rollData =
{
ids: {
actorId: this.defender.id,
actorTokenId: this.defenderTokenId,
opponentTokenId: this.attackerTokenId,
opponentId: this.attackerId,
},
mode: {
allowed: ['defense'],
current: 'defense'
},
attaque: RollDialogAdapter.mapActionAttaque(attackerRoll),
passeArme: attackerRoll.passeArme,
}
await RollDialog.create(rollData, {
onRoll: (dialog) => {
this._onCloseRollDialog(),
dialog.close()
},
customChatMessage: true,
callbacks: [async (actor, rd) => {
this.removeChatMessageActionsPasseArme(rd.passeArme)
// defense: esquive / arme de parade / competence de défense
await rd.active.actor.incDecItemUse(rd.current[PART_DEFENSE].defense?.id, !RdDCombat.isParticuliere(rd))
await this._onDefenseV2(rd)
}]
})
}
/* -------------------------------------------- */ /* -------------------------------------------- */
_prepareParade(attackerRoll, armeParade, competenceParade) { _prepareParade(attackerRoll, armeParade, competenceParade) {
let defenderRoll = { let defenderRoll = {
@@ -971,7 +1010,6 @@ export class RdDCombat {
return defenderRoll; return defenderRoll;
} }
async _onParade(defenderRoll) { async _onParade(defenderRoll) {
if (RdDCombat.isReussite(defenderRoll)) { if (RdDCombat.isReussite(defenderRoll)) {
await this._onParadeNormale(defenderRoll) await this._onParadeNormale(defenderRoll)
@@ -982,6 +1020,16 @@ export class RdDCombat {
} }
await this._onParadeEchec(defenderRoll) await this._onParadeEchec(defenderRoll)
} }
async _onDefenseV2(defenderRoll) {
if (RdDCombat.isReussite(defenderRoll)) {
await this._onParadeNormale(defenderRoll)
if (RdDCombat.isParticuliere(defenderRoll)) {
await this._onParadeParticuliere(defenderRoll)
}
return
}
await this._onParadeEchec(defenderRoll)
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async _onParadeParticuliere(defenderRoll) { async _onParadeParticuliere(defenderRoll) {
@@ -1255,4 +1303,8 @@ export class RdDCombat {
alias: alias alias: alias
}) })
} }
} }
function newFunction(attackerRoll) {
return attackerRoll.diffLibre;
}

View File

@@ -1,10 +1,9 @@
/* -------------------------------------------- */
import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ChatUtility } from "./chat-utility.js";
import { STATUSES } from "./settings/status-effects.js"; import { STATUSES } from "./settings/status-effects.js";
import { ITEM_TYPES } from "./constants.js"; import { ITEM_TYPES } from "./constants.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDRollResult } from "./rdd-roll-result.js"; import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDRoll } from "./rdd-roll.js";
import { MappingCreatureArme } from "./item/mapping-creature-arme.mjs";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDEmpoignade { export class RdDEmpoignade {
@@ -186,7 +185,7 @@ export class RdDEmpoignade {
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender) malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
} }
if (attacker.isCreatureEntite()) { if (attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData) MappingCreatureArme.setRollDataCreature(rollData)
} }
if (empoignade.system.pointsemp >= 2) { if (empoignade.system.pointsemp >= 2) {
if (!empoignade.system.ausol) { if (!empoignade.system.ausol) {

View File

@@ -1,5 +1,4 @@
import { RdDItemArme } from "./item/arme.js"; import { ATTAQUE_TYPE, RdDItemArme } from "./item/arme.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ITEM_TYPES } from "./constants.js"; import { ITEM_TYPES } from "./constants.js";
export class RdDHotbar { export class RdDHotbar {
@@ -20,10 +19,13 @@ export class RdDHotbar {
await game.user.assignHotbarMacro(macro, slot); await game.user.assignHotbarMacro(macro, slot);
} }
static $macroNameSuffix(armeCompetence) { static $macroNameSuffix(maniement) {
switch (armeCompetence) { switch (maniement) {
case '(1 main)': return ' (1 main)'; case ATTAQUE_TYPE.UNE_MAIN:
case '(2 mains)': return ' (2 main)'; case ATTAQUE_TYPE.DEUX_MAINS:
case ATTAQUE_TYPE.LANCER:
case ATTAQUE_TYPE.TIR:
return ' ' + maniement
case 'tir': return ' (tir)'; case 'tir': return ' (tir)';
case 'lancer': return ' (lancer)'; case 'lancer': return ' (lancer)';
case 'pugilat': return ' (pugilat)'; case 'pugilat': return ' (pugilat)';
@@ -39,22 +41,22 @@ export class RdDHotbar {
// Les armes peuvent avoir plusieurs usages // Les armes peuvent avoir plusieurs usages
if (item.system.competence != '') { if (item.system.competence != '') {
if (item.system.unemain) { if (item.system.unemain) {
await this.createItemMacro(item, slot++, '(1 main)') await this.createItemMacro(item, slot++, ATTAQUE_TYPE.UNE_MAIN)
} }
if (item.system.deuxmains) { if (item.system.deuxmains) {
await this.createItemMacro(item, slot++, '(2 mains)') await this.createItemMacro(item, slot++, ATTAQUE_TYPE.DEUX_MAINS)
} }
} }
if (item.system.lancer != '') { if (item.system.lancer != '') {
await this.createItemMacro(item, slot++, 'lancer') await this.createItemMacro(item, slot++, ATTAQUE_TYPE.LANCER)
} }
if (item.system.tir != '') { if (item.system.tir != '') {
await this.createItemMacro(item, slot++, 'lancer') await this.createItemMacro(item, slot++, ATTAQUE_TYPE.TIR)
} }
} }
return return
case ITEM_TYPES.competencecreature: case ITEM_TYPES.competencecreature:
const categorie = RdDItemCompetenceCreature.getCategorieAttaque(item) ?? 'competence'; const categorie = item.getCategorieAttaque() ?? 'competence';
await this.createItemMacro(item, slot, categorie) await this.createItemMacro(item, slot, categorie)
return return
default: default:
@@ -127,7 +129,7 @@ export class RdDHotbar {
} }
return actor.rollCompetence(item); return actor.rollCompetence(item);
case ITEM_TYPES.competencecreature: case ITEM_TYPES.competencecreature:
return item.system.iscombat && !item.system.isparade return item.system.iscombat
? actor.rollArme(item, categorieArme) ? actor.rollArme(item, categorieArme)
: actor.rollCompetence(item); : actor.rollCompetence(item);

View File

@@ -1,9 +1,7 @@
import { RdDRoll } from "./rdd-roll.js"; import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { Targets } from "./targets.js"; import { Targets } from "./targets.js";
import { ITEM_TYPES } from "./constants.js"; import { ITEM_TYPES } from "./constants.js";
import { RdDRollResult } from "./rdd-roll-result.js"; import { RdDRollResult } from "./rdd-roll-result.js";
import { Grammar } from "./grammar.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
/* On part du principe qu'une entité démarre tjs /* On part du principe qu'une entité démarre tjs

View File

@@ -19,7 +19,7 @@ import { RdDEmpoignade } from "./rdd-empoignade.js";
import { ExperienceLog } from "./actor/experience-log.js"; import { ExperienceLog } from "./actor/experience-log.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js"; import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { APP_ASTROLOGIE_REFRESH } from "./sommeil/app-astrologie.js"; import { APP_ASTROLOGIE_REFRESH } from "./sommeil/app-astrologie.js";
import { ITEM_TYPES, RDD_CONFIG } from "./constants.js"; import { ITEM_TYPES, RDD_CONFIG, SYSTEM_RDD } from "./constants.js";
import { RdDBaseActor } from "./actor/base-actor.js"; import { RdDBaseActor } from "./actor/base-actor.js";
import { RdDCarac } from "./rdd-carac.js"; import { RdDCarac } from "./rdd-carac.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js"; import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
@@ -271,6 +271,7 @@ export class RdDUtility {
// foundry et options // foundry et options
Handlebars.registerHelper('RDD_CONFIG', path => RDD_CONFIG[path]) Handlebars.registerHelper('RDD_CONFIG', path => RDD_CONFIG[path])
Handlebars.registerHelper('settings-get', (setting) => game.settings.get(SYSTEM_RDD, setting))
Handlebars.registerHelper('linkCompendium', (pack, id, name) => RdDUtility.linkCompendium(pack, id, name)); Handlebars.registerHelper('linkCompendium', (pack, id, name) => RdDUtility.linkCompendium(pack, id, name));
Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionnelles.isUsing(option)); Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionnelles.isUsing(option));
@@ -344,7 +345,7 @@ export class RdDUtility {
Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field)); Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field));
Handlebars.registerHelper('item-action-applies', (action, item, options) => ItemAction.applies(action, item, options)) Handlebars.registerHelper('item-action-applies', (action, item, options) => ItemAction.applies(action, item, options))
Handlebars.registerHelper('item-action-icon', (action, item) => ItemAction.icon(action, item)) Handlebars.registerHelper('item-action-icon', (action, item) => ItemAction.icon(action, item))
Handlebars.registerHelper('item-name', (item) => item.nameDisplay) Handlebars.registerHelper('item-name', (item) => item.nameDisplay)
// TMRs // TMRs
Handlebars.registerHelper('caseTmr-label', coord => TMRUtility.getTMRLabel(coord)); Handlebars.registerHelper('caseTmr-label', coord => TMRUtility.getTMRLabel(coord));

View File

@@ -4,6 +4,8 @@ import { PART_COMP } from "./roll-part-comp.mjs";
import { RdDResolutionTable } from "../rdd-resolution-table.js"; import { RdDResolutionTable } from "../rdd-resolution-table.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"; import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { PART_OEUVRE } from "./roll-part-oeuvre.mjs"; import { PART_OEUVRE } from "./roll-part-oeuvre.mjs";
import { ATTAQUE_TYPE, RdDItemArme } from "../item/arme.js";
import { RdDBonus } from "../rdd-bonus.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RollDialogAdapter { export class RollDialogAdapter {
@@ -87,4 +89,29 @@ export class RollDialogAdapter {
rollData.show.title = rollTitle rollData.show.title = rollTitle
} }
static mapActionAttaque(attackerRoll) {
if (attackerRoll.ids) {
return attackerRoll.current[PART_ATTAQUE]
}
const label = attackerRoll.alias + ' ' + attackerRoll.arme.name;
return {
// correspond à l'attaque de RollPartAttaque (dans rollDta.current.attaque)
label: label,
attaque: {
// correspond aux actions d'attaques dans RdDActor.listActionsAttaque
name: label,
// action: 'attaque',
arme: attackerRoll.arme,
comp: attackerRoll.competence,
main: RdDItemArme.getMainAttaque(attackerRoll.competence),
equipe: attackerRoll.arme.system.equipe,
// carac: { key: caracCode, value: caracValue },
// dommagesArme: dommagesArme,
},
diff: attackerRoll.diffLibre,
particuliere: attackerRoll.particuliere,
tactique: RdDBonus.find(attackerRoll.tactique),
dmg: attackerRoll.dmg,
}
}
} }

View File

@@ -187,7 +187,12 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
foundry.applications.handlebars.loadTemplates(ROLL_PARTS.map(p => p.template)) foundry.applications.handlebars.loadTemplates(ROLL_PARTS.map(p => p.template))
ROLL_PARTS.forEach(p => p.onReady()) ROLL_PARTS.forEach(p => p.onReady())
Handlebars.registerHelper('roll-centered-array', (base, show) => RollDialog.centeredArray(base, show)) Handlebars.registerHelper('roll-centered-array', (base, show) => {
show = Math.abs(show)
const start = base - show
return [...Array(2 * show + 1).keys()].map(it => start + it)
})
Handlebars.registerHelper('roll-list-item-value', (list, key, path = undefined) => { Handlebars.registerHelper('roll-list-item-value', (list, key, path = undefined) => {
const selected = list.find(p => p.key == key) const selected = list.find(p => p.key == key)
if (selected && path && path != '') { if (selected && path && path != '') {
@@ -195,6 +200,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
} }
return selected return selected
}) })
Handlebars.registerHelper('roll-part-context', (rollData, code) => { Handlebars.registerHelper('roll-part-context', (rollData, code) => {
const rollPart = ROLL_PARTS.find(it => it.code == code) const rollPart = ROLL_PARTS.find(it => it.code == code)
if (rollPart == undefined) { if (rollPart == undefined) {
@@ -211,12 +217,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
}) })
} }
static centeredArray(base, show) {
show = Math.abs(show)
const start = base - show
return [...Array(2 * show + 1).keys()].map(it => start + it)
}
static async create(rollData, rollOptions = {}) { static async create(rollData, rollOptions = {}) {
const rollDialog = new RollDialog(rollData, rollOptions) const rollDialog = new RollDialog(rollData, rollOptions)
rollDialog.render(true) rollDialog.render(true)
@@ -267,30 +268,33 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
/** pre-configure les paramètres des différentes parties de la fenêtre (par exemple, prépare les listes de caractéristiques/compétences */ /** pre-configure les paramètres des différentes parties de la fenêtre (par exemple, prépare les listes de caractéristiques/compétences */
$loadParts() { $loadParts() {
const rollData = this.rollData; const rollData = this.rollData;
const loadedMode = rollData.mode?.current
rollData.current = rollData.current ?? {} rollData.current = rollData.current ?? {}
rollData.selected = rollData.selected ?? {} rollData.selected = rollData.selected ?? {}
rollData.mode = rollData.mode ?? {} rollData.mode = rollData.mode ?? {}
rollData.mode.retry = rollData.mode.retry ?? false rollData.mode.retry = rollData.mode.retry ?? false
BASIC_PARTS.restore(rollData) BASIC_PARTS.restore(rollData)
const loadedMode = ROLL_MODE_TABS.find(m => m.code == rollData.mode?.current)?.code
const allowedModes = ROLL_MODE_TABS.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code)
rollData.mode.allowed = rollData.mode.retry ? [loadedMode] : rollData.mode.allowed ?? ROLL_MODE_TABS.map(m => m.code) rollData.mode.allowed = rollData.mode.retry ? [loadedMode] : rollData.mode.allowed ?? ROLL_MODE_TABS.map(m => m.code)
rollData.mode.current = loadedMode ?? ROLL_MODE_TABS.find(m => m.isAllowed(rollData) && m.visible(rollData))?.code ?? ROLL_MODE_COMP rollData.mode.current = allowedModes.find(m => m == rollData.mode?.current) ?? (allowedModes.length > 0 ? allowedModes[0] : ROLL_MODE_COMP)
this.getSelectedMode().setRollDataMode(rollData) this.getSelectedMode().setRollDataMode(rollData)
rollData.refs = this.$prepareRefs(rollData) rollData.refs = this.$prepareRefs(rollData)
rollData.options = rollData.options ?? { showDice: true, rollMode: game.settings.get("core", "rollMode") } rollData.options = rollData.options ?? { showDice: true, rollMode: game.settings.get("core", "rollMode") }
ROLL_PARTS.forEach(p => p.initialize(rollData)) ROLL_PARTS.forEach(p => p.initialize(rollData))
ROLL_PARTS.forEach(p => p.restore(rollData)) ROLL_PARTS.forEach(p => p.restore(rollData))
ROLL_PARTS.filter(p => p.isValid(rollData)) ROLL_PARTS.filter(p => p.isValid(rollData))
.forEach(p => { .forEach(p => {
p.loadRefs(rollData) p.loadRefs(rollData)
p.prepareContext(rollData) p.prepareContext(rollData)
}) })
this.selectMode(); this.selectMode();
} }
selectMode() { selectMode() {
this.rollData.mode.label = this.getSelectedMode().title(this.rollData) this.rollData.mode.label = this.getSelectedMode().title(this.rollData)
this.getSelectedMode().setRollDataMode(this.rollData) this.getSelectedMode().setRollDataMode(this.rollData)
@@ -360,7 +364,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
this.setModeTitle() this.setModeTitle()
const visibleRollParts = this.getActiveParts() const visibleRollParts = this.getActiveParts()
visibleRollParts.forEach(p => p.setExternalFilter(visibleRollParts, rollData)) visibleRollParts.forEach(p => p.applyExternalImpacts(visibleRollParts, rollData))
this.setSpecialComp(visibleRollParts); this.setSpecialComp(visibleRollParts);

View File

@@ -5,7 +5,7 @@ export class RollModeDefense extends RollMode {
get code() { return ROLL_MODE_DEFENSE } get code() { return ROLL_MODE_DEFENSE }
get name() { return `Se défendre` } get name() { return `Se défendre` }
title(rollData) { return `se défend${rollData.attacker ? ' de' : ''}` } title(rollData) { return `se défend${rollData.opponent ? ' de' : ''}` }
getOpponent(rollData) { getOpponent(rollData) {
return rollData.attacker return rollData.attacker

View File

@@ -37,6 +37,7 @@ export class RollPartAppelMoral extends RollPartCheckbox {
} }
return '<i class="fa-regular fa-face-meh"></i>' return '<i class="fa-regular fa-face-meh"></i>'
} }
getCheckboxLabel(rollData) { return "Appel au moral" } getCheckboxLabel(rollData) { return "Appel au moral" }
getCheckboxValue(rollData) { return 1 } getCheckboxValue(rollData) { return 1 }
} }

View File

@@ -1,12 +1,16 @@
import { Grammar } from "../grammar.js" import { RdDBonus } from "../rdd-bonus.js"
import { StatusEffects } from "../settings/status-effects.js"
import { ROLL_MODE_ATTAQUE } from "./roll-constants.mjs" import { ROLL_MODE_ATTAQUE } from "./roll-constants.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs" import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs" import { PART_COMP } from "./roll-part-comp.mjs"
import { RollPartSelect } from "./roll-part-select.mjs" import { RollPartSelect } from "./roll-part-select.mjs"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs" import { PART_SIGN } from "./roll-part-sign.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
export const PART_ATTAQUE = 'attaque' export const PART_ATTAQUE = 'attaque'
const TACTIQUES = RdDBonus.tactiques.filter(it => it.isTactique)
export class RollPartAttaque extends RollPartSelect { export class RollPartAttaque extends RollPartSelect {
get code() { return PART_ATTAQUE } get code() { return PART_ATTAQUE }
@@ -18,29 +22,71 @@ export class RollPartAttaque extends RollPartSelect {
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
const attaques = rollData.active.actor.listAttaques() const attaques = rollData.active.actor.listAttaques()
refs.attaques = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData.active.actor)) refs.attaques = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData.active.actor))
if (refs.attaques.length>0){ refs.tactiques = TACTIQUES
if (refs.attaques.length > 0) {
this.$selectAttaque(rollData) this.$selectAttaque(rollData)
} }
} }
choices(refs) { return refs.attaques } choices(refs) { return refs.attaques }
static $extractAttaque(action, actor) { static $extractAttaque(attaque, actor) {
return { return {
key: `${action.action}::${action.arme.id}::${action.comp.id}`, key: `${attaque.action}::${attaque.name}`,
label: action.name, label: attaque.name,
action: action, attaque: attaque,
arme: action.arme, tactique: TACTIQUES[0],
comp: action.comp,
} }
} }
prepareContext(rollData) {
const current = this.getCurrent(rollData)
current.defenseur = StatusEffects.getActorEffetSurprise(rollData.opponent?.actor, 0)
current.attaquant = StatusEffects.getActorEffetSurprise(rollData.active.actor, current.attaque.forceRequise)
current.dmg = this.$dmgRollV2(rollData, current)
}
$dmgRollV2(rollData, current) {
const actor = rollData.active.actor
const defender = rollData.opponent?.actor
const dmgArme = RdDBonus.dmgArme(current.attaque.arme, current.attaque.dommagesArme)
const dmg = {
total: 0,
dmgArme: dmgArme,
penetration: current.attaque.arme.penetration(),
dmgTactique: current.tactique?.dmg ?? 0,
dmgParticuliere: 0, // TODO RdDBonus._dmgParticuliere(rollData),
dmgSurprise: current.surpriseDefenseur?.dmg ?? 0,
mortalite: RdDBonus.mortalite(current.dmg?.mortalite, current.attaque.arme.system.mortalite, defender?.isEntite()),
dmgActor: RdDBonus.bonusDmg(actor, current.attaque.carac.key, dmgArme, current.attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - current.attaque.forceRequise)
}
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante
return dmg
}
getAjustements(rollData) {
const current = this.getCurrent(rollData)
const ajustements = []
if (current.tactique) {
ajustements.push({ label: current.tactique.label, diff: current.tactique.attaque })
}
if (current.surpriseDefenseur) {
ajustements.push({ label: current.surpriseDefenseur.label, diff: current.surpriseDefenseur.attaque })
}
return ajustements
}
$selectAttaque(rollData, key) { $selectAttaque(rollData, key) {
this.selectByKey(rollData, key, 0) this.selectByKey(rollData, key)
} }
async _onRender(rollDialog, context, options) { async _onRender(rollDialog, context, options) {
const selectAttaque = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-attaque"]`) const selectAttaque = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-attaque"]`)
const selectTactique = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-tactique"]`)
const checkMortalite = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="check-mortalite"]`)
const current = this.getCurrent(rollDialog.rollData)
selectAttaque.addEventListener("change", e => { selectAttaque.addEventListener("change", e => {
const selectOptions = e.currentTarget.options const selectOptions = e.currentTarget.options
@@ -49,18 +95,29 @@ export class RollPartAttaque extends RollPartSelect {
rollDialog.setModeTitle() rollDialog.setModeTitle()
rollDialog.render() rollDialog.render()
}) })
selectTactique.addEventListener("change", e => {
const selectOptions = e.currentTarget.options
const index = selectOptions.selectedIndex
current.tactique = RdDBonus.find(selectOptions[index]?.value)
rollDialog.render()
})
checkMortalite?.addEventListener("change", e => {
current.dmg.mortalite = (e.currentTarget.checked ? 'mortel' : 'non-mortel')
rollDialog.render()
})
} }
getExternalPartsFilter(partCode, rollData) { impactOtherPart(part, rollData) {
if (this.visible(rollData)) { if (this.visible(rollData)) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
switch (partCode) { switch (part.code) {
case PART_CARAC: return p => Grammar.equalsInsensitive(current.action.carac.key, p.key) case PART_CARAC: return part.filterCaracs(rollData, [current.attaque.carac.key])
case PART_COMP: return p => p.label == current.comp?.name case PART_COMP: return part.filterComps(rollData, [current.attaque.comp?.name])
case PART_SIGN: return part.setArme(rollData, current.attaque.forceRequise, false)
} }
} }
return undefined return undefined
} }
} }

View File

@@ -36,9 +36,13 @@ export class RollPartCarac extends RollPartSelect {
} }
} }
setFilter(rollData, filter) { filterCaracs(rollData, allowed) {
allowed = allowed.filter(it => it != undefined)
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
refs.caracs = refs.all.filter(filter) refs.caracs = allowed.length > 0
// ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.key)))
? refs.all.filter(it => allowed.includes(it.key))
: refs.all
} }
prepareContext(rollData) { prepareContext(rollData) {

View File

@@ -18,8 +18,8 @@ export class RollPartCheckbox extends RollPart {
} }
loadRefs(rollData) { loadRefs(rollData) {
const current = this.getCurrent(rollData) const refs = this.getRefs(rollData)
current.label = this.getCheckboxLabel(rollData) refs.label = this.getCheckboxLabel(rollData)
} }
prepareContext(rollData) { prepareContext(rollData) {
@@ -43,7 +43,7 @@ export class RollPartCheckbox extends RollPart {
} }
getCheckboxLabelAjustement(rollData) { getCheckboxLabelAjustement(rollData) {
return `${this.getCheckboxIcon(rollData)} ${this.getCurrent(rollData).label}` return `${this.getCheckboxIcon(rollData)} ${this.getRefs(rollData).label}`
} }
async _onRender(rollDialog, context, options) { async _onRender(rollDialog, context, options) {

View File

@@ -45,9 +45,13 @@ export class RollPartComp extends RollPartSelect {
} }
} }
setFilter(rollData, filter) { filterComps(rollData, allowed) {
allowed = allowed.filter(it => it != undefined)
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
refs.comps = refs.all.filter(filter) refs.comps = allowed.length > 0
// ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.label)))
? refs.all.filter(it => allowed.includes(it.label))
: refs.all
} }
prepareContext(rollData) { prepareContext(rollData) {

View File

@@ -1,17 +1,133 @@
import { ROLL_MODE_DEFENSE } from "./roll-constants.mjs" import { ITEM_TYPES } from "../constants.js"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs" import { Grammar } from "../grammar.js"
import { ATTAQUE_TYPE, RdDItemArme } from "../item/arme.js"
import { RdDBonus } from "../rdd-bonus.js"
import { CARACS } from "../rdd-carac.js"
import { StatusEffects } from "../settings/status-effects.js"
import { DIFF_MODE, ROLL_MODE_DEFENSE } from "./roll-constants.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
import { PART_DIFF } from "./roll-part-diff.mjs"
import { RollPartSelect } from "./roll-part-select.mjs"
import { PART_SIGN } from "./roll-part-sign.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
export const PART_DEFENSE = 'defense' export const PART_DEFENSE = 'defense'
export class RollPartDefense extends RollPart { export class RollPartDefense extends RollPartSelect {
get code() { return PART_DEFENSE } get code() { return PART_DEFENSE }
get section() { return ROLLDIALOG_SECTION.CHOIX } get section() { return ROLLDIALOG_SECTION.CHOIX }
visible(rollData) { return this.isRollMode(rollData, ROLL_MODE_DEFENSE) } visible(rollData) { return this.isRollMode(rollData, ROLL_MODE_DEFENSE) }
loadRefs(rollData) { static getDiffAttaque(attackerRoll) {
const refs = this.getRefs(rollData) // TODO: rollDataV2?
refs.defenses =[] return attackerRoll.diffLibre;
} }
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const attaque = rollData.attaque
const defenseur = rollData.active.actor
refs.isDistance = [ATTAQUE_TYPE.TIR, ATTAQUE_TYPE.LANCER].find(it => it == attaque?.main)
const esquives = refs.isDistance == ATTAQUE_TYPE.TIR ? [] : defenseur.getCompetencesEsquive()
.map(it => RollPartDefense.$extractEsquive(it, defenseur))
const parades = defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier()))
.map(it => RollPartDefense.$extractParade(it, attaque?.attaque.arme, defenseur))
refs.defenses = [...esquives, ...parades].filter(it => it != undefined)
this.$selectDefense(rollData)
}
static $extractEsquive(esquive, defenseur) {
return {
key: esquive.id,
label: esquive.name,
img: esquive.img,
// TODO: carac pour créatures
carac: defenseur.isPersonnage() ? CARACS.DEROBEE : esquive.name,
comp: esquive
}
}
static $extractParade(armeDefense, armeAttaque, defenseur) {
const comp = (ITEM_TYPES.competencecreature == armeDefense.type)
? armeDefense
: defenseur.getCompetence(armeDefense.system.competence)
return {
key: armeDefense.id,
label: 'Parade ' + armeDefense.name,
img: armeDefense.img,
// TODO: carac pour créatures
carac: defenseur.isPersonnage() ? CARACS.MELEE : comp.name,
comp: comp,
arme: armeDefense,
forceRequise: armeDefense ? RdDItemArme.valeurMain(armeDefense.system.force ?? 0, RdDItemArme.getMainAttaque(comp)) : 0,
typeParade: armeAttaque ? RdDItemArme.defenseArmeParade(armeDefense, armeAttaque) : 'norm'
}
}
prepareContext(rollData) {
const current = this.getCurrent(rollData)
current.defenseur = StatusEffects.getActorEffetSurprise(rollData.active.actor, current.forceRequise)
// current.dmg = this.$dmgRollV2(rollData, current)
}
getAjustements(rollData) {
return []
}
choices(refs) { return refs.defenses }
$selectDefense(rollData, key) {
this.selectByKey(rollData, key)
}
async _onRender(rollDialog, context, options) {
const selectDefense = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-defense"]`)
selectDefense.addEventListener("change", e => {
const selectOptions = e.currentTarget.options
const index = selectOptions.selectedIndex
this.$selectDefense(rollDialog.rollData, selectOptions[index]?.value)
rollDialog.setModeTitle()
rollDialog.render()
})
}
impactOtherPart(part, rollData) {
if (this.visible(rollData)) {
const current = this.getCurrent(rollData)
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [current.carac])
case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
case PART_DIFF: return part.setDiff(rollData, this.getDiffDefense(rollData))
case PART_SIGN: return part.setArme(rollData, current.forceRequise, this.isArmeDisparate(rollData))
}
}
return undefined
}
isArmeDisparate(rollData) {
const armeDefense = this.getCurrent(rollData).arme
if (armeDefense) {
const armeAttaque = rollData.attaque?.attaque.arme
return RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) == 'sign'
}
return false
}
getDiffDefense(rollData) {
const current = this.getCurrent(rollData)
const refs = this.getRefs(rollData)
if (refs.isDistance) {
// Déterminer la difficulté de parade
return { diff: 0, mode: DIFF_MODE.LIBRE }
}
else {
return { diff: rollData.attaque.diff, mode: DIFF_MODE.DEFENSE }
}
}
} }

View File

@@ -50,6 +50,12 @@ export class RollPartDiff extends RollPart {
) )
} }
setDiff(rollData, diffDefense) {
const current = this.getCurrent(rollData)
current.value = diffDefense.diff
current.mode = diffDefense.mode
}
getAjustements(rollData) { getAjustements(rollData) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
return [{ return [{
@@ -67,4 +73,5 @@ export class RollPartDiff extends RollPart {
}) })
} }
} }

View File

@@ -91,12 +91,12 @@ export class RollPartJeu extends RollPartSelect {
}) })
} }
getExternalPartsFilter(partCode, rollData) { impactOtherPart(part, rollData) {
if (this.visible(rollData)) { if (this.visible(rollData)) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
switch (partCode) { switch (part.code) {
case PART_CARAC: return p => current.caracs?.includes(Grammar.toLowerCaseNoAccent(p.key)) case PART_CARAC: return part.filterCaracs(rollData, current.caracs)
case PART_COMP: return p => p.label == current.comp?.name case PART_COMP: return part.filterComps(rollData,[current.comp?.name])
} }
} }
return undefined return undefined

View File

@@ -1,6 +1,6 @@
import { ITEM_TYPES } from "../constants.js" import { ITEM_TYPES } from "../constants.js"
import { Grammar } from "../grammar.js" import { Grammar } from "../grammar.js"
import { RdDCarac } from "../rdd-carac.js" import { CARACS, RdDCarac } from "../rdd-carac.js"
import { RdDTimestamp } from "../time/rdd-timestamp.js" import { RdDTimestamp } from "../time/rdd-timestamp.js"
import { TMRUtility } from "../tmr-utility.js" import { TMRUtility } from "../tmr-utility.js"
import { ROLL_MODE_MEDITATION } from "./roll-constants.mjs" import { ROLL_MODE_MEDITATION } from "./roll-constants.mjs"
@@ -104,12 +104,12 @@ export class RollPartMeditation extends RollPartSelect {
}) })
} }
getExternalPartsFilter(partCode, rollData) { impactOtherPart(part, rollData) {
if (this.visible(rollData)) { if (this.visible(rollData)) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
switch (partCode) { switch (part.code) {
case PART_CARAC: return p => RdDCarac.isIntellect(p.key) case PART_CARAC: return part.filterCaracs(rollData, [CARACS.INTELLECT])
case PART_COMP: return p => p.label == current.comp?.name case PART_COMP: return part.filterComps(rollData,[current.comp?.name])
} }
} }
return undefined return undefined

View File

@@ -43,15 +43,15 @@ export class RollPartOeuvre extends RollPartSelect {
loadRefs(rollData) { loadRefs(rollData) {
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
refs.oeuvres = rollData.active.actor.items refs.oeuvres = rollData.active.actor.items
.filter(it => it.isOeuvre() && RollPartOeuvre.getArt(it)) .filter(it => it.isOeuvre() && RollPartOeuvre.getArt(it))
.map(it => RollPartOeuvre.$extractOeuvre(it, rollData.active.actor)) .map(it => RollPartOeuvre.$extractOeuvre(it, rollData.active.actor))
if (refs.oeuvres.length > 0) { if (refs.oeuvres.length > 0) {
this.$selectOeuvre(rollData) this.$selectOeuvre(rollData)
} }
} }
choices(refs) { return refs.oeuvres } choices(refs) { return refs.oeuvres }
static $extractOeuvre(oeuvre, actor) { static $extractOeuvre(oeuvre, actor) {
const art = RollPartOeuvre.getArt(oeuvre) const art = RollPartOeuvre.getArt(oeuvre)
return { return {
@@ -64,7 +64,7 @@ export class RollPartOeuvre extends RollPartSelect {
comp: actor.getCompetence(art.competence(oeuvre)) comp: actor.getCompetence(art.competence(oeuvre))
} }
} }
static getArt(oeuvre) { static getArt(oeuvre) {
return ARTS.find(it => it.type == oeuvre.type) return ARTS.find(it => it.type == oeuvre.type)
} }
@@ -85,12 +85,12 @@ export class RollPartOeuvre extends RollPartSelect {
}) })
} }
getExternalPartsFilter(partCode, rollData) { impactOtherPart(part, rollData) {
if (this.visible(rollData)) { if (this.visible(rollData)) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
switch (partCode) { switch (part.code) {
case PART_CARAC: return p => current.caracs?.includes(Grammar.toLowerCaseNoAccent(p.key)) case PART_CARAC: return part.filterCaracs(rollData, current.caracs)
case PART_COMP: return p => p.label == current.comp?.name case PART_COMP: return part.filterComps(rollData,[current.comp?.name])
} }
} }
return undefined return undefined

View File

@@ -27,7 +27,7 @@ export class RollPartSelect extends RollPart {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
const newChoice = (choices.length == 0) const newChoice = (choices.length == 0)
? { key: '', value: defValue } ? { key: '', value: defValue }
: this.$getSelectedChoice(choices, key ?? current?.key ?? refs.key ?? '') : this.$getSelectedChoice(choices, key ?? current?.key ?? refs?.key ?? '')
this.setCurrent(rollData, newChoice) this.setCurrent(rollData, newChoice)
return newChoice return newChoice
} }

View File

@@ -1,4 +1,5 @@
import { Misc } from "../misc.js" import { Misc } from "../misc.js"
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"
import { StatusEffects } from "../settings/status-effects.js" import { StatusEffects } from "../settings/status-effects.js"
import { ROLL_MODE_ATTAQUE, ROLL_MODE_DEFENSE } from "./roll-constants.mjs" import { ROLL_MODE_ATTAQUE, ROLL_MODE_DEFENSE } from "./roll-constants.mjs"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs" import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"
@@ -22,11 +23,6 @@ export class RollPartSign extends RollPart {
this.setSaved(targetData, this.getCurrent(rollData)) this.setSaved(targetData, this.getCurrent(rollData))
} }
// visible(rollData) {
// const current = this.getCurrent(rollData)
// return current.surprise != ''
// }
isCombat(rollData) { isCombat(rollData) {
return [ROLL_MODE_ATTAQUE, ROLL_MODE_DEFENSE].includes(rollData.mode.current) || rollData.mode.isCombat return [ROLL_MODE_ATTAQUE, ROLL_MODE_DEFENSE].includes(rollData.mode.current) || rollData.mode.isCombat
} }
@@ -42,41 +38,61 @@ export class RollPartSign extends RollPart {
const actor = rollData.active.actor; const actor = rollData.active.actor;
const isCombat = this.isCombat(rollData) const isCombat = this.isCombat(rollData)
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
current.armeDisparate = isCombat && current.armeDisparate
current.forceRequise = current.forceRequise ?? 0
current.surprise = actor.getSurprise(isCombat) current.surprise = actor.getSurprise(isCombat)
current.reasons = actor.getEffects(it => StatusEffects.niveauSurprise(it) > 0).map(it => it.name) current.reasons = actor.getEffects(it => StatusEffects.niveauSurprise(it) > 0).map(it => it.name)
current.diviseur = 1 current.diviseur = 1
if (isCombat && actor.isDemiReve()) {
current.reasons.push('Demi-rêve en combat')
}
if (current.surprise == 'demi') { if (current.surprise == 'demi') {
current.diviseur *= 2 current.diviseur *= 2
} }
if (isCombat && actor.isDemiReve()) {
if (this.isAttaqueFinesse(rollData)) { current.reasons.push('Demi-rêve en combat')
current.diviseur *= 2
current.reasons.push('Attaque en finesse')
} }
if (this.isForceInsuffisante(rollData)) { if (this.isParadeArmeDisparate(current)) {
current.diviseur *= 2
current.reasons.push('Armes disparates')
}
if (this.isForceInsuffisante(actor, current)) {
current.diviseur *= 2 current.diviseur *= 2
current.reasons.push('Force insuffisante') current.reasons.push('Force insuffisante')
} }
if (this.isAttaqueFinesse(rollData)) {
current.diviseur *= 2
current.reasons.push('Particulière en finesse')
}
if (!ReglesOptionnelles.isUsing('tripleSignificative')) {
current.diviseur = Math.min(current.diviseur, 4);
}
current.reason = current.reasons.join(', ') current.reason = current.reasons.join(', ')
} }
isForceInsuffisante(rollData) { isAttaqueFinesse(rollData) {
//this.isCombat(rollData) && ... arme avec force min return ROLL_MODE_DEFENSE == rollData.mode.current && rollData.attaque.particuliere == 'finesse'
return this.isCombat(rollData) && true
} }
isAttaqueFinesse(rollData) { isForceInsuffisante(actor, current) {
// this.rollData.selected[PART_DEFENSE] && attaquant avec particulière en finesse if (actor?.isPersonnage()) {
return ROLL_MODE_DEFENSE == rollData.mode.current && true const requise = current.forceRequise
const force = parseInt(actor.system.carac.force.value)
return requise > force
}
return false
}
isParadeArmeDisparate(current) {
return current.armeDisparate
} }
getAjustements(rollData) { getAjustements(rollData) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
if (current.surprise == 'demi') { if (current.surprise == 'demi') {
return [{ label: 'Significative requise ' + Misc.getFractionOneN(current.diviseur), diff: undefined }] return [
{ label: 'Significative requise ' + Misc.getFractionOneN(current.diviseur), diff: undefined },
...current.reasons.map(it => { return { label: '<i class="fa-solid fa-triangle-exclamation"></i> ' + it, diff: undefined } })
]
} }
return [] return []
} }
@@ -90,4 +106,9 @@ export class RollPartSign extends RollPart {
}) })
} }
setArme(rollData, forceRequise, armeDisparate) {
const current = this.getCurrent(rollData)
current.armeDisparate = armeDisparate
current.forceRequise = forceRequise
}
} }

View File

@@ -6,6 +6,7 @@ import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
import { TMRUtility } from "../tmr-utility.js" import { TMRUtility } from "../tmr-utility.js"
import { RdDItemSort } from "../item-sort.js" import { RdDItemSort } from "../item-sort.js"
import { RollPartSelect } from "./roll-part-select.mjs" import { RollPartSelect } from "./roll-part-select.mjs"
import { CARACS } from "../rdd-carac.js"
export const PART_SORT = "sort" export const PART_SORT = "sort"
@@ -115,12 +116,12 @@ export class RollPartSort extends RollPartSelect {
}) })
} }
getExternalPartsFilter(partCode, rollData) { impactOtherPart(part, rollData) {
if (this.visible(rollData)) { if (this.visible(rollData)) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
switch (partCode) { switch (part.code) {
case PART_CARAC: return p => p.key == 'reve' case PART_CARAC: return part.filterCaracs(rollData, [CARACS.REVE])
case PART_COMP: return p => current.draconics?.includes(p.label) case PART_COMP: return part.filterComps(rollData,current.draconics ?? [])
} }
} }
return undefined return undefined

View File

@@ -19,14 +19,14 @@ export class RollPartTache extends RollPartSelect {
loadRefs(rollData) { loadRefs(rollData) {
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
refs.taches = rollData.active.actor.itemTypes[ITEM_TYPES.tache] refs.taches = rollData.active.actor.itemTypes[ITEM_TYPES.tache]
.filter(tache => tache.system.points_de_tache_courant < tache.system.points_de_tache) .filter(tache => tache.system.points_de_tache_courant < tache.system.points_de_tache)
.map(tache => RollPartTache.$extractTache(tache, rollData.active.actor)) .map(tache => RollPartTache.$extractTache(tache, rollData.active.actor))
if (refs.taches.length > 0) { if (refs.taches.length > 0) {
this.$selectTache(rollData) this.$selectTache(rollData)
} }
} }
choices(refs) { return refs.taches } choices(refs) { return refs.taches }
static $extractTache(tache, actor) { static $extractTache(tache, actor) {
return { return {
key: tache.id, key: tache.id,
@@ -53,12 +53,12 @@ export class RollPartTache extends RollPartSelect {
}) })
} }
getExternalPartsFilter(partCode, rollData) { impactOtherPart(part, rollData) {
if (this.visible(rollData)) { if (this.visible(rollData)) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
switch (partCode) { switch (part.code) {
case PART_CARAC: return p => Grammar.equalsInsensitive(p.key, current?.tache.system.carac) case PART_CARAC: return part.filterCaracs(rollData, [current?.tache.system.carac])
case PART_COMP: return p => p.label == current?.comp.name case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
} }
} }
return undefined return undefined

View File

@@ -72,16 +72,14 @@ export class RollPart {
prepareContext(rollData) { } prepareContext(rollData) { }
/** ---- cross roll-part filtering ---- */ /** ---- cross roll-part filtering ---- */
setFilter(rollData, filter) { } applyImpact(rollData, filter) { }
getSpecialComp(rollData) { return [] } getSpecialComp(rollData) { return [] }
setSpecialComp(comps) { } setSpecialComp(comps) { }
getExternalPartsFilter(partCode, rollData) { return undefined } impactOtherPart(partCode, rollData) { }
setExternalFilter(visibleRollParts, rollData) {
const predicate = Misc.and( applyExternalImpacts(rollParts, rollData) {
visibleRollParts.map(p => p.getExternalPartsFilter(this.code, rollData)).filter(f => f != undefined) rollParts.forEach(part => part.impactOtherPart(this, rollData))
)
this.setFilter(rollData, predicate);
} }
toTemplateData() { toTemplateData() {

View File

@@ -121,7 +121,7 @@ export const referenceAjustements = {
}, },
tactique: { tactique: {
isUsed: (rollData, actor) => rollData.tactique, isUsed: (rollData, actor) => rollData.tactique,
getLabel: (rollData, actor) => RdDBonus.find(rollData.tactique).descr, getLabel: (rollData, actor) => RdDBonus.find(rollData.tactique).label,
getValue: (rollData, actor) => RdDBonus.find(rollData.tactique).attaque, getValue: (rollData, actor) => RdDBonus.find(rollData.tactique).attaque,
}, },
finesse: { finesse: {
@@ -130,11 +130,11 @@ export const referenceAjustements = {
}, },
surprise: { surprise: {
isUsed: (rollData, actor) => actor.getSurprise(rollData.passeArme), isUsed: (rollData, actor) => actor.getSurprise(rollData.passeArme),
getDescr: (rollData, actor) => RdDBonus.find(actor.getSurprise()).descr getDescr: (rollData, actor) => RdDBonus.find(actor.getSurprise()).label
}, },
attaqueDefenseurSurpris: { attaqueDefenseurSurpris: {
isUsed: (rollData, actor) => rollData.surpriseDefenseur, isUsed: (rollData, actor) => rollData.surpriseDefenseur,
getLabel: (rollData, actor) => RdDBonus.find(rollData.surpriseDefenseur).descr + (rollData.attackerRoll ? '' : ' défenseur'), getLabel: (rollData, actor) => RdDBonus.find(rollData.surpriseDefenseur).label + (rollData.attackerRoll ? '' : ' défenseur'),
getValue: (rollData, actor) => RdDBonus.find(rollData.surpriseDefenseur).attaque, getValue: (rollData, actor) => RdDBonus.find(rollData.surpriseDefenseur).attaque,
}, },
armeParade: { armeParade: {

View File

@@ -2,7 +2,7 @@ import { SYSTEM_RDD } from "../constants.js"
import { Misc } from "../misc.js" import { Misc } from "../misc.js"
export const EXPORT_CSV_SCRIPTARIUM = 'export-csv-scriptarium' export const EXPORT_CSV_SCRIPTARIUM = 'export-csv-scriptarium'
export const ROLL_DIALOG_V2 = 'roll-drialog-v2' export const ROLL_DIALOG_V2 = 'roll-dialog-v2'
const OPTIONS_AVANCEES = [ const OPTIONS_AVANCEES = [
{ group: 'Fenêtres', name: ROLL_DIALOG_V2, descr: "Utiliser les nouvelles fenêtres de jet", default: false }, { group: 'Fenêtres', name: ROLL_DIALOG_V2, descr: "Utiliser les nouvelles fenêtres de jet", default: false },

View File

@@ -1,4 +1,6 @@
import { SYSTEM_RDD } from "../constants.js"; import { SYSTEM_RDD } from "../constants.js";
import { Misc } from "../misc.js";
import { RdDBonus } from "../rdd-bonus.js";
export const STATUSES = { export const STATUSES = {
StatusGrappling: 'grappling', StatusGrappling: 'grappling',
@@ -12,9 +14,11 @@ export const STATUSES = {
StatusBleeding: 'bleeding', StatusBleeding: 'bleeding',
StatusDead: 'dead', StatusDead: 'dead',
StatusDemiReve: 'demi-reve', StatusDemiReve: 'demi-reve',
StatusForceWeak: 'force insuffisante',
} }
const demiReveStatusEffect = { rdd: true, id: STATUSES.StatusDemiReve, name: 'EFFECT.StatusDemiReve', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd12.svg' }; const forceWeakStatusEffect = { rdd: true, id: STATUSES.StatusForceWeak, name: 'EFFECT.StatusForceWeak', img: 'systems/foundryvtt-reve-de-dragon/assets/actions/weak.svg' };
const demiReveStatusEffect = { rdd: true, id: STATUSES.StatusDemiReve, name: 'EFFECT.StatusDemiReve', img: 'systems/foundryvtt-reve-de-dragon/assets/actions/sort.svg' };
const rddStatusEffects = [ const rddStatusEffects = [
{ rdd: true, id: STATUSES.StatusGrappling, tint: '#33cc33', name: 'EFFECT.StatusGrappling', img: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' }, { rdd: true, id: STATUSES.StatusGrappling, tint: '#33cc33', name: 'EFFECT.StatusGrappling', img: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' },
{ rdd: true, id: STATUSES.StatusGrappled, tint: '#ff9900', name: 'EFFECT.StatusGrappled', img: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' }, { rdd: true, id: STATUSES.StatusGrappled, tint: '#ff9900', name: 'EFFECT.StatusGrappled', img: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' },
@@ -29,15 +33,15 @@ const rddStatusEffects = [
{ rdd: true, id: STATUSES.StatusBleeding, name: 'EFFECT.StatusBleeding', img: 'icons/svg/blood.svg' }, { rdd: true, id: STATUSES.StatusBleeding, name: 'EFFECT.StatusBleeding', img: 'icons/svg/blood.svg' },
{ rdd: true, id: STATUSES.StatusDead, name: 'EFFECT.StatusDead', img: 'icons/svg/skull.svg' }, { rdd: true, id: STATUSES.StatusDead, name: 'EFFECT.StatusDead', img: 'icons/svg/skull.svg' },
demiReveStatusEffect demiReveStatusEffect,
forceWeakStatusEffect
]; ];
const statusDemiSurprise = new Set([STATUSES.StatusStunned, STATUSES.StatusProne, STATUSES.StatusRestrained]) const statusDemiSurprise = new Set([STATUSES.StatusStunned, STATUSES.StatusProne, STATUSES.StatusRestrained, STATUSES.StatusForceWeak])
const statusSurpriseTotale = new Set([STATUSES.StatusUnconscious, STATUSES.StatusBlind, STATUSES.StatusComma]) const statusSurpriseTotale = new Set([STATUSES.StatusUnconscious, STATUSES.StatusBlind, STATUSES.StatusComma])
export class StatusEffects extends FormApplication { export class StatusEffects extends FormApplication {
static onReady() { static onReady() {
const rddEffectIds = rddStatusEffects.map(it => it.id); const rddEffectIds = rddStatusEffects.map(it => it.id);
rddStatusEffects.forEach(it => { rddStatusEffects.forEach(it => {
@@ -67,6 +71,27 @@ export class StatusEffects extends FormApplication {
console.log('statusEffects', CONFIG.statusEffects); console.log('statusEffects', CONFIG.statusEffects);
} }
static isSurprise(effect) {
return StatusEffects.niveauSurprise(effect, true) > 0
}
static getSurprise(effects, isCombat = undefined) {
return StatusEffects.typeSurprise(
effects.map(it => StatusEffects.niveauSurprise(it, isCombat))
.reduce(Misc.sum(), 0)
)
}
static getActorEffetSurprise(actor, forceRequise) {
const effets = actor?.getEffects(StatusEffects.isSurprise, forceRequise) ?? []
return {
effets: effets,
surprise: effets.length > 0
? RdDBonus.find(StatusEffects.getSurprise(effets, true))
: undefined
}
}
static niveauSurprise(effect, isCombat) { static niveauSurprise(effect, isCombat) {
if (statusSurpriseTotale.intersects(effect.statuses)) { if (statusSurpriseTotale.intersects(effect.statuses)) {
return 2 return 2

View File

@@ -1,6 +1,6 @@
<div> <div>
{{#if effects}} {{#if effects}}
{{#each effects as |effect key|}} {{#each effects as |effect|}}
<span class="active-effect" data-effect="{{effect.id}}"> <span class="active-effect" data-effect="{{effect.id}}">
<img class="button-effect-img {{#if @root.options.isGM}}delete-active-effect{{/if}}" src="{{effect.img}}" data-tooltip="{{localize effect.name}}" width="24" height="24" /> <img class="button-effect-img {{#if @root.options.isGM}}delete-active-effect{{/if}}" src="{{effect.img}}" data-tooltip="{{localize effect.name}}" width="24" height="24" />
</span> </span>

View File

@@ -22,19 +22,34 @@
{{#if (eq defender.type 'personnage')}} {{#if (eq defender.type 'personnage')}}
<a class='chat-card-button appel-chance-defense' <a class='chat-card-button appel-chance-defense'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'> data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Faire appel à la chance</a> Faire appel à la chance
</a> </a>
<br> <br>
{{/if}} {{/if}}
{{#if (and (eq defender.type 'personnage') (gt defender.system.compteurs.destinee.value 0))}} {{#if (and (eq defender.type 'personnage') (gt defender.system.compteurs.destinee.value 0))}}
<a class='chat-card-button appel-destinee-defense' <a class='chat-card-button appel-destinee-defense'
data-attackerId='{{attackerId}}' data-attackerTokenId='{{attackerToken.id}}' data-defenderTokenId='{{defenderToken.id}}'> data-attackerId='{{attackerId}}' data-attackerTokenId='{{attackerToken.id}}' data-defenderTokenId='{{defenderToken.id}}'>
Utiliser la destinée</a> Utiliser la destinée
</a> </a>
<br> <br>
{{/if}} {{/if}}
{{/unless}} {{/unless}}
{{else}} {{else}}
{{#if (settings-get 'rdd-advanced-roll-dialog-v2')}}
<a class='chat-card-button defense-button'
data-attackerId='{{attackerId}}'
data-attackerTokenId='{{attackerToken.id}}'
data-defenderTokenId='{{defenderToken.id}}'
>
Se défendre
{{#if (or (eq attaqueCategorie 'tir') (eq attaqueCategorie 'lancer'))}}
(difficulté à déterminer)
{{else}}
à {{diffLibre }}
{{/if}}
</a>
<br>
{{/if}}
{{#each armes as |arme key|}} {{#each armes as |arme key|}}
<a class='chat-card-button parer-button' <a class='chat-card-button parer-button'
data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
@@ -53,9 +68,9 @@
{{/each}} {{/each}}
{{#if mainsNues}} {{#if mainsNues}}
<a class='chat-card-button parer-button' <a class='chat-card-button parer-button'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}' data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'
data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'> data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'>
Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}} Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}}
</a> </a>
<br> <br>
{{/if}} {{/if}}

View File

@@ -2,13 +2,13 @@
<span {{#if ajustements}}class="tooltip-overflow tooltip-dotted" {{/if}}> <span {{#if ajustements}}class="tooltip-overflow tooltip-dotted" {{/if}}>
<span> <span>
<span>{{rolled.caracValue}} à {{plusMoins rolled.finalLevel}}</span> <span>{{rolled.caracValue}} à {{plusMoins rolled.finalLevel}}</span>
{{#if ajustements}} {{log rolled}}
{{/if}} {{#if (and rolled.factorHtml (gt rolled.factorHtml 1))}}
{{log this}} <span class="rdd-diviseur">&times;{{{rolled.factorHtml}}}</span>
{{#if rolled.factorHtml}}<span class="rdd-diviseur">&times;{{{rolled.factorHtml}}}</span>{{/if}} <span>= {{rolled.score}}%</span>
<span class="rdd-diviseur">&times;{{{rolled.factorHtml}}}</span>
{{else}}
<span>= {{rolled.score}}%</span> <span>= {{rolled.score}}%</span>
{{#if rolled.factorHtml}}
<span class="rdd-diviseur">&times;{{{rolled.factorHtml}}}</span>
{{/if}} {{/if}}
</span> </span>
<div class="tooltiptext ttt-ajustements"> <div class="tooltiptext ttt-ajustements">

View File

@@ -1,6 +1,6 @@
<div class="table-ajustement"> <div class="table-ajustement">
<span class="tooltip tooltip-dotted"> <span class="tooltip tooltip-dotted">
<span class="roll-part-resolution">Ajustements: {{rollData.current.carac.value}} à {{plusMoins rollData.current.totaldiff}}</span> <span class="roll-part-resolution">Jet: {{rollData.current.carac.value}} à {{plusMoins rollData.current.totaldiff}}</span>
<div class="tooltiptext ttt-ajustements"> <div class="tooltiptext ttt-ajustements">
{{#each rollData.ajustements as |ajust|}} {{#each rollData.ajustements as |ajust|}}
{{#if ajust}} {{#if ajust}}

View File

@@ -1,14 +1,56 @@
{{log 'roll-part-attaque.current' current}} <subline>
{{log 'roll-part-attaque.refs' refs}} <select name="select-attaque" {{#if rollData.mode.retry}}disabled{{/if}}>
{{selectOptions refs.attaques selected=current.key valueAttr="key" labelAttr="label"}}
</select>
</subline>
<roll-part-img> <roll-part-img>
{{#if current.attaque}} {{#if current.attaque.arme}}
<img src="{{current.attaque.arme.img}}" data-tooltip="{{current.attaque.arme.name}}" /> <img src="{{current.attaque.arme.img}}" data-tooltip="{{current.attaque.arme.name}}" />
{{/if}} {{/if}}
</roll-part-img> </roll-part-img>
<roll-part-detail> <roll-part-detail>
<subline> <subline>
<select name="select-attaque" {{#if rollData.mode.retry}}disabled{{/if}}> <label for="select-tactique">Tactique:</label>
{{selectOptions refs.attaques selected=current.key valueAttr="key" labelAttr="label"}} <select name="select-tactique" {{#if rollData.mode.retry}}disabled{{/if}}>
{{selectOptions refs.tactiques selected=current.tactique.key valueAttr="key" labelAttr="label"}}
</select> </select>
</subline> </subline>
<subline>
{{#if (eq current.attaque.arme.system.mortalite 'empoignade')}}
Empoignade, pas de dommages directs
{{else}}
{{#if (and (ne current.attaque.arme.system.mortalite 'non-mortel') (eq current.dmg.penetration 0))}}
<input name="check-mortalite" type="checkbox" {{#if (eq current.dmg.mortalite 'mortel')}}checked{{/if}} />
{{/if}}
<label for="check-mortalite">
Dommages: {{plusMoins current.dmg.total}} ({{current.dmg.mortalite}})
{{~#if current.dmg.penetration}}, pénétration {{current.dmg.penetration}}{{/if}}
</label>
{{/if}}
</subline>
{{#if current.attaquant.effets}}
<subline>
<span class="status-surprise">
Attaquant en&nbsp;<strong>{{lowerFirst current.attaquant.surprise.label}}</strong>:&nbsp;
{{#each current.attaquant.effets as |effect|}}
{{localize effect.name}}
<img class="button-effect-img" src="{{effect.img}}" data-tooltip="{{localize effect.name}}" data-effect="{{effect.id}}"/>
{{/each}}
</span>
</subline>
{{/if}}
{{#if current.defenseur.surprise}}
<subline>
<span class="status-surprise">
Defenseur en&nbsp;<strong>{{lowerFirst current.defenseur.surprise.label}}</strong>:&nbsp;
{{#each current.defenseur.effets as |effect|}}
{{localize effect.name}}
<img class="button-effect-img" src="{{effect.img}}" data-tooltip="{{localize effect.name}}" data-effect="{{effect.id}}"/>
{{/each}}
</span>
</subline>
{{/if}}
<subline>
</subline>
</roll-part-detail> </roll-part-detail>

View File

@@ -1,3 +1,3 @@
<input name="{{code}}" type="checkbox" {{#if current.checked}}checked{{/if}}/> <input name="{{code}}" type="checkbox" {{#if current.checked}}checked{{/if}}/>
{{#if current.icon}}{{{current.icon}}}{{/if}} {{#if current.icon}}{{{current.icon}}}{{/if}}
<label for="{{code}}">{{current.label}} : {{plusMoins current.value}}</label> <label for="{{code}}">{{either current.label refs.label}} : {{plusMoins current.value}}</label>

View File

@@ -1,12 +1,21 @@
<roll-part-img> <roll-part-img>
{{#if current.defense}} <img src="{{current.img}}" data-tooltip="{{current.name}}" />
<img src="{{current.defense.arme.img}}" data-tooltip="{{current.defense.arme.name}}" />
{{/if}}
</roll-part-img> </roll-part-img>
<roll-part-detail> <roll-part-detail>
<subline> <subline>
<select name="select-arme" {{#if rollData.mode.retry}}disabled{{/if}}> <select name="select-defense" {{#if rollData.mode.retry}}disabled{{/if}}>
{{selectOptions refs.defenses selected=current.key valueAttr="key" labelAttr="label"}} {{selectOptions refs.defenses selected=current.key valueAttr="key" labelAttr="label"}}
</select> </select>
</subline> </subline>
{{#if current.defenseur.effets}}
<subline>
<span class="status-surprise">
Defense en&nbsp;<strong>{{lowerFirst current.defenseur.surprise.label}}</strong>:&nbsp;
{{#each current.defenseur.effets as |effect|}}
{{localize effect.name}}
<img class="button-effect-img" src="{{effect.img}}" data-tooltip="{{localize effect.name}}" data-effect="{{effect.id}}"/>
{{/each}}
</span>
</subline>
{{/if}}
</roll-part-detail> </roll-part-detail>