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 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. 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. 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; flex: 1;
margin: 0; margin: 0;
line-height: 36px; line-height: 36px;
.bungeeInline(); .engli-Besh();
color: @colorOlive; color: @colorOlive;
&:hover { &:hover {
color: #111; color: #111;

View file

@ -458,8 +458,8 @@ export default class Actor5e extends Actor {
return weight + (q * w); return weight + (q * w);
}, 0); }, 0);
// [Optional] add Currency Weight // [Optional] add Currency Weight (for non-transformed actors)
if ( game.settings.get("sw5e", "currencyWeight") ) { if ( game.settings.get("sw5e", "currencyWeight") && actorData.data.currency ) {
const currency = actorData.data.currency; const currency = actorData.data.currency;
const numCoins = Object.values(currency).reduce((val, denom) => val += Math.max(denom, 0), 0); const numCoins = Object.values(currency).reduce((val, denom) => val += Math.max(denom, 0), 0);
weight += numCoins / CONFIG.SW5E.encumbrance.currencyPerWeight; weight += numCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
@ -553,10 +553,15 @@ export default class Actor5e extends Actor {
const isNPC = this.data.type === 'npc'; const isNPC = this.data.type === 'npc';
let initial = {}; let initial = {};
switch ( itemData.type ) { switch ( itemData.type ) {
case "weapon": case "weapon":
if ( getProperty(itemData, "data.equipped") === undefined ) {
initial["data.equipped"] = isNPC; // NPCs automatically equip weapons 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 = { const weaponProf = {
"natural": true, "natural": true,
"simpleVW": "sim", "simpleVW": "sim",
@ -565,16 +570,22 @@ export default class Actor5e extends Actor {
"martialVW": "mar", "martialVW": "mar",
"martialB": "mar", "martialB": "mar",
"martialLW": "mar" "martialLW": "mar"
}[itemData.data?.weaponType]; }[itemData.data?.weaponType]; // Player characters check proficiency
const actorWeaponProfs = this.data.data.traits?.weaponProf?.value || []; 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; initial["data.proficient"] = hasWeaponProf;
}
}
break; break;
case "equipment": case "equipment":
if ( getProperty(itemData, "data.equipped") === undefined ) {
initial["data.equipped"] = isNPC; // NPCs automatically equip equipment 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 = { const armorProf = {
"natural": true, "natural": true,
"clothing": true, "clothing": true,
@ -582,14 +593,16 @@ export default class Actor5e extends Actor {
"medium": "med", "medium": "med",
"heavy": "hvy", "heavy": "hvy",
"shield": "shl" "shield": "shl"
}[itemData.data?.armor?.type]; }[itemData.data?.armor?.type]; // Player characters check proficiency
const actorArmorProfs = this.data.data.traits?.armorProf?.value || []; 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; initial["data.proficient"] = hasEquipmentProf;
}
}
break; break;
case "power": case "power":
initial["data.prepared"] = true; // NPCs automatically prepare powers initial["data.prepared"] = true; // automatically prepare powers for everyone
break; break;
} }
mergeObject(itemData, initial); mergeObject(itemData, initial);
@ -1103,7 +1116,7 @@ export default class Actor5e extends Actor {
// Recover power slots // Recover power slots
for ( let [k, v] of Object.entries(data.powers) ) { 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. // Recover pact slots.
@ -1210,10 +1223,10 @@ export default class Actor5e extends Actor {
} }
// Get the original Actor data and the new source data // 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 = o.flags.sw5e || {};
o.flags.sw5e.transformOptions = {mergeSkills, mergeSaves}; o.flags.sw5e.transformOptions = {mergeSkills, mergeSaves};
const source = target.toJSON(); const source = duplicate(target.toJSON());
// Prepare new data to merge from the source // Prepare new data to merge from the source
const d = { const d = {
@ -1244,7 +1257,7 @@ export default class Actor5e extends Actor {
// Handle wildcard // Handle wildcard
if ( source.token.randomImg ) { if ( source.token.randomImg ) {
const images = await target.getTokenImages(); const images = await target.getTokenImages();
d.token.img = images[0]; d.token.img = images[Math.floor(Math.random() * images.length)];
} }
// Keep Token configurations // Keep Token configurations
@ -1328,7 +1341,7 @@ export default class Actor5e extends Actor {
newTokenData.actorId = newActor.id; newTokenData.actorId = newActor.id;
return newTokenData; 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() { static get defaultOptions() {
return mergeObject(super.defaultOptions, { return mergeObject(super.defaultOptions, {
scrollY: [ scrollY: [
".inventory .inventory-list", ".inventory .group-list",
".features .inventory-list", ".features .group-list",
".powerbook .inventory-list", ".powerbook .group-list",
".effects .inventory-list" ".effects .effects-list"
], ],
tabs: [{navSelector: ".tabs", contentSelector: ".sheet-body", initial: "description"}] tabs: [{navSelector: ".tabs", contentSelector: ".sheet-body", initial: "description"}]
}); });
@ -619,6 +619,11 @@ export default class ActorSheet5e extends ActorSheet {
itemData = scroll.data; 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 // Create the owned item as normal
return super._onDropItemCreate(itemData); return super._onDropItemCreate(itemData);
} }

View file

@ -619,6 +619,11 @@ export default class ActorSheet5e extends ActorSheet {
itemData = scroll.data; 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 // Create the owned item as normal
return super._onDropItemCreate(itemData); return super._onDropItemCreate(itemData);
} }

View file

@ -168,6 +168,8 @@ export default class AbilityUseDialog extends Dialog {
type: item.data.consumableType, type: item.data.consumableType,
value: uses.value, value: uses.value,
quantity: item.data.quantity, 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) { export const addChatMessageContextOptions = function(html, options) {
let canApply = li => { let canApply = li => {
const message = game.messages.get(li.data("messageId")); 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( 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. * 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 * 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. * @param {Number} multiplier A damage multiplier to apply to the rolled damage.
* @return {Promise} * @return {Promise}
*/ */
function applyChatCardDamage(roll, multiplier) { function applyChatCardDamage(li, multiplier) {
const amount = roll.find('.dice-total').text(); const message = game.messages.get(li.data("messageId"));
const roll = message.roll;
return Promise.all(canvas.tokens.controlled.map(t => { return Promise.all(canvas.tokens.controlled.map(t => {
const a = t.actor; 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); if ( tiebreaker ) parts.push(actor.data.data.abilities.dex.value / 100);
return parts.filter(p => p !== null).join(" + "); 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", "food": "SW5E.ConsumableFood",
"medpac": "SW5E.ConsumableMedpac", "medpac": "SW5E.ConsumableMedpac",
"technology": "SW5E.ConsumableTechnology", "technology": "SW5E.ConsumableTechnology",
"ammunition": "SW5E.ConsumableAmmunition", "ammo": "SW5E.ConsumableAmmunition",
"trinket": "SW5E.ConsumableTrinket", "trinket": "SW5E.ConsumableTrinket",
"force": "SW5E.ConsumableForce", "force": "SW5E.ConsumableForce",
"tech": "SW5E.ConsumableTech" "tech": "SW5E.ConsumableTech"

View file

@ -110,8 +110,8 @@ export async function d20Roll({parts=[], data={}, event={}, rollMode=null, templ
let adv = 0; let adv = 0;
fastForward = fastForward ?? (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey)); fastForward = fastForward ?? (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
if (fastForward) { if (fastForward) {
if ( advantage || event.altKey ) adv = 1; if ( advantage ?? event.altKey ) adv = 1;
else if ( disadvantage || event.ctrlKey || event.metaKey ) adv = -1; else if ( disadvantage ?? (event.ctrlKey || event.metaKey) ) adv = -1;
} }
// Define the inner roll function // Define the inner roll function

View file

@ -251,12 +251,14 @@ export default class Item5e extends Item {
// Item Actions // Item Actions
if ( data.hasOwnProperty("actionType") ) { if ( data.hasOwnProperty("actionType") ) {
// if this item is owned, we populate the label and saving throw during actor init
if (!this.isOwned) {
// Saving throws // Saving throws
this.getSaveDC(); this.getSaveDC();
// To Hit // To Hit
this.getAttackToHit(); this.getAttackToHit();
}
// Damage // Damage
let dam = data.damage || {}; let dam = data.damage || {};
@ -397,7 +399,7 @@ export default class Item5e extends Item {
// Define follow-up actions resulting from the item usage // Define follow-up actions resulting from the item usage
let createMeasuredTemplate = hasArea; // Trigger a template creation let createMeasuredTemplate = hasArea; // Trigger a template creation
let consumeRecharge = !!recharge.value; // Consume recharge 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 consumePowerSlot = requirePowerSlot; // Consume a power slot
let consumeUsage = !!uses.per; // Consume limited uses let consumeUsage = !!uses.per; // Consume limited uses
let consumeQuantity = uses.autoDestroy; // Consume quantity of the item in lieu of 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; if ( powerLevel ) rollData.item.level = powerLevel;
// Configure the damage roll // 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 = { const rollConfig = {
actor: this.actor, actor: this.actor,
critical: critical ?? event?.altKey ?? false, 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 * Migrate a single Actor entity to incorporate latest data model changes
* Return an Object of updateData to be applied * 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 * @return {Object} The updateData to apply
*/ */
export const migrateActorData = function(actor) { export const migrateActorData = function(actor) {
@ -232,13 +232,24 @@ export const migrateSceneData = function(scene) {
* Migrate the actor speed string to movement object * Migrate the actor speed string to movement object
* @private * @private
*/ */
function _migrateActorMovement(actor, updateData) { function _migrateActorMovement(actorData, updateData) {
const ad = actor.data; const ad = actorData.data;
const old = actor.type === 'vehicle' ? ad?.attributes?.speed : ad?.attributes?.speed?.value;
if ( typeof old !== "string" ) return; // 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(" "); const s = (old || "").split(" ");
if ( s.length > 0 ) updateData["data.attributes.movement.walk"] = Number.isNumeric(s[0]) ? parseInt(s[0]) : null; 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; updateData["data.attributes.-=speed"] = null;
}
return updateData return updateData
} }
@ -254,7 +265,7 @@ function _migrateActorSenses(actor, updateData) {
const original = ad.traits.senses || ""; const original = ad.traits.senses || "";
// Try to match old senses with the format like "Darkvision 60 ft, Blindsight 30 ft" // 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; let wasMatched = false;
// Match each comma-separated term // Match each comma-separated term

View file

@ -10,12 +10,16 @@
font-weight: 400; font-weight: 400;
src: url('./fonts/RussoOne.ttf'); src: url('./fonts/RussoOne.ttf');
} }
/* bungee-inline-regular - latin */
@font-face { @font-face {
font-family: 'Bungee Inline'; font-family: 'Engli-Besh';
font-style: normal; font-style: normal;
font-weight: 400; 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 */ /* open-sans-regular - latin */
@font-face { @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); box-shadow: 0 0 12px rgba(232, 17, 17, 0.5);
} }
body.dark-theme #chat-controls .roll-type-select { body.dark-theme #chat-controls .roll-type-select {
background: #363636; background: #4f4f4f;
color: #FFFFFF;
} }
body.dark-theme #chat-controls label { body.dark-theme #chat-controls label {
color: white; color: white;
} }
body.dark-theme #chat-form textarea { body.dark-theme #chat-form textarea {
background: #363636; background: #4f4f4f;
} }
body.dark-theme #combat #combat-round { body.dark-theme #combat #combat-round {
color: #E81111; color: #E81111;

View file

@ -54,11 +54,6 @@
font-weight: 400; font-weight: 400;
src: url('./fonts/EngliBesh-KG3W.ttf'); src: url('./fonts/EngliBesh-KG3W.ttf');
} }
.engli-Besh {
font-family: 'Engli-Besh';
font-size: 20px;
font-weight: 400;
}
/* ----------------------------------------- */ /* ----------------------------------------- */
/* Fonts */ /* Fonts */
/* ----------------------------------------- */ /* ----------------------------------------- */
@ -768,7 +763,7 @@ input[type="reset"]:disabled {
grid-template-rows: 1fr 26px auto; grid-template-rows: 1fr 26px auto;
grid-template-columns: 128px 1fr; grid-template-columns: 128px 1fr;
column-gap: 8px; column-gap: 8px;
row-gap: 8px; grid-row-gap: 8px;
} }
.sw5e.sheet.actor .swalt-sheet header img { .sw5e.sheet.actor .swalt-sheet header img {
grid-column-start: 1; grid-column-start: 1;
@ -1390,7 +1385,7 @@ input[type="reset"]:disabled {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
grid-gap: 4px; grid-gap: 4px;
row-gap: 4px; grid-row-gap: 4px;
} }
.sw5e.sheet.actor .swalt-sheet .tab.attributes .traits-resources section.traits ul.passives strong { .sw5e.sheet.actor .swalt-sheet .tab.attributes .traits-resources section.traits ul.passives strong {
font-size: 13px; font-size: 13px;
@ -1601,7 +1596,7 @@ input[type="reset"]:disabled {
} }
.sw5e.sheet.actor .swalt-sheet.limited { .sw5e.sheet.actor .swalt-sheet.limited {
grid-template-rows: 144px auto; grid-template-rows: 144px auto;
row-gap: 8px; grid-row-gap: 8px;
} }
.sw5e.sheet.actor .swalt-sheet.limited header { .sw5e.sheet.actor .swalt-sheet.limited header {
grid-template-rows: 1fr; grid-template-rows: 1fr;

View file

@ -508,6 +508,41 @@
height: 24px; height: 24px;
margin: 2px; 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 { .sw5e.sheet.actor {
/* ----------------------------------------- */ /* ----------------------------------------- */
/* Sheet Header */ /* Sheet Header */
@ -1557,7 +1592,7 @@
flex: 1; flex: 1;
margin: 0; margin: 0;
line-height: 36px; line-height: 36px;
font-family: 'Bungee Inline'; font-family: 'Engli-Besh';
font-size: 20px; font-size: 20px;
font-weight: 400; font-weight: 400;
color: #4b4a44; color: #4b4a44;
@ -1837,35 +1872,3 @@
max-width: 40px; max-width: 40px;
text-align: right; 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 Entities
import Actor5e from "./module/actor/entity.js"; import Actor5e from "./module/actor/entity.js";
import Item5e from "./module/item/entity.js"; import Item5e from "./module/item/entity.js";
import CharacterImporter from "./module/characterImporter.js";
// Import Applications // Import Applications
import AbilityTemplate from "./module/pixi/ability-template.js"; 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 ActorSheet5eNPC from "./module/actor/sheets/oldSheets/npc.js";
import ActorSheet5eVehicle from "./module/actor/sheets/oldSheets/vehicle.js"; import ActorSheet5eVehicle from "./module/actor/sheets/oldSheets/vehicle.js";
import ActorSheet5eCharacterNew from "./module/actor/sheets/newSheet/character.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 ItemSheet5e from "./module/item/sheet.js";
import ShortRestDialog from "./module/apps/short-rest.js"; import ShortRestDialog from "./module/apps/short-rest.js";
import TraitSelector from "./module/apps/trait-selector.js"; import TraitSelector from "./module/apps/trait-selector.js";
@ -53,6 +55,7 @@ Hooks.once("init", function() {
ActorSheet5eCharacter, ActorSheet5eCharacter,
ActorSheet5eCharacterNew, ActorSheet5eCharacterNew,
ActorSheet5eNPC, ActorSheet5eNPC,
ActorSheet5eNPCNew,
ActorSheet5eVehicle, ActorSheet5eVehicle,
ItemSheet5e, ItemSheet5e,
ShortRestDialog, ShortRestDialog,
@ -110,11 +113,16 @@ Hooks.once("init", function() {
makeDefault: false, makeDefault: false,
label: "SW5E.SheetClassCharacterOld" label: "SW5E.SheetClassCharacterOld"
}); });
Actors.registerSheet("sw5e", ActorSheet5eNPC, { Actors.registerSheet("sw5e", ActorSheet5eNPCNew, {
types: ["npc"], types: ["npc"],
makeDefault: true, makeDefault: true,
label: "SW5E.SheetClassNPC" label: "SW5E.SheetClassNPC"
}); });
Actors.registerSheet("sw5e", ActorSheet5eNPC, {
types: ["npc"],
makeDefault: false,
label: "SW5E.SheetClassNPCOld"
});
Actors.registerSheet('sw5e', ActorSheet5eVehicle, { Actors.registerSheet('sw5e', ActorSheet5eVehicle, {
types: ['vehicle'], types: ['vehicle'],
makeDefault: true, makeDefault: true,
@ -241,6 +249,7 @@ Hooks.on("renderSceneDirectory", (app, html, data)=> {
}); });
Hooks.on("renderActorDirectory", (app, html, data)=> { Hooks.on("renderActorDirectory", (app, html, data)=> {
setFolderBackground(html); setFolderBackground(html);
CharacterImporter.addImportButton(html);
}); });
Hooks.on("renderItemDirectory", (app, html, data)=> { Hooks.on("renderItemDirectory", (app, html, data)=> {
setFolderBackground(html); setFolderBackground(html);

View file

@ -2,7 +2,7 @@
"name": "sw5e", "name": "sw5e",
"title": "SW 5th Edition", "title": "SW 5th Edition",
"description": "A comprehensive game system for running games of SW 5th Edition in the Foundry VTT environment.", "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", "author": "Dev Team",
"scripts": [], "scripts": [],
"esmodules": ["sw5e.js"], "esmodules": ["sw5e.js"],
@ -135,8 +135,8 @@
"gridUnits": "ft", "gridUnits": "ft",
"primaryTokenAttribute": "attributes.hp", "primaryTokenAttribute": "attributes.hp",
"secondaryTokenAttribute": null, "secondaryTokenAttribute": null,
"minimumCoreVersion": "0.7.6", "minimumCoreVersion": "0.7.7",
"compatibleCoreVersion": "0.7.7", "compatibleCoreVersion": "0.7.9",
"url": "https://github.com/unrealkakeman89/sw5e", "url": "https://github.com/unrealkakeman89/sw5e",
"manifest": "https://raw.githubusercontent.com/unrealkakeman89/sw5e/master/system.json", "manifest": "https://raw.githubusercontent.com/unrealkakeman89/sw5e/master/system.json",
"download": "https://github.com/unrealkakeman89/sw5e/archive/master.zip" "download": "https://github.com/unrealkakeman89/sw5e/archive/master.zip"

View file

@ -29,7 +29,7 @@
</section> </section>
<section class="counters"> <section class="counters">
<div class="counter"> <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="counter-value">
<div class="death-success"> <div class="death-success">
<i class="fas fa-check"></i> <i class="fas fa-check"></i>