Update Core 1.2.2

Update core to 1.2.2.  Sheets are broken
This commit is contained in:
supervj 2021-01-18 23:49:04 -05:00
parent 8f2b0488a4
commit 9c6bd3873e
31 changed files with 189 additions and 114 deletions

View file

@ -426,11 +426,6 @@
border: none;
margin-right: 5px;
}
h4 {
margin: 0;
white-space: nowrap;
overflow-x: hidden;
}
}
}

View file

@ -1180,6 +1180,7 @@ export default class Actor5e extends Actor {
/* -------------------------------------------- */
/**
* Transform this Actor into another one.
*

View file

@ -119,22 +119,44 @@ export default class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/**
* Prepare the display of movement speed data for the Actor
* @param {object} actorData
* Prepare the display of movement speed data for the Actor*
* @param {object} actorData The Actor data being prepared.
* @param {boolean} [largestPrimary=false] Show the largest movement speed as "primary", otherwise show "walk"
* @returns {{primary: string, special: string}}
* @private
*/
_getMovementSpeed(actorData) {
_getMovementSpeed(actorData, largestPrimary=false) {
const movement = actorData.data.attributes.movement || {};
const speeds = [
// Prepare an array of available movement speeds
let speeds = [
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
[movement.fly, `${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` + (movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")],
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
].filter(s => !!s[0]).sort((a, b) => b[0] - a[0]);
return {
primary: `${movement.walk || 0} ${movement.units}`,
special: speeds.length ? speeds.map(s => s[1]).join(", ") : ""
]
if ( largestPrimary ) {
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
}
// Filter and sort speeds on their values
speeds = speeds.filter(s => !!s[0]).sort((a, b) => b[0] - a[0]);
// Case 1: Largest as primary
if ( largestPrimary ) {
let primary = speeds.shift();
return {
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
special: speeds.map(s => s[1]).join(", ")
}
}
// Case 2: Walk as primary
else {
return {
primary: `${movement.walk || 0} ${movement.units}`,
special: speeds.length ? speeds.map(s => s[1]).join(", ") : ""
}
}
}
@ -352,7 +374,7 @@ export default class ActorSheet5e extends ActorSheet {
1: '<i class="fas fa-check"></i>',
2: '<i class="fas fa-check-double"></i>'
};
return icons[level];
return icons[level] || icons[0];
}
/* -------------------------------------------- */

View file

@ -57,6 +57,9 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
// Experience Tracking
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
sheetData["classLabels"] = this.actor.itemTypes.class.map(c => c.name).join(", ");
sheetData["multiclassLabels"] = this.actor.itemTypes.class.map(c => {
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(' ')
}).join(', ');
// Return data for rendering
return sheetData;
@ -86,6 +89,18 @@ export default class ActorSheet5eCharacterNew extends ActorSheet5e {
// Item details
item.img = item.img || DEFAULT_TOKEN;
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
item.attunement = {
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
icon: "fa-sun",
cls: "not-attuned",
title: "SW5E.AttunementRequired"
},
[CONFIG.SW5E.attunementTypes.ATTUNED]: {
icon: "fa-sun",
cls: "attuned",
title: "SW5E.AttunementAttuned"
}
}[item.data.attunement];
// Item usage
item.hasUses = item.data.uses && (item.data.uses.max > 0);

View file

@ -115,7 +115,7 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
/** @override */
activateListeners(html) {
super.activateListeners(html);
html.find(".health .rollable").click(this._onRollHealthFormula.bind(this));
html.find(".health .rollable").click(this._onRollHPFormula.bind(this));
}
/* -------------------------------------------- */
@ -125,7 +125,7 @@ export default class ActorSheet5eNPCNew extends ActorSheet5e {
* @param {Event} event The original click event
* @private
*/
_onRollHealthFormula(event) {
_onRollHPFormula(event) {
event.preventDefault();
const formula = this.actor.data.data.attributes.hp.formula;
if ( !formula ) return;

View file

@ -56,6 +56,13 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
/* -------------------------------------------- */
/** @override */
_getMovementSpeed(actorData, largestPrimary=true) {
return super._getMovementSpeed(actorData, largestPrimary);
}
/* -------------------------------------------- */
/**
* Prepare items that are mounted to a vehicle and require one or more crew
* to operate.
@ -86,13 +93,6 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
/* -------------------------------------------- */
/** @override */
_getMovementSpeed(actorData) {
return {primary: "", special: ""};
}
/* -------------------------------------------- */
/**
* Organize Owned Items for rendering the Vehicle sheet.
* @private

View file

@ -119,22 +119,44 @@ export default class ActorSheet5e extends ActorSheet {
/* -------------------------------------------- */
/**
* Prepare the display of movement speed data for the Actor
* @param {object} actorData
* Prepare the display of movement speed data for the Actor*
* @param {object} actorData The Actor data being prepared.
* @param {boolean} [largestPrimary=false] Show the largest movement speed as "primary", otherwise show "walk"
* @returns {{primary: string, special: string}}
* @private
*/
_getMovementSpeed(actorData) {
_getMovementSpeed(actorData, largestPrimary=false) {
const movement = actorData.data.attributes.movement || {};
const speeds = [
// Prepare an array of available movement speeds
let speeds = [
[movement.burrow, `${game.i18n.localize("SW5E.MovementBurrow")} ${movement.burrow}`],
[movement.climb, `${game.i18n.localize("SW5E.MovementClimb")} ${movement.climb}`],
[movement.fly, `${game.i18n.localize("SW5E.MovementFly")} ${movement.fly}` + (movement.hover ? ` (${game.i18n.localize("SW5E.MovementHover")})` : "")],
[movement.swim, `${game.i18n.localize("SW5E.MovementSwim")} ${movement.swim}`]
].filter(s => !!s[0]).sort((a, b) => b[0] - a[0]);
return {
primary: `${movement.walk || 0} ${movement.units}`,
special: speeds.length ? speeds.map(s => s[1]).join(", ") : ""
]
if ( largestPrimary ) {
speeds.push([movement.walk, `${game.i18n.localize("SW5E.MovementWalk")} ${movement.walk}`]);
}
// Filter and sort speeds on their values
speeds = speeds.filter(s => !!s[0]).sort((a, b) => b[0] - a[0]);
// Case 1: Largest as primary
if ( largestPrimary ) {
let primary = speeds.shift();
return {
primary: `${primary ? primary[1] : "0"} ${movement.units}`,
special: speeds.map(s => s[1]).join(", ")
}
}
// Case 2: Walk as primary
else {
return {
primary: `${movement.walk || 0} ${movement.units}`,
special: speeds.length ? speeds.map(s => s[1]).join(", ") : ""
}
}
}
@ -352,7 +374,7 @@ export default class ActorSheet5e extends ActorSheet {
1: '<i class="fas fa-check"></i>',
2: '<i class="fas fa-check-double"></i>'
};
return icons[level];
return icons[level] || icons[0];
}
/* -------------------------------------------- */

View file

@ -46,6 +46,9 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
// Experience Tracking
sheetData["disableExperience"] = game.settings.get("sw5e", "disableExperienceTracking");
sheetData["classLabels"] = this.actor.itemTypes.class.map(c => c.name).join(", ");
sheetData["multiclassLabels"] = this.actor.itemTypes.class.map(c => {
return [c.data.data.archetype, c.name, c.data.data.levels].filterJoin(' ')
}).join(', ');
// Return data for rendering
return sheetData;
@ -76,12 +79,12 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
item.img = item.img || DEFAULT_TOKEN;
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
item.attunement = {
1: {
[CONFIG.SW5E.attunementTypes.REQUIRED]: {
icon: "fa-sun",
cls: "not-attuned",
title: "SW5E.AttunementRequired"
},
2: {
[CONFIG.SW5E.attunementTypes.ATTUNED]: {
icon: "fa-sun",
cls: "attuned",
title: "SW5E.AttunementAttuned"

View file

@ -56,6 +56,13 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
/* -------------------------------------------- */
/** @override */
_getMovementSpeed(actorData, largestPrimary=true) {
return super._getMovementSpeed(actorData, largestPrimary);
}
/* -------------------------------------------- */
/**
* Prepare items that are mounted to a vehicle and require one or more crew
* to operate.
@ -86,13 +93,6 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
/* -------------------------------------------- */
/** @override */
_getMovementSpeed(actorData) {
return {primary: "", special: ""};
}
/* -------------------------------------------- */
/**
* Organize Owned Items for rendering the Vehicle sheet.
* @private

View file

@ -127,10 +127,13 @@ export default class AbilityUseDialog extends Dialog {
});
}
const canCast = powerLevels.some(l => l.hasSlots);
if ( !canCast ) data.errors.push(game.i18n.format("SW5E.PowerCastNoSlots", {
level: CONFIG.SW5E.powerLevels[lvl],
name: data.item.name
}));
// Return merged data
data = mergeObject(data, { isPower: true, consumePowerSlot, powerLevels });
if ( !canCast ) data.errors.push("SW5E.PowerCastNoSlots");
// Merge power casting data
return mergeObject(data, { isPower: true, consumePowerSlot, powerLevels });
}
/* -------------------------------------------- */

View file

@ -49,7 +49,7 @@ export default class TraitSelector extends FormApplication {
}
// Return data
return {
return {
allowCustom: this.options.allowCustom,
choices: choices,
custom: attr ? attr.custom : ""
@ -85,4 +85,4 @@ export default class TraitSelector extends FormApplication {
// Update the object
this.object.update(updateData);
}
}
}

View file

@ -13,7 +13,7 @@ export const _getInitiativeFormula = function(combatant) {
let nd = 1;
let mods = "";
if (actor.getFlag("sw5e", "halflingLucky")) mods += "r=1";
if (actor.getFlag("sw5e", "halflingLucky")) mods += "r1=1";
if (actor.getFlag("sw5e", "initiativeAdv")) {
nd = 2;
mods += "kh";

View file

@ -4,14 +4,13 @@ import {ClassFeatures} from "./classFeatures.js"
export const SW5E = {};
// ASCII Artwork
SW5E.ASCII = `__________________________________________
_
| |
___| |_ __ _ _ ____ ____ _ _ __ ___
/ __| __/ _\\ | |__\\ \\ /\\ / / _\\ | |__/ __|
\\__ \\ || (_) | | \\ \V \V / (_) | | \\__ \\
|___/\\__\\__/_|_| \\_/\\_/ \\__/_|_| |___/
__________________________________________`;
SW5E.ASCII = `
___________ ___________
/ _____/ \\ / \\ ____/ ____
\\_____ \\\\ \\/\\/ /____ \\_/ __ \\
/ \\\\ // \\ ___/
\\______ / \\__/\\ //______ /\\__ >
\\/ \\/ \\/ \\/ `;
/**
@ -315,6 +314,7 @@ SW5E.damageResistanceTypes = duplicate(SW5E.damageTypes);
/* -------------------------------------------- */
// armor Types
SW5E.armorPropertiesTypes = {
"Absorptive": "SW5E.ArmorProperAbsorptive",
@ -349,6 +349,19 @@ SW5E.armorPropertiesTypes = {
"Versatile": "SW5E.ArmorProperVersatile"
};
/**
* The valid units of measure for movement distances in the game system.
* By default this uses the imperial units of feet and miles.
* @type {Object<string,string>}
*/
SW5E.movementTypes = {
"burrow": "SW5E.MovementBurrow",
"climb": "SW5E.MovementClimb",
"fly": "SW5E.MovementFly",
"swim": "SW5E.MovementSwim",
"walk": "SW5E.MovementWalk",
}
/**
* The valid units of measure for movement distances in the game system.
* By default this uses the imperial units of feet and miles.

View file

@ -1,6 +1,5 @@
import {simplifyRollFormula, d20Roll, damageRoll} from "../dice.js";
import AbilityUseDialog from "../apps/ability-use-dialog.js";
import AbilityTemplate from "../pixi/ability-template.js";
/**
* Override and extend the basic :class:`Item` implementation
@ -419,7 +418,7 @@ export default class Item5e extends Item {
// Handle power upcasting
if ( requirePowerSlot ) {
const slotLevel = configuration.level;
const powerLevel = slotLevel === "pact" ? actor.data.data.powerss.pact.level : parseInt(slotLevel);
const powerLevel = slotLevel === "pact" ? actor.data.data.powers.pact.level : parseInt(slotLevel);
if (powerLevel !== id.level) {
const upcastData = mergeObject(this.data, {"data.level": powerLevel}, {inplace: false});
item = this.constructor.createOwned(upcastData, actor); // Replace the item with an upcast version
@ -898,6 +897,7 @@ export default class Item5e extends Item {
* Place a damage roll using an item (weapon, feat, power, or equipment)
* Rely upon the damageRoll logic for the core implementation.
* @param {MouseEvent} [event] An event which triggered this roll, if any
* @param {boolean} [critical] Should damage be rolled as a critical hit?
* @param {number} [powerLevel] If the item is a power, override the level for damage scaling
* @param {boolean} [versatile] If the item is a weapon, roll damage using the versatile formula
* @param {object} [options] Additional options passed to the damageRoll function
@ -942,9 +942,9 @@ export default class Item5e extends Item {
// Scale damage from up-casting powers
if ( (this.data.type === "power") ) {
if ( (itemData.scaling.mode === "cantrip") ) {
if ( (itemData.scaling.mode === "atwill") ) {
const level = this.actor.data.type === "character" ? actorData.details.level : actorData.details.powerLevel;
this._scaleCantripDamage(parts, itemData.scaling.formula, level, rollData);
this._scaleAtWillDamage(parts, itemData.scaling.formula, level, rollData);
}
else if ( powerLevel && (itemData.scaling.mode === "level") && itemData.scaling.formula ) {
const scaling = itemData.scaling.formula;

View file

@ -61,6 +61,9 @@ export default class ItemSheet5e extends ItemSheet {
data.isFlatDC = getProperty(data.item.data, "save.scaling") === "flat";
data.isLine = ["line", "wall"].includes(data.item.data.target?.type);
// Original maximum uses formula
if ( this.item._data.data?.uses?.max ) data.data.uses.max = this.item._data.data.uses.max;
// Vehicles
data.isCrewed = data.item.data.activation?.type === 'crew';
data.isMountable = this._isItemMountable(data.item);
@ -91,7 +94,7 @@ export default class ItemSheet5e extends ItemSheet {
ammo[i.id] = `${i.name} (${i.data.data.quantity})`;
}
return ammo;
}, {});
}, {[item._id]: `${item.name} (${item.data.quantity})`});
}
// Attributes
@ -335,7 +338,7 @@ export default class ItemSheet5e extends ItemSheet {
// Render the Trait Selector dialog
new TraitSelector(this.item, {
name: a.dataset.edit,
name: a.dataset.target,
title: label.innerText,
choices: Object.entries(CONFIG.SW5E.skills).reduce((obj, e) => {
if ( choices.includes(e[0] ) ) obj[e[0]] = e[1];

View file

@ -127,7 +127,6 @@ export const migrateActorData = function(actor) {
const updateData = {};
// Actor Data Updates
_migrateActorBonuses(actor, updateData);
_migrateActorMovement(actor, updateData);
_migrateActorSenses(actor, updateData);
@ -229,28 +228,14 @@ export const migrateSceneData = function(scene) {
/* Low level migration utilities
/* -------------------------------------------- */
/**
* Migrate the actor bonuses object
* @private
*/
function _migrateActorBonuses(actor, updateData) {
const b = game.system.model.Actor.character.bonuses;
for ( let k of Object.keys(actor.data.bonuses || {}) ) {
if ( k in b ) updateData[`data.bonuses.${k}`] = b[k];
else updateData[`data.bonuses.-=${k}`] = null;
}
}
/* -------------------------------------------- */
/**
* Migrate the actor speed string to movement object
* @private
*/
function _migrateActorMovement(actor, updateData) {
const ad = actor.data;
const old = ad?.attributes?.speed?.value;
if ( old === undefined ) return;
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;
@ -302,7 +287,7 @@ function _migrateActorSenses(actor, updateData) {
*/
function _migrateItemAttunement(item, updateData) {
if ( item.data.attuned === undefined ) return;
updateData["data.attunement"] = 0;
updateData["data.attunement"] = CONFIG.SW5E.attunementTypes.NONE;
updateData["data.-=attuned"] = null;
return updateData;
}

View file

@ -29,8 +29,8 @@ export default class AbilityTemplate extends MeasuredTemplate {
// Additional type-specific data
switch ( templateShape ) {
case "cone": // 5e cone RAW should be 53.13 degrees
templateData.angle = 53.13;
case "cone":
templateData.angle = CONFIG.MeasuredTemplate.defaults.angle;
break;
case "rect": // 5e rectangular AoEs are always cubes
templateData.distance = Math.hypot(target.value, target.value);

File diff suppressed because one or more lines are too long

View file

@ -1370,7 +1370,7 @@ body {
}
.sw5e.chat-card .card-footer span {
border-right: 2px groove #FFF;
padding: 0 5px 0 0;
padding: 0 3px 0 0;
font-size: 10px;
}
.sw5e.chat-card .card-footer span:last-child {
@ -1784,4 +1784,4 @@ a.entity-link i::before {
top: 2px;
height: 15px;
width: 15px;
}
}

View file

@ -392,7 +392,7 @@ button:focus {
}
.sw5e.chat-card .card-footer span {
border-right: 2px groove #FFF;
padding: 0 4px 0 0;
padding: 0 3px 0 0;
font-size: 10px;
}
.sw5e.chat-card .card-footer span:last-child {

11
sw5e.js
View file

@ -44,7 +44,7 @@ import * as migrations from "./module/migration.js";
/* -------------------------------------------- */
Hooks.once("init", function() {
console.log(`SW5e | Initializing Star Wars 5th Edition System\n${SW5E.ASCII}`);
console.log(`SW5e | Initializing SW5E System\n${SW5E.ASCII}`);
// Create a SW5E namespace within the game global
game.sw5e = {
@ -80,7 +80,10 @@ Hooks.once("init", function() {
CONFIG.Actor.entityClass = Actor5e;
CONFIG.Item.entityClass = Item5e;
CONFIG.time.roundTime = 6;
// 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;
@ -145,7 +148,7 @@ Hooks.once("setup", function() {
"abilities", "abilityAbbreviations", "abilityActivationTypes", "abilityConsumptionTypes", "actorSizes", "alignments",
"armorProficiencies", "armorPropertiesTypes", "conditionTypes", "consumableTypes", "cover", "currencies", "damageResistanceTypes",
"damageTypes", "distanceUnits", "equipmentTypes", "healingTypes", "itemActionTypes", "languages",
"limitedUsePeriods", "movementUnits", "polymorphSettings", "proficiencyLevels", "senses", "skills",
"limitedUsePeriods", "movementTypes", "movementUnits", "polymorphSettings", "proficiencyLevels", "senses", "skills",
"powerComponents", "powerLevels", "powerPreparationModes", "powerScalingModes", "powerSchools", "targetTypes",
"timePeriods", "toolProficiencies", "weaponProficiencies", "weaponProperties", "weaponTypes"
];
@ -187,7 +190,7 @@ Hooks.once("ready", function() {
// Determine whether a system migration is required and feasible
if ( !game.user.isGM ) return;
const currentVersion = game.settings.get("sw5e", "systemMigrationVersion");
const NEEDS_MIGRATION_VERSION = "1.2.0";
const NEEDS_MIGRATION_VERSION = "1.2.1";
const COMPATIBLE_MIGRATION_VERSION = 0.80;
const needsMigration = currentVersion && isNewerVersion(NEEDS_MIGRATION_VERSION, currentVersion);
if ( !needsMigration ) return;

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": "R1-A1",
"version": "1.2.2",
"author": "Dev Team",
"scripts": [],
"esmodules": ["sw5e.js"],

View file

@ -43,6 +43,15 @@
"init": {
"value": 0,
"bonus": 0
},
"movement": {
"burrow": 0,
"climb": 0,
"fly": 0,
"swim": 0,
"walk": 30,
"units": "ft",
"hover": false
}
},
"details": {
@ -76,15 +85,6 @@
},
"creature": {
"attributes": {
"movement": {
"burrow": 0,
"climb": 0,
"fly": 0,
"swim": 0,
"walk": 30,
"units": "ft",
"hover": false
},
"senses": {
"darkvision": 0,
"blindsight": 0,
@ -94,9 +94,6 @@
"special": ""
},
"powercasting": "none",
"speed": {
"_deprecated": true
}
},
"details": {
"alignment": "",

View file

@ -6,7 +6,7 @@
<input name="name" type="text" value="{{actor.name}}" placeholder="{{ localize 'SW5E.Name' }}" />
</h1>
<div class="level-experience">
<div class="charlevel">
<div class="charlevel" title="{{multiclassLabels}}">
{{ localize "SW5E.Level" }} {{data.details.level}} {{classLabels}}
</div>
{{#unless disableExperience}}

View file

@ -44,6 +44,7 @@
<section>
<h1>Hit Points</h1>
<div class="attribute-value multiple">
<h4 class="attribute-name box-title rollable">{{ localize "SW5E.Health" }}</h4>
<input name="data.attributes.hp.value" type="text" value="{{data.attributes.hp.value}}"
data-dtype="Number" placeholder="10" class="value-number" />
<span class="value-separator">/</span>

View file

@ -12,7 +12,7 @@
<label class="{{#unless data.traits.senses}}inactive{{/unless}}">
{{#unless isVehicle}}
<label>{{localize "SW5E.Senses"}}</label>
<a class="config-button" data-action="senses" title="{{localize 'SW5E.MovementConfig'}}"><i class="fas fa-cog"></i></a>
<a class="config-button" data-action="senses" title="{{localize 'SW5E.SensesConfig'}}"><i class="fas fa-cog"></i></a>
<ul class="traits-list">
{{#each senses as |v k|}}
<li class="tag {{k}}">{{v}}</li>

View file

@ -55,11 +55,17 @@
placeholder="&mdash;" value="{{data.attributes.ac.motionless}}">
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{localize 'SW5E.Speed'}}</h4>
<li class="attribute movement">
<h4 class="attribute-name box-title">
{{ localize "SW5E.Movement" }}
<a class="config-button" data-action="movement" title="{{localize 'SW5E.MovementConfig'}}"><i class="fas fa-cog"></i></a>
</h4>
<div class="attribute-value">
<input name="data.attributes.speed" type="text" placeholder="&mdash;" value="{{data.attributes.speed}}"/>
<span>{{movement.primary}}</span>
</div>
<footer class="attribute-footer">
<span>{{movement.special}}</span>
</footer>
</li>
</ul>
</section>

View file

@ -10,7 +10,7 @@
</h1>
<aside class="header-exp flexcol">
<div class="charlevel">
<div class="charlevel" title="{{multiclassLabels}}">
<label>{{ localize "SW5E.Level" }} {{data.details.level}}</label>
<span class="levels">{{classLabels}}</span>
</div>

View file

@ -13,7 +13,7 @@
{{#unless isVehicle}}
<div class="form-group">
<label>{{localize "SW5E.Senses"}}</label>
<a class="config-button" data-action="senses" title="{{localize 'SW5E.MovementConfig'}}"><i class="fas fa-cog"></i></a>
<a class="config-button" data-action="senses" title="{{localize 'SW5E.SensesConfig'}}"><i class="fas fa-cog"></i></a>
<ul class="traits-list">
{{#each senses as |v k|}}
<li class="tag {{k}}">{{v}}</li>

View file

@ -55,11 +55,17 @@
placeholder="&mdash;" value="{{data.attributes.ac.motionless}}">
</footer>
</li>
<li class="attribute">
<h4 class="attribute-name box-title">{{localize 'SW5E.Speed'}}</h4>
<li class="attribute movement">
<h4 class="attribute-name box-title">
{{ localize "SW5E.Movement" }}
<a class="config-button" data-action="movement" title="{{localize 'SW5E.MovementConfig'}}"><i class="fas fa-cog"></i></a>
</h4>
<div class="attribute-value">
<input name="data.attributes.speed" type="text" placeholder="&mdash;" value="{{data.attributes.speed}}"/>
<span>{{movement.primary}}</span>
</div>
<footer class="attribute-footer">
<span>{{movement.special}}</span>
</footer>
</li>
</ul>
</section>

View file

@ -2,7 +2,7 @@
<p>{{ title }}</p>
<p class="notes">{{note}}</p>
{{#each errors}}
<p class="notification error">{{localize this}}</p>
<p class="notification error">{{this}}</p>
{{/each}}
{{#if consumePowerSlot}}