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
Développeur : VincentVk
Tests, Compendiums, Données: Fred, Fab, Grendel
Styles/CSS : Mandar
Tests, Compendiums, Données: Fred, Fab, Grendel, LeRatierBretonnien, VincentVk
Styles/CSS : Mandar, VincentVk
# 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.
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.

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
- Fix
- 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

View File

@@ -526,6 +526,7 @@ select,
grid-area: selection;
display: flex;
flex-direction: row;
margin: 0.1rem 0;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-img {
display: flex;
@@ -549,11 +550,20 @@ select,
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline {
display: flex;
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 {
display: flex;
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 {
display: flow;
width: 2.5rem;
@@ -608,6 +618,11 @@ select,
margin: 0 0.2rem;
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"] {
max-width: 6rem;
}

View File

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

View File

@@ -83,11 +83,14 @@
gap: 0.2rem;
align-items: start;
subline {
grid-area: selection;
display: flex;
flex-direction: row;
margin: 0.1rem 0;
}
roll-part-img {
display: flex;
flex-direction: column;
@@ -110,10 +113,19 @@
subline {
display: flex;
flex-direction: row;
margin: 0.1rem 0;
div.poesie-extrait{
display: flex;
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;
padding: 0;
}
img.button-effect-img {
max-width: 1rem;
max-height: 1rem;
margin: 0 0.1rem;
}
}
roll-carac select[name="select-carac"] {

View File

@@ -49,7 +49,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
});
foundry.utils.mergeObject(formData.calc, {
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),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.isSurenc() ? "Sur-Encombrement!" : "",

View File

@@ -31,7 +31,7 @@ export class ActorToken {
constructor(token) {
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.id = token.actor?.id
this.token = token

View File

@@ -14,7 +14,7 @@ import { STATUSES } from "./settings/status-effects.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.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 { DialogFabriquerPotion } from "./dialog-fabriquer-potion.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 { RdDCoeur } from "./coeur/rdd-coeur.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 { RdDItemTete } from "./item/tete.js";
import { RdDItemSort } from "./item-sort.js";
@@ -167,63 +166,63 @@ export class RdDActor extends RdDBaseActorSang {
.find(it => true)
}
/* -------------------------------------------- */
/** Retourne une liste triée d'actions d'armes avec le split arme1 main / arme 2 main / lancer */
listActionsAttaque() {
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;
isForceInsuffisante(forceRequise) {
const force = parseInt(this.system.carac.force.value)
return forceRequise > force
}
/* -------------------------------------------- */
$prepareAttaqueArme(arme, main) {
const comp = this.getCompetence(RdDActor.$getCompetenceAction(arme, main))
const caracCode = RdDActor.$getCaracAction(comp, main)
const caracValue = this.system.carac[caracCode].value
const dommages = arme.system.dommages.toString()
/** Retourne une liste triée d'actions d'armes avec le split arme1 main / arme 2 main / lancer */
listActionsAttaque() {
const actions = []
const uniques = []
// TODO: déplacer sur RdDItemArme
if (arme.system.unemain && arme.system.deuxmains && !dommages.includes("/")) {
ui.notifications.info(`Les dommages de l'arme à 1/2 mains ${arme.name} ne sont pas corrects (ie sous la forme X/Y)`)
}
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
const addAttaque = (arme, main) => {
const dommagesArme = RdDItemArme.valeurMain(arme.system.dommages, main)
const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
const ecaillesEfficacite = arme.system.magique ? arme.system.ecaille_efficacite : 0;
return {
name: arme.name + (main ? ' ' + main : ''),
action: 'attaque',
initOnly: false,
arme: arme,
comp: comp,
main: main,
carac: { key: caracCode, value: caracValue },
equipe: arme.system.equipe,
dmg: dmg,
initiative: RdDInitiative.calculInitiative(niveau, caracValue, ajustement)
const comp = this.getCompetence(RdDActor.$getCompetenceAction(arme, main))
const unique = [comp.id, arme.name, dommagesArme, forceRequise, ecaillesEfficacite].join('|');
if (uniques.includes(unique)) {
return
}
uniques.push(unique);
const caracCode = RdDActor.$getCaracAction(comp, main)
const caracValue = this.system.carac[caracCode].value
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) {
@@ -239,10 +238,10 @@ export class RdDActor extends RdDBaseActorSang {
static $getCompetenceAction(arme, main) {
switch (main) {
case '(1 main)': return arme.competence1Mains()
case '(2 mains)': return arme.competence2Mains()
case '(lancer)': return arme.system.lancer
case '(tir)': return arme.system.tir
case ATTAQUE_TYPE.UNE_MAIN: return arme.competence1Mains()
case ATTAQUE_TYPE.DEUX_MAINS: return arme.competence2Mains()
case ATTAQUE_TYPE.LANCER: return arme.system.lancer
case ATTAQUE_TYPE.TIR: return arme.system.tir
default: return arme.system.competence
}
}
@@ -2278,13 +2277,12 @@ export class RdDActor extends RdDBaseActorSang {
static _getComposantsCaracDerivee(caracName) {
switch (Grammar.toLowerCaseNoAccent(caracName)) {
case 'reve-actuel': case 'reve actuel': return ['reve']
case 'chance-actuelle': case 'chance actuelle': return ['chance']
case 'vie': return ['constitution']
case 'tir': return ['vue', 'dexterite']
case 'lancer': return ['force', 'dexterite', 'vue']
case 'melee': return ['force', 'agilite']
case 'derobee': return ['agilite']
case CARACS.REVE_ACTUEL: case 'reve actuel': return [CARACS.REVE]
case CARACS.CHANCE_ACTUELLE: case 'chance actuelle': return [CARACS.CHANCE]
case CARACS.TIR: return [CARACS.DEXTERITE, CARACS.VUE]
case CARACS.LANCER: return [CARACS.FORCE, CARACS.DEXTERITE, CARACS.VUE]
case CARACS.MELEE: return [CARACS.FORCE, CARACS.AGILITE]
case CARACS.DEROBEE: return [CARACS.AGILITE]
}
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 { RdDRollResult } from "../rdd-roll-result.js";
import { RdDItemArme } from "../item/arme.js";
import { RdDItemCompetence } from "../item-competence.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDItemArme } from "../item/arme.js";
import { ChatUtility } from "../chat-utility.js";
import { DialogValidationEncaissement } from "../dialog-validation-encaissement.js";
@@ -80,7 +80,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
getBonusDegat() { return RdDCarac.getCaracDerivee(this.getEncombrementMax()).plusdom }
getMoralTotal() { return 0 }
listeAmoureux() {return []}
listeAmoureux() { return [] }
getProtectionNaturelle() { return Number(this.system.attributs?.protection?.value ?? 0) }
getSConst() { return 0 }
@@ -113,7 +113,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
listActions({ isAttaque = false, isEquipe = false }) {
return this.itemTypes[ITEM_TYPES.competencecreature]
.filter(it => RdDItemCompetenceCreature.isAttaque(it))
.filter(it => it.isAttaque())
.map(it => RdDItemCompetenceCreature.attaqueCreature(it))
.filter(it => it != undefined);
}
@@ -191,6 +191,10 @@ export class RdDBaseActorReve extends RdDBaseActor {
return RdDItemArme.getArme(armeParadeId ? this.getEmbeddedDocument('Item', armeParadeId) : undefined)
}
isForceInsuffisante(forceRequise) {
return false
}
getDraconicOuPossession() { return POSSESSION_SANS_DRACONIC }
getPossession(possessionId) {
@@ -222,8 +226,13 @@ export class RdDBaseActorReve extends RdDBaseActor {
/* -------------------------------------------- */
isEffectAllowed(effectId) { return false }
getEffects(filter = e => true) {
return this.getEmbeddedCollection("ActiveEffect").filter(filter);
getEffects(filter = e => true, forceRequise = undefined) {
const effects = this.getEmbeddedCollection("ActiveEffect").filter(filter)
if (forceRequise && this.isForceInsuffisante(forceRequise)) {
/// TODO
effects.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
}
return effects
}
getEffectByStatus(statusId) {
@@ -259,11 +268,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
getSurprise(isCombat = undefined) {
return StatusEffects.typeSurprise(
this.getEffects()
.map(it => StatusEffects.niveauSurprise(it, isCombat))
.reduce(Misc.sum(), 0)
)
return StatusEffects.getSurprise(this.getEffects(), isCombat)
}
/* -------------------------------------------- */

View File

@@ -5,7 +5,6 @@ import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { Monnaie } from "../item-monnaie.js";
import { ITEM_TYPES } from "../constants.js";
import { RdDItem } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.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);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.competences.filter(it => it.type == ITEM_TYPES.competencecreature)
.forEach(it => it.isdommages = RdDItemCompetenceCreature.isDommages(it))
.forEach(it => it.isdommages = it.isDommages())
return formData;
}

View File

@@ -1,126 +1,78 @@
import { ITEM_TYPES } from "./constants.js";
import { Grammar } from "./grammar.js";
import { RdDInitiative } from "./initiative.mjs";
import { RdDItem } from "./item.js";
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" },
}
import { RdDInitiative } from "./initiative.mjs";
/* -------------------------------------------- */
export class RdDItemCompetenceCreature extends Item {
export class RdDItemCompetenceCreature extends RdDItem {
/* -------------------------------------------- */
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 ITEM_TYPE() { return ITEM_TYPES.competencecreature }
/* -------------------------------------------- */
static armeCreature(item) {
const categorieAttaque = RdDItemCompetenceCreature.getCategorieAttaque(item)
static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" }
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) {
// cloner pour ne pas modifier la compétence
return foundry.utils.mergeObject(item, {
action: item.isCompetencePossession() ? 'possession' : 'attaque',
const initative = RdDInitiative.calculInitiative(comp.system.niveau, comp.system.carac_value);
const armeComp = new RdDItem({
name: comp.name,
type: ITEM_TYPES.arme,
img: comp.img,
system: {
competence: item.name,
competence: comp.name,
cac: categorieAttaque == "naturelle" ? "naturelle" : "",
niveau: item.system.niveau,
initiative: RdDInitiative.calculInitiative(item.system.niveau, item.system.carac_value),
niveau: comp.system.niveau,
initiative: initative,
mortalite: comp.system.mortalite,
dommages: comp.system.dommages,
equipe: true,
resistance: 100,
dommagesReels: item.system.dommages,
penetration: 0,
force: 0,
rapide: true,
}
}, { inplace: false, });
}
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 {
});
const attaque = {
name: comp.name,
action: comp.isCompetencePossession() ? 'possession' : 'attaque',
initOnly: false,
arme: new RdDItem({
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,
}
}),
arme: armeComp,
comp: comp,
// main: '',
carac: { key: comp.name, value: comp.system.carac_value },
equipe: true,
mortalite: comp.system.mortalite,
dmg: comp.system.dommages,
initiative: initative
}
};
return attaque
}
return undefined;
}
/* -------------------------------------------- */
static isAttaque(item) {
return RdDItemCompetenceCreature.getCategorieAttaque(item) != undefined
isAttaque() {
return this.getCategorieAttaque() != undefined
}
static getCategorieAttaque(item) {
if (item.type == ITEM_TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
case "possession":
return item.system.categorie
}
getCategorieAttaque() {
switch (this.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
case "possession":
return this.system.categorie
}
return undefined
}
static isDommages(item) {
if (item.type == ITEM_TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
return true
}
}
return false
}
static isParade(item) {
if (item.type == ITEM_TYPES.competencecreature) {
return item.system.categorie_parade || item.system.isparade
isDommages() {
switch (this.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
return true
}
return false
}

View File

@@ -9,7 +9,6 @@ import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Misc } from "./misc.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { RdDItem } from "./item.js";
import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.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) {
formData.isparade = RdDItemCompetenceCreature.isParade(this.item)
formData.isdommages = RdDItemCompetenceCreature.isDommages(this.item)
formData.isparade = this.item.isParade()
formData.isdommages = this.item.isDommages()
}
if (this.item.type == ITEM_TYPES.tache ||
this.item.type == ITEM_TYPES.livre ||

View File

@@ -1,4 +1,7 @@
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 { Misc } from "./misc.js";
@@ -7,10 +10,6 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDUtility } from "./rdd-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.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 = [
ITEM_TYPES.arme,
@@ -186,7 +185,10 @@ export class RdDItem extends Item {
isMonnaie() { return this.type == ITEM_TYPES.monnaie; }
isNourritureBoisson() { return this.type == ITEM_TYPES.nourritureboisson; }
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) }
isEsquive() {
return (this.isCompetence()

View File

@@ -1,10 +1,9 @@
import { ITEM_TYPES } from "../constants.js";
import { RdDItem } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js"
import { BASE_CORPS_A_CORPS } from "./base-items.js";
import { Grammar } from "../grammar.js";
import { RdDInitiative } from "../initiative.mjs";
import { MappingCreatureArme } from "./mapping-creature-arme.mjs";
const nomCategorieParade = {
"sans-armes": "Sans arme",
@@ -19,24 +18,48 @@ const nomCategorieParade = {
"haches": "Haches",
"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 {
static get ITEM_TYPE() { return ITEM_TYPES.arme }
static get defaultIcon() {
return defaultItemImgArme
//return "systems/foundryvtt-reve-de-dragon/icons/armes_armure/epee_sord.webp";
}
static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/armes_armures/epee_gnome.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) {
switch (arme ? arme.type : '') {
case ITEM_TYPES.arme: return arme;
case ITEM_TYPES.competencecreature:
return RdDItemCompetenceCreature.armeCreature(arme);
return MappingCreatureArme.armeCreature(arme);
}
return RdDItemArme.corpsACorps();
}
@@ -47,11 +70,11 @@ export class RdDItemArme extends RdDItem {
return arme.name
case ITEM_TYPES.arme:
switch (maniement) {
case 'competence': return arme.system.competence;
case '(1 main)': return arme.competence1Mains()
case '(2 mains)': return arme.competence2Mains()
case '(tir)': case 'tir': return arme.system.tir
case '(lancer)': case 'lancer': return arme.system.lancer;
case ATTAQUE_TYPE.COMPETENCE: return arme.system.competence;
case ATTAQUE_TYPE.UNE_MAIN: return arme.competence1Mains()
case ATTAQUE_TYPE.DEUX_MAINS: return arme.competence2Mains()
case ATTAQUE_TYPE.TIR: case 'tir': return arme.system.tir
case ATTAQUE_TYPE.LANCER: case 'lancer': return arme.system.lancer;
}
}
return undefined
@@ -88,16 +111,13 @@ export class RdDItemArme extends RdDItem {
/* -------------------------------------------- */
static getCategorieParade(armeData) {
if (![ITEM_TYPES.arme, ITEM_TYPES.competencecreature].includes(armeData.type)) {
return ''
}
if (armeData.system.categorie_parade) {
return armeData.system.categorie_parade
}
// 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) {
return ITEM_TYPES.competencecreature;
}
@@ -192,7 +212,7 @@ export class RdDItemArme extends RdDItem {
return Number(arme.system.dommages)
}
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);
}
@@ -201,7 +221,7 @@ export class RdDItemArme extends RdDItem {
static armeUneOuDeuxMains(arme, aUneMain) {
if (arme && !arme.system.cac) {
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;
}
@@ -222,25 +242,10 @@ export class RdDItemArme extends RdDItem {
return false
}
static isAttaque(arme) {
switch (arme.type) {
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
isAttaque() {
return this.system.resistance > 0 || this.system.portee_courte > 0
}
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) {
let competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS
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"
}
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";
const conditionsTactiques = [
{ type: '', descr: '', dmg: 0, attaque: 0, parade: 0, esquive: true },
{ type: 'charge', descr: 'Charge', dmg: 2, attaque: 4, parade: -4, esquive: false },
{ type: 'feinte', descr: 'Feinte', dmg: 1, attaque: 1, parade: 0, esquive: true },
{ type: 'pret', descr: 'prêt', dmg: 0, attaque: 0, parade: 0, esquive: true },
{ type: 'demi', descr: 'Demi-surprise', dmg: 1, attaque: 0, parade: 0, esquive: true },
{ type: 'totale', descr: 'Surprise totale', dmg: 10, attaque: 6, parade: 0, esquive: true },
{ key: '', label: '', dmg: 0, attaque: 0, parade: 0, esquive: true, isTactique: false },
{ key: 'normale', label: 'Attaque normale', dmg: 0, attaque: 0, parade: 0, esquive: true, isTactique: true },
{ key: 'charge', label: 'Charge', dmg: 2, attaque: 4, parade: -4, esquive: false, isTactique: true },
{ key: 'feinte', label: 'Feinte', dmg: 1, attaque: 1, parade: 0, esquive: true, isTactique: true },
{ key: 'pret', label: 'prêt', dmg: 0, attaque: 0, parade: 0, esquive: true, isTactique: false },
{ 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 {
static get tactiques() {
return conditionsTactiques.filter(it => it.isTactique)
}
/* -------------------------------------------- */
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) {
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 = {
total: 0,
dmgArme: dmgArme,
@@ -41,39 +45,44 @@ export class RdDBonus {
dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used),
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;
}
/* -------------------------------------------- */
static description(condition) {
return RdDBonus.find(condition).descr;
return RdDBonus.find(condition).label
}
/* -------------------------------------------- */
static dmgBonus(condition) {
return RdDBonus.find(condition).dmg;
return RdDBonus.find(condition).dmg
}
/* -------------------------------------------- */
static bonusAttaque(condition) {
return RdDBonus.find(condition).attaque;
return RdDBonus.find(condition).attaque
}
/* -------------------------------------------- */
static _calculMortalite(rollData, isEntiteIncarnee) {
return RdDBonus.mortalite(rollData.dmg?.mortalite, rollData.arme?.system.mortalite, isEntiteIncarnee)
}
static mortalite(mortaliteSelect, mortaliteArme, isEntiteIncarnee) {
return isEntiteIncarnee ? "entiteincarnee"
: rollData.dmg?.mortalite
?? rollData.arme?.system.mortalite
: mortaliteSelect
?? mortaliteArme
?? "mortel";
}
/* -------------------------------------------- */
static dmgArme(arme) {
static dmgArme(arme, dommagesMain) {
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)
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) {
const dmgActor = actor.getBonusDegat()
if (categorie == undefined) {
return 0
}
const dmgActor = actor.getBonusDegat()
switch (categorie) {
case "tir": return 0;
case "lancer": return Math.max(0, Math.min(dmgArme, dmgActor));
case "(tir)": case "tir": return 0
case "(lancer)": case "lancer": return Math.max(0, Math.min(dmgArme, dmgActor));
}
return dmgActor;
}

View File

@@ -52,6 +52,15 @@ export const CARACS = {
EMPATHIE: 'empathie',
REVE: 'reve',
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 = {
[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.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' },
'protection': { code: 'protection', label: 'Protection naturelle', isCarac: false, path: 'system.attributs.protection.value' },
'beaute': { code: 'beaute', label: 'Beauté', isCarac: false, path: 'system.background.beaute.value' }
[CARACS.PROTECTION]: { code: CARACS.PROTECTION, label: 'Protection naturelle', isCarac: false, path: 'system.attributs.protection.value' },
[CARACS.BEAUTE]: { code: CARACS.BEAUTE, label: 'Beauté', isCarac: false, path: 'system.background.beaute.value' }
}
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 = {
'melee': { code: "melee", label: 'Mêlée', path: 'system.carac.melee.value' },
'tir': { code: "tir", label: 'Tir', path: 'system.carac.tir.value' },
'lancer': { code: "lancer", label: 'Lancer', path: 'system.carac.lancer.value' },
'derobee': { code: "derobee", label: 'Dérobée', path: 'system.carac.derobee.value' },
'chance-actuelle': { code: "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.MELEE]: { code: CARACS.MELEE, label: 'Mêlée', path: 'system.carac.melee.value' },
[CARACS.TIR]: { code: CARACS.TIR, label: 'Tir', path: 'system.carac.tir.value' },
[CARACS.LANCER]: { code: CARACS.LANCER, label: 'Lancer', path: 'system.carac.lancer.value' },
[CARACS.DEROBEE]: { code: CARACS.DEROBEE, label: 'Dérobée', path: 'system.carac.derobee.value' },
[CARACS.CHANCE_ACTUELLE]: { code: CARACS.CHANCE_ACTUELLE, label: 'Chance actuelle', path: 'system.carac.lancer.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')
@@ -125,11 +134,11 @@ export class RdDCarac {
}
static isIntellect(caracLabel) {
return Grammar.toLowerCaseNoAccent(caracLabel) == 'intellect';
return Grammar.toLowerCaseNoAccent(caracLabel) == CARACS.INTELLECT
}
static isVolonte(caracLabel) {
return Grammar.toLowerCaseNoAccent(caracLabel) == 'volonte';
return Grammar.toLowerCaseNoAccent(caracLabel) == CARACS.VOLONTE
}
static isChance(caracLabel) {
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 { RdDItemCompetenceCreature } from "./item-competencecreature.js";
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 = [
@@ -398,6 +402,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static registerChatCallbacks(html) {
for (let button of [
'.defense-button',
'.parer-button',
'.esquiver-button',
'.particuliere-attaque',
@@ -466,6 +471,8 @@ export class RdDCombat {
switch (button) {
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 '.esquiver-button': return this.esquive(attackerRoll, compId, competence);
case '.encaisser-button': return this.encaisser(attackerRoll, defenderRoll);
@@ -713,9 +720,6 @@ export class RdDCombat {
if (RdDCombat.isReussite(attackerRoll)) {
return await this._onAttaqueNormale(attackerRoll)
}
// if (RdDCombat.isParticuliere(attackerRoll) && attackerRoll.particuliere == undefined) {
// return
// }
if (RdDCombat.isEchecTotal(attackerRoll)) {
return await this._onAttaqueEchecTotal(attackerRoll)
}
@@ -857,7 +861,7 @@ export class RdDCombat {
/* -------------------------------------------- */
_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.forEach(armeDefense => {
// Ajout du # d'utilisation ce round
@@ -946,6 +950,41 @@ export class RdDCombat {
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) {
let defenderRoll = {
@@ -971,7 +1010,6 @@ export class RdDCombat {
return defenderRoll;
}
async _onParade(defenderRoll) {
if (RdDCombat.isReussite(defenderRoll)) {
await this._onParadeNormale(defenderRoll)
@@ -982,6 +1020,16 @@ export class RdDCombat {
}
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) {
@@ -1255,4 +1303,8 @@ export class RdDCombat {
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 { ITEM_TYPES } from "./constants.js";
import { ChatUtility } from "./chat-utility.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 {
@@ -186,7 +185,7 @@ export class RdDEmpoignade {
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
if (attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
MappingCreatureArme.setRollDataCreature(rollData)
}
if (empoignade.system.pointsemp >= 2) {
if (!empoignade.system.ausol) {

View File

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

View File

@@ -1,9 +1,7 @@
import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { Targets } from "./targets.js";
import { ITEM_TYPES } from "./constants.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { Grammar } from "./grammar.js";
/* -------------------------------------------- */
/* 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 { RdDCoeur } from "./coeur/rdd-coeur.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 { RdDCarac } from "./rdd-carac.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
@@ -271,6 +271,7 @@ export class RdDUtility {
// foundry et options
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('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('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-name', (item) => item.nameDisplay)
Handlebars.registerHelper('item-name', (item) => item.nameDisplay)
// TMRs
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 { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
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 {
@@ -87,4 +89,29 @@ export class RollDialogAdapter {
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))
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) => {
const selected = list.find(p => p.key == key)
if (selected && path && path != '') {
@@ -195,6 +200,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
}
return selected
})
Handlebars.registerHelper('roll-part-context', (rollData, code) => {
const rollPart = ROLL_PARTS.find(it => it.code == code)
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 = {}) {
const rollDialog = new RollDialog(rollData, rollOptions)
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 */
$loadParts() {
const rollData = this.rollData;
const loadedMode = rollData.mode?.current
rollData.current = rollData.current ?? {}
rollData.selected = rollData.selected ?? {}
rollData.mode = rollData.mode ?? {}
rollData.mode.retry = rollData.mode.retry ?? false
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.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)
rollData.refs = this.$prepareRefs(rollData)
rollData.options = rollData.options ?? { showDice: true, rollMode: game.settings.get("core", "rollMode") }
ROLL_PARTS.forEach(p => p.initialize(rollData))
ROLL_PARTS.forEach(p => p.restore(rollData))
ROLL_PARTS.filter(p => p.isValid(rollData))
.forEach(p => {
p.loadRefs(rollData)
p.prepareContext(rollData)
})
.forEach(p => {
p.loadRefs(rollData)
p.prepareContext(rollData)
})
this.selectMode();
}
selectMode() {
this.rollData.mode.label = this.getSelectedMode().title(this.rollData)
this.getSelectedMode().setRollDataMode(this.rollData)
@@ -360,7 +364,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
this.setModeTitle()
const visibleRollParts = this.getActiveParts()
visibleRollParts.forEach(p => p.setExternalFilter(visibleRollParts, rollData))
visibleRollParts.forEach(p => p.applyExternalImpacts(visibleRollParts, rollData))
this.setSpecialComp(visibleRollParts);

View File

@@ -5,7 +5,7 @@ export class RollModeDefense extends RollMode {
get code() { return ROLL_MODE_DEFENSE }
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) {
return rollData.attacker

View File

@@ -37,6 +37,7 @@ export class RollPartAppelMoral extends RollPartCheckbox {
}
return '<i class="fa-regular fa-face-meh"></i>'
}
getCheckboxLabel(rollData) { return "Appel au moral" }
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 { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.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'
const TACTIQUES = RdDBonus.tactiques.filter(it => it.isTactique)
export class RollPartAttaque extends RollPartSelect {
get code() { return PART_ATTAQUE }
@@ -18,29 +22,71 @@ export class RollPartAttaque extends RollPartSelect {
const refs = this.getRefs(rollData)
const attaques = rollData.active.actor.listAttaques()
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)
}
}
choices(refs) { return refs.attaques }
static $extractAttaque(action, actor) {
static $extractAttaque(attaque, actor) {
return {
key: `${action.action}::${action.arme.id}::${action.comp.id}`,
label: action.name,
action: action,
arme: action.arme,
comp: action.comp,
key: `${attaque.action}::${attaque.name}`,
label: attaque.name,
attaque: attaque,
tactique: TACTIQUES[0],
}
}
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) {
this.selectByKey(rollData, key, 0)
this.selectByKey(rollData, key)
}
async _onRender(rollDialog, context, options) {
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 => {
const selectOptions = e.currentTarget.options
@@ -49,18 +95,29 @@ export class RollPartAttaque extends RollPartSelect {
rollDialog.setModeTitle()
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)) {
const current = this.getCurrent(rollData)
switch (partCode) {
case PART_CARAC: return p => Grammar.equalsInsensitive(current.action.carac.key, p.key)
case PART_COMP: return p => p.label == current.comp?.name
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [current.attaque.carac.key])
case PART_COMP: return part.filterComps(rollData, [current.attaque.comp?.name])
case PART_SIGN: return part.setArme(rollData, current.attaque.forceRequise, false)
}
}
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)
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) {

View File

@@ -18,8 +18,8 @@ export class RollPartCheckbox extends RollPart {
}
loadRefs(rollData) {
const current = this.getCurrent(rollData)
current.label = this.getCheckboxLabel(rollData)
const refs = this.getRefs(rollData)
refs.label = this.getCheckboxLabel(rollData)
}
prepareContext(rollData) {
@@ -43,7 +43,7 @@ export class RollPartCheckbox extends RollPart {
}
getCheckboxLabelAjustement(rollData) {
return `${this.getCheckboxIcon(rollData)} ${this.getCurrent(rollData).label}`
return `${this.getCheckboxIcon(rollData)} ${this.getRefs(rollData).label}`
}
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)
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) {

View File

@@ -1,17 +1,133 @@
import { ROLL_MODE_DEFENSE } from "./roll-constants.mjs"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"
import { ITEM_TYPES } from "../constants.js"
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 class RollPartDefense extends RollPart {
export class RollPartDefense extends RollPartSelect {
get code() { return PART_DEFENSE }
get section() { return ROLLDIALOG_SECTION.CHOIX }
visible(rollData) { return this.isRollMode(rollData, ROLL_MODE_DEFENSE) }
loadRefs(rollData) {
const refs = this.getRefs(rollData)
refs.defenses =[]
static getDiffAttaque(attackerRoll) {
// TODO: rollDataV2?
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) {
const current = this.getCurrent(rollData)
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)) {
const current = this.getCurrent(rollData)
switch (partCode) {
case PART_CARAC: return p => current.caracs?.includes(Grammar.toLowerCaseNoAccent(p.key))
case PART_COMP: return p => p.label == current.comp?.name
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, current.caracs)
case PART_COMP: return part.filterComps(rollData,[current.comp?.name])
}
}
return undefined

View File

@@ -1,6 +1,6 @@
import { ITEM_TYPES } from "../constants.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 { TMRUtility } from "../tmr-utility.js"
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)) {
const current = this.getCurrent(rollData)
switch (partCode) {
case PART_CARAC: return p => RdDCarac.isIntellect(p.key)
case PART_COMP: return p => p.label == current.comp?.name
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [CARACS.INTELLECT])
case PART_COMP: return part.filterComps(rollData,[current.comp?.name])
}
}
return undefined

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { Misc } from "../misc.js"
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"
import { StatusEffects } from "../settings/status-effects.js"
import { ROLL_MODE_ATTAQUE, ROLL_MODE_DEFENSE } from "./roll-constants.mjs"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"
@@ -22,11 +23,6 @@ export class RollPartSign extends RollPart {
this.setSaved(targetData, this.getCurrent(rollData))
}
// visible(rollData) {
// const current = this.getCurrent(rollData)
// return current.surprise != ''
// }
isCombat(rollData) {
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 isCombat = this.isCombat(rollData)
const current = this.getCurrent(rollData)
current.armeDisparate = isCombat && current.armeDisparate
current.forceRequise = current.forceRequise ?? 0
current.surprise = actor.getSurprise(isCombat)
current.reasons = actor.getEffects(it => StatusEffects.niveauSurprise(it) > 0).map(it => it.name)
current.diviseur = 1
if (isCombat && actor.isDemiReve()) {
current.reasons.push('Demi-rêve en combat')
}
if (current.surprise == 'demi') {
current.diviseur *= 2
}
if (this.isAttaqueFinesse(rollData)) {
current.diviseur *= 2
current.reasons.push('Attaque en finesse')
if (isCombat && actor.isDemiReve()) {
current.reasons.push('Demi-rêve en combat')
}
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.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(', ')
}
isForceInsuffisante(rollData) {
//this.isCombat(rollData) && ... arme avec force min
return this.isCombat(rollData) && true
isAttaqueFinesse(rollData) {
return ROLL_MODE_DEFENSE == rollData.mode.current && rollData.attaque.particuliere == 'finesse'
}
isAttaqueFinesse(rollData) {
// this.rollData.selected[PART_DEFENSE] && attaquant avec particulière en finesse
return ROLL_MODE_DEFENSE == rollData.mode.current && true
isForceInsuffisante(actor, current) {
if (actor?.isPersonnage()) {
const requise = current.forceRequise
const force = parseInt(actor.system.carac.force.value)
return requise > force
}
return false
}
isParadeArmeDisparate(current) {
return current.armeDisparate
}
getAjustements(rollData) {
const current = this.getCurrent(rollData)
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 []
}
@@ -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 { RdDItemSort } from "../item-sort.js"
import { RollPartSelect } from "./roll-part-select.mjs"
import { CARACS } from "../rdd-carac.js"
export const PART_SORT = "sort"
@@ -115,12 +116,12 @@ export class RollPartSort extends RollPartSelect {
})
}
getExternalPartsFilter(partCode, rollData) {
impactOtherPart(part, rollData) {
if (this.visible(rollData)) {
const current = this.getCurrent(rollData)
switch (partCode) {
case PART_CARAC: return p => p.key == 'reve'
case PART_COMP: return p => current.draconics?.includes(p.label)
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [CARACS.REVE])
case PART_COMP: return part.filterComps(rollData,current.draconics ?? [])
}
}
return undefined

View File

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

View File

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

View File

@@ -121,7 +121,7 @@ export const referenceAjustements = {
},
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,
},
finesse: {
@@ -130,11 +130,11 @@ export const referenceAjustements = {
},
surprise: {
isUsed: (rollData, actor) => actor.getSurprise(rollData.passeArme),
getDescr: (rollData, actor) => RdDBonus.find(actor.getSurprise()).descr
getDescr: (rollData, actor) => RdDBonus.find(actor.getSurprise()).label
},
attaqueDefenseurSurpris: {
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,
},
armeParade: {

View File

@@ -2,7 +2,7 @@ import { SYSTEM_RDD } from "../constants.js"
import { Misc } from "../misc.js"
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 = [
{ 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 { Misc } from "../misc.js";
import { RdDBonus } from "../rdd-bonus.js";
export const STATUSES = {
StatusGrappling: 'grappling',
@@ -12,9 +14,11 @@ export const STATUSES = {
StatusBleeding: 'bleeding',
StatusDead: 'dead',
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 = [
{ 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' },
@@ -29,15 +33,15 @@ const rddStatusEffects = [
{ 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' },
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])
export class StatusEffects extends FormApplication {
static onReady() {
const rddEffectIds = rddStatusEffects.map(it => it.id);
rddStatusEffects.forEach(it => {
@@ -67,6 +71,27 @@ export class StatusEffects extends FormApplication {
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) {
if (statusSurpriseTotale.intersects(effect.statuses)) {
return 2

View File

@@ -1,6 +1,6 @@
<div>
{{#if effects}}
{{#each effects as |effect key|}}
{{#each effects as |effect|}}
<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" />
</span>

View File

@@ -22,19 +22,34 @@
{{#if (eq defender.type 'personnage')}}
<a class='chat-card-button appel-chance-defense'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Faire appel à la chance</a>
Faire appel à la chance
</a>
<br>
{{/if}}
{{#if (and (eq defender.type 'personnage') (gt defender.system.compteurs.destinee.value 0))}}
<a class='chat-card-button appel-destinee-defense'
data-attackerId='{{attackerId}}' data-attackerTokenId='{{attackerToken.id}}' data-defenderTokenId='{{defenderToken.id}}'>
Utiliser la destinée</a>
Utiliser la destinée
</a>
<br>
{{/if}}
{{/unless}}
{{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|}}
<a class='chat-card-button parer-button'
data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
@@ -53,9 +68,9 @@
{{/each}}
{{#if mainsNues}}
<a class='chat-card-button parer-button'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'
data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'>
Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}}
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'
data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'>
Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}}
</a>
<br>
{{/if}}

View File

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

View File

@@ -1,6 +1,6 @@
<div class="table-ajustement">
<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">
{{#each rollData.ajustements as |ajust|}}
{{#if ajust}}

View File

@@ -1,14 +1,56 @@
{{log 'roll-part-attaque.current' current}}
{{log 'roll-part-attaque.refs' refs}}
<subline>
<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>
{{#if current.attaque}}
{{#if current.attaque.arme}}
<img src="{{current.attaque.arme.img}}" data-tooltip="{{current.attaque.arme.name}}" />
{{/if}}
</roll-part-img>
<roll-part-detail>
<subline>
<select name="select-attaque" {{#if rollData.mode.retry}}disabled{{/if}}>
{{selectOptions refs.attaques selected=current.key valueAttr="key" labelAttr="label"}}
<label for="select-tactique">Tactique:</label>
<select name="select-tactique" {{#if rollData.mode.retry}}disabled{{/if}}>
{{selectOptions refs.tactiques selected=current.tactique.key valueAttr="key" labelAttr="label"}}
</select>
</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>

View File

@@ -1,3 +1,3 @@
<input name="{{code}}" type="checkbox" {{#if current.checked}}checked{{/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>
{{#if current.defense}}
<img src="{{current.defense.arme.img}}" data-tooltip="{{current.defense.arme.name}}" />
{{/if}}
<img src="{{current.img}}" data-tooltip="{{current.name}}" />
</roll-part-img>
<roll-part-detail>
<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"}}
</select>
</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>