diff --git a/less/original/chat.less b/less/original/chat.less
index 7bf83c2a..6aca1df4 100644
--- a/less/original/chat.less
+++ b/less/original/chat.less
@@ -23,7 +23,7 @@
flex: 1;
margin: 0;
line-height: 36px;
- .bungeeInline();
+ .engli-Besh();
color: @colorOlive;
&:hover {
color: #111;
diff --git a/module/actor/entity.js b/module/actor/entity.js
index a25fd738..127a0d05 100644
--- a/module/actor/entity.js
+++ b/module/actor/entity.js
@@ -458,8 +458,8 @@ export default class Actor5e extends Actor {
return weight + (q * w);
}, 0);
- // [Optional] add Currency Weight
- if ( game.settings.get("sw5e", "currencyWeight") ) {
+ // [Optional] add Currency Weight (for non-transformed actors)
+ if ( game.settings.get("sw5e", "currencyWeight") && actorData.data.currency ) {
const currency = actorData.data.currency;
const numCoins = Object.values(currency).reduce((val, denom) => val += Math.max(denom, 0), 0);
weight += numCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
@@ -553,43 +553,56 @@ export default class Actor5e extends Actor {
const isNPC = this.data.type === 'npc';
let initial = {};
switch ( itemData.type ) {
+
case "weapon":
- initial["data.equipped"] = isNPC; // NPCs automatically equip weapons
- let hasWeaponProf = isNPC; // NPCs automatically have weapon proficiency
- if ( !isNPC ) {
- const weaponProf = {
- "natural": true,
- "simpleVW": "sim",
- "simpleB": "sim",
- "simpleLW": "sim",
- "martialVW": "mar",
- "martialB": "mar",
- "martialLW": "mar"
- }[itemData.data?.weaponType];
- const actorWeaponProfs = this.data.data.traits?.weaponProf?.value || [];
- hasWeaponProf = (weaponProf === true) || actorWeaponProfs.includes(weaponProf);
+ if ( getProperty(itemData, "data.equipped") === undefined ) {
+ initial["data.equipped"] = isNPC; // NPCs automatically equip weapons
+ }
+ if ( getProperty(itemData, "data.proficient") === undefined ) {
+ if ( isNPC ) {
+ initial["data.proficient"] = true; // NPCs automatically have equipment proficiency
+ } else {
+ const weaponProf = {
+ "natural": true,
+ "simpleVW": "sim",
+ "simpleB": "sim",
+ "simpleLW": "sim",
+ "martialVW": "mar",
+ "martialB": "mar",
+ "martialLW": "mar"
+ }[itemData.data?.weaponType]; // Player characters check proficiency
+ const actorWeaponProfs = this.data.data.traits?.weaponProf?.value || [];
+ const hasWeaponProf = (weaponProf === true) || actorWeaponProfs.includes(weaponProf);
+ initial["data.proficient"] = hasWeaponProf;
+ }
}
- initial["data.proficient"] = hasWeaponProf;
break;
+
case "equipment":
- initial["data.equipped"] = isNPC; // NPCs automatically equip equipment
- let hasEquipmentProf = isNPC; // NPCs automatically have equipment proficiency
- if ( !isNPC ) {
- const armorProf = {
- "natural": true,
- "clothing": true,
- "light": "lgt",
- "medium": "med",
- "heavy": "hvy",
- "shield": "shl"
- }[itemData.data?.armor?.type];
- const actorArmorProfs = this.data.data.traits?.armorProf?.value || [];
- hasEquipmentProf = (armorProf === true) || actorArmorProfs.includes(armorProf);
+ if ( getProperty(itemData, "data.equipped") === undefined ) {
+ initial["data.equipped"] = isNPC; // NPCs automatically equip equipment
+ }
+ if ( getProperty(itemData, "data.proficient") === undefined ) {
+ if ( isNPC ) {
+ initial["data.proficient"] = true; // NPCs automatically have equipment proficiency
+ } else {
+ const armorProf = {
+ "natural": true,
+ "clothing": true,
+ "light": "lgt",
+ "medium": "med",
+ "heavy": "hvy",
+ "shield": "shl"
+ }[itemData.data?.armor?.type]; // Player characters check proficiency
+ const actorArmorProfs = this.data.data.traits?.armorProf?.value || [];
+ const hasEquipmentProf = (armorProf === true) || actorArmorProfs.includes(armorProf);
+ initial["data.proficient"] = hasEquipmentProf;
+ }
}
- initial["data.proficient"] = hasEquipmentProf;
break;
+
case "power":
- initial["data.prepared"] = true; // NPCs automatically prepare powers
+ initial["data.prepared"] = true; // automatically prepare powers for everyone
break;
}
mergeObject(itemData, initial);
@@ -1103,7 +1116,7 @@ export default class Actor5e extends Actor {
// Recover power slots
for ( let [k, v] of Object.entries(data.powers) ) {
- updateData[`data.powers.${k}.value`] = !Number.isNaN(v.override) ? v.override : (v.max ?? 0);
+ updateData[`data.powers.${k}.value`] = Number.isNumeric(v.override) ? v.override : (v.max ?? 0);
}
// Recover pact slots.
@@ -1210,10 +1223,10 @@ export default class Actor5e extends Actor {
}
// Get the original Actor data and the new source data
- const o = this.toJSON();
+ const o = duplicate(this.toJSON());
o.flags.sw5e = o.flags.sw5e || {};
o.flags.sw5e.transformOptions = {mergeSkills, mergeSaves};
- const source = target.toJSON();
+ const source = duplicate(target.toJSON());
// Prepare new data to merge from the source
const d = {
@@ -1244,7 +1257,7 @@ export default class Actor5e extends Actor {
// Handle wildcard
if ( source.token.randomImg ) {
const images = await target.getTokenImages();
- d.token.img = images[0];
+ d.token.img = images[Math.floor(Math.random() * images.length)];
}
// Keep Token configurations
@@ -1328,7 +1341,7 @@ export default class Actor5e extends Actor {
newTokenData.actorId = newActor.id;
return newTokenData;
});
- return canvas.scene.updateEmbeddedEntity("Token", updates);
+ return canvas.scene?.updateEmbeddedEntity("Token", updates);
}
/* -------------------------------------------- */
diff --git a/module/actor/sheets/newSheet/base.js b/module/actor/sheets/newSheet/base.js
index 1851c0a0..475f5a6d 100644
--- a/module/actor/sheets/newSheet/base.js
+++ b/module/actor/sheets/newSheet/base.js
@@ -619,6 +619,11 @@ export default class ActorSheet5e extends ActorSheet {
itemData = scroll.data;
}
+ // Ignore certain statuses
+ if ( itemData.data ) {
+ ["attunement", "equipped", "proficient", "prepared"].forEach(k => delete itemData.data[k]);
+ }
+
// Create the owned item as normal
return super._onDropItemCreate(itemData);
}
diff --git a/module/actor/sheets/oldSheets/base.js b/module/actor/sheets/oldSheets/base.js
index 485a78b7..c97dd95a 100644
--- a/module/actor/sheets/oldSheets/base.js
+++ b/module/actor/sheets/oldSheets/base.js
@@ -619,6 +619,11 @@ export default class ActorSheet5e extends ActorSheet {
itemData = scroll.data;
}
+ // Ignore certain statuses
+ if ( itemData.data ) {
+ ["attunement", "equipped", "proficient", "prepared"].forEach(k => delete itemData.data[k]);
+ }
+
// Create the owned item as normal
return super._onDropItemCreate(itemData);
}
diff --git a/module/apps/ability-use-dialog.js b/module/apps/ability-use-dialog.js
index a0f8b716..cc277f9b 100644
--- a/module/apps/ability-use-dialog.js
+++ b/module/apps/ability-use-dialog.js
@@ -168,6 +168,8 @@ export default class AbilityUseDialog extends Dialog {
type: item.data.consumableType,
value: uses.value,
quantity: item.data.quantity,
+ max: uses.max,
+ per: CONFIG.SW5E.limitedUsePeriods[uses.per]
});
}
diff --git a/module/chat.js b/module/chat.js
index f5b72e8d..f8ab035f 100644
--- a/module/chat.js
+++ b/module/chat.js
@@ -66,7 +66,7 @@ export const displayChatActionButtons = function(message, html, data) {
export const addChatMessageContextOptions = function(html, options) {
let canApply = li => {
const message = game.messages.get(li.data("messageId"));
- return message.isRoll && message.isContentVisible && canvas.tokens.controlled.length;
+ return message?.isRoll && message?.isContentVisible && canvas?.tokens.controlled.length;
};
options.push(
{
@@ -103,15 +103,16 @@ export const addChatMessageContextOptions = function(html, options) {
* Apply rolled dice damage to the token or tokens which are currently controlled.
* This allows for damage to be scaled by a multiplier to account for healing, critical hits, or resistance
*
- * @param {HTMLElement} roll The chat entry which contains the roll data
+ * @param {HTMLElement} li The chat entry which contains the roll data
* @param {Number} multiplier A damage multiplier to apply to the rolled damage.
* @return {Promise}
*/
-function applyChatCardDamage(roll, multiplier) {
- const amount = roll.find('.dice-total').text();
+function applyChatCardDamage(li, multiplier) {
+ const message = game.messages.get(li.data("messageId"));
+ const roll = message.roll;
return Promise.all(canvas.tokens.controlled.map(t => {
const a = t.actor;
- return a.applyDamage(amount, multiplier);
+ return a.applyDamage(roll.total, multiplier);
}));
}
diff --git a/module/combat.js b/module/combat.js
index b407c33a..8132a5ac 100644
--- a/module/combat.js
+++ b/module/combat.js
@@ -12,7 +12,7 @@ export const _getInitiativeFormula = function(combatant) {
let nd = 1;
let mods = "";
-
+
if (actor.getFlag("sw5e", "halflingLucky")) mods += "r1=1";
if (actor.getFlag("sw5e", "initiativeAdv")) {
nd = 2;
@@ -26,15 +26,3 @@ export const _getInitiativeFormula = function(combatant) {
if ( tiebreaker ) parts.push(actor.data.data.abilities.dex.value / 100);
return parts.filter(p => p !== null).join(" + ");
};
-
-/**
- * When the Combat encounter updates - re-render open Actor sheets for combatants in the encounter.
- */
-Hooks.on("updateCombat", (combat, data, options, userId) => {
- const updateTurn = ("turn" in data) || ("round" in data);
- if ( !updateTurn ) return;
- for ( let t of combat.turns ) {
- const a = t.actor;
- if ( t.actor ) t.actor.sheet.render(false);
- }
-});
diff --git a/module/dice.js b/module/dice.js
index c05dd3df..cea097fd 100644
--- a/module/dice.js
+++ b/module/dice.js
@@ -42,7 +42,7 @@ export function simplifyRollFormula(formula, data, {constantFirst = false} = {})
const parts = constantFirst ? // Order the rollable and constant terms, either constant first or second depending on the optional argumen
[constantPart, rollableFormula] : [rollableFormula, constantPart];
-
+
// Join the parts with a + sign, pass them to `Roll` once again to clean up the formula
return new Roll(parts.filterJoin(" + ")).formula;
}
@@ -52,7 +52,7 @@ export function simplifyRollFormula(formula, data, {constantFirst = false} = {})
/**
* Only some terms are supported by simplifyRollFormula, this method returns true when the term is not supported.
* @param {*} term - A single Dice term to check support on
- * @return {Boolean} True when unsupported, false if supported
+ * @return {Boolean} True when unsupported, false if supported
*/
function _isUnsupportedTerm(term) {
const diceTerm = term instanceof DiceTerm;
@@ -110,8 +110,8 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ
let adv = 0;
fastForward = fastForward ?? (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
if (fastForward) {
- if ( advantage || event.altKey ) adv = 1;
- else if ( disadvantage || event.ctrlKey || event.metaKey ) adv = -1;
+ if ( advantage ?? event.altKey ) adv = 1;
+ else if ( disadvantage ?? (event.ctrlKey || event.metaKey) ) adv = -1;
}
// Define the inner roll function
diff --git a/module/item/entity.js b/module/item/entity.js
index a1b64f0c..8b35bcc5 100644
--- a/module/item/entity.js
+++ b/module/item/entity.js
@@ -251,12 +251,14 @@ export default class Item5e extends Item {
// Item Actions
if ( data.hasOwnProperty("actionType") ) {
+ // if this item is owned, we populate the label and saving throw during actor init
+ if (!this.isOwned) {
+ // Saving throws
+ this.getSaveDC();
- // Saving throws
- this.getSaveDC();
-
- // To Hit
- this.getAttackToHit();
+ // To Hit
+ this.getAttackToHit();
+ }
// Damage
let dam = data.damage || {};
@@ -312,7 +314,7 @@ export default class Item5e extends Item {
* - item's actor's proficiency bonus if applicable
* - item's actor's global bonuses to the given item type
* - item's ammunition if applicable
- *
+ *
* @returns {Object} returns `rollData` and `parts` to be used in the item's Attack roll
*/
getAttackToHit() {
@@ -915,7 +917,8 @@ export default class Item5e extends Item {
if ( powerLevel ) rollData.item.level = powerLevel;
// Configure the damage roll
- const title = `${this.name} - ${game.i18n.localize("SW5E.DamageRoll")}`;
+ const actionFlavor = game.i18n.localize(itemData.actionType === "heal" ? "SW5E.Healing" : "SW5E.DamageRoll");
+ const title = `${this.name} - ${actionFlavor}`;
const rollConfig = {
actor: this.actor,
critical: critical ?? event?.altKey ?? false,
diff --git a/module/templates.js b/module/templates.js
index f3de3dc4..af337996 100644
--- a/module/templates.js
+++ b/module/templates.js
@@ -14,11 +14,11 @@ export const preloadHandlebarsTemplates = async function() {
"systems/sw5e/templates/actors/oldActor/parts/actor-inventory.html",
"systems/sw5e/templates/actors/oldActor/parts/actor-features.html",
"systems/sw5e/templates/actors/oldActor/parts/actor-powerbook.html",
- "systems/sw5e/templates/actors/oldActor/parts/actor-notes.html",
+ "systems/sw5e/templates/actors/oldActor/parts/actor-notes.html",
"systems/sw5e/templates/actors/newActor/parts/swalt-biography.html",
"systems/sw5e/templates/actors/newActor/parts/swalt-core.html",
- "systems/sw5e/templates/actors/newActor/parts/swalt-active-effects.html",
+ "systems/sw5e/templates/actors/newActor/parts/swalt-active-effects.html",
"systems/sw5e/templates/actors/newActor/parts/swalt-features.html",
"systems/sw5e/templates/actors/newActor/parts/swalt-inventory.html",
"systems/sw5e/templates/actors/newActor/parts/swalt-powerbook.html",
diff --git a/sw5e copy.css b/sw5e copy.css
index 58d9546a..237bb2cc 100644
--- a/sw5e copy.css
+++ b/sw5e copy.css
@@ -10,12 +10,16 @@
font-weight: 400;
src: url('./fonts/RussoOne.ttf');
}
-/* bungee-inline-regular - latin */
@font-face {
- font-family: 'Bungee Inline';
+ font-family: 'Engli-Besh';
font-style: normal;
font-weight: 400;
- src: url('./fonts/BungeeInline.ttf');
+ src: url('./fonts/EngliBesh-KG3W.ttf');
+}
+.engli-Besh {
+ font-family: 'Engli-Besh';
+ font-size: 20px;
+ font-weight: 400;
}
/* open-sans-regular - latin */
@font-face {
diff --git a/sw5e-global.css b/sw5e-global.css
index a54ef3d5..a9023252 100644
--- a/sw5e-global.css
+++ b/sw5e-global.css
@@ -54,11 +54,6 @@
font-weight: 400;
src: url('./fonts/EngliBesh-KG3W.ttf');
}
-.engli-Besh {
- font-family: 'Engli-Besh';
- font-size: 20px;
- font-weight: 400;
-}
/* ----------------------------------------- */
/* Fonts */
/* ----------------------------------------- */
@@ -768,7 +763,7 @@ input[type="reset"]:disabled {
grid-template-rows: 1fr 26px auto;
grid-template-columns: 128px 1fr;
column-gap: 8px;
- row-gap: 8px;
+ grid-row-gap: 8px;
}
.sw5e.sheet.actor .swalt-sheet header img {
grid-column-start: 1;
@@ -1390,7 +1385,7 @@ input[type="reset"]:disabled {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 4px;
- row-gap: 4px;
+ grid-row-gap: 4px;
}
.sw5e.sheet.actor .swalt-sheet .tab.attributes .traits-resources section.traits ul.passives strong {
font-size: 13px;
@@ -1601,7 +1596,7 @@ input[type="reset"]:disabled {
}
.sw5e.sheet.actor .swalt-sheet.limited {
grid-template-rows: 144px auto;
- row-gap: 8px;
+ grid-row-gap: 8px;
}
.sw5e.sheet.actor .swalt-sheet.limited header {
grid-template-rows: 1fr;
diff --git a/sw5e.css b/sw5e.css
index 98dde4b5..11a330c5 100644
--- a/sw5e.css
+++ b/sw5e.css
@@ -508,6 +508,41 @@
height: 24px;
margin: 2px;
}
+/* ----------------------------------------- */
+/* HUD
+/* ----------------------------------------- */
+.placeable-hud .control-icon {
+ box-sizing: content-box;
+ width: 40px;
+ flex: 0 0 40px;
+ margin: 8px 0;
+ font-size: 28px;
+ line-height: 40px;
+ text-align: center;
+ color: #FBF4F4;
+ background: rgba(0, 0, 0, 0.6);
+ box-shadow: 0 0 15px #000;
+ border: 1px solid #333;
+ border-radius: 8px;
+ pointer-events: all;
+}
+#token-hud .status-effects {
+ visibility: hidden;
+ position: absolute;
+ left: 50px;
+ top: 0;
+ display: grid;
+ padding: 3px;
+ box-sizing: content-box;
+ width: 100px;
+ color: #FBF4F4;
+ grid-template-columns: 25px 25px 25px 25px;
+ background: rgba(0, 0, 0, 0.6);
+ box-shadow: 0 0 15px #000;
+ border: 1px solid #333;
+ border-radius: 4px;
+ pointer-events: all;
+}
.sw5e.sheet.actor {
/* ----------------------------------------- */
/* Sheet Header */
@@ -1557,7 +1592,7 @@
flex: 1;
margin: 0;
line-height: 36px;
- font-family: 'Bungee Inline';
+ font-family: 'Engli-Besh';
font-size: 20px;
font-weight: 400;
color: #4b4a44;
@@ -1837,35 +1872,3 @@
max-width: 40px;
text-align: right;
}
-.placeable-hud .control-icon {
- box-sizing: content-box;
- width: 40px;
- flex: 0 0 40px;
- margin: 8px 0;
- font-size: 28px;
- line-height: 40px;
- text-align: center;
- color: #FBF4F4;
- background: rgba(0, 0, 0, 0.6);
- box-shadow: 0 0 15px #000;
- border: 1px solid #333;
- border-radius: 8px;
- pointer-events: all;
-}
-#token-hud .status-effects {
- visibility: hidden;
- position: absolute;
- left: 50px;
- top: 0;
- display: grid;
- padding: 3px;
- box-sizing: content-box;
- width: 100px;
- color: #FBF4F4;
- grid-template-columns: 25px 25px 25px 25px;
- background: rgba(0, 0, 0, 0.6);
- box-shadow: 0 0 15px #000;
- border: 1px solid #333;
- border-radius: 4px;
- pointer-events: all;
-}
diff --git a/sw5e.js b/sw5e.js
index bdbfb497..7c110d31 100644
--- a/sw5e.js
+++ b/sw5e.js
@@ -26,6 +26,7 @@ import ActorSheet5eCharacter from "./module/actor/sheets/oldSheets/character.js"
import ActorSheet5eNPC from "./module/actor/sheets/oldSheets/npc.js";
import ActorSheet5eVehicle from "./module/actor/sheets/oldSheets/vehicle.js";
import ActorSheet5eCharacterNew from "./module/actor/sheets/newSheet/character.js";
+import ActorSheet5eNPCNew from "./module/actor/sheets/newSheet/npc.js";
import ItemSheet5e from "./module/item/sheet.js";
import ShortRestDialog from "./module/apps/short-rest.js";
import TraitSelector from "./module/apps/trait-selector.js";
@@ -53,6 +54,7 @@ Hooks.once("init", function() {
ActorSheet5eCharacter,
ActorSheet5eCharacterNew,
ActorSheet5eNPC,
+ ActorSheet5eNPCNew,
ActorSheet5eVehicle,
ItemSheet5e,
ShortRestDialog,
@@ -86,7 +88,7 @@ Hooks.once("init", function() {
// 5e cone RAW should be 53.13 degrees
CONFIG.MeasuredTemplate.defaults.angle = 53.13;
-
+
// Add DND5e namespace for module compatability
game.dnd5e = game.sw5e;
CONFIG.DND5E = CONFIG.SW5E;
@@ -110,11 +112,16 @@ Hooks.once("init", function() {
makeDefault: false,
label: "SW5E.SheetClassCharacterOld"
});
- Actors.registerSheet("sw5e", ActorSheet5eNPC, {
+ Actors.registerSheet("sw5e", ActorSheet5eNPCNew, {
types: ["npc"],
makeDefault: true,
label: "SW5E.SheetClassNPC"
});
+ Actors.registerSheet("sw5e", ActorSheet5eNPC, {
+ types: ["npc"],
+ makeDefault: false,
+ label: "SW5E.SheetClassNPCOld"
+ });
Actors.registerSheet('sw5e', ActorSheet5eVehicle, {
types: ['vehicle'],
makeDefault: true,
diff --git a/system.json b/system.json
index daa334a3..dd9d6803 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.2.2",
+ "version": "1.2.3",
"author": "Dev Team",
"scripts": [],
"esmodules": ["sw5e.js"],
diff --git a/templates/actors/newActor/character-sheet.html b/templates/actors/newActor/character-sheet.html
index e96d7d3c..58de5c48 100644
--- a/templates/actors/newActor/character-sheet.html
+++ b/templates/actors/newActor/character-sheet.html
@@ -115,7 +115,6 @@
-
{{!-- PC Sheet Body --}}
@@ -143,4 +142,4 @@
{{> "systems/sw5e/templates/actors/newActor/parts/swalt-biography.html"}}
-
+
\ No newline at end of file