feature/v13-corrections #788

Merged
giteadmin merged 8 commits from feature/v13-corrections into v13 2025-12-15 22:20:26 +01:00
24 changed files with 414 additions and 291 deletions

View File

@@ -1,5 +1,19 @@
# 13.0 # 13.0
## 13.0.23 - Le marché d'Illysis
- Améliorations
- lors de la consommation suite à un achat, les jets d'appréciation/éthylisme
sont faits par l'acheteur. Du coup, c'est lui qui lance le jet d'appréciation
(pour un service ou un plat de qualité), et ses dés sont utilisés avec dice-so-nice
- les créatures ont le statut Inconscient quand leur endurance est à 0
- Fenêtre de jets V2
- les soins de blessures sont correctement appliqués
- correction d'affichage jet sans compétence/avec compétence
- correction de la prise en compte des tactiques
- correction du filtrage des défenses selon l'attaque
- lorsque le mode de visibilité des jets général est changé, les fenêtres de jets suivent ce changement
## 13.0.22 - Les reflets d'Illysis ## 13.0.22 - Les reflets d'Illysis
- Améliorations - Améliorations

View File

@@ -40,17 +40,21 @@ export class ChatVente {
static async diminuerQuantiteAchatVente(chatMessageId, quantite) { static async diminuerQuantiteAchatVente(chatMessageId, quantite) {
const chatMessage = game.messages.get(chatMessageId) const chatMessage = game.messages.get(chatMessageId)
const vente = ChatVente.getDetailVente(chatMessageId)
vente.nbLots = Math.max(0, vente.nbLots - quantite)
await chatMessage.setFlag(SYSTEM_RDD, NB_LOTS, vente.nbLots)
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.hbs', vente); const vente = ChatVente.getDetailVente(chatMessageId)
chatMessage.update({ content: html }); if (vente.nbLots <= quantite) {
chatMessage.render(true); ChatUtility.removeChatMessageId(chatMessageIdVente);
}
else {
vente.nbLots = vente.nbLots - quantite
await chatMessage.setFlag(SYSTEM_RDD, NB_LOTS, vente.nbLots)
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.hbs', vente)
await chatMessage.update({ content: html }, { render: true })
}
} }
static async displayAchatVente(vente) { static async displayAchatVente(vente) {
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.hbs', vente); const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.hbs', vente)
const chatMessage = await ChatMessage.create(RdDUtility.chatDataSetup(html)) const chatMessage = await ChatMessage.create(RdDUtility.chatDataSetup(html))
await chatMessage.setFlag(SYSTEM_RDD, NB_LOTS, vente.nbLots) await chatMessage.setFlag(SYSTEM_RDD, NB_LOTS, vente.nbLots)
await chatMessage.setFlag(SYSTEM_RDD, DETAIL_VENTE, { await chatMessage.setFlag(SYSTEM_RDD, DETAIL_VENTE, {

View File

@@ -220,7 +220,7 @@ export class RdDActor extends RdDBaseActorSang {
initiative: RdDInitiative.getRollInitiative(caracValue, niveau, ajustement) initiative: RdDInitiative.getRollInitiative(caracValue, niveau, ajustement)
}; };
attaque.isDistance = Distance.typeAttaqueDistance(attaque), attaque.isDistance = Distance.typeAttaqueDistance(attaque),
actions.push(attaque) actions.push(attaque)
} }
addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS) addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS)
@@ -1092,7 +1092,7 @@ export class RdDActor extends RdDBaseActorSang {
} }
$createSortReserve(sort) { $createSortReserve(sort) {
const ptReve = Number.isInteger(sort.system.ptreve) ? Number(sort.system.ptreve) :Number(sort.system.ptreve.match(/\d+/)) const ptReve = Number.isInteger(sort.system.ptreve) ? Number(sort.system.ptreve) : Number(sort.system.ptreve.match(/\d+/))
this.createEmbeddedDocuments("Item", this.createEmbeddedDocuments("Item",
[{ [{
type: ITEM_TYPES.sortreserve, type: ITEM_TYPES.sortreserve,
@@ -1316,14 +1316,6 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async consommerNourritureboisson(itemId, choix = { doses: 1, seForcer: false, supprimerSiZero: false }) { async consommerNourritureboisson(itemId, choix = { doses: 1, seForcer: false, supprimerSiZero: false }) {
if (!this.isOwner) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'consommerNourritureboisson', args: [itemId, choix]
})
return
}
const item = this.getItem(itemId) const item = this.getItem(itemId)
if (!item.getUtilisationCuisine()) { if (!item.getUtilisationCuisine()) {
return return
@@ -1333,26 +1325,36 @@ export class RdDActor extends RdDBaseActorSang {
return return
} }
const onManger = [ if (!this.isOwner) {
async () => await this.manger(item, choix.doses, { diminuerQuantite: false }), RdDBaseActor.remoteActorCall({
async () => await this.boire(item, choix.doses, { diminuerQuantite: false }), tokenId: this.token?.id,
async () => await item.diminuerQuantite(choix.doses, choix) actorId: this.id,
] method: 'consommerNourritureboisson', args: [itemId, choix]
})
return
}
if (await this._surmonterExotisme(item)) { if (await this._surmonterExotisme(item)) {
const appreciation = item.system.appreciation; if (item.system.qualite > 0) {
new Apprecier(this, appreciation, item.system.qualite) new Apprecier(this, item.system.appreciation, item.system.qualite).apprecier()
.apprecier(onManger) }
await this.onConsommerNourritureboisson(item, choix)
} }
else if (choix.seForcer) { else if (choix.seForcer) {
await this.jetDeMoral(MORAL.MALHEUREUX) await this.jetDeMoral(MORAL.MALHEUREUX)
await Promise.all(onManger.map(async callback => await callback())) await this.onConsommerNourritureboisson(item, choix)
} }
else { else {
ui.notifications.info(`${this.name} ne n'arrive pas à manger de ${item.name}`) ui.notifications.info(`${this.name} ne n'arrive pas à manger de ${item.name}`)
} }
} }
async onConsommerNourritureboisson(item, choix) {
await this.manger(item, choix.doses, { diminuerQuantite: false })
await this.boire(item, choix.doses, { diminuerQuantite: false })
await item.diminuerQuantite(choix.doses, choix)
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async _surmonterExotisme(item) { async _surmonterExotisme(item) {
const qualite = Math.min(item.system.qualite, 0) const qualite = Math.min(item.system.qualite, 0)
@@ -1363,7 +1365,7 @@ export class RdDActor extends RdDBaseActorSang {
const rolled = await this.doRollCaracCompetence(CARACS.VOLONTE, competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` }) const rolled = await this.doRollCaracCompetence(CARACS.VOLONTE, competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` })
return rolled.isSuccess return rolled.isSuccess
} }
return true; return true
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -1934,7 +1936,7 @@ export class RdDActor extends RdDBaseActorSang {
async getTacheBlessure(blesse, blessure) { async getTacheBlessure(blesse, blessure) {
const gravite = blessure?.system.gravite ?? 0; const gravite = blessure?.system.gravite ?? 0;
if (gravite > 0) { if (gravite > 0) {
const tache = this.itemTypes['tache'].find(it => it.system.itemId == blessure.id) const tache = this.itemTypes[ITEM_TYPES.tache].find(it => it.system.itemId == blessure.id)
?? await RdDItemBlessure.createTacheSoinBlessure(this, gravite); ?? await RdDItemBlessure.createTacheSoinBlessure(this, gravite);
await blessure?.updateTacheSoinBlessure(tache); await blessure?.updateTacheSoinBlessure(tache);
return tache return tache
@@ -1988,7 +1990,7 @@ export class RdDActor extends RdDBaseActorSang {
selected: { tache: { key: tache.id, forced: options.forced } }, selected: { tache: { key: tache.id, forced: options.forced } },
type: { allowed: [PART_TACHE], current: PART_TACHE } type: { allowed: [PART_TACHE], current: PART_TACHE }
} }
return await RollDialog.create(rollData) return await RollDialog.create(rollData, options)
} }
const compData = this.getCompetence(tache.system.competence) const compData = this.getCompetence(tache.system.competence)
@@ -2032,8 +2034,8 @@ export class RdDActor extends RdDBaseActorSang {
await this.santeIncDec("fatigue", rollData.tache.system.fatigue); await this.santeIncDec("fatigue", rollData.tache.system.fatigue);
await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-tache.hbs'); await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-tache.hbs');
if (options?.onRollAutomate) { if (options?.callbacks) {
options.onRollAutomate(rollData); await Promise.all(callbacks.map(callback => callback(rollData)))
} }
} }
@@ -2504,77 +2506,16 @@ export class RdDActor extends RdDBaseActorSang {
if (!blessure.system.premierssoins.done) { if (!blessure.system.premierssoins.done) {
const tache = await this.getTacheBlessure(blesse, blessure); const tache = await this.getTacheBlessure(blesse, blessure);
return await this.rollTache(tache.id, { return await this.rollTache(tache.id, {
onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r), callbacks: [async r => await blesse.onRollTachePremiersSoins(blessureId, r, this.id)],
title: 'Premiers soins', title: 'Premiers soins', forced: true
forced: true
}); });
} }
else if (!blessure.system.soinscomplets.done) { else if (!blessure.system.soinscomplets.done) {
const diff = blessure.system.difficulte + (blessure.system.premierssoins.bonus ?? 0); const diff = blessure.system.difficulte + (blessure.system.premierssoins.bonus ?? 0);
return await this.rollCaracCompetence(CARACS.DEXTERITE, "Chirurgie", diff, { return await this.rollCaracCompetence(CARACS.DEXTERITE, "Chirurgie", diff, {
title: "Soins complets", callbacks: [async r => await blesse.onRollSoinsComplets(blessureId, r, this.id)],
onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r), onRollDone: RollDialog.onRollDoneClose,
forced: true title: "Soins complets", forced: true
})
}
}
}
async onRollTachePremiersSoins(blessureId, rollData) {
if (!this.isOwner) {
return RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'onRollTachePremiersSoins', args: [blessureId, rollData]
})
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && !blessure.system.premierssoins.done) {
const tache = rollData.tache;
if (rollData.rolled.isETotal) {
await blessure.update({
'system.difficulte': blessure.system.difficulte - 1,
'system.premierssoins.tache': Math.max(0, tache.system.points_de_tache_courant)
})
}
else {
const bonus = tache.system.points_de_tache_courant - tache.system.points_de_tache
await blessure.update({
'system.premierssoins': {
done: (bonus >= 0),
bonus: Math.max(0, bonus),
tache: Math.max(0, tache.system.points_de_tache_courant)
}
})
if (bonus >= 0) {
await this.deleteEmbeddedDocuments('Item', [tache.id])
}
}
}
}
async onRollSoinsComplets(blessureId, rollData) {
if (!this.isOwner) {
return RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'onRollSoinsComplets', args: [blessureId, rollData]
})
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && blessure.system.premierssoins.done && !blessure.system.soinscomplets.done) {
// TODO: update de la blessure: passer par le MJ!
if (rollData.rolled.isETotal) {
await blessure.setSoinsBlessure({
difficulte: blessure.system.difficulte - 1,
premierssoins: { done: false, bonus: 0 }, soinscomplets: { done: false, bonus: 0 },
})
}
else {
// soins complets finis
await blessure.setSoinsBlessure({
soinscomplets: { done: true, bonus: Math.max(0, rollData.rolled.ptTache) },
}) })
} }
} }
@@ -2990,15 +2931,6 @@ export class RdDActor extends RdDBaseActorSang {
await this.diminuerQuantiteObjet(potion.id, 1, { supprimerSiZero: potion.supprimer }); await this.diminuerQuantiteObjet(potion.id, 1, { supprimerSiZero: potion.supprimer });
} }
/* -------------------------------------------- */
async onUpdateActor(update, options, actorId) {
const updatedEndurance = update?.system?.sante?.endurance
if (updatedEndurance && options.diff) {
await this.setEffect(STATUSES.StatusUnconscious, updatedEndurance.value == 0)
}
await super.onUpdateActor(update, options, actorId)
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { async onPreUpdateItem(item, change, options, id) {
if (item.isCompetencePersonnage() && item.system.defaut_carac && item.system.xp) { if (item.isCompetencePersonnage() && item.system.defaut_carac && item.system.xp) {

View File

@@ -26,7 +26,7 @@ import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES_CREATURES } fr
import { RollDataAjustements } from "../rolldata-ajustements-v1.js"; import { RollDataAjustements } from "../rolldata-ajustements-v1.js";
import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs"; import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
import RollDialog from "../roll/roll-dialog.mjs"; import RollDialog from "../roll/roll-dialog.mjs";
import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE} from "../roll/roll-constants.mjs"; import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE } from "../roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js"; import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js";
import { PART_COMP } from "../roll/roll-part-comp.mjs"; import { PART_COMP } from "../roll/roll-part-comp.mjs";
import { RdDInitiative } from "../initiative.mjs"; import { RdDInitiative } from "../initiative.mjs";
@@ -208,7 +208,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
.map(async it => await RdDEmpoignade.onImmobilisation(this, it))) .map(async it => await RdDEmpoignade.onImmobilisation(this, it)))
} }
async finDeRoundPossession(){ async finDeRoundPossession() {
await Promise.all(this.itemTypes[ITEM_TYPES.possession] await Promise.all(this.itemTypes[ITEM_TYPES.possession]
.map(async it => await RdDPossessionV2.onPossession(this, it))) .map(async it => await RdDPossessionV2.onPossession(this, it)))
} }
@@ -348,7 +348,10 @@ export class RdDBaseActorReve extends RdDBaseActor {
competence: competence, competence: competence,
show: { title: options?.title ?? '' } show: { title: options?.title ?? '' }
}, },
callbacks: [async r => this.$onRollCompetence(r, options)] callbacks: [
async r => this.$onRollCompetence(r, options),
...(options?.callbacks ?? [])
]
}); });
} }
/** /**

View File

@@ -7,6 +7,7 @@ import { RdDDice } from "../rdd-dice.js";
import { RdDItemBlessure } from "../item/blessure.js"; import { RdDItemBlessure } from "../item/blessure.js";
import { ChatUtility } from "../chat-utility.js"; import { ChatUtility } from "../chat-utility.js";
import { Misc } from "../misc.js"; import { Misc } from "../misc.js";
import { RdDBaseActor } from "./base-actor.js";
/** /**
* Classe de base pour les acteurs qui peuvent subir des blessures * Classe de base pour les acteurs qui peuvent subir des blessures
@@ -120,16 +121,14 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
/* -------------------------------------------- */ /* -------------------------------------------- */
async santeIncDec(name, inc, isCritique = false) { async santeIncDec(name, inc, isCritique = false) {
if (name == 'fatigue' && !ReglesOptionnelles.isUsing("appliquer-fatigue")) { if (name == 'fatigue' && !ReglesOptionnelles.isUsing("appliquer-fatigue")) {
return; return
}
if (!this.system.sante[name]) {
return
} }
const sante = foundry.utils.duplicate(this.system.sante) const sante = foundry.utils.duplicate(this.system.sante)
let compteur = sante[name]; const compteur = sante[name]
if (!compteur) { const result = { sonne: false }
return;
}
let result = {
sonne: false,
};
let perteEndurance = 0 let perteEndurance = 0
let minValue = name == "vie" ? -this.getSConst() - 1 : 0; let minValue = name == "vie" ? -this.getSConst() - 1 : 0;
@@ -171,6 +170,67 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
return result return result
} }
async onRollTachePremiersSoins(blessureId, rollData, soigneurId) {
if (!this.isOwner) {
return RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'onRollTachePremiersSoins', args: [blessureId, rollData, soigneurId]
})
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && !blessure.system.premierssoins.done) {
const tache = rollData.v2 ? rollData.current.tache.tache : rollData.tache
if (rollData.rolled.isETotal) {
await blessure.update({
'system.difficulte': blessure.system.difficulte - 1,
'system.premierssoins.tache': Math.max(0, tache.system.points_de_tache_courant)
})
}
else {
const bonus = tache.system.points_de_tache_courant - tache.system.points_de_tache
await blessure.update({
'system.premierssoins': {
done: (bonus >= 0),
bonus: Math.max(0, bonus),
tache: Math.max(0, tache.system.points_de_tache_courant)
}
})
if (bonus >= 0 && soigneurId) {
const soigneur = game.actors.get(soigneurId)
await soigneur.deleteEmbeddedDocuments('Item', [tache.id], { render: true })
}
}
}
}
async onRollSoinsComplets(blessureId, rollData) {
if (!this.isOwner) {
return RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'onRollSoinsComplets', args: [blessureId, rollData]
})
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && blessure.system.premierssoins.done && !blessure.system.soinscomplets.done) {
// TODO: update de la blessure: passer par le MJ!
if (rollData.rolled.isETotal) {
await blessure.setSoinsBlessure({
difficulte: blessure.system.difficulte - 1,
premierssoins: { done: false, bonus: 0 }, soinscomplets: { done: false, bonus: 0 },
})
}
else {
// soins complets finis
await blessure.setSoinsBlessure({
soinscomplets: { done: true, bonus: Math.max(0, rollData.rolled.ptTache) },
})
}
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
_computeEnduranceMax() { _computeEnduranceMax() {
const diffVie = this.system.sante.vie.max - this.system.sante.vie.value; const diffVie = this.system.sante.vie.max - this.system.sante.vie.value;
@@ -182,6 +242,14 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques)); return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques));
} }
async onUpdateActor(update, options, actorId) {
const updatedEndurance = update?.system?.sante?.endurance
if (updatedEndurance && options.diff) {
await this.setEffect(STATUSES.StatusUnconscious, updatedEndurance.value == 0)
}
await super.onUpdateActor(update, options, actorId)
}
async onCreateItem(item, options, id) { async onCreateItem(item, options, id) {
await this.changeItemEffects(item) await this.changeItemEffects(item)
await super.onCreateItem(item, options, id) await super.onCreateItem(item, options, id)

View File

@@ -23,7 +23,7 @@ export class RdDBaseActor extends Actor {
} }
static $findCaracByName(carac, name) { static $findCaracByName(carac, name) {
const caracList = Object.entries(carac); const caracList = Object.entries(carac)
let entry = Misc.findFirstLike(name, caracList, { mapper: it => it[0], description: 'caractéristique', onMessage: m => { } }); let entry = Misc.findFirstLike(name, caracList, { mapper: it => it[0], description: 'caractéristique', onMessage: m => { } });
if (!entry || entry.length == 0) { if (!entry || entry.length == 0) {
entry = Misc.findFirstLike(name, caracList, { mapper: it => it[1].label, description: 'caractéristique' }); entry = Misc.findFirstLike(name, caracList, { mapper: it => it[1].label, description: 'caractéristique' });
@@ -62,14 +62,14 @@ export class RdDBaseActor extends Actor {
} }
} }
static remoteActorCall(callData) { static remoteActorCall(callData, userId = undefined) {
if (game.user.isGM) { if (game.user.isGM) {
RdDBaseActor.onRemoteActorCall(callData, game.user.id) RdDBaseActor.onRemoteActorCall(callData, game.user.id)
return false return false
} }
else { else {
const gmUserId = Misc.firstConnectedGMId() const gmUserId = Misc.firstConnectedGMId()
if (gmUserId){ if (gmUserId) {
game.socket.emit(SYSTEM_SOCKET_ID, { game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_remote_actor_call", msg: "msg_remote_actor_call",
data: callData, data: callData,
@@ -166,14 +166,22 @@ export class RdDBaseActor extends Actor {
return this.system.sante.vie return this.system.sante.vie
} }
const carac = this.system.carac; const carac = {}
foundry.utils.mergeObject(carac, this.system.carac, { overwrite: false })
foundry.utils.mergeObject(carac, this.getCaracCompetenceCreature(), { overwrite: false })
return RdDBaseActor.$findCaracByName(carac, name); return RdDBaseActor.$findCaracByName(carac, name);
} }
getCaracCompetenceCreature() {
return this.isCreatureOuEntite()
? Object.fromEntries(this.itemTypes[ITEM_TYPES.competencecreature].map(it => [Grammar.toLowerCaseNoAccent(it.name), { label: it.name, value: it.system.carac_value }]))
: {}
}
mapCarac(caracCode) { return caracCode } mapCarac(caracCode) { return caracCode }
getCaracByName(name) { getCaracByName(name) {
name = this.mapCarac(Grammar.toLowerCaseNoAccent(name)) name = this.mapCarac(Grammar.toLowerCaseNoAccent(name)) ?? name
switch (name) { switch (name) {
case 'reve-actuel': case 'reve actuel': case 'reve-actuel': case 'reve actuel':
return this.getCaracReveActuel(); return this.getCaracReveActuel();
@@ -417,6 +425,14 @@ export class RdDBaseActor extends Actor {
} }
async depenserSols(sols) { async depenserSols(sols) {
if (!this.isOwner) {
return RdDBaseActor.remoteActorCall({
userId: Misc.connectedGMOrUser(),
tokenId: this.token?.id,
actorId: this.id,
method: 'depenserSols', args: [sols]
})
}
let reste = this.getFortune() - Number(sols); let reste = this.getFortune() - Number(sols);
if (reste >= 0) { if (reste >= 0) {
await Monnaie.optimiserFortune(this, reste); await Monnaie.optimiserFortune(this, reste);
@@ -425,32 +441,31 @@ export class RdDBaseActor extends Actor {
} }
async ajouterSols(sols, fromActorId = undefined) { async ajouterSols(sols, fromActorId = undefined) {
sols = Number(sols); sols = Number(sols)
if (sols == 0) { if (sols == 0) {
return; return
} }
if (sols < 0) { if (sols < 0) {
ui.notifications.error(`Impossible d'ajouter un gain de ${sols} <0`); ui.notifications.error(`Impossible d'ajouter un gain de ${sols} <0`)
return; return
} }
if (fromActorId && !this.isOwner) { if (fromActorId && !this.isOwner) {
RdDBaseActor.remoteActorCall({ return RdDBaseActor.remoteActorCall({
userId: Misc.connectedGMOrUser(), userId: Misc.connectedGMOrUser(),
tokenId: this.token?.id, tokenId: this.token?.id,
actorId: this.id, actorId: this.id,
method: 'ajouterSols', args: [sols, fromActorId] method: 'ajouterSols', args: [sols, fromActorId]
}); })
} }
else {
const fromActor = game.actors.get(fromActorId)
await Monnaie.optimiserFortune(this, sols + this.getFortune());
RdDAudio.PlayContextAudio("argent"); // Petit son const fromActor = game.actors.get(fromActorId)
ChatMessage.create({ await Monnaie.optimiserFortune(this, sols + this.getFortune());
whisper: ChatUtility.getOwners(this),
content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.` RdDAudio.PlayContextAudio("argent"); // Petit son
}); ChatMessage.create({
} whisper: ChatUtility.getOwners(this),
content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.`
})
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -462,24 +477,28 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async achatVente(achat) { async achatVente(achat) {
if (achat.vendeurId == achat.acheteurId) { if (achat.vendeurId == achat.acheteurId) {
ui.notifications.info("Inutile de se vendre à soi-même"); ui.notifications.info("Inutile de se vendre à soi-même")
return; return
} }
if (!game.user.isGM) { const cout = Number(achat.prixTotal ?? 0)
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined
if (acheteur && !acheteur.isOwner) {
ui.notifications.warn(`${game.user.id} n'est pas propriétaire de ${this.name} et effectue un achat pour lui!`)
RdDBaseActor.remoteActorCall({ RdDBaseActor.remoteActorCall({
actorId: achat.vendeurId ?? achat.acheteurId, actorId: achat.acheteurId,
method: 'achatVente', args: [achat] method: 'achatVente', args: [achat]
}); });
return return
} }
const cout = Number(achat.prixTotal ?? 0);
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined; const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined
const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot); const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot);
const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id); const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id);
if (!itemVendu) { if (!itemVendu) {
ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !` : `Impossible de retrouver: ${achat.vente.item.name} !`); ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !` : `Impossible de retrouver: ${achat.vente.item.name} !`);
return; return
} }
if (vendeur && !vendeur.verifierQuantite(itemVendu, quantite)) { if (vendeur && !vendeur.verifierQuantite(itemVendu, quantite)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`); ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
@@ -487,10 +506,10 @@ export class RdDBaseActor extends Actor {
} }
if (acheteur && !acheteur.verifierFortune(cout)) { if (acheteur && !acheteur.verifierFortune(cout)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Vous n'avez pas assez d'argent pour payer ${Math.ceil(cout / 100)} sols !`); ChatUtility.notifyUser(achat.userId, 'warn', `Vous n'avez pas assez d'argent pour payer ${Math.ceil(cout / 100)} sols !`);
return; return
} }
await vendeur?.vendre(itemVendu, quantite, cout); await vendeur?.vendre(itemVendu, quantite, cout, acheteur);
await acheteur?.acheter(itemVendu, quantite, cout, achat) await acheteur?.acheter(itemVendu, quantite, cout, achat, vendeur)
if (cout > 0) { if (cout > 0) {
RdDAudio.PlayContextAudio("argent"); RdDAudio.PlayContextAudio("argent");
@@ -505,22 +524,32 @@ export class RdDBaseActor extends Actor {
}); });
if (!achat.vente.quantiteIllimite) { if (!achat.vente.quantiteIllimite) {
if (achat.vente.nbLots <= achat.choix.nombreLots) { await this.updateMessageVente(achat.chatMessageIdVente, achat.choix.nombreLots);
ChatUtility.removeChatMessageId(achat.chatMessageIdVente);
}
else if (achat.chatMessageIdVente) {
await ChatVente.diminuerQuantiteAchatVente(achat.chatMessageIdVente, achat.choix.nombreLots)
}
} }
} }
async vendre(item, quantite, cout) { async updateMessageVente(chatMessageIdVente, nombreLots) {
await this.ajouterSols(cout); const chatMessage = game.messages.get(chatMessageIdVente)
await this.decrementerQuantiteItem(item, quantite); if (!chatMessage.isOwner) {
return RdDBaseActor.remoteActorCall({
userId: Misc.firstConnectedGMId(),
actorId: this.id,
method: 'updateMessageVente', args: [chatMessageIdVente, nombreLots]
})
}
await ChatVente.diminuerQuantiteAchatVente(chatMessageIdVente, nombreLots);
}
async vendre(item, quantite, cout, acheteur) {
await this.ajouterSols(cout, acheteur?.id)
await this.decrementerQuantiteItem(item.id, quantite)
} }
async acheter(item, quantite, cout, achat) { async acheter(item, quantite, cout, achat) {
await this.depenserSols(cout) await this.depenserSols(cout)
if (!this.isOwner) {
ui.notifications.warn(`${game.user.id} n'est pas propriétaire de ${this.name} et effectue un achat!`)
}
const createdItemId = await this.creerQuantiteItem(item, quantite) const createdItemId = await this.creerQuantiteItem(item, quantite)
if (item.type == ITEM_TYPES.nourritureboisson && achat.choix.consommer && createdItemId != undefined) { if (item.type == ITEM_TYPES.nourritureboisson && achat.choix.consommer && createdItemId != undefined) {
achat.choix.doses = achat.choix.nombreLots; achat.choix.doses = achat.choix.nombreLots;
@@ -542,43 +571,46 @@ export class RdDBaseActor extends Actor {
async consommerNourritureboisson(itemId, choix, userId) { } async consommerNourritureboisson(itemId, choix, userId) { }
async decrementerQuantiteItem(item, quantite, options = { supprimerSiZero: true }) { async decrementerQuantiteItem(itemId, quantite, options = { supprimerSiZero: true }) {
const item = this.items.get(itemId)
if (item.isService()) { if (item.isService()) {
return; return
} }
const itemId = item.id;
let resteQuantite = (item.system.quantite ?? 1) - quantite; if (!this.isOwner) {
if (resteQuantite <= 0) { return RdDBaseActor.remoteActorCall({
if (options.supprimerSiZero) { userId: Misc.connectedGMOrUser(),
await this.deleteEmbeddedDocuments("Item", [item.id]); tokenId: this.token?.id,
} actorId: this.id,
else { method: 'decrementerQuantiteItem', args: [itemId, quantite, options]
await this.updateEmbeddedDocuments("Item", [{ _id: itemId, 'system.quantite': 0 }]); })
}
if (resteQuantite < 0) {
ui.notifications.warn(`La quantité de ${item.name} était insuffisante, l'objet a donc été supprimé`)
}
} }
else if (resteQuantite > 0) {
const realItem = this.getItem(item.id) const resteQuantite = Math.max((item.system.quantite ?? 1) - quantite, 0)
realItem.update({ 'system.quantite': resteQuantite }); if (resteQuantite <= 0 && options.supprimerSiZero) {
await this.updateEmbeddedDocuments("Item", [{ _id: item.id, 'system.quantite': resteQuantite }]); await this.deleteEmbeddedDocuments("Item", [itemId]);
}
else {
await this.updateEmbeddedDocuments("Item", [{ _id: itemId, 'system.quantite': resteQuantite }])
} }
} }
async creerQuantiteItem(item, quantite) { async creerQuantiteItem(item, quantite) {
if (this.canReceive(item)) { if (!this.canReceive(item)) {
const isItemEmpilable = "quantite" in item.system; return
const baseItem = {
type: item.type,
img: item.img,
name: item.name,
system: foundry.utils.mergeObject(item.system, { quantite: isItemEmpilable ? quantite : undefined }, { inplace: false })
};
const newItems = isItemEmpilable ? [baseItem] : Array.from({ length: quantite }, (_, i) => baseItem);
const items = await this.createEmbeddedDocuments("Item", newItems);
return items.length > 0 ? items[0].id : undefined;
} }
const isItemEmpilable = "quantite" in item.system;
const baseItem = {
type: item.type,
img: item.img,
name: item.name,
system: foundry.utils.mergeObject(item.system, { quantite: isItemEmpilable ? quantite : undefined }, { inplace: false })
}
const newItems = isItemEmpilable ? [baseItem] : Array.from({ length: quantite }, (_, i) => baseItem);
const items = await this.createEmbeddedDocuments("Item", newItems);
return newItems.length > 0 ? items[0].id : undefined;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -866,4 +898,5 @@ export class RdDBaseActor extends Actor {
} }
}) })
} }
} }

View File

@@ -28,11 +28,11 @@ export class RdDCommerce extends RdDBaseActor {
await super.depenserSols(cout) await super.depenserSols(cout)
} }
async decrementerQuantiteItem(item, quantite) { async decrementerQuantiteItem(itemId, quantite) {
if (this.system.illimite) { if (this.system.illimite) {
return; return;
} }
await super.decrementerQuantiteItem(item, quantite, { supprimerSiZero: false }); await super.decrementerQuantiteItem(itemId, quantite, { supprimerSiZero: false });
} }
calculerPrix(item) { calculerPrix(item) {

View File

@@ -233,4 +233,25 @@ export class ChatUtility {
static async setTimestamp(chatMessage) { static async setTimestamp(chatMessage) {
await chatMessage.setFlag(SYSTEM_RDD, 'rdd-timestamp', game.system.rdd.calendrier.getTimestamp()); await chatMessage.setFlag(SYSTEM_RDD, 'rdd-timestamp', game.system.rdd.calendrier.getTimestamp());
} }
// TODO: find ChatLog to change the action for data-action flush
// async flush() {
// const question = game.i18n.localize("AreYouSure");
// const warning = game.i18n.localize("CHAT.FlushWarning");
// return foundry.applications.api.DialogV2.confirm({
// window: {title: "CHAT.FlushTitle"},
// content: `<p><strong>${question}</strong> ${warning}</p>`,
// position: {
// top: window.innerHeight - 150,
// left: window.innerWidth - 720
// },
// yes: {
// callback: async () => {
// await this.documentClass.deleteDocuments([], {deleteAll: true});
// const jumpToBottomElement = document.querySelector(".jump-to-bottom");
// jumpToBottomElement.hidden = true;
// }
// }
// });
// }
} }

View File

@@ -45,7 +45,7 @@ export class RdDItemArme extends RdDItem {
isParade() { return this.system.resistance > 0 && this.system.categorie_parade } isParade() { return this.system.resistance > 0 && this.system.categorie_parade }
isBouclier() { return RdDItemArme.getCategorieParade(this).includes('bouclier') } isBouclier() { return RdDItemArme.getCategorieParade(this).includes('bouclier') }
getCompetenceAction(main) { getCompetenceAction(main) {
switch (main) { switch (main) {
case ATTAQUE_TYPE.UNE_MAIN: return this.competence1Mains() case ATTAQUE_TYPE.UNE_MAIN: return this.competence1Mains()
@@ -174,24 +174,30 @@ export class RdDItemArme extends RdDItem {
static defenseArmeParade(armeAttaque, armeParade) { static defenseArmeParade(armeAttaque, armeParade) {
const defCategory = RdDItemArme.getCategorieParade(armeParade) const defCategory = RdDItemArme.getCategorieParade(armeParade)
if (defCategory == 'boucliers') { switch (defCategory) {
return 'norm' case 'boucliers':
return 'norm'
case '':
return 'impossible'
} }
if (armeAttaque?.system?.competence?.toLowerCase().match(/(fléau)/)) { if (RdDItemArme.$isFleau(armeAttaque)) {
return '' return 'impossible'
}
if (armeParade.system?.tir) {
return ''
} }
const attCategory = RdDItemArme.getCategorieParade(armeAttaque) const attCategory = RdDItemArme.getCategorieParade(armeAttaque)
switch (attCategory) { switch (attCategory) {
case 'armes-naturelles': case 'sans-armes': case 'sans-armes':
return defCategory == 'sans-armes' ? 'norm' : '' case 'armes-naturelles':
return defCategory == attCategory ? 'norm' : 'impossible'
default: default:
return RdDItemArme.needParadeSignificative(armeAttaque, armeParade) ? 'sign' : 'norm' return RdDItemArme.needParadeSignificative(armeAttaque, armeParade) ? 'sign' : 'norm'
} }
} }
static $isFleau(armeAttaque) {
return armeAttaque?.system?.competence?.toLowerCase().match(/(fléau)/);
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static needParadeSignificative(armeAttaque, armeParade) { static needParadeSignificative(armeAttaque, armeParade) {
if (!armeAttaque || !armeParade) { if (!armeAttaque || !armeParade) {

View File

@@ -104,7 +104,7 @@ export class Apprecier {
apprecier(callbacks = []) { apprecier(callbacks = []) {
if (this.qualite <= 0) { if (this.qualite <= 0) {
this.raisons.push(`la qualité ${this.qualite} est négative.`) this.raisons.push(`la qualité ${this.qualite} est insuffisante.`)
} }
if (this.qualite <= this.actor.getMoralTotal()) { if (this.qualite <= this.actor.getMoralTotal()) {
this.raisons.push(`la qualité de ${this.qualite} est inférieure au moral de ${this.actor.getMoralTotal()}.`) this.raisons.push(`la qualité de ${this.qualite} est inférieure au moral de ${this.actor.getMoralTotal()}.`)

View File

@@ -775,7 +775,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
_prepareAttaque(competence, arme) { _prepareAttaque(competence, arme) {
let rollData = { let rollData = {
mode: ROLL_TYPE_ATTAQUE, mode: 'attaque',
alias: this.attacker?.getAlias(), alias: this.attacker?.getAlias(),
passeArme: foundry.utils.randomID(16), passeArme: foundry.utils.randomID(16),
mortalite: arme?.system.mortalite, mortalite: arme?.system.mortalite,
@@ -1378,6 +1378,7 @@ export class RdDCombat {
return return
} }
const alias = token?.name ?? actor.getAlias(); const alias = token?.name ?? actor.getAlias();
const blessuresGraves = actor.countBlessures(it => it.isGrave());
const formData = { const formData = {
combatId: combat._id, combatId: combat._id,
alias: alias, alias: alias,
@@ -1388,7 +1389,7 @@ export class RdDCombat {
actorId: actor.id, actorId: actor.id,
actor: actor, actor: actor,
tokenId: token.id, tokenId: token.id,
isGrave: actor.countBlessures(it => it.isGrave()) > 0, blessuresGraves: blessuresGraves,
isCritique: actor.countBlessures(it => it.isCritique()) > 0 isCritique: actor.countBlessures(it => it.isCritique()) > 0
} }
await ChatMessage.create({ await ChatMessage.create({

View File

@@ -49,9 +49,8 @@ export default class ChatRollResult {
roll.active.actor, roll.active.actor,
roll.current?.rollmode?.key roll.current?.rollmode?.key
) )
const save = RollDialog.saveParts(roll, impacts)
await this.saveChatMessageRoll(chatMessage, save) await this.saveChatMessageRoll(chatMessage, roll, impacts)
return chatMessage return chatMessage
} }
@@ -166,8 +165,20 @@ export default class ChatRollResult {
return roll.active?.actor ?? (roll.ids?.actorId ? game.actors.get(roll.ids.actorId) : undefined) return roll.active?.actor ?? (roll.ids?.actorId ? game.actors.get(roll.ids.actorId) : undefined)
} }
async saveChatMessageRoll(chatMessage, savedRoll) { async updateChatMessage(chatMessage, savedRoll) {
await ChatUtility.setMessageData(chatMessage, 'rollData', savedRoll) RollDialog.loadRollData(savedRoll)
savedRoll.dmg = savedRoll.current.attaque?.dmg
await this.saveChatMessageRoll(chatMessage, savedRoll)
this.prepareDisplay(savedRoll)
chatMessage.update({ content: await this.buildRollHtml(savedRoll) })
chatMessage.render(true)
}
async saveChatMessageRoll(chatMessage, roll, impacts = undefined) {
const save = RollDialog.saveParts(roll, impacts)
await ChatUtility.setMessageData(chatMessage, 'rollData', save)
} }
loadChatMessageRoll(chatMessage) { loadChatMessageRoll(chatMessage) {
@@ -176,16 +187,6 @@ export default class ChatRollResult {
return savedRoll return savedRoll
} }
async updateChatMessage(chatMessage, savedRoll) {
await this.saveChatMessageRoll(chatMessage, savedRoll)
const copy = foundry.utils.duplicate(savedRoll)
RollDialog.loadRollData(copy)
savedRoll.dmg = copy.current.attaque?.dmg
this.prepareDisplay(copy)
chatMessage.update({ content: await this.buildRollHtml(copy) })
chatMessage.render(true)
}
onClickAppelChance(event) { onClickAppelChance(event) {
event.preventDefault() event.preventDefault()
const chatMessage = ChatUtility.getChatMessage(event) const chatMessage = ChatUtility.getChatMessage(event)
@@ -197,28 +198,28 @@ export default class ChatRollResult {
) )
} }
async onAppelChanceSuccess(reRoll, chatMessage) { async onAppelChanceSuccess(roll, chatMessage) {
reRoll.type.retry = true roll.type.retry = true
await this.updateChatMessage(chatMessage, reRoll) await this.updateChatMessage(chatMessage, roll)
const callbacks = [ChatUtility.remover(chatMessage)] const callbacks = [ChatUtility.remover(chatMessage)]
// TODO: annuler les effets... // TODO: annuler les effets...
switch (reRoll.type.current) { switch (roll.type.current) {
case ROLL_TYPE_DEFENSE: case ROLL_TYPE_DEFENSE:
this.getCombat(reRoll)?.doRollDefense(reRoll, callbacks) this.getCombat(roll)?.doRollDefense(roll, callbacks)
break break
case ROLL_TYPE_ATTAQUE: case ROLL_TYPE_ATTAQUE:
this.getCombat(reRoll)?.doRollAttaque(reRoll, callbacks) this.getCombat(roll)?.doRollAttaque(roll, callbacks)
break break
default: { default: {
await RollDialog.create(reRoll, { onRollDone: RollDialog.onRollDoneClose, callbacks }) await RollDialog.create(roll, { onRollDone: RollDialog.onRollDoneClose, callbacks })
} }
} }
} }
async onAppelChanceEchec(reRoll, chatMessage) { async onAppelChanceEchec(roll, chatMessage) {
reRoll.type.retry = true roll.type.retry = true
await this.updateChatMessage(chatMessage, reRoll) await this.updateChatMessage(chatMessage, roll)
} }
onClickAppelDestinee(event) { onClickAppelDestinee(event) {
@@ -229,10 +230,9 @@ export default class ChatRollResult {
const actor = this.getActiveActor(savedRoll) const actor = this.getActiveActor(savedRoll)
Misc.doIfOwner(actor, it => it.appelDestinee(async () => { Misc.doIfOwner(actor, it => it.appelDestinee(async () => {
const reRoll = foundry.utils.duplicate(savedRoll) savedRoll.type.retry = true
reRoll.type.retry = true RdDResolutionTable.significativeRequise(savedRoll.rolled)
RdDResolutionTable.significativeRequise(reRoll.rolled) await this.updateChatMessage(chatMessage, savedRoll)
await this.updateChatMessage(chatMessage, reRoll)
})) }))
} }

View File

@@ -42,7 +42,6 @@ import ChatRollResult from "./chat-roll-result.mjs";
import { renderTemplate } from "../constants.js"; import { renderTemplate } from "../constants.js";
import { RollTypeCuisine } from "./roll-type-cuisine.mjs"; import { RollTypeCuisine } from "./roll-type-cuisine.mjs";
import { RollPartCuisine } from "./roll-part-cuisine.mjs"; 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 { ActorImpacts } from "../technical/actor-impacts.mjs";
import { RollPartEmpoignade } from "./roll-part-empoignade.mjs"; import { RollPartEmpoignade } from "./roll-part-empoignade.mjs";
import { RollPartEmpoignadeTaille } from "./roll-part-empoignade-taille.mjs"; import { RollPartEmpoignadeTaille } from "./roll-part-empoignade-taille.mjs";
@@ -199,9 +198,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
} }
static onRollDoneClose(dialog, roll) { static onRollDoneClose(dialog, roll) {
if (roll.type.retry || !OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST)) { dialog.close()
dialog.close()
}
} }
static init() { } static init() { }
@@ -289,6 +286,13 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
// rien pour l'instant // rien pour l'instant
} }
static getAllowedParts(rollData) {
return rollData.type?.allowed
? ROLL_PARTS.filter(p => RollDialog.$isIntersecting(rollData.type.allowed, p.rollTypes))
: ROLL_PARTS
}
/** pre-configure les paramètres des différentes parties de la fenêtre (par exemple, prépare les listes de caractéristiques/compétences */ /** pre-configure les paramètres des différentes parties de la fenêtre (par exemple, prépare les listes de caractéristiques/compétences */
static $prepareRollData(rollData) { static $prepareRollData(rollData) {
rollData.current = rollData.current ?? {} rollData.current = rollData.current ?? {}
@@ -302,17 +306,17 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
? [potential] ? [potential]
: (rollData.type.allowed ?? ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code) ?? [ROLL_TYPE_COMP]) : (rollData.type.allowed ?? ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code) ?? [ROLL_TYPE_COMP])
const rollType = allowed.find(c => c == rollData.type.current) ?? allowed[0] const rollType = allowed.find(c => c == rollData.type.current) ?? allowed[0]
const allowedRollParts = RollDialog.getAllowedParts(rollData)
rollData.type.allowed = allowed rollData.type.allowed = allowed
rollData.type.current = rollType rollData.type.current = rollType
ALL_ROLL_TYPES.find(m => m.code == rollType).setRollDataType(rollData) ALL_ROLL_TYPES.find(m => m.code == rollType).setRollDataType(rollData)
rollData.refs = foundry.utils.mergeObject(rollData.refs ?? {}, Object.fromEntries(ROLL_PARTS.map(p => [p.code, {}]))); rollData.refs = foundry.utils.mergeObject(rollData.refs ?? {}, Object.fromEntries(allowedRollParts.map(p => [p.code, {}])));
rollData.options = rollData.options ?? { rollMode: game.settings.get("core", "rollMode") } rollData.options = rollData.options ?? { rollMode: game.settings.get("core", "rollMode") }
ROLL_PARTS.forEach(p => RollDialog.$initializeRollPart(rollData, p.code)) allowedRollParts.forEach(p => RollDialog.$initializeRollPart(rollData, p.code))
ROLL_PARTS allowedRollParts
.filter(p => RollDialog.$isIntersecting(allowed, p.rollTypes))
.filter(p => p.isValid(rollData)) .filter(p => p.isValid(rollData))
.forEach(p => { .forEach(p => {
p.restore(rollData) p.restore(rollData)
@@ -350,11 +354,13 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
} }
if (from.current) { if (from.current) {
// stockage de current // stockage de current
ROLL_PARTS.filter(p => p.isActive(from)) RollDialog.getAllowedParts(from)
.filter(p => p.isActive(from))
.forEach(p => p.storeClean(from, target)) .forEach(p => p.storeClean(from, target))
} }
} }
} }
const target = RollBasicParts.initFrom(rollData) const target = RollBasicParts.initFrom(rollData)
saveBasics(rollData, target) saveBasics(rollData, target)
if (impacts) { if (impacts) {
@@ -382,12 +388,14 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
onClose: rollOptions.onClose ?? RollDialog.onCloseDoNothing onClose: rollOptions.onClose ?? RollDialog.onCloseDoNothing
} }
this.chatRollResult = new ChatRollResult() this.chatRollResult = new ChatRollResult()
this.rollParts = RollDialog.getAllowedParts(rollData)
this.selectType() this.selectType()
this.registerHooks(rollData) this.registerHooks(rollData)
} }
registerHooks(rollData) { registerHooks(rollData) {
ROLL_PARTS.filter(p => p.isValid(rollData)) this.rollParts.filter(p => p.isValid(rollData))
.forEach(p => p.getHooks(this).forEach(h => { .forEach(p => p.getHooks(this).forEach(h => {
const hook = h.hook; const hook = h.hook;
const id = Hooks.on(hook, h.fn) const id = Hooks.on(hook, h.fn)
@@ -407,13 +415,12 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
selectedType.onSelect(this.rollData) selectedType.onSelect(this.rollData)
this.rollData.type.label = selectedType.title(this.rollData) this.rollData.type.label = selectedType.title(this.rollData)
ROLL_PARTS.find(it => it.code == PART_CARAC).filterCaracs(this.rollData) this.rollParts.find(it => it.code == PART_CARAC).filterCaracs(this.rollData)
ROLL_PARTS.find(it => it.code == PART_COMP).filterComps(this.rollData) this.rollParts.find(it => it.code == PART_COMP).filterComps(this.rollData)
} }
getActiveParts() {
static getActiveParts(rollData) { return this.rollParts.filter(p => p.isActive(this.rollData))
return ROLL_PARTS.filter(p => p.isActive(rollData))
} }
rollTitle(rollData) { rollTitle(rollData) {
@@ -448,12 +455,13 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
)) ))
Promise.all( Promise.all(
RollDialog.getActiveParts(this.rollData).map(async p => await p._onRender(this, context, options)) this.getActiveParts().map(async p => await p._onRender(this, context, options))
) )
} }
static getAjustements(rollData) { static getAjustements(rollData) {
return RollDialog.getActiveParts(rollData) return RollDialog.getAllowedParts(rollData)
.filter(p => p.isActive(rollData))
.map(p => p.getAjustements(rollData)) .map(p => p.getAjustements(rollData))
.reduce((a, b) => a.concat(b)) .reduce((a, b) => a.concat(b))
.sort((a, b) => a.value == undefined ? 1 : b.value == undefined ? -1 : 0) .sort((a, b) => a.value == undefined ? 1 : b.value == undefined ? -1 : 0)
@@ -470,8 +478,8 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
.map(m => m.toTypeData(rollData)) .map(m => m.toTypeData(rollData))
RollBasicParts.loadSurprises(rollData, this.getSelectedType().code) RollBasicParts.loadSurprises(rollData, this.getSelectedType().code)
rollData.type.label = this.getSelectedType()?.title(rollData) rollData.type.label = this.getSelectedType()?.title(rollData)
//TOCHECK: set type.label ?
const visibleRollParts = RollDialog.getActiveParts(rollData) const visibleRollParts = this.getActiveParts()
visibleRollParts.forEach(p => p.applyExternalImpacts(visibleRollParts, rollData)) visibleRollParts.forEach(p => p.applyExternalImpacts(visibleRollParts, rollData))
this.setSpecialComp(visibleRollParts); this.setSpecialComp(visibleRollParts);
@@ -480,7 +488,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
RollDialog.calculAjustement(rollData) RollDialog.calculAjustement(rollData)
const templates = RollDialog.getActiveParts(rollData).map(p => p.toTemplateData()) const templates = visibleRollParts.map(p => p.toTemplateData())
const context = await super._prepareContext() const context = await super._prepareContext()
return foundry.utils.mergeObject( return foundry.utils.mergeObject(
{ {
@@ -494,8 +502,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
const specialComp = visibleRollParts.map(p => p.getSpecialComp(this.rollData)) const specialComp = visibleRollParts.map(p => p.getSpecialComp(this.rollData))
.reduce((a, b) => a.concat(b)) .reduce((a, b) => a.concat(b))
if (specialComp.length > 0) { if (specialComp.length > 0) {
const rollPartComp = RollDialog.getActiveParts(this.rollData) const rollPartComp = this.getActiveParts().find(it => it.code == PART_COMP)
.find(it => it.code == PART_COMP);
rollPartComp?.setSpecialComp(this.rollData, specialComp) rollPartComp?.setSpecialComp(this.rollData, specialComp)
} }
} }
@@ -535,12 +542,15 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
roll.result = selectedRollType.getResult(roll, impacts) roll.result = selectedRollType.getResult(roll, impacts)
console.info('RollDialog.roll:', roll) console.info('RollDialog.roll:', roll)
const callbacks = [ const callbacks = [
...this.rollOptions.callbacks,
...selectedRollType.callbacks(this.rollOptions), ...selectedRollType.callbacks(this.rollOptions),
...this.rollOptions.callbacks,
] ]
await Promise.all(callbacks.map(async callback => await callback(roll))) for (let callback of callbacks) {
await callback(roll)
}
await impacts.applyImpacts() await impacts.applyImpacts()
selectedRollType.onApplyImpacts(roll, impacts) selectedRollType.onApplyImpacts(roll, impacts)

View File

@@ -46,19 +46,25 @@ export class RollPartAttaque extends RollPartSelect {
return it.arme.isEmpoignade() return it.arme.isEmpoignade()
} }
store(rollData, targetData) { restore(rollData) {
super.store(rollData, targetData) const saved = this.getSaved(rollData) ?? {}
this.getSaved(targetData).dmg = this.getCurrent(rollData).dmg this.setCurrent(rollData, {
key: saved.key,
tactique: saved.tactique,
dmg: saved.dmg
})
} }
restore(rollData) { store(rollData, targetData) {
const saved = this.getSaved(rollData) const current = this.getCurrent(rollData)
super.restore(rollData) this.setSaved(targetData, {
if (saved.dmg != undefined) { key: current.key,
this.getCurrent(rollData).dmg = this.getSaved(rollData).dmg tactique: current.tactique,
} dmg: current.dmg
})
} }
findAttaque(attaques, saved) { findAttaque(attaques, saved) {
return attaques.find(at => at.arme.id == saved?.arme?.id && return attaques.find(at => at.arme.id == saved?.arme?.id &&
at.comp.id == saved?.comp?.id at.comp.id == saved?.comp?.id
@@ -102,7 +108,11 @@ export class RollPartAttaque extends RollPartSelect {
$selectAttaque(rollData, key) { $selectAttaque(rollData, key) {
const tactique = this.getCurrent(rollData).tactique
this.selectByKey(rollData, key) this.selectByKey(rollData, key)
if (tactique) {
this.getCurrent(rollData).tactique = tactique
}
} }
async _onRender(rollDialog, context, options) { async _onRender(rollDialog, context, options) {

View File

@@ -36,7 +36,7 @@ export class RollPartDefense extends RollPartSelect {
: defenseur.getCompetencesEsquive() : defenseur.getCompetencesEsquive()
const parades = isEmpoignade const parades = isEmpoignade
? [RdDItemArme.empoignade(defenseur)] ? [RdDItemArme.empoignade(defenseur)]
: defenseur.items.filter(it => it.isParade() && (!refs.distance || it.isBouclier())) : this.$getParades(defenseur, attackerRoll, refs.distance)
refs.defenses = [ refs.defenses = [
...esquives.map(it => RollPartDefense.$extractEsquive(it, defenseur, attackerRoll)), ...esquives.map(it => RollPartDefense.$extractEsquive(it, defenseur, attackerRoll)),
@@ -45,6 +45,14 @@ export class RollPartDefense extends RollPartSelect {
this.$selectDefense(rollData) this.$selectDefense(rollData)
} }
$getParades(defenseur, attackerRoll, distance) {
const parades = defenseur.items.filter(it => it.isParade() && (!distance || it.isBouclier()))
const armeAttaque = attackerRoll?.current?.attaque?.arme
return armeAttaque
? parades.filter(armeDefense => RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) != 'impossible')
: parades
}
static $extractEsquive(esquive, defenseur, attackerRoll) { static $extractEsquive(esquive, defenseur, attackerRoll) {
const defense = { const defense = {
key: esquive.id, key: esquive.id,
@@ -124,19 +132,19 @@ export class RollPartDefense extends RollPartSelect {
case PART_CARAC: return part.filterCaracs(rollData, refs.defenses.length > 0 ? [current.carac] : ['impossible']) case PART_CARAC: return part.filterCaracs(rollData, refs.defenses.length > 0 ? [current.carac] : ['impossible'])
case PART_COMP: return part.filterComps(rollData, refs.defenses.length > 0 ? [current.comp?.name] : ['impossible']) case PART_COMP: return part.filterComps(rollData, refs.defenses.length > 0 ? [current.comp?.name] : ['impossible'])
case PART_DIFF: return part.setDiff(rollData, this.getDiffDefense(rollData)) case PART_DIFF: return part.setDiff(rollData, this.getDiffDefense(rollData))
case PART_SIGN: return part.setArme(rollData, this.isArmeDisparate(rollData), current.forceRequise) case PART_SIGN: return part.setArme(rollData, this.getArmeDisparate(rollData), current.forceRequise)
} }
} }
return undefined return undefined
} }
isArmeDisparate(rollData) { getArmeDisparate(rollData) {
const armeDefense = this.getCurrent(rollData).arme const armeDefense = this.getCurrent(rollData).arme
if (armeDefense) { if (armeDefense) {
const armeAttaque = rollData.attackerRoll?.current.attaque.arme const armeAttaque = rollData.attackerRoll?.current.attaque.arme
return RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) == 'sign' return RdDItemArme.defenseArmeParade(armeAttaque, armeDefense)
} }
return false return 'norm'
} }
getDiffDefense(rollData) { getDiffDefense(rollData) {

View File

@@ -29,4 +29,17 @@ export class RollPartRollMode extends RollPart {
rollDialog.render() rollDialog.render()
})) }))
} }
getHooks(rollDialog) {
return [
{ hook: "clientSettingChanged", fn: (setting, update, options, id) => this.onUpdateSetting(rollDialog, setting, update, options, id) },
]
}
async onUpdateSetting(rollDialog, setting, update, options, id) {
if (setting == 'core.rollMode') {
this.setCurrent(rollDialog.rollData, { key: game.settings.get("core", "rollMode") })
rollDialog.render()
}
}
} }

View File

@@ -40,7 +40,7 @@ export class RollPartSign extends RollPart {
const actor = rollData.active.actor; const actor = rollData.active.actor;
const isCombat = this.isCombat(rollData) const isCombat = this.isCombat(rollData)
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
current.armeDisparate = isCombat && current.armeDisparate current.armeDisparate = isCombat ? current.armeDisparate : 'norm'
current.surprise = actor.getSurprise(isCombat, current.forceRequise ?? 0) current.surprise = actor.getSurprise(isCombat, current.forceRequise ?? 0)
current.reasons = actor.getEffects(it => StatusEffects.niveauSurprise(it, isCombat) > 0, current.forceRequise ?? 0) current.reasons = actor.getEffects(it => StatusEffects.niveauSurprise(it, isCombat) > 0, current.forceRequise ?? 0)
.map(it => { return { img: it.img, label: game.i18n.localize(it.name) } }) .map(it => { return { img: it.img, label: game.i18n.localize(it.name) } })
@@ -81,7 +81,7 @@ export class RollPartSign extends RollPart {
} }
isParadeArmeDisparate(current) { isParadeArmeDisparate(current) {
return current.armeDisparate return current.armeDisparate == 'sign'
} }
getAjustements(rollData) { getAjustements(rollData) {

View File

@@ -1,4 +1,3 @@
import { ITEM_TYPES } from "../constants.js"
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js" import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"
import { DIFF, ROLL_TYPE_TACHE } from "./roll-constants.mjs" import { DIFF, ROLL_TYPE_TACHE } from "./roll-constants.mjs"
import { PART_TACHE } from "./roll-part-tache.mjs" import { PART_TACHE } from "./roll-part-tache.mjs"
@@ -19,25 +18,22 @@ export class RollTypeTache extends RollType {
this.setDiffType(rollData, DIFF.AUCUN) this.setDiffType(rollData, DIFF.AUCUN)
} }
callbacks(rollOptions) { return [ async r => await RollTypeTache.$onRollTache(r, rollOptions)] } callbacks(rollOptions) { return [async r => await RollTypeTache.$onRollTache(r)] }
static async $onRollTache(rollData, rollOptions) { static async $onRollTache(rollData) {
const actor = rollData.active.actor const actor = rollData.active.actor
const tache = rollData.current[PART_TACHE].tache const tache = rollData.current[PART_TACHE].tache
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
await actor.santeIncDec("fatigue", tache.system.fatigue)
}
rollData.current[PART_TACHE].tache = await tache.update({ rollData.current[PART_TACHE].tache = await tache.update({
'system.points_de_tache_courant': tache.system.points_de_tache_courant + rollData.rolled.ptTache, 'system.points_de_tache_courant': tache.system.points_de_tache_courant + rollData.rolled.ptTache,
'system.nb_jet_succes': tache.system.nb_jet_succes + (rollData.rolled.isSuccess ? 1 : 0), 'system.nb_jet_succes': tache.system.nb_jet_succes + (rollData.rolled.isSuccess ? 1 : 0),
'system.nb_jet_echec': tache.system.nb_jet_echec + (rollData.rolled.isSuccess ? 0 : 1), 'system.nb_jet_echec': tache.system.nb_jet_echec + (rollData.rolled.isSuccess ? 0 : 1),
'system.difficulte': tache.system.difficulte - (rollData.rolled.isETotal ? 1 : 0), 'system.difficulte': tache.system.difficulte - (rollData.rolled.isETotal ? 1 : 0),
}, {render:true}) }, { render: true })
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
if (rollOptions?.onRollAutomate) { await actor.santeIncDec("fatigue", tache.system.fatigue)
await rollOptions.onRollAutomate(rollData)
} }
} }
} }

View File

@@ -63,5 +63,5 @@ export class RollType {
* @returns undefined ou une structure contenant les informations requise pour afficher * @returns undefined ou une structure contenant les informations requise pour afficher
*/ */
getResult(rollData, impacts) { return { messages: [] } } getResult(rollData, impacts) { return { messages: [] } }
onApplyImpacts(roll, impacts) { } onApplyImpacts(rollData, impacts) { }
} }

View File

@@ -3,11 +3,9 @@ import { Misc } from "../misc.js"
export const EXPORT_CSV_SCRIPTARIUM = 'export-csv-scriptarium' export const EXPORT_CSV_SCRIPTARIUM = 'export-csv-scriptarium'
export const ROLL_DIALOG_V2 = 'roll-dialog-v2' export const ROLL_DIALOG_V2 = 'roll-dialog-v2'
export const ROLL_DIALOG_V2_TEST = 'roll-dialog-v2-test'
const OPTIONS_AVANCEES = [ const OPTIONS_AVANCEES = [
{ group: 'Fenêtres', name: ROLL_DIALOG_V2, descr: "Utiliser les nouvelles fenêtres de jet", default: false }, { group: 'Fenêtres', name: ROLL_DIALOG_V2, descr: "Utiliser les nouvelles fenêtres de jet", default: false },
{ group: 'Fenêtres', name: ROLL_DIALOG_V2_TEST, descr: "Mode de test des nouvelles fenêtres", default: false },
{ group: 'Menus', name: EXPORT_CSV_SCRIPTARIUM, descr: "Proposer le menu d'export csv Scriptarium", default: false }, { group: 'Menus', name: EXPORT_CSV_SCRIPTARIUM, descr: "Proposer le menu d'export csv Scriptarium", default: false },
] ]

View File

@@ -3,11 +3,12 @@
</h4> </h4>
<p data-combatid="{{combatId}}" data-combatmessage="actor-turn-summary">{{blessuresStatus}}</p> <p data-combatid="{{combatId}}" data-combatmessage="actor-turn-summary">{{blessuresStatus}}</p>
<p>Son état général est de : {{etatGeneral}} {{#if isSonne}} et est <strong>sonné</strong>{{/if}}</p> <p>Son état général est de : {{etatGeneral}} {{#if isSonne}} et est <strong>sonné</strong>{{/if}}</p>
{{#if isGrave}} {{#if blessuresGraves}}
<p>{{alias}} souffre de Blessure(s) Grave(s) : n'oubliez pas de faire un Jet de Vie toutes les SC ({{SConst}}) minutes. Un point d'Endurance a été retiré automatiquement.</p> <p>{{alias}} souffre {{#if (eq blessuresGraves 1)}}d'une <strong>blessure grave</strong>{{else}}
de {{blessuresGraves}} <strong>blessures graves</strong>{{/if}} : n'oubliez pas de faire un Jet de Vie toutes les SC ({{SConst}}) minutes. Les pertes d'endurance sont automatiques à la fin du round.</p>
{{/if}} {{/if}}
{{#if isCritique}} {{#if isCritique}}
<p>{{alias}} souffre d'une <strong>Blessure Critique</strong> : faites un <p>{{alias}} souffre d'une <strong>blessure critique</strong> : faites un
<a class="chat-card-button chat-jet-vie" <a class="chat-card-button chat-jet-vie"
data-tokenId="{{tokenId}}" data-tokenId="{{tokenId}}"
data-actorId="{{actorId}}">Jet de Vie.<a></p> data-actorId="{{actorId}}">Jet de Vie.<a></p>

View File

@@ -11,8 +11,13 @@
{{#if (eq active.surprise.key 'totale')}} {{#if (eq active.surprise.key 'totale')}}
<span><strong>{{active.name}}</strong> est totalement surpris</span> <span><strong>{{active.name}}</strong> est totalement surpris</span>
{{else}} {{else}}
{{log 'attackerRoll' attackerRoll}}
<span><strong>{{active.name}}</strong> doit se défendre <span><strong>{{active.name}}</strong> doit se défendre
{{~#if (eq active.surprise.key 'demi')}} avec une significative {{/if}} d'une attaque {{~#if (eq active.surprise.key 'demi')}} avec une significative {{/if}} d'une
{{#if (eq attackerRoll.current.attaque.tactique.key 'charge')}}charge
{{else if (eq attackerRoll.current.attaque.tactique.key 'feinte')}}feinte
{{else}}attaque
{{/if}}
{{~#if attackerRoll.particuliere}} <strong>particulière en {{~#if attackerRoll.particuliere}} <strong>particulière en
{{~#if (eq attackerRoll.particuliere 'finesse')}} finesse {{~#if (eq attackerRoll.particuliere 'finesse')}} finesse
{{else if (eq attackerRoll.particuliere 'force')}} force {{else if (eq attackerRoll.particuliere 'force')}} force

View File

@@ -8,9 +8,9 @@
<hr> <hr>
<span> <span>
{{#if jetVie.rolled.isSuccess}} {{#if jetVie.rolled.isSuccess}}
{{alias}} a réussi son jet d'éthylisme, il a consommé {{doses}} doses sans effet. {{alias}} a réussi son jet d'éthylisme, {{doses}} doses ont été consommées sans effet.
{{else}} {{else}}
{{alias}} a échoué son jet d'éthylisme, il est maintenant {{nomEthylisme}} ({{ajustementEthylique}}). {{alias}} a échoué son jet d'éthylisme et est maintenant {{nomEthylisme}} ({{ajustementEthylique}}).
{{/if}} {{/if}}
</span> </span>
{{#if jetVie.rolled.isEchec}} {{#if jetVie.rolled.isEchec}}
@@ -19,8 +19,8 @@
{{alias}} perd {{perteEndurance.perte}} points d'endurance. {{#if perteEndurance.perteVie}}<br/>Il tombe inconscient et perd un point de vie.{{/if}} {{alias}} perd {{perteEndurance.perte}} points d'endurance. {{#if perteEndurance.perteVie}}<br/>Il tombe inconscient et perd un point de vie.{{/if}}
{{#if jetMoral}} {{#if jetMoral}}
<br/>Jet de moral {{#if jetMoral.succes}}réussi{{else}}manqué{{/if}} en situation heureuse ({{jetMoral.jet}}/{{jetMoral.difficulte}}). <br/>Jet de moral {{#if jetMoral.succes}}réussi{{else}}manqué{{/if}} en situation heureuse ({{jetMoral.jet}}/{{jetMoral.difficulte}}).
{{#if (gt jetMoral.ajustement 0)}}L'alcool met {{alias}} en joie. Il gagne un point de moral. {{#if (gt jetMoral.ajustement 0)}}L'alcool met en joie {{alias}} qui gagne un point de moral.
{{else if (lt jetMoral.ajustement 0)}}{{alias}} a l'alcool triste. Il perd un point de moral. {{else if (lt jetMoral.ajustement 0)}}{{alias}} a l'alcool triste et perd un point de moral.
{{else}}{{alias}} garde son moral.{{/if}} {{else}}{{alias}} garde son moral.{{/if}}
{{/if}} {{/if}}
</span> </span>

View File

@@ -7,7 +7,7 @@
{{active.name}} fait un jet {{active.name}} fait un jet
{{#if current.comp.key}}de {{current.comp.label}}{{/if}} {{#if current.comp.key}}de {{current.comp.label}}{{/if}}
{{#if type.appreciation}}d'appréciation {{#if type.appreciation}}d'appréciation
{{else if (ne current.comp.key '')}}sans compétence {{else if (eq current.comp.key '')}}sans compétence
{{/if}} {{/if}}
</div> </div>