diff --git a/src/acks.css b/src/acks.css index 1713fb2..02103f0 100644 --- a/src/acks.css +++ b/src/acks.css @@ -78,13 +78,14 @@ animation: 0.8s ease-in 1s infinite alternate notify; } .acks.sheet.actor .sheet-tabs { + border-bottom: none; + justify-content: flex-end; position: absolute; transform: rotate(90deg); top: 365px; right: -169px; width: 320px; border-top: none; - height: 18px; z-index: -1; } .acks.sheet.actor .sheet-tabs .item { @@ -95,7 +96,6 @@ background: url("/ui/parchment.jpg"); border-top-right-radius: 4px; border-top-left-radius: 80px; - border-bottom: 1px solid rgba(0, 0, 0, 0.15); box-shadow: 0 0 6px 1px rgba(0, 0, 0, 0.9); font-size: 12px; filter: brightness(0.9); @@ -107,6 +107,9 @@ text-shadow: none; margin-bottom: -1px; } +.acks.sheet.actor .sheet-tabs .item:not(.active) { + border-bottom: 1px solid rgba(0, 0, 0, 0.15); +} .acks.sheet.actor .sheet-body { height: calc(100% - 140px); } @@ -491,7 +494,7 @@ border-radius: 8px; background: url("/systems/acks/assets/treasure.png") no-repeat center; background-size: cover; - padding: 5px 8px; + padding: 16px 8px; cursor: pointer; filter: grayscale(1) opacity(0.5); } @@ -633,7 +636,6 @@ border: none; } .acks.chat-card .card-content .treasure-list .treasure div { - text-indent: 10px; font-size: 14px; font-weight: bold; } diff --git a/src/acks.js b/src/acks.js index e974248..a2d1469 100644 --- a/src/acks.js +++ b/src/acks.js @@ -40,8 +40,8 @@ Hooks.once("init", async function () { // Register custom system settings registerSettings(); - CONFIG.Actor.entityClass = AcksActor; - CONFIG.Item.entityClass = AcksItem; + CONFIG.Actor.documentClass = AcksActor; + CONFIG.Item.documentClass = AcksItem; // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); @@ -54,7 +54,9 @@ Hooks.once("init", async function () { makeDefault: true, }); Items.unregisterSheet("core", ItemSheet); - Items.registerSheet("acks", AcksItemSheet, { makeDefault: true }); + Items.registerSheet("acks", AcksItemSheet, { + makeDefault: true, + }); await preloadHandlebarsTemplates(); }); @@ -86,10 +88,10 @@ Hooks.on("renderSidebarTab", async (object, html) => { } }); -Hooks.on("preCreateCombatant", (combat, data, options, id) => { - let init = game.settings.get("acks", "initiative"); - if (init == "group") { - AcksCombat.addCombatant(combat, data, options, id); +Hooks.on("createCombatant", async (combatant, options, userId) => { + const init = game.settings.get("acks", "initiative"); + if (init === "group") { + await AcksCombat.addCombatant(combatant, options, userId); } }); diff --git a/src/module/actor/actor-sheet.js b/src/module/actor/actor-sheet.js index d67c5da..c996cfb 100644 --- a/src/module/actor/actor-sheet.js +++ b/src/module/actor/actor-sheet.js @@ -74,7 +74,7 @@ export class AcksActorSheet extends ActorSheet { _onItemSummary(event) { event.preventDefault(); let li = $(event.currentTarget).parents(".item"), - item = this.actor.getOwnedItem(li.data("item-id")), + item = this.actor.items.get(li.data("item-id")), description = TextEditor.enrichHTML(item.data.data.description); // Toggle summary if (li.hasClass("expanded")) { @@ -94,7 +94,7 @@ export class AcksActorSheet extends ActorSheet { async _onSpellChange(event) { event.preventDefault(); const itemId = event.currentTarget.closest(".item").dataset.itemId; - const item = this.actor.getOwnedItem(itemId); + const item = this.actor.items.get(itemId); if (event.target.dataset.field == "cast") { return item.update({ "data.cast": parseInt(event.target.value) }); } else if (event.target.dataset.field == "memorize") { @@ -110,7 +110,7 @@ export class AcksActorSheet extends ActorSheet { .find(".item"); spells.each((_, el) => { let itemId = el.dataset.itemId; - const item = this.actor.getOwnedItem(itemId); + const item = this.actor.items.get(itemId); item.update({ _id: item.id, "data.cast": 0, @@ -130,7 +130,7 @@ export class AcksActorSheet extends ActorSheet { html.find(".item .item-controls .item-show").click(async (ev) => { const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.getOwnedItem(li.data("itemId")); + const item = this.actor.items.get(li.data("itemId")); item.show(); }); @@ -143,7 +143,7 @@ export class AcksActorSheet extends ActorSheet { html.find(".item .item-rollable .item-image").click(async (ev) => { const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.getOwnedItem(li.data("itemId")); + const item = this.actor.items.get(li.data("itemId")); if (item.type == "weapon") { if (this.actor.data.type === "monster") { item.update({ diff --git a/src/module/actor/character-sheet.js b/src/module/actor/character-sheet.js index f83df82..764ab00 100644 --- a/src/module/actor/character-sheet.js +++ b/src/module/actor/character-sheet.js @@ -121,7 +121,7 @@ export class AcksActorSheetCharacter extends AcksActorSheet { async _onQtChange(event) { event.preventDefault(); const itemId = event.currentTarget.closest(".item").dataset.itemId; - const item = this.actor.getOwnedItem(itemId); + const item = this.actor.items.get(itemId); return item.update({ "data.quantity.value": parseInt(event.target.value) }); } @@ -198,14 +198,16 @@ export class AcksActorSheetCharacter extends AcksActorSheet { // Update Inventory Item html.find(".item-edit").click((ev) => { const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.getOwnedItem(li.data("itemId")); + const item = this.actor.items.get(li.data("itemId")); item.sheet.render(true); }); // Delete Inventory Item html.find(".item-delete").click((ev) => { const li = $(ev.currentTarget).parents(".item"); - this.actor.deleteOwnedItem(li.data("itemId")); + this.actor.deleteEmbeddedDocuments("Item", [ + li.data("itemId"), + ]); li.slideUp(200, () => this.render(false)); }); @@ -226,7 +228,7 @@ export class AcksActorSheetCharacter extends AcksActorSheet { ); }); - html.find(".item-create").click((event) => { + html.find(".item-create").click(async (event) => { event.preventDefault(); const header = event.currentTarget; const type = header.dataset.type; @@ -236,14 +238,16 @@ export class AcksActorSheetCharacter extends AcksActorSheet { data: duplicate(header.dataset), }; delete itemData.data["type"]; - return this.actor.createOwnedItem(itemData); + await this.actor.createEmbeddedDocuments("Item", [ + itemData, + ]); }); //Toggle Equipment html.find(".item-toggle").click(async (ev) => { const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.getOwnedItem(li.data("itemId")); - await this.actor.updateOwnedItem({ + const item = this.actor.items.get(li.data("itemId")); + await item.update({ _id: li.data("itemId"), data: { equipped: !item.data.data.equipped, diff --git a/src/module/actor/entity.js b/src/module/actor/entity.js index 3b53e6b..24a9f7b 100644 --- a/src/module/actor/entity.js +++ b/src/module/actor/entity.js @@ -35,24 +35,26 @@ export class AcksActor extends Actor { /* -------------------------------------------- */ /* Socket Listeners and Handlers /* -------------------------------------------- */ - getExperience(value, options = {}) { + async getExperience(value, options = {}) { if (this.data.type != "character") { return; } + let modified = Math.floor( value + (this.data.data.details.xp.bonus * value) / 100 ); - return this.update({ + + await this.update({ "data.details.xp.value": modified + this.data.data.details.xp.value, - }).then(() => { - const speaker = ChatMessage.getSpeaker({ actor: this }); - ChatMessage.create({ - content: game.i18n.format("ACKS.messages.GetExperience", { - name: this.name, - value: modified, - }), - speaker, - }); + }); + + const speaker = ChatMessage.getSpeaker({ actor: this }); + await ChatMessage.create({ + content: game.i18n.format("ACKS.messages.GetExperience", { + name: this.name, + value: modified, + }), + speaker, }); } @@ -73,7 +75,7 @@ export class AcksActor extends Actor { } } - generateSave(hd) { + async generateSave(hd) { let saves = {}; for (let i = 0; i <= hd; i++) { let tmp = CONFIG.ACKS.monster_saves[i]; @@ -81,7 +83,8 @@ export class AcksActor extends Actor { saves = tmp; } } - this.update({ + + await this.update({ "data.saves": { death: { value: saves.d, @@ -106,9 +109,13 @@ export class AcksActor extends Actor { /* Rolls */ /* -------------------------------------------- */ - rollHP(options = {}) { - let roll = new Roll(this.data.data.hp.hd).roll(); - return this.update({ + async rollHP(options = {}) { + let roll = new Roll(this.data.data.hp.hd); + await roll.evaluate({ + async: true, + }); + + await this.update({ data: { hp: { max: roll.total, @@ -572,7 +579,7 @@ export class AcksActor extends Actor { const dh = Math.clamped(hp.value - amount, -99, hp.max); // Update the Actor - return this.update({ + await this.update({ "data.hp.value": dh, }); } @@ -698,10 +705,10 @@ export class AcksActor extends Actor { // Compute treasure let total = 0; let treasure = this.data.items.filter( - (i) => i.type == "item" && i.data.treasure + (i) => i.data.type == "item" && i.data.data.treasure ); treasure.forEach((item) => { - total += item.data.quantity.value * item.data.cost + total += item.data.data.quantity.value * item.data.data.cost }); data.treasure = total; } @@ -718,14 +725,14 @@ export class AcksActor extends Actor { const data = this.data.data; data.aac.naked = baseAac + data.scores.dex.mod; data.ac.naked = baseAc - data.scores.dex.mod; - const armors = this.data.items.filter((i) => i.type == "armor"); + const armors = this.data.items.filter((i) => i.data.type == "armor"); armors.forEach((a) => { - if (a.data.equipped && a.data.type != "shield") { - baseAc = a.data.ac.value; - baseAac = a.data.aac.value; - } else if (a.data.equipped && a.data.type == "shield") { - AcShield = a.data.ac.value; - AacShield = a.data.aac.value; + if (a.data.data.equipped && a.data.type != "shield") { + baseAc = a.data.data.ac; + baseAac = a.data.data.aac.value; + } else if (a.data.data.equipped && a.data.type == "shield") { + AcShield = a.data.data.ac; + AacShield = a.data.data.aac.value; } }); data.aac.value = baseAac + data.scores.dex.mod + AacShield + data.aac.mod; diff --git a/src/module/actor/monster-sheet.js b/src/module/actor/monster-sheet.js index b54b7ca..b3dfbfd 100644 --- a/src/module/actor/monster-sheet.js +++ b/src/module/actor/monster-sheet.js @@ -76,12 +76,11 @@ export class AcksActorSheetMonster extends AcksActorSheet { // Settings data.config.morale = game.settings.get("acks", "morale"); - data.data.details.treasure.link = TextEditor.enrichHTML(data.data.details.treasure.table); + data.data.data.details.treasure.link = TextEditor.enrichHTML(data.data.data.details.treasure.table); data.isNew = this.actor.isNew(); return data; } - async _onDrop(event) { super._onDrop(event); let data; @@ -137,13 +136,11 @@ export class AcksActorSheetMonster extends AcksActorSheet { } async _resetCounters(event) { - const weapons = this.actor.data.items.filter(i => i.type === 'weapon'); - for (let wp of weapons) { - const item = this.actor.getOwnedItem(wp._id); - await item.update({ + for (const weapon of this.actor.itemTypes["weapon"]) { + await weapon.update({ data: { counter: { - value: parseInt(wp.data.counter.max), + value: parseInt(weapon.data.data.counter.max, 10), }, }, }); @@ -153,7 +150,7 @@ export class AcksActorSheetMonster extends AcksActorSheet { async _onCountChange(event) { event.preventDefault(); const itemId = event.currentTarget.closest(".item").dataset.itemId; - const item = this.actor.getOwnedItem(itemId); + const item = this.actor.items.get(itemId); if (event.target.dataset.field == "value") { return item.update({ "data.counter.value": parseInt(event.target.value), @@ -194,18 +191,20 @@ export class AcksActorSheetMonster extends AcksActorSheet { // Update Inventory Item html.find(".item-edit").click((ev) => { const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.getOwnedItem(li.data("itemId")); + const item = this.actor.items.get(li.data("itemId")); item.sheet.render(true); }); // Delete Inventory Item html.find(".item-delete").click((ev) => { const li = $(ev.currentTarget).parents(".item"); - this.actor.deleteOwnedItem(li.data("itemId")); + this.actor.deleteEmbeddedDocuments("Item", [ + li.data("itemId"), + ]); li.slideUp(200, () => this.render(false)); }); - html.find(".item-create").click((event) => { + html.find(".item-create").click(async (event) => { event.preventDefault(); const header = event.currentTarget; const type = header.dataset.type; @@ -224,14 +223,18 @@ export class AcksActorSheetMonster extends AcksActorSheet { // Getting back to main logic if (type == "choice") { const choices = header.dataset.choices.split(","); - this._chooseItemType(choices).then((dialogInput) => { + this._chooseItemType(choices).then(async (dialogInput) => { const itemData = createItem(dialogInput.type, dialogInput.name); - this.actor.createOwnedItem(itemData, {}); + await this.actor.createEmbeddedDocuments("Item", [ + itemData, + ]); }); return; } const itemData = createItem(type); - return this.actor.createOwnedItem(itemData, {}); + await this.actor.createEmbeddedDocuments("Item", [ + itemData, + ]); }); html.find(".item-reset").click((ev) => { @@ -250,7 +253,7 @@ export class AcksActorSheetMonster extends AcksActorSheet { html.find(".item-pattern").click(ev => { const li = $(ev.currentTarget).parents(".item"); - const item = this.actor.getOwnedItem(li.data("itemId")); + const item = this.actor.items.get(li.data("itemId")); let currentColor = item.data.data.pattern; let colors = Object.keys(CONFIG.ACKS.colors); let index = colors.indexOf(currentColor); diff --git a/src/module/combat.js b/src/module/combat.js index 618d0b6..8286c17 100644 --- a/src/module/combat.js +++ b/src/module/combat.js @@ -1,79 +1,112 @@ export class AcksCombat { - static rollInitiative(combat, data) { - // Check groups + static async rollInitiative(combat, data) { + // Initialize groups. data.combatants = []; let groups = {}; combat.data.combatants.forEach((cbt) => { - groups[cbt.flags.acks.group] = { present: true }; + groups[cbt.data.flags.acks.group] = {present: true}; data.combatants.push(cbt); }); - // Roll init - Object.keys(groups).forEach((group) => { - let roll = new Roll("1d6").roll(); - roll.toMessage({ - flavor: game.i18n.format('ACKS.roll.initiative', { group: CONFIG["ACKS"].colors[group] }), + // Roll initiative for each group. + for (const group in groups) { + const roll = new Roll("1d6"); + await roll.evaluate({async: true}); + await roll.toMessage({ + flavor: game.i18n.format('ACKS.roll.initiative', { + group: CONFIG["ACKS"].colors[group], + }), }); - groups[group].initiative = roll.total; - }); - // Set init - for (let i = 0; i < data.combatants.length; ++i) { - if (!data.combatants[i].actor) { + groups[group].initiative = roll.total; + } + + // Set the inititative for each group combatant. + for (const combatant of data.combatants) { + if (!combatant.actor) { return; } - data.combatants[i].initiative = - groups[data.combatants[i].flags.acks.group].initiative; - if (data.combatants[i].actor.data.data.isSlow) { - data.combatants[i].initiative -= 1; - } + + let initiative = groups[combatant.data.flags.acks.group].initiative; + if (combatant.actor.data.data.isSlow) { + initiative -= 1; + } + + await combatant.update({ + initiative: initiative, + }); } + combat.setupTurns(); } static async resetInitiative(combat, data) { - let reroll = game.settings.get("acks", "initiative"); + const reroll = game.settings.get("acks", "initiativePersistence"); if (!["reset", "reroll"].includes(reroll)) { return; } + combat.resetAll(); } static async individualInitiative(combat, data) { - let updates = []; - let messages = []; - combat.data.combatants.forEach((c, i) => { - // This comes from foundry.js, had to remove the update turns thing - // Roll initiative - const cf = combat._getInitiativeFormula(c); - const roll = combat._getInitiativeRoll(c, cf); + const updates = []; + const messages = []; + + let index = 0; + + for (const [id, combatant] of combat.data.combatants.entries()) { + const roll = combatant.getInitiativeRoll(); + await roll.evaluate({async: true}); let value = roll.total; - if (combat.settings.skipDefeated && c.defeated) { + + if (combat.settings.skipDefeated && combatant.defeated) { value = -790; } - updates.push({ _id: c._id, initiative: value }); + + updates.push({ + _id: id, + initiative: value, + }); // Determine the roll mode let rollMode = game.settings.get("core", "rollMode"); - if ((c.token.hidden || c.hidden) && (rollMode === "roll")) rollMode = "gmroll"; + if ((combatant.token.hidden || combatant.hidden) + && (rollMode === "roll")) { + rollMode = "gmroll"; + } // Construct chat message data - let messageData = mergeObject({ + const messageData = mergeObject({ speaker: { scene: canvas.scene._id, - actor: c.actor ? c.actor._id : null, - token: c.token._id, - alias: c.token.name + actor: combatant.actor?.id || null, + token: combatant.token.id, + alias: combatant.token.name }, - flavor: game.i18n.format('ACKS.roll.individualInit', { name: c.token.name }) + flavor: game.i18n.format('ACKS.roll.individualInit', { + name: combatant.token.name, + }), }, {}); - const chatData = roll.toMessage(messageData, { rollMode, create: false }); - if (i > 0) chatData.sound = null; // Only play 1 sound for the whole set + const chatData = await roll.toMessage(messageData, { + rollMode, + create: false, + }); + + // Only play one sound for the whole set. + if (index > 0) { + chatData.sound = null; + } + messages.push(chatData); - }); - await combat.updateEmbeddedEntity("Combatant", updates); - await CONFIG.ChatMessage.entityClass.create(messages); + + ++index; + } + + await combat.updateEmbeddedDocuments("Combatant", updates); + await CONFIG.ChatMessage.documentClass.create(messages); + data.turn = 0; } @@ -88,24 +121,25 @@ export class AcksCombat { ? '' : span.innerHTML; }); - + html.find(".combatant").each((_, ct) => { // Append spellcast and retreat const controls = $(ct).find(".combatant-controls .combatant-control"); - const cmbtant = object.combat.getCombatant(ct.dataset.combatantId); - const moveActive = cmbtant.flags.acks && cmbtant.flags.acks.moveInCombat ? "active" : ""; + const cmbtant = game.combat.combatants.get(ct.dataset.combatantId); + const moveActive = cmbtant.data.flags.acks?.moveInCombat ? "active" : ""; controls.eq(1).after( `` ); - const spellActive = cmbtant.flags.acks && cmbtant.flags.acks.prepareSpell ? "active" : ""; + const spellActive = cmbtant.data.flags.acks?.prepareSpell ? "active" : ""; controls.eq(1).after( `` ); - const holdActive = cmbtant.flags.acks && cmbtant.flags.acks.holdTurn ? "active" : ""; + const holdActive = cmbtant.data.flags.acks?.holdTurn ? "active" : ""; controls.eq(1).after( `` ); }); + AcksCombat.announceListener(html); let init = game.settings.get("acks", "initiative") === "group"; @@ -127,8 +161,8 @@ export class AcksCombat { $(ct).find(".roll").remove(); // Get group color - const cmbtant = object.combat.getCombatant(ct.dataset.combatantId); - let color = cmbtant.flags.acks.group; + const combatant = object.viewed.combatants.get(ct.dataset.combatantId); + let color = combatant.data.flags.acks?.group; // Append colored flag let controls = $(ct).find(".combatant-controls"); @@ -136,6 +170,7 @@ export class AcksCombat { `` ); }); + AcksCombat.addListeners(html); } @@ -155,7 +190,7 @@ export class AcksCombat { ct.initiative && ct.initiative != "-789.00" && ct._id != data._id && - ct.flags.acks.group == combatant.flags.acks.group + ct.data.flags.acks.group == combatant.data.flags.acks.group ) { groupInit = ct.initiative; // Set init @@ -166,75 +201,109 @@ export class AcksCombat { } static announceListener(html) { - html.find(".combatant-control.hold-turn").click((ev) => { - ev.preventDefault(); + html.find(".combatant-control.hold-turn").click(async (event) => { + event.preventDefault(); + // Toggle hold announcement - let id = $(ev.currentTarget).closest(".combatant")[0].dataset.combatantId; - let isActive = ev.currentTarget.classList.contains('active'); - game.combat.updateCombatant({ + const id = $(event.currentTarget).closest(".combatant")[0].dataset.combatantId; + const isActive = event.currentTarget.classList.contains('active'); + const combatant = game.combat.combatants.get(id); + await combatant.update({ _id: id, - flags: { acks: { holdTurn: !isActive } }, + flags: { + acks: { + holdTurn: !isActive, + }, + }, }); }) - html.find(".combatant-control.prepare-spell").click((ev) => { - ev.preventDefault(); + + html.find(".combatant-control.prepare-spell").click(async (event) => { + event.preventDefault(); + // Toggle spell announcement - let id = $(ev.currentTarget).closest(".combatant")[0].dataset.combatantId; - let isActive = ev.currentTarget.classList.contains('active'); - game.combat.updateCombatant({ + const id = $(event.currentTarget).closest(".combatant")[0].dataset.combatantId; + const isActive = event.currentTarget.classList.contains('active'); + const combatant = game.combat.combatants.get(id); + await combatant.update({ _id: id, - flags: { acks: { prepareSpell: !isActive } }, + flags: { + acks: { + prepareSpell: !isActive, + }, + }, }); }); - html.find(".combatant-control.move-combat").click((ev) => { - ev.preventDefault(); + + html.find(".combatant-control.move-combat").click(async (event) => { + event.preventDefault(); + // Toggle retreat announcement - let id = $(ev.currentTarget).closest(".combatant")[0].dataset.combatantId; - let isActive = ev.currentTarget.classList.contains('active'); - game.combat.updateCombatant({ + const id = $(event.currentTarget).closest(".combatant")[0].dataset.combatantId; + const isActive = event.currentTarget.classList.contains('active'); + const combatant = game.combat.combatants.get(id); + await combatant.update({ _id: id, - flags: { acks: { moveInCombat: !isActive } }, + flags: { + acks: { + moveInCombat: !isActive, + }, + }, }); }); } static addListeners(html) { // Cycle through colors - html.find(".combatant-control.flag").click((ev) => { + html.find(".combatant-control.flag").click(async (event) => { + event.preventDefault(); + if (!game.user.isGM) { return; } - let currentColor = ev.currentTarget.style.color; - let colors = Object.keys(CONFIG.ACKS.colors); + + const currentColor = event.currentTarget.style.color; + const colors = Object.keys(CONFIG.ACKS.colors); let index = colors.indexOf(currentColor); if (index + 1 == colors.length) { index = 0; } else { index++; } - let id = $(ev.currentTarget).closest(".combatant")[0].dataset.combatantId; - game.combat.updateCombatant({ + + const id = $(event.currentTarget).closest(".combatant")[0].dataset.combatantId; + const combatant = game.combat.combatants.get(id); + await combatant.update({ _id: id, - flags: { acks: { group: colors[index] } }, + flags: { + acks: { + group: colors[index], + }, + }, }); }); - html.find('.combat-control[data-control="reroll"]').click((ev) => { + html.find('.combat-control[data-control="reroll"]').click(async (event) => { + event.preventDefault(); + if (!game.combat) { return; } - let data = {}; + + const data = {}; AcksCombat.rollInitiative(game.combat, data); - game.combat.update({ data: data }).then(() => { - game.combat.setupTurns(); - }); + + await game.combat.update({ + data: data, + }) + + game.combat.setupTurns(); }); } - static addCombatant(combat, data, options, id) { - let token = canvas.tokens.get(data.tokenId); + static async addCombatant(combatant, options, userId) { let color = "black"; - switch (token.data.disposition) { + switch (combatant.token.data.disposition) { case -1: color = "red"; break; @@ -245,12 +314,16 @@ export class AcksCombat { color = "green"; break; } - data.flags = { - acks: { - group: color, + + await combatant.update({ + flags: { + acks: { + group: color, + }, }, - }; + }); } + static activateCombatant(li) { const turn = game.combat.turns.findIndex(turn => turn._id === li.data('combatant-id')); game.combat.update({turn: turn}) @@ -265,12 +338,13 @@ export class AcksCombat { } static async preUpdateCombat(combat, data, diff, id) { - let init = game.settings.get("acks", "initiative"); - let reroll = game.settings.get("acks", "initiative"); if (!data.round) { return; } + if (data.round !== 1) { + const reroll = game.settings.get("acks", "initiativePersistence"); + if (reroll === "reset") { AcksCombat.resetInitiative(combat, data, diff, id); return; @@ -278,6 +352,9 @@ export class AcksCombat { return; } } + + const init = game.settings.get("acks", "initiative"); + if (init === "group") { AcksCombat.rollInitiative(combat, data, diff, id); } else if (init === "individual") { diff --git a/src/module/dialog/character-creation.js b/src/module/dialog/character-creation.js index ae8cce3..6a8c47c 100644 --- a/src/module/dialog/character-creation.js +++ b/src/module/dialog/character-creation.js @@ -1,13 +1,11 @@ -import { AcksActor } from '../actor/entity.js'; import { AcksDice } from "../dice.js"; export class AcksCharacterCreator extends FormApplication { static get defaultOptions() { const options = super.defaultOptions; options.classes = ["acks", "dialog", "creator"], - options.id = 'character-creator'; - options.template = - 'systems/acks/templates/actors/dialogs/character-creation.html'; + options.id = 'character-creator'; + options.template = 'systems/acks/templates/actors/dialogs/character-creation.html'; options.width = 235; return options; } @@ -169,7 +167,10 @@ export class AcksCharacterCreator extends FormApplication { } } }; - this.object.createOwnedItem(itemData); + + await this.object.createEmbeddedDocuments("Item", [ + itemData, + ]); } /** * This method is called upon form submission after form data is validated diff --git a/src/module/dialog/character-modifiers.js b/src/module/dialog/character-modifiers.js index 6211943..1c7efa6 100644 --- a/src/module/dialog/character-modifiers.js +++ b/src/module/dialog/character-modifiers.js @@ -1,6 +1,4 @@ // eslint-disable-next-line no-unused-vars -import { AcksActor } from '../actor/entity.js'; - export class AcksCharacterModifiers extends FormApplication { static get defaultOptions() { const options = super.defaultOptions; @@ -29,8 +27,10 @@ export class AcksCharacterModifiers extends FormApplication { * @return {Object} */ getData() { - let data = this.object.data; + const data = this.object.data; + data.user = game.user; + return data; } diff --git a/src/module/dialog/entity-tweaks.js b/src/module/dialog/entity-tweaks.js index 61e4f25..70a1299 100644 --- a/src/module/dialog/entity-tweaks.js +++ b/src/module/dialog/entity-tweaks.js @@ -1,6 +1,4 @@ // eslint-disable-next-line no-unused-vars -import { AcksActor } from '../actor/entity.js'; - export class AcksEntityTweaks extends FormApplication { static get defaultOptions() { const options = super.defaultOptions; @@ -28,12 +26,15 @@ export class AcksEntityTweaks extends FormApplication { * @return {Object} */ getData() { - let data = this.object.data; + const data = this.object.data; + if (this.object.data.type === 'character') { data.isCharacter = true; } + data.user = game.user; data.config = CONFIG.ACKS; + return data; } @@ -52,9 +53,11 @@ export class AcksEntityTweaks extends FormApplication { */ async _updateObject(event, formData) { event.preventDefault(); - // Update the actor + + // Update the actor. this.object.update(formData); - // Re-draw the updated sheet + + // Render the updated sheet. this.object.sheet.render(true); } } diff --git a/src/module/dialog/party-sheet.js b/src/module/dialog/party-sheet.js index 5e527fb..f140dd6 100644 --- a/src/module/dialog/party-sheet.js +++ b/src/module/dialog/party-sheet.js @@ -30,12 +30,14 @@ export class AcksPartySheet extends FormApplication { const settings = { ascending: game.settings.get('acks', 'ascendingAC') }; - let data = { + + const data = { data: this.object, config: CONFIG.ACKS, user: game.user, settings: settings }; + return data; } @@ -55,15 +57,18 @@ export class AcksPartySheet extends FormApplication { _dealXP(ev) { // Grab experience const template = ` -
`; - let pcs = this.object.entities.filter((e) => { - return e.getFlag('acks', 'party') && e.data.type == "character"; + + `; + + let pcs = this.object.documents.filter((actor) => { + return actor.getFlag('acks', 'party') && actor.data.type === "character"; }); + new Dialog({ title: "Deal Experience", content: template, @@ -89,11 +94,13 @@ export class AcksPartySheet extends FormApplication { }).render(true); } - async _selectActors(ev) { + async _selectActors(event) { + event.preventDefault(); + const template = "/systems/acks/templates/apps/party-select.html"; const templateData = { - actors: this.object.entities - } + actors: this.object.documents, + }; const content = await renderTemplate(template, templateData); new Dialog({ title: "Select Party Characters", @@ -106,7 +113,7 @@ export class AcksPartySheet extends FormApplication { let checks = html.find("input[data-action='select-actor']"); checks.each(async (_, c) => { let key = c.getAttribute('name'); - await this.object.entities[key].setFlag('acks', 'party', c.checked); + await this.object.documents[key].setFlag('acks', 'party', c.checked); }); }, }, @@ -117,12 +124,13 @@ export class AcksPartySheet extends FormApplication { /** @override */ activateListeners(html) { super.activateListeners(html); + html .find(".item-controls .item-control .select-actors") .click(this._selectActors.bind(this)); - - html.find(".item-controls .item-control .deal-xp").click(this._dealXP.bind(this)); - + + html.find(".item-controls .item-control .deal-xp").click(this._dealXP.bind(this)); + html.find("a.resync").click(() => this.render(true)); html.find(".field-img button[data-action='open-sheet']").click((ev) => { diff --git a/src/module/dice.js b/src/module/dice.js index 555cf37..5110645 100644 --- a/src/module/dice.js +++ b/src/module/dice.js @@ -63,7 +63,7 @@ export class AcksDice { const template = "systems/acks/templates/chat/roll-result.html"; let chatData = { - user: game.user._id, + user: game.user.id, speaker: speaker, }; @@ -78,7 +78,10 @@ export class AcksDice { parts.push(form.bonus.value); } - const roll = new Roll(parts.join("+"), data).roll(); + const roll = new Roll(parts.join("+"), data); + await roll.evaluate({ + async: true, + }); // Convert the roll to a chat message and return the roll let rollMode = game.settings.get("core", "rollMode"); @@ -89,10 +92,13 @@ export class AcksDice { rollMode = game.user.isGM ? "selfroll" : "blindroll"; } - if (["gmroll", "blindroll"].includes(rollMode)) + if (["gmroll", "blindroll"].includes(rollMode)) { chatData["whisper"] = ChatMessage.getWhisperRecipients("GM"); - if (rollMode === "selfroll") chatData["whisper"] = [game.user._id]; - if (rollMode === "blindroll") { + } + + if (rollMode === "selfroll") { + chatData["whisper"] = [game.user.id]; + } else if (rollMode === "blindroll") { chatData["blind"] = true; data.roll.blindroll = true; } @@ -230,14 +236,21 @@ export class AcksDice { // Optionally include a situational bonus if (form !== null && form.bonus.value) parts.push(form.bonus.value); - const roll = new Roll(parts.join("+"), data).roll(); - const dmgRoll = new Roll(data.roll.dmg.join("+"), data).roll(); + const roll = new Roll(parts.join("+"), data); + await roll.evaluate({ + async: true, + }); + + const dmgRoll = new Roll(data.roll.dmg.join("+"), data); + await dmgRoll.evaluate({ + async: true, + }); // Add minimal damage of 1 if (dmgRoll.total < 1) { dmgRoll._total = 1; } - + // Convert the roll to a chat message and return the roll let rollMode = game.settings.get("core", "rollMode"); rollMode = form ? form.rollMode.value : rollMode; diff --git a/src/module/helpers.js b/src/module/helpers.js index 49472f5..7dd4479 100644 --- a/src/module/helpers.js +++ b/src/module/helpers.js @@ -33,7 +33,7 @@ export const registerHelpers = async function () { Handlebars.registerHelper("mult", function (lh, rh) { return parseFloat(lh) * parseFloat(rh); }); - + Handlebars.registerHelper("multround", function (lh, rh) { return Math.round(parseFloat(lh) * parseFloat(rh) * 100) / 100; }) @@ -47,8 +47,8 @@ export const registerHelpers = async function () { }); Handlebars.registerHelper("getTagIcon", function (tag) { - let idx = Object.keys(CONFIG.ACKS.tags).find(k => (CONFIG.ACKS.tags[k] == tag)); - return CONFIG.ACKS.tag_images[idx]; + const index = Object.keys(CONFIG.ACKS.tags).find(k => (CONFIG.ACKS.tags[k] == tag)); + return CONFIG.ACKS.tag_images[index]; }); Handlebars.registerHelper("counter", function (status, value, max) { diff --git a/src/module/item/entity.js b/src/module/item/entity.js index 37f3f13..63050cb 100644 --- a/src/module/item/entity.js +++ b/src/module/item/entity.js @@ -280,7 +280,7 @@ export class AcksItem extends Item { const token = this.actor.token; const templateData = { actor: this.actor, - tokenId: token ? `${token.scene._id}.${token.id}` : null, + tokenId: token ? `${token.parent.id}.${token.id}` : null, item: this.data, data: this.getChatData(), labels: this.labels, @@ -297,11 +297,11 @@ export class AcksItem extends Item { // Basic chat message data const chatData = { - user: game.user._id, + user: game.user.id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, content: html, speaker: { - actor: this.actor._id, + actor: this.actor.id, token: this.actor.token, alias: this.actor.name, }, @@ -311,7 +311,7 @@ export class AcksItem extends Item { let rollMode = game.settings.get("core", "rollMode"); if (["gmroll", "blindroll"].includes(rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM"); - if (rollMode === "selfroll") chatData["whisper"] = [game.user._id]; + if (rollMode === "selfroll") chatData["whisper"] = [game.user.id]; if (rollMode === "blindroll") chatData["blind"] = true; // Create the chat message @@ -355,7 +355,7 @@ export class AcksItem extends Item { if (!actor) return; // Get the Item - const item = actor.getOwnedItem(card.dataset.itemId); + const item = actor.items.get(card.dataset.itemId); if (!item) { return ui.notifications.error( `The requested item ${card.dataset.itemId} no longer exists on Actor ${actor.name}` @@ -395,7 +395,7 @@ export class AcksItem extends Item { const [sceneId, tokenId] = tokenKey.split("."); const scene = game.scenes.get(sceneId); if (!scene) return null; - const tokenData = scene.getEmbeddedEntity("Token", tokenId); + const tokenData = scene.tokens.get(tokenId); if (!tokenData) return null; const token = new Token(tokenData); return token.actor; diff --git a/src/module/macros.js b/src/module/macros.js index 259d946..8ce7b75 100644 --- a/src/module/macros.js +++ b/src/module/macros.js @@ -17,7 +17,7 @@ export async function createAcksMacro(data, slot) { // Create the macro command const command = `game.acks.rollItemMacro("${item.name}");`; - let macro = game.macros.entities.find(m => (m.name === item.name) && (m.command === command)); + let macro = game.macros.find(m => (m.name === item.name) && (m.command === command)); if ( !macro ) { macro = await Macro.create({ name: item.name, diff --git a/src/module/party.js b/src/module/party.js index 94ea77a..f3c5f2a 100644 --- a/src/module/party.js +++ b/src/module/party.js @@ -9,7 +9,6 @@ export const addControl = (object, html) => { } export const showPartySheet = (object) => { - event.preventDefault(); new AcksPartySheet(object, { top: window.screen.height / 2 - 180, left:window.screen.width / 2 - 140, diff --git a/src/module/settings.js b/src/module/settings.js index 3c0d718..d23a781 100644 --- a/src/module/settings.js +++ b/src/module/settings.js @@ -1,6 +1,18 @@ -export const registerSettings = function () { - +export const registerSettings = () => { game.settings.register("acks", "initiative", { + name: game.i18n.localize("ACKS.Setting.Initiative"), + hint: game.i18n.localize("ACKS.Setting.InitiativeHint"), + default: "individual", + scope: "world", + type: String, + config: true, + choices: { + individual: "ACKS.Setting.InitiativeIndividual", + group: "ACKS.Setting.InitiativeGroup", + }, + }); + + game.settings.register("acks", "initiativePersistence", { name: game.i18n.localize("ACKS.Setting.RerollInitiative"), hint: game.i18n.localize("ACKS.Setting.RerollInitiativeHint"), default: "reset", diff --git a/src/module/treasure.js b/src/module/treasure.js index de0f1dc..9e4ff8c 100644 --- a/src/module/treasure.js +++ b/src/module/treasure.js @@ -25,52 +25,54 @@ export const augmentTable = (table, html, data) => { html.find(".sheet-footer .roll").replaceWith(roll); } - html.find(".roll-treasure").click((ev) => { - rollTreasure(table.object, { event: ev }); + html.find(".roll-treasure").click(async (event) => { + await rollTreasure(table.object, { event: event }); }); }; -function drawTreasure(table, data) { - const percent = (chance) => { - const roll = new Roll("1d100").roll(); - return roll.total <= chance; - }; +async function drawTreasure(table, data) { data.treasure = {}; if (table.getFlag('acks', 'treasure')) { - table.results.forEach((r) => { - if (percent(r.weight)) { - const text = table._getResultChatText(r); - data.treasure[r._id] = ({ - img: r.img, + for (const result of table.results) { + const roll = new Roll("1d100"); + await roll.evaluate({async: true}); + + if (roll.total <= result.data.weight) { + const text = result.getChatText(); + data.treasure[result.id] = ({ + img: result.img, text: TextEditor.enrichHTML(text), }); - if ((r.type === CONST.TABLE_RESULT_TYPES.ENTITY) && (r.collection === "RollTable")) { - const embeddedTable = game.tables.get(r.resultId); - drawTreasure(embeddedTable, data.treasure[r._id]); + + if ((result.data.type === CONST.TABLE_RESULT_TYPES.DOCUMENT) + && (result.collection === "RollTable")) { + const embeddedTable = game.tables.get(result.resultId); + drawTreasure(embeddedTable, data.treasure[result.id]); } } - }); + } } else { - const results = table.roll().results; - results.forEach((s) => { - const text = TextEditor.enrichHTML(table._getResultChatText(s)); - data.treasure[s._id] = {img: s.img, text: text}; + const results = await table.roll().results; + results.forEach((result) => { + const text = TextEditor.enrichHTML(result.getChatText()); + data.treasure[result.id] = {img: result.img, text: text}; }); } + return data; } async function rollTreasure(table, options = {}) { // Draw treasure - const data = drawTreasure(table, {}); + const data = await drawTreasure(table, {}); let templateData = { treasure: data.treasure, table: table, }; - + // Animation if (options.event) { - let results = $(event.currentTarget.parentElement) + let results = $(options.event.currentTarget.parentElement) .prev() .find(".table-result"); results.each((_, item) => { @@ -83,7 +85,7 @@ async function rollTreasure(table, options = {}) { let html = await renderTemplate( "systems/acks/templates/chat/roll-treasure.html", - templateData + templateData, ); let chatData = { @@ -93,7 +95,7 @@ async function rollTreasure(table, options = {}) { let rollMode = game.settings.get("core", "rollMode"); if (["gmroll", "blindroll"].includes(rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM"); - if (rollMode === "selfroll") chatData["whisper"] = [game.user._id]; + if (rollMode === "selfroll") chatData["whisper"] = [game.user.id]; if (rollMode === "blindroll") chatData["blind"] = true; ChatMessage.create(chatData); diff --git a/src/system.json b/src/system.json index 61c889a..cce5ba8 100644 --- a/src/system.json +++ b/src/system.json @@ -3,8 +3,8 @@ "title": "Adventurer Conqueror King System", "description": "Play B/X or other OSR compatible content using the ACKS system", "version": "0.7.5", - "minimumCoreVersion": "0.7.4", - "compatibleCoreVersion": "0.7.9", + "minimumCoreVersion": "9", + "compatibleCoreVersion": "9", "templateVersion": 2, "author": "The Happy Anarchist", "esmodules": ["acks.js"], diff --git a/src/templates/actors/character-sheet.html b/src/templates/actors/character-sheet.html index 44e910d..d6d9d45 100644 --- a/src/templates/actors/character-sheet.html +++ b/src/templates/actors/character-sheet.html @@ -12,7 +12,7 @@ {{localize "ACKS.category.abilities"}} - {{#if data.spells.enabled}} + {{#if data.data.spells.enabled}} {{localize "ACKS.category.spells"}} @@ -33,7 +33,7 @@