diff --git a/README.md b/README.md index 2a7231fa4..1a4d44d3f 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # [Users Guide](https://bit.ly/2JaSlQd) for GURPS 4e Game Aid for Foundry VTT If you can't access the Google doc, here is a [PDF](https://github.com/crnormand/gurps/raw/main/docs/Guide%20for%20GURPS%204e%20on%20Foundry%20VTT.pdf) of the latest version. -# Current Release Version 0.9.5 +# Current Release Version 0.9.6 ### [Change Log](changelog.md) The list was getting just too long, so it has been moved to a separate file. Click above to see what has changed. diff --git a/changelog.md b/changelog.md index c1008cf50..d56556f26 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,15 @@ If you can't access the Google doc, here is a [PDF](https://github.com/crnormand This is what we are currently working on: +## History + +0.9.6 - 5/20/2021 + +- Update migration code to remove possible infinite loop issue +- Make Skill/Spell/Attack (Melee/Ranged/Damage) OtF formulas case insensitive + +0.9.5 - 5/19/2021 + - Fixed /status command when token.actor == null - Refactor regex pattern matching to utilities.js - Added "apply condition" buttons to slam results chat messages. These buttons will select the affected combatant and either roll DX and apply the Prone condition if failed, or directly apply Prone if appropriate. @@ -35,8 +44,6 @@ This is what we are currently working on: - Added support for /qty detecting the current equipment - Added support for parameters and return values from script macros (GURPS.chatargs & GURPS.chatreturn) -## History - 0.9.4 - 5/06/2021 - Fixed /status command to accept either 't' or 'toggle' diff --git a/lib/migration.js b/lib/migration.js new file mode 100644 index 000000000..c1064deea --- /dev/null +++ b/lib/migration.js @@ -0,0 +1,38 @@ +import { recurselist } from './utilities.js' +import * as Settings from './miscellaneous-settings.js' + + +export class Migration { + + static async migrateTo096() { + ui.notifications.info("Please wait, migrating Actors to v0.9.6") + console.log("Migrating Actors to v0.9.6") + for ( let actor of game.actors.entities ) { + let commit = { "data.migrationversion" : '0.9.6' } + for (const attr in actor.data.data.attributes) { + if (actor.data.data.attributes[attr].import == null) + commit = { ...commit, ...{ ['data.attributes.' + attr + '.import']: actor.data.data.attributes[attr].value } } + } + recurselist(actor.data.data.skills, (e, k, d) => { + if (e.import == null) commit = { ...commit, ...{ ['data.skills.' + k + '.import']: e.level || 1 } } + }) + recurselist(actor.data.data.spells, (e, k, d) => { + if (e.import == null) commit = { ...commit, ...{ ['data.spells.' + k + '.import']: e.level | 1 } } + }) + recurselist(actor.data.data.melee, (e, k, d) => { + if (e.import == null) commit = { ...commit, ...{ ['data.melee.' + k + '.import']: e.level | 1 } } + }) + recurselist(actor.data.data.ranged, (e, k, d) => { + if (e.import == null) commit = { ...commit, ...{ ['data.ranged.' + k + '.import']: e.level | 1} } + }) + // We must delay the upgrade of older actor's 'import' keys, since upon startup, the actor may not know which collection it belongs to + if (Object.keys(commit).length > 0) { + console.log("Updating " + actor.name) + console.log(GURPS.objToString(commit)) + await actor.update(commit) + } + } + ui.notifications.info("Migration to v0.9.6 complete!") + game.settings.set(Settings.SYSTEM_NAME, Settings.SETTING_MIGRATION_VERSION, '0.9.6') + } +} \ No newline at end of file diff --git a/lib/miscellaneous-settings.js b/lib/miscellaneous-settings.js index 119843989..4bf1ab001 100755 --- a/lib/miscellaneous-settings.js +++ b/lib/miscellaneous-settings.js @@ -5,6 +5,7 @@ import Initiative from './initiative.js' import { i18n } from '../lib/utilities.js' export const SYSTEM_NAME = 'gurps' +export const SETTING_MIGRATION_VERSION = 'migration-version' export const SETTING_DEFAULT_LOCATION = 'default-hitlocation' export const SETTING_SIMPLE_DAMAGE = 'combat-simple-damage' export const SETTING_APPLY_DIVISOR = 'combat-apply-divisor' @@ -57,6 +58,16 @@ export function initializeSettings() { onChange: value => console.log(`Change Log version : ${value}`), }) + // Keep track of the last version number + game.settings.register(SYSTEM_NAME, SETTING_MIGRATION_VERSION, { + name: 'Migration Version', + scope: 'world', + config: false, + type: String, + default: '0.0.0', + onChange: value => console.log(`Migration Log version : ${value}`), + }) + // In case the user wants to see what changed between versions game.settings.register(SYSTEM_NAME, SETTING_SHOW_CHANGELOG, { name: "Show 'Read Me' on version change", diff --git a/lib/parselink.js b/lib/parselink.js index e93ad2307..9857efddd 100755 --- a/lib/parselink.js +++ b/lib/parselink.js @@ -314,10 +314,10 @@ export function parselink(str, htmldesc, clrdmods = false) { // - ([^\|])* - COMMENT - zero or more run of characters that do not include | // - (\|.*)? - REMAINDER - | followed by any run of characters (optional) // - let parse = str.replace(/^S[pkPK]?:"([^"]+)" ?([-+]\d+)? ?([^\|]*)(\|.*)?/g, '$1~$2~$3~$4') + let parse = str.replace(/^S[pkPK]?:"([^"]+)" ?([-+]\d+)? ?([^\|]*)(\|.*)?/gi, '$1~$2~$3~$4') if (parse == str) { // Use quotes to capture skill/spell name (with as many * as they want to embed) - parse = str.replace(/^S[pkPK]?:([^\| \?\(+-]+\*?) ?([-+]\d+)? ?([^\|]*)(\|.*)?/g, '$1~$2~$3~$4') + parse = str.replace(/^S[pkPK]?:([^\| \?\(+-]+\*?) ?([-+]\d+)? ?([^\|]*)(\|.*)?/gi, '$1~$2~$3~$4') } if (parse != str) { @@ -333,9 +333,9 @@ export function parselink(str, htmldesc, clrdmods = false) { let floatingAttribute = null let floatingLabel = null let floatingType = null - let matches = comment.match(/(\((Based|Base|B): ?[^\)]+\))/g) + let matches = comment.match(/(\((Based|Base|B): ?[^\)]+\))/gi) if (!!matches) { - floatingLabel = comment.replace(/.*\((Based|Base|B): ?([^\)]+)\).*/g, '$2') + floatingLabel = comment.replace(/.*\((Based|Base|B): ?([^\)]+)\).*/gi, '$2') //console.log(`floating ${floatingLabel}`) comment = comment @@ -420,10 +420,10 @@ export function parselink(str, htmldesc, clrdmods = false) { } // Simple, no-spaces, no quotes melee/ranged name (with optional *s) - parse = str.replace(/^[MRAD]:([^ "+-]+\*?) ?([-+]\d+)? ?(.*)/g, '$1~$2~$3') + parse = str.replace(/^[MRAD]:([^ "+-]+\*?) ?([-+]\d+)? ?(.*)/gi, '$1~$2~$3') if (parse == str) { // Use quotes to capture skill/spell name (with optional *s) - parse = str.replace(/^[MRAD]:"([^"]+)" ?([-+]\d+)? ?(.*)/g, '$1~$2~$3') + parse = str.replace(/^[MRAD]:"([^"]+)" ?([-+]\d+)? ?(.*)/gi, '$1~$2~$3') } if (parse != str) { let a = parse.split('~') @@ -451,8 +451,8 @@ export function parselink(str, htmldesc, clrdmods = false) { comment = '' } if (!!costs) spantext += ' ' + costs - let isMelee = !!str.match(/^[AMD]/) - let isRanged = !!str.match(/^[ARD]/) + let isMelee = !!str.match(/^[AMD]/i) + let isRanged = !!str.match(/^[ARD]/i) let action = { orig: str, type: str.startsWith('D') ? 'attackdamage' : 'attack', diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 5df7b3b51..076e46e55 100755 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -150,7 +150,7 @@ export class GurpsActorSheet extends ActorSheet { const w = 50; const h = 50; const preview = DragDrop.createDragImage(img, w, h); - ev.dataTransfer.setDragImage(preview, w/2, h/2); + ev.dataTransfer.setDragImage(preview, 0, 0); } let newd = { actorid: this.actor.id, diff --git a/module/actor.js b/module/actor.js index 810564146..6728d7b50 100755 --- a/module/actor.js +++ b/module/actor.js @@ -15,6 +15,7 @@ import { ResourceTrackerManager } from '../module/actor/resource-tracker-manager import ApplyDamageDialog from './damage/applydamage.js' import * as HitLocations from '../module/hitlocation/hitlocation.js' import * as settings from '../lib/miscellaneous-settings.js' +import { SemanticVersion } from '../lib/semver.js' export class GurpsActor extends Actor { /** @override */ @@ -49,27 +50,22 @@ export class GurpsActor extends Actor { // Initialize the attribute current values/levels. The code is expecting 'value' or 'level' for many things, and instead of changing all of the GUIs and OTF logic // we are just going to switch the rug out from underneath. "Import" data will be in the 'import' key and then we will calculate value/level when the actor is loaded. - // If import keys don't exist, set them to the current value and commit to upgrade older actors _initCurrents() { + let v = this.data.data.migrationversion + if (!v) return // currently, only need to check for the initial version, but in the future, we might need to check against SemanticVersion.fromString(v) // Attributes need to have 'value' set because Foundry expects objs with value and max to be attributes (so we can't use currentvalue) let commit = {} for (const attr in this.data.data.attributes) { - if (this.data.data.attributes[attr].import == null) - commit = { ...commit, ...{ ['data.attributes.' + attr + '.import']: this.data.data.attributes[attr].value } } - // backward compat - else this.data.data.attributes[attr].value = this.data.data.attributes[attr].import + this.data.data.attributes[attr].value = this.data.data.attributes[attr].import } recurselist(this.data.data.skills, (e, k, d) => { - if (e.import == null) commit = { ...commit, ...{ ['data.skills.' + k + '.import']: e.level } } - else e.level = parseInt(e.import) + e.level = parseInt(e.import) }) recurselist(this.data.data.spells, (e, k, d) => { - if (e.import == null) commit = { ...commit, ...{ ['data.spells.' + k + '.import']: e.level } } - else e.level = parseInt(e.import) + e.level = parseInt(e.import) }) recurselist(this.data.data.melee, (e, k, d) => { - if (e.import == null) commit = { ...commit, ...{ ['data.melee.' + k + '.import']: e.level } } - else e.level = parseInt(e.import) + e.level = parseInt(e.import) if (!isNaN(parseInt(e.parry))) { // allows for '14f' and 'no' let base = 3 + Math.floor(e.level / 2) @@ -87,11 +83,8 @@ export class GurpsActor extends Actor { } }) recurselist(this.data.data.ranged, (e, k, d) => { - if (e.import == null) commit = { ...commit, ...{ ['data.ranged.' + k + '.import']: e.level } } - else e.level = parseInt(e.import) + e.level = parseInt(e.import) }) - // We must delay the upgrade of older actor's 'import' keys, since upon startup, the actor may not know which collection it belongs to - if (Object.keys(commit).length > 0) setTimeout(() => this.update(commit), 1000) } _applyItemBonuses() { @@ -372,7 +365,7 @@ export class GurpsActor extends Actor { console.log("Importing '" + nm + "'") // this is how you have to update the domain object so that it is synchronized. - let commit = {} + let commit = { "data.migrationversion" : game.system.data.version } if (!game.settings.get(settings.SYSTEM_NAME, settings.SETTING_IGNORE_IMPORT_NAME)) { commit = { ...commit, ...{ name: nm } } @@ -2033,8 +2026,8 @@ export class Named { } export class NamedCost extends Named { - constructor() { - super() + constructor(n1) { + super(n1) this.points = 0 } } @@ -2047,16 +2040,16 @@ export class Leveled extends NamedCost { } export class Skill extends Leveled { - constructor() { - super() + constructor(n1, lvl) { + super(n1, lvl) this.type = '' // "DX/E"; this.relativelevel = '' // "DX+1"; } } export class Spell extends Leveled { - constructor() { - super() + constructor(n1, lvl) { + super(n1, lvl) this.class = '' this.college = '' this.cost = '' @@ -2069,8 +2062,8 @@ export class Spell extends Leveled { } export class Advantage extends NamedCost { - constructor() { - super() + constructor(n1) { + super(n1) this.userdesc = '' this.note = '' } @@ -2088,8 +2081,8 @@ export class Attack extends Named { } export class Melee extends Attack { - constructor() { - super() + constructor(n1, lvl, dmg) { + super(n1, lvl, dmg) this.weight = '' this.techlevel = '' @@ -2101,8 +2094,8 @@ export class Melee extends Attack { } export class Ranged extends Attack { - constructor() { - super() + constructor(n1, lvl, dmg) { + super(n1, lvl, dmg) this.bulk = '' this.legalityclass = '' diff --git a/module/gurps.js b/module/gurps.js index 5f785618c..cf8a5390d 100755 --- a/module/gurps.js +++ b/module/gurps.js @@ -20,6 +20,7 @@ import { doRoll } from '../module/dierolls/dieroll.js' import { ResourceTrackerManager } from './actor/resource-tracker-manager.js' import { DamageTables, initializeDamageTables } from '../module/damage/damage-tables.js' import RegisterChatProcessors from '../module/chat/chat-processors.js' +import { Migration } from '../lib/migration.js' export const GURPS = {} window.GURPS = GURPS // Make GURPS global! @@ -871,6 +872,7 @@ function findSkillSpell(actor, sname, isSkillOnly = false, isSpellOnly = false) if (!actor) return t if (!!actor.data?.data?.additionalresources) actor = actor.data sname = makeRegexPatternFrom(sname, false) + sname = new RegExp(sname, "i"); let best = 0 if (!isSpellOnly) recurselist(actor.data.skills, s => { @@ -896,6 +898,7 @@ function findAdDisad(actor, sname) { if (!actor) return t if (!!actor.data?.data?.additionalresources) actor = actor.data sname = makeRegexPatternFrom(sname, false) + sname = new RegExp(sname, "i"); recurselist(actor.data.ads, s => { if (s.name.match(sname)) { t = s @@ -910,6 +913,7 @@ function findAttack(actor, sname, isMelee = true, isRanged = true) { if (!actor) return t if (!!actor.data?.data?.additionalresources) actor = actor.data sname = makeRegexPatternFrom(sname, false) + sname = new RegExp(sname, "i"); if (isMelee) t = actor.data.melee?.findInProperties(a => (a.name + (!!a.mode ? ' (' + a.mode + ')' : '')).match(sname)) if (isRanged && !t) @@ -1547,6 +1551,13 @@ Hooks.once('ready', async function () { template: 'systems/gurps/templates/threed6.html', classes: [], }).render(true) + + // Test for migration + const mv = game.settings.get(settings.SYSTEM_NAME, settings.SETTING_MIGRATION_VERSION) || '0.0.1' + console.log("Migration version: " + mv) + const migrationVersion = SemanticVersion.fromString(mv) + const v096 = SemanticVersion.fromString('0.9.6') + if (migrationVersion.isLowerThan(v096)) Migration.migrateTo096() // Show changelog const v = game.settings.get(settings.SYSTEM_NAME, settings.SETTING_CHANGELOG_VERSION) || '0.0.1' diff --git a/system.json b/system.json index df7affdf3..f00ddd922 100755 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "name": "gurps", "title": "GURPS 4th Edition Game Aid (Unofficial)", "description": "A game aid to help play GURPS 4e for Foundry VTT", - "version": "0.9.4", + "version": "0.9.6", "minimumCoreVersion": "0.7.5", "compatibleCoreVersion": "0.7.9", "templateVersion": 2, @@ -44,7 +44,7 @@ "secondaryTokenAttribute": "FP", "url": "https://github.com/crnormand/gurps", "manifest": "https://raw.githubusercontent.com/crnormand/gurps/release/system.json", - "download": "https://github.com/crnormand/gurps/archive/0.9.4.zip", + "download": "https://github.com/crnormand/gurps/archive/0.9.6.zip", "socket": true, "license": "LICENSE.txt" }