diff --git a/lang/en.json b/lang/en.json index 1e8118cd..dfce7374 100644 --- a/lang/en.json +++ b/lang/en.json @@ -90,6 +90,7 @@ "SW5E.AbilityUseRechargeHint": "This {type} is depleted and must be recharged!", "SW5E.AbilityUseUnavailableHint": "There are no uses of this item remaining!", "SW5E.AbilityUseUse": "Use Ability", + "SW5E.AbilityUseConfig": "Usage Configuration", "SW5E.AbilityWis": "Wisdom", "SW5E.AbilityWisAbbr": "wis", "SW5E.AC": "AC", @@ -351,6 +352,10 @@ "SW5E.EffectsCategoryInactive": "Inactive Effects", "SW5E.EffectCreate": "Create Effect", "SW5E.EffectDelete": "Delete Effect", + "SW5E.EffectTemporary": "Temporary Effects", + "SW5E.EffectPassive": "Passive Effects", + "SW5E.EffectInactive": "Inactive Effects", + "SW5E.EffectNew": "New Effect", "SW5E.EffectEdit": "Edit Effect", "SW5E.Effects": "Effects", "SW5E.EffectToggle": "Toggle Effect", @@ -387,6 +392,7 @@ "SW5E.FeatureRechargeOn": "Recharge On", "SW5E.FeatureRechargeResult": "1d6 Result", "SW5E.Features": "Features", + "SW5E.Feats": "Feats", "SW5E.FeatureUsage": "Feature Usage", "SW5E.FeatureType": "Feature Type", "SW5E.FeetAbbr": "ft.", @@ -592,6 +598,7 @@ "SW5E.ItemTypeConsumable": "Consumable", "SW5E.ItemTypeConsumablePl": "Consumables", "SW5E.ItemTypeContainer": "Container", + "SW5E.ItemTypeBackpack": "Container", "SW5E.ItemTypeContainerPl": "Containers", "SW5E.ItemTypeDeployment": "Deployment", "SW5E.ItemTypeDeploymentPl": "Deployments", @@ -753,6 +760,7 @@ "SW5E.LongRestGritty": "Long Rest (7 days)", "SW5E.LongRestNormal": "Long Rest (8 hours)", "SW5E.LongRestOvernight": "Long Rest (New Day)", + "SW5E.LongRestHint": "Take a long rest? On a long rest you will recover hit points, half your maximum hit dice, class resources, limited use item charges, and power points.", "SW5E.LongRestResult": "{name} takes a long rest.", "SW5E.LongRestResultFP": "{name} takes a long rest and recovers {force} Force Points.", "SW5E.LongRestResultFPHD": "{name} takes a long rest and recovers {force} Force Points and {dice} Hit Dice.", @@ -786,6 +794,8 @@ "SW5E.MovementTurn": "Turning", "SW5E.MovementUnits": "Units", "SW5E.MovementWalk": "Walk", + "SW5E.NewDay": "Is New Day?", + "SW5E.NewDayHint": "Recover limited use abilities which recharge \"per day\"?", "SW5E.Name": "Character Name", "SW5E.NoCharges": "No Charges", "SW5E.None": "None", @@ -900,6 +910,7 @@ "SW5E.ResourcePrimary": "Resource 1", "SW5E.ResourceSecondary": "Resource 2", "SW5E.ResourceTertiary": "Resource 3", + "SW5E.Rest": "Rest", "SW5E.RestL": "L. Rest", "SW5E.RestS": "S. Rest", "SW5E.Ritual": "Ritual", diff --git a/module/actor/entity.js b/module/actor/entity.js index 228b08d4..d0aafc84 100644 --- a/module/actor/entity.js +++ b/module/actor/entity.js @@ -1688,11 +1688,13 @@ export default class Actor5e extends Actor { // Get the Tokens which represent this actor if ( canvas.ready ) { const tokens = this.getActiveTokens(true); + const tokenData = await original.getTokenData(); const tokenUpdates = tokens.map(t => { - const tokenData = original.data.token.toJSON(); - tokenData._id = t.id; - tokenData.actorId = original.id; - return tokenData; + const update = duplicate(tokenData); + update._id = t.id; + delete update.x; + delete update.y; + return update; }); canvas.scene.updateEmbeddedDocuments("Token", tokenUpdates); } diff --git a/module/actor/sheets/newSheet/base.js b/module/actor/sheets/newSheet/base.js index 938601b7..65a4bd6e 100644 --- a/module/actor/sheets/newSheet/base.js +++ b/module/actor/sheets/newSheet/base.js @@ -781,7 +781,7 @@ export default class ActorSheet5e extends ActorSheet { const header = event.currentTarget; const type = header.dataset.type; const itemData = { - name: game.i18n.format("SW5E.ItemNew", {type: type.capitalize()}), + name: game.i18n.format("SW5E.ItemNew", {type: game.i18n.localize(`SW5E.ItemType${type.capitalize()}`)}), type: type, data: foundry.utils.deepClone(header.dataset) }; diff --git a/module/apps/ability-use-dialog.js b/module/apps/ability-use-dialog.js index ca9067d3..4b92b14d 100644 --- a/module/apps/ability-use-dialog.js +++ b/module/apps/ability-use-dialog.js @@ -39,7 +39,7 @@ export default class AbilityUseDialog extends Dialog { // Prepare dialog form data const data = { item: item.data, - title: game.i18n.format("SW5E.AbilityUseHint", item.data), + title: game.i18n.format("SW5E.AbilityUseHint", {type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`), name: item.name}), note: this._getAbilityUseNote(item.data, uses, recharge), consumePowerSlot: false, consumeRecharge: recharges, @@ -59,7 +59,7 @@ export default class AbilityUseDialog extends Dialog { const label = game.i18n.localize("SW5E.AbilityUse" + (data.isPower ? "Cast" : "Use")); return new Promise((resolve) => { const dlg = new this(item, { - title: `${item.name}: Usage Configuration`, + title: `${item.name}: ${game.i18n.localize("SW5E.AbilityUseConfig")}`, content: html, buttons: { use: { @@ -187,7 +187,7 @@ export default class AbilityUseDialog extends Dialog { // Abilities which use Recharge if ( !!recharge.value ) { return game.i18n.format(recharge.charged ? "SW5E.AbilityUseChargedHint" : "SW5E.AbilityUseRechargeHint", { - type: item.type, + type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`), }) } @@ -201,7 +201,7 @@ export default class AbilityUseDialog extends Dialog { else if ( item.data.quantity === 1 && uses.autoDestroy ) str = "SW5E.AbilityUseConsumableDestroyHint"; else if ( item.data.quantity > 1 ) str = "SW5E.AbilityUseConsumableQuantityHint"; return game.i18n.format(str, { - type: item.data.consumableType, + type: game.i18n.localize(`SW5E.Consumable${item.data.consumableType.capitalize()}`), value: uses.value, quantity: item.data.quantity, max: uses.max, @@ -212,7 +212,7 @@ export default class AbilityUseDialog extends Dialog { // Other Items else { return game.i18n.format("SW5E.AbilityUseNormalHint", { - type: item.type, + type: game.i18n.localize(`SW5E.ItemType${item.type.capitalize()}`), value: uses.value, max: uses.max, per: CONFIG.SW5E.limitedUsePeriods[uses.per] diff --git a/module/apps/long-rest.js b/module/apps/long-rest.js index 6287e0f8..3e57cea7 100644 --- a/module/apps/long-rest.js +++ b/module/apps/long-rest.js @@ -40,11 +40,11 @@ export default class LongRestDialog extends Dialog { static async longRestDialog({ actor } = {}) { return new Promise((resolve, reject) => { const dlg = new this(actor, { - title: "Long Rest", + title: game.i18n.localize("SW5E.LongRest"), buttons: { rest: { icon: '', - label: "Rest", + label: game.i18n.localize("SW5E.Rest"), callback: html => { let newDay = true; if (game.settings.get("sw5e", "restVariant") !== "gritty") @@ -54,7 +54,7 @@ export default class LongRestDialog extends Dialog { }, cancel: { icon: '', - label: "Cancel", + label: game.i18n.localize("Cancel"), callback: reject } }, diff --git a/module/apps/short-rest.js b/module/apps/short-rest.js index 8a164af7..22a186ab 100644 --- a/module/apps/short-rest.js +++ b/module/apps/short-rest.js @@ -93,11 +93,11 @@ export default class ShortRestDialog extends Dialog { static async shortRestDialog({actor}={}) { return new Promise((resolve, reject) => { const dlg = new this(actor, { - title: "Short Rest", + title: game.i18n.localize("SW5E.ShortRest"), buttons: { rest: { icon: '', - label: "Rest", + label: game.i18n.localize("SW5E.Rest"), callback: html => { let newDay = false; if (game.settings.get("sw5e", "restVariant") === "gritty") @@ -107,7 +107,7 @@ export default class ShortRestDialog extends Dialog { }, cancel: { icon: '', - label: "Cancel", + label: game.i18n.localize("Cancel"), callback: reject } }, diff --git a/module/config.js b/module/config.js index 399fe55a..c2696683 100644 --- a/module/config.js +++ b/module/config.js @@ -103,6 +103,27 @@ SW5E.weaponProficiencies = { "vbw": "SW5E.WeaponVibrowhipProficiency" }; +/** + * A map of weapon item proficiency to actor item proficiency + * Used when a new player owned item is created + * @type {Object} + */ + SW5E.weaponProficienciesMap = { + "natural": true, + "simpleB": "sim", + "simpleVW": "sim", + "simpleLW": "sim", + "martialB": "mar", + "martialVW": "mar", + "martialLW": "mar", + + // "simpleM": "sim", + // "simpleR": "sim", + // "martialM": "mar", + // "martialR": "mar" +} + + // TODO: Check to see if this can be used // It's not actually been used anywhere in DND5e 1.3.2 // Note name mapped to ID in compendium @@ -270,8 +291,8 @@ SW5E.abilityActivationTypes = { "hour": SW5E.timePeriods.hour, "day": SW5E.timePeriods.day, "special": SW5E.timePeriods.spec, - "legendary": "SW5E.LegAct", - "lair": "SW5E.LairAct", + "legendary": "SW5E.LegendaryActionLabel", + "lair": "SW5E.LairActionLabel", "crew": "SW5E.VehicleCrewAction" }; @@ -413,6 +434,19 @@ SW5E.armorProficiencies = { "shl": "SW5E.EquipmentShieldProficiency" }; +/** + * A map of armor item proficiency to actor item proficiency + * Used when a new player owned item is created + * @type {Object} + */ +SW5E.armorProficienciesMap = { + "natural": true, + "clothing": true, + "light": "lgt", + "medium": "med", + "heavy": "hvy", + "shield": "shl" +} /* -------------------------------------------- */ diff --git a/module/effects.js b/module/effects.js index 7a70430b..ef66ef77 100644 --- a/module/effects.js +++ b/module/effects.js @@ -11,7 +11,7 @@ export function onManageActiveEffect(event, owner) { switch ( a.dataset.action ) { case "create": return owner.createEmbeddedDocuments("ActiveEffect", [{ - label: "New Effect", + label: game.i18n.localize("SW5E.EffectNew"), icon: "icons/svg/aura.svg", origin: owner.uuid, "duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined, @@ -37,17 +37,17 @@ export function prepareActiveEffectCategories(effects) { const categories = { temporary: { type: "temporary", - label: "SW5E.EffectsCategoryTemporary", + label: game.i18n.localize("SW5E.EffectTemporary"), effects: [] }, passive: { type: "passive", - label: "SW5E.EffectsCategoryPassive", + label: game.i18n.localize("SW5E.EffectPassive"), effects: [] }, inactive: { type: "inactive", - label: "SW5E.EffectsCategoryInactive", + label: game.i18n.localize("SW5E.EffectInactive"), effects: [] } }; diff --git a/module/item/entity.js b/module/item/entity.js index 02824961..9f588254 100644 --- a/module/item/entity.js +++ b/module/item/entity.js @@ -1295,7 +1295,10 @@ export default class Item5e extends Item { const abl = this.abilityMod; if ( abl ) { const ability = rollData.abilities[abl]; - rollData["mod"] = ability.mod || 0; + if ( !ability ) { + console.warn(`Item ${this.name} in Actor ${this.actor.name} has an invalid item ability modifier of ${abl} defined`); + } + rollData["mod"] = ability?.mod || 0; } // Include a proficiency score @@ -1536,14 +1539,7 @@ export default class Item5e extends Item { if ( isNPC ) { updates["data.proficient"] = true; // NPCs automatically have equipment proficiency } else { - const armorProf = { - "natural": true, - "clothing": true, - "light": "lgt", - "medium": "med", - "heavy": "hvy", - "shield": "shl" - }[data.data?.armor?.type]; // Player characters check proficiency + const armorProf = CONFIG.DND5E.armorProficienciesMap[data.data?.armor?.type]; // Player characters check proficiency const actorArmorProfs = actorData.data.traits?.armorProf?.value || []; updates["data.proficient"] = (armorProf === true) || actorArmorProfs.includes(armorProf); } @@ -1579,15 +1575,7 @@ export default class Item5e extends Item { updates["data.proficient"] = true; // NPCs automatically have equipment proficiency } else { // TODO: With the changes to make weapon proficiencies more verbose, this may need revising - const weaponProf = { - "natural": true, - "simpleVW": "sim", - "simpleB": "sim", - "simpleLW": "sim", - "martialVW": "mar", - "martialB": "mar", - "martialLW": "mar" - }[data.data?.weaponType]; // Player characters check proficiency + const weaponProf = CONFIG.DND5E.weaponProficienciesMap[data.data?.weaponType]; // Player characters check proficiency const actorWeaponProfs = actorData.data.traits?.weaponProf?.value || []; updates["data.proficient"] = (weaponProf === true) || actorWeaponProfs.includes(weaponProf); } diff --git a/module/pixi/ability-template.js b/module/pixi/ability-template.js index d72cb886..3af99181 100644 --- a/module/pixi/ability-template.js +++ b/module/pixi/ability-template.js @@ -90,8 +90,7 @@ export default class AbilityTemplate extends MeasuredTemplate { if ( now - moveTime <= 20 ) return; const center = event.data.getLocalPosition(this.layer); const snapped = canvas.grid.getSnappedPosition(center.x, center.y, 2); - this.data.x = snapped.x; - this.data.y = snapped.y; + this.data.update({x: snapped.x, y: snapped.y}); this.refresh(); moveTime = now; }; diff --git a/system.json b/system.json index ac481ad6..39760b3f 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "name": "sw5e", "title": "SW 5th Edition", "description": "A comprehensive game system for running games of SW 5th Edition in the Foundry VTT environment.", - "version": "1.3.3.R1-A6", + "version": "1.3.5.R1-A6", "author": "Dev Team", "scripts": [], "esmodules": ["sw5e.js"], @@ -152,7 +152,7 @@ "primaryTokenAttribute": "attributes.hp", "secondaryTokenAttribute": null, "minimumCoreVersion": "0.8.3", - "compatibleCoreVersion": "0.8.6", + "compatibleCoreVersion": "0.8.7", "url": "https://github.com/unrealkakeman89/sw5e", "manifest": "https://raw.githubusercontent.com/unrealkakeman89/sw5e/master/system.json", "download": "https://github.com/unrealkakeman89/sw5e/archive/master.zip" diff --git a/templates/actors/newActor/npc-sheet.html b/templates/actors/newActor/npc-sheet.html index 70552323..463c6d57 100644 --- a/templates/actors/newActor/npc-sheet.html +++ b/templates/actors/newActor/npc-sheet.html @@ -97,13 +97,13 @@ value="{{ability.value}}" data-dtype="Number" placeholder="10" />