Compare commits

..

1 Commits

Author SHA1 Message Date
0f3d1e708d Empoignade V2 2025-10-22 01:17:41 +02:00
60 changed files with 292 additions and 80025 deletions

View File

@@ -25,7 +25,4 @@ Merci à eux !!
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.
# Licences
- L'export pdf utilise la librairie [pdf-lib](https://pdf-lib.js.org/) sous licence [MIT](pdf-lib-LICENSE.md)
L'ensemble du code est sous licence Creative Commons.

View File

@@ -1,65 +0,0 @@
<?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 448 434"
version="1.1"
id="svg6"
sodipodi:docname="surenc.svg"
width="448"
height="434"
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="3840"
inkscape:window-height="2054"
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="224"
inkscape:cy="210"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<g
class=""
id="g4"
transform="translate(-32,-46)">
<path
d="m 256,46 c -45.074,0 -82,36.926 -82,82 0,25.812 12.123,48.936 30.938,64 H 128 L 32,480 H 480 L 384,192 H 307.062 C 325.877,176.936 338,153.812 338,128 338,82.926 301.074,46 256,46 Z m 0,36 c 25.618,0 46,20.382 46,46 0,25.618 -20.382,46 -46,46 -25.618,0 -46,-20.382 -46,-46 0,-25.618 20.382,-46 46,-46 z m -82.215,202.95 h 23.5 v 33.263 l 33.873,-33.264 h 27.283 l -43.883,43.15 48.4,47.974 H 233.54 l -36.255,-35.888 v 35.888 h -23.5 z m 119.934,21.24 c 4.76,0 8.952,0.934 12.573,2.806 3.62,1.872 6.938,4.82 9.95,8.85 v -10.13 h 21.972 v 61.462 c 0,10.986 -3.48,19.368 -10.438,25.146 -6.917,5.82 -16.968,8.727 -30.152,8.727 -4.272,0 -8.4,-0.325 -12.39,-0.976 a 77.367,77.367 0 0 1 -12.024,-2.99 v -17.03 c 3.826,2.198 7.57,3.826 11.23,4.884 3.664,1.098 7.347,1.648 11.05,1.648 7.162,0 12.41,-1.566 15.746,-4.7 3.337,-3.132 5.006,-8.035 5.006,-14.708 v -4.7 c -3.01,3.986 -6.328,6.916 -9.95,8.788 -3.62,1.87 -7.813,2.808 -12.573,2.808 -8.343,0 -15.238,-3.275 -20.69,-9.826 -5.453,-6.592 -8.18,-14.974 -8.18,-25.146 0,-10.214 2.727,-18.576 8.18,-25.086 5.452,-6.55 12.347,-9.827 20.69,-9.827 z m 8.118,15.746 c -4.517,0 -8.038,1.67 -10.56,5.005 -2.523,3.338 -3.784,8.058 -3.784,14.162 0,6.266 1.22,11.026 3.662,14.28 2.442,3.215 6.003,4.823 10.682,4.823 4.557,0 8.096,-1.67 10.62,-5.006 2.522,-3.337 3.784,-8.036 3.784,-14.098 0,-6.104 -1.262,-10.824 -3.785,-14.16 -2.523,-3.337 -6.062,-5.006 -10.62,-5.006 z"
fill="#ffffff"
fill-opacity="1"
id="path2" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 716 KiB

After

Width:  |  Height:  |  Size: 716 KiB

View File

@@ -1,41 +1,20 @@
# 13.0
## 13.0.16 - La paix d'Illysis
- Export de personnages sous forme de feuille de personnage pdf
- Correction d'un problème à l'ouverture de personnages saisis dans d'anciennes versions
- Fenêtre de jet v2
- on peut faire des jets en dehors des combats
## 13.0.15 - Les pièces d'Illysis
- On peut de nouveau acheter dans les commerces
- Corrections V13
- Les textes sur fond "parchemin" ne sont plus affichés en blanc
## 13.0.14 - Le familier d'Illysis
- Les réussites particulières en demi-surprise sont de simples réussites
- Les images des scènes par défaut sont corrigées
- Ajout d'une image de status "sur-encombré"
- Correction V13
- couleur lisible dans les sommaires des journaux et des compendiums
- Amélioration des entités:
- l'attaquant ne sait plus que c'est une entité de cauchemar (surprise!)
- l'encaissement indique une blessure dans le tchat... même si ce n'est que de l'endurance
- les blurettes suivent les règles des entités de cauchemar (p322)
- Nouvelle fenêtre de jets de dés
- attaque/défense des créatures
- Attaque/défense des créatures
- les attaques/parades avec une arme trop lourde se font en demi-surprise
- les demandes de défense disparaîssent une fois prises en compte
- empoignade
- l'empoignade est possible avec une initiative d'empoignade, ou en cours d'empoignade
- seule la dague, le pugilat et la dague sont possibles en cours d'empoignade
- jet de Dextérité/Dague pour utiliser la dague en cours d'empoignade (p136)
- attaquer avec une arme un empoigneur donne un +4 si pas d'empoignade (p134)
- la différence de taille donne un bonus/malus en cours d'empoignade (p135)
- jet de Dextérité/Dague pour utiliser la dague en cours d'empoignade
- attaquer avec une arme un empoigneur donne un +4 si pas d'empoignade
- les dommages de l'empoignade ajoutent/enlèvent un point d'empoignade
- le statut d'empoignade est affiché sur les tokens
- les défenses contre une empoignade sont corrigées
## 13.0.13 - L'épanouissement d'Illysis

View File

@@ -648,7 +648,8 @@ select,
.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section[name="coeur"] select[name="coeur"] {
max-width: 4rem;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section img {
.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section[name="empoignade"] img {
/* image de d100 */
max-width: 1rem;
max-height: 1rem;
gap: 0;
@@ -660,6 +661,10 @@ select,
/* image de d100 */
max-width: 2.5rem;
max-height: 2.5rem;
gap: 0;
margin: 0;
padding: 0;
filter: invert(0.8);
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-buttons {
display: flex;
@@ -1378,9 +1383,6 @@ select,
margin: 0.1rem 0;
align-items: center;
}
.system-foundryvtt-reve-de-dragon .prosemirror menu {
background-color: var(--color-background-chat-message);
}
.system-foundryvtt-reve-de-dragon .app.sheet .editor.prosemirror {
height: fit-content;
min-height: 5rem;
@@ -1572,70 +1574,22 @@ select,
.system-foundryvtt-reve-de-dragon .type-compendium {
font-size: 0.6rem;
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body,
.system-foundryvtt-reve-de-dragon .application .window-content,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar {
background: url(../assets/ui/bg_left.webp) no-repeat left top;
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body label,
.system-foundryvtt-reve-de-dragon .application .window-content label,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar label,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body .hint,
.system-foundryvtt-reve-de-dragon .application .window-content .hint,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar .hint,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body .permissions-list,
.system-foundryvtt-reve-de-dragon .application .window-content .permissions-list,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar .permissions-list,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body nav.tabs,
.system-foundryvtt-reve-de-dragon .application .window-content nav.tabs,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar nav.tabs,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body nav.tabs button,
.system-foundryvtt-reve-de-dragon .application .window-content nav.tabs button,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar nav.tabs button,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body nav.tabs button .count,
.system-foundryvtt-reve-de-dragon .application .window-content nav.tabs button .count,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar nav.tabs button .count,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body button,
.system-foundryvtt-reve-de-dragon .application .window-content button,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar button {
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body a,
.system-foundryvtt-reve-de-dragon .application .window-content a,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar a {
color: var(--color-dark-3);
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body a.filter.active,
.system-foundryvtt-reve-de-dragon .application .window-content a.filter.active,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar a.filter.active {
color: var(--color-dark-1);
}
.system-foundryvtt-reve-de-dragon .window-app .window-content {
background: url(../assets/ui/bg_left.webp) no-repeat left top;
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header {
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-header {
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
color: #ffffff;
}
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header label,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header .hint,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header .permissions-list,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs button,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs button .count,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header div,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header button {
color: rgba(255, 255, 255, 0.9);
}
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header input {
color: rgba(255, 255, 255, 0.9);
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-header :is(
input[type="text"],
input[type="number"],
input[type="password"],
input[type="datetime-local"],
input[type="date"],
input[type="time"]) {
color: rgba(255, 255, 255, 0.75);
background: rgba(255, 255, 255, 0.1);
border: 0 none;
margin-bottom: 0.2rem;
}
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header input[type="checkbox"] {
color: rgba(255, 255, 255, 0.75);
}
.system-foundryvtt-reve-de-dragon input[type="number"] {
text-align: right;
padding-right: 0.5rem;
@@ -1676,7 +1630,7 @@ select,
width: calc(100% - 2px);
height: var(--form-field-height);
margin: 0;
color: var(--rdd-color-text-primary);
color: var(--color-text-dark-primary);
border-radius: 0.2rem;
}
.system-foundryvtt-reve-de-dragon form.app-personnage-aleatoire h2 {

View File

@@ -1,6 +1,6 @@
{
"TYPES": {
"Actor": {
"Actor": {
"personnage": "Personnage",
"creature": "Créature",
"entite": "Entité de cauchemar",
@@ -67,7 +67,6 @@
"StatusComma": "Comma",
"StatusDead": "Mort",
"StatusDemiReve": "Demi-rêve",
"StatusSurEnc": "Sur-encombrement",
"StatusForceWeak": "Force insuffisante"
}
}
}

View File

@@ -656,15 +656,9 @@
margin: 0.1rem 0;
align-items: center;
}
.prosemirror {
menu{
background-color: var(--color-background-chat-message);
}
}
.app.sheet .editor.prosemirror {
height: fit-content;
min-height: 5rem;
}
.app.sheet .editor.prosemirror .editor-container {
min-height: 5rem;
@@ -871,60 +865,26 @@
.type-compendium {
font-size: 0.6rem;
}
.window-app.sheet .window-content .sheet-body,
.application .window-content,
.journal-entry .journal-sidebar {
background: url(../assets/ui/bg_left.webp) no-repeat left top;
color: var(--rdd-color-text-primary);
label ,
.hint ,
.permissions-list ,
nav.tabs,
nav.tabs button,
nav.tabs button .count,
button {
color: var(--rdd-color-text-primary);
}
a {
color: var(--color-dark-3);
}
a.filter.active {
color: var(--color-dark-1);
}
}
/* ======================================== */
/* Sheet */
.window-app .window-content{
background: url(../assets/ui/bg_left.webp) no-repeat left top;
color: var(--rdd-color-text-primary);
.sheet-header {
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
label ,
.hint ,
.permissions-list ,
nav.tabs,
nav.tabs button,
nav.tabs button .count,
div,
button {
color: rgba(255, 255, 255, 0.9);
}
input {
//color: rgba(255, 255, 255, 0);
color: rgba(255, 255, 255, 0.9);
border: 0 none;
margin-bottom: 0.2rem;
}
input[type="checkbox"] {
// background-color: hsla(268, 41%, 56%, 0.9);
color: rgba(255, 255, 255, 0.75);
}
}
}
.window-app.sheet .window-content .sheet-header{
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
color: rgba(255, 255, 255, 1);
}
.window-app.sheet .window-content .sheet-header :is(
input[type="text"],
input[type="number"],
input[type="password"],
input[type="datetime-local"],
input[type="date"],
input[type="time"]) {
color: rgba(255, 255, 255, 0.75);
background: rgba(255, 255, 255, 0.1);
border: 0 none;
margin-bottom: 0.2rem;
}
input[type="number"] {
text-align: right;
@@ -968,7 +928,7 @@
width: calc(100% - 2px);
height: var(--form-field-height);
margin: 0;
color: var(--rdd-color-text-primary);
color: var(--color-text-dark-primary);
border-radius: 0.2rem;
}
form.app-personnage-aleatoire {

View File

@@ -224,7 +224,8 @@
max-width: 4rem;
}
roll-conditions roll-section img {
roll-conditions roll-section[name="empoignade"] img {
/* image de d100 */
max-width: 1rem;
max-height: 1rem;
gap: 0;
@@ -236,6 +237,10 @@
/* image de d100 */
max-width: 2.5rem;
max-height: 2.5rem;
gap: 0;
margin: 0;
padding: 0;
filter: invert(0.8);
}
roll-buttons {

View File

@@ -375,6 +375,14 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.actor.update(formData);
}
async splitItem(item) {
const dialog = await DialogSplitItem.create(item, (item, split) => this._onSplitItem(item, split));
dialog.render(true);

View File

@@ -190,7 +190,7 @@ export class RdDActor extends RdDBaseActorSang {
const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
const ecaillesEfficacite = arme.system.magique ? arme.system.ecaille_efficacite : 0;
const comp = this.getCompetence(arme.getCompetenceAction(main))
const comp = this.getCompetence(RdDActor.$getCompetenceAction(arme, main))
const unique = [comp.id, arme.name, dommages, forceRequise, ecaillesEfficacite].join('|');
if (uniques.includes(unique)) {
return
@@ -218,14 +218,18 @@ export class RdDActor extends RdDBaseActorSang {
})
}
addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS)
this.itemTypes[ITEM_TYPES.arme]
.filter(it => it.isAttaque())
.sort(Misc.ascending(it => it.name))
.forEach(arme => arme.getTypeAttaques().forEach(t => addAttaque(arme, t)))
.forEach(arme => {
if (arme.system.unemain && arme.system.competence && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.UNE_MAIN) }
if (arme.system.deuxmains && arme.system.competence && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.DEUX_MAINS) }
if (arme.system.lancer && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.LANCER) }
if (arme.system.tir) { addAttaque(arme, ATTAQUE_TYPE.TIR) }
})
addAttaque(RdDItemArme.pugilat(this), ATTAQUE_TYPE.CORPS_A_CORPS)
addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS)
return actions
}
@@ -241,6 +245,16 @@ export class RdDActor extends RdDBaseActorSang {
}
}
static $getCompetenceAction(arme, main) {
switch (main) {
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
}
}
/* -------------------------------------------- */
async $perteReveEnchantementsChateauDormants() {
const toUpdate = this.items.filter(it => [ITEM_TYPES.potion, ITEM_TYPES.gemme].includes(it.type))
@@ -742,18 +756,20 @@ export class RdDActor extends RdDBaseActorSang {
let updates = {};
if (caracName == LIST_CARAC_PERSONNAGE.reve.code) {
if (to > Misc.toInt(this.system.reve.seuil.value)) {
updates[`system.reve.seuil.value`] = to
updates[`system.reve.seuil.value`] = to; // SFA : Direct and packed changes
//this.setPointsDeSeuil(to);
}
}
if (caracName == LIST_CARAC_PERSONNAGE.chance.code) {
if (to > Misc.toInt(this.system.compteurs.chance.value)) {
updates[`system.compteurs.chance.value`] = to
updates[`system.compteurs.chance.value`] = to; // SFA : Direct and packed changes
//this.setPointsDeChance(to);
}
}
let selectedCarac = this.findCaracByName(caracName);
const from = selectedCarac.value
updates[`system.carac.${caracName}.value`] = to;
await this.update(updates, { noHook: true });
await this.update(updates);
await ExperienceLog.add(this, XP_TOPIC.CARAC, from, to, caracName);
}
@@ -1738,7 +1754,7 @@ export class RdDActor extends RdDBaseActorSang {
this.tmrApp?.close();
this.tmrApp = undefined;
}
}],
} ],
onRollDone: RollDialog.onRollDoneClose,
onClose: () => {
this.tmrApp?.restoreTMRAfterAction();
@@ -2592,7 +2608,7 @@ export class RdDActor extends RdDBaseActorSang {
if (item?.isEquipable()) {
const isEquipe = !item.system.equipe;
await item.update({ "system.equipe": isEquipe });
this.computeEncTotal()
this.computeEncTotal();
if (isEquipe)
this.verifierForceMin(item);
}
@@ -2983,7 +2999,6 @@ export class RdDActor extends RdDBaseActorSang {
if (updatedEndurance && options.diff) {
await this.setEffect(STATUSES.StatusUnconscious, updatedEndurance.value == 0)
}
await super.onUpdateActor(update, options, actorId)
}
/* -------------------------------------------- */
@@ -3026,12 +3041,9 @@ export class RdDActor extends RdDBaseActorSang {
break
case ITEM_TYPES.empoignade:
await RdDEmpoignade.deleteLinkedEmpoignade(this.id, item)
// TODO: check remaining emp.
await this.setEffect(STATUSES.StatusGrappled, false)
await this.setEffect(STATUSES.StatusGrappling, false)
break
}
await super.onDeleteItem(item, options, id)
super.onDeleteItem(item, options, id)
}
/* -------------------------------------------- */

View File

@@ -56,15 +56,13 @@ export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
if (this.options.vueDetaillee) {
// On carac change
this.html.find('.carac-value').change(async event => {
if (event.currentTarget.name.includes("carac.")) {
let caracName = event.currentTarget.name.replace("carac.", "")
await this.actor.updateCarac(caracName, parseInt(event.currentTarget.value))
}
})
let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "")
await this.actor.updateCarac(caracName, parseInt(event.target.value))
});
// On competence change
this.html.find('.competence-value').change(async event => {
let compName = event.currentTarget.attributes.compname.value
await this.actor.updateCompetence(compName, parseInt(event.currentTarget.value))
await this.actor.updateCompetence(compName, parseInt(event.target.value))
});
}
}

View File

@@ -89,6 +89,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
getSConst() { return 0 }
/* -------------------------------------------- */
isSurenc() { return false }
computeMalusSurEncombrement() { return 0 }
ajustementAstrologique() { return 0 }
@@ -125,8 +126,6 @@ export class RdDBaseActorReve extends RdDBaseActor {
async remiseANeuf() { }
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
computeResumeBlessure() { }
countBlessures(filter = it => !it.isContusion()) { return 0 }
async santeIncDec(name, inc, isCritique = false) { }
async finDeRound(options = { terminer: false }) {
@@ -229,9 +228,49 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
}
/* -------------------------------------------- */
isEffectAllowed(effectId) { return false }
getEffects(filter = e => true, forceRequise = undefined) {
const effects = this.getEmbeddedCollection("ActiveEffect")
const selected = effects.filter(filter)
if (forceRequise && this.isForceInsuffisante(forceRequise)) {
selected.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
}
return selected
}
getEffectByStatus(statusId) {
return this.getEffects().find(it => it.statuses.has(statusId));
}
async setEffect(statusId, status) {
if (this.isEffectAllowed(statusId)) {
const effect = this.getEffectByStatus(statusId);
if (!status && effect) {
await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id], { render: true })
}
if (status && !effect) {
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(statusId)], { render: true })
}
}
}
async removeEffect(id) {
this.removeEffects(it => it.id == id)
}
async removeEffects(filter = e => true) {
if (game.user.isGM) {
const effectsToRemove = this.getEffects(filter);
const ids = effectsToRemove.map(it => it.id);
await this.deleteEmbeddedDocuments('ActiveEffect', ids);
}
}
/* -------------------------------------------- */
isDemiReve() {
return this.getEffectsByStatus(STATUSES.StatusDemiReve).length > 0
return this.getEffectByStatus(STATUSES.StatusDemiReve) != undefined
}
getSurprise(isCombat = undefined, forceRequise = undefined) {

View File

@@ -186,7 +186,6 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
await this.changeBleedingState()
break
}
await super.onCreateItem(item, options, id)
}
async onUpdateItem(item, options, id) {
@@ -195,7 +194,6 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
await this.changeBleedingState()
break
}
await super.onUpdateItem(item, options, id)
}
async changeBleedingState() {
@@ -315,7 +313,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
}
isSonne() {
return this.getEffectsByStatus(STATUSES.StatusStunned).length > 0
return this.getEffectByStatus(STATUSES.StatusStunned)
}
isEffectAllowed(effectId) { return true }

View File

@@ -49,7 +49,7 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
formData.calc = {
fortune: Monnaie.toSolsDeniers(this.actor.getFortune()),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
encTotal: this.actor.getEncTotal(),
encTotal: await this.actor.computeEncTotal(),
}
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
@@ -229,6 +229,14 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.actor.update(formData);
}
async splitItem(item) {
const dialog = await DialogSplitItem.create(item, (item, split) => this._onSplitItem(item, split));
dialog.render(true);

View File

@@ -10,7 +10,7 @@ import { RdDConfirm } from "../rdd-confirm.js";
import { RdDUtility } from "../rdd-utility.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
import { RdDItem } from "../item.js";
import { StatusEffects, STATUSES } from "../settings/status-effects.js";
import { STATUSES } from "../settings/status-effects.js";
export class RdDBaseActor extends Actor {
@@ -243,68 +243,18 @@ export class RdDBaseActor extends Actor {
getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
getEncombrementMax() { return 0 }
isSurenc() { return false }
/* -------------------------------------------- */
isEffectAllowed(effectId) { return false }
getEffects(filter = e => true, forceRequise = undefined) {
const effects = this.getEmbeddedCollection("ActiveEffect")
const selected = effects.filter(filter)
if (forceRequise && this.isForceInsuffisante(forceRequise)) {
selected.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
}
return selected
}
getEffectsByStatus(effectId) {
return this.getEffects().filter(it => it.statuses.has(effectId))
}
async setEffect(effectId, status) {
if (this.isEffectAllowed(effectId)) {
const effects = this.getEffectsByStatus(effectId)
if (!status && effects.length > 0) {
await this.deleteEmbeddedDocuments('ActiveEffect', effects.map(it => it.id), { render: true })
}
if (status && effects.length == 0) {
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(effectId)], { render: true })
}
}
}
async removeEffect(id) {
this.removeEffects(it => it.id == id)
}
async removeEffects(filter = e => true) {
if (game.user.isGM) {
const effectsToRemove = this.getEffects(filter);
const ids = effectsToRemove.map(it => it.id);
await this.deleteEmbeddedDocuments('ActiveEffect', ids);
}
}
/* -------------------------------------------- */
async updateCarac(caracName, to) {
}
async onUpdateActor(change, options, actorId) {
const updatedCarac = change?.system?.carac
if (updatedCarac && (updatedCarac.force || updatedCarac.reve || updatedCarac.taille)) {
console.log(' onUpdateActor', change, options, actorId)
await this.setEffect(STATUSES.StatusSurEnc, this.isSurenc())
}
}
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
async onCreateItem(item, options, id) {
}
async onCreateItem(item, options, id) { }
async onUpdateItem(item, options, id) {
}
async onUpdateItem(item, options, id) { }
async onUpdateActor(update, options, actorId) { }
async onDeleteItem(item, options, id) {
if (item.isInventaire()) {
@@ -312,7 +262,6 @@ export class RdDBaseActor extends Actor {
}
}
async _removeItemFromConteneur(item) {
const updates = this.items.filter(it => it.isConteneur() && it.system.contenu.includes(item.id))
.map(conteneur => {
@@ -561,22 +510,16 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
async computeEncTotal() {
if (this.pack) {
this.encTotal = 0
}
else {
const wasSurenc = this.isSurenc()
if (!this.pack) {
this.encTotal = this.items.filter(it => RdDItem.getItemTypesInventaire().includes(it.type))
.map(it => it.getEncTotal()).reduce(Misc.sum(), 0)
const isSurenc = this.isSurenc()
if (isSurenc != wasSurenc) {
await this.setEffect(STATUSES.StatusSurEnc, isSurenc)
}
return this.encTotal;
}
return 0;
}
getEncTotal() {
return Math.floor(this.encTotal ?? 0)
return Math.floor(this.encTotal ?? 0);
}
async createItem(type, name = undefined) {
@@ -627,7 +570,7 @@ export class RdDBaseActor extends Actor {
}
}
}
await this.computeEncTotal()
await this.computeEncTotal();
return result;
}

View File

@@ -29,7 +29,7 @@ export class ExperienceLog {
};
console.log('ExperienceLog.add', newXpLog)
const newExperienceLog = (actor.system.experiencelog ?? []).concat([newXpLog]);
await actor.update({ [`system.experiencelog`]: newExperienceLog }, { noHook: true });
await actor.update({ [`system.experiencelog`]: newExperienceLog });
}
static labelTopic(topic) {

View File

@@ -1,288 +0,0 @@
import { ACTOR_TYPES, ITEM_TYPES } from "../../constants.js"
import { Grammar } from "../../grammar.js"
import { RdDItemArme } from "../../item/arme.js"
import { CATEGORIES_COMPETENCE_COMBAT } from "../../item/base-items.js"
import { Misc } from "../../misc.js"
import { RdDTimestamp } from "../../time/rdd-timestamp.js"
import { PDFDocument } from "./pdf-lib/pdf-lib.esm.js"
const copyProperty = (actor, path) => foundry.utils.getProperty(actor, path)
// const findItem = (actor, itemType, itemName) => actor.itemTypes[itemType].find(it => Grammar.equalsInsensitive(formCompName(it.name), formCompName(itemName)))
// const findItemPos = (actor, itemType, pos) => actor.itemTypes[itemType].length <= pos ? actor.itemTypes[itemType][pos] : length
// const findProperty = (it, path) => it ? foundry.utils.getProperty(it, path) : undefined
// const findItemProperty = (actor, itemType, itemName, path) => findProperty(findItem(actor, itemType, itemName), path)
// const findItemPosProperty = (actor, itemType, pos, path) => findProperty(findItemPos(actor, itemType, pos), path)
// const findArmeProperty = (actor, pos, path) => findProperty(findItemPos(actor, ITEM_TYPES.arme, pos), path)
// const findSortProperty = (actor, pos, path) => findProperty(findItemPos(actor, ITEM_TYPES.sort, pos), path)
// const itemFormPath = (type, pos, property) => `${type}s.${pos}.${property}`
const ACTOR_TO_FORM_MAPPING = [
{ path: 'name' },
{ path: 'system.carac.taille.value' },
{ path: 'system.carac.apparence.value' },
{ path: 'system.carac.apparence.xp' },
{ path: 'system.carac.constitution.value' },
{ path: 'system.carac.constitution.xp' },
{ path: 'system.carac.force.value' },
{ path: 'system.carac.force.value' },
{ path: 'system.carac.force.xp' },
{ path: 'system.carac.agilite.value' },
{ path: 'system.carac.agilite.xp' },
{ path: 'system.carac.dexterite.value' },
{ path: 'system.carac.dexterite.xp' },
{ path: 'system.carac.vue.value' },
{ path: 'system.carac.vue.xp' },
{ path: 'system.carac.ouie.value' },
{ path: 'system.carac.ouie.xp' },
{ path: 'system.carac.odoratgout.value' },
{ path: 'system.carac.odoratgout.xp' },
{ path: 'system.carac.volonte.value' },
{ path: 'system.carac.volonte.xp' },
{ path: 'system.carac.empathie.value' },
{ path: 'system.carac.empathie.xp' },
{ path: 'system.carac.intellect.value' },
{ path: 'system.carac.intellect.xp' },
{ path: 'system.carac.reve.value' },
{ path: 'system.carac.reve.xp' },
{ path: 'system.carac.chance.value' },
{ path: 'system.carac.chance.xp' },
{ path: 'system.age' },
{ path: 'system.sexe' },
{ path: 'system.taille' },
{ path: 'system.poids' },
{ path: 'system.cheveux' },
{ path: 'system.yeux' },
{ path: 'system.beaute' },
{ path: 'system.main' },
{ path: 'system.heure' },
{ path: 'computed.hn.heure', getter: actor => (RdDTimestamp.definition(actor.system.heure)?.heure ?? 0) + 1 },
{ path: 'computed.hn.label', getter: actor => RdDTimestamp.definition(actor.system.heure)?.avecArticle },
{ path: 'system.carac.melee.value' },
{ path: 'system.carac.tir.value' },
{ path: 'system.carac.lancer.value' },
{ path: 'system.carac.derobee.value' },
{ path: 'system.sante.vie.value' },
{ path: 'system.sante.endurance.value' },
{ path: 'system.attributs.sust.value' },
{ path: 'system.attributs.sconst.value' },
{ path: 'system.attributs.encombrement.value' },
{ path: 'system.attributs.plusdom.value', getter: actor => Misc.toSignedString(actor.system.attributs.plusdom.value) },
// , getter: actor => actor.get
]
export default class ExportPdf {
static init() {
Hooks.on("getActorContextOptions", (actorDirectory, menus) => { ExportPdf.onActorDirectoryMenu(actorDirectory, menus) })
}
static onActorDirectoryMenu(actorDirectory, menus) {
menus.push({
name: 'Export PDF',
icon: '<i class="fa-regular fa-file-pdf"></i>',
condition: target => actorDirectory.id == 'actors' && ExportPdf.$isActorPersonnage(this.$getActor(target)),
callback: async target => await ExportPdf.exportActor(target)
})
}
static $getActor(target) {
const entryId = $(target).closest(".directory-item")?.data("entryId")
return game.actors.get(entryId)
}
static $isActorPersonnage(actor) {
return actor?.type == ACTOR_TYPES.personnage
}
static async exportActor(target) {
const actor = ExportPdf.$getActor(target)
if (!ExportPdf.$isActorPersonnage(actor)) {
ui.notifications.error("Pas de personnage sélectionné")
return
}
const templatePdf = '/systems/foundryvtt-reve-de-dragon/assets/feuille-personnage.pdf';
const pdfBytes = await fetch(templatePdf).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(pdfBytes)
const exporter = new ExportPdf(actor, pdfDoc)
exporter.generateFeuillePersonnage()
}
constructor(actor, pdfDoc) {
this.pdfDoc = pdfDoc
this.form = this.pdfDoc.getForm()
this.actor = actor
this.allComps = this.actor.itemTypes[ITEM_TYPES.competence]
this.comps = this.allComps
.filter(it => !it.isNiveauBase())
.sort(Misc.ascending(it => it.name))
this.compsNonArmes = this.comps.filter(it => !ExportPdf.isCompCombat(it))
this.compsArmes = this.comps.filter(it => ExportPdf.isCompCombat(it))
this.addedComps = new Set([])
}
static isCompCombat(comp) {
return CATEGORIES_COMPETENCE_COMBAT.includes(comp.system.categorie) && !Grammar.includesLowerCaseNoAccent(comp.name, "corps à corps") && !Grammar.includesLowerCaseNoAccent(comp.name, "esquive")
}
async generateFeuillePersonnage() {
this.$exportActorFields()
this.$exportCompetences()
this.$exportArchetype()
this.$exportArmes()
this.$exportSorts()
const pdfBytes = await this.pdfDoc.save();
const filename = `rdd-${this.actor.name.slugify()}.pdf`;
foundry.utils.saveDataToFile(pdfBytes, "application/pdf", filename);
}
$exportActorFields() {
ACTOR_TO_FORM_MAPPING.forEach(async (mapping) => {
const path = mapping.path
const value = mapping.getter ? mapping.getter(this.actor) : copyProperty(this.actor, path)
this.$setFormValue(path, value)
})
}
$exportCompetences() {
this.compsNonArmes
.filter(it => !this.addedComps.has(it.id))
.forEach(comp => {
const formCompName = Grammar.toLowerCaseNoAccent(comp.name.replaceAll(/(\s|-|\')/g, '_'))
this.$setFormCompetence(formCompName, comp)
})
const musique = this.compsNonArmes.filter(it => Grammar.includesLowerCaseNoAccent(it.name, 'musique'))
.sort(Misc.descending(it => it.system.niveau))
.filter(it => !this.addedComps.has(it.id))
.find(it => true)
if (musique) {
this.$setFormCompetence('musique', musique)
}
}
$exportArchetype() {
this.allComps.sort(Misc.ascending(it => it.system.niveau_archetype))
.forEach(comp => {
let formCompName = Grammar.toLowerCaseNoAccent(comp.name.replaceAll(/(\s|-|\')/g, '_'))
if (formCompName.includes('musique')) {
formCompName = 'musique'
}
this.$setFormValue(`competences.${formCompName}.niveau_archetype`, comp.system.niveau_archetype)
})
}
$setFormCompetenceArchetype(formCompName, comp, baseFormName = 'competences') {
}
$exportArmes() {
const uniques = new Set([])
const armes = this.actor.itemTypes[ITEM_TYPES.arme].map(arme => arme.getTypeAttaques()
.map(main => {
const compName = arme.getCompetenceAction(main)
const dommages = RdDItemArme.valeurMain(arme.system.dommages, main)
const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
const comp = this.compsArmes.find(it => Grammar.equalsInsensitive(it.name, compName)) ?? this.actor.findItemLike(compName, ITEM_TYPES.competence)
const unique = [comp.id, arme.name, dommages, forceRequise].join('|');
if (uniques.has(unique)) {
return undefined
}
uniques.add(unique)
return { arme: arme, comp: comp, main: main }
}))
.reduce((a, b) => a.concat(b))
.filter(it => it != undefined && !it.comp.isNiveauBase())
.sort(Misc.descending(it => it.comp.niveau))
for (let pos = 0; pos < armes.length; pos++) {
const it = armes[pos]
this.$setFormArmeCompetence(pos, it.comp, it.arme, it.main, it.main)
}
// TODO: list comps without weapons
// TODO: list other comps not in the standard list -- use an instance of ExportPdf to hold state/built list
const otherComps = this.comps.filter(it => !this.addedComps.has(it.id))
for (let pos = 0; pos < otherComps.length; pos++) {
const comp = otherComps[pos]
this.$setFormCompetence(pos, comp, 'competences')
this.$setFormValue(`competences.${pos}.name`, comp.name)
}
}
$setFormCompetence(formCompName, comp, baseFormName = 'competences') {
if (this.form.getFieldMaybe(`${baseFormName}.${formCompName}.niveau`)) {
this.addedComps.add(comp.id)
}
if (comp.system.niveau != comp.system.base) {
this.$setFormValue(`${baseFormName}.${formCompName}.niveau`, comp.system.niveau)
}
if (comp.system.xp > 0) {
this.$setFormValue(`${baseFormName}.${formCompName}.xp`, comp.system.xp)
}
if (comp.system.xp_sort > 0) {
this.$setFormValue(`${baseFormName}.${formCompName}.xp_sort`, comp.system.xp_sort)
}
if (CATEGORIES_COMPETENCE_COMBAT.includes(comp.system.categorie)) {
this.$setFormValue(`${baseFormName}.${formCompName}.init`, comp.getBaseInit())
}
}
$setFormArmeCompetence(pos, comp, arme, main) {
this.$setFormCompetence(pos, comp, 'armes')
this.$setFormValue(`armes.${pos}.name`, arme.name)
this.$setFormValue(`armes.${pos}.main`, main)
this.$setFormValue(`armes.${pos}.plusdom`, RdDItemArme.valeurMain(arme.system.dommages, main))
}
$exportSorts() {
const sorts = this.actor.itemTypes[ITEM_TYPES.sort].sort(Misc.ascending(s => ExportPdf.$orderDraconic(s) + s.name))
for (let pos = 0; pos < sorts.length; pos++) {
const sort = sorts[pos]
this.$setFormSort(pos, sort)
}
}
$setFormSort(pos, sort) {
this.$setFormValue(`sorts.${pos}.name`, sort.name)
this.$setFormValue(`sorts.${pos}.voie`, sort.system.draconic)
this.$setFormValue(`sorts.${pos}.tmr`, sort.system.caseTMRSpeciale ?? sort.system.caseTMR)
this.$setFormValue(`sorts.${pos}.diff`, sort.system.difficulte)
this.$setFormValue(`sorts.${pos}.reve`, sort.system.ptreve)
this.$setFormValue(`sorts.${pos}.bonuscase`, sort.system.bonuscase)
}
static $orderDraconic(s) {
switch (s.system.draconic.substring(0, 1)) {
case 'O': return 1
case 'H': return 2
case 'N': return 3
case 'T': return 4
}
return 5
}
$setFormValue(path, value) {
const hasField = this.form.getFieldMaybe(path)
if (hasField && value != undefined) {
const field = this.form.getTextField(path)
field.setText(value.toString())
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -62,7 +62,6 @@ export const RDD_CONFIG = {
demiReve: 'systems/foundryvtt-reve-de-dragon/assets/actions/sort.svg',
empoignade: 'systems/foundryvtt-reve-de-dragon/assets/actions/empoignade.svg',
forceWeak: 'systems/foundryvtt-reve-de-dragon/assets/actions/weak.svg',
surenc: 'systems/foundryvtt-reve-de-dragon/assets/actions/surenc.svg',
},
encaissement: {
mortel: 'mortel',

View File

@@ -20,11 +20,11 @@ export class RdDInitiative {
return "1d6" + (base >= 0 ? "+" : "") + base
}
static ajustementInitiative(caracValue, niveau, bonus = 0) {
static ajustementInitiative(caracValue, niveau, bonus) {
return niveau + Math.floor(caracValue / 2) + bonus
}
static formule(phase, carac, niveau, bonusMalus = 0) {
static formule(phase, carac, niveau, bonusMalus) {
const ajustement = RdDInitiative.ajustementInitiative(carac, niveau, bonusMalus)
return { phase, ajustement }
}

View File

@@ -1,8 +1,7 @@
import { Grammar } from "./grammar.js";
import { RdDInitiative } from "./initiative.mjs";
import { RdDItem } from "./item.js";
import { CATEGORIES_COMPETENCE_COMBAT, CATEGORIES_COMPETENCES, SANS_COMPETENCE } from "./item/base-items.js";
import { CATEGORIES_COMPETENCES, SANS_COMPETENCE } from "./item/base-items.js";
import { Misc } from "./misc.js";
const competenceTroncs = [["Esquive", "Dague", "Corps à corps"],
@@ -47,30 +46,6 @@ export class RdDItemCompetence extends RdDItem {
static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" }
isNiveauBase() {
return this.system.niveau == this.system.base && this.system.xp == 0
}
getBaseInit() {
const carac = this.getInitCarac()
if (carac == undefined) {
return undefined
}
return RdDInitiative.ajustementInitiative(carac.value, this.system.niveau)
}
getInitCarac() {
if (!this.actor) {
return undefined
}
switch (this.system.categorie) {
case CATEGORIES_COMPETENCES.melee.key: return this.actor.system.carac.melee
case CATEGORIES_COMPETENCES.tir.key: return this.actor.system.carac.tir
case CATEGORIES_COMPETENCES.lancer.key: return this.actor.system.carac.lancer
case CATEGORIES_COMPETENCES.draconic.key: return this.actor.system.carac.reve
}
return undefined
}
/* -------------------------------------------- */
static getLabelCategorie(category) {
return CATEGORIES_COMPETENCES[category].label;

View File

@@ -260,7 +260,7 @@ export class RdDItemSheetV1 extends foundry.appv1.sheets.ItemSheet {
/* -------------------------------------------- */
/** @override */
async _updateObject(event, formData) {
_updateObject(event, formData) {
switch (this.item.type) {
case ITEM_TYPES.sort:
formData['system.bonuscase'] = RdDItemSort.bonuscasesToString(RdDItemSheetV1._listCaseTmr(
@@ -273,7 +273,8 @@ export class RdDItemSheetV1 extends foundry.appv1.sheets.ItemSheet {
formData['system.niveau'] = formData['system.niveau'] ?? formData['system.base']
break
}
return await super._updateObject(event, formData)
return this.item.update(formData)
}
/* -------------------------------------------- */

View File

@@ -62,7 +62,7 @@ export class RdDItemSort extends Item {
static diffReve(sort) { return RdDItemSort.toVar((sort.system.difficulte.match(/\-?(\d)+/) ? 'R' : 'R ') + sort.system.difficulte) }
static coutReve(sort) { return RdDItemSort.toVar((Number.isInteger(sort.system.ptreve || sort.system.ptreve.match(/(\d)+\+?/)) ? 'r' : 'r ') + sort.system.ptreve) }
static coutReve(sort) { return RdDItemSort.toVar((sort.system.ptreve.match(/(\d)+\+?/) ? 'r' : 'r ') + sort.system.ptreve) }
static getDraconicsSort(competencesDraconic, sort) {
// se baser sur la voie du sort?
switch (Grammar.toLowerCaseNoAccent(sort.name)) {

View File

@@ -45,16 +45,6 @@ export class RdDItemArme extends RdDItem {
isParade() { return this.system.resistance > 0 && this.system.categorie_parade }
isBouclier() { return RdDItemArme.getCategorieParade(this).includes('bouclier') }
getCompetenceAction(main) {
switch (main) {
case ATTAQUE_TYPE.UNE_MAIN: return this.competence1Mains()
case ATTAQUE_TYPE.DEUX_MAINS: return this.competence2Mains()
case ATTAQUE_TYPE.LANCER: return this.system.lancer
case ATTAQUE_TYPE.TIR: return this.system.tir
default: return this.system.competence
}
}
/* -------------------------------------------- */
static valeurMain(valeurs, main) {
valeurs = valeurs?.toString() ?? ""
@@ -88,26 +78,17 @@ export class RdDItemArme extends RdDItem {
return arme.name
case ITEM_TYPES.arme:
switch (maniement) {
case ATTAQUE_TYPE.COMPETENCE: return arme.system.competence
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
case ATTAQUE_TYPE.LANCER: case 'lancer': return arme.system.lancer;
case ATTAQUE_TYPE.CORPS_A_CORPS: return CORPS_A_CORPS
}
}
return undefined
}
getTypeAttaques() {
return [
...(this.system.unemain && this.system.competence && this.system.resistance > 0) ? [ATTAQUE_TYPE.UNE_MAIN] : [],
...(this.system.deuxmains && this.system.competence && this.system.resistance > 0) ? [ATTAQUE_TYPE.DEUX_MAINS] : [],
...(this.system.lancer && this.system.resistance > 0) ? [ATTAQUE_TYPE.LANCER] : [],
...(this.system.tir) ? [ATTAQUE_TYPE.TIR] : [],
]
}
static computeNiveauArmes(armes, competences) {
for (const arme of armes) {
arme.system.niveau = RdDItemArme.niveauCompetenceArme(arme, competences);

View File

@@ -14,28 +14,27 @@ export const SANS_COMPETENCE = {
description: "",
descriptionmj: "",
defaut_carac: "",
},
},
img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp"
}
}
export const CATEGORIES_COMPETENCES = {
generale: { key: 'generale', base: -4, label: "Générales" },
particuliere: { key: 'particuliere', base: -8, label: "Particulières" },
specialisee: { key: 'specialisee', base: -11, label: "Spécialisées" },
connaissance: { key: 'connaissance', base: -11, label: "Connaissances" },
draconic: { key: 'draconic', base: -11, label: "Draconic" },
melee: { key: 'melee', base: -6, label: "Mêlée" },
tir: { key: 'tir', base: -8, label: "Tir" },
lancer: { key: 'lancer', base: -8, label: "Lancer" }
"generale": { base: -4, label: "Générales" },
"particuliere": { base: -8, label: "Particulières" },
"specialisee": { base: -11, label: "Spécialisées" },
"connaissance": { base: -11, label: "Connaissances" },
"draconic": { base: -11, label: "Draconic" },
"melee": { base: -6, label: "Mêlée" },
"tir": { base: -8, label: "Tir" },
"lancer": { base: -8, label: "Lancer" }
}
export const CATEGORIES_COMPETENCE_COMBAT = [CATEGORIES_COMPETENCES.melee, CATEGORIES_COMPETENCES.tir, CATEGORIES_COMPETENCES.lancer].map(it => it.key)
export const CATEGORIES_COMPETENCES_CREATURES = {
generale: { key: 'generale', base: 0, label: "Générale" },
naturelle: { key: 'naturelle', base: 0, label: "Arme naturelle" },
melee: { key: 'melee', base: 0, label: "Mêlée" },
parade: { key: 'parade', base: 0, label: "Parade" },
tir: { key: 'tir', base: 0, label: "Tir" },
lancer: { key: 'lancer', base: 0, label: "Lancer" },
possession: { key: 'possession', base: 0, label: "Possession" },
"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

@@ -12,13 +12,12 @@ const _SPACEHOLDER = { placeholder: true }
const _VENDRE = {
code: 'item-vendre', label: 'Vendre ou donner', icon: it => 'fa-solid fa-comments-dollar',
filter: it => Misc.toInt(it.system.quantite) > 0 || it.parent?.type == ACTOR_TYPES.commerce,
filter: it => Misc.toInt(it.system.quantite) > 0,
action: (item, actor) => item.proposerVente()
}
const _ACHETER = {
code: 'item-acheter', label: 'Acheter', icon: it => 'fa-regular fa-coins',
filter: it => it.parent?.type == ACTOR_TYPES.commerce,
allowLimited: true,
filter: it => Misc.toInt(it.system.quantite) > 0 && it.parent?.type == ACTOR_TYPES.commerce,
action: (item, actor) => actor.vente(item)
}
const _MONTRER = {

View File

@@ -74,7 +74,6 @@ export class RdDBonus {
dmgForceInsuffisante: Math.min(0, actor.getForce() - (attaque.forceRequise ?? 0)),
dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0
}
dmg.isEmpoignade = dmg.mortalite == RDD_CONFIG.encaissement.empoignade
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante + dmg.dmgDiffLibre
return dmg
}

View File

@@ -58,9 +58,9 @@ export class RdDCombatManager extends Combat {
static getCombatant(actorId, tokenId) {
return game.combat?.combatants.find(it => it.actor.id == actorId &&
return game.combat.combatants.find(it => it.actor.id == actorId &&
it.token.id == tokenId
)
);
}
/* -------------------------------------------- */
async nextRound() {
@@ -408,6 +408,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static registerChatCallbacks(html) {
for (let button of [
'.button-defense',
'.button-parade',
'.button-esquive',
'.button-encaisser',
@@ -479,6 +480,8 @@ export class RdDCombat {
switch (button) {
case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '.button-defense': return this.defenseV2(attackerRoll);
case '.button-parade': return this.parade(attackerRoll, armeParadeId);
case '.button-esquive': return this.esquive(attackerRoll, compId, competence);
case '.button-encaisser': return this.encaisser(attackerRoll, defenderRoll);
@@ -728,24 +731,24 @@ export class RdDCombat {
async _chatMessageDefenseV2(paramDemandeDefense) {
const attackerRoll = paramDemandeDefense.attackerRoll;
RollBasicParts.loadSurprises(attackerRoll)
attackerRoll.passeArme = attackerRoll.passeArme ?? foundry.utils.randomID(16)
attackerRoll.dmg = RdDBonus.dmgRollV2(attackerRoll, attackerRoll.current.attaque)
const defenseData = RollBasicParts.prepareDefense(attackerRoll)
const attaque = RollDialog.saveParts(attackerRoll)
const defense = {
attackerRoll: attaque,
ids: RollBasicParts.reverseIds(attaque),
passeArme: attaque.passeArme ?? foundry.utils.randomID(16)
}
const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attacker?.getAlias(),
whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', defenseData)
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', attackerRoll)
});
// flag pour garder les jets d'attaque/defense
ChatUtility.setMessageData(choixDefense, 'demande-defense', true)
ChatUtility.setMessageData(choixDefense, 'rollData', {
ids: defenseData.ids,
attackerRoll: RollDialog.saveParts(attackerRoll),
passeArme: defenseData.passeArme
})
ChatUtility.setMessageData(choixDefense, 'rollData', defense)
}
/* -------------------------------------------- */
@@ -1055,7 +1058,7 @@ export class RdDCombat {
dialog.render(true);
}
async defenseV2(attackerRoll, callbacks = []) {
async defenseV2(attackerRoll) {
// this._prepareParade(attackerRoll, arme, competence);
RollDialog.loadRollData(attackerRoll)
await this.doRollDefense({
@@ -1068,7 +1071,7 @@ export class RdDCombat {
type: { allowed: [ROLL_TYPE_DEFENSE], current: ROLL_TYPE_DEFENSE },
attackerRoll: attackerRoll,
passeArme: attackerRoll.passeArme,
}, callbacks)
})
}
async doRollDefense(rollData, callbacks = []) {

View File

@@ -22,38 +22,29 @@ export class RdDEmpoignade {
static async ajustementEmpoignade(attacker, defender, adjust = 1) {
const empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const empId = empoignade?.system.empoignadeid ?? foundry.utils.randomID(16)
if (empoignade) {
if (empoignade.system.empoigneurid == defender.id) {
adjust = - adjust
}
empoignade.system.pointsemp += adjust
await RdDEmpoignade.$updateEtatEmpoignade(empoignade, attacker, defender)
RdDEmpoignade.$updateEtatEmpoignade(empoignade)
}
else {
await RdDEmpoignade.$createEtatEmpoignade({
RdDEmpoignade.$createEtatEmpoignade( {
name: `Empoignade de ${attacker.name} sur ${defender.name}`,
type: ITEM_TYPES.empoignade,
system: {
description: "",
empoignadeid: empId,
empoignadeid: foundry.utils.randomID(16),
empoigneurid: attacker.id,
empoigneid: defender.id,
pointsemp: adjust,
empoigneurname: attacker.name,
empoignename: defender.name
}
}, attacker, defender)
}
)
}
const result = RdDEmpoignade.getEmpoignadeById(defender, empId);
const defGrappled = result.system.pointsemp == (result.system.empoigneid == defender.id ? 2 : -2)
const attGrappled = result.system.pointsemp == (result.system.empoigneurid == attacker.id ? -2 : 2)
const grappling = Math.abs(result.system.pointsemp) > 0
await defender.setEffect(STATUSES.StatusGrappling, grappling && !defGrappled)
await attacker.setEffect(STATUSES.StatusGrappling, grappling && !attGrappled)
await defender.setEffect(STATUSES.StatusGrappled, defGrappled)
await attacker.setEffect(STATUSES.StatusGrappled, attGrappled)
return result
}
@@ -371,21 +362,18 @@ export class RdDEmpoignade {
}
/* -------------------------------------------- */
static async $updateEtatEmpoignade(empoignade, attacker, defender) {
static async $updateEtatEmpoignade(empoignade) {
console.log("UPDATE Empoignade", empoignade)
const belligerants = [
attacker ?? game.actors.get(empoignade.system.empoigneurid),
defender ?? game.actors.get(empoignade.system.empoigneid)]
await Promise.all(
belligerants.map(async belligerant => {
const emp = RdDEmpoignade.getEmpoignadeById(belligerant, empoignade.system.empoignadeid)
return await belligerant.updateEmbeddedDocuments('Item', [{
_id: emp._id,
"system.pointsemp": empoignade.system.pointsemp,
"system.ausol": empoignade.system.ausol
}])
}))
let defender = game.actors.get(empoignade.system.empoigneid)
let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
let update = { _id: emp._id, "system.pointsemp": empoignade.system.pointsemp, "system.ausol": empoignade.system.ausol }
await defender.updateEmbeddedDocuments('Item', [update])
let attacker = game.actors.get(empoignade.system.empoigneurid)
emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
update = { _id: emp._id, "system.pointsemp": empoignade.system.pointsemp, "system.ausol": empoignade.system.ausol }
await attacker.updateEmbeddedDocuments('Item', [update])
}
/* -------------------------------------------- */

View File

@@ -89,7 +89,6 @@ import { Migrations } from './migrations.js'
import RollDialog from "./roll/roll-dialog.mjs"
import ChatRollResult from "./roll/chat-roll-result.mjs"
import ExportPdf from "./actor/export-pdf/export-pdf.mjs"
/**
* RdD system
@@ -297,7 +296,6 @@ export class SystemReveDeDragon {
RdDPossession.init()
TMRRencontres.init()
ExportScriptarium.init()
ExportPdf.init()
RollDialog.init()
ChatRollResult.init()
}

View File

@@ -334,7 +334,7 @@ export class RdDRoll extends Dialog {
// Mise à jour valeurs
this.html.find(".dialog-roll-title").text(this._getTitle(rollData));
this.html.find("input.check-mortalite").prop('checked', rollData.dmg.mortalite == RDD_CONFIG.encaissement.nonmortel);
this.html.find("label.dmg-arme-actor").text(rollData.dmg.isEmpoignade ? EMPOIGNADE : Misc.toSignedString(rollData.dmg.total));
this.html.find("label.dmg-arme-actor").text(rollData.dmg.mortalite == EMPOIGNADE ? EMPOIGNADE : Misc.toSignedString(rollData.dmg.total));
this.html.find("label.arme-mortalite").text(rollData.dmg.mortalite);
this.html.find("div.placeholder-ajustements").empty().append(adjustements);
this.html.find("div.placeholder-resolution").empty().append(resolutionTable)

View File

@@ -5,6 +5,7 @@ import { RdDCombat } from "../rdd-combat.js"
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
import { RdDResolutionTable } from "../rdd-resolution-table.js"
import { RDD_CONFIG, renderTemplate } from "../constants.js"
import { EMPOIGNADE } from "../item/arme.js"
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js"
import { RollTypeCuisine } from "./roll-type-cuisine.mjs"
import { RollTypeMeditation } from "./roll-type-meditation.mjs"
@@ -67,7 +68,7 @@ export default class ChatRollResult {
isShowEncaissement(roll) {
switch (roll.type.current) {
case ROLL_TYPE_DEFENSE:
return roll.rolled.isEchec
return roll.rolled.isEchec && roll.attackerRoll?.dmg.mortalite != EMPOIGNADE
}
return false
}
@@ -132,8 +133,8 @@ export default class ChatRollResult {
async chatListeners(html) {
$(html).on("click", '.appel-chance', event => this.onClickAppelChance(event))
$(html).on("click", '.appel-destinee', event => this.onClickAppelDestinee(event))
$(html).on("click", '.button-defense', event => this.onClickDefense(event))
$(html).on("click", '.encaissement', event => this.onClickEncaissement(event))
$(html).on("click", '.point-empoignade', event => this.onClickMarquerEmpoignade(event))
$(html).on("click", '.resister-recul', event => this.onClickRecul(event))
$(html).on("click", '.choix-particuliere', event => this.onClickChoixParticuliere(event))
$(html).on("click", '.faire-gouter', event => this.onClickFaireGouter(event))
@@ -220,41 +221,27 @@ export default class ChatRollResult {
})
}
async onClickDefense(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = this.loadChatMessageRoll(chatMessage)
const attackerRoll = savedRoll.attackerRoll
this.getCombat(attackerRoll)?.defenseV2(attackerRoll,
[roll => { ChatUtility.removeChatMessageId(chatMessage.id) }]
)
}
async onClickEncaissement(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const isMessageDemande = ChatUtility.getMessageData(chatMessage, 'demande-defense')
const savedRoll = this.loadChatMessageRoll(chatMessage)
const attaque = savedRoll.attackerRoll
const defender = game.actors.get(savedRoll.ids.actorId)
const attacker = game.actors.get(savedRoll.ids.opponentId)
const defenderToken = savedRoll.ids.actorTokenId ? canvas.tokens.get(savedRoll.ids.actorTokenId) : undefined
const attackerToken = savedRoll.ids.opponentTokenId ? canvas.tokens.get(savedRoll.ids.opponentTokenId) : undefined
switch (attaque.dmg.mortalite) {
case RDD_CONFIG.encaissement.empoignade:
savedRoll.done = savedRoll.done ?? {}
savedRoll.done.empoignade = await RdDEmpoignade.ajustementEmpoignade(attackerToken.actor, defenderToken.actor)
break
case RDD_CONFIG.encaissement.entiteincarnee:
case RDD_CONFIG.encaissement.nonmortel:
case RDD_CONFIG.encaissement.mortel:
const defender = defenderToken?.actor ?? game.actors.get(savedRoll.ids.actorId)
const attacker = attackerToken?.actor ?? game.actors.get(savedRoll.ids.opponentId)
await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
break
}
if (isMessageDemande) {
ChatUtility.removeChatMessageId(chatMessage.id)
} else {
savedRoll.done.encaissement = true
await this.updateChatMessage(chatMessage, savedRoll)
}
await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
savedRoll.done.encaissement = true
await this.updateChatMessage(chatMessage, savedRoll)
}
async onClickMarquerEmpoignade(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = this.loadChatMessageRoll(chatMessage)
const attaque = savedRoll.attackerRoll
const attackerToken = attaque.ids.actorTokenId ? canvas.tokens.get(attaque.ids.actorTokenId) : undefined
const defenderToken = attaque.ids.opponentTokenId ? canvas.tokens.get(attaque.ids.opponentTokenId) : undefined
RdDEmpoignade.ajustementEmpoignade(attackerToken.actor, defenderToken.actor)
}
async onClickRecul(event) {

View File

@@ -54,27 +54,13 @@ export class RollBasicParts {
}
}
static prepareDefense(attackerRoll) {
if (!attackerRoll.passeArme) {
attackerRoll.passeArme = foundry.utils.randomID(16);
}
return {
ids: RollBasicParts.reverseIds(attackerRoll),
active: attackerRoll.opponent,
opponent: attackerRoll.active,
attackerRoll: attackerRoll,
passeArme: attackerRoll.passeArme,
show: { encaissement: true }
}
}
static reverseIds(rollData) {
return {
sceneId: rollData.ids.sceneId,
actorId: rollData.ids.opponentId,
actorTokenId: rollData.ids.opponentTokenId,
opponentId: rollData.ids.actorId,
opponentTokenId: rollData.ids.actorTokenId
opponentTokenId: rollData.actorTokenId
}
}

View File

@@ -124,7 +124,7 @@ export class RollDialogAdapter {
const attaque = rollData.current.attaque;
const choix = []
const isEmpoignade = attaque.dmg.isEmpoignade
const isEmpoignade = attaque.dmg.mortalite == RDD_CONFIG.encaissement.empoignade
const isCharge = attaque.tactique == 'charge'
/* TODO: cas de créatures faisant des lancers, Glou, Glipzouk */
const isMeleeDiffNegative = (attaque.comp.type == ITEM_TYPES.competencecreature || rollData.current.carac.key == CARACS.MELEE)

View File

@@ -45,7 +45,6 @@ import { RollPartCuisine } from "./roll-part-cuisine.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "../settings/options-avancees.js";
import { ActorImpacts } from "../technical/actor-impacts.mjs";
import { RollPartEmpoignade } from "./roll-part-empoignade.mjs";
import { RollPartEmpoignadeTaille } from "./roll-part-empoignade-taille.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
@@ -87,7 +86,6 @@ const ROLL_PARTS = [
new RollPartConditions(),
new RollPartEthylisme(),
new RollPartMalusArmure(),
new RollPartEmpoignadeTaille(),
new RollPartEmpoignade(),
new RollPartEncTotal(),
new RollPartSurEnc(),

View File

@@ -1,4 +1,4 @@
import { ITEM_TYPES, RDD_CONFIG } from "../constants.js"
import { ITEM_TYPES } from "../constants.js"
import { ATTAQUE_TYPE, RdDItemArme } from "../item/arme.js"
import { CARACS } from "../rdd-carac.js"
import { DIFF, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
@@ -15,8 +15,6 @@ export class RollPartDefense extends RollPartSelect {
get code() { return PART_DEFENSE }
get section() { return ROLLDIALOG_SECTION.CHOIX }
isValid(rollData) { return rollData.attackerRoll != undefined }
visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_DEFENSE) }
static getDiffAttaque(attackerRoll) {
@@ -29,22 +27,13 @@ export class RollPartDefense extends RollPartSelect {
const attackerRoll = rollData.attackerRoll
const defenseur = rollData.active.actor
refs.isDistance = [ATTAQUE_TYPE.TIR, ATTAQUE_TYPE.LANCER].find(it => it == attackerRoll?.main)
const isEmpoignade = attackerRoll.dmg.isEmpoignade
const isEmpoignadeEnCours = isEmpoignade && defenseur.itemTypes[ITEM_TYPES.empoignade].find(it =>
[it.system.empoigneurid, it.system.empoigneid].includes(rollData.ids.opponentId) &&
it.system.pointsemp != 0)
const esquives = refs.isDistance == ATTAQUE_TYPE.TIR ? [] : defenseur.getCompetencesEsquive()
.map(it => RollPartDefense.$extractEsquive(it, defenseur))
const esquives = (refs.isDistance == ATTAQUE_TYPE.TIR || isEmpoignadeEnCours)
? []
: defenseur.getCompetencesEsquive()
const parades = isEmpoignade
? [RdDItemArme.empoignade(defenseur)]
: defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier()))
const parades = defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier()))
.map(it => RollPartDefense.$extractParade(it, attackerRoll?.arme, defenseur))
refs.defenses = [
...esquives.map(it => RollPartDefense.$extractEsquive(it, defenseur)),
...parades.map(it => RollPartDefense.$extractParade(it, attackerRoll?.arme, defenseur))
]
refs.defenses = [...esquives, ...parades].filter(it => it != undefined)
this.$selectDefense(rollData)
}

View File

@@ -1,40 +0,0 @@
import { RDD_CONFIG } from "../constants.js"
import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
import { RdDEmpoignade } from "../rdd-empoignade.js"
import { COMBAT_ROLL_TYPES } from "./roll-constants.mjs"
import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
const EMPOIGNADE_TAILLE = "empoignade-taille"
export class RollPartEmpoignadeTaille extends RollPartCheckbox {
get code() { return EMPOIGNADE_TAILLE }
isValid(rollData) {
return RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
}
visible(rollData) {
return COMBAT_ROLL_TYPES.includes(rollData.type.current) &&
RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor) &&
this.getTailleDiff(rollData) != 0
}
getTailleDiff(rollData) {
const taille = rollData.active.actor.getTaille()
const tailleOpponent = rollData.opponent.actor.getTaille()
const diff = taille - tailleOpponent
const diffTailleAbs = Math.max(0, Math.abs(diff) - 1)
const signDiff = Math.sign(diff)
return signDiff * diffTailleAbs
}
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.empoignade}">` }
getCheckboxLabel(rollData) {
return `Taille ${rollData.active.actor.getTaille()} vs ${rollData.opponent.actor.getTaille()} `
}
getCheckboxValue(rollData) {
return this.getTailleDiff(rollData)
}
}

View File

@@ -23,6 +23,7 @@ export class RollPartEmpoignade extends RollPartCheckbox {
!RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId) &&
!RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor)
}
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.empoignade}">` }
getCheckboxLabel(rollData) { return "vs. empoigneur" }

View File

@@ -1,4 +1,3 @@
import { RDD_CONFIG } from "../constants.js"
import { RdDItemCompetence } from "../item-competence.js"
import { RdDCarac } from "../rdd-carac.js"
import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
@@ -26,7 +25,7 @@ export class RollPartEncTotal extends RollPartCheckbox {
})
}
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.surenc}">` }
getCheckboxIcon(rollData) { return '<i class="fa-solid fa-weight-hanging"></i>' }
getCheckboxLabel(rollData) { return "Enc. total" }
getCheckboxValue(rollData) { return - rollData.active.actor.getEncTotal() }
}

View File

@@ -74,7 +74,7 @@ export class RollPartMeditation extends RollPartSelect {
getAjustements(rollData) {
const malus = this.getMalusEchecs(rollData)
const malusEchecs = malus == 0 ? [] : [{ label: "Méditation", value: malus }]
const malusEchecs = malusEchecs == 0 ? [] : [{ label: "Méditation", value: malus }]
const malusConditions = { label: "Conditions", value: this.getMalusConditions(rollData) }
return [malusConditions, ...malusEchecs]
}

View File

@@ -1,4 +1,3 @@
import { RDD_CONFIG } from "../constants.js"
import { RdDCarac } from "../rdd-carac.js"
import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
@@ -11,7 +10,7 @@ export class RollPartSurEnc extends RollPartCheckbox {
visible(rollData) {
return RdDCarac.isActionPhysique(rollData.current.carac.key) && rollData.active.actor.isSurenc()
}
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.surenc}">` }
getCheckboxIcon(rollData) { return '<i class="fa-solid fa-weight-hanging"></i>' }
getCheckboxLabel(rollData) { return "Sur-enc." }
getCheckboxValue(rollData) { return rollData.active.actor.computeMalusSurEncombrement() }
}

View File

@@ -14,17 +14,16 @@ export const STATUSES = {
StatusBleeding: 'bleeding',
StatusDead: 'dead',
StatusDemiReve: 'demi-reve',
StatusSurEnc: 'sur-encombrement',
StatusForceWeak: 'force insuffisante',
}
export const forceWeakStatusEffect = { rdd: true, id: STATUSES.StatusForceWeak, name: 'EFFECT.StatusForceWeak', img: RDD_CONFIG.icons.forceWeak };
export const surEncEffect = { rdd: true, id: STATUSES.StatusSurEnc, name: 'EFFECT.StatusSurEnc', img: RDD_CONFIG.icons.surenc };
export const demiReveStatusEffect = { rdd: true, id: STATUSES.StatusDemiReve, name: 'EFFECT.StatusDemiReve', img: RDD_CONFIG.icons.demiReve };
export const demiReveStatusEffect = {
rdd: true, id: STATUSES.StatusDemiReve, name: 'EFFECT.StatusDemiReve', img: RDD_CONFIG.icons.demiReve
};
const rddStatusEffects = [
{ rdd: true, id: STATUSES.StatusGrappling, name: 'EFFECT.StatusGrappling', img: RDD_CONFIG.icons.empoignade },
{ rdd: true, id: STATUSES.StatusGrappled, tint: '#d5633d', name: 'EFFECT.StatusGrappled', img: RDD_CONFIG.icons.empoignade },
{ rdd: true, id: STATUSES.StatusGrappling, tint: '#33cc33', name: 'EFFECT.StatusGrappling', img: RDD_CONFIG.icons.empoignade },
{ rdd: true, id: STATUSES.StatusGrappled, tint: '#ff9900', name: 'EFFECT.StatusGrappled', img: RDD_CONFIG.icons.empoignade },
{ rdd: true, id: STATUSES.StatusRestrained, name: 'EFFECT.StatusRestrained', img: 'icons/svg/net.svg' },
{ rdd: true, id: STATUSES.StatusStunned, name: 'EFFECT.StatusStunned', img: 'icons/svg/stoned.svg', "duration.rounds": 1 },
@@ -37,8 +36,7 @@ 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,
forceWeakStatusEffect,
surEncEffect,
forceWeakStatusEffect
];
const statusDemiSurprise = new Set([STATUSES.StatusStunned, STATUSES.StatusProne, STATUSES.StatusRestrained, STATUSES.StatusForceWeak])
@@ -47,9 +45,8 @@ const statusSurpriseTotale = new Set([STATUSES.StatusUnconscious, STATUSES.Statu
export class StatusEffects extends FormApplication {
static onReady() {
const rddEffectIds = rddStatusEffects.map(it => it.id)
const rddEffectIds = rddStatusEffects.map(it => it.id);
rddStatusEffects.forEach(it => {
it.name = game.i18n.localize(it.name)
it.statuses = new Set([it.id])
})
const defaultStatusEffectIds = CONFIG.statusEffects.map(it => it.id);
@@ -138,7 +135,7 @@ export class StatusEffects extends FormApplication {
}
static prepareActiveEffect(effectId) {
let status = rddStatusEffects.find(it => it.statuses? it.statuses.has(effectId) : it.id == effectId)
let status = rddStatusEffects.find(it => it.id == effectId)
if (status) {
status = foundry.utils.duplicate(status)
status.statuses = new Set([effectId])

View File

@@ -27,7 +27,6 @@
"dependencies": {
"gulp": "^5.0.0",
"gulp-less": "^5.0.0",
"pdf-lib": "^1.17.1",
"rollup-plugin-visualizer": "^5.12.0"
},
"repository": {

View File

@@ -16,7 +16,7 @@ background:
alphaThreshold: 0
foreground: null
foregroundElevation: 4
thumb: systems/foundryvtt-reve-de-dragon/pic/YSpVuLeMCX9tAmgn-thumb.webp
thumb: systems/foundryvtt-reve-de-dragon/assets/scenes/YSpVuLeMCX9tAmgn-thumb.webp
width: 1920
height: 1080
padding: 0.25

View File

@@ -7,7 +7,7 @@ initial:
x: null
'y': null
scale: 0.5
thumb: systems/foundryvtt-reve-de-dragon/pic/9fmf9lcb3L9XO3bJ-thumb.png
thumb: systems/foundryvtt-reve-de-dragon/assets/scenes/9fmf9lcb3L9XO3bJ-thumb.png
width: 3521
height: 2492
padding: 0.25
@@ -38,7 +38,7 @@ regions: []
ownership:
default: 0
background:
src: systems/foundryvtt-reve-de-dragon/pic/ecran_rdd.webp
src: systems/foundryvtt-reve-de-dragon/assets/ecran_rdd.webp
offsetX: 0
offsetY: 0
anchorX: 0

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Andrew Dillon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -4,7 +4,7 @@
<li class="caracteristique flexrow list-item {{#if carac.isLevelUp}}xp-level-up{{/if}}" data-attribute="{{key}}">
{{#if (eq key 'taille')}}
<span class="carac-label" name="system.carac.{{key}}.label">{{carac.label}}</span>
<input class="carac-value" type="number" name="carac.{{key}}" value="{{carac.value}}" data-dtype="number" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input class="carac-value" type="number" name="system.carac.{{key}}.value" value="{{carac.value}}" data-dtype="number" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<label class="carac-xp"/>
{{else}}
{{#if (actor-default @root.type 'carac' key 'derivee')}}
@@ -23,7 +23,7 @@
{{else}}
<span class="carac-label" name="system.carac.{{key}}.label"><a class="roll-carac" data-carac-name={{key}}>{{carac.label}}</a></span>
{{/if}}
<input class="carac-value" type="number" name="carac.{{key}}" value="{{carac.value}}" data-dtype="number" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input class="carac-value" type="number" name="system.carac.{{key}}.value" value="{{carac.value}}" data-dtype="number" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<span class="carac-xp">
<input class="carac-xp" type="number" name="system.carac.{{key}}.xp" value="{{carac.xp}}" data-dtype="number"
data-tooltip="Vous devez acquérir {{carac.xpNext}} points d'Experience pour augmenter votre {{carac.label}}"

View File

@@ -40,7 +40,6 @@
</div>
<hr>
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.hbs"}}
{{log @root.options}}
{{#unless system.illimite}}
{{#if @root.options.isObserver}}
<hr>

View File

@@ -1,24 +1,25 @@
{{log this}}
<div class="roll-chat" data-passearme="{{passeArme}}">
<div class="chat-img">
<img src="{{active.img}}" data-tooltip="{{active.name}}" />
<img src="{{opponent.img}}" data-tooltip="{{opponent.name}}" />
</div>
<div class="chat-header">
<h4>Défense de {{active.name}}</h4>
<h4>Défense de {{opponent.name}}</h4>
</div>
<div class="chat-resume">
{{#if (eq active.surprise.key 'totale')}}
<span><strong>{{active.name}}</strong> est totalement surpris</span>
{{#if (eq opponent.surprise.key 'totale')}}
<span><strong>{{opponent.name}}</strong> est totalement surpris</span>
{{else}}
<span><strong>{{active.name}}</strong> doit se défendre
{{~#if (eq active.surprise.key 'demi')}} avec une significative {{/if}} d'une attaque
{{~#if attackerRoll.particuliere}} <strong>particulière en
{{~#if (eq attackerRoll.particuliere 'finesse')}} finesse
<span><strong>{{opponent.name}}</strong> doit se défendre
{{~#if (eq opponent.surprise.key 'demi')}} avec une significative {{/if}} d'une attaque
{{~#if particuliere}} <strong>particulière en
{{~#if (eq particuliere 'finesse')}} finesse
{{else if (eq particuliere 'force')}} force
{{else if (eq particuliere 'rapidite')}} rapidité
{{/if~}}</strong>
{{/if}} de {{opponent.name}} ({{attackerRoll.current.attaque.label}}):
{{/if}} de {{active.name}} ({{current.attaque.label}}):
</span>
{{/if}}
</div>
@@ -27,23 +28,35 @@
</div>
<div class="chat-actions">
{{#unless (eq active.surprise.key 'totale')}}
{{#unless (eq opponent.surprise.key 'totale')}}
<a class='chat-card-button button-defense'
data-attackerId='{{ids.opponentId}}'
data-attackerTokenId='{{ids.opponentTokenId}}'
data-defenderId='{{ids.actorId}}'
data-defenderTokenId='{{ids.actorTokenId}}'
data-attackerId='{{ids.actorId}}'
data-attackerTokenId='{{ids.actorTokenId}}'
data-defenderTokenId='{{ids.opponentTokenId}}'
>
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/defense.svg"/>
Se défendre
{{#if (or (eq attackerRoll.attaqueCategorie 'tir') (eq attackerRoll.attaqueCategorie 'lancer'))}}
{{#if (or (eq attaqueCategorie 'tir') (eq attaqueCategorie 'lancer'))}}
(difficulté à déterminer)
{{else}}
à {{attackerRoll.current.diff.value}}
à {{current.diff.value}}
{{/if}}
</a>
{{/unless}}
{{> 'partial-encaissement'}}
{{#if (eq dmg.mortalite 'empoignade')}}
<a class='chat-card-button point-empoignade' data-tooltip="Subir un point d'empoignade">
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/empoignade.svg"/>
Marquer le point d'empoignade
</a>
{{else}}
<a class='chat-card-button encaissement'
data-tooltip="Encaisser à {{plusMoins dmg.total}} {{#if (eq dmg.mortalite 'non-mortel')~}}(non-mortel){{/if}}"
>
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/encaisser.svg"/>
Encaisser à {{plusMoins dmg.total}}
{{#if (eq dmg.mortalite 'non-mortel')~}}(non-mortel){{/if}}
</a>
{{/if}}
</div>
<div class="chat-buttons">

View File

@@ -1,29 +1,15 @@
{{log 'partial-encaissement' this}}
{{#if show.encaissement}}
{{#if done.encaissement}}
<span class='chat-card-info'>
{{#if (eq attackerRoll.dmg.mortalite 'empoignade')}}
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/empoignade.svg"/>
{{opponent.name}} a {{done.empoignade.system.pointsemp}} point d'empoignade contre {{active.name}}.
{{#if (gt done.empoignade.system.pointsemp 2)}} Si {{active.name}} ne se libère pas, il sera immobilisé à la fin du round{{/if}}
{{else}}
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/encaisser.svg"/>
{{active.name}} a encaissé
{{/if}}
</span>
<span class='chat-card-info'>
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/encaisser.svg"/>
{{active.name}} a encaissé
</span>
{{else}}
{{#if (eq attackerRoll.dmg.mortalite 'empoignade')}}
<a class='chat-card-button encaissement' data-tooltip="Marquer un point d'empoignade">
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/empoignade.svg"/>
Marquer un point d'empoignade
</a>
{{else}}
<a class='chat-card-button encaissement'
data-tooltip="Encaisser à {{plusMoins attackerRoll.dmg.total}} {{#if (eq attackerRoll.dmg.mortalite 'non-mortel')~}}(non-mortel){{/if}}"
>
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/encaisser.svg"/> Encaisser à {{plusMoins attackerRoll.dmg.total}}
{{#if (eq attackerRoll.dmg.mortalite 'non-mortel')~}}(non-mortel){{/if}}
</a>
{{/if}}
<a class='chat-card-button encaissement'
data-tooltip="Encaisser à {{plusMoins attackerRoll.dmg.total}} {{#if (eq attackerRoll.dmg.mortalite 'non-mortel')~}}(non-mortel){{/if}}"
>
<img src="systems/foundryvtt-reve-de-dragon/assets/actions/encaisser.svg"/> Encaisser à {{plusMoins attackerRoll.dmg.total}}
{{#if (eq attackerRoll.dmg.mortalite 'non-mortel')~}}(non-mortel){{/if}}
</a>
{{/if}}
{{/if}}