diff --git a/module/actor/actor-sheet.js b/module/actor/actor-sheet.js index 0687df0..f18410b 100644 --- a/module/actor/actor-sheet.js +++ b/module/actor/actor-sheet.js @@ -304,31 +304,23 @@ export class MothershipActorSheet extends ActorSheet { weapon.sheet.render(true); }); - + // Rollable Attribute html.find('.stat-roll').click(ev => { const div = $(ev.currentTarget); const statName = div.data("key"); - const attribute = this.actor.system.stats[statName]; - var shifted = false; - if (ev.shiftKey) shifted = true; - this.actor.rollStat(attribute, shifted); + this.actor.rollCheck(null,'low',statName,null,null); }); // Rollable Skill html.find('.skill-roll').click(ev => { const li = event.currentTarget.closest(".item"); - this.actor.rollSkill(li.dataset.itemId, { - event: event - }); + this.actor.rollCheck(null,null,null,li.dataset.itemId,null); }); // Rollable Weapon html.find('.weapon-roll').click(ev => { const li = ev.currentTarget.closest(".item"); - - this.actor.rollWeapon(li.dataset.itemId, { - event: ev - }); + this.actor.rollCheck(null,'low','combat',null,li.dataset.itemId); }); // Rollable Item/Anything with a description that we want to click on. @@ -339,11 +331,11 @@ export class MothershipActorSheet extends ActorSheet { }); }); + //increase ammo html.on('mousedown', '.weapon-ammo', ev => { const li = ev.currentTarget.closest(".item"); const item = duplicate(this.actor.getEmbeddedDocument("Item", li.dataset.itemId)) let amount = item.system.ammo; - if (event.button == 0) { if (amount >= 0) { item.system.ammo = Number(amount) + 1; @@ -353,35 +345,13 @@ export class MothershipActorSheet extends ActorSheet { item.system.ammo = Number(amount) - 1; } } - this.actor.updateEmbeddedDocuments('Item', [item]); }); //Reload Shots html.on('mousedown', '.weapon-reload', ev => { const li = ev.currentTarget.closest(".item"); - const item = duplicate(this.actor.getEmbeddedDocument("Item", li.dataset.itemId)) - - if (!item.system.useAmmo) { - item.system.curShots = item.system.shots; - } else { - item.system.ammo += item.system.curShots; - let reloadAmount = Math.min(item.system.ammo, item.system.shots); - item.system.curShots = reloadAmount; - - item.system.ammo -= reloadAmount; - } - - this.actor.updateEmbeddedDocuments('Item', [item]); - - let actor = this.actor; - let speaker = ChatMessage.getSpeaker({ actor }); - ChatMessage.create({ - speaker, - content: `Reloading ` + item.name + "..." - }, - { chatBubble: true }); - + this.actor.reloadWeapon(li.dataset.itemId); }); // Rollable Item/Anything with a description that we want to click on. @@ -390,6 +360,7 @@ export class MothershipActorSheet extends ActorSheet { attribute.label = "Calm"; this.actor.rollStress(attribute); }); + // Rollable Item/Anything with a description that we want to click on. html.find('.stress-roll').click(ev => { //roll hotbar macro for stress diff --git a/module/actor/actor.js b/module/actor/actor.js index 8e256b2..9d82bb2 100644 --- a/module/actor/actor.js +++ b/module/actor/actor.js @@ -240,7 +240,6 @@ export class MothershipActor extends Actor { } //central roll parsing function | TAKES rollResult: [Foundry roll object], zeroBased: true, checkCrit: true, rollTarget: 41 | RETURNS enriched Foundry roll object - //IN PROGRESS, still translating from macro parseRollResult(rollString,rollResult,zeroBased,checkCrit,rollTarget,comparison) { //init vars let doubles = new Set([0, 11, 22, 33, 44, 55, 66, 77, 88, 99]); @@ -541,223 +540,328 @@ export class MothershipActor extends Actor { await game.dice3d.waitFor3DAnimationByMessageID(chatId); } - - -/* rollStatSelect(statList) { //REMOVEME? candidate for removal - - let selectList = ""; - - statList.forEach(stat => selectList += "") - statList.forEach(stat => console.log(stat[0])); - - console.log(statList); - - let d = new Dialog({ - title: "Select Roll Type", - content: "

Stat


", + //central adding addribute function | TAKES skill | RETURNS player selected attribute + chooseAttribute(rollString,aimFor,attribute,skill,weapon) { + //open dialog asking player to choose a core stat + new Dialog({ + title: `Choose a Stat`, + content: ` + +
+

Select a Stat

Choose the Stat that best suits the nature of this Skill Check. You will add your skill bonus to the Stat value for this roll (giving you a higher number to roll under).
+
+ + + + + `, buttons: { - roll: { - icon: '', - label: "Roll", - callback: (html) => this.rollStat(this.system.stats[html.find('[id=\"stat\"]')[0].value]) + button1: { + label: `Advantage`, + callback: (html) => this.rollCheck(`1d100 [+]`,`low`,html.find("input[name='stat']:checked").attr("value"),skill,weapon), + icon: `` }, - cancel: { - icon: '', - label: "Cancel", - callback: () => { } - } - }, - default: "roll", - close: () => { } - }); - d.render(true); - } */ - -/* rollStat(attribute, shifted = false) { //REMOVEME? candidate for removal - console.log(attribute); - - let attLabel = attribute.label?.charAt(0).toUpperCase() + attribute.label?.toLowerCase().slice(1); - if (!attribute.label && isNaN(attLabel)) - attLabel = attribute.charAt(0)?.toUpperCase() + attribute.toLowerCase().slice(1); - - //this.rollAttribute(attribute, "none"); - if(shifted){ - this.rollAttribute(attribute, 'None'); - } - else { - let d = new Dialog({ - title: "Select Roll Type", - content: "

Advantages/Disadvantage


", - buttons: { - roll: { - icon: '', - label: "Roll", - callback: (html) => this.rollAttribute(attribute, html.find('[id=\"advantage\"]')[0].value) - }, - cancel: { - icon: '', - label: "Cancel", - callback: () => { } - } + button2: { + label: `Normal`, + callback: (html) => this.rollCheck(`1d100`,`low`,html.find("input[name='stat']:checked").attr("value"),skill,weapon), + icon: `` }, - default: "roll", - close: () => { } - }); - d.render(true); - } - } */ - -/* rollStress(attribute) { //REMOVEME? candidate for removal - //this.rollAttribute(attribute, "none"); - let rollOver = true; - if (attribute.label == "Calm") - rollOver = false; - - let attLabel = attribute.label?.charAt(0).toUpperCase() + attribute.label?.toLowerCase().slice(1); - if (!attribute.label && isNaN(attLabel)) - attLabel = attribute.charAt(0)?.toUpperCase() + attribute.toLowerCase().slice(1); - - let d = new Dialog({ - title: "Select Roll Type", - content: "

Advantages/Disadvantage


", - buttons: { - roll: { - icon: '', - label: "Roll", - callback: (html) => this.rollAttribute(attribute, html.find('[id=\"advantage\"]')[0].value, '', rollOver) - }, - cancel: { - icon: '', - label: "Cancel", - callback: () => { } - } - }, - default: "roll", - close: () => { } - }); - d.render(true); - } */ - -/* rollWeapon(itemId, options = { event: null }) { //REMOVEME? candidate for removal - - let item = duplicate(this.getEmbeddedDocument("Item",itemId)); - - console.log(item); - - if (item.system.useAmmo == true || item.system.shots > 0) { - if (item.system.shots == 0) { - if (item.system.ammo > 0) { - item.system.ammo -= 1; - this.updateEmbeddedDocuments('Item', [item]); - } else { - - //Select the stat of the roll. - let t = new Dialog({ - title: "Reload", - content: "

Out of Ammo


", - buttons: { - cancel: { - icon: '', - label: "Cancel", - callback: () => { } - } - }, - default: "roll", - close: () => { } - }); - t.render(true); - return; - } - } else { - if (item.system.curShots > 0) { - let subAmount = Math.max(item.system.shotsPerFire, 1); - item.system.curShots = Math.max(item.system.curShots - subAmount, 0); - console.log("Unloading Shots"); - this.updateEmbeddedDocuments('Item', [item]); - } - else { - if (item.system.ammo > 0 || !item.system.useAmmo) { - //Select the stat of the roll. - let t = new Dialog({ - title: "Reload", - content: "

Reload?


", - buttons: { - roll: { - icon: '', - label: "Reload", - callback: (html) => this.reloadWeapon(itemId) - }, - cancel: { - icon: '', - label: "Cancel", - callback: () => { } - } - }, - default: "roll", - close: () => { } - }); - t.render(true); - } else { - //Select the stat of the roll. - let t = new Dialog({ - title: "Reload", - content: "

Out of Ammo


", - buttons: { - cancel: { - icon: '', - label: "Cancel", - callback: () => { } - } - }, - default: "roll", - close: () => { } - }); - t.render(true); - } - - // let actor = this; - // let speaker = ChatMessage.getSpeaker({ actor }); - // ChatMessage.create({ - // speaker, - // content: item.system.ammo > 0 ? `I need to reload my ` + item.name + "!" : "I'm out of ammo!", - // type: CHAT_MESSAGE_TYPES.EMOTE - // }, - // { chatBubble: true }); - return; + button3: { + label: `Disadvantage`, + callback: (html) => this.rollCheck(`1d100 [-]`,`low`,html.find("input[name='stat']:checked").attr("value"),skill,weapon), + icon: `` } } - } + },{width: 600,height: 475}).render(true); + } + //central adding skill function | TAKES nothing | RETURNS player selected skill + chooseSkill(rollString,aimFor,attribute,skill,weapon) { + //get list of player skills + let playerItems = this.items; + //create header for skill list + let skillHeader = ` + +
+

Add a Skill?

If you have a Skill that is relevant to the task at hand, you can add the Skill’s bonus to your Stat or Save before making your roll (giving you a higher number to roll under).
+
`; + //create footer for skill list + const skillFooter = `

Select your roll type:

`; + //create template for skill row + const skillRow = ` + `; + //create skillList string + let skillList = ``; + //create skill counter + let skillCount = 0; + //create dialog pixel counter + let dialogHeight = 192; + //loop through and create skill rows + for (let item of playerItems) { + //check if this is a skill + if (item.type === "skill") { + //set temprow as template + let tempRow = skillRow; + //replace ID + tempRow = tempRow.replaceAll("[RADIO_ID]",item.name); + //replace value + tempRow = tempRow.replace("[RADIO_VALUE]",item.system.bonus); + //replace img + tempRow = tempRow.replace("[RADIO_IMG]",item.img); + //replace name + tempRow = tempRow.replace("[RADIO_NAME]",item.name); + //replace desc + tempRow = tempRow.replace("[RADIO_DESC]",item.system.description.replace("

","

: ")); + //add to skillList + skillList = skillList + tempRow; + //increment skill count + skillCount++; + //increment pixel counter + dialogHeight = dialogHeight + 77; + } + } + //check if there are no skills, and adjust prompt accordingly + if (skillCount === 0) { + //set window height + dialogHeight = 105; + //make skill header blank + skillHeader = ``; + } + //make content string + let skillHtml = skillHeader + skillList + skillFooter; + //make window setting + let dlgTitle = ''; + if (attribute === 'sanity' || attribute === 'fear' || attribute === 'body') { + dlgTitle = `Save`; + } else if (weapon) { + dlgTitle = `Attack`; + } else { + dlgTitle = `Stat Check`; + } //Select the stat of the roll. - let t = new Dialog({ - title: "Select Stat", - content: "

Advantage/Disadvantage


\ - \ -

Skill Bonus


", - buttons: { - roll: { - icon: '', - label: "Roll", - callback: (html) => this.rollAttribute(this.system.stats["combat"], html.find('[id=\"advantage\"]')[0].value, item, false, html.find('[id=\"bonus\"]')[0].value) - }, - cancel: { - icon: '', - label: "Cancel", - callback: () => { } - } - }, - default: "roll", - close: () => { } - }); - t.render(true); - } */ + new Dialog({ + title: dlgTitle, + content: skillHtml, + buttons: { + button1: { + label: `Advantage`, + callback: (html) => this.rollCheck(`1d100 [+]`,aimFor,attribute,html.find("input[name='skill']:checked").attr("id"),weapon), + icon: `` + }, + button2: { + label: `Normal`, + callback: (html) => this.rollCheck(`1d100`,aimFor,attribute,html.find("input[name='skill']:checked").attr("id"),weapon), + icon: `` + }, + button3: { + label: `Disadvantage`, + callback: (html) => this.rollCheck(`1d100 [-]`,aimFor,attribute,html.find("input[name='skill']:checked").attr("id"),weapon), + icon: `` + } + } + },{width: 600,height: dialogHeight}).render(true); + } + + //central adding skill function | TAKES nothing | RETURNS player selected skill + chooseAdvantage(rollString,aimFor,attribute,skill,weapon) { + //create footer for skill list + let advHtml = `

Select your roll type:

`; + //make window setting + let dlgTitle = ''; + if (attribute === 'sanity' || attribute === 'fear' || attribute === 'body') { + dlgTitle = `Save`; + } else if (weapon) { + dlgTitle = `Attack`; + } else { + dlgTitle = `Stat Check`; + } + //Select the stat of the roll. + new Dialog({ + title: dlgTitle, + content: advHtml, + buttons: { + button1: { + label: `Advantage`, + callback: (html) => this.rollCheck(`1d100 [+]`,aimFor,attribute,skill,weapon), + icon: `` + }, + button2: { + label: `Normal`, + callback: (html) => this.rollCheck(`1d100`,aimFor,attribute,skill,weapon), + icon: `` + }, + button3: { + label: `Disadvantage`, + callback: (html) => this.rollCheck(`1d100 [-]`,aimFor,attribute,skill,weapon), + icon: `` + } + } + },{width: 600,height: 105}).render(true); + } + + //central check rolling function | TAKES attribute: [attribute object], skill: [skill item], attack: true, weapon: [weapon item] | RETURNS chat message showing check result + //IN PROGRESS, CONVERTING FROM MOD + async rollCheck(rollString,aimFor,attribute,skill,weapon) { + //first we need to bounce this request away if certain parameters are NULL + //if attribute is blank, redirect player to choose an attribute + if (attribute === undefined) { + //run the choose attribute function + this.chooseAttribute(rollString,aimFor,attribute,skill,weapon); + //exit function + return; + } + //if skill is blank and actor is a character, redirect player to choose a skill + if (skill === undefined && this.actor.type === 'character') { + //run the choose attribute function + this.chooseSkill(rollString,aimFor,attribute,skill,weapon); + //exit function + return; + } + //if rollString is STILL blank, redirect player to choose the roll + if (rollString === undefined) { + //run the choose attribute function + this.chooseAdvantage(rollString,aimFor,attribute,skill,weapon); + //exit function + return; + } + //roll the dice + //parse the roll string + let parsedRollString = this.parseRollString(rollString,aimFor); + //roll the dice + let rollResult = await new Roll(parsedRollString).evaluate(); + //interpret the results + let parsedRollResult = this.parseRollResult(rollString,rollResult,zeroBased,checkCrit,rollTarget,comparison); + + +//all sorts of stuff +//I LEFT OFF HERE :)/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + //push chat message + //make message + let macroMsg = await rollResult.toMessage({ + id: chatId, + user: game.user.id, + speaker: {actor: this.id, token: this.token, alias: this.name}, + content: messageTemplate + },{keepId:true}); + //make dice + await game.dice3d.waitFor3DAnimationByMessageID(chatId); + } reloadWeapon(itemId, options = { event: null }) { + //dupe item to work with let item = duplicate(this.getEmbeddedDocument("Item",itemId)); - + //reload if (event.button == 0) { if (!item.system.useAmmo) { item.system.curShots = item.system.shots; @@ -765,312 +869,62 @@ export class MothershipActor extends Actor { item.system.ammo += item.system.curShots; let reloadAmount = Math.min(item.system.ammo, item.system.shots); item.system.curShots = reloadAmount; - item.system.ammo -= reloadAmount; } } - + //update the item this.updateEmbeddedDocuments('Item', [item]); + //define macroresult + let macroResult = ''; + //create chat message template + if (item.name.length >= 22) { + macroResult = ` +
+
+
+
${item.name}
+
+
+
+
Weapon reloaded.
+
+
+
+
+ `; + } else { + macroResult = ` +
+
+
+
${item.name}
+
+
+
+
Weapon reloaded.
+
+
+
+
+ `; + } + //push message + ChatMessage.create({ + user: game.user._id, + speaker: { + actor: this.id, + token: this.token, + alias: this.name + }, + content: macroResult + }); } - printDescription(itemId, options = { event: null }) { let item = duplicate(this.getEmbeddedDocument("Item",itemId)); this.chatDesc(item); } -/* rollSkill(itemId, options = { event: null }) { //REMOVEME? candidate for removal - let item = duplicate(this.getEmbeddedDocument("Item",itemId)); - - // let attLabel = skill.label?.charAt(0).toUpperCase() + skill.label?.toLowerCase().slice(1); - // if (!skill.label && isNaN(attLabel)) - // attLabel = skill.charAt(0)?.toUpperCase() + item.name.toLowerCase().slice(1); - - //this.rollAttribute(attribute, "none"); - - //Select the stat of the roll. - let t = new Dialog({ - title: "Select Stat", - content: "

Stat


\ -

Advantage/Disadvantage


", - buttons: { - roll: { - icon: '', - label: "Roll", - callback: (html) => this.rollAttribute(this.system.stats[html.find('[id=\"stat\"]')[0].value], html.find('[id=\"advantage\"]')[0].value, item) - }, - cancel: { - icon: '', - label: "Cancel", - callback: () => { } - } - }, - default: "roll", - close: () => { } - }); - t.render(true); - } */ - -/* async rollAttribute(attribute, advantage, item = "", rollOver = false, bonus = 0) { //REMOVEME? candidate for removal - let attributeName = attribute.label?.charAt(0).toUpperCase() + attribute.label?.toLowerCase().slice(1); - if (!attribute.label && isNaN(attributeName)) - attributeName = attribute.charAt(0)?.toUpperCase() + attribute.toLowerCase().slice(1); - - let isStress = (attribute.label == "Stress" ? true : false); - // Roll - let firstEdition = game.settings.get("mosh", "firstEdition"); - - let diceformular = (isStress ? (firstEdition ? "1d20" : "2d10") : "1d100"); - - if(isNaN(bonus)){ - bonus = 0; - item.system.bonus = 0; - } - // if (advantage != undefined && advantage != NaN && advantage != 0) { - // diceformular = diceformular + "+" + advantage + "d6kh"; - // } - let r = new Roll(diceformular, {}); - r.evaluate({async: false}); - - let rSplit = ("" + r._total).split(""); - - //Advantage roll - let a = new Roll(diceformular, {}); - a.evaluate({async: false}); - - if (r._total == 100) { - //r.results[0] = 0; - r._total = 0; - } - - - if (a._total == 100) { - //a.results[0] = 0; - a._total = 0; - } - - let range = "N/A"; - let damageRoll = 0; - if (item.type == "weapon") { - - if(item.system.damage.includes("[+]")){ - var newRoll = item.system.damage.replace("[+]", ""); - newRoll = "{"+newRoll+", "+newRoll+"}kh"; - console.log(newRoll); - damageRoll = new Roll(newRoll); - } else if(item.system.damage.includes("[-]")){ - var newRoll = item.system.damage.replace("[-]", ""); - newRoll = "{"+newRoll+", "+newRoll+"}kl"; - console.log(newRoll); - damageRoll = new Roll(newRoll); - } - else { - damageRoll = new Roll(item.system.damage); - } - - damageRoll.evaluate({async: false}); - range = item.system.ranges.value; - - } - - // Format Dice - const diceData = this.formatDice(r); - - let mod = 0; - if(item.type == "skill"){ - mod += item.system.bonus; - } - - - if (attribute.mod != 0) mod += attribute.mod; - - if(isNaN(mod)) mod = 0; - - let targetValue = attribute.value + mod + (item == "" ? 0 : parseInt(bonus)); - - let critical = false; - - if (r._total == 0 && !isStress || rSplit[0] == rSplit[1] && !isStress) - critical = true; - - - let panic = false; - let panicText = ''; - if(isStress && r._total <= attribute.value && firstEdition){ - await(async () => { - // const contents = await game.packs.get("Tables").getContent(); - // const table= contents.find(i => i.name === `Panic`); - let tableLocation = game.settings.get("mosh", "panicTable"); - const documentIndex = game.packs.get(tableLocation).index.getName("Panic"); - const doc = await game.packs.get(tableLocation).getDocument(documentIndex._id); - panic = true; - panicText = doc.getResultsForRoll(r._total)[0].data.text; - - //then just put your normal table rolling command using `table` - })() - - } - - //Here's where we handle the result text. - let resultText = ""; - - if (rollOver == true) { - if (critical) - resultText = (r._total > targetValue ? "Critical Success" : "Critical Failure"); - else - resultText = (r._total > targetValue ? "Success" : "Failure"); - } else { - if (critical) - resultText = (r._total < targetValue ? "Critical Success" : "Critical Failure"); - else - resultText = (r._total < targetValue ? "Success" : "Failure"); - } - - var templateData = { - actor: this, - stat: { - name: attributeName.toUpperCase() - }, - data: { - diceTotal: { - value: r._total, - advantageValue: a._total, - damageValue: damageRoll._total, - damageRoll: damageRoll - }, - resultText: { - value: resultText - }, - isCreature: { - value: this.type == "creature" ? true : false - }, - panic: panic, - panicText : panicText - }, - target: attribute.value, - mod: mod, - item: item, - targetValue: targetValue, - useSkill: item.type == "skill" ? true : false, - isWeapon: item.type == "weapon" ? true : false, - range: range, - critical: critical, - advantage: advantage == "advantage" ? true : false, - bonus: bonus, - diceData - }; - - let chatData = { - user: game.user.id, - speaker: { - actor: this.id, - token: this.token, - alias: this.name - } - }; - - let rollMode = game.settings.get("core", "rollMode"); - if (["gmroll", "blindroll"].includes(rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM"); - - //comment this out: - if (this.data.type == "creature") { - chatData.whisper = game.user._id; - } - - let template = 'systems/mosh/templates/chat/statroll.html'; - renderTemplate(template, templateData).then(content => { - chatData.content = content; - if (game.dice3d) { - if(!damageRoll) - game.dice3d.showForRoll(r, game.user, true, chatData.whisper, chatData.blind).then(displayed => ChatMessage.create(chatData)); - else - game.dice3d.showForRoll(damageRoll, game.user, true, chatData.whisper, chatData.blind).then(displayed => ChatMessage.create(chatData)); - - } else { - chatData.sound = CONFIG.sounds.dice; - ChatMessage.create(chatData); - } - }); - } */ - -/* formatDice(diceRoll) { //REMOVEME? candidate for removal - let diceData = { dice: [] }; - - if (diceRoll != null) { - let pushDice = (diceData, total, faces, color) => { - let img = null; - if ([4, 6, 8, 10, 12, 20].indexOf(faces) > -1) { - img = `../icons/svg/d${faces}-grey.svg`; - } - diceData.dice.push({ - img: img, - result: total, - dice: true, - color: color - }); - }; - - for (let i = 0; i < diceRoll.terms.length; i++) { - if (diceRoll.terms[i] instanceof Die) { - let pool = diceRoll.terms[i].results; - let faces = diceRoll.terms[i].faces; - - pool.forEach((pooldie) => { - if (pooldie.discarded) { - pushDice(diceData, pooldie.result, faces, "#777"); - } else { - pushDice(diceData, pooldie.result, faces, "white"); - } - - }); - } else if (typeof diceRoll.terms[i] == 'string') { - const parsed = parseInt(diceRoll.terms[i]); - if (!isNaN(parsed)) { - diceData.dice.push({ - img: null, - result: parsed, - dice: false, - color: 'white' - }); - } else { - diceData.dice.push({ - img: null, - result: diceRoll.terms[i], - dice: false - }); - } - } - else if (typeof diceRoll.terms[i] == 'number') { - const parsed = parseInt(diceRoll.terms[i]); - if (!isNaN(parsed)) { - diceData.dice.push({ - img: null, - result: parsed, - dice: false, - color: 'white' - }); - } else { - diceData.dice.push({ - img: null, - result: diceRoll.terms[i], - dice: false - }); - } - } - } - } - - return diceData; - } */ - // Print the item description into the chat. chatDesc(item) { let itemName = item.name?.charAt(0).toUpperCase() + item.name?.toLowerCase().slice(1);