forked from GitHub-Mirrors/foundry-sw5e
Merge branch 'Develop' into professorbunbury-sw5e
This commit is contained in:
commit
08a5c0be33
41 changed files with 6024 additions and 5848 deletions
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
8
CONTRIBUTIONS.md
Normal file
8
CONTRIBUTIONS.md
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
flex: 1;
|
||||
margin: 0;
|
||||
line-height: 36px;
|
||||
.bungeeInline();
|
||||
.engli-Besh();
|
||||
color: @colorOlive;
|
||||
&:hover {
|
||||
color: #111;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
121
module/characterImporter.js
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 || {};
|
||||
|
@ -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,
|
||||
|
|
|
@ -120,8 +120,8 @@ 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
|
||||
* @return {Object} The updateData to apply
|
||||
* @param {object} actor The actor data object to update
|
||||
* @return {Object} The updateData to apply
|
||||
*/
|
||||
export const migrateActorData = function(actor) {
|
||||
const updateData = {};
|
||||
|
@ -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;
|
||||
const s = (old || "").split(" ");
|
||||
if ( s.length > 0 ) updateData["data.attributes.movement.walk"] = Number.isNumeric(s[0]) ? parseInt(s[0]) : null;
|
||||
updateData["data.attributes.-=speed"] = null;
|
||||
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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
69
sw5e.css
69
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;
|
||||
}
|
||||
|
|
11
sw5e.js
11
sw5e.js
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue