Compare commits

..

4 Commits

Author SHA1 Message Date
2bbf606f30 Fix User ... lacks permission to update
Correction de l'erreur qui était affichée chez les joueurs lors de
hooks utilisés pour effectuer des modifications sur des
documents:

- ChatMessage, ajout de flags pour l'heure
- Item au sein d'un Actor pour mettre à jour certains éléments
2024-06-11 02:49:18 +02:00
a385b98126 Various fixes for v12 2024-06-07 11:12:00 +02:00
8775df5285 Merge pull request 'Corrections v12' (#700) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#700
2024-06-01 09:11:18 +02:00
e19577eab2 Fix for v12 2024-05-31 21:48:19 +02:00
24 changed files with 115 additions and 85 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

4
icons/heures/.directory Normal file
View File

@ -0,0 +1,4 @@
[Dolphin]
Timestamp=2024,5,29,20,57,41.954
Version=4
VisibleRoles=Details_text,Details_size,Details_modificationtime,Details_creationtime,CustomizedDetails

View File

@ -38,10 +38,22 @@ export class ChatVente {
return foundry.utils.mergeObject(detail, { acheteur })
}
static async diminuerQuantiteAchatVente(chatMessageId, quantite) {
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)
static async setDetailAchatVente(chatMessage, vente) {
await chatMessage?.setFlag(SYSTEM_RDD, NB_LOTS, vente.nbLots)
await chatMessage?.setFlag(SYSTEM_RDD, DETAIL_VENTE, {
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente);
chatMessage.update({ content: html });
chatMessage.render(true);
}
static async displayAchatVente(vente) {
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente);
const chatMessage = await ChatMessage.create(RdDUtility.chatDataSetup(html))
await chatMessage.setFlag(SYSTEM_RDD, NB_LOTS, vente.nbLots)
await chatMessage.setFlag(SYSTEM_RDD, DETAIL_VENTE, {
item: vente.item,
properties: vente.item.getProprietes(),
vendeurId: vente.vendeurId,
@ -50,21 +62,4 @@ export class ChatVente {
prixLot: vente.prixLot
})
}
static async diminuerQuantite(chatMessageId, quantite) {
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.html', vente);
chatMessage.update({ content: html });
chatMessage.render(true);
}
static async displayAchatVente(venteData) {
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', venteData);
const chatMessage = await ChatMessage.create(RdDUtility.chatDataSetup(html))
await ChatVente.setDetailAchatVente(chatMessage, venteData)
}
}

View File

@ -902,7 +902,7 @@ export class RdDActor extends RdDBaseActorSang {
async ajouterRefoulement(value = 1, refouler) {
let refoulement = this.system.reve.refoulement.value + value;
const roll = new Roll("1d20");
await roll.evaluate({ async: true });
await roll.evaluate();
await roll.toMessage({ flavor: `${this.name} refoule ${refouler} pour ${value} points de refoulement (total: ${refoulement})` });
if (roll.total <= refoulement) {
refoulement = 0;
@ -991,9 +991,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
buildTMRInnaccessible() {
return this.items[TYPES.casetmr]
.filter(it => EffetsDraconiques.isInnaccessible(it))
.map(it => it.system.coord)
return this.items.filter(it => it.type == TYPES.casetmr).filter(it => EffetsDraconiques.isInnaccessible(it)).map(it => it.system.coord)
}
/* -------------------------------------------- */
@ -2628,13 +2626,13 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async resetItemUse() {
await this.unsetFlag(SYSTEM_RDD, 'itemUse');
await this.setFlag(SYSTEM_RDD, 'itemUse', {});
}
/* -------------------------------------------- */
async incDecItemUse(itemId, inc = 1) {
let itemUse = foundry.utils.duplicate(this.getFlag(SYSTEM_RDD, 'itemUse') ?? {});
const currentItemUse = this.getFlag(SYSTEM_RDD, 'itemUse');
let itemUse = currentItemUse ? foundry.utils.duplicate(currentItemUse) : {};
itemUse[itemId] = (itemUse[itemId] ?? 0) + inc;
await this.setFlag(SYSTEM_RDD, 'itemUse', itemUse);
console.log("ITEM USE INC", inc, itemUse);

View File

@ -32,10 +32,10 @@ export class RdDBaseActor extends Actor {
}
static init() {
Hooks.on("preUpdateItem", (item, change, options, id) => RdDBaseActor.getParentActor(item)?.onPreUpdateItem(item, change, options, id));
Hooks.on("createItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onCreateItem(item, options, id));
Hooks.on("deleteItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onDeleteItem(item, options, id));
Hooks.on("updateActor", (actor, change, options, actorId) => actor.onUpdateActor(change, options, actorId));
Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id))
Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id))
Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id))
Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(actor)?.onUpdateActor(change, options, actorId))
}
static onSocketMessage(sockmsg) {
@ -82,10 +82,6 @@ export class RdDBaseActor extends Actor {
static extractActorMin = (actor) => { return { id: actor?.id, type: actor?.type, name: actor?.name, img: actor?.img }; };
static getParentActor(document) {
return document?.parent instanceof Actor ? document.parent : undefined
}
/**
* Cette methode surcharge Actor.create() pour ajouter si besoin des Items par défaut:
* compétences et monnaies.
@ -381,7 +377,7 @@ export class RdDBaseActor extends Actor {
ChatUtility.removeChatMessageId(achat.chatMessageIdVente);
}
else if (achat.chatMessageIdVente) {
await ChatVente.diminuerQuantite(achat.chatMessageIdVente, achat.choix.nombreLots)
await ChatVente.diminuerQuantiteAchatVente(achat.chatMessageIdVente, achat.choix.nombreLots)
}
}
}

View File

@ -149,14 +149,13 @@ export class ChatUtility {
}
static async setMessageData(chatMessage, key, flag) {
if (flag) {
await chatMessage.setFlag(SYSTEM_RDD, key, JSON.stringify(flag));
if (flag && chatMessage.isAuthor) {
await chatMessage.setFlag(SYSTEM_RDD, key, flag)
}
}
static getMessageData(chatMessage, key) {
const json = chatMessage.getFlag(SYSTEM_RDD, key);
return json ? JSON.parse(json) : undefined;
return chatMessage.getFlag(SYSTEM_RDD, key);
}
static getChatMessage(event) {
@ -175,6 +174,8 @@ export class ChatUtility {
}
static async onCreateChatMessage(chatMessage, options, id) {
await chatMessage.setFlag(SYSTEM_RDD, 'rdd-timestamp', game.system.rdd.calendrier.getTimestamp());
if (chatMessage.isAuthor) {
await chatMessage.setFlag(SYSTEM_RDD, 'rdd-timestamp', game.system.rdd.calendrier.getTimestamp());
}
}
}

View File

@ -117,8 +117,8 @@ export class RdDCoeur {
}
ChatUtility.removeChatMessageId(infoCoeur.chatMessageId)
infoCoeur.target.jetTendre = (await (new Roll('1d6').evaluate({ async: true }))).total
infoCoeur.source.jetTendre = (await (new Roll('1d6').evaluate({ async: true }))).total
infoCoeur.target.jetTendre = (await (new Roll('1d6').evaluate())).total
infoCoeur.source.jetTendre = (await (new Roll('1d6').evaluate())).total
const diff = Math.abs(infoCoeur.source.jetTendre - infoCoeur.target.jetTendre)
for (let amoureux of [infoCoeur.source, infoCoeur.target]) {
const actorAmoureux = game.actors.get(amoureux.actor.id);

View File

@ -29,5 +29,25 @@ export const RDD_CONFIG = {
"incarne": "Incarnée",
"nonincarne": "Non Incarnée",
"blurette": "Blurette"
}
},
heuresRdD : [
{value : "vaisseau", label: "Vaisseau", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd01.webp"},
{value : "sirene", label: "Sirène", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd02.webp"},
{value : "faucon", label: "Faucon", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd03.webp"},
{value : "couronne", label: "Couronne", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd04.webp"},
{value : "dragon", label: "Dragon", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd05.webp"},
{value : "epees", label: "Epées", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd06.webp"},
{value : "lyre", label: "Lyre", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd07.webp"},
{value : "serpent", label: "Serpent", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd08.webp"},
{value : "poissonacrobate", label: "Poisson Acrobate", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd09.webp"},
{value : "araignee", label: "Araignée", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd10.webp"},
{value : "roseau", label: "Roseau", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd11.webp"},
{value : "chateaudormant", label: "Chateau Dormant", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd12.webp"}
],
raretes: [
{value: "Commune", label: "Commune"},
{value: "Frequente", label: "Fréquente"},
{value: "Rare", label: "Rare"},
{value: "Rarissime", label: "Rarissime"}
]
}

View File

@ -108,8 +108,8 @@ export class RdDItemSheet extends ItemSheet {
const competences = await SystemCompendiums.getCompetences('personnage');
formData.categories = this.item.getCategories()
if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') {
formData.caracList = foundry.utils.duplicate(game.system.model.Actor.personnage.carac)
formData.caracList["reve-actuel"] = foundry.utils.duplicate(game.system.model.Actor.personnage.reve.reve)
formData.caracList = foundry.utils.duplicate(game.model.Actor.personnage.carac)
formData.caracList["reve-actuel"] = foundry.utils.duplicate(game.model.Actor.personnage.reve.reve)
formData.competences = competences;
}
if (this.item.type == 'arme') {

View File

@ -48,7 +48,7 @@ export class RdDItemBlessure extends RdDItem {
let lostEndurance = 0
let lostVie = 0
if (definition.endurance) {
lostEndurance = new Roll(definition.endurance).roll({async: false}).total;
lostEndurance = await new Roll(definition.endurance).roll().total;
actor.santeIncDec("endurance", -Number(lostEndurance));
}
if (definition.vie) {

View File

@ -567,7 +567,7 @@ export class Migrations {
if (currentVersion.startsWith("v")) {
currentVersion = currentVersion.substring(1)
}
if (isNewerVersion(game.system.version, currentVersion)) {
if (foundry.utils.isNewerVersion(game.system.version, currentVersion)) {
// if (true) { /* comment previous and uncomment here to test before upgrade */
const migrations = Migrations.getMigrations().filter(m => isNewerVersion(m.version, currentVersion));
if (migrations.length > 0) {

View File

@ -166,15 +166,39 @@ export class Misc {
}
static firstConnectedGM() {
return game.users.filter(u => u.isGM && u.active).sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active);
return game.users.sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active);
}
static connectedGMs() {
return game.users.filter(u => u.isGM && u.active);
}
static isOwnerPlayer(actor, user = undefined) {
return actor.testUserPermission(user ?? game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER)
/**
* This helper method allows to get the docuument, for a single user (either first connected GM, or the owner
* if there is no connected GMs), or else return undefined.
*
* This allows for example update hooks that should apply modifications to actors to be called only for one
* user (preventing the "User ... lacks permission to update Item" that was occuring on hooks when Item updates
* were triggering other changes)
*
* @param {*} document the Document with is potentially an Actor
* @returns the actor if either the game.user is the first connected GM, or if the game.user is the owner
* and there is no connected GM
*/
static documentIfResponsible(document) {
if (document instanceof Document) {
if (Misc.isUniqueConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) {
return document
}
}
return undefined
}
static isOwnerPlayerOrUniqueConnectedGM(actor, user = undefined) {
return Misc.isOwnerPlayer(actor, user) ?? Misc.isUniqueConnectedGM();
static isOwnerPlayer(actor) {
return actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER)
}
static isOwnerPlayerOrUniqueConnectedGM(actor) {
return Misc.isOwnerPlayer(actor) ?? Misc.isUniqueConnectedGM();
}
/**

View File

@ -119,7 +119,7 @@ export class RdDCombatManager extends Combat {
//console.log("Combatat", c);
const roll = combatant.getInitiativeRoll(rollFormula);
if (!roll.total) {
roll.evaluate({ async: false });
await roll.evaluate();
}
const total = Math.max(roll.total, 0.00);
console.log("Compute init for", rollFormula, roll, total, combatant);

View File

@ -138,7 +138,7 @@ export class RdDDice {
static async roll(formula, options = { showDice: SHOW_DICE, rollMode: undefined }) {
const roll = new Roll(RdDDice._formulaOrFake(formula, options));
await roll.evaluate({ async: true });
await roll.evaluate();
await this.showDiceSoNice(roll, options);
return roll;
}
@ -216,7 +216,7 @@ export class RdDDice {
static async fakeD10(faces) {
let roll = new Roll(`1d${faces}`);
await roll.evaluate({ async: true });
await roll.evaluate();
return roll.total;
}

View File

@ -56,7 +56,7 @@ const temperatures = [
export class RdDMeteo {
static async getForce() {
const roll = new Roll(`1dr`);
await roll.evaluate({ async: true });
await roll.evaluate();
return roll.total;
}
@ -67,14 +67,14 @@ export class RdDMeteo {
static async getTemperature() {
const degre = await RdDMeteo.getForce();
const rollChaudFroid = new Roll('1d2');
await rollChaudFroid.evaluate({ async: true });
await rollChaudFroid.evaluate();
const chaudFroid = rollChaudFroid.total == 1;
return chaudFroid.total ? degre : -degre;
}
static async getDirection(direction) {
const roll = new Roll(`1d16`);
await roll.evaluate({ async: true });
await roll.evaluate();
switch (roll.total % 16) {
case 0: return 'Nord';
case 1: return 'Nord Nord Est';

View File

@ -300,6 +300,14 @@ export class RdDUtility {
Handlebars.registerHelper('plusMoins', diff => (diff > 0 ? '+' : '') + Math.round(diff))
Handlebars.registerHelper('experienceLog-topic', topic => ExperienceLog.labelTopic(topic));
// Handle v12 removal of this helper
Handlebars.registerHelper('select', function (selected, options) {
const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));
const rgx = new RegExp(' value=[\"\']' + escapedValue + '[\"\']');
const html = options.fn(this);
return html.replace(rgx, "$& selected");
});
return loadTemplates(templatePaths);
}
@ -624,7 +632,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static async _evaluatePerte(formula, over20) {
let perte = new Roll(formula, { over20: over20 });
await perte.evaluate({ async: true });
await perte.evaluate();
return perte.total;
}

View File

@ -17,7 +17,7 @@ export class AutoAdjustDarkness {
static async adjust(darkness) {
if (AutoAdjustDarkness.isAuto()) {
const scene = game.scenes.viewed;
if (scene?.globalLight && scene?.tokenVision) {
if (scene?.environment?.globalLight?.enabled && scene?.tokenVision) {
await scene.update({ darkness });
}
}

View File

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

View File

@ -2,8 +2,6 @@
<div class="flexrow">
<input type="number" name="{{path}}.nombre" value="{{nombre}}" data-dtype="Number"/>
<select name="{{path}}.unite" data-dtype="String" >
{{#select unite}}
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-periode.html"}}
{{/select}}
{{selectOptions (timestamp-formulesPeriode) selected=unite labelAttr="label" nameAttr="code" valueAttr="code"}}
</select>
</div>

View File

@ -6,9 +6,7 @@
type="number" data-dtype="Number" min="1" max="28"
name="{{path}}.jourDuMois" value="{{jourDuMois}}" />
<select {{#if disabled}}{{disabled}}{{/if}} name="{{path}}.mois" class="calendar-signe-heure" data-dtype="String">
{{#select mois.key}}
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-heures.html"}}
{{/select}}
{{select config.heuresRdD selected=mois.key labelAttr="label" nameAttr="value" valueAttr="value"}}
</select>
{{timestamp-imgSigne mois}}
<input {{#if disabled}}{{disabled}}{{/if}} type="number" class="number-x2" name="{{path}}.annee" value="{{annee}}" data-dtype="Number"/>
@ -17,9 +15,7 @@
<label></label>
<label>heure</label>
<select {{#if disabled}}{{disabled}}{{/if}} name="{{path}}.heure" class="calendar-signe-heure" data-dtype="String">
{{#select heure.key}}
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-heures.html"}}
{{/select}}
{{select config.heuresRdD selected=heure.key labelAttr="label" nameAttr="value" valueAttr="value"}}
</select>
{{timestamp-imgSigne heure}}
<input {{#if disabled}}{{disabled}}{{/if}} type="number" class="number-x2" name="{{path}}.minute" value="{{minute}}" data-dtype="Number"/>

View File

@ -26,9 +26,7 @@
</label>
<span class="flexrow">
<select name="milieu-{{key}}-rarete" class="environnement-rarete flex-shrink" data-dtype="String">
{{#select env.rarete}}
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-rarete.html"}}
{{/select}}
{{selectOptions config.rarete selected=env.rarete labelAttr="label" valueAttr="value" nameAttr="value"}}
</select>
{{rangePicker name="milieu-{{key}}-frequence" value=env.frequence min=(rarete-getChamp env.rarete 'min') max=(rarete-getChamp env.rarete 'max') step=1}}
<label>[{{rarete-getChamp env.rarete 'min'}}-{{rarete-getChamp env.rarete 'max'}}]</label>

View File

@ -7,7 +7,7 @@
{{else}}
<input class="resource-content select-effect" type="checkbox" name="{{effect.id}}" {{#if effect.active}}checked{{/if}}/>
{{/if}}
<img class="button-effect-img" height="16" width="16" src="{{effect.icon}}" data-tooltip="{{localize effect.name}}" />
<img class="button-effect-img" height="16" width="16" src="{{effect.img}}" data-tooltip="{{localize effect.name}}" />
<label>{{localize effect.name}}</label>
</li>
{{/each}}

View File

@ -3,7 +3,7 @@
{{#each systemCompendiums as |definition key|}}
<li class="flexrow">
<label>{{definition.label}}</label>
<select data-dtype="String" class="system-compendium-setting flex-grow-2" data-compendium="{{definition.compendium}}" >
<select data-dtype="String" class="system-compendium-setting flex-grow-2" data-compendium="{{definition.compendium}}" >
{{#select definition.value}}
{{#each @root.availableCompendiums as |available key|}}
{{#if (eq available.type definition.type)}}

View File

@ -1,19 +1,11 @@
<div>
<label>Conditions</label>
<select name="diffConditions" data-dtype="Number">
{{#select '0'}}
{{#each actorAstrologie.ajustements as |ajustement|}}
<option value={{ajustement}}>{{ajustement}}</option>
{{/each}}
{{/select}}
{{selectOptions actorAstrologie.ajustements selected='0'}}
</select>
<label>&nbsp;&nbsp;Jours</label>
<select name="joursAstrologie" data-dtype="Number">
{{#select ''}}
{{#each dates as |date|}}
<option value={{date.index}}>{{date.label}}</option>
{{/each}}
{{/select}}
{{selectOptions dates selected='' labelAttr='label' valueAttr='index' nameAttr='index'}}
</select>
<label>
&nbsp;&nbsp;Etat Général: {{actorAstrologie.etat}}