diff --git a/dist/module/actor/entity.js b/dist/module/actor/entity.js
index 8a87cce4..28d83f90 100644
--- a/dist/module/actor/entity.js
+++ b/dist/module/actor/entity.js
@@ -480,15 +480,16 @@ export class OseActor extends Actor {
const rollData = {
actor: this.data,
item: attData.item,
+ itemId: attData.item._id,
roll: {
type: options.type,
thac0: thac0,
dmg: dmgParts,
save: attData.roll.save,
- target: attData.roll.target,
+ target: attData.roll.target
+
},
};
-
// Roll and return
return OseDice.Roll({
event: options.event,
@@ -497,6 +498,7 @@ export class OseActor extends Actor {
skipDialog: options.skipDialog,
speaker: ChatMessage.getSpeaker({ actor: this }),
flavor: label,
+ flags: {ose: {roll: 'attack', itemId: attData.item._id}},
title: label,
});
}
diff --git a/dist/module/combat.js b/dist/module/combat.js
index 7da4572c..7bac9428 100644
--- a/dist/module/combat.js
+++ b/dist/module/combat.js
@@ -2,43 +2,55 @@ export class OseCombat {
static STATUS_SLOW = -789;
static STATUS_DIZZY = -790;
- static rollInitiative(combat, data) {
+ static debounce(callback, wait) {
+ let timeoutId = null;
+ return (...args) => {
+ window.clearTimeout(timeoutId);
+ timeoutId = window.setTimeout(() => {
+ callback.apply(null, args);
+ }, wait);
+ };
+ }
+ static async rollInitiative(combat, data) {
// Check groups
data.combatants = [];
let groups = {};
combat.data.combatants.forEach((cbt) => {
- const group = cbt.getFlag("ose", "group");
+ const group = cbt.getFlag('ose', 'group');
groups[group] = { present: true };
data.combatants.push(cbt);
});
-
// Roll init
- Object.keys(groups).forEach((group) => {
- let roll = new Roll("1d6").evaluate({async: false});
- roll.toMessage({
- flavor: game.i18n.format('OSE.roll.initiative', { group: CONFIG["OSE"].colors[group] }),
+ for(let group in groups){
+ // Object.keys(groups).forEach((group) => {
+ let roll = new Roll('1d6').evaluate({ async: false });
+ await roll.toMessage({
+ flavor: game.i18n.format('OSE.roll.initiative', { group: CONFIG['OSE'].colors[group] })
});
groups[group].initiative = roll.total;
- });
-
+ // });
+ };
// Set init
for (let i = 0; i < data.combatants.length; ++i) {
- if (!data.combatants[i].actor) {
- return;
- }
- if (data.combatants[i].actor.data.data.isSlow) {
- data.combatants[i].update({initiative: OseCombat.STATUS_SLOW});
- } else {
- const group = data.combatants[i].getFlag("ose", "group");
- data.combatants[i].update({initiative: groups[group].initiative});
+ if (game.user.isGM) {
+ if (!data.combatants[i].actor) {
+ return;
+ }
+ if (data.combatants[i].actor.data.data.isSlow) {
+ await data.combatants[i].update({ initiative: OseCombat.STATUS_SLOW });
+ } else {
+ const group = data.combatants[i].getFlag('ose', 'group');
+ this.debounce(data.combatants[i].update({ initiative: groups[group].initiative }), 500);
+ }
}
}
- combat.setupTurns();
+
+ await combat.setupTurns();
}
static async resetInitiative(combat, data) {
- let reroll = game.settings.get("ose", "rerollInitiative");
- if (!["reset", "reroll"].includes(reroll)) {
+ let reroll = game.settings.get('ose', 'rerollInitiative');
+ if (!['reset', 'reroll'].includes(reroll)) {
return;
}
combat.resetAll();
@@ -47,98 +59,98 @@ export class OseCombat {
static async individualInitiative(combat, data) {
let updates = [];
let messages = [];
- combat.data.combatants.forEach((c, i) => {
+
+ for (let i = 0; i < combat.data.combatants.size; i++) {
+ let c = combat.data.combatants.contents[i];
+
// This comes from foundry.js, had to remove the update turns thing
// Roll initiative
- const cf = c._getInitiativeFormula(c);
- const roll = c.getInitiativeRoll(cf);
+ const cf = await c._getInitiativeFormula(c);
+ const roll = await c.getInitiativeRoll(cf).evaluate({ async: true });
+
let value = roll.total;
if (combat.settings.skipDefeated && c.defeated) {
value = OseCombat.STATUS_DIZZY;
}
- updates.push({ _id: c.id, initiative: value });
+ const data = { _id: c.id, initiative: value };
+
+ updates.push(data);
// Determine the roll mode
- let rollMode = game.settings.get("core", "rollMode");;
- if ((c.token.hidden || c.hidden) && (rollMode === "roll")) rollMode = "gmroll";
+ let rollMode = game.settings.get('core', 'rollMode');
+ if ((c.token.hidden || c.hidden) && rollMode === 'roll') rollMode = 'gmroll';
// Construct chat message data
- // Construct chat message data
- let messageData = foundry.utils.mergeObject({
- speaker: {
- scene: combat.scene.id,
- actor: c.actor?.id,
- token: c.token?.id,
- alias: c.name
+ let messageData = foundry.utils.mergeObject(
+ {
+ speaker: {
+ scene: combat.scene.id,
+ actor: c.actor?.id,
+ token: c.token?.id,
+ alias: c.name
+ },
+ flavor: game.i18n.format('OSE.roll.individualInit', { name: c.token.name }),
+ flags: { 'ose.initiativeRoll': true }
},
- flavor: game.i18n.format('OSE.roll.individualInit', { name: c.token.name }),
- flags: {"ose.initiativeRoll": true}
- }, {});
- const chatData = roll.toMessage(messageData, { rollMode: c.hidden && (rollMode === "roll") ? "gmroll" : rollMode, create: false });
-
- if (i > 0) chatData.sound = null; // Only play 1 sound for the whole set
+ {}
+ );
+ const chatData = await roll.toMessage(messageData, {
+ rollMode: c.hidden && rollMode === 'roll' ? 'gmroll' : rollMode,
+ create: false
+ });
+ if (i > 0) chatData.sound = null; // Only play 1 sound for the whole set
messages.push(chatData);
- });
-
- await combat.updateEmbeddedDocuments("Combatant", updates);
+ }
+ if (game.user.isGM) {
+ await combat.updateEmbeddedDocuments('Combatant', updates);
+ }
await ChatMessage.implementation.create(messages);
data.turn = 0;
}
static format(object, html, user) {
- html.find(".initiative").each((_, span) => {
+ html.find('.initiative').each((_, span) => {
span.innerHTML =
- span.innerHTML == `${OseCombat.STATUS_SLOW}`
- ? ''
- : span.innerHTML;
- span.innerHTML =
- span.innerHTML == `${OseCombat.STATUS_DIZZY}`
- ? ''
- : span.innerHTML;
+ span.innerHTML == `${OseCombat.STATUS_SLOW}` ? '' : span.innerHTML;
+ span.innerHTML = span.innerHTML == `${OseCombat.STATUS_DIZZY}` ? '' : span.innerHTML;
});
- html.find(".combatant").each((_, ct) => {
+ html.find('.combatant').each((_, ct) => {
// Append spellcast and retreat
- const controls = $(ct).find(".combatant-controls .combatant-control");
+ const controls = $(ct).find('.combatant-controls .combatant-control');
const cmbtant = object.viewed.combatants.get(ct.dataset.combatantId);
- const moveInCombat = cmbtant.getFlag("ose", "moveInCombat");
- const preparingSpell = cmbtant.getFlag("ose", "prepareSpell");
- const moveActive = moveInCombat ? "active" : "";
- controls.eq(1).after(
- ``
- );
- const spellActive = preparingSpell ? "active" : "";
- controls.eq(1).after(
- ``
- );
+ const moveInCombat = cmbtant.getFlag('ose', 'moveInCombat');
+ const preparingSpell = cmbtant.getFlag('ose', 'prepareSpell');
+ const moveActive = moveInCombat ? 'active' : '';
+ controls.eq(1).after(``);
+ const spellActive = preparingSpell ? 'active' : '';
+ controls
+ .eq(1)
+ .after(``);
});
OseCombat.announceListener(html);
- let init = game.settings.get("ose", "initiative") === "group";
+ let init = game.settings.get('ose', 'initiative') === 'group';
if (!init) {
return;
}
html.find('.combat-control[data-control="rollNPC"]').remove();
html.find('.combat-control[data-control="rollAll"]').remove();
- let trash = html.find(
- '.encounters .combat-control[data-control="endCombat"]'
- );
- $(
- ''
- ).insertBefore(trash);
+ let trash = html.find('.encounters .combat-control[data-control="endCombat"]');
+ $('').insertBefore(trash);
- html.find(".combatant").each((_, ct) => {
+ html.find('.combatant').each((_, ct) => {
// Can't roll individual inits
- $(ct).find(".roll").remove();
+ $(ct).find('.roll').remove();
// Get group color
const cmbtant = object.viewed.combatants.get(ct.dataset.combatantId);
- let color = cmbtant.getFlag("ose", "group");
+ let color = cmbtant.getFlag('ose', 'group');
// Append colored flag
- let controls = $(ct).find(".combatant-controls");
+ let controls = $(ct).find('.combatant-controls');
controls.prepend(
``
);
@@ -147,53 +159,52 @@ export class OseCombat {
}
static updateCombatant(combatant, data) {
- let init = game.settings.get("ose", "initiative");
+ let init = game.settings.get('ose', 'initiative');
// Why do you reroll ?
if (combatant.actor.data.data.isSlow) {
data.initiative = -789;
return;
}
- if (data.initiative && init == "group") {
+ if (data.initiative && init == 'group') {
let groupInit = data.initiative;
- const cmbtGroup = combatant.getFlag("ose", "group");
+ const cmbtGroup = combatant.getFlag('ose', 'group');
// Check if there are any members of the group with init
game.combats.viewed.combatants.forEach((ct) => {
- const group = ct.getFlag("ose", "group");
- if (
- ct.initiative &&
- ct.initiative != "-789.00" &&
- ct.id != data.id &&
- group == cmbtGroup
- ) {
+ const group = ct.getFlag('ose', 'group');
+ if (ct.initiative && ct.initiative != '-789.00' && ct.id != data.id && group == cmbtGroup) {
// Set init
- combatant.update({initiative: parseInt(ct.initiative)});
+ if (game.user.isGM) {
+ combatant.update({ initiative: parseInt(groupInit) });
+ }
}
});
}
}
static announceListener(html) {
- html.find(".combatant-control.prepare-spell").click((ev) => {
+ html.find('.combatant-control.prepare-spell').click((ev) => {
ev.preventDefault();
// Toggle spell announcement
- let id = $(ev.currentTarget).closest(".combatant")[0].dataset.combatantId;
+ let id = $(ev.currentTarget).closest('.combatant')[0].dataset.combatantId;
let isActive = ev.currentTarget.classList.contains('active');
const combatant = game.combat.combatants.get(id);
- combatant.setFlag("ose", "prepareSpell", !isActive);
+ combatant.setFlag('ose', 'prepareSpell', !isActive);
});
- html.find(".combatant-control.move-combat").click((ev) => {
+ html.find('.combatant-control.move-combat').click((ev) => {
ev.preventDefault();
// Toggle spell announcement
- let id = $(ev.currentTarget).closest(".combatant")[0].dataset.combatantId;
+ let id = $(ev.currentTarget).closest('.combatant')[0].dataset.combatantId;
let isActive = ev.currentTarget.classList.contains('active');
const combatant = game.combat.combatants.get(id);
- combatant.setFlag("ose", "moveInCombat", !isActive);
- })
+ if (game.user.isGM) {
+ combatant.setFlag('ose', 'moveInCombat', !isActive);
+ }
+ });
}
static addListeners(html) {
// Cycle through colors
- html.find(".combatant-control.flag").click((ev) => {
+ html.find('.combatant-control.flag').click((ev) => {
if (!game.user.isGM) {
return;
}
@@ -205,9 +216,11 @@ export class OseCombat {
} else {
index++;
}
- let id = $(ev.currentTarget).closest(".combatant")[0].dataset.combatantId;
+ let id = $(ev.currentTarget).closest('.combatant')[0].dataset.combatantId;
const combatant = game.combat.combatants.get(id);
- combatant.setFlag("ose", "group", colors[index]);
+ if (game.user.isGM) {
+ combatant.setFlag('ose', 'group', colors[index]);
+ }
});
html.find('.combat-control[data-control="reroll"]').click((ev) => {
@@ -216,63 +229,67 @@ export class OseCombat {
}
let data = {};
OseCombat.rollInitiative(game.combat, data);
- game.combat.update({ data: data }).then(() => {
- game.combat.setupTurns();
- });
+ if (game.user.isGM) {
+ game.combat.update({ data: data }).then(() => {
+ game.combat.setupTurns();
+ });
+ }
});
}
static addCombatant(combat, data, options, id) {
let token = canvas.tokens.get(data.tokenId);
- let color = "black";
+ let color = 'black';
switch (token.data.disposition) {
case -1:
- color = "red";
+ color = 'red';
break;
case 0:
- color = "yellow";
+ color = 'yellow';
break;
case 1:
- color = "green";
+ color = 'green';
break;
}
data.flags = {
ose: {
- group: color,
- },
+ group: color
+ }
};
}
static activateCombatant(li) {
- const turn = game.combat.turns.findIndex(turn => turn.id === li.data('combatant-id'));
- game.combat.update({ turn: turn })
+ const turn = game.combat.turns.findIndex((turn) => turn.id === li.data('combatant-id'));
+ if (game.user.isGM) {
+ game.combat.update({ turn: turn });
+ }
}
static addContextEntry(html, options) {
options.unshift({
- name: "Set Active",
+ name: 'Set Active',
icon: '',
callback: OseCombat.activateCombatant
});
}
static async preUpdateCombat(combat, data, diff, id) {
- let init = game.settings.get("ose", "initiative");
- let reroll = game.settings.get("ose", "rerollInitiative");
+ let init = game.settings.get('ose', 'initiative');
+ let reroll = game.settings.get('ose', 'rerollInitiative');
if (!data.round) {
return;
}
if (data.round !== 1) {
- if (reroll === "reset") {
+ if (reroll === 'reset') {
OseCombat.resetInitiative(combat, data, diff, id);
return;
- } else if (reroll === "keep") {
+ } else if (reroll === 'keep') {
return;
}
}
- if (init === "group") {
+ if (init === 'group') {
OseCombat.rollInitiative(combat, data, diff, id);
- } else if (init === "individual") {
+ } else if (init === 'individual') {
OseCombat.individualInitiative(combat, data, diff, id);
}
}
diff --git a/dist/module/dice.js b/dist/module/dice.js
index 466adcd4..4dbf1b6d 100644
--- a/dist/module/dice.js
+++ b/dist/module/dice.js
@@ -188,23 +188,24 @@ export class OseDice {
static async sendAttackRoll({
parts = [],
data = {},
+ flags= {},
title = null,
flavor = null,
speaker = null,
form = null,
} = {}) {
const template = "systems/ose/dist/templates/chat/roll-attack.html";
-
let chatData = {
user: game.user.id,
speaker: speaker,
+ flags: flags
};
let templateData = {
title: title,
flavor: flavor,
data: data,
- config: CONFIG.OSE,
+ config: CONFIG.OSE
};
// Optionally include a situational bonus
@@ -367,6 +368,8 @@ export class OseDice {
flavor = null,
title = null,
chatMessage = true,
+ flags = {}
+
} = {}) {
let rolled = false;
const template = "systems/ose/dist/templates/chat/roll-dialog.html";
@@ -378,7 +381,6 @@ export class OseDice {
: game.settings.get("core", "rollMode"),
rollModes: CONFIG.Dice.rollModes,
};
-
let rollData = {
parts: parts,
data: data,
@@ -386,6 +388,7 @@ export class OseDice {
flavor: flavor,
speaker: speaker,
chatMessage: chatMessage,
+ flags: flags
};
if (skipDialog) {
return ["melee", "missile", "attack"].includes(data.roll.type)
diff --git a/dist/ose.js b/dist/ose.js
index d1e7d1ad..02ff0ca1 100644
--- a/dist/ose.js
+++ b/dist/ose.js
@@ -1,38 +1,39 @@
// Import Modules
-import { OseItemSheet } from "./module/item/item-sheet.js";
-import { OseActorSheetCharacter } from "./module/actor/character-sheet.js";
-import { OseActorSheetMonster } from "./module/actor/monster-sheet.js";
-import { preloadHandlebarsTemplates } from "./module/preloadTemplates.js";
-import { OseActor } from "./module/actor/entity.js";
-import { OseItem } from "./module/item/entity.js";
-import { OSE } from "./module/config.js";
-import { registerSettings } from "./module/settings.js";
-import { registerHelpers } from "./module/helpers.js";
-import * as chat from "./module/chat.js";
-import * as treasure from "./module/treasure.js";
-import * as macros from "./module/macros.js";
-import * as party from "./module/party.js";
-import { OseCombat } from "./module/combat.js";
-import * as renderList from "./module/renderList.js";
+import { OseItemSheet } from './module/item/item-sheet.js';
+import { OseActorSheetCharacter } from './module/actor/character-sheet.js';
+import { OseActorSheetMonster } from './module/actor/monster-sheet.js';
+import { preloadHandlebarsTemplates } from './module/preloadTemplates.js';
+import { OseActor } from './module/actor/entity.js';
+import { OseItem } from './module/item/entity.js';
+import { OSE } from './module/config.js';
+import { registerSettings } from './module/settings.js';
+import { registerHelpers } from './module/helpers.js';
+import * as chat from './module/chat.js';
+import * as treasure from './module/treasure.js';
+import * as macros from './module/macros.js';
+import * as party from './module/party.js';
+import { OseCombat } from './module/combat.js';
+import * as renderList from './module/renderList.js';
/* -------------------------------------------- */
/* Foundry VTT Initialization */
/* -------------------------------------------- */
-Hooks.once("init", async function () {
+Hooks.once('init', async function () {
/**
* Set an initiative formula for the system
* @type {String}
*/
CONFIG.Combat.initiative = {
- formula: "1d6 + @initiative.value",
- decimals: 2,
+ formula: '1d6 + @initiative.value',
+ decimals: 2
};
CONFIG.OSE = OSE;
game.ose = {
rollItemMacro: macros.rollItemMacro,
+ oseCombat: OseCombat
};
// Custom Handlebars helpers
@@ -45,22 +46,22 @@ Hooks.once("init", async function () {
CONFIG.Item.documentClass = OseItem;
// Register sheet application classes
- Actors.unregisterSheet("core", ActorSheet);
- Actors.registerSheet("ose", OseActorSheetCharacter, {
- types: ["character"],
+ Actors.unregisterSheet('core', ActorSheet);
+ Actors.registerSheet('ose', OseActorSheetCharacter, {
+ types: ['character'],
makeDefault: true,
- label: "OSE.SheetClassCharacter",
+ label: 'OSE.SheetClassCharacter'
});
- Actors.registerSheet("ose", OseActorSheetMonster, {
- types: ["monster"],
+ Actors.registerSheet('ose', OseActorSheetMonster, {
+ types: ['monster'],
makeDefault: true,
- label: "OSE.SheetClassMonster",
+ label: 'OSE.SheetClassMonster'
});
- Items.unregisterSheet("core", ItemSheet);
- Items.registerSheet("ose", OseItemSheet, {
+ Items.unregisterSheet('core', ItemSheet);
+ Items.registerSheet('ose', OseItemSheet, {
makeDefault: true,
- label: "OSE.SheetClassItem",
+ label: 'OSE.SheetClassItem'
});
await preloadHandlebarsTemplates();
@@ -69,16 +70,9 @@ Hooks.once("init", async function () {
/**
* This function runs after game data has been requested and loaded from the servers, so entities exist
*/
-Hooks.once("setup", function () {
+Hooks.once('setup', function () {
// Localize CONFIG objects once up-front
- const toLocalize = [
- "saves_short",
- "saves_long",
- "scores",
- "armor",
- "colors",
- "tags",
- ];
+ const toLocalize = ['saves_short', 'saves_long', 'scores', 'armor', 'colors', 'tags'];
for (let o of toLocalize) {
CONFIG.OSE[o] = Object.entries(CONFIG.OSE[o]).reduce((obj, e) => {
obj[e[0]] = game.i18n.localize(e[1]);
@@ -87,70 +81,65 @@ Hooks.once("setup", function () {
}
// Custom languages
- const languages = game.settings.get("ose", "languages");
- if (languages != "") {
- const langArray = languages.split(",");
+ const languages = game.settings.get('ose', 'languages');
+ if (languages != '') {
+ const langArray = languages.split(',');
langArray.forEach((l, i) => (langArray[i] = l.trim()));
CONFIG.OSE.languages = langArray;
}
});
-Hooks.once("ready", async () => {
- Hooks.on("hotbarDrop", (bar, data, slot) =>
- macros.createOseMacro(data, slot)
- );
+Hooks.once('ready', async () => {
+ Hooks.on('hotbarDrop', (bar, data, slot) => macros.createOseMacro(data, slot));
});
// License and KOFI infos
-Hooks.on("renderSidebarTab", async (object, html) => {
+Hooks.on('renderSidebarTab', async (object, html) => {
if (object instanceof ActorDirectory) {
party.addControl(object, html);
}
if (object instanceof Settings) {
- let gamesystem = html.find("#game-details");
+ let gamesystem = html.find('#game-details');
// SRD Link
- let ose = gamesystem.find("h4").last();
- ose.append(
- ` SRD`
- );
+ let ose = gamesystem.find('h4').last();
+ ose.append(` SRD`);
// License text
- const template = "systems/ose/dist/templates/chat/license.html";
+ const template = 'systems/ose/dist/templates/chat/license.html';
const rendered = await renderTemplate(template);
- gamesystem.find(".system").append(rendered);
+ gamesystem.find('.system').append(rendered);
// User guide
let docs = html.find("button[data-action='docs']");
- const styling =
- "border:none;margin-right:2px;vertical-align:middle;margin-bottom:5px";
+ const styling = 'border:none;margin-right:2px;vertical-align:middle;margin-bottom:5px';
$(
``
).insertAfter(docs);
html.find('button[data-action="userguide"]').click((ev) => {
- new FrameViewer("https://mesfoliesludiques.gitlab.io/foundryvtt-ose", {
- resizable: true,
+ new FrameViewer('https://mesfoliesludiques.gitlab.io/foundryvtt-ose', {
+ resizable: true
}).render(true);
});
}
});
-Hooks.on("preCreateCombatant", (combat, data, options, id) => {
- let init = game.settings.get("ose", "initiative");
- if (init == "group") {
+Hooks.on('preCreateCombatant', (combat, data, options, id) => {
+ let init = game.settings.get('ose', 'initiative');
+ if (init == 'group') {
OseCombat.addCombatant(combat, data, options, id);
}
});
-Hooks.on("updateCombatant", OseCombat.updateCombatant);
-Hooks.on("renderCombatTracker", OseCombat.format);
-Hooks.on("preUpdateCombat", OseCombat.preUpdateCombat);
-Hooks.on("getCombatTrackerEntryContext", OseCombat.addContextEntry);
+Hooks.on('updateCombatant', OseCombat.debounce(OseCombat.updateCombatant), 100);
+Hooks.on('renderCombatTracker', OseCombat.debounce(OseCombat.format, 100));
+Hooks.on('preUpdateCombat', OseCombat.preUpdateCombat);
+Hooks.on('getCombatTrackerEntryContext', OseCombat.debounce(OseCombat.addContextEntry, 100));
-Hooks.on("renderChatLog", (app, html, data) => OseItem.chatListeners(html));
-Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions);
-Hooks.on("renderChatMessage", chat.addChatMessageButtons);
-Hooks.on("renderRollTableConfig", treasure.augmentTable);
-Hooks.on("updateActor", party.update);
+Hooks.on('renderChatLog', (app, html, data) => OseItem.chatListeners(html));
+Hooks.on('getChatLogEntryContext', chat.addChatMessageContextOptions);
+Hooks.on('renderChatMessage', chat.addChatMessageButtons);
+Hooks.on('renderRollTableConfig', treasure.augmentTable);
+Hooks.on('updateActor', party.update);
-Hooks.on("renderCompendium", renderList.RenderCompendium);
-Hooks.on("renderSidebarDirectory", renderList.RenderDirectory);
+Hooks.on('renderCompendium', renderList.RenderCompendium);
+Hooks.on('renderSidebarDirectory', renderList.RenderDirectory);