diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index ad506fed..f07eb2d4 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -9,6 +9,14 @@ Happy gaming ! When not specified, all changes were made by @castanhocorreia, @HavlockV, and @snap01. +## Version 0.10.10 + +When not specified, all changes were made by @castanhocorreia, @HavlockV, and @snap01. + +- Change default font for sheetV2 to one more readable on iOS devices +- Fix average characteristic calculations +- Update to Italian localization, thanks to @Stefano1975t #1474 + ## Version 0.10.9 When not specified, all changes were made by @castanhocorreia, @HavlockV, and @snap01. diff --git a/.github/TRANSLATIONS.md b/.github/TRANSLATIONS.md index eff938b4..d28b357e 100644 --- a/.github/TRANSLATIONS.md +++ b/.github/TRANSLATIONS.md @@ -2,7 +2,7 @@ Thank you for being interested in making Call of Cthulhu 7th Edition for Foundry VTT better! Below is a list of translations keys on existing files that still need translated, based on `en.json`. Feel free to create a new `*.json` file for a language that is not shown here! -The following translations are currently up to date **fr**, **pl** +The following translations are currently up to date **fr**, **it**, **pl** The following translations have more than 50 untranslated strings [are you able to help?](./ABANDONED.md) @@ -20,21 +20,21 @@ The following translations have more than 50 untranslated strings [are you able -|Key|de|es|it|ja|zh-TW| -|:---|:---:|:---:|:---:|:---:|:---:| -|**Remaining**:|**1**|**1**|**5**|**10**|**12**| -|[CoC7.EraRegency](#coc7eraregency)|✅|✅|✅|✅|❌| -|[CoC7.EraRegencyPulp](#coc7eraregencypulp)|✅|✅|✅|✅|❌| -|[CoC7.ErrorTokenIncorrect](#coc7errortokenincorrect)|✅|✅|✅|❌|❌| -|[CoC7.PersonalSpecialityPlaceholder](#coc7personalspecialityplaceholder)|❌|❌|❌|❌|❌| -|[CoC7.SanityLossType](#coc7sanitylosstype)|✅|✅|✅|❌|❌| -|[CoC7.System.Documentation](#coc7systemdocumentation)|✅|✅|✅|❌|❌| -|[CoC7.TokenCreationRoll.ButtonAverage](#coc7tokencreationrollbuttonaverage)|✅|✅|❌|❌|❌| -|[CoC7.TokenCreationRoll.ButtonRoll](#coc7tokencreationrollbuttonroll)|✅|✅|❌|❌|❌| -|[CoC7.TokenCreationRoll.Prompt](#coc7tokencreationrollprompt)|✅|✅|❌|❌|❌| -|[CoC7.TokenCreationRoll.Title](#coc7tokencreationrolltitle)|✅|✅|❌|❌|❌| -|[SETTINGS.SceneDistanceNotCalcualtedNoError](#settingsscenedistancenotcalcualtednoerror)|✅|✅|✅|❌|❌| -|[SETTINGS.SceneDistanceNotCalcualtedNoErrorHint](#settingsscenedistancenotcalcualtednoerrorhint)|✅|✅|✅|❌|❌| +|Key|de|es|ja|zh-TW| +|:---|:---:|:---:|:---:|:---:| +|**Remaining**:|**1**|**1**|**10**|**12**| +|[CoC7.EraRegency](#coc7eraregency)|✅|✅|✅|❌| +|[CoC7.EraRegencyPulp](#coc7eraregencypulp)|✅|✅|✅|❌| +|[CoC7.ErrorTokenIncorrect](#coc7errortokenincorrect)|✅|✅|❌|❌| +|[CoC7.PersonalSpecialityPlaceholder](#coc7personalspecialityplaceholder)|❌|❌|❌|❌| +|[CoC7.SanityLossType](#coc7sanitylosstype)|✅|✅|❌|❌| +|[CoC7.System.Documentation](#coc7systemdocumentation)|✅|✅|❌|❌| +|[CoC7.TokenCreationRoll.ButtonAverage](#coc7tokencreationrollbuttonaverage)|✅|✅|❌|❌| +|[CoC7.TokenCreationRoll.ButtonRoll](#coc7tokencreationrollbuttonroll)|✅|✅|❌|❌| +|[CoC7.TokenCreationRoll.Prompt](#coc7tokencreationrollprompt)|✅|✅|❌|❌| +|[CoC7.TokenCreationRoll.Title](#coc7tokencreationrolltitle)|✅|✅|❌|❌| +|[SETTINGS.SceneDistanceNotCalcualtedNoError](#settingsscenedistancenotcalcualtednoerror)|✅|✅|❌|❌| +|[SETTINGS.SceneDistanceNotCalcualtedNoErrorHint](#settingsscenedistancenotcalcualtednoerrorhint)|✅|✅|❌|❌| ##### CoC7.EraRegency ``` "CoC7.EraRegency": "Regency Cthulhu - Standard",``` ##### CoC7.EraRegencyPulp diff --git a/lang/it.json b/lang/it.json index 264eccd4..ea6008d0 100644 --- a/lang/it.json +++ b/lang/it.json @@ -838,6 +838,7 @@ "CoC7.EmptySkillList": "Aggiungi un'abilità rilasciandola qui.", "CoC7.EmptyItemList": "Aggiungi un oggetto rilasciandolo qui.", "CoC7.EmptyCharacterSkillList": "Aggiungi una configurazione, una professione o un'abilità rilasciandola qui.", + "CoC7.PersonalSpecialityPlaceholder": "altre abilità come specialità personale o del periodo", "CoC7.PersonalSpeciality": "Numero di specialità personali o dell'epoca", "CoC7.AdditionalSkills": "Abilità aggiuntive", "CoC7.SkillSelectionWindow": "Seleziona abilità facoltative", @@ -1293,5 +1294,10 @@ "CoC7.CoCIDFlag.error.documents-not-found": "Documenti non trovati nel mondo o nei Compendium corrispondenti ai CoC ID ({cocids}), lingua \"{lang}\", e periodo \"{era}\". Per favore controlla il tuo Periodo per le impostazioni del mondo.", "CoC7.CoCIDBatch.title": "Impostazione di gruppo dell'ID di Sistema", - "CoC7.CoCIDBatch.summary": "Il Sistema CoC7 ha introdotto un ID di Sistema (CoC ID). Questo permette al sistema di identificare i documenti di FoundryVTT per abilità di esempio. Questa pagina ti permetterà di impostare l'id per esistenti documenti {type}. L'ID di Sistema dovrebbe usare la traduzione in inglese del nome per supportare la localizzazione per tutte le lingue." + "CoC7.CoCIDBatch.summary": "Il Sistema CoC7 ha introdotto un ID di Sistema (CoC ID). Questo permette al sistema di identificare i documenti di FoundryVTT per abilità di esempio. Questa pagina ti permetterà di impostare l'id per esistenti documenti {type}. L'ID di Sistema dovrebbe usare la traduzione in inglese del nome per supportare la localizzazione per tutte le lingue.", + + "CoC7.TokenCreationRoll.Title": "Lancio di dadi rilevato", + "CoC7.TokenCreationRoll.Prompt": "Quest pedina ha caratteristiche o abilità da lancio di dadi.
Cosa vuoi fare?", + "CoC7.TokenCreationRoll.ButtonRoll": "Lancia dadi per tutto", + "CoC7.TokenCreationRoll.ButtonAverage": "Media per tutto" } diff --git a/module/actors/actor.js b/module/actors/actor.js index 827e18a9..ade37939 100644 --- a/module/actors/actor.js +++ b/module/actors/actor.js @@ -1,4 +1,5 @@ /* global Actor, Application, CONFIG, CONST, Dialog, Die, foundry, fromUuid, fromUuidSync, game, Hooks, Roll, TextEditor, Token, ui */ +import { AverageRoll } from '../apps/average-roll.js' import { COC7 } from '../config.js' import CoC7ActiveEffect from '../active-effect.js' import { CoC7ChatMessage } from '../apps/coc7-chat-message.js' @@ -2844,14 +2845,8 @@ export class CoCActor extends Actor { const characteristics = {} for (const [key, value] of Object.entries(this.system.characteristics)) { if (value.formula && !value.formula.startsWith('@')) { - const max = new Roll(value.formula).evaluate({ maximize: true }).total - const min = new Roll(value.formula).evaluate({ minimize: true }).total - const average = Math.floor((max + min) / 2) - const charValue = - average % 5 === 0 ? average : Math.round(average / 10) * 10 - if (charValue) { - characteristics[`system.characteristics.${key}.value`] = charValue - } + const average = new AverageRoll('(' + value.formula + ')').evaluate({ minimize: true, maximize: true }).total + characteristics[`system.characteristics.${key}.value`] = average } } diff --git a/module/apps/average-roll.js b/module/apps/average-roll.js new file mode 100644 index 00000000..8dd12862 --- /dev/null +++ b/module/apps/average-roll.js @@ -0,0 +1,97 @@ +/* global MathTerm, NumericTerm, ParentheticalTerm, Roll, RollTerm */ +class AverageParentheticalTerm extends ParentheticalTerm { + /** @inheritdoc */ + _evaluateSync ({ minimize = false, maximize = false } = {}) { + // Evaluate the inner Roll + const roll = this.roll || new AverageRoll(this.term) + this.roll = roll.evaluate({ minimize, maximize, async: false }) + + // Propagate flavor text to inner terms + if (this.flavor) this.roll.terms.forEach(t => { t.options.flavor = t.options.flavor ?? this.flavor }) + return this + } +} + +export class AverageRoll extends Roll { + /** + * Evaluate the roll synchronously. + * A temporary helper method used to migrate behavior from 0.7.x (sync by default) to 0.9.x (async by default). + * @param {object} [options] Options which inform how evaluation is performed + * @param {boolean} [options.minimize] Force the result to be minimized + * @param {boolean} [options.maximize] Force the result to be maximized + * @returns {Roll} + * @private + */ + _evaluateSync ({ minimize = false, maximize = false } = {}) { + // Step 1 - Replace intermediate terms with evaluated numbers + this.terms = this.terms.map(term => { + if (!(term instanceof RollTerm)) { + throw new Error('Roll evaluation encountered an invalid term which was not a RollTerm instance') + } + if (term.isIntermediate) { + term.evaluate({ minimize, maximize, async: false }) + this._dice = this._dice.concat(term.dice) + + // This section is replaced to calculate the average + let total = term.total + if (minimize && maximize && term.dice.length) { + total = Math.floor((term.dice[0].faces + 1) / 2 * term.total) + } + + return new NumericTerm({ number: total, options: term.options }) + } + return term + }) + + // Step 2 - Simplify remaining terms + this.terms = this.constructor.simplifyTerms(this.terms) + + // Step 3 - Evaluate remaining terms + this.terms = this.terms.map(term => { + if (!term._evaluated) { + if (typeof term.faces !== 'undefined') { + return new NumericTerm({ number: Math.floor((term.faces + 1) / 2 * term.number), options: term.options }) + } else { + term.evaluate({ minimize, maximize, async: false }) + } + } + return term + }) + + // Step 4 - Evaluate the final expression + this._total = this._evaluateTotal() + return this + } + + /** + * Split a formula by identifying its outer-most parenthetical and math terms + * @param {string} _formula The raw formula to split + * @returns {string[]} An array of terms, split on parenthetical terms + * @private + */ + static _splitParentheses (_formula) { + return this._splitGroup(_formula, { + openRegexp: AverageParentheticalTerm.OPEN_REGEXP, + closeRegexp: AverageParentheticalTerm.CLOSE_REGEXP, + openSymbol: '(', + closeSymbol: ')', + onClose: group => { + // Extract group arguments + const fn = group.open.slice(0, -1) + const expression = group.terms.join('') + const options = { flavor: group.flavor ? group.flavor.slice(1, -1) : undefined } + + // Classify the resulting terms + const terms = [] + if (fn in Math) { + const args = this._splitMathArgs(expression) + terms.push(new MathTerm({ fn, terms: args, options })) + } else { + if (fn) terms.push(fn) + terms.push(new AverageParentheticalTerm({ term: expression, options })) + } + return terms + } + }) + } +} diff --git a/styles/system/constants.less b/styles/system/constants.less index 43403ddc..1f6b8030 100644 --- a/styles/system/constants.less +++ b/styles/system/constants.less @@ -32,7 +32,7 @@ // } .defaultFont { - font-family: customSheetFont, 'Bradley Hand', cursive; + font-family: customSheetFont, 'Noto Sans'; } .chatFont { diff --git a/system.json b/system.json index 67cd96b6..8f7ce2a3 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "CoC7", "title": "Call of Cthulhu 7th Edition", "description": "An implementation of the Call of Cthulhu 7th Edition game system for Foundry Virtual Tabletop.", - "version": "0.10.9", + "version": "0.10.10", "authors": [ { "name": "Miskatonic Investigative Society" @@ -19,6 +19,7 @@ ], "templateVersion": 1, "styles": [ + "https://fonts.googleapis.com/css?family=Noto%20Sans", "lib/game-icons/game-icons.css", "coc7g.css" ], @@ -145,5 +146,5 @@ "secondaryTokenAttribute": "attribs.san", "url": "https://github.com/Miskatonic-Investigative-Society/CoC7-FoundryVTT", "manifest": "https://github.com/Miskatonic-Investigative-Society/CoC7-FoundryVTT/releases/latest/download/system.json", - "download": "https://github.com/Miskatonic-Investigative-Society/CoC7-FoundryVTT/releases/download/0.10.9/system.zip" + "download": "https://github.com/Miskatonic-Investigative-Society/CoC7-FoundryVTT/releases/download/0.10.10/system.zip" }