WIP: Attack rolls rework

master
U~man 2020-07-25 20:25:54 +02:00
parent f31bc3e790
commit 58cbeb95a9
8 changed files with 137 additions and 162 deletions

View File

@ -1,6 +1,6 @@
{ {
"dataPath": "C:\\Users\\oreo\\AppData\\Local\\FoundryVTT", "dataPath": "/home/oreo/.local/share/FoundryVTT",
"manifest": "src", "manifest": "src",
"repository": "https://gitlab.com/mesfoliesludiques/foundryvtt-ose", "repository": "https://gitlab.com/mesfoliesludiques/foundryvtt-ose",
"rawURL": "https://gitlab.com/mesfoliesludiques/foundryvtt-ose/-/raw" "rawURL": "https://gitlab.com/mesfoliesludiques/foundryvtt-ose/-/raw"
} }

View File

@ -28,7 +28,7 @@
"OSE.RollExample": "e.g. +1d4", "OSE.RollExample": "e.g. +1d4",
"OSE.roll.formula": "{label} roll", "OSE.roll.formula": "{label} roll",
"OSE.roll.appearing": "Appearing roll", "OSE.roll.appearing": "Appearing roll ({type})",
"OSE.roll.morale": "Morale check", "OSE.roll.morale": "Morale check",
"OSE.roll.hd": "Hit Dice roll", "OSE.roll.hd": "Hit Dice roll",
"OSE.roll.attacksWith": "Attacks with {name}", "OSE.roll.attacksWith": "Attacks with {name}",

View File

@ -24,8 +24,8 @@ export class OseActorSheet extends ActorSheet {
_createEditor(target, editorOptions, initialContent) { _createEditor(target, editorOptions, initialContent) {
// remove some controls to the editor as the space is lacking // remove some controls to the editor as the space is lacking
if (target == 'data.details.description') { if (target == "data.details.description") {
editorOptions.toolbar = 'styleselect bullist hr table removeFormat save'; editorOptions.toolbar = "styleselect bullist hr table removeFormat save";
} }
super._createEditor(target, editorOptions, initialContent); super._createEditor(target, editorOptions, initialContent);
} }
@ -83,7 +83,9 @@ export class OseActorSheet extends ActorSheet {
summary.slideUp(200, () => summary.remove()); summary.slideUp(200, () => summary.remove());
} else { } else {
// Add item tags // Add item tags
let div = $(`<div class="item-summary"><ol class="tag-list">${item.getTags()}</ol><div>${description}</div></div>`); let div = $(
`<div class="item-summary"><ol class="tag-list">${item.getTags()}</ol><div>${description}</div></div>`
);
li.parents(".item-entry").append(div.hide()); li.parents(".item-entry").append(div.hide());
div.slideDown(200); div.slideDown(200);
} }
@ -104,13 +106,15 @@ export class OseActorSheet extends ActorSheet {
} }
async _resetSpells(event) { async _resetSpells(event) {
let spells = $(event.currentTarget).closest(".inventory.spells").find(".item"); let spells = $(event.currentTarget)
.closest(".inventory.spells")
.find(".item");
spells.each((_, el) => { spells.each((_, el) => {
let itemId = el.dataset.itemId; let itemId = el.dataset.itemId;
const item = this.actor.getOwnedItem(itemId); const item = this.actor.getOwnedItem(itemId);
item.update({ item.update({
_id: item.id, _id: item.id,
"data.cast": item.data.data.memorized, "data.cast": item.data.data.memorized,
}); });
}); });
} }
@ -131,7 +135,7 @@ export class OseActorSheet extends ActorSheet {
html.find(".item .item-controls .item-show").click(async (ev) => { html.find(".item .item-controls .item-show").click(async (ev) => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
const item = this.actor.getOwnedItem(li.data("itemId")); const item = this.actor.getOwnedItem(li.data("itemId"));
item.roll({ skipDialog: event.ctrlKey }); item.show();
}); });
html.find(".item .item-rollable .item-image").click(async (ev) => { html.find(".item .item-rollable .item-image").click(async (ev) => {
@ -143,11 +147,11 @@ export class OseActorSheet extends ActorSheet {
data: { counter: { value: item.data.data.counter.value - 1 } }, data: { counter: { value: item.data.data.counter.value - 1 } },
}); });
} }
item.rollWeapon({ event: ev }); item.rollWeapon({ skipDialog: ev.ctrlKey });
} else if (item.type == "spell") { } else if (item.type == "spell") {
item.spendSpell(); item.spendSpell({ skipDialog: ev.ctrlKey });
} else { } else {
item.rollFormula({ event: ev }); item.rollFormula({ skipDialog: ev.ctrlKey });
} }
}); });
@ -208,12 +212,14 @@ export class OseActorSheet extends ActorSheet {
// Resize editors // Resize editors
let editors = html.find(".editor"); let editors = html.find(".editor");
editors.each((id, editor) => { editors.each((id, editor) => {
let container = editor.closest('.resizable-editor'); let container = editor.closest(".resizable-editor");
if (container) { if (container) {
let heightDelta = this.position.height - this.options.height; let heightDelta = this.position.height - this.options.height;
editor.style.height = `${heightDelta + parseInt(container.dataset.editorSize)}px`; editor.style.height = `${
heightDelta + parseInt(container.dataset.editorSize)
}px`;
} }
}) });
} }
_onConfigureActor(event) { _onConfigureActor(event) {

View File

@ -118,14 +118,12 @@ export class OseActor extends Actor {
const rollParts = ["1d20"]; const rollParts = ["1d20"];
const data = { const data = {
...this.data, actor: this.data,
...{ roll: {
rollData: { type: "above",
type: "above", target: this.data.data.saves[save].value,
target: this.data.data.saves[save].value,
details: game.i18n.format("OSE.roll.details.save", { save: label }),
},
}, },
details: game.i18n.format("OSE.roll.details.save", { save: label }),
}; };
let skip = options.event && options.event.ctrlKey; let skip = options.event && options.event.ctrlKey;
@ -146,12 +144,9 @@ export class OseActor extends Actor {
const rollParts = ["2d6"]; const rollParts = ["2d6"];
const data = { const data = {
...this.data, roll: {
...{ type: "below",
rollData: { target: this.data.data.details.morale,
type: "below",
target: this.data.data.details.morale,
},
}, },
}; };
@ -172,12 +167,9 @@ export class OseActor extends Actor {
const rollParts = ["2d6"]; const rollParts = ["2d6"];
const data = { const data = {
...this.data, roll: {
...{ type: "below",
rollData: { target: this.data.data.retainer.loyalty,
type: "below",
target: this.data.data.retainer.loyalty,
},
}, },
}; };
@ -197,27 +189,24 @@ export class OseActor extends Actor {
const rollParts = ["2d6"]; const rollParts = ["2d6"];
const data = { const data = {
...this.data, roll: {
...{ type: "table",
rollData: { table: {
type: "table", 2: game.i18n.format("OSE.reaction.Hostile", {
table: { name: this.data.name,
2: game.i18n.format("OSE.reaction.Hostile", { }),
name: this.data.name, 3: game.i18n.format("OSE.reaction.Unfriendly", {
}), name: this.data.name,
3: game.i18n.format("OSE.reaction.Unfriendly", { }),
name: this.data.name, 6: game.i18n.format("OSE.reaction.Neutral", {
}), name: this.data.name,
6: game.i18n.format("OSE.reaction.Neutral", { }),
name: this.data.name, 9: game.i18n.format("OSE.reaction.Indifferent", {
}), name: this.data.name,
9: game.i18n.format("OSE.reaction.Indifferent", { }),
name: this.data.name, 12: game.i18n.format("OSE.reaction.Friendly", {
}), name: this.data.name,
12: game.i18n.format("OSE.reaction.Friendly", { }),
name: this.data.name,
}),
},
}, },
}, },
}; };
@ -241,16 +230,14 @@ export class OseActor extends Actor {
const rollParts = ["1d20"]; const rollParts = ["1d20"];
const data = { const data = {
...this.data, roll: {
...{ type: "check",
rollData: { target: this.data.data.scores[score].value,
type: "check",
target: this.data.data.scores[score].value,
details: game.i18n.format("OSE.roll.details.attribute", {
score: label,
}),
},
}, },
details: game.i18n.format("OSE.roll.details.attribute", {
score: label,
}),
}; };
let skip = options.event && options.event.ctrlKey; let skip = options.event && options.event.ctrlKey;
@ -275,11 +262,8 @@ export class OseActor extends Actor {
} }
const data = { const data = {
...this.data, roll: {
...{ type: "hitdice",
rollData: {
type: "hit dice",
},
}, },
}; };
@ -306,9 +290,8 @@ export class OseActor extends Actor {
label = "(1)"; label = "(1)";
} }
const data = { const data = {
...this.data, roll: {
...{ type: {
rollData: {
type: "appearing", type: "appearing",
}, },
}, },
@ -321,8 +304,8 @@ export class OseActor extends Actor {
data: data, data: data,
skipDialog: true, skipDialog: true,
speaker: ChatMessage.getSpeaker({ actor: this }), speaker: ChatMessage.getSpeaker({ actor: this }),
flavor: game.i18n.localize("OSE.roll.appearing"), flavor: game.i18n.format("OSE.roll.appearing", { type: label }),
title: game.i18n.localize("OSE.roll.appearing"), title: game.i18n.format("OSE.roll.appearing", { type: label }),
}); });
} }
@ -331,16 +314,13 @@ export class OseActor extends Actor {
const rollParts = ["1d6"]; const rollParts = ["1d6"];
const data = { const data = {
...this.data, roll: {
...{ type: "below",
rollData: { target: this.data.data.exploration[expl],
type: "below",
target: this.data.data.exploration[expl],
details: game.i18n.format("OSE.roll.details.exploration", {
expl: label,
}),
},
}, },
details: game.i18n.format("OSE.roll.details.exploration", {
expl: label,
}),
}; };
let skip = options.event && options.event.ctrlKey; let skip = options.event && options.event.ctrlKey;
@ -361,25 +341,22 @@ export class OseActor extends Actor {
const data = this.data.data; const data = this.data.data;
const rollData = { const rollData = {
...this.data, actor: this.data,
...{ item: attData.item,
rollData: { roll: {
type: "damage", type: "damage",
stat: attData.type,
scores: data.scores,
},
}, },
}; };
let dmgParts = []; let dmgParts = [];
if (!attData.dmg) { if (!attData.roll.dmg) {
dmgParts.push("1d6"); dmgParts.push("1d6");
} else { } else {
dmgParts.push(attData.dmg); dmgParts.push(attData.roll.dmg);
} }
// Add Str to damage // Add Str to damage
if (attData.type == "melee") { if (attData.roll.type == "melee") {
dmgParts.push(data.scores.str.mod); dmgParts.push(data.scores.str.mod);
} }
@ -396,15 +373,20 @@ export class OseActor extends Actor {
} }
rollAttack(attData, options = {}) { rollAttack(attData, options = {}) {
console.log("ACTOR", attData);
const data = this.data.data; const data = this.data.data;
const rollParts = ["1d20"]; const rollParts = ["1d20"];
const dmgParts = []; const dmgParts = [];
let label = game.i18n.format("OSE.roll.attacks", { name: this.data.name }); let label = game.i18n.format("OSE.roll.attacks", {
if (!attData.dmg) { name: this.data.name,
});
if (!attData.item) {
dmgParts.push("1d6"); dmgParts.push("1d6");
} else { } else {
label = game.i18n.format("OSE.roll.attacksWith", { name: attData.label }); label = game.i18n.format("OSE.roll.attacksWith", {
dmgParts.push(attData.dmg); name: attData.item.name,
});
dmgParts.push(attData.item.data.damage);
} }
let ascending = game.settings.get("ose", "ascendingAC"); let ascending = game.settings.get("ose", "ascendingAC");
@ -422,27 +404,23 @@ export class OseActor extends Actor {
data.thac0.mod.melee.toString() data.thac0.mod.melee.toString()
); );
} }
if (attData.bonus) { if (attData.item && attData.item.data.bonus) {
rollParts.push(attData.bonus); rollParts.push(attData.item.data.bonus);
} }
let thac0 = data.thac0.value; let thac0 = data.thac0.value;
if (attData.type == "melee") { if (attData.type == "melee") {
dmgParts.push(data.scores.str.mod); dmgParts.push(data.scores.str.mod);
} }
const rollData = { const rollData = {
...this.data, actor: this.data,
...{ item: attData.item,
rollData: { roll: {
type: "attack", type: attData.type,
thac0: thac0, thac0: thac0,
save: attData.save, dmg: dmgParts,
weapon: {
parts: dmgParts,
},
},
}, },
}; };
let skip = options.event && options.event.ctrlKey; let skip = options.event && options.event.ctrlKey;
// Roll and return // Roll and return

View File

@ -47,7 +47,7 @@ export class OseCombat {
// Determine the roll mode // Determine the roll mode
let rollMode = game.settings.get("core", "rollMode"); let rollMode = game.settings.get("core", "rollMode");
if ((c.token.hidden || c.hidden) && (rollMode === "roll")) rollMode = "gmroll"; if ((c.token.hidden || c.hidden) && (rollMode === "roll")) rollMode = "gmroll";
// Construct chat message data // Construct chat message data
let messageData = mergeObject({ let messageData = mergeObject({

View File

@ -3,35 +3,35 @@ export class OseDice {
let result = { let result = {
isSuccess: false, isSuccess: false,
isFailure: false, isFailure: false,
target: data.rollData.target, target: data.roll.target,
total: roll.total, total: roll.total,
}; };
let die = roll.parts[0].total; let die = roll.parts[0].total;
if (data.rollData.type == "above") { if (data.roll.type == "above") {
// SAVING THROWS // SAVING THROWS
if (roll.total >= result.target) { if (roll.total >= result.target) {
result.isSuccess = true; result.isSuccess = true;
} else { } else {
result.isFailure = true; result.isFailure = true;
} }
} else if (data.rollData.type == "below") { } else if (data.roll.type == "below") {
// MORALE, EXPLORATION // MORALE, EXPLORATION
if (roll.total <= result.target) { if (roll.total <= result.target) {
result.isSuccess = true; result.isSuccess = true;
} else { } else {
result.isFailure = true; result.isFailure = true;
} }
} else if (data.rollData.type == "check") { } else if (data.roll.type == "check") {
// SCORE CHECKS (1s and 20s) // SCORE CHECKS (1s and 20s)
if (die == 1 || (roll.total <= result.target && die < 20)) { if (die == 1 || (roll.total <= result.target && die < 20)) {
result.isSuccess = true; result.isSuccess = true;
} else { } else {
result.isFailure = true; result.isFailure = true;
} }
} else if (data.rollData.type == "table") { } else if (data.roll.type == "table") {
// Reaction // Reaction
let table = data.rollData.table; let table = data.roll.table;
let output = ""; let output = "";
for (let i = 0; i <= roll.total; i++) { for (let i = 0; i <= roll.total; i++) {
if (table[i]) { if (table[i]) {
@ -65,8 +65,8 @@ export class OseDice {
}; };
// Optionally include a situational bonus // Optionally include a situational bonus
if (form !== null) data["bonus"] = form.bonus.value; // if (form !== null) data["bonus"] = form.bonus.value;
if (data["bonus"]) parts.push(data["bonus"]); if (data.item && data.item.data.bonus) parts.push(data.item.data.bonus);
const roll = new Roll(parts.join("+"), data).roll(); const roll = new Roll(parts.join("+"), data).roll();
@ -75,7 +75,7 @@ export class OseDice {
rollMode = form ? form.rollMode.value : rollMode; rollMode = form ? form.rollMode.value : rollMode;
// Force blind roll (ability formulas) // Force blind roll (ability formulas)
if (data.rollData.blindroll) { if (data.blindroll) {
rollMode = "blindroll"; rollMode = "blindroll";
} }
@ -122,7 +122,7 @@ export class OseDice {
target: "", target: "",
total: roll.total, total: roll.total,
}; };
result.target = data.rollData.thac0; result.target = data.roll.thac0;
if (game.settings.get("ose", "ascendingAC")) { if (game.settings.get("ose", "ascendingAC")) {
result.details = game.i18n.format("OSE.messages.AttackAscendingSuccess", { result.details = game.i18n.format("OSE.messages.AttackAscendingSuccess", {
result: roll.total, result: roll.total,
@ -173,7 +173,7 @@ export class OseDice {
if (data["bonus"]) parts.push(data["bonus"]); if (data["bonus"]) parts.push(data["bonus"]);
const roll = new Roll(parts.join("+"), data).roll(); const roll = new Roll(parts.join("+"), data).roll();
const dmgRoll = new Roll(data.rollData.weapon.parts.join("+"), data).roll(); const dmgRoll = new Roll(data.roll.dmg.join("+"), data).roll();
// Convert the roll to a chat message and return the roll // Convert the roll to a chat message and return the roll
let rollMode = game.settings.get("core", "rollMode"); let rollMode = game.settings.get("core", "rollMode");
@ -237,13 +237,10 @@ export class OseDice {
static async Roll({ static async Roll({
parts = [], parts = [],
data = {}, data = {},
options = {},
event = null,
skipDialog = false, skipDialog = false,
speaker = null, speaker = null,
flavor = null, flavor = null,
title = null, title = null,
item = false,
} = {}) { } = {}) {
let rolled = false; let rolled = false;
@ -263,7 +260,7 @@ export class OseDice {
speaker: speaker, speaker: speaker,
}; };
if (skipDialog) { if (skipDialog) {
return data.rollData.type === "attack" return data.roll.type === "attack"
? OseDice.sendAttackRoll(rollData) ? OseDice.sendAttackRoll(rollData)
: OseDice.sendRoll(rollData); : OseDice.sendRoll(rollData);
} }
@ -275,10 +272,10 @@ export class OseDice {
callback: (html) => { callback: (html) => {
rolled = true; rolled = true;
rollData.form = html[0].children[0]; rollData.form = html[0].children[0];
roll = console.log(data);
data.rollData.type === "attack" roll = ["melee", "missile"].includes(data.roll.type)
? OseDice.sendAttackRoll(rollData) ? OseDice.sendAttackRoll(rollData)
: OseDice.sendRoll(rollData); : OseDice.sendRoll(rollData);
}, },
}, },
cancel: { cancel: {

View File

@ -67,7 +67,7 @@ export class OseItem extends Item {
rollWeapon(options = {}) { rollWeapon(options = {}) {
let isNPC = this.actor.data.type != "character"; let isNPC = this.actor.data.type != "character";
const data = this.data.data; const data = this.data.data;
let type = "raw"; let type = "melee";
if (data.missile && data.melee && !isNPC) { if (data.missile && data.melee && !isNPC) {
// Dialog // Dialog
new Dialog({ new Dialog({
@ -80,11 +80,11 @@ export class OseItem extends Item {
callback: () => { callback: () => {
this.actor.rollAttack( this.actor.rollAttack(
{ {
type: "melee", item: this.data,
save: this.data.data.save, actor: this.actor.data,
label: this.name, roll: {
dmg: this.data.data.damage, type: "melee",
bonus: data.bonus, },
}, },
options options
); );
@ -96,10 +96,11 @@ export class OseItem extends Item {
callback: () => { callback: () => {
this.actor.rollAttack( this.actor.rollAttack(
{ {
type: "missile", roll: {
label: this.name, type: "missile",
save: this.data.data.save, },
dmg: this.data.data.damage, actor: this.actor.data,
item: this.data,
}, },
options options
); );
@ -111,16 +112,14 @@ export class OseItem extends Item {
return true; return true;
} else if (data.missile && !isNPC) { } else if (data.missile && !isNPC) {
type = "missile"; type = "missile";
} else if (data.melee && !isNPC) {
type = "melee";
} }
this.actor.rollAttack( this.actor.rollAttack(
{ {
type: type, roll: {
label: this.name, type: type,
save: data.save, },
dmg: data.damage, actor: this.actor.data,
bonus: data.bonus, item: this.data,
}, },
options options
); );
@ -140,13 +139,12 @@ export class OseItem extends Item {
let type = data.rollType; let type = data.rollType;
const newData = { const newData = {
...this.data, actor: this.actor.data,
...{ item: this.data,
rollData: { roll: {
type: type, type: type,
target: data.rollTarget, target: data.rollTarget,
blindroll: data.blindroll, blindroll: data.blindroll,
},
}, },
}; };
@ -168,7 +166,7 @@ export class OseItem extends Item {
cast: this.data.data.cast - 1, cast: this.data.data.cast - 1,
}, },
}).then(() => { }).then(() => {
this.roll({ skipDialog: true }); this.show({ skipDialog: true });
}); });
} }
@ -270,13 +268,10 @@ export class OseItem extends Item {
} }
/** /**
* Roll the item to Chat, creating a chat card which contains follow up attack or damage roll options * Show the item to Chat, creating a chat card which contains follow up attack or damage roll options
* @return {Promise} * @return {Promise}
*/ */
async roll({ skipDialog = false } = {}) { async show() {
if (this.data.type == "weapon") {
if (this.rollWeapon(skipDialog)) return;
}
// Basic template rendering data // Basic template rendering data
const token = this.actor.token; const token = this.actor.token;
const templateData = { const templateData = {

View File

@ -1,5 +1,4 @@
{{log data}} <div class="ose chat-card item-card" data-actor-id="{{actor._id}}" data-item-id="{{item._id}}"
<div class="ose chat-card item-card" data-actor-id="{{actor._id}}" data-item-id="{{item._id}}">
{{#if tokenId}}data-token-id="{{tokenId}}"{{/if}}> {{#if tokenId}}data-token-id="{{tokenId}}"{{/if}}>
<div class="ose chat-block"> <div class="ose chat-block">
<div class="flexrow chat-header"> <div class="flexrow chat-header">