Merge branch 'Develop' into professorbunbury-sw5e

This commit is contained in:
CK 2021-02-04 15:34:12 -05:00 committed by GitHub
commit 08a5c0be33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 6024 additions and 5848 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

8
CONTRIBUTIONS.md Normal file
View file

@ -0,0 +1,8 @@
Rick Fisto
- [Fisto's Codex](https://www.gmbinder.com/share/-M-qA_FYgTwJjU8yFjjx)
Heresy
- [Heritic's Guide to the Galaxy](https://www.gmbinder.com/share/-M815p5BfQ0wbdKY7zqN)
Erikstormtrooper
- [Englibesh Font](http://www.erikstormtrooper.com/englibesh.htm)

View file

@ -27,3 +27,10 @@ may do this by cloning the repository or downloading a zip archive from the
Code and content contributions are accepted. Please feel free to submit issues to the issue tracker or submit merge
requests for code changes. Approval for such requests involves code and (if necessary) design review by The Dev Team.
Please reach out on the SW5E Foundry Dev Discord with any questions.
## Compatible Modules and Optimum Settings
- DAE (Dynamic Active Effects) is needed for many automatic features.
-**Please enable: "Include active effects in special traits display" in "Configure Game Settings> Module Settings> Dynamic Active Effects".**
- Midi QoL is compatible with great features
- Token Action Hud has compatibility

View file

@ -23,7 +23,7 @@
flex: 1;
margin: 0;
line-height: 36px;
.bungeeInline();
.engli-Besh();
color: @colorOlive;
&:hover {
color: #111;

View file

@ -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,10 +553,15 @@ export default class Actor5e extends Actor {
const isNPC = this.data.type === 'npc';
let initial = {};
switch ( itemData.type ) {
case "weapon":
if ( getProperty(itemData, "data.equipped") === undefined ) {
initial["data.equipped"] = isNPC; // NPCs automatically equip weapons
let hasWeaponProf = isNPC; // NPCs automatically have weapon proficiency
if ( !isNPC ) {
}
if ( getProperty(itemData, "data.proficient") === undefined ) {
if ( isNPC ) {
initial["data.proficient"] = true; // NPCs automatically have equipment proficiency
} else {
const weaponProf = {
"natural": true,
"simpleVW": "sim",
@ -565,16 +570,22 @@ export default class Actor5e extends Actor {
"martialVW": "mar",
"martialB": "mar",
"martialLW": "mar"
}[itemData.data?.weaponType];
}[itemData.data?.weaponType]; // Player characters check proficiency
const actorWeaponProfs = this.data.data.traits?.weaponProf?.value || [];
hasWeaponProf = (weaponProf === true) || actorWeaponProfs.includes(weaponProf);
}
const hasWeaponProf = (weaponProf === true) || actorWeaponProfs.includes(weaponProf);
initial["data.proficient"] = hasWeaponProf;
}
}
break;
case "equipment":
if ( getProperty(itemData, "data.equipped") === undefined ) {
initial["data.equipped"] = isNPC; // NPCs automatically equip equipment
let hasEquipmentProf = isNPC; // NPCs automatically have equipment proficiency
if ( !isNPC ) {
}
if ( getProperty(itemData, "data.proficient") === undefined ) {
if ( isNPC ) {
initial["data.proficient"] = true; // NPCs automatically have equipment proficiency
} else {
const armorProf = {
"natural": true,
"clothing": true,
@ -582,14 +593,16 @@ export default class Actor5e extends Actor {
"medium": "med",
"heavy": "hvy",
"shield": "shl"
}[itemData.data?.armor?.type];
}[itemData.data?.armor?.type]; // Player characters check proficiency
const actorArmorProfs = this.data.data.traits?.armorProf?.value || [];
hasEquipmentProf = (armorProf === true) || actorArmorProfs.includes(armorProf);
}
const hasEquipmentProf = (armorProf === true) || actorArmorProfs.includes(armorProf);
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);
}
/* -------------------------------------------- */

View file

@ -33,10 +33,10 @@ export default class ActorSheet5e extends ActorSheet {
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
scrollY: [
".inventory .inventory-list",
".features .inventory-list",
".powerbook .inventory-list",
".effects .inventory-list"
".inventory .group-list",
".features .group-list",
".powerbook .group-list",
".effects .effects-list"
],
tabs: [{navSelector: ".tabs", contentSelector: ".sheet-body", initial: "description"}]
});
@ -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);
}

View file

@ -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);
}

View file

@ -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]
});
}

121
module/characterImporter.js Normal file
View file

@ -0,0 +1,121 @@
export default class CharacterImporter {
// transform JSON from sw5e.com to Foundry friendly format
// and insert new actor
static async transform(rawCharacter){
const sourceCharacter = JSON.parse(rawCharacter); //source character
const details = {
species: sourceCharacter.attribs.find(e => e.name == "race").current,
background: sourceCharacter.attribs.find(e => e.name == "background").current,
alignment: sourceCharacter.attribs.find(e => e.name == "alignment").current
}
const hp = {
value: sourceCharacter.attribs.find(e => e.name == "hp").current,
min: 0,
max: sourceCharacter.attribs.find(e => e.name == "hp").current,
temp: sourceCharacter.attribs.find(e => e.name == "hp_temp").current
};
const ac = {
value: sourceCharacter.attribs.find(e => e.name == "ac").current
};
const abilities = {
str: {
value: sourceCharacter.attribs.find(e => e.name == "strength").current,
proficient: sourceCharacter.attribs.find(e => e.name == 'strength_save_prof').current ? 1 : 0
},
dex: {
value: sourceCharacter.attribs.find(e => e.name == "dexterity").current,
proficient: sourceCharacter.attribs.find(e => e.name == 'dexterity_save_prof').current ? 1 : 0
},
con: {
value: sourceCharacter.attribs.find(e => e.name == "constitution").current,
proficient: sourceCharacter.attribs.find(e => e.name == 'constitution_save_prof').current ? 1 : 0
},
int: {
value: sourceCharacter.attribs.find(e => e.name == "intelligence").current,
proficient: sourceCharacter.attribs.find(e => e.name == 'intelligence_save_prof').current ? 1 : 0
},
wis: {
value: sourceCharacter.attribs.find(e => e.name == "wisdom").current,
proficient: sourceCharacter.attribs.find(e => e.name == 'wisdom_save_prof').current ? 1 : 0
},
cha: {
value: sourceCharacter.attribs.find(e => e.name == "charisma").current,
proficient: sourceCharacter.attribs.find(e => e.name == 'charisma_save_prof').current ? 1 : 0
},
};
const targetCharacter = {
name: sourceCharacter.name,
type: "character",
data: {
abilities: abilities,
details: details,
attributes: {
ac: ac,
hp: hp
}
}
};
let actor = await Actor.create(targetCharacter);
const profession = sourceCharacter.attribs.find(e => e.name == "class").current;
let professionLevel = sourceCharacter.attribs.find(e => e.name == "class_display").current;
professionLevel = parseInt( professionLevel.replace(/[^0-9]/g,'') ); //remove a-z, leaving only integers
CharacterImporter.addClasses(profession, professionLevel, actor);
}
static async addClasses(profession, level, actor){
let classes = await game.packs.get('sw5e.classes').getContent();
let assignedClass = classes.find( c => c.name === profession );
assignedClass.data.data.levels = level;
await actor.createEmbeddedEntity("OwnedItem", assignedClass.data, { displaySheet: false });
}
static addImportButton(html){
const header = $("#actors").find("header.directory-header");
const search = $("#actors").children().find("div.header-search");
const newImportButtonDiv = $("#actors").children().find("div.header-actions").clone();
const newSearch = search.clone();
search.remove();
newImportButtonDiv.attr('id', 'character-sheet-import');
header.append(newImportButtonDiv);
newImportButtonDiv.children("button").remove();
newImportButtonDiv.append("<button class='create-entity' id='cs-import-button'><i class='fas fa-upload'></i> Import Character</button>");
newSearch.appendTo(header);
let characterImportButton = $("#cs-import-button");
characterImportButton.click(ev => {
let content = '<h1>Saved Character JSON Import</h1> '
+ '<label for="character-json">Paste character JSON here:</label> '
+ '</br>'
+ '<textarea id="character-json" name="character-json" rows="10" cols="50"></textarea>';
let importDialog = new Dialog({
title: "Import Character from SW5e.com",
content: content,
buttons: {
"Import": {
icon: '<i class="fas fa-file-import"></i>',
label: "Import Character",
callback: (e) => {
let characterData = $('#character-json').val();
console.log('Parsing Character JSON');
CharacterImporter.transform(characterData);
}
},
"Cancel": {
icon: '<i class="fas fa-times-circle"></i>',
label: "Cancel",
callback: () => {},
}
}
})
importDialog.render(true);
});
}
}

View file

@ -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);
}));
}

View file

@ -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);
}
});

View file

@ -274,7 +274,7 @@ SW5E.consumableTypes = {
"food": "SW5E.ConsumableFood",
"medpac": "SW5E.ConsumableMedpac",
"technology": "SW5E.ConsumableTechnology",
"ammunition": "SW5E.ConsumableAmmunition",
"ammo": "SW5E.ConsumableAmmunition",
"trinket": "SW5E.ConsumableTrinket",
"force": "SW5E.ConsumableForce",
"tech": "SW5E.ConsumableTech"

View file

@ -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

View file

@ -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();
// To Hit
this.getAttackToHit();
}
// Damage
let dam = data.damage || {};
@ -397,7 +399,7 @@ export default class Item5e extends Item {
// Define follow-up actions resulting from the item usage
let createMeasuredTemplate = hasArea; // Trigger a template creation
let consumeRecharge = !!recharge.value; // Consume recharge
let consumeResource = !!resource.target && (resource.type !== "ammo") // Consume a linked (non-ammo) resource
let consumeResource = !!resource.target && resource.type !== "ammo" && !['simpleB', 'martialB'].includes(id.weaponType); // Consume a linked (non-ammo) resource, ignore if use is from a blaster
let consumePowerSlot = requirePowerSlot; // Consume a power slot
let consumeUsage = !!uses.per; // Consume limited uses
let consumeQuantity = uses.autoDestroy; // Consume quantity of the item in lieu of uses
@ -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,

View file

@ -120,7 +120,7 @@ export const migrateCompendium = async function(pack) {
/**
* Migrate a single Actor entity to incorporate latest data model changes
* Return an Object of updateData to be applied
* @param {Actor} actor The actor to Update
* @param {object} actor The actor data object to update
* @return {Object} The updateData to apply
*/
export const migrateActorData = function(actor) {
@ -232,13 +232,24 @@ export const migrateSceneData = function(scene) {
* Migrate the actor speed string to movement object
* @private
*/
function _migrateActorMovement(actor, updateData) {
const ad = actor.data;
const old = actor.type === 'vehicle' ? ad?.attributes?.speed : ad?.attributes?.speed?.value;
if ( typeof old !== "string" ) return;
function _migrateActorMovement(actorData, updateData) {
const ad = actorData.data;
// Work is needed if old data is present
const old = actorData.type === 'vehicle' ? ad?.attributes?.speed : ad?.attributes?.speed?.value;
const hasOld = old !== undefined;
if ( hasOld ) {
// If new data is not present, migrate the old data
const hasNew = ad?.attributes?.movement?.walk !== undefined;
if ( !hasNew && (typeof old === "string") ) {
const s = (old || "").split(" ");
if ( s.length > 0 ) updateData["data.attributes.movement.walk"] = Number.isNumeric(s[0]) ? parseInt(s[0]) : null;
}
// Remove the old attribute
updateData["data.attributes.-=speed"] = null;
}
return updateData
}
@ -254,7 +265,7 @@ function _migrateActorSenses(actor, updateData) {
const original = ad.traits.senses || "";
// Try to match old senses with the format like "Darkvision 60 ft, Blindsight 30 ft"
const pattern = /([A-z]+)\s?([0-9]+)\s?([A-z]+)?/
const pattern = /([A-z]+)\s?([0-9]+)\s?([A-z]+)?/;
let wasMatched = false;
// Match each comma-separated term

View file

@ -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 {

View file

@ -234,13 +234,14 @@ body.dark-theme .dice-roll .dice-total.fumble {
box-shadow: 0 0 12px rgba(232, 17, 17, 0.5);
}
body.dark-theme #chat-controls .roll-type-select {
background: #363636;
background: #4f4f4f;
color: #FFFFFF;
}
body.dark-theme #chat-controls label {
color: white;
}
body.dark-theme #chat-form textarea {
background: #363636;
background: #4f4f4f;
}
body.dark-theme #combat #combat-round {
color: #E81111;

View file

@ -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;

View file

@ -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;
}

11
sw5e.js
View file

@ -17,6 +17,7 @@ import { measureDistances, getBarAttribute } from "./module/canvas.js";
// Import Entities
import Actor5e from "./module/actor/entity.js";
import Item5e from "./module/item/entity.js";
import CharacterImporter from "./module/characterImporter.js";
// Import Applications
import AbilityTemplate from "./module/pixi/ability-template.js";
@ -26,6 +27,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 +55,7 @@ Hooks.once("init", function() {
ActorSheet5eCharacter,
ActorSheet5eCharacterNew,
ActorSheet5eNPC,
ActorSheet5eNPCNew,
ActorSheet5eVehicle,
ItemSheet5e,
ShortRestDialog,
@ -110,11 +113,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,
@ -241,6 +249,7 @@ Hooks.on("renderSceneDirectory", (app, html, data)=> {
});
Hooks.on("renderActorDirectory", (app, html, data)=> {
setFolderBackground(html);
CharacterImporter.addImportButton(html);
});
Hooks.on("renderItemDirectory", (app, html, data)=> {
setFolderBackground(html);

View file

@ -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": "R1-A2",
"author": "Dev Team",
"scripts": [],
"esmodules": ["sw5e.js"],
@ -135,8 +135,8 @@
"gridUnits": "ft",
"primaryTokenAttribute": "attributes.hp",
"secondaryTokenAttribute": null,
"minimumCoreVersion": "0.7.6",
"compatibleCoreVersion": "0.7.7",
"minimumCoreVersion": "0.7.7",
"compatibleCoreVersion": "0.7.9",
"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"

View file

@ -29,7 +29,7 @@
</section>
<section class="counters">
<div class="counter">
<h4 class="death-save rollable">{{ localize "SW5E.DeathSave" }}</h4>
<h4 class="death-save rollable" data-action="rollDeathSave">{{ localize "SW5E.DeathSave" }}</h4>
<div class="counter-value">
<div class="death-success">
<i class="fas fa-check"></i>