diff --git a/CHANGELOG.md b/CHANGELOG.md index 43d588e..721e954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,3 +40,9 @@ Aug 1, 2022 ## v0.0.8 Aug 2, 2022 * Fix issue with fudges dialog from Times removal + +## v0.0.9 +Aug 7, 2022 +* Lots of refactoring to cleanup code +* Remove fudges for actors +* Simple and Avg Karma diff --git a/README.md b/README.md index 066deeb..82eeb19 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,20 @@ Currently being (quasi) managed here: https://github.com/users/UranusBytes/proje Extensive logging is used within the module, with debug logging enabled with the package debugging enabled ## Known Issues -* Raw die rolls for actors not working (even if fudge defined on GM/Player) * It's possible to define a fudge that is impossible to achieve (especially when considering modifiers. Or to define a fudge of "> 20" for a d20). The failsafe is attempting to fudge 150 times, at which point the closest possible is provided. * When the fudge config dialog is open, fudge status/list is not updated if any are changed by other GMs and/or PC/Actor rolls * Completely incompatible with [Better Rolls for 5e](https://github.com/RedReign/FoundryVTT-BetterRolls5e) #6 +* If both Fudge and Karma are enabled, a single roll that's being fudged can be influed by karma # Current module Functionality ## Fudge Allow the GM to influence raw die rolls (just the dice) or roll totals (dice + modifiers) +## Karma +Keep a history of die rolls per user, and if they are below a threshold over a defined history, either force the next roll to be above a minimum value (Simple), or increase the roll by a small amount until the average is above the threshold (Average) + +# Global Config +The individual functionality can be enabled/disabled within the module settings + # Fudge  @@ -70,16 +76,37 @@ A green circular arrow means the fudge will re-enable itself after being used (p  -# Mechanics +## Mechanics The way Fudge works is that the next die roll of that type (either system specific, total result w/ modifiers, or a raw die roll) for that Player or Actor will be evaluated against the formula defined. If it doesn't meet the formula criteria, then the result is rerolled in the background (max of 150 times), with the final result presented to the PC. As it is re-rolling, if the attempted re-roll is "closer" to the desired fudge value, it will be kept. (For circumstances where the fudge can never be achieved, at least get as close as possible) The GM will get a whisper that outlines if the fudge was used (with all failed results), or if it was removed without being used (if first roll met formula criteria). When a fudge is used to influence a die (or would have, but original result was sufficient), then the fudge is disabled (unless persistence is enabled).  +# Karma + + +With the module enabled, a praying hands icon will be displayed above the message tray. + + + +Clicking on this icon will open a configuration dialog. +The available karma options can be enabled by clicking on the button + + + +Within the dialog, the logic used to influence each Karma module is adjustable. +The current history of player rolls is displayed. + + + + +# Mechanics +Karma only works on raw die rolls; it does not influence total rolls directly (only indirectly by influencing the raw rolls) + # Future Planned Functionality -## Karmic dice -All for gradual adjustment/influence of player dice over time... +## RNG Alternatives +Alternative RNG # Thanks None of this module would be possible without the inspiration, and continued guidance/support/feedback, from @apoapostolov. Thank you! \ No newline at end of file diff --git a/docs/die-hard-config.jpg b/docs/die-hard-config.jpg new file mode 100644 index 0000000..8566fe5 Binary files /dev/null and b/docs/die-hard-config.jpg differ diff --git a/docs/die-hard-fudge-2.jpg b/docs/die-hard-fudge-2.jpg index de257cf..c542b01 100644 Binary files a/docs/die-hard-fudge-2.jpg and b/docs/die-hard-fudge-2.jpg differ diff --git a/docs/die-hard-karma-0.jpg b/docs/die-hard-karma-0.jpg new file mode 100644 index 0000000..00d20ff Binary files /dev/null and b/docs/die-hard-karma-0.jpg differ diff --git a/docs/die-hard-karma-1.jpg b/docs/die-hard-karma-1.jpg new file mode 100644 index 0000000..6c40a9d Binary files /dev/null and b/docs/die-hard-karma-1.jpg differ diff --git a/docs/die-hard-karma-2.jpg b/docs/die-hard-karma-2.jpg new file mode 100644 index 0000000..eac8dc2 Binary files /dev/null and b/docs/die-hard-karma-2.jpg differ diff --git a/docs/die-hard-karma.jpg b/docs/die-hard-karma.jpg new file mode 100644 index 0000000..8916467 Binary files /dev/null and b/docs/die-hard-karma.jpg differ diff --git a/module.json b/module.json index b0d7e34..f9ced50 100644 --- a/module.json +++ b/module.json @@ -3,7 +3,7 @@ "title": "Die Hard", "description": "Adjustments for DND5e & PF2e die rolls like fudging...", "author": "Jeremy (UranusBytes)", - "version": "0.0.8", + "version": "0.0.9", "minimumCoreVersion": "9", "compatibleCoreVersion": "9", "esmodules": [ diff --git a/scripts/classes/DieHardFudgeD20Roll.js b/scripts/classes/DISABLED_DieHardFudgeD20Roll.js similarity index 91% rename from scripts/classes/DieHardFudgeD20Roll.js rename to scripts/classes/DISABLED_DieHardFudgeD20Roll.js index 5e15903..e45d500 100644 --- a/scripts/classes/DieHardFudgeD20Roll.js +++ b/scripts/classes/DISABLED_DieHardFudgeD20Roll.js @@ -9,7 +9,7 @@ Because this extends D20Roll, I could not find a clean way to import. I tried m import D20Roll from '../../../../systems/dnd5e/module/dice/d20-roll.js' import {dieHardLog} from "../lib/helpers.js"; -export default class DieHardFudgeD20Roll extends D20Roll { +export default class DISABLED_DieHardFudgeD20Roll extends D20Roll { // This is a simple extension constructor(formula, data, options) { super(formula, data, options); diff --git a/scripts/classes/DieHardTests.js b/scripts/classes/DISABLED_DieHardTests.js similarity index 100% rename from scripts/classes/DieHardTests.js rename to scripts/classes/DISABLED_DieHardTests.js diff --git a/scripts/classes/DieHard.js b/scripts/classes/DieHard.js index ae1ceda..e491d10 100644 --- a/scripts/classes/DieHard.js +++ b/scripts/classes/DieHard.js @@ -1,46 +1,239 @@ -import {dieHardLog, insertAfter} from "../lib/helpers.js"; -import DieHardFudgeDialog from "./DieHardFudgeDialog.js"; +import {dieHardLog, insertAfter} from '../lib/helpers.js'; +import DieHardFudgeDialog from './DieHardFudgeDialog.js'; +import DieHardKarmaDialog from './DieHardKarmaDialog.js'; +import DieHardDnd5e from './DieHardDnd5e.js'; +import DieHardPf2e from './DieHardPf2e.js'; +export const DieHardSetting = (setting) => game.settings.get('foundry-die-hard', setting); export default class DieHard { constructor() { dieHardLog(true, 'DieHard - constructor'); + } + + static renderDieHardIcons() { + dieHardLog(false, 'DieHard.renderDieHardIcons') + if (DieHardSetting('dieHardSettings').system == null) { + dieHardLog(false, 'Unsupported system for world; not rendering side bar') + return + } + dieHardLog(false, 'Render side bar') + + if (document.querySelector('.die-hard-pause-fudge-icon') === null) { + let fudgeButton = document.createElement('label'); + fudgeButton.classList.add('die-hard-fudge-icon'); + fudgeButton.innerHTML = ''; + fudgeButton.addEventListener('click', async (ev) => { + new DieHardFudgeDialog().render(true); + }); + fudgeButton.addEventListener('contextmenu', async (ev) => { + game.settings.get('foundry-die-hard', 'dieHardSettings').system.disableAllFudges(); + }); + + // ToDo: Fix this ugly hack + // the document object isn't existing sometimes yet, so just ignore. It'll eventually render + try { + //insertAfter(pauseButton, document.querySelector('.chat-control-icon')); + insertAfter(fudgeButton, document.querySelector('.chat-control-icon')); + //DieHardSetting('dieHardSettings').system.refreshActiveFudgesIcon() + } + catch (e) { } + } - // Setup default settings; + if (document.querySelector('.die-hard-karma-icon') === null) { + let karmaButton = document.createElement('label'); + karmaButton.classList.add('die-hard-karma-icon'); + karmaButton.innerHTML = ''; + karmaButton.addEventListener('click', async (ev) => { + new DieHardKarmaDialog().render(true); + }); + // ToDo: Fix this ugly hack + // the document object isn't existing sometimes yet, so just ignore. It'll eventually render + try { + insertAfter(karmaButton, document.querySelector('.chat-control-icon')); + } catch (e) { + } + } + DieHard.refreshDieHardIcons(); } - init() { - dieHardLog(true, 'DieHard - init'); + static getDefaultDieHardSettings() { + dieHardLog(false, 'DieHard.getDefaultDieHardSettings') + let dieHardSettings = { + system: null, + debug: { + allActors: true + }, + fudgeConfig: { + maxFudgeAttemptsPerRoll: 150, + globalDisable: false + }, + gmFudges: [] + }; + + if (game.system.id == 'dnd5e') { + dieHardLog(true, 'Configuring for dndn5e system') + dieHardSettings.system = new DieHardDnd5e; + } else if (game.system.id == 'pf2e') { + dieHardLog(true, 'Configuring for pf2e system') + dieHardSettings.system = new DieHardPf2e; + } else { + dieHardLog(true, 'Unsupport game system: ' + game.system.id) + } + return dieHardSettings } - static renderFudgeIcon() { - if (game.settings.get('foundry-die-hard', 'dieHardSettings').system == null) { - dieHardLog(false, 'Unsupported system for world; not rendering side bar') - return + static getDefaultSimpleKarmaSettings() { + return { + enabled: true, + history: 5, + threshold: 5, + floor: 5 } - dieHardLog(false, 'Render side bar') - let fudgeButton = document.createElement('label'); - //fudgeButton.setAttribute('id', 'die-hard-fudge-icon'); - fudgeButton.classList.add('die-hard-fudge-icon'); - // fudgeButton.innerHTML = '
'; - fudgeButton.innerHTML = ''; - - fudgeButton.addEventListener("click", async (ev) => { - new DieHardFudgeDialog().render(true); - }); - fudgeButton.addEventListener("contextmenu", async (ev) => { - game.settings.get('foundry-die-hard', 'dieHardSettings').system.disableAllFudges(); - }); - - // ToDo: Fix this ugly hack - // the document object isn't existing sometimes yet, so just ignore. It'll eventually render - try { - //insertAfter(pauseButton, document.querySelector('.chat-control-icon')); - insertAfter(fudgeButton, document.querySelector('.chat-control-icon')); - game.settings.get('foundry-die-hard', 'dieHardSettings').system.refreshActiveFudgesIcon() + } + + static getAvgSimpleKarmaSettings() { + return { + enabled: true, + history: 5, + threshold: 5, + nudge: 5 + } + } + + static registerSettings() { + dieHardLog(false, 'DieHard.registerSettings') + + // Enables fudge + game.settings.register('foundry-die-hard', 'fudgeEnabled', { + name: 'Fudge Enabled', + hint: 'Fudge Enabled', + scope: 'world', + config: true, + default: true, + type: Boolean, + onChange: DieHard.refreshDieHardStatus + }); + + // Enables karma + game.settings.register('foundry-die-hard', 'karmaEnabled', { + name: 'Karma Enabled', + hint: 'Karma Enabled', + scope: 'world', + config: true, + default: true, + type: Boolean, + onChange: DieHard.refreshDieHardStatus + }); + + // Enables debug die + game.settings.register('foundry-die-hard', 'debugDieResultEnabled', { + name: 'Debug Die Result Enabled', + hint: 'Enable the use of Debug Die Result', + scope: 'world', + config: true, + default: true, + type: Boolean + }); + game.settings.register('foundry-die-hard', 'debugDieResult', { + name: 'Debug Die Result', + hint: 'Make every initial roll of die value', + scope: 'world', + config: true, + default: 5, + type: Number, + range: { + min: 1, + max: 20, + step: 1 + } + }); + + game.settings.register('foundry-die-hard', 'dieHardSettings', { + name: '', + default: DieHard.getDefaultDieHardSettings(), + type: Object, + scope: 'world', + config: false, + }); + + // Enables fudge + game.settings.register('foundry-die-hard', 'simpleKarmaSettings', { + name: 'Simple Karma Settings', + hint: 'Simple Karma Settings', + scope: 'world', + config: false, + default: DieHard.getDefaultSimpleKarmaSettings(), + type: Object + }); + + // Enables karma + game.settings.register('foundry-die-hard', 'avgKarmaSettings', { + name: 'Average Karma Settings', + hint: 'Average Karma Settings', + scope: 'world', + config: false, + default: DieHard.getAvgSimpleKarmaSettings(), + type: Object + }); + + + } + + static async refreshDieHardStatus() { + dieHardLog(false, 'DieHard.refreshDieHardStatus'); + await DieHard.refreshDieHardIcons() + DieHardSetting('dieHardSettings').system.refreshActiveFudgesIcon() + } + + static async refreshDieHardIcons() { + dieHardLog(false, 'DieHard.refreshDieHardIcons'); + // Ugly fix for objects not existing yet + // ToDo: clean this up + try{ + if (DieHardSetting('fudgeEnabled')) { + if (DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + document.getElementById('die-hard-pause-fudge-icon').classList.remove('die-hard-icon-hidden'); + document.getElementById('die-hard-fudge-icon').classList.add('die-hard-icon-hidden'); + return; + } else { + document.getElementById('die-hard-pause-fudge-icon').classList.add('die-hard-icon-hidden'); + document.getElementById('die-hard-fudge-icon').classList.remove('die-hard-icon-hidden'); + } + if (DieHardSetting('dieHardSettings').system.hasActiveFudges()) { + document.getElementById('die-hard-fudge-icon').classList.add('die-hard-fudge-icon-active'); + } else { + document.getElementById('die-hard-fudge-icon').classList.remove('die-hard-fudge-icon-active'); + } + } else { + document.getElementById('die-hard-pause-fudge-icon').classList.add('die-hard-icon-hidden'); + document.getElementById('die-hard-fudge-icon').classList.add('die-hard-icon-hidden'); + } + + if (DieHardSetting('karmaEnabled')) { + document.getElementById('die-hard-karma-icon').classList.remove('die-hard-icon-hidden'); + } else { + document.getElementById('die-hard-karma-icon').classList.add('die-hard-icon-hidden'); + } } catch (e) { } } + + static async dmToGm(message) { + var dm_ids = []; + for (let user of game.users.values()) { + if (user.isGM) { + dm_ids.push(user.id) + } + } + ChatMessage.create({ + user: game.user.id, + type: CONST.CHAT_MESSAGE_TYPES.WHISPER, + whisper: dm_ids, + blind: true, + content: message + }) + } } \ No newline at end of file diff --git a/scripts/classes/DieHardConfig.js b/scripts/classes/DieHardConfig.js deleted file mode 100644 index 2941b2c..0000000 --- a/scripts/classes/DieHardConfig.js +++ /dev/null @@ -1,75 +0,0 @@ -import {dieHardLog} from "../lib/helpers.js"; -import DieHardDnd5e from "./DieHardDnd5e.js"; -import DieHardPf2e from "./DieHardPf2e.js"; - -export default class DieHardConfig { - // static get defaultOptions() { - // dieHardLog(true,'DieHardConfig - defaultOptions') - // return { - // system: null, - // fudgeConfig: { - // maxFudgeAttemptsPerRoll: 150 - // }, - // activeFudges: { - // actorId: { - // whatId: { - // - // } - // } - // }, - // pendingFudge: { - // who: null, - // what: null, - // how: null - // } - // }; - // } - - constructor() { - dieHardLog(true, 'DieHardConfig - constructor'); - - // Setup default settings; - - } - - init() { - dieHardLog(true, 'DieHardConfig - init'); - } - - getData() { - - } - - static registerSettings() { - dieHardLog(true, 'DieHardConfig - registerSettings') - let dieHardSettings = { - system: null, - debug: { - allActors: true - }, - fudgeConfig: { - maxFudgeAttemptsPerRoll: 150, - globalDisable: false - }, - gmFudges: [] - }; - - if (game.system.id == 'dnd5e') { - dieHardLog(true, 'Configuring for dndn5e system') - dieHardSettings.system = new DieHardDnd5e; - } else if (game.system.id == 'pf2e') { - dieHardLog(true, 'Configuring for pf2e system') - dieHardSettings.system = new DieHardPf2e; - } else { - dieHardLog(true, 'Unsupport game system: ' + game.system.id) - } - - game.settings.register('foundry-die-hard', 'dieHardSettings', { - name: '', - default: dieHardSettings, - type: Object, - scope: 'world', - config: false, - }); - } -} \ No newline at end of file diff --git a/scripts/classes/DieHardDnd5e.js b/scripts/classes/DieHardDnd5e.js index 1df2fd3..fd2eadc 100644 --- a/scripts/classes/DieHardDnd5e.js +++ b/scripts/classes/DieHardDnd5e.js @@ -1,11 +1,12 @@ import {dieHardLog} from "../lib/helpers.js"; import DieHardSystem from "./DieHardSystem.js"; -import DieHardFudgeD20Roll from "./DieHardFudgeD20Roll.js"; +import DISABLED_DieHardFudgeD20Roll from "./DISABLED_DieHardFudgeD20Roll.js"; +import {DieHardSetting} from "./DieHard.js"; export default class DieHardDnd5e extends DieHardSystem{ constructor() { - dieHardLog(false, 'DieHardDnd5e - constructor'); + dieHardLog(false, 'DieHardDnd5e.constructor'); super(); libWrapper.register('foundry-die-hard', 'CONFIG.Actor.documentClass.prototype.rollAbilitySave', this.actorRollAbilitySave, 'WRAPPER'); @@ -17,8 +18,8 @@ export default class DieHardDnd5e extends DieHardSystem{ libWrapper.register('foundry-die-hard', 'game.dnd5e.dice.D20Roll.prototype._evaluate', this.d20rollEvaluate, 'WRAPPER'); - // See notes in DieHardFudgeD20Roll - CONFIG.Dice.DieHardFudgeD20Roll = DieHardFudgeD20Roll; + // See notes in DISABLED_DieHardFudgeD20Roll + CONFIG.Dice.DieHardFudgeD20Roll = DISABLED_DieHardFudgeD20Roll; this.totalRollClassName = ["Roll", "D20Roll"] this.fudgeWhatOptions = [ @@ -45,19 +46,10 @@ export default class DieHardDnd5e extends DieHardSystem{ ] } - - hookReady() { dieHardLog(false, 'Dnd 5e System Hook - Ready'); } - isPromise(p) { - if (typeof p === 'object' && typeof p.then === 'function') { - return true; - } - - return false; - } fudgeD20Roll(result, evaluate_options) { let functionLogName = 'DieHardDnd5e.fudgeD20Roll' @@ -81,7 +73,7 @@ export default class DieHardDnd5e extends DieHardSystem{ SafetyLoopIndex--; // ToDo: Can a "clone()" or a "reroll()" be used instead? https://foundryvtt.com/api/Roll.html#clone - const new_roll = new DieHardFudgeD20Roll( + const new_roll = new DISABLED_DieHardFudgeD20Roll( result.formula, result.data, { flavor: result.options.flavor, @@ -146,120 +138,79 @@ export default class DieHardDnd5e extends DieHardSystem{ let result = wrapped.call(evaluate_options) // If a fudge re-roll is allowed if (fudge){ - result.then(function(value) {game.settings.get('foundry-die-hard', 'dieHardSettings').system.fudgeD20Roll(value, evaluate_options)}) + result.then(function(value) {DieHardSetting('dieHardSettings').system.fudgeD20Roll(value, evaluate_options)}) } - /* - Original WORKING ??? - //let result = wrapped(evaluate_options); - ORIGINAL - let result = wrapped.bind(this)(evaluate_options); - // If a fudge re-roll is allowed - if (fudge){ - result.then((value) => game.settings.get('foundry-die-hard', 'dieHardSettings').system.fudgeD20Roll(value, evaluate_options)); - } - */ - return result } - /** - * Generic wrapper for all rolls - * @param options - * @param actorId - * @param rollType - */ - wrappedRoll(options, actorId, rollType) { + wrappedRoll(options, actorId, rollType) { let functionLogName = 'DieHardDnd5e.wrappedRoll' - //dieHardLog(false, 'DieHardDnd5e.wrappedRoll - this', this); - //dieHardLog(false, 'DieHardDnd5e.wrappedRoll - options', options); - //dieHardLog(false, 'DieHardDnd5e.wrappedRoll - actorId', actorId); dieHardLog(false, functionLogName + ' - rollType', rollType); - // Check if actor has an active fudge - let actorFudges = game.actors.get(actorId).getFlag('foundry-die-hard', 'fudges'); - if (! Array.isArray(actorFudges)) { - actorFudges = [] - } - dieHardLog(false, functionLogName + ' - actorFudges', actorFudges); - let fudgeIndex = actorFudges.findIndex(element => { return ((element.whatId === rollType) && (element.statusActive));}); - if (fudgeIndex !== -1) { - dieHardLog(false, functionLogName + ' - active actor fudge', actorFudges[fudgeIndex]); - foundry.utils.mergeObject(options, {data: {fudge: true, fudgeOperator: actorFudges[fudgeIndex].operator, fudgeOperatorValue: actorFudges[fudgeIndex].operatorValue, fudgeHow: actorFudges[fudgeIndex].howFormula }}); - if (actorFudges[fudgeIndex].statusEndless) { - dieHardLog(false, functionLogName + ' - fudge is endless'); - } else { - // Disable the fudge - actorFudges[fudgeIndex].statusActive = false - - // Delete the fudge from the actor - // let deletedFudge = actorFudges.splice(fudgeIndex, 1) - game.actors.get(actorId).setFlag('foundry-die-hard', 'fudges', actorFudges); - // Check if still have active fudges; - this.refreshActiveFudgesIcon(); - } - } - - // Check if user has an active fudge - let userFudges = game.users.current.getFlag('foundry-die-hard', 'fudges'); - if (! Array.isArray(userFudges)) { - userFudges = [] - } - dieHardLog(false, functionLogName + ' - userFudges', userFudges); - fudgeIndex = userFudges.findIndex(element => { return ((element.whatId === rollType) && (element.statusActive));}); - if (fudgeIndex !== -1) { - dieHardLog(false, functionLogName + ' - active user fudge', userFudges[fudgeIndex]); - foundry.utils.mergeObject(options, {data: {fudge: true, fudgeOperator: userFudges[fudgeIndex].operator, fudgeOperatorValue: userFudges[fudgeIndex].operatorValue, fudgeHow: userFudges[fudgeIndex].howFormula }}); - if (userFudges[fudgeIndex].statusEndless) { - dieHardLog(false, functionLogName + ' - fudge is endless'); - } else { - // Disable the fudge - userFudges[fudgeIndex].statusActive = false - - // Delete the fudge from the user - // let deletedFudge = userFudges.splice(fudgeIndex, 1) - game.users.current.setFlag('foundry-die-hard', 'fudges', userFudges); - // Check if still have active fudges; - this.refreshActiveFudgesIcon(); + if (! DieHardSetting('fudgeEnabled') ) { + dieHardLog(true, functionLogName + ' - Fudge disabled'); + } else if (DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + dieHardLog(true, functionLogName + ' - Fudging Globally disabled'); + } else { + // Check if user has an active fudge + let userFudge = this.getUserFudge(rollType) + if (userFudge !== null) { + dieHardLog(false, functionLogName + ' - active user fudge', userFudge); + foundry.utils.mergeObject(options, { + data: { + fudge: true, + fudgeOperator: userFudge.operator, + fudgeOperatorValue: userFudge.operatorValue, + fudgeHow: userFudge.howFormula + } + }); + if (userFudge.statusEndless) { + dieHardLog(false, functionLogName + ' - fudge is endless'); + } else { + // Disable the fudge + this.disableUserFudge(userFudge.id) + } } } } actorRollSkill(wrapped, skillId, options={}) { dieHardLog(false, 'DieHardDnd5e.actorRollSkill', this); - if (!game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { - game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollSkill') + if (!DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + DieHardSetting('dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollSkill') } wrapped(skillId, options); } actorRollAbilitySave(wrapped, abilityId, options={}) { dieHardLog(false, 'DieHardDnd5e.actorRollAbilitySave', this); - if (!game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { - game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollAbilitySave') + if (!DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + DieHardSetting('dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollAbilitySave') } wrapped(abilityId, options); } actorRollAbilityTest(wrapped, abilityId, options={}) { dieHardLog(false, 'DieHardDnd5e.actorRollAbilityTest', this); - if (!game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { - game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollAbilityTest') + if (!DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + DieHardSetting('dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollAbilityTest') } wrapped(abilityId, options); } actorRollDeathSave(wrapped, options={}) { dieHardLog(false, 'DieHardDnd5e.actorRollDeathSave', this); - if (!game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { - game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollDeathSave') + if (!DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + DieHardSetting('dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollDeathSave') } wrapped(options); } entityRollAttack(wrapped, options={}) { dieHardLog(false, 'DieHardDnd5e.entityRollAttack', this); - if (!game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { - game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.actor.id, 'entityRollAttack') + if (!DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + DieHardSetting('dieHardSettings').system.wrappedRoll(options, this.actor.id, 'entityRollAttack') } wrapped(options); } diff --git a/scripts/classes/DieHardFudgeDialog.js b/scripts/classes/DieHardFudgeDialog.js index addc26b..41c718c 100644 --- a/scripts/classes/DieHardFudgeDialog.js +++ b/scripts/classes/DieHardFudgeDialog.js @@ -1,4 +1,5 @@ import {dieHardLog} from "../lib/helpers.js"; +import DieHard, {DieHardSetting} from "./DieHard.js"; export default class DieHardFudgeDialog extends FormApplication { static get defaultOptions() { @@ -23,7 +24,7 @@ export default class DieHardFudgeDialog extends FormApplication { this.operatorValue = null; Hooks.once('closeApplication', (app, html) => { if (app.id === 'die-hard-fudge-config') { - game.settings.get('foundry-die-hard', 'dieHardSettings').system.refreshActiveFudgesIcon() + DieHardSetting('dieHardSettings').system.refreshActiveFudgesIcon() } }) } @@ -31,7 +32,7 @@ export default class DieHardFudgeDialog extends FormApplication { async getData() { dieHardLog(false, 'DieHardFudgeDialog - getData') let activeFudges = [] - let allFudges = game.settings.get('foundry-die-hard', 'dieHardSettings').system.getAllFudges() + let allFudges = DieHardSetting('dieHardSettings').system.getAllFudges() for (let fudgeType in allFudges) { dieHardLog(false, 'DieHardFudgeDialog - fudgeType', fudgeType, allFudges[fudgeType]) for (let typeObject in allFudges[fudgeType]) { @@ -46,10 +47,10 @@ export default class DieHardFudgeDialog extends FormApplication { } let dialogData = { - whoGmOptions: game.settings.get('foundry-die-hard', 'dieHardSettings').system.getFudgeWhoGmOptions(), - whoUserOptions: game.settings.get('foundry-die-hard', 'dieHardSettings').system.getFudgeWhoUserOptions(), - whoActorOptions: game.settings.get('foundry-die-hard', 'dieHardSettings').system.getFudgeWhoActorOptions(), - whatOptions: game.settings.get('foundry-die-hard', 'dieHardSettings').system.getFudgeWhatOptions(), + whoGmOptions: DieHardSetting('dieHardSettings').system.getFudgeWhoGmOptions(), + whoUserOptions: DieHardSetting('dieHardSettings').system.getFudgeWhoUserOptions(), + //whoActorOptions: DieHardSetting('dieHardSettings').system.getFudgeWhoActorOptions(), + whatOptions: DieHardSetting('dieHardSettings').system.getFudgeWhatOptions(), activeFudges: activeFudges }; dieHardLog(false, 'DieHardFudgeDialog - dialogData', dialogData) @@ -248,11 +249,11 @@ export default class DieHardFudgeDialog extends FormApplication { } let whatOption = {} if (formData.fudgeWhat.slice(0,3) === 'raw') { - whatOption = game.settings.get('foundry-die-hard', 'dieHardSettings').system.getFudgeWhatRawOptions().find(element => element.id === formData.fudgeWhat); + whatOption = DieHardSetting('dieHardSettings').system.getFudgeWhatRawOptions().find(element => element.id === formData.fudgeWhat); } else if (formData.fudgeWhat.slice(0,5) === 'total') { - whatOption = game.settings.get('foundry-die-hard', 'dieHardSettings').system.getFudgeWhatTotalOptions().find(element => element.id === formData.fudgeWhat); + whatOption = DieHardSetting('dieHardSettings').system.getFudgeWhatTotalOptions().find(element => element.id === formData.fudgeWhat); } else { - whatOption = game.settings.get('foundry-die-hard', 'dieHardSettings').system.getFudgeWhatOptions().find(element => element.id === formData.fudgeWhat); + whatOption = DieHardSetting('dieHardSettings').system.getFudgeWhatOptions().find(element => element.id === formData.fudgeWhat); } let fudgeTimes = 1; diff --git a/scripts/classes/DieHardKarmaDialog.js b/scripts/classes/DieHardKarmaDialog.js new file mode 100644 index 0000000..c4c917f --- /dev/null +++ b/scripts/classes/DieHardKarmaDialog.js @@ -0,0 +1,115 @@ +import {dieHardLog} from "../lib/helpers.js"; +import DieHard, {DieHardSetting} from "./DieHard.js"; + +export default class DieHardKarmaDialog extends FormApplication { + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ['form'], + closeOnSubmit: false, + submitOnChange: true, + submitOnClose: true, + popOut: true, + editable: game.user.isGM, + width: 500, + template: 'modules/foundry-die-hard/templates/die-hard-karma-config.html', + id: 'die-hard-karma-config', + title: 'Die Hard Karma Config', + }); + } + + constructor() { + dieHardLog(false, 'DieHardKarmaDialog.constructor') + super(); + Hooks.once('closeApplication', (app, html) => { + if (app.id === 'die-hard-karma-config') { + DieHard.refreshDieHardIcons() + } + }) + } + + async getData() { + dieHardLog(false, 'DieHardKarmaDialog.getData') + + let dialogData = { + simpleKarma: DieHardSetting('simpleKarmaSettings'), + simpleKarmaPlayerStats: this.getkarmaPlayerStats('simpleKarma'), + avgKarma: DieHardSetting('avgKarmaSettings'), + avgKarmaPlayerStats: this.getkarmaPlayerStats('avgKarma') + }; + dieHardLog(false, 'DieHardKarmaDialog.getData', dialogData) + return dialogData; + } + + getkarmaPlayerStats(karmaType) { + let playerStats = [] + for (let userId of game.users.keys()) { + let curUser = game.users.get(userId); + dieHardLog(false, 'DieHardKarmaDialog.getkarmaPlayerStats - game.users[user]', curUser) + let curUserStats = curUser.getFlag('foundry-die-hard', karmaType) + if (!Array.isArray(curUserStats)) { + curUserStats = [] + } + let curUserAvg = Math.round((curUserStats.reduce((a, b) => a + b, 0) / curUserStats.length) * 10) /10 + if (isNaN(curUserAvg)) { + curUserAvg = 0 + } + playerStats.push({ + name: curUser.name, + stats: curUserStats, + statsString: curUserStats.join(', '), + avg: curUserAvg + }) + } + return playerStats + } + + async _updateObject(event, formData) { + dieHardLog(false, 'DieHardKarmaDialog._updateObject') + + let originalKarmaSimpleSettings = game.settings.get('foundry-die-hard', 'simpleKarmaSettings') + let karmaSimpleSettings = { + enabled: formData.karmaSimpleEnabled, + history: formData.karmaSimpleHistory, + threshold: formData.karmaSimpleThreshold, + floor: formData.karmaSimpleFloor + } + await game.settings.set('foundry-die-hard', 'simpleKarmaSettings', karmaSimpleSettings) + if (formData.karmaSimpleEnabled) { + document.getElementById('divKarmaSimpleHistory').style.display = ''; + document.getElementById('divKarmaSimpleThreshold').style.display = ''; + document.getElementById('divKarmaSimpleFloor').style.display = ''; + document.getElementById('divKarmaSimplePlayerStats').style.display = ''; + } else { + document.getElementById('divKarmaSimpleHistory').style.display = 'none'; + document.getElementById('divKarmaSimpleThreshold').style.display = 'none'; + document.getElementById('divKarmaSimpleFloor').style.display = 'none'; + document.getElementById('divKarmaSimplePlayerStats').style.display = 'none'; + } + + let originalKarmaAvgSettings = game.settings.get('foundry-die-hard', 'avgKarmaSettings') + let karmaAvgSettings = { + enabled: formData.karmaAvgEnabled, + history: formData.karmaAvgHistory, + threshold: formData.karmaAvgThreshold, + nudge: formData.karmaAvgNudge + } + document.getElementById('karmaAvgThresholdMore').innerText = formData.karmaAvgThreshold; + await game.settings.set('foundry-die-hard', 'avgKarmaSettings', karmaAvgSettings) + if (formData.karmaAvgEnabled) { + document.getElementById('divKarmaAvgHistory').style.display = ''; + document.getElementById('divKarmaAvgThreshold').style.display = ''; + document.getElementById('divKarmaAvgNudge').style.display = ''; + document.getElementById('divKarmaAvgPlayerStats').style.display = ''; + } else { + document.getElementById('divKarmaAvgHistory').style.display = 'none'; + document.getElementById('divKarmaAvgThreshold').style.display = 'none'; + document.getElementById('divKarmaAvgNudge').style.display = 'none'; + document.getElementById('divKarmaAvgPlayerStats').style.display = 'none'; + } + + if (originalKarmaSimpleSettings.enabled !== formData.karmaSimpleEnabled || originalKarmaAvgSettings.enabled !== formData.karmaAvgEnabled){ + this.setPosition({height: 'auto'}) + } + } +} + diff --git a/scripts/classes/DieHardPf2e.js b/scripts/classes/DieHardPf2e.js index abf83f3..aa193be 100644 --- a/scripts/classes/DieHardPf2e.js +++ b/scripts/classes/DieHardPf2e.js @@ -1,306 +1,19 @@ import {dieHardLog} from "../lib/helpers.js"; import DieHardSystem from "./DieHardSystem.js"; -//import DieHardFudgeD20Roll from "./DieHardFudgeD20Roll.js"; +import {DieHardSetting} from "./DieHard.js"; export default class DieHardPf2e extends DieHardSystem { constructor() { - dieHardLog(false, 'DieHardPf2e - constructor'); + dieHardLog(false, 'DieHardPf2e.constructor'); super(); - // ToDo: Disabled for now since not used - // libWrapper.register('foundry-die-hard', 'game.pf2e.Check.roll', this.wrappedCheckRoll, 'WRAPPER'); - this.totalRollClassName = ["CheckRoll", "StrikeAttackRoll"] this.fudgeWhatOptions = [] - /* - this.fudgeWhatOptions = [ - { - id: 'actorSavingThrow', - name: 'Saving Throw' - }, - { - id: 'actorSkillCheck', - name: 'Skill Check' - }, - { - id: 'actorMeleeStrike', - name: 'Melee Strike' - }, - { - id: 'actorRangedStrike', - name: 'Ranged Strike' - }, - { - id: 'actorSpellAttack', - name: 'Spell Attack' - }, - { - id: 'pf2eD20', - name: 'Any PF2e d20' - } - ] - */ } - hookReady() { dieHardLog(false, 'PF2e System Hook - Ready'); } - - isPromise(p) { - if (typeof p === 'object' && typeof p.then === 'function') { - return true; - } - - return false; - } -/* - fudgeD20Roll(result, evaluate_options) { - dieHardLog(false, 'DieHardDnd5e - fudgeD20Roll'); - // dieHardLog(false, 'DieHardDnd5e - fudgeD20Roll - result', result); - let fudgeOperator = result.data.fudgeOperator - let fudgeOperatorValue = result.data.fudgeOperatorValue - - let gen_new_result = false; - let evalResult = this.evalFudge(result.total, fudgeOperator, fudgeOperatorValue) - if (evalResult) { - dieHardLog(false, 'DieHardDnd5e - fudgeD20Roll: Fudge not needed, but still wiped from actor'); - this.dmToGm('DieHard-Fudge: Fudge not needed, but still wiped from actor...'); - } else { - gen_new_result = true; - let dmMessage = "Fudge (" + result.data.fudgeHow + ") values:" + result.total; - - // This is a safety to prevent endless loops from possibly sneaking in - let SafetyLoopIndex = game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.maxFudgeAttemptsPerRoll; - while (gen_new_result && SafetyLoopIndex > 0) { - SafetyLoopIndex--; - - // ToDo: Can a "clone()" or a "reroll()" be used instead? https://foundryvtt.com/api/Roll.html#clone - const new_roll = new DieHardFudgeD20Roll( - result.formula, - result.data, { - flavor: result.options.flavor, - advantageMode: result.options.advantageMode, - defaultRollMode: result.options.defaultRollMode, - rollMode: result.options.rollMode, - critical: result.options.critical, - fumble: result.options.fumble, - targetValue: result.options.targetValue, - elvenAccuracy: result.options.elvenAccuracy, - halflingLucky: result.options.halflingLucky, - reliableTalent: result.options.reliableTalent - }); - new_roll.evaluate({async: false, minimize: evaluate_options.minimize, maximize: evaluate_options.maximize}); - dieHardLog(false, 'DieHardDnd5e - fudgeD20Roll: New roll: ', new_roll) - - evalResult = this.evalFudge(new_roll.total, fudgeOperator, fudgeOperatorValue) - if (evalResult) { - dieHardLog(false, 'DieHardDnd5e - fudgeD20Roll: New result: ' + new_roll.total) - gen_new_result = false; - foundry.utils.mergeObject(result, new_roll); - this.dmToGm(dmMessage); - } else { - // New roll is insufficient, but lets at least check if it is "closer" - if (this.isBetterFudge(result.total, new_roll.total, fudgeOperator, fudgeOperatorValue)) { - dieHardLog(false, 'DieHardDnd5e - fudgeD20Roll: New result insufficient, but at least better (' + new_roll.total + "). Try again (tries left: " + SafetyLoopIndex + ")...") - foundry.utils.mergeObject(result, new_roll); - } else { - dieHardLog(false, 'DieHardDnd5e - fudgeD20Roll: New result insufficient (' + new_roll.total + "). Try again (tries left: " + SafetyLoopIndex + ")...") - } - dmMessage += ',' + new_roll.total; - } - } - if (SafetyLoopIndex === 0) { - dieHardLog(false, 'DieHardDnd5e - fudgeD20Roll: Tried until retry safety killed...'); - this.dmToGm('DieHard-Fudge: Gave up trying to fudge; loop safety reached...'); - } - } - - dieHardLog(false, 'Done with modify_results', result); - return result - } - - d20rollEvaluate(wrapped, evaluate_options) { - dieHardLog(false, 'DieHardDnd5e.d20rollEvaluate', this.data); - - let fudge = false; - if (this.data.fudge === true) { - evaluate_options.async = false; - - - if (this instanceof CONFIG.Dice.DieHardFudgeD20Roll) { - // This is a recursive roll; do sync - evaluate_options.async = false; - } else { - // This is a root roll, so allow fudge re-roll - fudge = true; - } - } else { - dieHardLog(false, 'DieHardDnd5e.d20rollEvaluate - No fudging planned for this roll'); - } - - let result = wrapped.call(evaluate_options) - // If a fudge re-roll is allowed - if (fudge) { - result.then(function (value) { - game.settings.get('foundry-die-hard', 'dieHardSettings').system.fudgeD20Roll(value, evaluate_options) - }) - } - - - return result - } - */ - /** - * Generic wrapper for all PF2e Check rolls - */ - - // Currently not used, since can't find way to intercept lower level roll - /* - wrappedCheckRoll(wrapped, check, context, event, callback) { - dieHardLog(true, 'DieHardPf2e.wrappedCheckRoll - this', this); - dieHardLog(true, 'DieHardPf2e.wrappedCheckRoll - arguments.length', arguments.length); - dieHardLog(true, 'DieHardPf2e.wrappedCheckRoll - arguments', arguments); - - if (game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { - dieHardLog(true, 'DieHardPf2e.wrappedCheckRoll - Globally disabled', game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable); - // Globally disabled - wrapped(check, context, event, callback); - return - } - - // Convert PF2e check to rollType - let rollType = 'pf2eD20' - if (check.name.indexOf('Saving Throw')) { - rollType = 'actorSavingThrow' - } else if (check.name.indexOf('Skill Check')) { - rollType = 'actorSkillCheck' - } else if (check.name.indexOf('Melee Strike')) { - rollType = 'actorMeleeStrike' - } else if (check.name.indexOf('Ranged Strike')) { - rollType = 'actorRangedStrike' - } else if (check.name.indexOf('Spell Attack')) { - rollType = 'actorSpellAttack' - } - - // Check if actor has an active fudge - let actorFudges = game.actors.get(context.actor.id).getFlag('foundry-die-hard', 'fudges'); - if (!Array.isArray(actorFudges)) { - actorFudges = [] - } - dieHardLog(false, 'DieHardPf2e.wrappedCheckRoll - actorFudges', actorFudges); - let fudgeIndex = actorFudges.findIndex(element => { - return element.whatId === rollType; - }); - if (fudgeIndex !== -1 && actorFudges[fudgeIndex].statusActive) { - dieHardLog(false, 'DieHardPf2e.wrappedCheckRoll - active actor fudge', actorFudges[fudgeIndex]); - foundry.utils.mergeObject(check, { - data: { - fudge: true, - fudgeOperator: actorFudges[fudgeIndex].operator, - fudgeOperatorValue: actorFudges[fudgeIndex].operatorValue, - fudgeHow: actorFudges[fudgeIndex].howFormula - } - }); - if (actorFudges[fudgeIndex].statusEndless) { - dieHardLog(false, 'DieHardSystem.wrappedRoll - fudge is endless'); - } else { - // Disable the fudge - actorFudges[fudgeIndex].statusActive = false - - // Delete the fudge from the actor - //let deletedFudge = actorFudges.splice(fudgeIndex, 1) - game.actors.get(context.actor.id).setFlag('foundry-die-hard', 'fudges', actorFudges); - // Check if still have active fudges; - game.settings.get('foundry-die-hard', 'dieHardSettings').system.refreshActiveFudgesIcon(); - } - dieHardLog(false, 'DieHardPf2e.wrappedCheckRoll - merged check', check); - } - - // Check if user has an active fudge - let userFudges = game.users.current.getFlag('foundry-die-hard', 'fudges'); - dieHardLog(false, 'DieHardPf2e.wrappedCheckRoll - userFudges', userFudges); - if (!Array.isArray(userFudges)) { - userFudges = [] - } - fudgeIndex = userFudges.findIndex(element => { - return element.whatId === rollType; - }); - if (fudgeIndex !== -1 && userFudges[fudgeIndex].statusActive) { - dieHardLog(false, 'DieHardPf2e.wrappedCheckRoll - active user fudge', userFudges[fudgeIndex]); - foundry.utils.mergeObject(check, { - data: { - fudge: true, - fudgeOperator: userFudges[fudgeIndex].operator, - fudgeOperatorValue: userFudges[fudgeIndex].operatorValue, - fudgeHow: userFudges[fudgeIndex].howFormula - } - }); - if (userFudges[fudgeIndex].statusEndless) { - dieHardLog(false, 'DieHardPf2e.wrappedCheckRoll - fudge is endless'); - } else { - // Disable the fudge - userFudges[fudgeIndex].statusActive = false - - // Delete the fudge from the user - // let deletedFudge = userFudges.splice(fudgeIndex, 1) - game.users.current.setFlag('foundry-die-hard', 'fudges', userFudges); - // Check if still have active fudges; - game.settings.get('foundry-die-hard', 'dieHardSettings').system.refreshActiveFudgesIcon(); - } - dieHardLog(false, 'DieHardPf2e.wrappedCheckRoll - merged check', check); - } - let result = wrapped(check, context, event, callback); - dieHardLog(false, 'DieHardPf2e.wrappedCheckRoll - result', result); - } - */ - -/* - actorRollSave(wrapped, skillId, options = {}) { - dieHardLog(true, 'DieHardPf2e.actorRollSave', this, arguments.length); - // game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollSkill') - wrapped(skillId, options); - } - - actorRollAttribute(wrapped, event, attributeName) { - dieHardLog(true, 'DieHardPf2e.actorRollAttribute', this, arguments.length, event, attributeName); - // game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollAbilitySave') - wrapped(abilityId, options); - } - - d20Roll(wrapped, event, item, pars, data, template, title, speaker, flavor, onClose, dialogOptions, rollMode, rollType) { - dieHardLog(true, 'DieHardPf2e.d20Roll', this, arguments.length, event, attributeName); - // game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollAbilitySave') - wrapped(abilityId, options); - } - - checkRoll(wrapped, check, context, event, callback) { - dieHardLog(true, 'DieHardPf2e.checkRoll - this', this); - dieHardLog(true, 'DieHardPf2e.checkRoll - arguments.length', arguments.length, check, context, event, callback); - // game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollAbilitySave') - wrapped(check, context, event, callback); - } -*/ - /* - actorRollAbilityTest(wrapped, abilityId, options = {}) { - dieHardLog(false, 'DieHardDnd5e.actorRollAbilityTest', this); - game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollAbilityTest') - wrapped(abilityId, options); - } - - actorRollDeathSave(wrapped, options = {}) { - dieHardLog(false, 'DieHardDnd5e.actorRollDeathSave', this); - game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.id, 'actorRollDeathSave') - wrapped(options); - } - - entityRollAttack(wrapped, options = {}) { - dieHardLog(false, 'DieHardDnd5e.entityRollAttack', this); - game.settings.get('foundry-die-hard', 'dieHardSettings').system.wrappedRoll(options, this.actor.id, 'entityRollAttack') - wrapped(options); - } - */ - } diff --git a/scripts/classes/DieHardSystem.js b/scripts/classes/DieHardSystem.js index ea13095..f97218e 100644 --- a/scripts/classes/DieHardSystem.js +++ b/scripts/classes/DieHardSystem.js @@ -1,14 +1,16 @@ -import {dieHardLog} from "../lib/helpers.js"; +import {dieHardLog, makeId} from "../lib/helpers.js"; import DieHardFudgeRoll from "./DieHardFudgeRoll.js"; -import DieHardFudgeD20Roll from "./DieHardFudgeD20Roll.js"; +import DieHard, {DieHardSetting} from "./DieHard.js"; -export default class DieHardSystem{ +export default class DieHardSystem { constructor() { - dieHardLog(false, 'DieHardSystem - constructor'); + dieHardLog(false, 'DieHardSystem.constructor'); - // Generic rolls + // Total rolls CONFIG.Dice.Roll = Roll; libWrapper.register('foundry-die-hard', 'CONFIG.Dice.Roll.prototype.evaluate', this.wrapRollEvaluate, 'WRAPPER'); + + // Raw rolls libWrapper.register('foundry-die-hard', 'CONFIG.Dice.termTypes.DiceTerm.prototype.roll', this.wrapDiceTermRoll, 'MIXED'); CONFIG.Dice.DieHardFudgeRoll = DieHardFudgeRoll; @@ -74,23 +76,29 @@ export default class DieHardSystem{ id: 'totald4', name: 'Total d4' } - ] + ] } evalFudge(result, operator, operatorValue) { - dieHardLog(false, 'DieHardSystem - evalFudge', result, operator, operatorValue); + dieHardLog(false, 'DieHardSystem.evalFudge', result, operator, operatorValue); switch (operator) { - case '>': return result > operatorValue; - case '<': return result < operatorValue; - case '>=': return result >= operatorValue; - case '<=': return result <= operatorValue; - case '!=': return result !== operatorValue; - case '=': return result === operatorValue; + case '>': + return result > operatorValue; + case '<': + return result < operatorValue; + case '>=': + return result >= operatorValue; + case '<=': + return result <= operatorValue; + case '!=': + return result !== operatorValue; + case '=': + return result === operatorValue; } } isBetterFudge(oldTotal, newTotal, operator, operatorValue) { - dieHardLog(false, 'DieHardSystem - isBetterFudge', oldTotal, newTotal, operator, operatorValue); + dieHardLog(false, 'DieHardSystem.isBetterFudge', oldTotal, newTotal, operator, operatorValue); switch (operator) { case '>': return newTotal > oldTotal; @@ -107,294 +115,345 @@ export default class DieHardSystem{ } } - wrapDiceTermRoll(wrapped, eval_options) { - let functionLogName = 'DieHardSystem.wrapDiceTermRoll' - dieHardLog(false, functionLogName); - dieHardLog(false, functionLogName + ' - this', this); - dieHardLog(false, functionLogName + ' - eval_options', eval_options); - - if (game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { - dieHardLog(true, functionLogName + ' - Globally disabled', game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable); - // Globally disabled - return wrapped(eval_options) - } - - // Check if user has an active raw fudge + getUserFudge(fudgeType) { + dieHardLog(false, 'DieHardSystem.getUserFudge'); let userFudges = game.users.current.getFlag('foundry-die-hard', 'fudges'); - if (! Array.isArray(userFudges)) { - userFudges = [] + dieHardLog(false, 'DieHardSystem.getUserFudge - userFudges', userFudges); + if (!Array.isArray(userFudges)) { + return null } - let fudgeIndex = userFudges.findIndex(element => { return ((element.whatId === ('rawd' + this.faces)) && (element.statusActive));}); + let fudgeIndex = userFudges.findIndex(element => { + return ((element.whatId === fudgeType) && (element.statusActive)); + }); + dieHardLog(false, 'DieHardSystem.getUserFudge - fudgeIndex', fudgeIndex); if (fudgeIndex !== -1) { - dieHardLog(false, functionLogName + ' - active user raw fudge', userFudges[fudgeIndex]); + return userFudges[fudgeIndex] + } else { + return null + } + } - // Time to make the fudge - let gen_new_result = true; - let failedRolls = []; - let SafetyLoopIndex = game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.maxFudgeAttemptsPerRoll; - let newResult = undefined - let roll = {result: undefined, active: true}; - while (gen_new_result && SafetyLoopIndex > 0) { - SafetyLoopIndex--; + disableUserFudge(fudgeId) { + dieHardLog(false, 'DieHardSystem.disableUserFudge'); + let userFudges = game.users.current.getFlag('foundry-die-hard', 'fudges'); + let fudgeIndex = userFudges.findIndex(element => { + return (element.id === fudgeId); + }); + userFudges[fudgeIndex].statusActive = false + game.users.current.setFlag('foundry-die-hard', 'fudges', userFudges); + DieHard.refreshDieHardIcons() + } - // This is copied from resources/app/client/dice/terms/dice.js - rolls method - if ( eval_options.minimize ) roll.result = Math.min(1, this.faces); - else if ( eval_options.maximize ) newResult = this.faces; - else newResult = Math.ceil(CONFIG.Dice.randomUniform() * this.faces); + /** + * Wrapper for raw dice + * @param wrapped + * @param eval_options + * @returns {{result: undefined, active: boolean}|*} + */ + wrapDiceTermRoll(wrapped, eval_options) { + let functionLogName = 'DieHardSystem.wrapDiceTermRoll' + dieHardLog(false, functionLogName); - let evalResult = game.settings.get('foundry-die-hard', 'dieHardSettings').system.evalFudge(newResult, userFudges[fudgeIndex].operator, userFudges[fudgeIndex].operatorValue) - if (evalResult) { - dieHardLog(false, functionLogName + ' - New result: ' + newResult) - gen_new_result = false; - roll.result = newResult - this.results.push(roll); - game.settings.get('foundry-die-hard', 'dieHardSettings').system.dmToGm("Fudge (" + userFudges[fudgeIndex].howFormula + ") values: " + failedRolls.join(', ')); - } else { - // New roll is insufficient, but lets at least check if it is "closer" - if (game.settings.get('foundry-die-hard', 'dieHardSettings').system.isBetterFudge(roll.result, newResult, userFudges[fudgeIndex].operator, userFudges[fudgeIndex].operatorValue)) { - dieHardLog(false, functionLogName + ' - New result insufficient, but at least better (' + newResult + "). Try again (tries left: " + SafetyLoopIndex + ")...") + if (! DieHardSetting('fudgeEnabled') ) { + dieHardLog(true, functionLogName + ' - Fudge disabled'); + } else if (DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + dieHardLog(true, functionLogName + ' - Fudging Globally disabled'); + } else { + // Check if user has an active raw fudge + let userFudge = DieHardSetting('dieHardSettings').system.getUserFudge('rawd' + this.faces) + dieHardLog(true, functionLogName + ' - userFudge', userFudge); + if (userFudge !== null) { + dieHardLog(false, functionLogName + ' - active user raw fudge', userFudge); + + // Time to make the fudge + let gen_new_result = true; + let failedRolls = []; + let SafetyLoopIndex = DieHardSetting('dieHardSettings').fudgeConfig.maxFudgeAttemptsPerRoll; + let newResult = undefined + let roll = {result: undefined, active: true}; + while (gen_new_result && SafetyLoopIndex > 0) { + SafetyLoopIndex--; + + // ToDo: remove this hack + // Only force the roll on the first die + if (DieHardSetting('debugDieResultEnabled') && newResult === undefined) { + newResult = DieHardSetting('debugDieResult') + dieHardLog(true, functionLogName + ' - debugDieResult used', newResult); + } else { + // This is copied from resources/app/client/dice/terms/dice.js - rolls method + if (eval_options.minimize) roll.result = Math.min(1, this.faces); + else if (eval_options.maximize) newResult = this.faces; + else newResult = Math.ceil(CONFIG.Dice.randomUniform() * this.faces); + } + + let evalResult = DieHardSetting('dieHardSettings').system.evalFudge(newResult, userFudge.operator, userFudge.operatorValue) + if (evalResult) { + dieHardLog(false, functionLogName + ' - New result: ' + newResult) + gen_new_result = false; roll.result = newResult + this.results.push(roll); + DieHard.dmToGm("Raw Fudge (" + userFudge.howFormula + ") values: " + failedRolls.join(', ') + " Final: " + newResult); } else { - dieHardLog(false, functionLogName + ' - New result insufficient (' + newResult + "). Try again (tries left: " + SafetyLoopIndex + ")...") + // New roll is insufficient, but lets at least check if it is "closer" + if (DieHardSetting('dieHardSettings').system.isBetterFudge(roll.result, newResult, userFudge.operator, userFudge.operatorValue)) { + dieHardLog(false, functionLogName + ' - New result insufficient, but at least better (' + newResult + "). Try again (tries left: " + SafetyLoopIndex + ")...") + roll.result = newResult + } else { + dieHardLog(false, functionLogName + ' - New result insufficient (' + newResult + "). Try again (tries left: " + SafetyLoopIndex + ")...") + } + failedRolls.push(newResult); } - failedRolls.push(newResult); } + if (SafetyLoopIndex === 0) { + dieHardLog(false, functionLogName + ' - Tried until retry safety killed...'); + DieHard.dmToGm('DieHard-Fudge: Gave up trying to fudge; loop safety reached...'); + } + if (userFudge.statusEndless) { + dieHardLog(false, functionLogName + ' - fudge is endless'); + } else { + this.disableUserFudge(userFudge.id) + } + // Return the fudged roll; no taking karma into consideration + return roll } + // No user fudging to occur; continue roll as usual + } - if (userFudges[fudgeIndex].statusEndless) { - dieHardLog(false, functionLogName + ' - fudge is endless'); - } else { - // Disable the fudge - userFudges[fudgeIndex].statusActive = false - - // Delete the fudge from the user - // let deletedFudge = userFudges.splice(fudgeIndex,1) - game.users.current.setFlag('foundry-die-hard', 'fudges', userFudges); - // Check if still have active fudges; - game.settings.get('foundry-die-hard', 'dieHardSettings').system.refreshActiveFudgesIcon() - } - return roll + if (! DieHardSetting('karmaEnabled') ) { + dieHardLog(true, functionLogName + ' - Karma disabled'); + } else if (this.faces !== 20) { + dieHardLog(true, functionLogName + ' - Karma enabled, but wrong die type', this.faces); } else { - // No fudging to occur; let the normal roll occur - return wrapped(eval_options) - } - } + let simpleKarmaSettings = DieHardSetting('simpleKarmaSettings') + if (simpleKarmaSettings.enabled) { + dieHardLog(true, functionLogName + ' - Simple Karma', this); + let simpleKarmaData = game.users.current.getFlag('foundry-die-hard', 'simpleKarma') + if (!Array.isArray(simpleKarmaData)) { + simpleKarmaData = [] + } - wrapRollEvaluate(wrapped, eval_options) { - let functionLogName = 'DieHardSystem.wrapRollEvaluate' - dieHardLog(false, functionLogName); - dieHardLog(false, functionLogName + ' - arguments', arguments); - dieHardLog(false, functionLogName + ' - this', this); + let newResult = undefined + let roll = {result: undefined, active: true}; - // dieHardLog(true, 'DieHardDnd5e - wrapRollEvaluate - this.constructor.name', this.constructor.name); - dieHardLog(false, functionLogName + ' - eval_options', eval_options); - //dieHardLog(true, 'DieHardDnd5e - wrapRollEvaluate - game.users.current.data.name', game.users.current.data.name); - dieHardLog(true, functionLogName + ' - this.constructor.name', this.constructor.name); + // This is copied from resources/app/client/dice/terms/dice.js - rolls method + if (eval_options.minimize) roll.result = Math.min(1, this.faces); + else if (eval_options.maximize) newResult = this.faces; + else newResult = Math.ceil(CONFIG.Dice.randomUniform() * this.faces); - if (game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { - dieHardLog(true, functionLogName + ' - Globally disabled', game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable); - // Globally disabled - return wrapped(eval_options) - } + // ToDo: remove this hack + if (DieHardSetting('debugDieResultEnabled')) { + newResult = DieHardSetting('debugDieResult') + dieHardLog(true, functionLogName + ' - debugDieResult used', newResult); + } - // Check if a total die roll (otherwise some type of system specific roll) - if (game.settings.get('foundry-die-hard', 'dieHardSettings').system.totalRollClassName.indexOf(this.constructor.name) !== -1){ - dieHardLog(false, functionLogName + ' - total roll; figure out if needs to be fudged or is a recursive fudge'); + roll.result = newResult + simpleKarmaData.push(newResult) - let fudge = false; - for (let die in this.dice) { - if (typeof this.dice[die] === 'function') { - dieHardLog(false, functionLogName + ' - die is function; ignore'); - continue; + while (simpleKarmaData.length > simpleKarmaSettings.history) { + simpleKarmaData.shift() } + dieHardLog(true, functionLogName + ' - Simple Karma Data', simpleKarmaData); + let tempResult = simpleKarmaData.findIndex(element => {return element > simpleKarmaSettings.threshold}) + dieHardLog(true, functionLogName + ' - Simple Karma tempResult', tempResult); + if (simpleKarmaData.length === simpleKarmaSettings.history && tempResult === -1) { + dieHardLog(true, functionLogName + ' - Simple Karma adjustment needed', newResult); + let originalResult = newResult + while (newResult < simpleKarmaSettings.floor) { + // This is copied from resources/app/client/dice/terms/dice.js - rolls method + if (eval_options.minimize) roll.result = Math.min(1, this.faces); + else if (eval_options.maximize) newResult = this.faces; + else newResult = Math.ceil(CONFIG.Dice.randomUniform() * this.faces); + roll.result = newResult + dieHardLog(true, functionLogName + ' - Simple Karma adjustment - new result', newResult); + } - dieHardLog(false, functionLogName + ' - DEBUG POINT'); - // Check if actor has an active total fudge - // ToDo: something goes here... #6 - /* - // Check if actor has an active fudge - let actorFudges = game.actors.get(actorId).getFlag('foundry-die-hard', 'fudges'); - if (! Array.isArray(actorFudges)) { - actorFudges = [] + simpleKarmaData.push(newResult) + while (simpleKarmaData.length > simpleKarmaSettings.history) { + simpleKarmaData.shift() + } + DieHard.dmToGm('DieHard-Karma: Simple Karma for ' + game.users.current.name + ' adjusted a roll of ' + originalResult + ' to a ' + newResult); } - //dieHardLog(false, 'DieHardDnd5e.wrappedRoll - actorFudges', actorFudges); - let fudgeIndex = actorFudges.findIndex(element => { return element.whatId === rollType;}); - if (fudgeIndex !== -1 && actorFudges[fudgeIndex].statusActive) { - dieHardLog(false, 'DieHardDnd5e.wrappedRoll - active actor fudge', actorFudges[fudgeIndex]); - foundry.utils.mergeObject(options, {data: {fudge: true, fudgeOperator: actorFudges[fudgeIndex].operator, fudgeOperatorValue: actorFudges[fudgeIndex].operatorValue, fudgeHow: actorFudges[fudgeIndex].howFormula }}); - // Delete the fudge from the actor - let deletedFudge = actorFudges.splice(fudgeIndex,1) - game.actors.get(actorId).setFlag('foundry-die-hard', 'fudges', actorFudges); - // Check if still have active fudges; - this.refreshActiveFudgesIcon(); + + game.users.current.setFlag('foundry-die-hard', 'simpleKarma', simpleKarmaData) + this.results.push(roll); + return roll + } else { + dieHardLog(true, functionLogName + ' - Simple Karma disabled'); + } + + let avgKarmaSettings = DieHardSetting('avgKarmaSettings') + if (avgKarmaSettings.enabled) { + dieHardLog(true, functionLogName + ' - Avg Karma', this); + let avgKarmaData = game.users.current.getFlag('foundry-die-hard', 'avgKarma') + if (!Array.isArray(avgKarmaData)) { + avgKarmaData = [] } - */ + let newResult = undefined + let roll = {result: undefined, active: true}; + + // This is copied from resources/app/client/dice/terms/dice.js - rolls method + if (eval_options.minimize) roll.result = Math.min(1, this.faces); + else if (eval_options.maximize) newResult = this.faces; + else newResult = Math.ceil(CONFIG.Dice.randomUniform() * this.faces); - // Check if user has an active total fudge - let userFudges = game.users.current.getFlag('foundry-die-hard', 'fudges'); - if (! Array.isArray(userFudges)) { - userFudges = [] + // ToDo: remove this hack + if (DieHardSetting('debugDieResultEnabled')) { + newResult = DieHardSetting('debugDieResult') + dieHardLog(true, functionLogName + ' - debugDieResult used', newResult); } - let fudgeIndex = userFudges.findIndex(element => { return ((element.whatId === ('totald' + this.dice[die].faces)) && (element.statusActive));}); - dieHardLog(false, functionLogName + ' - dice faces', this.dice[die].faces); - if (fudgeIndex !== -1) { - dieHardLog(false, functionLogName + ' - active user total fudge', userFudges[fudgeIndex]); - foundry.utils.mergeObject(this, {data: {fudge: true, fudgeOperator: userFudges[fudgeIndex].operator, fudgeOperatorValue: userFudges[fudgeIndex].operatorValue, fudgeHow: userFudges[fudgeIndex].howFormula }}); + roll.result = newResult + avgKarmaData.push(newResult) - if (userFudges[fudgeIndex].statusEndless) { - dieHardLog(false, functionLogName + ' - fudge is endless'); - } else { - // Disable the fudge - userFudges[fudgeIndex].statusActive = false - - // Delete the fudge from the user - // let deletedFudge = userFudges.splice(fudgeIndex,1) - game.users.current.setFlag('foundry-die-hard', 'fudges', userFudges); - // Check if still have active fudges; - game.settings.get('foundry-die-hard', 'dieHardSettings').system.refreshActiveFudgesIcon() - } - // This is a root roll, so allow fudge re-roll - fudge = true; - // Stop looking for more opportunities to fudge - break + while (avgKarmaData.length > avgKarmaSettings.history) { + avgKarmaData.shift() } - } - let result = null - if (fudge) { - result = wrapped({minimize: eval_options.minimize, maximize: eval_options.maximize, async: false}) - dieHardLog(false, functionLogName + ' - roll can be fudged'); - if (this instanceof CONFIG.Dice.DieHardFudgeRoll) { - dieHardLog(false, functionLogName + 'e - recursive roll', this); - } else { - dieHardLog(false, functionLogName + ' - base roll', this); - dieHardLog(false, functionLogName + ' - result', result); - let gen_new_result = false; - let evalResult = game.settings.get('foundry-die-hard', 'dieHardSettings').system.evalFudge(this.total, this.data.fudgeOperator, this.data.fudgeOperatorValue) + dieHardLog(true, functionLogName + ' - Avg Karma Data', avgKarmaData); + let tempResult = avgKarmaData.reduce((a, b) => a + b, 0) / avgKarmaData.length; + dieHardLog(true, functionLogName + ' - Avg Karma tempResult', tempResult); + if (avgKarmaData.length === avgKarmaSettings.history && tempResult <= avgKarmaSettings.threshold) { + dieHardLog(true, functionLogName + ' - Avg Karma adjustment needed', newResult); + let originalResult = newResult + newResult += avgKarmaSettings.nudge + + if (newResult > this.faces) { + newResult = this.faces + } + roll.result = newResult + dieHardLog(true, functionLogName + ' - Avg Karma adjustment - new result', newResult); - if (evalResult) { - dieHardLog(false, functionLogName + ' - Fudge not needed, but still wiped'); - game.settings.get('foundry-die-hard', 'dieHardSettings').system.dmToGm('DieHard-Fudge: Fudge not needed, but still wiped...'); - } else { - gen_new_result = true; - dieHardLog(false, functionLogName + ' - Start fudging'); - let dmMessage = "Fudge (" + result.data.fudgeHow + ") values:" + result.total; - let SafetyLoopIndex = game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.maxFudgeAttemptsPerRoll; - while (gen_new_result && SafetyLoopIndex > 0) { - SafetyLoopIndex--; - let new_roll = new DieHardFudgeRoll(this._formula, this.data, this.options) - new_roll.evaluate({async:false}) - evalResult = game.settings.get('foundry-die-hard', 'dieHardSettings').system.evalFudge(new_roll.total, this.data.fudgeOperator, this.data.fudgeOperatorValue) - if (evalResult) { - dieHardLog(false, functionLogName + ' - New result: ' + new_roll.total) - gen_new_result = false; - foundry.utils.mergeObject(this, new_roll); - game.settings.get('foundry-die-hard', 'dieHardSettings').system.dmToGm(dmMessage); - } else { - // New roll is insufficient, but lets at least check if it is "closer" - if (game.settings.get('foundry-die-hard', 'dieHardSettings').system.isBetterFudge(this.total, new_roll.total, this.data.fudgeOperator, this.data.fudgeOperatorValue)) { - dieHardLog(false, functionLogName + ' - New result insufficient, but at least better (' + new_roll.total + "). Try again (tries left: " + SafetyLoopIndex + ")...") - foundry.utils.mergeObject(this, new_roll); - } else { - dieHardLog(false, functionLogName + ' - New result insufficient (' + new_roll.total + "). Try again (tries left: " + SafetyLoopIndex + ")...") - } - dmMessage += ',' + new_roll.total; - } - } - if (SafetyLoopIndex === 0) { - dieHardLog(false, functionLogName + ' - Tried until retry safety killed...'); - game.settings.get('foundry-die-hard', 'dieHardSettings').system.dmToGm('DieHard-Fudge: Gave up trying to fudge; loop safety reached...'); - } + avgKarmaData.push(newResult) + while (avgKarmaData.length > avgKarmaData.history) { + avgKarmaData.shift() } + DieHard.dmToGm('DieHard-Karma: Avg Karma for ' + game.users.current.name + ' adjusted a roll of ' + originalResult + ' to a ' + newResult); + } else { + dieHardLog(true, functionLogName + ' - Avg Karma adjustment not needed', avgKarmaData.length, avgKarmaSettings.history, avgKarmaSettings.threshold); } + game.users.current.setFlag('foundry-die-hard', 'avgKarma', avgKarmaData) + this.results.push(roll); + return roll } else { - result = wrapped(eval_options) - } - return result - /* - // Check if actor has an active fudge - let actorFudges = game.actors.get(actorId).getFlag('foundry-die-hard', 'fudges'); - if (! Array.isArray(actorFudges)) { - actorFudges = [] - } - //dieHardLog(false, 'DieHardDnd5e.wrappedRoll - actorFudges', actorFudges); - let fudgeIndex = actorFudges.findIndex(element => { return element.whatId === rollType;}); - if (fudgeIndex !== -1 && actorFudges[fudgeIndex].statusActive) { - dieHardLog(false, 'DieHardDnd5e.wrappedRoll - active actor fudge', actorFudges[fudgeIndex]); - foundry.utils.mergeObject(options, {data: {fudge: true, fudgeOperator: actorFudges[fudgeIndex].operator, fudgeOperatorValue: actorFudges[fudgeIndex].operatorValue, fudgeHow: actorFudges[fudgeIndex].howFormula }}); - // Delete the fudge from the actor - let deletedFudge = actorFudges.splice(fudgeIndex,1) - game.actors.get(actorId).setFlag('foundry-die-hard', 'fudges', actorFudges); - // Check if still have active fudges; - this.refreshActiveFudgesIcon(); + dieHardLog(true, functionLogName + ' - avg Karma disabled'); } - */ - } else { - // Only this works! let result = wrapped(eval_options) return result - // let result = wrapped(eval_options) - return wrapped(eval_options) - } - } - dmToGm(message) { - var dm_ids = []; - // dieHardLog(false, 'DieHardSystem.dmToGm - game.users.values()', game.users.values()); - for (let user of game.users.values()) { - // dieHardLog(false, 'DieHardSystem.dmToGm - user', user); - if (user.isGM) { - dm_ids.push(user.id) - // dieHardLog(false, 'DieHardSystem.dmToGm - Added', user); - } + } - // dieHardLog(false, 'DieHardSystem.dmToGm - dm_ids', dm_ids); - let whisper_to_dm = ChatMessage.create({ - user: game.user.id, - type: CONST.CHAT_MESSAGE_TYPES.WHISPER, - whisper: dm_ids, - blind: true, - content: message - }) + return wrapped(eval_options) } /** - Return an array of all actors (map of id and name), defaulting to ones associated with an active player + * Wrapper for a total roll + * @param wrapped + * @param eval_options + * @returns {*} */ - getActors(activeOnly = true, includeFudges = false) { - dieHardLog(false, 'DieHardSystem : getActors'); - if (game.settings.get('foundry-die-hard', 'dieHardSettings').debug.allActors) { - activeOnly = false - } - let onlineUsers = this.getUsers(activeOnly) - let actors = [] - for (let actorId of game.actors.keys()) { - let curActor = game.actors.get(actorId); - if(curActor.data.type === 'character') { - if (activeOnly) { - for (let user of onlineUsers) { - if (curActor.data.permission[user.id] !== undefined) { - let newActor = {id: actorId, name: curActor.name} - if (includeFudges) { - newActor.fudges = curActor.getFlag('foundry-die-hard', 'fudges') + wrapRollEvaluate(wrapped, eval_options) { + let uuid = makeId(6) + let functionLogName = 'DieHardSystem.wrapRollEvaluate-' + uuid + dieHardLog(false, functionLogName); + + if (! DieHardSetting('fudgeEnabled') ) { + dieHardLog(true, functionLogName + ' - Fudge disabled'); + } else if (DieHardSetting('dieHardSettings').fudgeConfig.globalDisable) { + dieHardLog(true, functionLogName + ' - Fudging Globally disabled'); + } else { + // Check if a total die roll (otherwise some type of system specific roll) + if (DieHardSetting('dieHardSettings').system.totalRollClassName.indexOf(this.constructor.name) !== -1) { + dieHardLog(false, functionLogName + ' - total roll; figure out if needs to be fudged or is a recursive fudge'); + + for (let die in this.dice) { + if (typeof this.dice[die] === 'function') { + dieHardLog(false, functionLogName + ' - die is function; ignore'); + continue; + } + + dieHardLog(false, functionLogName + ' - dice faces', this.dice[die].faces); + let userFudge = DieHardSetting('dieHardSettings').system.getUserFudge('totald' + this.dice[die].faces) + if (userFudge !== null) { + dieHardLog(false, functionLogName + ' - active user total fudge', userFudge); + foundry.utils.mergeObject(this, { + data: { + fudge: true, + fudgeOperator: userFudge.operator, + fudgeOperatorValue: userFudge.operatorValue, + fudgeHow: userFudge.howFormula } - actors.push(newActor) + }); + + if (userFudge.statusEndless) { + dieHardLog(false, functionLogName + ' - fudge is endless'); + } else { + this.disableUserFudge(userFudge.id) } + // This is a root roll, so allow fudge re-roll + // Stop looking for more opportunities to fudge + break } - } else { - let newActor = {id: actorId, name: curActor.name} - if (includeFudges) { - newActor.fudges = curActor.getFlag('foundry-die-hard', 'fudges') + } + let result = null + if (this.data.fudge !== undefined) { + result = wrapped({minimize: eval_options.minimize, maximize: eval_options.maximize, async: false}) + dieHardLog(false, functionLogName + ' - roll can be fudged'); + if (this instanceof CONFIG.Dice.DieHardFudgeRoll) { + dieHardLog(false, functionLogName + 'e - recursive roll', this); + } else { + dieHardLog(false, functionLogName + ' - base roll', this); + dieHardLog(false, functionLogName + ' - result', result); + let gen_new_result = false; + let evalResult = DieHardSetting('dieHardSettings').system.evalFudge(this.total, this.data.fudgeOperator, this.data.fudgeOperatorValue) + + if (evalResult) { + dieHardLog(false, functionLogName + ' - Fudge not needed, but still disabled'); + DieHard.dmToGm('DieHard-Fudge: Total Fudge not needed, but still disabled...'); + dieHardLog(false, functionLogName + ' - dmToGm'); + + } else { + let failedRolls = [this.total]; + gen_new_result = true; + dieHardLog(false, functionLogName + ' - Start fudging'); + let SafetyLoopIndex = DieHardSetting('dieHardSettings').fudgeConfig.maxFudgeAttemptsPerRoll; + while (gen_new_result && SafetyLoopIndex > 0) { + SafetyLoopIndex--; + let new_roll = new DieHardFudgeRoll(this._formula, this.data, this.options) + new_roll.evaluate({async: false}) + evalResult = DieHardSetting('dieHardSettings').system.evalFudge(new_roll.total, this.data.fudgeOperator, this.data.fudgeOperatorValue) + if (evalResult) { + dieHardLog(false, functionLogName + ' - New result: ' + new_roll.total) + gen_new_result = false; + foundry.utils.mergeObject(this, new_roll); + DieHard.dmToGm("Total Fudge (" + result.data.fudgeHow + ") values: " + failedRolls.join(', ') + " Final: " + new_roll.total); + } else { + // New roll is insufficient, but lets at least check if it is "closer" + if (DieHardSetting('dieHardSettings').system.isBetterFudge(this.total, new_roll.total, this.data.fudgeOperator, this.data.fudgeOperatorValue)) { + dieHardLog(false, functionLogName + ' - New result insufficient, but at least better (' + new_roll.total + "). Try again (tries left: " + SafetyLoopIndex + ")...") + foundry.utils.mergeObject(this, new_roll); + } else { + dieHardLog(false, functionLogName + ' - New result insufficient (' + new_roll.total + "). Try again (tries left: " + SafetyLoopIndex + ")...") + } + failedRolls.push(new_roll.total); + } + } + if (SafetyLoopIndex === 0) { + dieHardLog(false, functionLogName + ' - Tried until retry safety killed...'); + DieHard.dmToGm('DieHard-Fudge: Gave up trying to fudge; loop safety reached...'); + } + } } - actors.push(newActor) + } else { + result = wrapped(eval_options) } + return result } + // No user fudging to occur; continue roll as usual } - return actors; + return wrapped(eval_options) } /** - Return an array of all users (map of id and name), defaulting to ones currently active + Return an array of all users (map of id and name), defaulting to ones currently active */ getUsers(activeOnly = true, includeFudges = false, getGM = false, userId = null) { dieHardLog(false, 'DieHardSystem : getUsers', activeOnly, includeFudges, getGM, userId); @@ -428,14 +487,14 @@ export default class DieHardSystem{ } return activeUsers } - + /** - Return an array of all fudges + Return an array of all fudges */ getAllFudges() { dieHardLog(false, 'DieHardSystem : getAllFudges') let fudges = { - actorFudges: this.getActors(false, true), + // actorFudges: this.getActors(false, true), userFudges: this.getUsers(false, true), gmFudges: this.getUsers(false, true, true) } @@ -454,13 +513,16 @@ export default class DieHardSystem{ if (curFudges[fudgeType][typeObject].fudges.length > 0) { return true; } - } catch (e) {} + } catch (e) { + } } } return false; } async refreshActiveFudgesIcon() { + /* + Handled in DieHard.refreshDieHardIcons if (game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) { document.getElementById('die-hard-pause-fudge-icon').classList.remove("die-hard-icon-hidden"); document.getElementById('die-hard-fudge-icon').classList.add("die-hard-icon-hidden"); @@ -469,11 +531,18 @@ export default class DieHardSystem{ document.getElementById('die-hard-pause-fudge-icon').classList.add("die-hard-icon-hidden"); document.getElementById('die-hard-fudge-icon').classList.remove("die-hard-icon-hidden"); } - if (this.hasActiveFudges()) { - document.getElementById('die-hard-fudge-icon').classList.add("die-hard-fudge-icon-active"); - } else { - document.getElementById('die-hard-fudge-icon').classList.remove("die-hard-fudge-icon-active"); + */ + + // Ugly fix for objects not existing yet + // ToDo: clean this up + try{ + if (this.hasActiveFudges()) { + document.getElementById('die-hard-fudge-icon').classList.add("die-hard-fudge-icon-active"); + } else { + document.getElementById('die-hard-fudge-icon').classList.remove("die-hard-fudge-icon-active"); + } } + catch (e) { } } getFudgeWhatOptions() { @@ -487,8 +556,9 @@ export default class DieHardSystem{ getFudgeWhatTotalOptions() { return this.fudgeWhatTotalOptions; } + /** - Get an object of all who options + Get an object of all who options */ getFudgeWhoUserOptions() { return this.getUsers() @@ -498,32 +568,14 @@ export default class DieHardSystem{ return this.getUsers(true, false, true) } - getFudgeWhoActorOptions() { - return this.getActors() - } - hookReady() { dieHardLog(false, 'DieHardSystem : hookReady') } - _getRandomInt(max) { - return Math.floor(Math.random() * max); - } // game.settings.get('foundry-die-hard', 'dieHardSettings').system.deleteAllFudges() deleteAllFudges() { dieHardLog(false, 'DieHardSystem : deleteAllFudges') - // Actors - let actors = game.settings.get('foundry-die-hard', 'dieHardSettings').system.getActors(); - for (let actor in actors) { - try { - game.actors.get(actors[actor].id).setFlag('foundry-die-hard', 'fudges', null) - game.actors.get(actors[actor].id).setFlag('foundry-die-hard', 'activeFudges', null) - game.actors.get(actors[actor].id).setFlag('foundry-die-hard', 'actorFudges', null) - } - catch (e) {} - } - // Players let users = game.settings.get('foundry-die-hard', 'dieHardSettings').system.getUsers(false); for (let user in users) { @@ -531,8 +583,8 @@ export default class DieHardSystem{ game.user.get(users[user].id).setFlag('foundry-die-hard', 'fudges', null) game.user.get(users[user].id).setFlag('foundry-die-hard', 'activeFudges', null) game.user.get(users[user].id).setFlag('foundry-die-hard', 'userFudges', null) + } catch (e) { } - catch (e) {} } @@ -543,16 +595,16 @@ export default class DieHardSystem{ game.user.get(users[user].id).setFlag('foundry-die-hard', 'fudges', null) game.user.get(users[user].id).setFlag('foundry-die-hard', 'activeFudges', null) game.user.get(users[user].id).setFlag('foundry-die-hard', 'userFudges', null) + } catch (e) { } - catch (e) {} } } disableAllFudges() { dieHardLog(false, 'DieHardSystem : disableAllFudges', game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable) - game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable = ! game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable - game.settings.get('foundry-die-hard', 'dieHardSettings').system.refreshActiveFudgesIcon(); + DieHardSetting('dieHardSettings').fudgeConfig.globalDisable = !game.settings.get('foundry-die-hard', 'dieHardSettings').fudgeConfig.globalDisable + DieHard.refreshDieHardIcons(); } static registerTests = context => { diff --git a/scripts/classes/DieHardVersionNotification.js b/scripts/classes/DieHardVersionNotification.js index ccb99f2..d28bb6b 100644 --- a/scripts/classes/DieHardVersionNotification.js +++ b/scripts/classes/DieHardVersionNotification.js @@ -5,7 +5,7 @@ import {dieHardLog} from "../lib/helpers.js"; export default class DieHardVersionNotification { static checkVersion() { let functionLogName = 'DieHardVersionNotification.checkVersion' - let notificationVersion = 3 + let notificationVersion = 4 if (game.user.isGM && game.user.getFlag('foundry-die-hard', 'versionNotification') !== notificationVersion) { dieHardLog(false, functionLogName + ' - Send version notification'); let commonFooter = "To report problems:
" + + "This version includes the following major changes: