forked from GitHub-Mirrors/foundry-sw5e
System 1.1.1 ** Requires Foundry 0.7.6
System main update to be inline with dnd5e 1.1.1 Added active effects to as many sheets as I thought applicable. Please check loot, I made an attempt but it may be broken All .less .css and actor .html updates were made to the old actors. New actors may be broken with this update removed templates\actors\oldActor\parts\actor-effects.html for newer templates\actors\parts\active-effects.html removed module\apps\cast-dialog, templates\apps\cast-cast.html, and templates\items\cast.html. I do not think they are used, I think they were deprecated when powers were treated as items, if not we can add them back in. **NOTE** REQUIRES Foundry 0.7.6
This commit is contained in:
parent
27f5fa3670
commit
68a1b6a9f0
58 changed files with 1417 additions and 1706 deletions
23
lang/en.json
23
lang/en.json
|
@ -263,6 +263,11 @@
|
|||
"SW5E.FeatureActionRecharge": "Action Recharge",
|
||||
"SW5E.Flaws": "Flaws",
|
||||
|
||||
"SW5E.EffectCreate": "Create Effect",
|
||||
"SW5E.EffectToggle": "Toggle Effect",
|
||||
"SW5E.EffectEdit": "Edit Effect",
|
||||
"SW5E.EffectDelete": "Delete Effect",
|
||||
|
||||
"SW5E.ItemTypeArchetype": "Archetype",
|
||||
"SW5E.ItemTypeBackground": "Background",
|
||||
"Sw5E.ItemTypeBackgroundPl": "Backgrounds",
|
||||
|
@ -332,6 +337,12 @@
|
|||
"SW5E.FlagsRemarkableAthleteHint": "Half-Proficiency (rounded-up) to physical Ability Checks and Initiative.",
|
||||
"SW5E.FlagsCritThreshold": "Critical Hit Threshold",
|
||||
"SW5E.FlagsCritThresholdHint": "Allow for expanded critical range; for example Improved or Superior Critical",
|
||||
"SW5E.FlagsWeaponCritThreshold": "Weapon Critical Hit Threshold",
|
||||
"SW5E.FlagsWeaponCritThresholdHint": "An expanded critical hit threshold for weapon attacks.",
|
||||
"SW5E.FlagsPowerCritThreshold": "Power Critical Hit Threshold",
|
||||
"SW5E.FlagsPowerCritThresholdHint": "An expanded critical hit threshold for power attacks.",
|
||||
"SW5E.FlagsMeleeCriticalDice": "Melee Critical Damage Dice",
|
||||
"SW5E.FlagsMeleeCriticalDiceHint": "A number of additional damage dice added to melee weapon critical hits.",
|
||||
|
||||
"SW5E.Flat": "Flat",
|
||||
"SW5E.Formula": "Formula",
|
||||
|
@ -582,6 +593,17 @@
|
|||
"SW5E.RollMode": "Roll Mode",
|
||||
"SW5E.RollSituationalBonus": "Situational Bonus?",
|
||||
"SW5E.Save": "Save",
|
||||
|
||||
"SW5E.MovementConfig": "Configure Movement Speed",
|
||||
"SW5E.MovementConfigHint": "Configure the movement speed and special movement attributes of this creature.",
|
||||
"SW5E.MovementWalk": "Walk",
|
||||
"SW5E.MovementBurrow": "Burrow",
|
||||
"SW5E.MovementClimb": "Climb",
|
||||
"SW5E.MovementHover": "Hover",
|
||||
"SW5E.MovementFly": "Fly",
|
||||
"SW5E.MovementSwim": "Swim",
|
||||
"SW5E.MovementUnits": "Units",
|
||||
|
||||
"SW5E.SheetClassCharacter": "Default Character Sheet",
|
||||
"SW5E.SheetClassCharacterOld": "Old Character Sheet",
|
||||
"SW5E.SheetClassNPC": "Default NPC Sheet",
|
||||
|
@ -836,6 +858,7 @@
|
|||
"SW5E.available": "available",
|
||||
"SW5E.description": "A comprehensive game system for running games of Star Wars 5th Edition in the Foundry VTT environment.",
|
||||
"SW5E.of": "of",
|
||||
"SW5E.per": "per",
|
||||
"SW5E.power": "power",
|
||||
"SETTINGS.5eAllowPolymorphingL": "Allow players to polymorph their own actors.",
|
||||
"SETTINGS.5eAllowPolymorphingN": "Allow Polymorphing",
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
.russoOne(14px);
|
||||
color: @colorOlive;
|
||||
border-bottom: 1px solid @colorFaint;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ----------------------------------------- */
|
||||
|
@ -142,6 +143,7 @@
|
|||
font-family: "Signika", sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -413,46 +415,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Inventory item lists
|
||||
.inventory-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 5px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
color: @colorTan;
|
||||
|
||||
// Inventory Item
|
||||
.item {
|
||||
line-height: 30px;
|
||||
padding: 0 2px; // to align with the header border
|
||||
border-bottom: 1px solid @colorFaint;
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
// Item Header Name
|
||||
.item-name {
|
||||
cursor: pointer;
|
||||
max-height: 30px;
|
||||
overflow: hidden;
|
||||
|
||||
.item-image {
|
||||
flex: 0 0 30px;
|
||||
background-size: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
&.rollable:hover .item-image {
|
||||
background-image: url("../../icons/svg/d20-grey.svg") !important;
|
||||
}
|
||||
&.rollable .item-image:hover {
|
||||
background-image: url("../../icons/svg/d20-black.svg") !important;
|
||||
}
|
||||
|
||||
i.attuned {
|
||||
color: @colorTan;
|
||||
}
|
||||
|
@ -474,49 +448,26 @@
|
|||
flex: 0 0 80px;
|
||||
text-align: right;
|
||||
font-size: 11px;
|
||||
color: @colorTan;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
// Inventory Header
|
||||
.inventory-header {
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: @borderGroove;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
|
||||
h3 {
|
||||
margin: 0 -5px 0 0;
|
||||
padding-left: 5px;
|
||||
.russoOne();
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.item-controls a.item-create {
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Item names
|
||||
.item-name {
|
||||
color: @colorDark;
|
||||
}
|
||||
|
||||
// Item Detail Sections
|
||||
.item-detail {
|
||||
flex: 0 0 70px;
|
||||
font-size: 12px;
|
||||
color: @colorTan;
|
||||
text-align: center;
|
||||
border-right: 1px solid @colorFaint;
|
||||
word-break: break-word;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
&.item-action {flex: 0 0 100px}
|
||||
}
|
||||
|
@ -527,24 +478,9 @@
|
|||
border-right: 1px solid @colorFaint;
|
||||
}
|
||||
|
||||
.item-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// Item Control Buttons
|
||||
.item-controls {
|
||||
flex: 0 0 44px;
|
||||
.flexrow();
|
||||
justify-content: flex-end;
|
||||
|
||||
a {
|
||||
flex: 0 0 22px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: @colorTan;
|
||||
}
|
||||
}
|
||||
|
||||
// Item Dropdown Summary
|
||||
|
@ -553,6 +489,7 @@
|
|||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
padding: 0.25em 0.5em;
|
||||
color: @colorDark;
|
||||
border-top: 1px solid @colorFaint;
|
||||
}
|
||||
}
|
||||
|
@ -693,44 +630,6 @@
|
|||
// Empty powerbook controls
|
||||
.powerbook-empty .item-controls { flex: 1; }
|
||||
|
||||
/* ----------------------------------------- */
|
||||
/* Active Effects */
|
||||
/* ----------------------------------------- */
|
||||
|
||||
.effects {
|
||||
.effect-name{
|
||||
flex: 2;
|
||||
align-items: center;
|
||||
color: @colorDark;
|
||||
h4 { margin: 0; }
|
||||
}
|
||||
|
||||
.effect-icon {
|
||||
flex: 0 0 30px;
|
||||
height: 30px;
|
||||
margin-right: 5px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.effect-source,
|
||||
.effect-duration {
|
||||
text-align: center;
|
||||
border-left: 1px solid @colorFaint;
|
||||
border-right: 1px solid @colorFaint;
|
||||
}
|
||||
|
||||
.effect-controls {
|
||||
flex: 0 0 60px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.effect {
|
||||
align-items: center;
|
||||
border-bottom: 1px solid @colorFaint;
|
||||
&:last-child { border-bottom: none; }
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------- */
|
||||
/* TinyMCE */
|
||||
/* ----------------------------------------- */
|
||||
|
@ -739,3 +638,18 @@
|
|||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
#actor-flags {
|
||||
.window-content {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
form {
|
||||
height: 100%;
|
||||
}
|
||||
.form-body {
|
||||
height: calc(100% - 40px);
|
||||
padding-right: 8px;
|
||||
margin-bottom: 4px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
|
@ -362,6 +362,108 @@
|
|||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------- */
|
||||
/* Items Lists */
|
||||
/* ----------------------------------------- */
|
||||
|
||||
.items-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
color: @colorTan;
|
||||
|
||||
// Child lists
|
||||
.item-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// Individual Item
|
||||
.item {
|
||||
align-items: center;
|
||||
padding: 0 2px; // to align with the header border
|
||||
border-bottom: 1px solid @colorFaint;
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
.item-name {
|
||||
color: @colorDark;
|
||||
.item-image {
|
||||
flex: 0 0 30px;
|
||||
height: 30px;
|
||||
background-size: 30px;
|
||||
border: none;
|
||||
margin-right: 5px;
|
||||
}
|
||||
h4 {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Section Header
|
||||
.items-header {
|
||||
height: 28px;
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: @borderGroove;
|
||||
font-weight: bold;
|
||||
> * {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.item-name {
|
||||
padding-left: 5px;
|
||||
.modesto();
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
// Item Name
|
||||
.item-name {
|
||||
flex: 2;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// Control Buttons
|
||||
.item-controls {
|
||||
flex: 0 0 60px;
|
||||
justify-content: space-between;
|
||||
a {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------- */
|
||||
/* Active Effects */
|
||||
/* ----------------------------------------- */
|
||||
|
||||
.effects .item {
|
||||
.effect-source,
|
||||
.effect-duration,
|
||||
.effect-controls {
|
||||
text-align: center;
|
||||
border-left: 1px solid @colorFaint;
|
||||
border-right: 1px solid @colorFaint;
|
||||
font-size: 12px;
|
||||
}
|
||||
.effect-controls {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -116,17 +116,17 @@
|
|||
}
|
||||
|
||||
.form-group.uses-per {
|
||||
.form-fields {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
input {
|
||||
flex: 1;
|
||||
flex: 0 0 32px;
|
||||
}
|
||||
span {
|
||||
flex: 0 0 16px;
|
||||
}
|
||||
select {
|
||||
flex: 3;
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
span.sep {
|
||||
flex: 0 0 8px;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import AbilityTemplate from "../pixi/ability-template.js";
|
|||
import {SW5E} from '../config.js';
|
||||
|
||||
/**
|
||||
* Extend the base Actor class to implement additional logic specialized for SW5e.
|
||||
* Extend the base Actor class to implement additional system-specific logic for SW5e.
|
||||
*/
|
||||
export default class Actor5e extends Actor {
|
||||
|
||||
|
@ -20,48 +20,6 @@ export default class Actor5e extends Actor {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @override
|
||||
* TODO: This becomes unnecessary after 0.7.x is released
|
||||
*/
|
||||
initialize() {
|
||||
try {
|
||||
this.prepareData();
|
||||
} catch(err) {
|
||||
console.error(`Failed to initialize data for ${this.constructor.name} ${this.id}:`);
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @override
|
||||
* TODO: This becomes unnecessary after 0.7.x is released
|
||||
*/
|
||||
prepareData() {
|
||||
const is07x = !isNewerVersion("0.7.1", game.data.version);
|
||||
if ( is07x ) this.data = duplicate(this._data);
|
||||
if (!this.data.img) this.data.img = CONST.DEFAULT_TOKEN;
|
||||
if ( !this.data.name ) this.data.name = "New " + this.entity;
|
||||
this.prepareBaseData();
|
||||
this.prepareEmbeddedEntities();
|
||||
if ( is07x ) this.applyActiveEffects();
|
||||
this.prepareDerivedData();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @override
|
||||
* TODO: This becomes unnecessary after 0.7.x is released
|
||||
*/
|
||||
applyActiveEffects() {
|
||||
if (!isNewerVersion("0.7.1", game.data.version)) return super.applyActiveEffects();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
prepareBaseData() {
|
||||
switch ( this.data.type ) {
|
||||
|
@ -116,6 +74,11 @@ export default class Actor5e extends Actor {
|
|||
abl.save = Math.max(abl.save, originalSaves[id].save);
|
||||
}
|
||||
}
|
||||
|
||||
// Inventory encumbrance
|
||||
data.attributes.encumbrance = this._computeEncumbrance(actorData);
|
||||
|
||||
// Prepare skills
|
||||
this._prepareSkills(actorData, bonuses, checkBonus, originalSkills);
|
||||
|
||||
// Determine Initiative Modifier
|
||||
|
@ -126,7 +89,8 @@ export default class Actor5e extends Actor {
|
|||
if ( joat ) init.prof = Math.floor(0.5 * data.attributes.prof);
|
||||
else if ( athlete ) init.prof = Math.ceil(0.5 * data.attributes.prof);
|
||||
else init.prof = 0;
|
||||
init.bonus = Number(init.value + (flags.initiativeAlert ? 5 : 0));
|
||||
init.value = init.value ?? 0;
|
||||
init.bonus = init.value + (flags.initiativeAlert ? 5 : 0);
|
||||
init.total = init.mod + init.prof + init.bonus;
|
||||
|
||||
// Prepare power-casting data
|
||||
|
@ -169,7 +133,7 @@ export default class Actor5e extends Actor {
|
|||
}
|
||||
return obj;
|
||||
}, {});
|
||||
data.prof = this.data.data.attributes.prof;
|
||||
data.prof = this.data.data.attributes.prof || 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -177,33 +141,36 @@ export default class Actor5e extends Actor {
|
|||
|
||||
/**
|
||||
* Return the features which a character is awarded for each class level
|
||||
* @param cls {Object} Data object for class, equivalent to Item5e.data or raw compendium entry
|
||||
* @param {string} className The class name being added
|
||||
* @param {string} subclassName The subclass of the class being added, if any
|
||||
* @param {number} level The number of levels in the added class
|
||||
* @param {number} priorLevel The previous level of the added class
|
||||
* @return {Promise<Item5e[]>} Array of Item5e entities
|
||||
*/
|
||||
static async getClassFeatures(cls) {
|
||||
const level = cls.data.levels;
|
||||
const className = cls.name.toLowerCase();
|
||||
static async getClassFeatures({className="", subclassName="", level=1, priorLevel=0}={}) {
|
||||
className = className.toLowerCase();
|
||||
subclassName = subclassName.slugify();
|
||||
|
||||
// Get the configuration of features which may be added
|
||||
const clsConfig = CONFIG.SW5E.classFeatures[className];
|
||||
if (!clsConfig) return [];
|
||||
let featureIDs = clsConfig["features"][level] || [];
|
||||
const subclassName = cls.data.subclass.toLowerCase().slugify();
|
||||
if (!clsConfig) return [];
|
||||
|
||||
// Identify subclass features
|
||||
if ( subclassName !== "" ) {
|
||||
const subclassConfig = clsConfig["subclasses"][subclassName];
|
||||
if ( subclassConfig !== undefined ) {
|
||||
const subclassFeatureIDs = subclassConfig["features"][level];
|
||||
if ( subclassFeatureIDs ) {
|
||||
featureIDs = featureIDs.concat(subclassFeatureIDs);
|
||||
}
|
||||
}
|
||||
else console.warn("Invalid subclass: " + subclassName);
|
||||
// Acquire class features
|
||||
let ids = [];
|
||||
for ( let [l, f] of Object.entries(clsConfig.features || {}) ) {
|
||||
l = parseInt(l);
|
||||
if ( (l <= level) && (l > priorLevel) ) ids = ids.concat(f);
|
||||
}
|
||||
|
||||
// Acquire subclass features
|
||||
const subConfig = clsConfig.subclasses[subclassName] || {};
|
||||
for ( let [l, f] of Object.entries(subConfig.features || {}) ) {
|
||||
l = parseInt(l);
|
||||
if ( (l <= level) && (l > priorLevel) ) ids = ids.concat(f);
|
||||
}
|
||||
|
||||
// Load item data for all identified features
|
||||
const features = await Promise.all(featureIDs.map(id => fromUuid(id)));
|
||||
const features = await Promise.all(ids.map(id => fromUuid(id)));
|
||||
|
||||
// Class powers should always be prepared
|
||||
for ( const feature of features ) {
|
||||
|
@ -237,35 +204,28 @@ export default class Actor5e extends Actor {
|
|||
for (let u of updated instanceof Array ? updated : [updated]) {
|
||||
const item = this.items.get(u._id);
|
||||
if (!item || (item.data.type !== "class")) continue;
|
||||
const classData = duplicate(item.data);
|
||||
let changed = false;
|
||||
const updateData = expandObject(u);
|
||||
const config = {
|
||||
className: updateData.name || item.data.name,
|
||||
subclassName: updateData.data.subclass || item.data.data.subclass,
|
||||
level: getProperty(updateData, "data.levels"),
|
||||
priorLevel: item ? item.data.data.levels : 0
|
||||
}
|
||||
|
||||
// Get and create features for an increased class level
|
||||
const newLevels = getProperty(u, "data.levels");
|
||||
if (newLevels && (newLevels > item.data.data.levels)) {
|
||||
classData.data.levels = newLevels;
|
||||
changed = true;
|
||||
}
|
||||
let changed = false;
|
||||
if ( config.level && (config.level > config.priorLevel)) changed = true;
|
||||
if ( config.subclassName !== item.data.data.subclass ) changed = true;
|
||||
|
||||
// Get features for a newly changed subclass
|
||||
const newSubclass = getProperty(u, "data.subclass");
|
||||
if (newSubclass && (newSubclass !== item.data.data.subclass)) {
|
||||
classData.data.subclass = newSubclass;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Get the new features
|
||||
// Get features to create
|
||||
if ( changed ) {
|
||||
const features = await Actor5e.getClassFeatures(classData);
|
||||
if ( features.length ) toCreate.push(...features);
|
||||
const existing = new Set(this.items.map(i => i.name));
|
||||
const features = await Actor5e.getClassFeatures(config);
|
||||
for ( let f of features ) {
|
||||
if ( !existing.has(f.name) ) toCreate.push(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// De-dupe created items with ones that already exist (by name)
|
||||
if ( toCreate.length ) {
|
||||
const existing = new Set(this.items.map(i => i.name));
|
||||
toCreate = toCreate.filter(c => !existing.has(c.name));
|
||||
}
|
||||
return toCreate
|
||||
}
|
||||
|
||||
|
@ -301,9 +261,6 @@ export default class Actor5e extends Actor {
|
|||
const required = xp.max - prior;
|
||||
const pct = Math.round((xp.value - prior) * 100 / required);
|
||||
xp.pct = Math.clamped(pct, 0, 100);
|
||||
|
||||
// Inventory encumbrance
|
||||
data.attributes.encumbrance = this._computeEncumbrance(actorData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -369,17 +326,17 @@ export default class Actor5e extends Actor {
|
|||
}
|
||||
if ( joat && (skl.value === 0 ) ) multi = 0.5;
|
||||
|
||||
// Retain the maximum skill proficiency when skill proficiencies are merged
|
||||
if ( originalSkills ) {
|
||||
skl.value = Math.max(skl.value, originalSkills[id].value);
|
||||
}
|
||||
|
||||
// Compute modifier
|
||||
skl.bonus = checkBonus + skillBonus;
|
||||
skl.mod = data.abilities[skl.ability].mod;
|
||||
skl.prof = round(multi * data.attributes.prof);
|
||||
skl.total = skl.mod + skl.prof + skl.bonus;
|
||||
|
||||
// If we merged skills when transforming, take the highest bonus here.
|
||||
if (originalSkills && skl.value > 0.5) {
|
||||
skl.total = Math.max(skl.total, originalSkills[id].total);
|
||||
}
|
||||
|
||||
// Compute passive bonus
|
||||
const passive = observant && (feats.observantFeat.skills.includes(id)) ? 5 : 0;
|
||||
skl.passive = 10 + skl.total + passive;
|
||||
|
@ -489,8 +446,8 @@ export default class Actor5e extends Actor {
|
|||
else powers.pact.max = Math.max(1, Math.min(pl, 2), Math.min(pl - 8, 3), Math.min(pl - 13, 4));
|
||||
powers.pact.value = Math.min(powers.pact.value, powers.pact.max);
|
||||
} else {
|
||||
powers.pact.level = 0;
|
||||
powers.pact.max = 0;
|
||||
powers.pact.max = parseInt(powers.pact.override) || 0
|
||||
powers.pact.level = powers.pact.max > 0 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,14 +470,14 @@ export default class Actor5e extends Actor {
|
|||
if ( !physicalItems.includes(i.type) ) return weight;
|
||||
const q = i.data.quantity || 0;
|
||||
const w = i.data.weight || 0;
|
||||
return weight + Math.round(q * w * 10) / 10;
|
||||
return weight + (q * w);
|
||||
}, 0);
|
||||
|
||||
// [Optional] add Currency Weight
|
||||
if ( game.settings.get("sw5e", "currencyWeight") ) {
|
||||
const currency = actorData.data.currency;
|
||||
const numCoins = Object.values(currency).reduce((val, denom) => val += Math.max(denom, 0), 0);
|
||||
weight += Math.round((numCoins * 10) / CONFIG.SW5E.encumbrance.currencyPerWeight) / 10;
|
||||
weight += numCoins / CONFIG.SW5E.encumbrance.currencyPerWeight;
|
||||
}
|
||||
|
||||
// Determine the encumbrance size class
|
||||
|
@ -535,9 +492,10 @@ export default class Actor5e extends Actor {
|
|||
if ( this.getFlag("sw5e", "powerfulBuild") ) mod = Math.min(mod * 2, 8);
|
||||
|
||||
// Compute Encumbrance percentage
|
||||
weight = weight.toNearest(0.1);
|
||||
const max = actorData.data.abilities.str.value * CONFIG.SW5E.encumbrance.strMultiplier * mod;
|
||||
const pct = Math.clamped((weight* 100) / max, 0, 100);
|
||||
return { value: weight, max, pct, encumbered: pct > (2/3) };
|
||||
const pct = Math.clamped((weight * 100) / max, 0, 100);
|
||||
return { value: weight.toNearest(0.1), max, pct, encumbered: pct > (2/3) };
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -564,9 +522,6 @@ export default class Actor5e extends Actor {
|
|||
/** @override */
|
||||
async update(data, options={}) {
|
||||
|
||||
// TODO: 0.7.1 compatibility - remove when stable
|
||||
if ( !data.hasOwnProperty("data") ) data = expandObject(data);
|
||||
|
||||
// Apply changes in Actor size to Token width/height
|
||||
const newSize = getProperty(data, "data.traits.size");
|
||||
if ( newSize && (newSize !== getProperty(this.data, "data.traits.size")) ) {
|
||||
|
@ -577,7 +532,7 @@ export default class Actor5e extends Actor {
|
|||
data["token.width"] = size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reset death save counters
|
||||
if ( (this.data.data.attributes.hp.value <= 0) && (getProperty(data, "data.attributes.hp.value") > 0) ) {
|
||||
setProperty(data, "data.attributes.death.success", 0);
|
||||
|
@ -858,7 +813,7 @@ export default class Actor5e extends Actor {
|
|||
rollAbilitySave(abilityId, options={}) {
|
||||
const label = CONFIG.SW5E.abilities[abilityId];
|
||||
const abl = this.data.data.abilities[abilityId];
|
||||
|
||||
|
||||
// Construct parts
|
||||
const parts = ["@mod"];
|
||||
const data = {mod: abl.mod};
|
||||
|
@ -937,7 +892,7 @@ export default class Actor5e extends Actor {
|
|||
|
||||
// Take action depending on the result
|
||||
const success = roll.total >= 10;
|
||||
const d20 = roll.dice[0].total;
|
||||
const d20 = roll.dice[0].total;
|
||||
|
||||
// Save success
|
||||
if ( success ) {
|
||||
|
@ -1429,14 +1384,16 @@ export default class Actor5e extends Actor {
|
|||
if ( !original ) return;
|
||||
|
||||
// Get the Tokens which represent this actor
|
||||
const tokens = this.getActiveTokens(true);
|
||||
const tokenUpdates = tokens.map(t => {
|
||||
const tokenData = duplicate(original.data.token);
|
||||
tokenData._id = t.id;
|
||||
tokenData.actorId = original.id;
|
||||
return tokenData;
|
||||
});
|
||||
canvas.scene.updateEmbeddedEntity("Token", tokenUpdates);
|
||||
if ( canvas.ready ) {
|
||||
const tokens = this.getActiveTokens(true);
|
||||
const tokenUpdates = tokens.map(t => {
|
||||
const tokenData = duplicate(original.data.token);
|
||||
tokenData._id = t.id;
|
||||
tokenData.actorId = original.id;
|
||||
return tokenData;
|
||||
});
|
||||
canvas.scene.updateEmbeddedEntity("Token", tokenUpdates);
|
||||
}
|
||||
|
||||
// Delete the polymorphed Actor and maybe re-render the original sheet
|
||||
const isRendered = this.sheet.rendered;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import Item5e from "../../item/entity.js";
|
||||
import TraitSelector from "../../apps/trait-selector.js";
|
||||
import ActorSheetFlags from "../../apps/actor-flags.js";
|
||||
import MovementConfig from "../../apps/movement-config.js";
|
||||
import {SW5E} from '../../config.js';
|
||||
import {onManageActiveEffect, prepareActiveEffectCategories} from "../../effects.js";
|
||||
|
||||
/**
|
||||
* Extend the basic ActorSheet class to do all the SW5e things!
|
||||
* Extend the basic ActorSheet class to suppose SW5e-specific logic and functionality.
|
||||
* This sheet is an Abstract layer which is not used.
|
||||
* @extends {ActorSheet}
|
||||
*/
|
||||
|
@ -94,6 +96,9 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
}
|
||||
}
|
||||
|
||||
// Movement speeds
|
||||
data.movement = this._getMovementSpeed(data.actor);
|
||||
|
||||
// Update traits
|
||||
this._prepareTraits(data.actor.data.traits);
|
||||
|
||||
|
@ -101,7 +106,7 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
this._prepareItems(data);
|
||||
|
||||
// Prepare active effects
|
||||
this._prepareEffects(data);
|
||||
data.effects = prepareActiveEffectCategories(this.entity.effects);
|
||||
|
||||
// Return data to the sheet
|
||||
return data
|
||||
|
@ -109,6 +114,28 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Prepare the display of movement speed data for the Actor
|
||||
* @param {object} actorData
|
||||
* @returns {{primary: string, special: string}}
|
||||
* @private
|
||||
*/
|
||||
_getMovementSpeed(actorData) {
|
||||
const movement = actorData.data.attributes.movement;
|
||||
const 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(", ") : ""
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Prepare the data structure for traits data like languages, resistances & vulnerabilities, and proficiencies
|
||||
* @param {object} traits The raw traits data object from the actor data
|
||||
|
@ -147,43 +174,6 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Prepare the data structure for Active Effects which are currently applied to the Actor.
|
||||
* @param {object} data The object of rendering data which is being prepared
|
||||
* @private
|
||||
*/
|
||||
_prepareEffects(data) {
|
||||
|
||||
// Define effect header categories
|
||||
const categories = {
|
||||
temporary: {
|
||||
label: "Temporary Effects",
|
||||
effects: []
|
||||
},
|
||||
passive: {
|
||||
label: "Passive Effects",
|
||||
effects: []
|
||||
},
|
||||
inactive: {
|
||||
label: "Inactive Effects",
|
||||
effects: []
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate over active effects, classifying them into categories
|
||||
for ( let e of this.actor.effects ) {
|
||||
e._getSourceName(); // Trigger a lookup for the source name
|
||||
if ( e.data.disabled ) categories.inactive.effects.push(e);
|
||||
else if ( e.isTemporary ) categories.temporary.effects.push(e);
|
||||
else categories.passive.effects.push(e);
|
||||
}
|
||||
|
||||
// Add the prepared categories of effects to the rendering data
|
||||
return data.effects = categories;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Insert a power into the powerbook object when rendering the character sheet
|
||||
* @param {Object} data The Actor data being prepared
|
||||
|
@ -242,7 +232,7 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
registerSection(sl, lvl, CONFIG.SW5E.powerLevels[lvl], levels[sl]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Pact magic users have cantrips and a pact magic section
|
||||
if ( levels.pact && levels.pact.max ) {
|
||||
if ( !powerbook["0"] ) registerSection("power0", 0, CONFIG.SW5E.powerLevels[0]);
|
||||
|
@ -363,7 +353,7 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
filterLists.on("click", ".filter-item", this._onToggleFilter.bind(this));
|
||||
|
||||
// Item summaries
|
||||
html.find('.item .item-name h4').click(event => this._onItemSummary(event));
|
||||
html.find('.item .item-name.rollable h4').click(event => this._onItemSummary(event));
|
||||
|
||||
// Editable Only Listeners
|
||||
if ( this.isEditable ) {
|
||||
|
@ -383,6 +373,7 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
html.find('.trait-selector').click(this._onTraitSelector.bind(this));
|
||||
|
||||
// Configure Special Flags
|
||||
html.find('.configure-movement').click(this._onMovementConfig.bind(this));
|
||||
html.find('.configure-flags').click(this._onConfigureFlags.bind(this));
|
||||
|
||||
// Owned Item management
|
||||
|
@ -393,8 +384,7 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
html.find('.slot-max-override').click(this._onPowerSlotOverride.bind(this));
|
||||
|
||||
// Active Effect management
|
||||
html.find(".effect-control").click(this._onManageActiveEffect.bind(this));
|
||||
|
||||
html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.entity));
|
||||
}
|
||||
|
||||
// Owner Only Listeners
|
||||
|
@ -565,7 +555,7 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
/** @override */
|
||||
async _onDropItemCreate(itemData) {
|
||||
|
||||
|
@ -576,9 +566,7 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
}
|
||||
|
||||
// Create the owned item as normal
|
||||
// TODO remove conditional logic in 0.7.x
|
||||
if (isNewerVersion(game.data.version, "0.6.9")) return super._onDropItemCreate(itemData);
|
||||
else return this.actor.createEmbeddedEntity("OwnedItem", itemData);
|
||||
return super._onDropItemCreate(itemData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -731,28 +719,6 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Manage Active Effect instances through the Actor Sheet via effect control buttons.
|
||||
* @param {MouseEvent} event The left-click event on the effect control
|
||||
* @private
|
||||
*/
|
||||
_onManageActiveEffect(event) {
|
||||
event.preventDefault();
|
||||
const a = event.currentTarget;
|
||||
const li = a.closest(".effect");
|
||||
const effect = this.actor.effects.get(li.dataset.effectId);
|
||||
switch ( a.dataset.action ) {
|
||||
case "edit":
|
||||
return effect.sheet.render(true);
|
||||
case "delete":
|
||||
return effect.delete();
|
||||
case "toggle":
|
||||
return effect.update({disabled: !effect.data.disabled});
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Handle rolling an Ability check, either a test or a saving throw
|
||||
* @param {Event} event The originating click event
|
||||
|
@ -825,6 +791,18 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Handle spawning the TraitSelector application which allows a checkbox of multiple trait options
|
||||
* @param {Event} event The click event which originated the selection
|
||||
* @private
|
||||
*/
|
||||
_onMovementConfig(event) {
|
||||
event.preventDefault();
|
||||
new MovementConfig(this.object).render(true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
_getHeaderButtons() {
|
||||
let buttons = super._getHeaderButtons();
|
||||
|
@ -839,90 +817,4 @@ export default class ActorSheet5e extends ActorSheet {
|
|||
});
|
||||
return buttons;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* DEPRECATED */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* TODO: Remove once 0.7.x is release
|
||||
* @deprecated since 0.7.0
|
||||
*/
|
||||
async _onDrop (event) {
|
||||
event.preventDefault();
|
||||
|
||||
// Get dropped data
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(event.dataTransfer.getData('text/plain'));
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
if ( !data ) return false;
|
||||
|
||||
// Handle the drop with a Hooked function
|
||||
const allowed = Hooks.call("dropActorSheetData", this.actor, this, data);
|
||||
if ( allowed === false ) return;
|
||||
|
||||
// Case 1 - Dropped Item
|
||||
if ( data.type === "Item" ) {
|
||||
return this._onDropItem(event, data);
|
||||
}
|
||||
|
||||
// Case 2 - Dropped Actor
|
||||
if ( data.type === "Actor" ) {
|
||||
return this._onDropActor(event, data);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* TODO: Remove once 0.7.x is release
|
||||
* @deprecated since 0.7.0
|
||||
*/
|
||||
async _onDropItem(event, data) {
|
||||
if ( !this.actor.owner ) return false;
|
||||
let itemData = await this._getItemDropData(event, data);
|
||||
|
||||
// Handle item sorting within the same Actor
|
||||
const actor = this.actor;
|
||||
let sameActor = (data.actorId === actor._id) || (actor.isToken && (data.tokenId === actor.token.id));
|
||||
if (sameActor) return this._onSortItem(event, itemData);
|
||||
|
||||
// Create a new item
|
||||
this._onDropItemCreate(itemData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* TODO: Remove once 0.7.x is release
|
||||
* @deprecated since 0.7.0
|
||||
*/
|
||||
async _getItemDropData(event, data) {
|
||||
let itemData = null;
|
||||
|
||||
// Case 1 - Import from a Compendium pack
|
||||
if (data.pack) {
|
||||
const pack = game.packs.get(data.pack);
|
||||
if (pack.metadata.entity !== "Item") return;
|
||||
itemData = await pack.getEntry(data.id);
|
||||
}
|
||||
|
||||
// Case 2 - Data explicitly provided
|
||||
else if (data.data) {
|
||||
itemData = data.data;
|
||||
}
|
||||
|
||||
// Case 3 - Import from World entity
|
||||
else {
|
||||
let item = game.items.get(data.id);
|
||||
if (!item) return;
|
||||
itemData = item.data;
|
||||
}
|
||||
|
||||
// Return a copy of the extracted data
|
||||
return duplicate(itemData);
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
|||
backpack: { label: "SW5E.ItemTypeContainerPl", items: [], dataset: {type: "backpack"} },
|
||||
loot: { label: "SW5E.ItemTypeLootPl", items: [], dataset: {type: "loot"} }
|
||||
};
|
||||
|
||||
|
||||
// Partition items by category
|
||||
let [items, powers, feats, classes, species, archetypes, classfeatures, backgrounds, fightingstyles, fightingmasteries, lightsaberforms] = data.items.reduce((arr, item) => {
|
||||
|
||||
|
@ -109,7 +109,7 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
|||
for ( let i of items ) {
|
||||
i.data.quantity = i.data.quantity || 0;
|
||||
i.data.weight = i.data.weight || 0;
|
||||
i.totalWeight = Math.round(i.data.quantity * i.data.weight * 10) / 10;
|
||||
i.totalWeight = (i.data.quantity * i.data.weight).toNearest(0.1);
|
||||
inventory[i.type].items.push(i);
|
||||
}
|
||||
|
||||
|
@ -123,12 +123,12 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
|||
const features = {
|
||||
classes: { label: "SW5E.ItemTypeClassPl", items: [], hasActions: false, dataset: {type: "class"}, isClass: true },
|
||||
classfeatures: { label: "SW5E.ItemTypeClassFeats", items: [], hasActions: false, dataset: {type: "classfeature"}, isClassfeature: true },
|
||||
archetype: { label: "SW5E.ItemTypeArchetype", items: [], hasActions: false, dataset: {type: "archetype"}, isArchetype: true },
|
||||
species: { label: "SW5E.ItemTypeSpecies", items: [], hasActions: false, dataset: {type: "species"}, isSpecies: true },
|
||||
background: { label: "SW5E.ItemTypeBackground", items: [], hasActions: false, dataset: {type: "background"}, isBackground: true },
|
||||
fightingstyles: { label: "SW5E.ItemTypeFightingStylePl", items: [], hasActions: false, dataset: {type: "fightingstyle"}, isFightingstyle: true },
|
||||
fightingmasteries: { label: "SW5E.ItemTypeFightingMasteryPl", items: [], hasActions: false, dataset: {type: "fightingmastery"}, isFightingmastery: true },
|
||||
lightsaberforms: { label: "SW5E.ItemTypeLightsaberFormPl", items: [], hasActions: false, dataset: {type: "lightsaberform"}, isLightsaberform: true },
|
||||
archetype: { label: "SW5E.ItemTypeArchetype", items: [], hasActions: false, dataset: {type: "archetype"}, isArchetype: true },
|
||||
species: { label: "SW5E.ItemTypeSpecies", items: [], hasActions: false, dataset: {type: "species"}, isSpecies: true },
|
||||
background: { label: "SW5E.ItemTypeBackground", items: [], hasActions: false, dataset: {type: "background"}, isBackground: true },
|
||||
fightingstyles: { label: "SW5E.ItemTypeFightingStylePl", items: [], hasActions: false, dataset: {type: "fightingstyle"}, isFightingstyle: true },
|
||||
fightingmasteries: { label: "SW5E.ItemTypeFightingMasteryPl", items: [], hasActions: false, dataset: {type: "fightingmastery"}, isFightingmastery: true },
|
||||
lightsaberforms: { label: "SW5E.ItemTypeLightsaberFormPl", items: [], hasActions: false, dataset: {type: "lightsaberform"}, isLightsaberform: true },
|
||||
active: { label: "SW5E.FeatureActive", items: [], hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
||||
passive: { label: "SW5E.FeaturePassive", items: [], hasActions: false, dataset: {type: "feat"} }
|
||||
};
|
||||
|
@ -138,13 +138,13 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
|||
}
|
||||
classes.sort((a, b) => b.levels - a.levels);
|
||||
features.classes.items = classes;
|
||||
features.classfeatures.items = classfeatures;
|
||||
features.archetype.items = archetypes;
|
||||
features.species.items = species;
|
||||
features.background.items = backgrounds;
|
||||
features.fightingstyles.items = fightingstyles;
|
||||
features.fightingmasteries.items = fightingmasteries;
|
||||
features.lightsaberforms.items = lightsaberforms;
|
||||
features.classfeatures.items = classfeatures;
|
||||
features.archetype.items = archetypes;
|
||||
features.species.items = species;
|
||||
features.background.items = backgrounds;
|
||||
features.fightingstyles.items = fightingstyles;
|
||||
features.fightingmasteries.items = fightingmasteries;
|
||||
features.lightsaberforms.items = lightsaberforms;
|
||||
|
||||
// Assign and return
|
||||
data.inventory = Object.values(inventory);
|
||||
|
@ -189,9 +189,6 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
|||
super.activateListeners(html);
|
||||
if ( !this.options.editable ) return;
|
||||
|
||||
// Inventory Functions
|
||||
html.find(".currency-convert").click(this._onConvertCurrency.bind(this));
|
||||
|
||||
// Item State Toggling
|
||||
html.find('.item-toggle').click(this._onToggleItem.bind(this));
|
||||
|
||||
|
@ -199,8 +196,8 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
|||
html.find('.short-rest').click(this._onShortRest.bind(this));
|
||||
html.find('.long-rest').click(this._onLongRest.bind(this));
|
||||
|
||||
// Death saving throws
|
||||
html.find('.death-save').click(this._onDeathSave.bind(this));
|
||||
// Rollable sheet actions
|
||||
html.find(".rollable[data-action]").click(this._onSheetAction.bind(this));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -210,14 +207,19 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
|||
* @param {MouseEvent} event The originating click event
|
||||
* @private
|
||||
*/
|
||||
_onDeathSave(event) {
|
||||
_onSheetAction(event) {
|
||||
event.preventDefault();
|
||||
return this.actor.rollDeathSave({event: event});
|
||||
const button = event.currentTarget;
|
||||
switch( button.dataset.action ) {
|
||||
case "rollDeathSave":
|
||||
return this.actor.rollDeathSave({event: event});
|
||||
case "rollInitiative":
|
||||
return this.actor.rollInitiative({createCombatants: true});
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Handle toggling the state of an Owned Item within the Actor
|
||||
* @param {Event} event The triggering click event
|
||||
|
@ -259,53 +261,39 @@ export default class ActorSheet5eCharacter extends ActorSheet5e {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Handle mouse click events to convert currency to the highest possible denomination
|
||||
* @param {MouseEvent} event The originating click event
|
||||
* @private
|
||||
*/
|
||||
async _onConvertCurrency(event) {
|
||||
event.preventDefault();
|
||||
return Dialog.confirm({
|
||||
title: `${game.i18n.localize("SW5E.CurrencyConvert")}`,
|
||||
content: `<p>${game.i18n.localize("SW5E.CurrencyConvertHint")}</p>`,
|
||||
yes: () => this.actor.convertCurrency()
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
async _onDropItemCreate(itemData) {
|
||||
let addLevel = false;
|
||||
|
||||
// Upgrade the number of class levels a character has and add features
|
||||
if ( itemData.type === "class" ) {
|
||||
const cls = this.actor.itemTypes.class.find(c => c.name === itemData.name);
|
||||
const classWasAlreadyPresent = !!cls;
|
||||
let priorLevel = cls?.data.data.levels ?? 0;
|
||||
const hasClass = !!cls;
|
||||
|
||||
// Add new features for class level
|
||||
if ( !classWasAlreadyPresent ) {
|
||||
Actor5e.getClassFeatures(itemData).then(features => {
|
||||
this.actor.createEmbeddedEntity("OwnedItem", features);
|
||||
});
|
||||
// Increment levels instead of creating a new item
|
||||
if ( hasClass ) {
|
||||
const next = Math.min(priorLevel + 1, 20 + priorLevel - this.actor.data.data.details.level);
|
||||
if ( next > priorLevel ) {
|
||||
itemData.levels = next;
|
||||
await cls.update({"data.levels": next});
|
||||
addLevel = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the actor already has the class, increment the level instead of creating a new item
|
||||
// then add new features as long as level increases
|
||||
if ( classWasAlreadyPresent ) {
|
||||
const lvl = cls.data.data.levels;
|
||||
const newLvl = Math.min(lvl + 1, 20 + lvl - this.actor.data.data.details.level);
|
||||
if ( !(lvl === newLvl) ) {
|
||||
cls.update({"data.levels": newLvl});
|
||||
itemData.data.levels = newLvl;
|
||||
Actor5e.getClassFeatures(itemData).then(features => {
|
||||
this.actor.createEmbeddedEntity("OwnedItem", features);
|
||||
});
|
||||
}
|
||||
return
|
||||
// Add class features
|
||||
if ( !hasClass || addLevel ) {
|
||||
const features = await Actor5e.getClassFeatures({
|
||||
className: itemData.name,
|
||||
subclassName: itemData.data.subclass,
|
||||
level: itemData.levels,
|
||||
priorLevel: priorLevel
|
||||
});
|
||||
await this.actor.createEmbeddedEntity("OwnedItem", features);
|
||||
}
|
||||
}
|
||||
|
||||
super._onDropItemCreate(itemData);
|
||||
// Default drop handling if levels were not added
|
||||
if ( !addLevel ) super._onDropItemCreate(itemData);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ export default class ActorSheet5eNPC 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));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -116,7 +116,7 @@ export default class ActorSheet5eNPC 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;
|
||||
|
|
|
@ -49,12 +49,9 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
|||
totalWeight /= CONFIG.SW5E.encumbrance.vehicleWeightMultiplier;
|
||||
|
||||
// Compute overall encumbrance
|
||||
const enc = {
|
||||
max: actorData.data.attributes.capacity.cargo,
|
||||
value: Math.round(totalWeight * 10) / 10
|
||||
};
|
||||
enc.pct = Math.min(enc.value * 100 / enc.max, 99);
|
||||
return enc;
|
||||
const max = actorData.data.attributes.capacity.cargo;
|
||||
const pct = Math.clamped((totalWeight * 100) / max, 0, 100);
|
||||
return {value: totalWeight.toNearest(0.1), max, pct};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -89,6 +86,13 @@ export default class ActorSheet5eVehicle extends ActorSheet5e {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
_getMovementSpeed(actorData) {
|
||||
return {primary: "", special: ""};
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Organize Owned Items for rendering the Vehicle sheet.
|
||||
* @private
|
||||
|
|
|
@ -70,7 +70,7 @@ export default class AbilityUseDialog extends Dialog {
|
|||
dlg.render(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Helpers */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* An application class which provides advanced configuration for special character flags which modify an Actor
|
||||
* @extends {BaseEntitySheet}
|
||||
* @implements {BaseEntitySheet}
|
||||
*/
|
||||
export default class ActorSheetFlags extends BaseEntitySheet {
|
||||
static get defaultOptions() {
|
||||
static get defaultOptions() {
|
||||
const options = super.defaultOptions;
|
||||
return mergeObject(options, {
|
||||
id: "actor-flags",
|
||||
|
@ -16,22 +16,16 @@ export default class ActorSheetFlags extends BaseEntitySheet {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Configure the title of the special traits selection window to include the Actor name
|
||||
* @type {String}
|
||||
*/
|
||||
/** @override */
|
||||
get title() {
|
||||
return `${game.i18n.localize('SW5E.FlagsTitle')}: ${this.object.name}`;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Prepare data used to render the special Actor traits selection UI
|
||||
* @return {Object}
|
||||
*/
|
||||
/** @override */
|
||||
getData() {
|
||||
const data = super.getData();
|
||||
const data = {};
|
||||
data.actor = this.object;
|
||||
data.flags = this._getFlags();
|
||||
data.bonuses = this._getBonuses();
|
||||
|
@ -43,17 +37,18 @@ export default class ActorSheetFlags extends BaseEntitySheet {
|
|||
/**
|
||||
* Prepare an object of flags data which groups flags by section
|
||||
* Add some additional data for rendering
|
||||
* @return {Object}
|
||||
* @return {object}
|
||||
*/
|
||||
_getFlags() {
|
||||
const flags = {};
|
||||
const baseData = this.entity._data;
|
||||
for ( let [k, v] of Object.entries(CONFIG.SW5E.characterFlags) ) {
|
||||
if ( !flags.hasOwnProperty(v.section) ) flags[v.section] = {};
|
||||
let flag = duplicate(v);
|
||||
flag.type = v.type.name;
|
||||
flag.isCheckbox = v.type === Boolean;
|
||||
flag.isSelect = v.hasOwnProperty('choices');
|
||||
flag.value = this.entity.getFlag("sw5e", k);
|
||||
flag.value = getProperty(baseData.flags, `sw5e.${k}`);
|
||||
flags[v.section][`flags.sw5e.${k}`] = flag;
|
||||
}
|
||||
return flags;
|
||||
|
@ -63,7 +58,7 @@ export default class ActorSheetFlags extends BaseEntitySheet {
|
|||
|
||||
/**
|
||||
* Get the bonuses fields and their localization strings
|
||||
* @return {Array}
|
||||
* @return {Array<object>}
|
||||
* @private
|
||||
*/
|
||||
_getBonuses() {
|
||||
|
@ -82,17 +77,14 @@ export default class ActorSheetFlags extends BaseEntitySheet {
|
|||
{name: "data.bonuses.power.dc", label: "SW5E.BonusPowerDC"}
|
||||
];
|
||||
for ( let b of bonuses ) {
|
||||
b.value = getProperty(this.object.data, b.name) || "";
|
||||
b.value = getProperty(this.object._data, b.name) || "";
|
||||
}
|
||||
return bonuses;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Update the Actor using the configured flags
|
||||
* Remove/unset any flags which are no longer configured
|
||||
*/
|
||||
/** @override */
|
||||
async _updateObject(event, formData) {
|
||||
const actor = this.object;
|
||||
let updateData = expandObject(formData);
|
||||
|
@ -100,10 +92,12 @@ export default class ActorSheetFlags extends BaseEntitySheet {
|
|||
// Unset any flags which are "false"
|
||||
let unset = false;
|
||||
const flags = updateData.flags.sw5e;
|
||||
//clone flags to dnd5e for module compatability
|
||||
updateData.flags.dnd5e = updateData.flags.sw5e
|
||||
for ( let [k, v] of Object.entries(flags) ) {
|
||||
if ( [undefined, null, "", false, 0].includes(v) ) {
|
||||
delete flags[k];
|
||||
if ( hasProperty(actor.data.flags, `sw5e.${k}`) ) {
|
||||
if ( hasProperty(actor._data.flags, `sw5e.${k}`) ) {
|
||||
unset = true;
|
||||
flags[`-=${k}`] = null;
|
||||
}
|
||||
|
@ -118,10 +112,6 @@ export default class ActorSheetFlags extends BaseEntitySheet {
|
|||
}
|
||||
|
||||
// Diff the data against any applied overrides and apply
|
||||
// TODO: Remove this logical gate once 0.7.x is release channel
|
||||
if ( !isNewerVersion("0.7.1", game.data.version) ){
|
||||
updateData = diffObject(this.object.data, updateData);
|
||||
}
|
||||
await actor.update(updateData, {diff: false});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
/**
|
||||
* A specialized Dialog subclass for casting a cast item at a certain level
|
||||
* @type {Dialog}
|
||||
*/
|
||||
export class CastDialog extends Dialog {
|
||||
constructor(actor, item, dialogData={}, options={}) {
|
||||
super(dialogData, options);
|
||||
this.options.classes = ["sw5e", "dialog"];
|
||||
|
||||
/**
|
||||
* Store a reference to the Actor entity which is casting the cast
|
||||
* @type {Actor5e}
|
||||
*/
|
||||
this.actor = actor;
|
||||
|
||||
/**
|
||||
* Store a reference to the Item entity which is the cast being cast
|
||||
* @type {Item5e}
|
||||
*/
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Rendering */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* A constructor function which displays the Cast Cast Dialog app for a given Actor and Item.
|
||||
* Returns a Promise which resolves to the dialog FormData once the workflow has been completed.
|
||||
* @param {Actor5e} actor
|
||||
* @param {Item5e} item
|
||||
* @return {Promise}
|
||||
*/
|
||||
static async create(actor, item) {
|
||||
const ad = actor.data.data;
|
||||
const id = item.data.data;
|
||||
|
||||
// Determine whether the cast may be upcast
|
||||
const lvl = id.level;
|
||||
const canUpcast = (lvl > 0) && CONFIG.SW5E.castUpcastModes.includes(id.preparation.mode);
|
||||
|
||||
// Determine the levels which are feasible
|
||||
let lmax = 0;
|
||||
const castLevels = Array.fromRange(10).reduce((arr, i) => {
|
||||
if ( i < lvl ) return arr;
|
||||
const l = ad.casts["cast"+i] || {max: 0, override: null};
|
||||
let max = parseInt(l.override || l.max || 0);
|
||||
let slots = Math.clamped(parseInt(l.value || 0), 0, max);
|
||||
if ( max > 0 ) lmax = i;
|
||||
arr.push({
|
||||
level: i,
|
||||
label: i > 0 ? `${CONFIG.SW5E.castLevels[i]} (${slots} Slots)` : CONFIG.SW5E.castLevels[i],
|
||||
canCast: canUpcast && (max > 0),
|
||||
hasSlots: slots > 0
|
||||
});
|
||||
return arr;
|
||||
}, []).filter(sl => sl.level <= lmax);
|
||||
|
||||
const pact = ad.casts.pact;
|
||||
if (pact.level >= lvl) {
|
||||
// If this character has pact slots, present them as an option for
|
||||
// casting the cast.
|
||||
castLevels.push({
|
||||
level: 'pact',
|
||||
label: game.i18n.localize('SW5E.CastLevelPact')
|
||||
+ ` (${game.i18n.localize('SW5E.Level')} ${pact.level}) `
|
||||
+ `(${pact.value} ${game.i18n.localize('SW5E.Slots')})`,
|
||||
canCast: canUpcast,
|
||||
hasSlots: pact.value > 0
|
||||
});
|
||||
}
|
||||
|
||||
const canCast = castLevels.some(l => l.hasSlots);
|
||||
|
||||
// Render the Cast casting template
|
||||
const html = await renderTemplate("systems/sw5e/templates/apps/cast-cast.html", {
|
||||
item: item.data,
|
||||
canCast: canCast,
|
||||
canUpcast: canUpcast,
|
||||
castLevels,
|
||||
hasPlaceableTemplate: game.user.can("TEMPLATE_CREATE") && item.hasAreaTarget
|
||||
});
|
||||
|
||||
// Create the Dialog and return as a Promise
|
||||
return new Promise((resolve, reject) => {
|
||||
const dlg = new this(actor, item, {
|
||||
title: `${item.name}: Cast Configuration`,
|
||||
content: html,
|
||||
buttons: {
|
||||
cast: {
|
||||
icon: '<i class="fas fa-magic"></i>',
|
||||
label: "Cast",
|
||||
callback: html => resolve(new FormData(html[0].querySelector("#cast-config-form")))
|
||||
}
|
||||
},
|
||||
default: "cast",
|
||||
close: reject
|
||||
});
|
||||
dlg.render(true);
|
||||
});
|
||||
}
|
||||
}
|
32
module/apps/movement-config.js
Normal file
32
module/apps/movement-config.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* A simple form to set actor movement speeds
|
||||
* @implements {BaseEntitySheet}
|
||||
*/
|
||||
export default class MovementConfig extends BaseEntitySheet {
|
||||
|
||||
/** @override */
|
||||
static get defaultOptions() {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
title: "SW5E.MovementConfig",
|
||||
classes: ["sw5e"],
|
||||
template: "systems/sw5e/templates/apps/movement-config.html",
|
||||
width: 240,
|
||||
height: "auto"
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
getData(options) {
|
||||
const data = {
|
||||
movement: duplicate(this.entity._data.data.attributes.movement),
|
||||
units: CONFIG.SW5E.movementUnits
|
||||
}
|
||||
for ( let [k, v] of Object.entries(data.movement) ) {
|
||||
if ( ["units", "hover"].includes(k) ) continue;
|
||||
data.movement[k] = Number.isNumeric(v) ? v.toNearest(0.1) : 0;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ export default class ShortRestDialog extends Dialog {
|
|||
/** @override */
|
||||
getData() {
|
||||
const data = super.getData();
|
||||
|
||||
|
||||
// Determine Hit Dice
|
||||
data.availableHD = this.actor.data.items.reduce((hd, item) => {
|
||||
if ( item.type === "class" ) {
|
||||
|
@ -49,7 +49,7 @@ export default class ShortRestDialog extends Dialog {
|
|||
}, {});
|
||||
data.canRoll = this.actor.data.data.attributes.hd > 0;
|
||||
data.denomination = this._denom;
|
||||
|
||||
|
||||
// Determine rest type
|
||||
const variant = game.settings.get("sw5e", "restVariant");
|
||||
data.promptNewDay = variant !== "epic"; // It's never a new day when only resting 1 minute
|
||||
|
|
|
@ -11,14 +11,16 @@ export const highlightCriticalSuccessFailure = function(message, html, data) {
|
|||
const d = roll.dice[0];
|
||||
|
||||
// Ensure it is an un-modified d20 roll
|
||||
const isD20 = (d.faces === 20) && ( d.results.length === 1 );
|
||||
const isD20 = (d.faces === 20) && ( d.values.length === 1 );
|
||||
if ( !isD20 ) return;
|
||||
const isModifiedRoll = ("success" in d.results[0]) || d.options.marginSuccess || d.options.marginFailure;
|
||||
if ( isModifiedRoll ) return;
|
||||
|
||||
// Highlight successes and failures
|
||||
if ( d.options.critical && (d.total >= d.options.critical) ) html.find(".dice-total").addClass("critical");
|
||||
else if ( d.options.fumble && (d.total <= d.options.fumble) ) html.find(".dice-total").addClass("fumble");
|
||||
const critical = d.options.critical || 20;
|
||||
const fumble = d.options.fumble || 1;
|
||||
if ( d.total >= critical ) html.find(".dice-total").addClass("critical");
|
||||
else if ( d.total <= fumble ) html.find(".dice-total").addClass("fumble");
|
||||
else if ( d.options.target ) {
|
||||
if ( roll.total >= d.options.target ) html.find(".dice-total").addClass("success");
|
||||
else html.find(".dice-total").addClass("failure");
|
||||
|
@ -33,7 +35,8 @@ export const highlightCriticalSuccessFailure = function(message, html, data) {
|
|||
export const displayChatActionButtons = function(message, html, data) {
|
||||
const chatCard = html.find(".sw5e.chat-card");
|
||||
if ( chatCard.length > 0 ) {
|
||||
html.find(".flavor-text").remove();
|
||||
const flavor = html.find(".flavor-text");
|
||||
if ( flavor.text() === html.find(".item-name").text() ) flavor.remove();
|
||||
|
||||
// If the user is the message author or the actor owner, proceed
|
||||
let actor = game.actors.get(data.message.speaker.actor);
|
||||
|
|
|
@ -27,41 +27,14 @@ export const _getInitiativeFormula = function(combatant) {
|
|||
return parts.filter(p => p !== null).join(" + ");
|
||||
};
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* TODO: A temporary shim until 0.7.x becomes stable
|
||||
* @override
|
||||
* When the Combat encounter updates - re-render open Actor sheets for combatants in the encounter.
|
||||
*/
|
||||
TokenConfig.getTrackedAttributes = function(data, _path=[]) {
|
||||
|
||||
// Track the path and record found attributes
|
||||
const attributes = {
|
||||
"bar": [],
|
||||
"value": []
|
||||
};
|
||||
|
||||
// Recursively explore the object
|
||||
for ( let [k, v] of Object.entries(data) ) {
|
||||
let p = _path.concat([k]);
|
||||
|
||||
// Check objects for both a "value" and a "max"
|
||||
if ( v instanceof Object ) {
|
||||
const isBar = ("value" in v) && ("max" in v);
|
||||
if ( isBar ) attributes.bar.push(p);
|
||||
else {
|
||||
const inner = this.getTrackedAttributes(data[k], p);
|
||||
attributes.bar.push(...inner.bar);
|
||||
attributes.value.push(...inner.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise identify values which are numeric or null
|
||||
else if ( Number.isNumeric(v) || (v === null) ) {
|
||||
attributes.value.push(p);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return attributes;
|
||||
};
|
||||
});
|
||||
|
|
|
@ -325,17 +325,31 @@ 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.movementUnits = {
|
||||
"ft": "SW5E.DistFt",
|
||||
"mi": "SW5E.DistMi"
|
||||
}
|
||||
|
||||
/**
|
||||
* The valid units of measure for the range of an action or effect.
|
||||
* This object automatically includes the movement units from SW5E.movementUnits
|
||||
* @type {Object<string,string>}
|
||||
*/
|
||||
SW5E.distanceUnits = {
|
||||
"none": "SW5E.None",
|
||||
"self": "SW5E.DistSelf",
|
||||
"touch": "SW5E.DistTouch",
|
||||
"ft": "SW5E.DistFt",
|
||||
"mi": "SW5E.DistMi",
|
||||
"spec": "SW5E.Special",
|
||||
"any": "SW5E.DistAny"
|
||||
};
|
||||
for ( let [k, v] of Object.entries(SW5E.movementUnits) ) {
|
||||
SW5E.distanceUnits[k] = v;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
@ -413,7 +427,7 @@ SW5E.healingTypes = {
|
|||
* Enumerate the denominations of hit dice which can apply to classes in the SW5E system
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
SW5E.hitDieTypes = ["d4", "d6", "d8", "d10", "d12"];
|
||||
SW5E.hitDieTypes = ["d4", "d6", "d8", "d10", "d12", "d20"];
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -461,15 +475,14 @@ SW5E.skills = {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
SW5E.powerPreparationModes = {
|
||||
"prepared": "SW5E.PowerPrepPrepared",
|
||||
"always": "SW5E.PowerPrepAlways",
|
||||
"atwill": "SW5E.PowerPrepAtWill",
|
||||
"innate": "SW5E.PowerPrepInnate",
|
||||
"prepared": "SW5E.PowerPrepPrepared"
|
||||
"innate": "SW5E.PowerPrepInnate"
|
||||
};
|
||||
|
||||
SW5E.powerUpcastModes = ["always", "pact", "prepared"];
|
||||
|
||||
|
||||
SW5E.powerProgression = {
|
||||
"none": "SW5E.PowerNone",
|
||||
"full": "SW5E.PowerProgFull",
|
||||
|
@ -901,15 +914,27 @@ SW5E.characterFlags = {
|
|||
type: Boolean
|
||||
},
|
||||
"weaponCriticalThreshold": {
|
||||
name: "SW5E.FlagsCritThreshold",
|
||||
hint: "SW5E.FlagsCritThresholdHint",
|
||||
name: "SW5E.FlagsWeaponCritThreshold",
|
||||
hint: "SW5E.FlagsWeaponCritThresholdHint",
|
||||
section: "Feats",
|
||||
type: Number,
|
||||
placeholder: 20
|
||||
},
|
||||
"powerCriticalThreshold": {
|
||||
name: "SW5E.FlagsPowerCritThreshold",
|
||||
hint: "SW5E.FlagsPowerCritThresholdHint",
|
||||
section: "Feats",
|
||||
type: Number,
|
||||
placeholder: 20
|
||||
},
|
||||
"meleeCriticalDamageDice": {
|
||||
name: "SW5E.FlagsMeleeCriticalDice",
|
||||
hint: "SW5E.FlagsMeleeCriticalDiceHint",
|
||||
section: "Feats",
|
||||
type: Number,
|
||||
placeholder: 0
|
||||
}
|
||||
};
|
||||
|
||||
// Configure allowed status flags
|
||||
SW5E.allowedActorFlags = [
|
||||
"isPolymorphed", "originalActor"
|
||||
].concat(Object.keys(SW5E.characterFlags));
|
||||
SW5E.allowedActorFlags = ["isPolymorphed", "originalActor"].concat(Object.keys(SW5E.characterFlags));
|
||||
|
|
273
module/dice.js
273
module/dice.js
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* A standardized helper function for managing core 5e "d20 rolls"
|
||||
*
|
||||
* Holding SHIFT, ALT, or CTRL when the attack is rolled will "fast-forward".
|
||||
* This chooses the default options of a normal attack with no bonus, Advantage, or Disadvantage respectively
|
||||
*
|
||||
/**
|
||||
* A standardized helper function for managing core 5e "d20 rolls"
|
||||
*
|
||||
* Holding SHIFT, ALT, or CTRL when the attack is rolled will "fast-forward".
|
||||
* This chooses the default options of a normal attack with no bonus, Advantage, or Disadvantage respectively
|
||||
*
|
||||
* @param {Array} parts The dice roll component parts, excluding the initial d20
|
||||
* @param {Object} data Actor or item data against which to parse the roll
|
||||
* @param {Event|object} event The triggering event which initiated the roll
|
||||
|
@ -27,72 +27,72 @@
|
|||
* @param {object} messageData Additional data which is applied to the created Chat Message, if any
|
||||
*
|
||||
* @return {Promise} A Promise which resolves once the roll workflow has completed
|
||||
*/
|
||||
export async function d20Roll({parts=[], data={}, event={}, rollMode=null, template=null, title=null, speaker=null,
|
||||
flavor=null, fastForward=null, dialogOptions,
|
||||
advantage=null, disadvantage=null, critical=20, fumble=1, targetValue=null,
|
||||
elvenAccuracy=false, halflingLucky=false, reliableTalent=false,
|
||||
chatMessage=true, messageData={}}={}) {
|
||||
|
||||
// Prepare Message Data
|
||||
messageData.flavor = flavor || title;
|
||||
messageData.speaker = speaker || ChatMessage.getSpeaker();
|
||||
const messageOptions = {rollMode: rollMode || game.settings.get("core", "rollMode")};
|
||||
parts = parts.concat(["@bonus"]);
|
||||
|
||||
// Handle fast-forward events
|
||||
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;
|
||||
*/
|
||||
export async function d20Roll({parts=[], data={}, event={}, rollMode=null, template=null, title=null, speaker=null,
|
||||
flavor=null, fastForward=null, dialogOptions,
|
||||
advantage=null, disadvantage=null, critical=20, fumble=1, targetValue=null,
|
||||
elvenAccuracy=false, halflingLucky=false, reliableTalent=false,
|
||||
chatMessage=true, messageData={}}={}) {
|
||||
|
||||
// Prepare Message Data
|
||||
messageData.flavor = flavor || title;
|
||||
messageData.speaker = speaker || ChatMessage.getSpeaker();
|
||||
const messageOptions = {rollMode: rollMode || game.settings.get("core", "rollMode")};
|
||||
parts = parts.concat(["@bonus"]);
|
||||
|
||||
// Handle fast-forward events
|
||||
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;
|
||||
}
|
||||
|
||||
// Define the inner roll function
|
||||
const _roll = (parts, adv, form) => {
|
||||
|
||||
// Determine the d20 roll and modifiers
|
||||
let nd = 1;
|
||||
let mods = halflingLucky ? "r=1" : "";
|
||||
|
||||
// Handle advantage
|
||||
if (adv === 1) {
|
||||
nd = elvenAccuracy ? 3 : 2;
|
||||
messageData.flavor += ` (${game.i18n.localize("SW5E.Advantage")})`;
|
||||
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].advantage = true;
|
||||
mods += "kh";
|
||||
}
|
||||
|
||||
// Define the inner roll function
|
||||
const _roll = (parts, adv, form) => {
|
||||
|
||||
// Determine the d20 roll and modifiers
|
||||
let nd = 1;
|
||||
let mods = halflingLucky ? "r=1" : "";
|
||||
|
||||
// Handle advantage
|
||||
if (adv === 1) {
|
||||
nd = elvenAccuracy ? 3 : 2;
|
||||
messageData.flavor += ` (${game.i18n.localize("SW5E.Advantage")})`;
|
||||
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].advantage = true;
|
||||
mods += "kh";
|
||||
}
|
||||
|
||||
// Handle disadvantage
|
||||
else if (adv === -1) {
|
||||
nd = 2;
|
||||
messageData.flavor += ` (${game.i18n.localize("SW5E.Disadvantage")})`;
|
||||
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].disadvantage = true;
|
||||
mods += "kl";
|
||||
}
|
||||
// Handle disadvantage
|
||||
else if (adv === -1) {
|
||||
nd = 2;
|
||||
messageData.flavor += ` (${game.i18n.localize("SW5E.Disadvantage")})`;
|
||||
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].disadvantage = true;
|
||||
mods += "kl";
|
||||
}
|
||||
|
||||
// Prepend the d20 roll
|
||||
let formula = `${nd}d20${mods}`;
|
||||
if (reliableTalent) formula = `{${nd}d20${mods},10}kh`;
|
||||
parts.unshift(formula);
|
||||
|
||||
// Optionally include a situational bonus
|
||||
if ( form ) {
|
||||
data['bonus'] = form.bonus.value;
|
||||
messageOptions.rollMode = form.rollMode.value;
|
||||
}
|
||||
if (!data["bonus"]) parts.pop();
|
||||
|
||||
// Optionally include an ability score selection (used for tool checks)
|
||||
const ability = form ? form.ability : null;
|
||||
if (ability && ability.value) {
|
||||
data.ability = ability.value;
|
||||
const abl = data.abilities[data.ability];
|
||||
if (abl) {
|
||||
data.mod = abl.mod;
|
||||
messageData.flavor += ` (${CONFIG.SW5E.abilities[data.ability]})`;
|
||||
}
|
||||
// Optionally include a situational bonus
|
||||
if ( form ) {
|
||||
data['bonus'] = form.bonus.value;
|
||||
messageOptions.rollMode = form.rollMode.value;
|
||||
}
|
||||
if (!data["bonus"]) parts.pop();
|
||||
|
||||
// Optionally include an ability score selection (used for tool checks)
|
||||
const ability = form ? form.ability : null;
|
||||
if (ability && ability.value) {
|
||||
data.ability = ability.value;
|
||||
const abl = data.abilities[data.ability];
|
||||
if (abl) {
|
||||
data.mod = abl.mod;
|
||||
messageData.flavor += ` (${CONFIG.SW5E.abilities[data.ability]})`;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the roll
|
||||
let roll = new Roll(parts.join(" + "), data);
|
||||
|
@ -139,73 +139,76 @@
|
|||
*/
|
||||
async function _d20RollDialog({template, title, parts, data, rollMode, dialogOptions, roll}={}) {
|
||||
|
||||
// Render modal dialog
|
||||
template = template || "systems/sw5e/templates/chat/roll-dialog.html";
|
||||
let dialogData = {
|
||||
formula: parts.join(" + "),
|
||||
data: data,
|
||||
rollMode: rollMode,
|
||||
rollModes: CONFIG.Dice.rollModes,
|
||||
config: CONFIG.SW5E
|
||||
};
|
||||
const html = await renderTemplate(template, dialogData);
|
||||
// Render modal dialog
|
||||
template = template || "systems/sw5e/templates/chat/roll-dialog.html";
|
||||
let dialogData = {
|
||||
formula: parts.join(" + "),
|
||||
data: data,
|
||||
rollMode: rollMode,
|
||||
rollModes: CONFIG.Dice.rollModes,
|
||||
config: CONFIG.SW5E
|
||||
};
|
||||
const html = await renderTemplate(template, dialogData);
|
||||
|
||||
// Create the Dialog window
|
||||
return new Promise(resolve => {
|
||||
new Dialog({
|
||||
title: title,
|
||||
content: html,
|
||||
buttons: {
|
||||
advantage: {
|
||||
label: game.i18n.localize("SW5E.Advantage"),
|
||||
callback: html => resolve(roll(parts, 1, html[0].querySelector("form")))
|
||||
},
|
||||
normal: {
|
||||
label: game.i18n.localize("SW5E.Normal"),
|
||||
callback: html => resolve(roll(parts, 0, html[0].querySelector("form")))
|
||||
},
|
||||
disadvantage: {
|
||||
label: game.i18n.localize("SW5E.Disadvantage"),
|
||||
callback: html => resolve(roll(parts, -1, html[0].querySelector("form")))
|
||||
}
|
||||
// Create the Dialog window
|
||||
return new Promise(resolve => {
|
||||
new Dialog({
|
||||
title: title,
|
||||
content: html,
|
||||
buttons: {
|
||||
advantage: {
|
||||
label: game.i18n.localize("SW5E.Advantage"),
|
||||
callback: html => resolve(roll(parts, 1, html[0].querySelector("form")))
|
||||
},
|
||||
default: "normal",
|
||||
close: () => resolve(null)
|
||||
}, dialogOptions).render(true);
|
||||
});
|
||||
}
|
||||
normal: {
|
||||
label: game.i18n.localize("SW5E.Normal"),
|
||||
callback: html => resolve(roll(parts, 0, html[0].querySelector("form")))
|
||||
},
|
||||
disadvantage: {
|
||||
label: game.i18n.localize("SW5E.Disadvantage"),
|
||||
callback: html => resolve(roll(parts, -1, html[0].querySelector("form")))
|
||||
}
|
||||
},
|
||||
default: "normal",
|
||||
close: () => resolve(null)
|
||||
}, dialogOptions).render(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* A standardized helper function for managing core 5e "d20 rolls"
|
||||
*
|
||||
* Holding SHIFT, ALT, or CTRL when the attack is rolled will "fast-forward".
|
||||
* This chooses the default options of a normal attack with no bonus, Critical, or no bonus respectively
|
||||
*
|
||||
* @param {Array} parts The dice roll component parts, excluding the initial d20
|
||||
* @param {Actor} actor The Actor making the damage roll
|
||||
* @param {Object} data Actor or item data against which to parse the roll
|
||||
* @param {Event|object}[event The triggering event which initiated the roll
|
||||
* @param {string} rollMode A specific roll mode to apply as the default for the resulting roll
|
||||
* @param {String} template The HTML template used to render the roll dialog
|
||||
* @param {String} title The dice roll UI window title
|
||||
* @param {Object} speaker The ChatMessage speaker to pass when creating the chat
|
||||
* @param {string} flavor Flavor text to use in the posted chat message
|
||||
* @param {boolean} allowCritical Allow the opportunity for a critical hit to be rolled
|
||||
* @param {Boolean} critical Flag this roll as a critical hit for the purposes of fast-forward rolls
|
||||
* @param {number} criticalBonusDice A number of bonus damage dice that are added for critical hits
|
||||
* @param {number} criticalMultiplier A critical hit multiplier which is applied to critical hits
|
||||
* @param {Boolean} fastForward Allow fast-forward advantage selection
|
||||
* @param {Function} onClose Callback for actions to take when the dialog form is closed
|
||||
* @param {Object} dialogOptions Modal dialog options
|
||||
* @param {boolean} chatMessage Automatically create a Chat Message for the result of this roll
|
||||
* @param {object} messageData Additional data which is applied to the created Chat Message, if any
|
||||
*
|
||||
* @return {Promise} A Promise which resolves once the roll workflow has completed
|
||||
*/
|
||||
export async function damageRoll({parts, actor, data, event={}, rollMode=null, template, title, speaker, flavor,
|
||||
allowCritical=true, critical=false, criticalBonusDice=0, criticalMultiplier=2, fastForward=null,
|
||||
dialogOptions={}, chatMessage=true, messageData={}}={}) {
|
||||
|
||||
/**
|
||||
* A standardized helper function for managing core 5e "d20 rolls"
|
||||
*
|
||||
* Holding SHIFT, ALT, or CTRL when the attack is rolled will "fast-forward".
|
||||
* This chooses the default options of a normal attack with no bonus, Critical, or no bonus respectively
|
||||
*
|
||||
* @param {Array} parts The dice roll component parts, excluding the initial d20
|
||||
* @param {Actor} actor The Actor making the damage roll
|
||||
* @param {Object} data Actor or item data against which to parse the roll
|
||||
* @param {Event|object}[event The triggering event which initiated the roll
|
||||
* @param {string} rollMode A specific roll mode to apply as the default for the resulting roll
|
||||
* @param {String} template The HTML template used to render the roll dialog
|
||||
* @param {String} title The dice roll UI window title
|
||||
* @param {Object} speaker The ChatMessage speaker to pass when creating the chat
|
||||
* @param {string} flavor Flavor text to use in the posted chat message
|
||||
* @param {boolean} allowCritical Allow the opportunity for a critical hit to be rolled
|
||||
* @param {Boolean} critical Flag this roll as a critical hit for the purposes of fast-forward rolls
|
||||
* @param {Boolean} fastForward Allow fast-forward advantage selection
|
||||
* @param {Function} onClose Callback for actions to take when the dialog form is closed
|
||||
* @param {Object} dialogOptions Modal dialog options
|
||||
* @param {boolean} chatMessage Automatically create a Chat Message for the result of this roll
|
||||
* @param {object} messageData Additional data which is applied to the created Chat Message, if any
|
||||
*
|
||||
* @return {Promise} A Promise which resolves once the roll workflow has completed
|
||||
*/
|
||||
export async function damageRoll({parts, actor, data, event={}, rollMode=null, template, title, speaker, flavor,
|
||||
allowCritical=true, critical=false, fastForward=null, dialogOptions, chatMessage=true, messageData={}}={}) {
|
||||
|
||||
// Prepare Message Data
|
||||
messageData.flavor = flavor || title;
|
||||
messageData.speaker = speaker || ChatMessage.getSpeaker();
|
||||
|
@ -213,8 +216,8 @@ async function _d20RollDialog({template, title, parts, data, rollMode, dialogOpt
|
|||
parts = parts.concat(["@bonus"]);
|
||||
fastForward = fastForward ?? (event && (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey));
|
||||
|
||||
// Define inner roll function
|
||||
const _roll = function(parts, crit, form) {
|
||||
// Define inner roll function
|
||||
const _roll = function(parts, crit, form) {
|
||||
|
||||
// Optionally include a situational bonus
|
||||
if ( form ) {
|
||||
|
@ -224,17 +227,17 @@ async function _d20RollDialog({template, title, parts, data, rollMode, dialogOpt
|
|||
if (!data["bonus"]) parts.pop();
|
||||
|
||||
// Create the damage roll
|
||||
let roll = new Roll(parts.join("+"), data);
|
||||
let roll = new Roll(parts.join("+"), data);
|
||||
|
||||
// Modify the damage formula for critical hits
|
||||
if ( crit === true ) {
|
||||
let add = (actor && actor.getFlag("sw5e", "savageAttacks")) ? 1 : 0;
|
||||
let mult = 2;
|
||||
// TODO Backwards compatibility - REMOVE LATER
|
||||
if (isNewerVersion(game.data.version, "0.6.9")) roll.alter(mult, add);
|
||||
else roll.alter(add, mult);
|
||||
messageData.flavor += ` (${game.i18n.localize("SW5E.Critical")})`;
|
||||
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].critical = true;
|
||||
roll.alter(criticalMultiplier, 0); // Multiply all dice
|
||||
if ( roll.terms[0] instanceof Die ) { // Add bonus dice for only the main dice term
|
||||
roll.terms[0].alter(1, criticalBonusDice);
|
||||
roll._formula = roll.formula;
|
||||
}
|
||||
messageData.flavor += ` (${game.i18n.localize("SW5E.Critical")})`;
|
||||
if ( "flags.sw5e.roll" in messageData ) messageData["flags.sw5e.roll"].critical = true;
|
||||
}
|
||||
|
||||
// Execute the roll
|
||||
|
|
63
module/effects.js
vendored
Normal file
63
module/effects.js
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* Manage Active Effect instances through the Actor Sheet via effect control buttons.
|
||||
* @param {MouseEvent} event The left-click event on the effect control
|
||||
* @param {Actor|Item} owner The owning entity which manages this effect
|
||||
*/
|
||||
export function onManageActiveEffect(event, owner) {
|
||||
event.preventDefault();
|
||||
const a = event.currentTarget;
|
||||
const li = a.closest("li");
|
||||
const effect = li.dataset.effectId ? owner.effects.get(li.dataset.effectId) : null;
|
||||
switch ( a.dataset.action ) {
|
||||
case "create":
|
||||
return ActiveEffect.create({
|
||||
label: "New Effect",
|
||||
icon: "icons/svg/aura.svg",
|
||||
origin: owner.uuid,
|
||||
"duration.rounds": li.dataset.effectType === "temporary" ? 1 : undefined,
|
||||
disabled: li.dataset.effectType === "inactive"
|
||||
}, owner).create();
|
||||
case "edit":
|
||||
return effect.sheet.render(true);
|
||||
case "delete":
|
||||
return effect.delete();
|
||||
case "toggle":
|
||||
return effect.update({disabled: !effect.data.disabled});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data structure for Active Effects which are currently applied to an Actor or Item.
|
||||
* @param {ActiveEffect[]} effects The array of Active Effect instances to prepare sheet data for
|
||||
* @return {object} Data for rendering
|
||||
*/
|
||||
export function prepareActiveEffectCategories(effects) {
|
||||
|
||||
// Define effect header categories
|
||||
const categories = {
|
||||
temporary: {
|
||||
type: "temporary",
|
||||
label: "Temporary Effects",
|
||||
effects: []
|
||||
},
|
||||
passive: {
|
||||
type: "passive",
|
||||
label: "Passive Effects",
|
||||
effects: []
|
||||
},
|
||||
inactive: {
|
||||
type: "inactive",
|
||||
label: "Inactive Effects",
|
||||
effects: []
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate over active effects, classifying them into categories
|
||||
for ( let e of effects ) {
|
||||
e._getSourceName(); // Trigger a lookup for the source name
|
||||
if ( e.data.disabled ) categories.inactive.effects.push(e);
|
||||
else if ( e.isTemporary ) categories.temporary.effects.push(e);
|
||||
else categories.passive.effects.push(e);
|
||||
}
|
||||
return categories;
|
||||
}
|
|
@ -161,6 +161,7 @@ export default class Item5e extends Item {
|
|||
|
||||
// Power Level, School, and Components
|
||||
if ( itemData.type === "power" ) {
|
||||
data.preparation.mode = data.preparation.mode || "prepared";
|
||||
labels.level = C.powerLevels[data.level];
|
||||
labels.school = C.powerSchools[data.school];
|
||||
labels.components = Object.entries(data.components).reduce((arr, c) => {
|
||||
|
@ -180,33 +181,33 @@ export default class Item5e extends Item {
|
|||
else labels.featType = game.i18n.localize("SW5E.Passive");
|
||||
}
|
||||
|
||||
// Species Items
|
||||
else if ( itemData.type === "species" ) {
|
||||
// labels.species = C.species[data.species];
|
||||
}
|
||||
// Archetype Items
|
||||
else if ( itemData.type === "archetype" ) {
|
||||
// Species Items
|
||||
else if ( itemData.type === "species" ) {
|
||||
// labels.species = C.species[data.species];
|
||||
}
|
||||
// Archetype Items
|
||||
else if ( itemData.type === "archetype" ) {
|
||||
// labels.archetype = C.archetype[data.archetype];
|
||||
}
|
||||
// Background Items
|
||||
else if ( itemData.type === "background" ) {
|
||||
|
||||
}
|
||||
// Class Feature Items
|
||||
// Background Items
|
||||
else if ( itemData.type === "background" ) {
|
||||
// labels.background = C.background[data.background];
|
||||
}
|
||||
// Class Feature Items
|
||||
else if ( itemData.type === "classfeature" ) {
|
||||
|
||||
// labels.classFeature = C.classFeature[data.classFeature];
|
||||
}
|
||||
// Fighting Style Items
|
||||
else if ( itemData.type === "fightingstyle" ) {
|
||||
|
||||
// labels.fightingstyle = C.fightingstyle[data.fightingstyle];
|
||||
}
|
||||
// Fighting Mastery Items
|
||||
else if ( itemData.type === "fightingmastery" ) {
|
||||
|
||||
// labels.fightingmastery = C.fightingmastery[data.fightingmastery];
|
||||
}
|
||||
// Lightsaber Form Items
|
||||
else if ( itemData.type === "lightsaberform" ) {
|
||||
|
||||
// labels.lightsaberform = C.lightsaberform[data.lightsaberform];
|
||||
}
|
||||
|
||||
// Equipment Items
|
||||
|
@ -322,7 +323,7 @@ export default class Item5e extends Item {
|
|||
user: game.user._id,
|
||||
type: CONST.CHAT_MESSAGE_TYPES.OTHER,
|
||||
content: html,
|
||||
flavor: this.name,
|
||||
flavor: this.data.data.chatFlavor || this.name,
|
||||
speaker: {
|
||||
actor: this.actor._id,
|
||||
token: this.actor.token,
|
||||
|
@ -348,7 +349,7 @@ export default class Item5e extends Item {
|
|||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
/**
|
||||
* For items which consume a resource, handle the consumption of that resource when the item is used.
|
||||
* There are four types of ability consumptions which are handled:
|
||||
* 1. Ammunition (on attack rolls)
|
||||
|
@ -367,7 +368,6 @@ export default class Item5e extends Item {
|
|||
if ( !consume.type ) return true;
|
||||
const actor = this.actor;
|
||||
const typeLabel = CONFIG.SW5E.abilityConsumptionTypes[consume.type];
|
||||
const amount = parseInt(consume.amount || 1);
|
||||
|
||||
// Only handle certain types for certain actions
|
||||
if ( ((consume.type === "ammo") && !isAttack ) || ((consume.type !== "ammo") && !isCard) ) return true;
|
||||
|
@ -380,6 +380,7 @@ export default class Item5e extends Item {
|
|||
|
||||
// Identify the consumed resource and it's quantity
|
||||
let consumed = null;
|
||||
let amount = parseInt(consume.amount || 1);
|
||||
let quantity = 0;
|
||||
switch ( consume.type ) {
|
||||
case "attribute":
|
||||
|
@ -393,7 +394,13 @@ export default class Item5e extends Item {
|
|||
break;
|
||||
case "charges":
|
||||
consumed = actor.items.get(consume.target);
|
||||
quantity = consumed ? consumed.data.data.uses.value : 0;
|
||||
if ( !consumed ) break;
|
||||
const uses = consumed.data.data.uses;
|
||||
if ( uses.per && uses.max ) quantity = uses.value;
|
||||
else if ( consumed.data.data.recharge?.value ) {
|
||||
quantity = consumed.data.data.recharge.charged ? 1 : 0;
|
||||
amount = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -418,7 +425,11 @@ export default class Item5e extends Item {
|
|||
await consumed.update({"data.quantity": remaining});
|
||||
break;
|
||||
case "charges":
|
||||
await consumed.update({"data.uses.value": remaining});
|
||||
const uses = consumed.data.data.uses || {};
|
||||
const recharge = consumed.data.data.recharge || {};
|
||||
if ( uses.per && uses.max ) await consumed.update({"data.uses.value": remaining});
|
||||
else if ( recharge.value ) await consumed.update({"data.recharge.charged": false});
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -436,7 +447,7 @@ export default class Item5e extends Item {
|
|||
// Configure whether to consume a limited use or to place a template
|
||||
const charge = this.data.data.recharge;
|
||||
const uses = this.data.data.uses;
|
||||
let usesCharges = !!uses.per && (uses.max > 0);
|
||||
let usesCharges = !!uses.per && !!uses.max;
|
||||
let placeTemplate = false;
|
||||
let consume = charge.value || usesCharges;
|
||||
|
||||
|
@ -637,40 +648,37 @@ export default class Item5e extends Item {
|
|||
}
|
||||
|
||||
// Attack Bonus
|
||||
if ( itemData.attackBonus ) parts.push(itemData.attackBonus);
|
||||
const actorBonus = actorData?.bonuses?.[itemData.actionType] || {};
|
||||
if ( itemData.attackBonus || actorBonus.attack ) {
|
||||
parts.push("@atk");
|
||||
rollData["atk"] = [itemData.attackBonus, actorBonus.attack].filterJoin(" + ");
|
||||
}
|
||||
if ( actorBonus.attack ) parts.push(actorBonus.attack);
|
||||
|
||||
// Ammunition Bonus
|
||||
delete this._ammo;
|
||||
const consume = itemData.consume;
|
||||
if ( consume?.type === "ammo" ) {
|
||||
const ammo = this.actor.items.get(consume.target);
|
||||
if(ammo?.data){
|
||||
const q = ammo.data.data.quantity;
|
||||
const consumeAmount = consume.amount ?? 0;
|
||||
if ( q && (q - consumeAmount >= 0) ) {
|
||||
let ammoBonus = ammo.data.data.attackBonus;
|
||||
if ( ammoBonus ) {
|
||||
parts.push("@ammo");
|
||||
rollData["ammo"] = ammoBonus;
|
||||
title += ` [${ammo.name}]`;
|
||||
this._ammo = ammo;
|
||||
}
|
||||
// Ammunition Bonus
|
||||
delete this._ammo;
|
||||
const consume = itemData.consume;
|
||||
if ( consume?.type === "ammo" ) {
|
||||
const ammo = this.actor.items.get(consume.target);
|
||||
if(ammo?.data){
|
||||
const q = ammo.data.data.quantity;
|
||||
const consumeAmount = consume.amount ?? 0;
|
||||
if ( q && (q - consumeAmount >= 0) ) {
|
||||
this._ammo = ammo;
|
||||
let ammoBonus = ammo.data.data.attackBonus;
|
||||
if ( ammoBonus ) {
|
||||
parts.push("@ammo");
|
||||
rollData["ammo"] = ammoBonus;
|
||||
title += ` [${ammo.name}]`;
|
||||
}
|
||||
//}else{
|
||||
// ui.notifications.error(game.i18n.format("SW5E.ConsumeWarningNoResource", {name: this.name, type: typeLabel}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Compose roll options
|
||||
const rollConfig = mergeObject({
|
||||
parts: parts,
|
||||
actor: this.actor,
|
||||
data: rollData,
|
||||
title: title,
|
||||
flavor: title,
|
||||
speaker: ChatMessage.getSpeaker({actor: this.actor}),
|
||||
dialogOptions: {
|
||||
width: 400,
|
||||
|
@ -681,9 +689,11 @@ export default class Item5e extends Item {
|
|||
}, options);
|
||||
rollConfig.event = options.event;
|
||||
|
||||
// Expanded weapon critical threshold
|
||||
// Expanded critical hit thresholds
|
||||
if (( this.data.type === "weapon" ) && flags.weaponCriticalThreshold) {
|
||||
rollConfig.critical = parseInt(flags.weaponCriticalThreshold);
|
||||
} else if (( this.data.type === "power" ) && flags.powerCriticalThreshold) {
|
||||
rollConfig.critical = parseInt(flags.powerCriticalThreshold);
|
||||
}
|
||||
|
||||
// Elven Accuracy
|
||||
|
@ -710,28 +720,41 @@ 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
|
||||
*
|
||||
* @return {Promise<Roll>} A Promise which resolves to the created Roll instance
|
||||
* Rely upon the damageRoll logic for the core implementation.
|
||||
* @param {MouseEvent} [event] An event which triggered this roll, if any
|
||||
* @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
|
||||
* @return {Promise<Roll>} A Promise which resolves to the created Roll instance
|
||||
*/
|
||||
rollDamage({event, powerLevel=null, versatile=false}={}) {
|
||||
rollDamage({event, powerLevel=null, versatile=false, options={}}={}) {
|
||||
if ( !this.hasDamage ) throw new Error("You may not make a Damage Roll with this Item.");
|
||||
const itemData = this.data.data;
|
||||
const actorData = this.actor.data.data;
|
||||
if ( !this.hasDamage ) {
|
||||
throw new Error("You may not make a Damage Roll with this Item.");
|
||||
}
|
||||
const messageData = {"flags.sw5e.roll": {type: "damage", itemId: this.id }};
|
||||
|
||||
// Get roll data
|
||||
const parts = itemData.damage.parts.map(d => d[0]);
|
||||
const rollData = this.getRollData();
|
||||
if ( powerLevel ) rollData.item.level = powerLevel;
|
||||
|
||||
// Get message labels
|
||||
// Configure the damage roll
|
||||
const title = `${this.name} - ${game.i18n.localize("SW5E.DamageRoll")}`;
|
||||
let flavor = this.labels.damageTypes.length ? `${title} (${this.labels.damageTypes})` : title;
|
||||
|
||||
// Define Roll parts
|
||||
const parts = itemData.damage.parts.map(d => d[0]);
|
||||
const rollConfig = {
|
||||
event: event,
|
||||
parts: parts,
|
||||
actor: this.actor,
|
||||
data: rollData,
|
||||
title: title,
|
||||
flavor: this.labels.damageTypes.length ? `${title} (${this.labels.damageTypes})` : title,
|
||||
speaker: ChatMessage.getSpeaker({actor: this.actor}),
|
||||
dialogOptions: {
|
||||
width: 400,
|
||||
top: event ? event.clientY - 80 : null,
|
||||
left: window.innerWidth - 710
|
||||
},
|
||||
messageData: messageData
|
||||
};
|
||||
|
||||
// Adjust damage from versatile usage
|
||||
if ( versatile && itemData.damage.versatile ) {
|
||||
|
@ -751,37 +774,27 @@ export default class Item5e extends Item {
|
|||
}
|
||||
}
|
||||
|
||||
// Define Roll Data
|
||||
// Add damage bonus formula
|
||||
const actorBonus = getProperty(actorData, `bonuses.${itemData.actionType}`) || {};
|
||||
if ( actorBonus.damage && parseInt(actorBonus.damage) !== 0 ) {
|
||||
parts.push("@dmg");
|
||||
rollData["dmg"] = actorBonus.damage;
|
||||
if ( actorBonus.damage && (parseInt(actorBonus.damage) !== 0) ) {
|
||||
parts.push(actorBonus.damage);
|
||||
}
|
||||
|
||||
// Ammunition Damage
|
||||
// Add ammunition damage
|
||||
if ( this._ammo ) {
|
||||
parts.push("@ammo");
|
||||
rollData["ammo"] = this._ammo.data.data.damage.parts.map(p => p[0]).join("+");
|
||||
flavor += ` [${this._ammo.name}]`;
|
||||
rollConfig.flavor += ` [${this._ammo.name}]`;
|
||||
delete this._ammo;
|
||||
}
|
||||
|
||||
// Scale melee critical hit damage
|
||||
if ( itemData.actionType === "mwak" ) {
|
||||
rollConfig.criticalBonusDice = this.actor.getFlag("sw5e", "meleeCriticalDamageDice") ?? 0;
|
||||
}
|
||||
|
||||
// Call the roll helper utility
|
||||
return damageRoll({
|
||||
event: event,
|
||||
parts: parts,
|
||||
actor: this.actor,
|
||||
data: rollData,
|
||||
title: title,
|
||||
flavor: flavor,
|
||||
speaker: ChatMessage.getSpeaker({actor: this.actor}),
|
||||
dialogOptions: {
|
||||
width: 400,
|
||||
top: event ? event.clientY - 80 : null,
|
||||
left: window.innerWidth - 710
|
||||
},
|
||||
messageData
|
||||
});
|
||||
return damageRoll(mergeObject(rollConfig, options));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -793,22 +806,7 @@ export default class Item5e extends Item {
|
|||
_scaleAtWillDamage(parts, scale, level, rollData) {
|
||||
const add = Math.floor((level + 1) / 6);
|
||||
if ( add === 0 ) return;
|
||||
|
||||
// FUTURE SOLUTION - 0.7.0 AND LATER
|
||||
if (isNewerVersion(game.data.version, "0.6.9")) {
|
||||
this._scaleDamage(parts, scale || parts.join(" + "), add, rollData)
|
||||
|
||||
}
|
||||
|
||||
// LEGACY SOLUTION - 0.6.x AND OLDER
|
||||
// TODO: Deprecate the legacy solution one FVTT 0.7.x is RELEASE
|
||||
else {
|
||||
if ( scale && (scale !== parts[0]) ) {
|
||||
parts[0] = parts[0] + " + " + scale.replace(new RegExp(Roll.diceRgx, "g"), (match, nd, d) => `${add}d${d}`);
|
||||
} else {
|
||||
parts[0] = parts[0].replace(new RegExp(Roll.diceRgx, "g"), (match, nd, d) => `${parseInt(nd)+add}d${d}`);
|
||||
}
|
||||
}
|
||||
this._scaleDamage(parts, scale || parts.join(" + "), add, rollData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -826,20 +824,7 @@ export default class Item5e extends Item {
|
|||
_scalePowerDamage(parts, baseLevel, powerLevel, formula, rollData) {
|
||||
const upcastLevels = Math.max(powerLevel - baseLevel, 0);
|
||||
if ( upcastLevels === 0 ) return parts;
|
||||
|
||||
// FUTURE SOLUTION - 0.7.0 AND LATER
|
||||
if (isNewerVersion(game.data.version, "0.6.9")) {
|
||||
this._scaleDamage(parts, formula, upcastLevels, rollData);
|
||||
}
|
||||
|
||||
// LEGACY SOLUTION - 0.6.x AND OLDER
|
||||
// TODO: Deprecate the legacy solution one FVTT 0.7.x is RELEASE
|
||||
else {
|
||||
const bonus = new Roll(formula);
|
||||
bonus.alter(0, upcastLevels);
|
||||
parts.push(bonus.formula);
|
||||
}
|
||||
return parts;
|
||||
this._scaleDamage(parts, formula, upcastLevels, rollData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -882,7 +867,7 @@ export default class Item5e extends Item {
|
|||
/**
|
||||
* Place an attack roll using an item (weapon, feat, power, or equipment)
|
||||
* Rely upon the d20Roll logic for the core implementation
|
||||
*
|
||||
*
|
||||
* @return {Promise<Roll>} A Promise which resolves to the created Roll instance
|
||||
*/
|
||||
async rollFormula(options={}) {
|
||||
|
@ -899,7 +884,7 @@ export default class Item5e extends Item {
|
|||
const roll = new Roll(rollData.item.formula, rollData).roll();
|
||||
roll.toMessage({
|
||||
speaker: ChatMessage.getSpeaker({actor: this.actor}),
|
||||
flavor: this.data.data.chatFlavor || title,
|
||||
flavor: title,
|
||||
rollMode: game.settings.get("core", "rollMode"),
|
||||
messageData: {"flags.sw5e.roll": {type: "other", itemId: this.id }}
|
||||
});
|
||||
|
@ -910,7 +895,7 @@ export default class Item5e extends Item {
|
|||
|
||||
/**
|
||||
* Use a consumable item, deducting from the quantity or charges of the item.
|
||||
* @param {boolean} configureDialog Whether to show a configuration dialog
|
||||
* @param {boolean} configureDialog Whether to show a configuration dialog
|
||||
* @return {boolean} Whether further execution should be prevented
|
||||
* @private
|
||||
*/
|
||||
|
@ -972,7 +957,7 @@ export default class Item5e extends Item {
|
|||
if ( this.owner && this.owner.sheet ) this.owner.sheet.minimize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
@ -1021,7 +1006,7 @@ export default class Item5e extends Item {
|
|||
template: "systems/sw5e/templates/chat/tool-roll-dialog.html",
|
||||
title: title,
|
||||
speaker: ChatMessage.getSpeaker({actor: this.actor}),
|
||||
flavor: `${this.name} - ${game.i18n.localize("SW5E.ToolCheck")}`,
|
||||
flavor: title,
|
||||
dialogOptions: {
|
||||
width: 400,
|
||||
top: options.event ? options.event.clientY - 80 : null,
|
||||
|
@ -1055,8 +1040,8 @@ export default class Item5e extends Item {
|
|||
}
|
||||
|
||||
// Include a proficiency score
|
||||
const prof = "proficient" in rollData.item ? (rollData.item.proficient || 0) : 1;
|
||||
rollData["prof"] = Math.floor(prof * rollData.attributes.prof);
|
||||
const prof = ("proficient" in rollData.item) ? (rollData.item.proficient || 0) : 1;
|
||||
rollData["prof"] = Math.floor(prof * (rollData.attributes.prof || 0));
|
||||
return rollData;
|
||||
}
|
||||
|
||||
|
@ -1189,7 +1174,7 @@ export default class Item5e extends Item {
|
|||
if ( !targets.length ) ui.notifications.warn(game.i18n.localize("SW5E.ActionWarningNoToken"));
|
||||
return targets;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Factory Methods */
|
||||
/* -------------------------------------------- */
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import TraitSelector from "../apps/trait-selector.js";
|
||||
import {onManageActiveEffect, prepareActiveEffectCategories} from "../effects.js";
|
||||
|
||||
/**
|
||||
* Override and extend the core ItemSheet implementation to handle specific item types
|
||||
|
@ -7,8 +8,11 @@ import TraitSelector from "../apps/trait-selector.js";
|
|||
export default class ItemSheet5e extends ItemSheet {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
// Expand the default size of the class sheet
|
||||
if ( this.object.data.type === "class" ) {
|
||||
this.options.width = 600;
|
||||
this.options.width = this.position.width = 600;
|
||||
this.options.height = this.position.height = 680;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +22,7 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
static get defaultOptions() {
|
||||
return mergeObject(super.defaultOptions, {
|
||||
width: 560,
|
||||
height: "auto",
|
||||
height: 400,
|
||||
classes: ["sw5e", "sheet", "item"],
|
||||
resizable: true,
|
||||
scrollY: [".tab.details"],
|
||||
|
@ -37,17 +41,17 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
getData() {
|
||||
const data = super.getData();
|
||||
async getData(options) {
|
||||
const data = super.getData(options);
|
||||
data.labels = this.item.labels;
|
||||
data.config = CONFIG.SW5E;
|
||||
|
||||
// Item Type, Status, and Details
|
||||
data.itemType = data.item.type.titleCase();
|
||||
data.itemType = game.i18n.localize(`ITEM.Type${data.item.type.titleCase()}`);
|
||||
data.itemStatus = this._getItemStatus(data.item);
|
||||
data.itemProperties = this._getItemProperties(data.item);
|
||||
data.isPhysical = data.item.data.hasOwnProperty("quantity");
|
||||
|
||||
|
||||
// Potential consumption targets
|
||||
data.abilityConsumptionTargets = this._getItemConsumptionTargets(data.item);
|
||||
|
||||
|
@ -55,17 +59,20 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
data.hasAttackRoll = this.item.hasAttack;
|
||||
data.isHealing = data.item.data.actionType === "heal";
|
||||
data.isFlatDC = getProperty(data.item.data, "save.scaling") === "flat";
|
||||
data.isLine = ["line", "wall"].includes(data.item.data.target?.type);
|
||||
data.isLine = ["line", "wall"].includes(data.item.data.target?.type);
|
||||
|
||||
// Vehicles
|
||||
data.isCrewed = data.item.data.activation?.type === 'crew';
|
||||
data.isMountable = this._isItemMountable(data.item);
|
||||
|
||||
// Prepare Active Effects
|
||||
data.effects = prepareActiveEffectCategories(this.entity.effects);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get the valid item consumption targets which exist on the actor
|
||||
* @param {Object} item Item data for the item being displayed
|
||||
* @return {{string: string}} An object of potential consumption targets
|
||||
|
@ -109,6 +116,8 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
// Charges
|
||||
else if ( consume.type === "charges" ) {
|
||||
return actor.items.reduce((obj, i) => {
|
||||
|
||||
// Limited-use items
|
||||
const uses = i.data.data.uses || {};
|
||||
if ( uses.per && uses.max ) {
|
||||
const label = uses.per === "charges" ?
|
||||
|
@ -116,6 +125,10 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
` (${game.i18n.format("SW5E.AbilityUseConsumableLabel", {max: uses.max, per: uses.per})})`;
|
||||
obj[i.id] = i.name + label;
|
||||
}
|
||||
|
||||
// Recharging items
|
||||
const recharge = i.data.data.recharge || {};
|
||||
if ( recharge.value ) obj[i.id] = `${i.name} (${game.i18n.format("SW5E.Recharge")})`;
|
||||
return obj;
|
||||
}, {})
|
||||
}
|
||||
|
@ -177,31 +190,26 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
}
|
||||
|
||||
else if ( item.type === "species" ) {
|
||||
|
||||
//props.push(labels.species);
|
||||
}
|
||||
else if ( item.type === "archetype" ) {
|
||||
|
||||
//props.push(labels.archetype);
|
||||
}
|
||||
else if ( item.type === "background" ) {
|
||||
//props.push(labels.background);
|
||||
}
|
||||
else if ( item.type === "classfeature" ) {
|
||||
//props.push(labels.classfeature);
|
||||
}
|
||||
else if ( item.type === "fightingmastery" ) {
|
||||
//props.push(labels.fightingmastery);
|
||||
}
|
||||
else if ( item.type === "fightingstyle" ) {
|
||||
//props.push(labels.fightingstyle);
|
||||
}
|
||||
else if ( item.type === "lightsaberform" ) {
|
||||
//props.push(labels.lightsaberform);
|
||||
}
|
||||
|
||||
else if ( item.type === "background" ) {
|
||||
|
||||
}
|
||||
|
||||
else if ( item.type === "classfeature" ) {
|
||||
|
||||
}
|
||||
|
||||
else if ( item.type === "fightingmastery" ) {
|
||||
|
||||
}
|
||||
|
||||
else if ( item.type === "fightingstyle" ) {
|
||||
|
||||
}
|
||||
|
||||
else if ( item.type === "lightsaberform" ) {
|
||||
|
||||
}
|
||||
|
||||
// Action type
|
||||
if ( item.data.actionType ) {
|
||||
|
@ -240,8 +248,8 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
|
||||
/** @override */
|
||||
setPosition(position={}) {
|
||||
if ( !this._minimized ) {
|
||||
position.height = this._tabs[0].active === "details" ? "auto" : this.options.height;
|
||||
if ( !(this._minimized || position.height) ) {
|
||||
position.height = (this._tabs[0].active === "details") ? "auto" : this.options.height;
|
||||
}
|
||||
return super.setPosition(position);
|
||||
}
|
||||
|
@ -251,17 +259,20 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
_updateObject(event, formData) {
|
||||
_getSubmitData(updateData={}) {
|
||||
|
||||
// TODO: This can be removed once 0.7.x is release channel
|
||||
if ( !formData.data ) formData = expandObject(formData);
|
||||
// Create the expanded update data object
|
||||
const fd = new FormDataExtended(this.form, {editors: this.editors});
|
||||
let data = fd.toObject();
|
||||
if ( updateData ) data = mergeObject(data, updateData);
|
||||
else data = expandObject(data);
|
||||
|
||||
// Handle Damage Array
|
||||
const damage = formData.data?.damage;
|
||||
// Handle Damage array
|
||||
const damage = data.data?.damage;
|
||||
if ( damage ) damage.parts = Object.values(damage?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||
|
||||
// Update the Item
|
||||
super._updateObject(event, formData);
|
||||
// Return the flattened submission data
|
||||
return flattenObject(data);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -269,8 +280,14 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
/** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
html.find(".damage-control").click(this._onDamageControl.bind(this));
|
||||
html.find('.trait-selector.class-skills').click(this._onConfigureClassSkills.bind(this));
|
||||
if ( this.isEditable ) {
|
||||
html.find(".damage-control").click(this._onDamageControl.bind(this));
|
||||
html.find('.trait-selector.class-skills').click(this._onConfigureClassSkills.bind(this));
|
||||
html.find(".effect-control").click(ev => {
|
||||
if ( this.item.isOwned ) return ui.notifications.warn("Managing Active Effects within an Owned Item is not currently supported and will be added in a subsequent update.")
|
||||
onManageActiveEffect(ev, this.item)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -328,4 +345,12 @@ export default class ItemSheet5e extends ItemSheet {
|
|||
maximum: skills.number
|
||||
}).render(true)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
async _onSubmit(...args) {
|
||||
if ( this._tabs[0].active === "details" ) this.position.height = "auto";
|
||||
await super._onSubmit(...args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ export const migrateWorld = async function() {
|
|||
await a.update(updateData, {enforceTypes: false});
|
||||
}
|
||||
} catch(err) {
|
||||
err.message = `Failed sw5e system migration for Actor ${a.name}: ${err.message}`;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +28,7 @@ export const migrateWorld = async function() {
|
|||
await i.update(updateData, {enforceTypes: false});
|
||||
}
|
||||
} catch(err) {
|
||||
err.message = `Failed sw5e system migration for Item ${i.name}: ${err.message}`;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
@ -40,15 +42,15 @@ export const migrateWorld = async function() {
|
|||
await s.update(updateData, {enforceTypes: false});
|
||||
}
|
||||
} catch(err) {
|
||||
err.message = `Failed sw5e system migration for Scene ${s.name}: ${err.message}`;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate World Compendium Packs
|
||||
const packs = game.packs.filter(p => {
|
||||
return (p.metadata.package === "world") && ["Actor", "Item", "Scene"].includes(p.metadata.entity)
|
||||
});
|
||||
for ( let p of packs ) {
|
||||
for ( let p of game.packs ) {
|
||||
if ( p.metadata.package !== "world" ) continue;
|
||||
if ( !["Actor", "Item", "Scene"].includes(p.metadata.entity) ) continue;
|
||||
await migrateCompendium(p);
|
||||
}
|
||||
|
||||
|
@ -68,27 +70,46 @@ export const migrateCompendium = async function(pack) {
|
|||
const entity = pack.metadata.entity;
|
||||
if ( !["Actor", "Item", "Scene"].includes(entity) ) return;
|
||||
|
||||
// Unlock the pack for editing
|
||||
const wasLocked = pack.locked;
|
||||
await pack.configure({locked: false});
|
||||
|
||||
// Begin by requesting server-side data model migration and get the migrated content
|
||||
await pack.migrate();
|
||||
const content = await pack.getContent();
|
||||
|
||||
// Iterate over compendium entries - applying fine-tuned migration functions
|
||||
for ( let ent of content ) {
|
||||
let updateData = {};
|
||||
try {
|
||||
let updateData = null;
|
||||
if (entity === "Item") updateData = migrateItemData(ent.data);
|
||||
else if (entity === "Actor") updateData = migrateActorData(ent.data);
|
||||
else if ( entity === "Scene" ) updateData = migrateSceneData(ent.data);
|
||||
if (!isObjectEmpty(updateData)) {
|
||||
expandObject(updateData);
|
||||
updateData["_id"] = ent._id;
|
||||
await pack.updateEntity(updateData);
|
||||
console.log(`Migrated ${entity} entity ${ent.name} in Compendium ${pack.collection}`);
|
||||
switch (entity) {
|
||||
case "Actor":
|
||||
updateData = migrateActorData(ent.data);
|
||||
break;
|
||||
case "Item":
|
||||
updateData = migrateItemData(ent.data);
|
||||
break;
|
||||
case "Scene":
|
||||
updateData = migrateSceneData(ent.data);
|
||||
break;
|
||||
}
|
||||
} catch(err) {
|
||||
if ( isObjectEmpty(updateData) ) continue;
|
||||
|
||||
// Save the entry, if data was changed
|
||||
updateData["_id"] = ent._id;
|
||||
await pack.updateEntity(updateData);
|
||||
console.log(`Migrated ${entity} entity ${ent.name} in Compendium ${pack.collection}`);
|
||||
}
|
||||
|
||||
// Handle migration failures
|
||||
catch(err) {
|
||||
err.message = `Failed sw5e system migration for entity ${ent.name} in pack ${pack.collection}: ${err.message}`;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the original locked status for the pack
|
||||
pack.configure({locked: wasLocked});
|
||||
console.log(`Migrated all ${entity} entities from Compendium ${pack.collection}`);
|
||||
};
|
||||
|
||||
|
@ -107,9 +128,7 @@ export const migrateActorData = function(actor) {
|
|||
|
||||
// Actor Data Updates
|
||||
_migrateActorBonuses(actor, updateData);
|
||||
|
||||
// Remove deprecated fields
|
||||
_migrateRemoveDeprecated(actor, updateData);
|
||||
_migrateActorMovement(actor, updateData);
|
||||
|
||||
// Migrate Owned Items
|
||||
if ( !actor.items ) return updateData;
|
||||
|
@ -172,11 +191,6 @@ function cleanActorData(actorData) {
|
|||
*/
|
||||
export const migrateItemData = function(item) {
|
||||
const updateData = {};
|
||||
|
||||
// Remove deprecated fields
|
||||
_migrateRemoveDeprecated(item, updateData);
|
||||
|
||||
// Return the migrated update data
|
||||
return updateData;
|
||||
};
|
||||
|
||||
|
@ -225,31 +239,17 @@ function _migrateActorBonuses(actor, updateData) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* A general migration to remove all fields from the data model which are flagged with a _deprecated tag
|
||||
* Migrate the actor bonuses object
|
||||
* @private
|
||||
*/
|
||||
const _migrateRemoveDeprecated = function(ent, updateData) {
|
||||
const flat = flattenObject(ent.data);
|
||||
|
||||
// Identify objects to deprecate
|
||||
const toDeprecate = Object.entries(flat).filter(e => e[0].endsWith("_deprecated") && (e[1] === true)).map(e => {
|
||||
let parent = e[0].split(".");
|
||||
parent.pop();
|
||||
return parent.join(".");
|
||||
});
|
||||
|
||||
// Remove them
|
||||
for ( let k of toDeprecate ) {
|
||||
let parts = k.split(".");
|
||||
parts[parts.length-1] = "-=" + parts[parts.length-1];
|
||||
updateData[`data.${parts.join(".")}`] = null;
|
||||
}
|
||||
};
|
||||
function _migrateActorMovement(actor, updateData) {
|
||||
if ( actor.data.attributes?.movement?.walk !== undefined ) return;
|
||||
const s = (actor.data.attributes?.speed?.value || "").split(" ");
|
||||
if ( s.length > 0 ) updateData["data.attributes.movement.walk"] = Number.isNumeric(s[0]) ? parseInt(s[0]) : null;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -280,3 +280,24 @@ export async function purgeFlags(pack) {
|
|||
}
|
||||
await pack.configure({locked: true});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Purge the data model of any inner objects which have been flagged as _deprecated.
|
||||
* @param {object} data The data to clean
|
||||
* @private
|
||||
*/
|
||||
export function removeDeprecatedObjects(data) {
|
||||
for ( let [k, v] of Object.entries(data) ) {
|
||||
if ( getType(v) === "Object" ) {
|
||||
if (v._deprecated === true) {
|
||||
console.log(`Deleting deprecated object key ${k}`);
|
||||
delete data[k];
|
||||
}
|
||||
else removeDeprecatedObjects(v);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ export default class AbilityTemplate extends MeasuredTemplate {
|
|||
templateData.width = target.value;
|
||||
templateData.direction = 45;
|
||||
break;
|
||||
case "ray": // 5e rays are most commonly 1 square (5 ft) in width
|
||||
case "ray": // 5e rays are most commonly 1 square (5 ft) in width
|
||||
templateData.width = target.width ?? canvas.dimensions.distance;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -7,11 +7,11 @@ export const registerSystemSettings = function() {
|
|||
name: "System Migration Version",
|
||||
scope: "world",
|
||||
config: false,
|
||||
type: Number,
|
||||
default: 0
|
||||
type: String,
|
||||
default: ""
|
||||
});
|
||||
|
||||
/**
|
||||
/**
|
||||
* Register resting variants
|
||||
*/
|
||||
game.settings.register("sw5e", "restVariant", {
|
||||
|
@ -82,18 +82,6 @@ export const registerSystemSettings = function() {
|
|||
type: Boolean,
|
||||
});
|
||||
|
||||
/**
|
||||
* Option to automatically create Power Measured Template on roll
|
||||
*/
|
||||
game.settings.register("sw5e", "alwaysPlacePowerTemplate", {
|
||||
name: "SETTINGS.5eAutoPowerTemplateN",
|
||||
hint: "SETTINGS.5eAutoPowerTemplateL",
|
||||
scope: "client",
|
||||
config: true,
|
||||
default: false,
|
||||
type: Boolean
|
||||
});
|
||||
|
||||
/**
|
||||
* Option to automatically collapse Item Card descriptions
|
||||
*/
|
||||
|
|
|
@ -4,17 +4,16 @@
|
|||
* @return {Promise}
|
||||
*/
|
||||
export const preloadHandlebarsTemplates = async function() {
|
||||
return loadTemplates([
|
||||
|
||||
// Define template paths to load
|
||||
const templatePaths = [
|
||||
// Shared Partials
|
||||
"systems/sw5e/templates/actors/parts/active-effects.html",
|
||||
|
||||
// Actor Sheet Partials
|
||||
"systems/sw5e/templates/actors/oldActor/parts/actor-traits.html",
|
||||
"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-effects.html",
|
||||
|
||||
|
||||
"systems/sw5e/templates/actors/newActor/parts/swalt-biography.html",
|
||||
"systems/sw5e/templates/actors/newActor/parts/swalt-core.html",
|
||||
|
@ -30,8 +29,5 @@ export const preloadHandlebarsTemplates = async function() {
|
|||
"systems/sw5e/templates/items/parts/item-activation.html",
|
||||
"systems/sw5e/templates/items/parts/item-description.html",
|
||||
"systems/sw5e/templates/items/parts/item-mountable.html"
|
||||
];
|
||||
|
||||
// Load the template parts
|
||||
return loadTemplates(templatePaths);
|
||||
]);
|
||||
};
|
||||
|
|
257
sw5e.css
257
sw5e.css
|
@ -231,6 +231,12 @@
|
|||
/* ----------------------------------------- */
|
||||
/* Trait Lists */
|
||||
/* ----------------------------------------- */
|
||||
/* ----------------------------------------- */
|
||||
/* Items Lists */
|
||||
/* ----------------------------------------- */
|
||||
/* ----------------------------------------- */
|
||||
/* Active Effects */
|
||||
/* ----------------------------------------- */
|
||||
}
|
||||
.sw5e.sheet .window-content {
|
||||
overflow-y: hidden;
|
||||
|
@ -404,6 +410,88 @@
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.sw5e.sheet .items-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
color: #7a7971;
|
||||
}
|
||||
.sw5e.sheet .items-list .item-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.sw5e.sheet .items-list .item {
|
||||
align-items: center;
|
||||
padding: 0 2px;
|
||||
border-bottom: 1px solid #c9c7b8;
|
||||
}
|
||||
.sw5e.sheet .items-list .item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.sw5e.sheet .items-list .item .item-name {
|
||||
color: #191813;
|
||||
}
|
||||
.sw5e.sheet .items-list .item .item-name .item-image {
|
||||
flex: 0 0 30px;
|
||||
height: 30px;
|
||||
background-size: 30px;
|
||||
border: none;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.sw5e.sheet .items-list .item .item-name h4 {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.sw5e.sheet .items-list .items-header {
|
||||
height: 28px;
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: 2px groove #eeede0;
|
||||
font-weight: bold;
|
||||
}
|
||||
.sw5e.sheet .items-list .items-header > * {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.sw5e.sheet .items-list .items-header .item-name {
|
||||
padding-left: 5px;
|
||||
font-family: 'Russo One';
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.sw5e.sheet .items-list .item-name {
|
||||
flex: 2;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
align-items: center;
|
||||
}
|
||||
.sw5e.sheet .items-list .item-controls {
|
||||
flex: 0 0 60px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.sw5e.sheet .items-list .item-controls a {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.sw5e.sheet .effects .item .effect-source,
|
||||
.sw5e.sheet .effects .item .effect-duration,
|
||||
.sw5e.sheet .effects .item .effect-controls {
|
||||
text-align: center;
|
||||
border-left: 1px solid #c9c7b8;
|
||||
border-right: 1px solid #c9c7b8;
|
||||
font-size: 12px;
|
||||
}
|
||||
.sw5e.sheet .effects .item .effect-controls {
|
||||
border: none;
|
||||
}
|
||||
/* ----------------------------------------- */
|
||||
/* Trait Selector
|
||||
/* ----------------------------------------- */
|
||||
|
@ -447,9 +535,6 @@
|
|||
/* Powerbook */
|
||||
/* ----------------------------------------- */
|
||||
/* ----------------------------------------- */
|
||||
/* Active Effects */
|
||||
/* ----------------------------------------- */
|
||||
/* ----------------------------------------- */
|
||||
/* TinyMCE */
|
||||
/* ----------------------------------------- */
|
||||
}
|
||||
|
@ -515,6 +600,7 @@
|
|||
font-weight: 400;
|
||||
color: #4b4a44;
|
||||
border-bottom: 1px solid #c9c7b8;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sw5e.sheet.actor .tab.attributes {
|
||||
overflow: hidden;
|
||||
|
@ -559,6 +645,7 @@
|
|||
font-family: "Signika", sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sw5e.sheet.actor .ability-scores {
|
||||
flex: 0 0 100px;
|
||||
|
@ -771,35 +858,10 @@
|
|||
border-bottom: 2px groove #eeede0;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 5px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
color: #7a7971;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item {
|
||||
line-height: 30px;
|
||||
padding: 0 2px;
|
||||
border-bottom: 1px solid #c9c7b8;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item .item-name {
|
||||
cursor: pointer;
|
||||
max-height: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item .item-name .item-image {
|
||||
flex: 0 0 30px;
|
||||
background-size: 30px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item .item-name h4 {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item .item-name.rollable:hover .item-image {
|
||||
background-image: url("../../icons/svg/d20-grey.svg") !important;
|
||||
|
@ -821,36 +883,14 @@
|
|||
flex: 0 0 80px;
|
||||
text-align: right;
|
||||
font-size: 11px;
|
||||
color: #7a7971;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .inventory-header {
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: 2px groove #eeede0;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .inventory-header h3 {
|
||||
margin: 0 -5px 0 0;
|
||||
padding-left: 5px;
|
||||
font-family: 'Russo One';
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .inventory-header .item-controls a.item-create {
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-name {
|
||||
color: #191813;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-detail {
|
||||
flex: 0 0 70px;
|
||||
font-size: 12px;
|
||||
color: #7a7971;
|
||||
text-align: center;
|
||||
border-right: 1px solid #c9c7b8;
|
||||
word-break: break-word;
|
||||
|
@ -868,45 +908,15 @@
|
|||
border-left: 1px solid #c9c7b8;
|
||||
border-right: 1px solid #c9c7b8;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-controls {
|
||||
flex: 0 0 44px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-controls > * {
|
||||
flex: 1;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-controls .flex1 {
|
||||
flex: 1;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-controls .flex2 {
|
||||
flex: 2;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-controls .flex3 {
|
||||
flex: 3;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-controls .flex4 {
|
||||
flex: 4;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-controls a {
|
||||
flex: 0 0 22px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: #7a7971;
|
||||
}
|
||||
.sw5e.sheet.actor .inventory-list .item-summary {
|
||||
flex: 0 0 100%;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
padding: 0.25em 0.5em;
|
||||
color: #191813;
|
||||
border-top: 1px solid #c9c7b8;
|
||||
}
|
||||
.sw5e.sheet.actor .encumbrance {
|
||||
|
@ -1034,42 +1044,25 @@
|
|||
.sw5e.sheet.actor .powerbook-empty .item-controls {
|
||||
flex: 1;
|
||||
}
|
||||
.sw5e.sheet.actor .effects .effect-name {
|
||||
flex: 2;
|
||||
align-items: center;
|
||||
color: #191813;
|
||||
}
|
||||
.sw5e.sheet.actor .effects .effect-name h4 {
|
||||
margin: 0;
|
||||
}
|
||||
.sw5e.sheet.actor .effects .effect-icon {
|
||||
flex: 0 0 30px;
|
||||
height: 30px;
|
||||
margin-right: 5px;
|
||||
border: none;
|
||||
}
|
||||
.sw5e.sheet.actor .effects .effect-source,
|
||||
.sw5e.sheet.actor .effects .effect-duration {
|
||||
text-align: center;
|
||||
border-left: 1px solid #c9c7b8;
|
||||
border-right: 1px solid #c9c7b8;
|
||||
}
|
||||
.sw5e.sheet.actor .effects .effect-controls {
|
||||
flex: 0 0 60px;
|
||||
text-align: right;
|
||||
}
|
||||
.sw5e.sheet.actor .effects .effect {
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #c9c7b8;
|
||||
}
|
||||
.sw5e.sheet.actor .effects .effect:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.sw5e.sheet.actor .editor {
|
||||
padding: 0 8px;
|
||||
}
|
||||
#actor-flags .window-content {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
#actor-flags form {
|
||||
height: 100%;
|
||||
}
|
||||
#actor-flags .form-body {
|
||||
height: calc(100% - 40px);
|
||||
padding-right: 8px;
|
||||
margin-bottom: 4px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.sw5e.sheet.item {
|
||||
min-height: 420px;
|
||||
min-height: 400px;
|
||||
max-height: 95%;
|
||||
min-width: 480px;
|
||||
/* ----------------------------------------- */
|
||||
/* Sheet Header */
|
||||
/* ----------------------------------------- */
|
||||
|
@ -1090,7 +1083,7 @@
|
|||
border: 2px solid #000;
|
||||
}
|
||||
.sw5e.sheet.item .sheet-header .item-subtitle {
|
||||
flex: 0 0 80px;
|
||||
flex: 0 0 100px;
|
||||
height: 60px;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
|
@ -1165,14 +1158,15 @@
|
|||
.sw5e.sheet.item .details .form-group.input-select-select select {
|
||||
flex: 1.5;
|
||||
}
|
||||
.sw5e.sheet.item .details .form-group.uses-per .form-fields {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.sw5e.sheet.item .details .form-group.uses-per input {
|
||||
flex: 1;
|
||||
flex: 0 0 32px;
|
||||
}
|
||||
.sw5e.sheet.item .details .form-group.uses-per span {
|
||||
flex: 0 0 16px;
|
||||
}
|
||||
.sw5e.sheet.item .details .form-group.uses-per select {
|
||||
flex: 3;
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
.sw5e.sheet.item .details span.sep {
|
||||
flex: 0 0 8px;
|
||||
|
@ -1544,30 +1538,3 @@
|
|||
max-width: 40px;
|
||||
text-align: right;
|
||||
}
|
||||
input[type="number"] {
|
||||
width: calc(100% - 2px);
|
||||
min-width: 20px;
|
||||
height: 26px;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
padding: 1px 3px;
|
||||
margin: 0;
|
||||
color: #191813;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
text-align: inherit;
|
||||
line-height: inherit;
|
||||
border: 1px solid #7a7971;
|
||||
border-radius: 3px;
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
input[type="number"]:focus {
|
||||
box-shadow: 0 0 5px red;
|
||||
}
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
|
51
sw5e.js
51
sw5e.js
|
@ -29,6 +29,7 @@ import ActorSheet5eCharacterNew from "./module/actor/sheets/newSheet/character.j
|
|||
import ItemSheet5e from "./module/item/sheet.js";
|
||||
import ShortRestDialog from "./module/apps/short-rest.js";
|
||||
import TraitSelector from "./module/apps/trait-selector.js";
|
||||
import MovementConfig from "./module/apps/movement-config.js";
|
||||
|
||||
// Import Helpers
|
||||
import * as chat from "./module/chat.js";
|
||||
|
@ -54,7 +55,8 @@ Hooks.once("init", function() {
|
|||
ActorSheet5eVehicle,
|
||||
ItemSheet5e,
|
||||
ShortRestDialog,
|
||||
TraitSelector
|
||||
TraitSelector,
|
||||
MovementConfig
|
||||
},
|
||||
canvas: {
|
||||
AbilityTemplate
|
||||
|
@ -74,7 +76,7 @@ Hooks.once("init", function() {
|
|||
CONFIG.SW5E = SW5E;
|
||||
CONFIG.Actor.entityClass = Actor5e;
|
||||
CONFIG.Item.entityClass = Item5e;
|
||||
if ( CONFIG.time ) CONFIG.time.roundTime = 6; // TODO remove conditional after 0.7.x
|
||||
CONFIG.time.roundTime = 6;
|
||||
|
||||
// Add DND5e namespace for module compatability
|
||||
game.dnd5e = game.sw5e;
|
||||
|
@ -132,18 +134,18 @@ Hooks.once("setup", function() {
|
|||
|
||||
// Localize CONFIG objects once up-front
|
||||
const toLocalize = [
|
||||
"abilities", "abilityAbbreviations", "alignments", "conditionTypes", "consumableTypes", "currencies",
|
||||
"damageTypes", "damageResistanceTypes", "distanceUnits", "equipmentTypes", "healingTypes", "itemActionTypes",
|
||||
"limitedUsePeriods", "senses", "skills", "powerComponents", "powerLevels", "powerPreparationModes", "powerSchools",
|
||||
"powerScalingModes", "targetTypes", "timePeriods", "weaponProperties", "weaponTypes", "languages",
|
||||
"polymorphSettings", "armorProficiencies", "weaponProficiencies", "toolProficiencies", "abilityActivationTypes",
|
||||
"abilityConsumptionTypes", "actorSizes", "proficiencyLevels", "armorPropertiesTypes", "cover"
|
||||
"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",
|
||||
"powerComponents", "powerLevels", "powerPreparationModes", "powerScalingModes", "powerSchools", "targetTypes",
|
||||
"timePeriods", "toolProficiencies", "weaponProficiencies", "weaponProperties", "weaponTypes"
|
||||
];
|
||||
|
||||
// Exclude some from sorting where the default order matters
|
||||
const noSort = [
|
||||
"abilities", "alignments", "currencies", "distanceUnits", "itemActionTypes", "proficiencyLevels",
|
||||
"limitedUsePeriods", "powerComponents", "powerLevels", "weaponTypes"
|
||||
"abilities", "alignments", "currencies", "distanceUnits", "movementUnits", "itemActionTypes", "proficiencyLevels",
|
||||
"limitedUsePeriods", "powerComponents", "powerLevels", "powerPreparationModes", "weaponTypes"
|
||||
];
|
||||
|
||||
// Localize and sort CONFIG objects
|
||||
|
@ -171,22 +173,23 @@ Hooks.once("setup", function() {
|
|||
*/
|
||||
Hooks.once("ready", function() {
|
||||
|
||||
// Determine whether a system migration is required and feasible
|
||||
const currentVersion = game.settings.get("sw5e", "systemMigrationVersion");
|
||||
const NEEDS_MIGRATION_VERSION = 0.84;
|
||||
const COMPATIBLE_MIGRATION_VERSION = 0.80;
|
||||
let needMigration = (currentVersion < NEEDS_MIGRATION_VERSION) || (currentVersion === null);
|
||||
|
||||
// Perform the migration
|
||||
if ( needMigration && game.user.isGM ) {
|
||||
if ( currentVersion && (currentVersion < COMPATIBLE_MIGRATION_VERSION) ) {
|
||||
ui.notifications.error(`Your SW5E system data is from too old a Foundry version and cannot be reliably migrated to the latest version. The process will be attempted, but errors may occur.`, {permanent: true});
|
||||
}
|
||||
migrations.migrateWorld();
|
||||
}
|
||||
|
||||
// Wait to register hotbar drop hook on ready so that modules could register earlier if they want to
|
||||
Hooks.on("hotbarDrop", (bar, data, slot) => macros.create5eMacro(data, slot));
|
||||
|
||||
// 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.1.0";
|
||||
const COMPATIBLE_MIGRATION_VERSION = 0.80;
|
||||
const needsMigration = currentVersion && isNewerVersion(NEEDS_MIGRATION_VERSION, currentVersion);
|
||||
if ( !needsMigration ) return;
|
||||
|
||||
// Perform the migration
|
||||
if ( currentVersion && isNewerVersion(COMPATIBLE_MIGRATION_VERSION, currentVersion) ) {
|
||||
const warning = `Your SW5e system data is from too old a Foundry version and cannot be reliably migrated to the latest version. The process will be attempted, but errors may occur.`;
|
||||
ui.notifications.error(warning, {permanent: true});
|
||||
}
|
||||
migrations.migrateWorld();
|
||||
});
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
|
34
system.json
34
system.json
|
@ -2,7 +2,7 @@
|
|||
"name": "sw5e",
|
||||
"title": "SW 5th Edition",
|
||||
"description": "A comprehensive game system for running games of SW 5th Edition in the Foundry VTT environment.",
|
||||
"version": 0.98,
|
||||
"version": "1.1.1",
|
||||
"author": "Dev Team",
|
||||
"scripts": [],
|
||||
"esmodules": ["sw5e.js"],
|
||||
|
@ -56,17 +56,17 @@
|
|||
"path": "./packs/packs/feats.db",
|
||||
"entity": "Item"
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "fightingstyles",
|
||||
"label": "Fighting Styles",
|
||||
"path": "./packs/packs/fightingstyles.db",
|
||||
"entity": "Item"
|
||||
"label": "Fighting Styles",
|
||||
"path": "./packs/packs/fightingstyles.db",
|
||||
"entity": "Item"
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "fightingmasteries",
|
||||
"label": "Fighting Masteries",
|
||||
"path": "./packs/packs/fightingmasteries.db",
|
||||
"entity": "Item"
|
||||
"label": "Fighting Masteries",
|
||||
"path": "./packs/packs/fightingmasteries.db",
|
||||
"entity": "Item"
|
||||
},
|
||||
{
|
||||
"name": "forcepowers",
|
||||
|
@ -103,20 +103,20 @@
|
|||
"label": "Species Traits",
|
||||
"path": "./packs/packs/speciestraits.db",
|
||||
"entity": "Item"
|
||||
},
|
||||
{
|
||||
},
|
||||
{
|
||||
"name": "tables",
|
||||
"label": "Tables",
|
||||
"path": "./packs/packs/tables.db",
|
||||
"entity": "RollTable"
|
||||
},
|
||||
{
|
||||
},
|
||||
{
|
||||
"name": "techpowers",
|
||||
"label": "Tech Powers",
|
||||
"path": "./packs/packs/techpowers.db",
|
||||
"entity": "Item"
|
||||
},
|
||||
{
|
||||
},
|
||||
{
|
||||
"name": "weapons",
|
||||
"label": "Weapons",
|
||||
"path": "./packs/packs/weapons.db",
|
||||
|
@ -135,8 +135,8 @@
|
|||
"gridUnits": "ft",
|
||||
"primaryTokenAttribute": "attributes.hp",
|
||||
"secondaryTokenAttribute": null,
|
||||
"minimumCoreVersion": "0.5.6",
|
||||
"compatibleCoreVersion": "0.7.5",
|
||||
"minimumCoreVersion": "0.7.6",
|
||||
"compatibleCoreVersion": "0.7.6",
|
||||
"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"
|
||||
|
|
|
@ -76,10 +76,18 @@
|
|||
},
|
||||
"creature": {
|
||||
"attributes": {
|
||||
"movement": {
|
||||
"burrow": 0,
|
||||
"climb": 0,
|
||||
"fly": 0,
|
||||
"swim": 0,
|
||||
"walk": 30,
|
||||
"units": "ft",
|
||||
"hover": false
|
||||
},
|
||||
"powercasting": "int",
|
||||
"speed": {
|
||||
"value": "30 ft",
|
||||
"special": ""
|
||||
"_deprecated": true
|
||||
}
|
||||
},
|
||||
"details": {
|
||||
|
@ -481,14 +489,14 @@
|
|||
"value": ""
|
||||
}
|
||||
},
|
||||
"speciesDescription": {
|
||||
"data": "$characteristics-table",
|
||||
"description": {
|
||||
"value": "",
|
||||
"chat": "",
|
||||
"unidentified": ""
|
||||
},
|
||||
"traits": ""
|
||||
"speciesDescription": {
|
||||
"data": "$characteristics-table",
|
||||
"description": {
|
||||
"value": "",
|
||||
"chat": "",
|
||||
"unidentified": ""
|
||||
},
|
||||
"traits": ""
|
||||
},
|
||||
"physicalItem": {
|
||||
"quantity": 1,
|
||||
|
@ -560,14 +568,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"archetype": {
|
||||
"templates": ["archetypeDescription"],
|
||||
"className": "",
|
||||
"description": ""
|
||||
},
|
||||
"background": {
|
||||
"templates": ["backgroundDescription"]
|
||||
},
|
||||
"archetype": {
|
||||
"templates": ["archetypeDescription"],
|
||||
"className": "",
|
||||
"description": ""
|
||||
},
|
||||
"background": {
|
||||
"templates": ["backgroundDescription"]
|
||||
},
|
||||
"backpack": {
|
||||
"templates": ["itemDescription", "physicalItem"],
|
||||
"capacity": {
|
||||
|
@ -593,19 +601,19 @@
|
|||
},
|
||||
"powercasting": "none"
|
||||
},
|
||||
"classfeature": {
|
||||
"templates": ["itemDescription", "activatedEffect", "action"],
|
||||
"className": ""
|
||||
},
|
||||
"fightingmastery": {
|
||||
"templates": ["fightingmasteryDescription"]
|
||||
},
|
||||
"fightingstyle": {
|
||||
"templates": ["fightingstyleDescription"]
|
||||
},
|
||||
"lightsaberform": {
|
||||
"templates": ["lightsaberformDescription"]
|
||||
},
|
||||
"classfeature": {
|
||||
"templates": ["itemDescription", "activatedEffect", "action"],
|
||||
"className": ""
|
||||
},
|
||||
"fightingmastery": {
|
||||
"templates": ["fightingmasteryDescription"]
|
||||
},
|
||||
"fightingstyle": {
|
||||
"templates": ["fightingstyleDescription"]
|
||||
},
|
||||
"lightsaberform": {
|
||||
"templates": ["lightsaberformDescription"]
|
||||
},
|
||||
"consumable": {
|
||||
"templates": ["itemDescription", "physicalItem", "activatedEffect", "action"],
|
||||
"consumableType": "potion",
|
||||
|
@ -641,7 +649,7 @@
|
|||
},
|
||||
"species": {
|
||||
"templates": ["speciesDescription"]
|
||||
},
|
||||
},
|
||||
"tool": {
|
||||
"templates": ["itemDescription", "physicalItem"],
|
||||
"ability": "int",
|
||||
|
|
|
@ -82,25 +82,25 @@
|
|||
</li>
|
||||
|
||||
<li class="attribute">
|
||||
<h4 class="attribute-name box-title">{{ localize "SW5E.Speed" }}</h4>
|
||||
<h4 class="attribute-name box-title">
|
||||
{{ localize "SW5E.Speed" }}
|
||||
</h4>
|
||||
<div class="attribute-value">
|
||||
<input name="data.attributes.speed.value" type="text"
|
||||
value="{{data.attributes.speed.value}}" placeholder="0"/>
|
||||
<span>{{movement.primary}}</span>
|
||||
</div>
|
||||
<footer class="attribute-footer">
|
||||
<input type="text" class="speed" name="data.attributes.speed.special"
|
||||
value="{{data.attributes.speed.special}}" placeholder="{{ localize 'SW5E.SpeedSpecial' }}"/>
|
||||
<span>{{movement.special}}</span>
|
||||
</footer>
|
||||
</li>
|
||||
|
||||
<li class="attribute initiative">
|
||||
<h4 class="attribute-name box-title">{{ localize "SW5E.Initiative" }}</h4>
|
||||
<h4 class="attribute-name box-title rollable" data-action="rollInitiative">{{ localize "SW5E.Initiative" }}</h4>
|
||||
<div class="attribute-value">
|
||||
<span>{{numberFormat data.attributes.init.total decimals=0 sign=true}}</span>
|
||||
</div>
|
||||
<footer class="attribute-footer">
|
||||
<span>{{ localize "SW5E.Modifier" }}</span>
|
||||
<input name="data.attributes.init.value" type="number" placeholder="0"
|
||||
<input name="data.attributes.init.value" type="text" data-dtype="Number" placeholder="0"
|
||||
value="{{numberFormat data.attributes.init.value decimals=0 sign=true}}"/>
|
||||
</footer>
|
||||
</li>
|
||||
|
@ -108,17 +108,17 @@
|
|||
</section>
|
||||
</header>
|
||||
|
||||
{{!-- NPC Sheet Navigation --}}
|
||||
{{!-- Character Sheet Navigation --}}
|
||||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="attributes">{{ localize "SW5E.Attributes" }}</a>
|
||||
<a class="item" data-tab="inventory">{{ localize "SW5E.Inventory" }}</a>
|
||||
<a class="item" data-tab="features">{{ localize "SW5E.Features" }}</a>
|
||||
<a class="item" data-tab="powerbook">{{ localize "SW5E.Powerbook" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<a class="item" data-tab="biography">{{ localize "SW5E.Biography" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- NPC Sheet Body --}}
|
||||
{{!-- Character Sheet Body --}}
|
||||
<section class="sheet-body">
|
||||
|
||||
<div class="tab attributes flexrow" data-group="primary" data-tab="attributes">
|
||||
|
@ -183,7 +183,7 @@
|
|||
{{!-- Counters --}}
|
||||
<div class="counters">
|
||||
<div class="counter flexrow death-saves">
|
||||
<h4 class="death-save rollable">{{ localize "SW5E.DeathSave" }}</h4>
|
||||
<h4 class="rollable" data-action="rollDeathSave">{{ localize "SW5E.DeathSave" }}</h4>
|
||||
<div class="counter-value">
|
||||
<i class="fas fa-check"></i>
|
||||
<input name="data.attributes.death.success" type="number" placeholder="0"
|
||||
|
@ -228,12 +228,12 @@
|
|||
<div class="tab powerbook flexcol" data-group="primary" data-tab="powerbook">
|
||||
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-powerbook.html"}}
|
||||
</div>
|
||||
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-effects.html"}}
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
|
||||
{{!-- Biography Tab --}}
|
||||
<div class="tab flexrow" data-group="primary" data-tab="biography">
|
||||
<div class="characteristics flexcol">
|
||||
|
|
|
@ -62,14 +62,14 @@
|
|||
</li>
|
||||
|
||||
<li class="attribute">
|
||||
<h4 class="attribute-name box-title">{{ localize "SW5E.Speed" }}</h4>
|
||||
<h4 class="attribute-name box-title">
|
||||
{{ localize "SW5E.Speed" }}
|
||||
</h4>
|
||||
<div class="attribute-value">
|
||||
<input name="data.attributes.speed.value" type="text"
|
||||
value="{{data.attributes.speed.value}}" placeholder="0"/>
|
||||
<span>{{movement.primary}}</span>
|
||||
</div>
|
||||
<footer class="attribute-footer">
|
||||
<input type="text" class="speed" name="data.attributes.speed.special"
|
||||
value="{{data.attributes.speed.special}}" placeholder="{{ localize 'SW5E.SpeedSpecial' }}"/>
|
||||
<span>{{movement.special}}</span>
|
||||
</footer>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -81,6 +81,7 @@
|
|||
<a class="item active" data-tab="attributes">{{ localize "SW5E.Attributes" }}</a>
|
||||
<a class="item" data-tab="features">{{ localize "SW5E.Features" }}</a>
|
||||
<a class="item" data-tab="powerbook">{{ localize "SW5E.Powerbook" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<a class="item" data-tab="biography">{{ localize "SW5E.Biography" }}</a>
|
||||
</nav>
|
||||
|
||||
|
@ -143,24 +144,29 @@
|
|||
<div class="counter-value">
|
||||
<input name="data.resources.lair.value" type="checkbox" value="{{data.resources.lair.value}}"
|
||||
data-dtype="Boolean" {{checked data.resources.lair.value}}/>
|
||||
<input name="data.resources.lair.initiative" type="number" value="{{data.resources.lair.initiative}}" placeholder="20"/>
|
||||
</div>
|
||||
<input name="data.resources.lair.initiative" type="number" value="{{data.resources.lair.initiative}}" placeholder="20"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Traits --}}
|
||||
{{> "systems/sw5e/templates/actors/parts/actor-traits.html"}}
|
||||
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-traits.html"}}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{{!-- Features Tab --}}
|
||||
<div class="tab features flexcol" data-group="primary" data-tab="features">
|
||||
{{> "systems/sw5e/templates/actors/parts/actor-features.html" sections=features}}
|
||||
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-features.html" sections=features}}
|
||||
</div>
|
||||
|
||||
{{!-- Powerbook Tab --}}
|
||||
<div class="tab powerbook flexcol" data-group="primary" data-tab="powerbook">
|
||||
{{> "systems/sw5e/templates/actors/parts/actor-powerbook.html"}}
|
||||
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-powerbook.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Biography Tab --}}
|
||||
|
@ -168,4 +174,4 @@
|
|||
{{editor content=data.details.biography.value target="data.details.biography.value" button=true owner=owner editable=editable}}
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</form>
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<ol class="inventory-list">
|
||||
{{#each effects as |section sid|}}
|
||||
<li class="inventory-header flexrow">
|
||||
<h3 class="effect-name flexrow">{{localize section.label}}</h3>
|
||||
<div class="effect-source">Source</div>
|
||||
<div class="effect-source">Duration</div>
|
||||
<div class="effect-controls"></div>
|
||||
</li>
|
||||
|
||||
<ol class="effects-list item-list">
|
||||
{{#each section.effects as |effect|}}
|
||||
<li class="effect flexrow" data-effect-id="{{effect.id}}">
|
||||
<div class="effect-name flexrow">
|
||||
<img class="effect-icon" src="{{effect.data.icon}}"/>
|
||||
<h4>{{effect.data.label}}</h4>
|
||||
</div>
|
||||
<div class="effect-source">{{effect.sourceName}}</div>
|
||||
<div class="effect-duration">{{effect.duration.label}}</div>
|
||||
<div class="effect-controls">
|
||||
<a class="effect-control" data-action="toggle"><i class="fas fa-circle-notch"></i></a>
|
||||
<a class="effect-control" data-action="edit"><i class="fas fa-edit"></i></a>
|
||||
<a class="effect-control" data-action="delete"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
{{/each}}
|
||||
</ol>
|
|
@ -8,9 +8,9 @@
|
|||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<ol class="inventory-list">
|
||||
<ol class="items-list inventory-list">
|
||||
{{#each sections as |section sid|}}
|
||||
<li class="inventory-header flexrow">
|
||||
<li class="items-header flexrow">
|
||||
<h3 class="item-name flexrow">{{localize section.label}}</h3>
|
||||
|
||||
{{#if section.hasActions}}
|
||||
|
@ -25,7 +25,7 @@
|
|||
{{/if}}
|
||||
|
||||
{{#if ../owner}}
|
||||
<div class="item-controls">
|
||||
<div class="item-controls flexrow">
|
||||
<a class="item-control item-create" title="{{localize 'SW5E.FeatureAdd'}}" {{#each section.dataset as |v k|}}data-{{k}}="{{v}}"{{/each}}>
|
||||
<i class="fas fa-plus"></i> {{localize "SW5E.Add"}}
|
||||
</a>
|
||||
|
@ -78,7 +78,7 @@
|
|||
<div class="item-detail player-classfeatures">
|
||||
{{item.data.name}}
|
||||
</div>
|
||||
|
||||
|
||||
{{else if section.isFightingstyles}}
|
||||
<div class="item-detail player-fightingstyles">
|
||||
{{item.data.name}}
|
||||
|
@ -119,7 +119,7 @@
|
|||
{{/if}}
|
||||
|
||||
{{#if ../../owner}}
|
||||
<div class="item-controls">
|
||||
<div class="item-controls flexrow">
|
||||
{{#if section.crewable}}
|
||||
<a class="item-control item-toggle {{item.toggleClass}}"
|
||||
title="{{item.toggleTitle}}">
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
<ol class="currency flexrow">
|
||||
<h3>
|
||||
{{localize "SW5E.Currency"}}
|
||||
<a class="currency-convert" title="Convert Currency"><i class="fas fa-coins"></i></a>
|
||||
</h3>
|
||||
{{#each data.currency as |v k|}}
|
||||
<label class="denomination {{k}}">{{ lookup ../config.currencies k }}</label>
|
||||
|
@ -23,9 +22,9 @@
|
|||
{{/unless}}
|
||||
</div>
|
||||
|
||||
<ol class="inventory-list">
|
||||
<ol class="items-list inventory-list">
|
||||
{{#each sections as |section sid|}}
|
||||
<li class="inventory-header flexrow">
|
||||
<li class="items-header flexrow">
|
||||
<h3 class="item-name flexrow">{{localize section.label}}</h3>
|
||||
|
||||
{{#if section.columns}}
|
||||
|
@ -33,18 +32,18 @@
|
|||
<div class="item-detail {{css}}">{{label}}</div>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{#if ../isCharacter}}
|
||||
<div class="item-detail item-weight">{{localize "SW5E.Weight"}}</div>
|
||||
{{/if}}
|
||||
{{#if ../isCharacter}}
|
||||
<div class="item-detail item-weight">{{localize "SW5E.Weight"}}</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="item-detail item-uses">{{localize "SW5E.Charges"}}</div>
|
||||
<div class="item-detail item-action">{{localize "SW5E.Usage"}}</div>
|
||||
<div class="item-detail item-uses">{{localize "SW5E.Charges"}}</div>
|
||||
<div class="item-detail item-action">{{localize "SW5E.Usage"}}</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if ../owner}}
|
||||
<div class="item-controls">
|
||||
<div class="item-controls flexrow">
|
||||
<a class="item-control item-create" title='{{localize "SW5E.ItemCreate"}}'
|
||||
{{#each section.dataset as |v k|}}data-{{k}}="{{v}}"{{/each}}>
|
||||
{{#each section.dataset as |v k|}}data-{{k}}="{{v}}"{{/each}}>
|
||||
<i class="fas fa-plus"></i> {{localize "SW5E.Add"}}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -53,13 +52,13 @@
|
|||
|
||||
<ol class="item-list">
|
||||
{{#each section.items as |item iid|}}
|
||||
<li class="item flexrow {{section.css}}"
|
||||
data-item-id="{{#if section.editableName}}{{iid}}{{else}}{{item._id}}{{/if}}">
|
||||
<div class="item-name flexrow rollable">
|
||||
{{#if section.editableName}}
|
||||
<input type="text" value="{{item.name}}">
|
||||
{{else}}
|
||||
<div class="item-image" style="background-image: url('{{item.img}}')"></div>
|
||||
<li class="item flexrow {{section.css}}"
|
||||
data-item-id="{{#if section.editableName}}{{iid}}{{else}}{{item._id}}{{/if}}">
|
||||
<div class="item-name flexrow rollable">
|
||||
{{#if section.editableName}}
|
||||
<input type="text" value="{{item.name}}">
|
||||
{{else}}
|
||||
<div class="item-image" style="background-image: url('{{item.img}}')"></div>
|
||||
<h4>
|
||||
{{item.name~}}
|
||||
{{~#if item.isStack}} ({{item.data.quantity}}){{/if}}
|
||||
|
@ -82,32 +81,32 @@
|
|||
</div>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{#if ../../isCharacter}}
|
||||
<div class="item-detail item-weight">
|
||||
{{#if item.totalWeight}}
|
||||
<div class="item-detail">
|
||||
{{ item.totalWeight }} {{localize "SW5E.AbbreviationLbs"}}
|
||||
{{#if ../../isCharacter}}
|
||||
<div class="item-detail item-weight">
|
||||
{{#if item.totalWeight}}
|
||||
<div class="item-detail">
|
||||
{{ item.totalWeight }} {{localize "SW5E.AbbreviationLbs"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="item-detail item-uses">
|
||||
{{#if item.hasUses }}
|
||||
<input type="text" value="{{item.data.uses.value}}" placeholder="0"/>
|
||||
/ {{item.data.uses.max}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="item-detail item-uses">
|
||||
{{#if item.hasUses }}
|
||||
<input type="text" value="{{item.data.uses.value}}" placeholder="0"/>
|
||||
/ {{item.data.uses.max}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="item-detail item-action">
|
||||
{{#if item.data.activation.type }}
|
||||
{{item.labels.activation}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="item-detail item-action">
|
||||
{{#if item.data.activation.type }}
|
||||
{{item.labels.activation}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if ../../owner}}
|
||||
<div class="item-controls">
|
||||
<div class="item-controls flexrow">
|
||||
{{#unless @root.isVehicle}}
|
||||
<a class="item-control item-toggle {{item.toggleClass}}" title='{{item.toggleTitle}}'><i class="fas fa-shield-alt"></i></a>
|
||||
{{/unless}}
|
||||
|
|
|
@ -27,12 +27,10 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<ol class="inventory-list">
|
||||
<ol class="items-list inventory-list">
|
||||
{{#each powerbook as |section|}}
|
||||
<li class="item flexrow inventory-header powerbook-header">
|
||||
<div class="item-name flexrow">
|
||||
<h3>{{section.label}}</h3>
|
||||
</div>
|
||||
<li class="items-header powerbook-header flexrow">
|
||||
<h3 class="item-name flexrow">{{section.label}}</h3>
|
||||
|
||||
<div class="power-slots">
|
||||
{{#if section.usesSlots}}
|
||||
|
@ -46,6 +44,7 @@
|
|||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{ else }}
|
||||
<span>{{{section.uses}}}</span>
|
||||
<span class="sep"> / </span>
|
||||
|
@ -57,7 +56,7 @@
|
|||
<div class="power-action">{{localize "SW5E.PowerUsage"}}</div>
|
||||
<div class="power-target">{{localize "SW5E.PowerTarget"}}</div>
|
||||
|
||||
<div class="item-controls">
|
||||
<div class="item-controls flexrow">
|
||||
{{#if section.canCreate}}
|
||||
<a class="item-control item-create" title="{{localize 'SW5E.PowerCreate'}}" {{#each section.dataset as |v k|}}data-{{k}}="{{v}}"{{/each}}>
|
||||
<i class="fas fa-plus"></i> {{localize "SW5E.Add"}}
|
||||
|
@ -91,9 +90,8 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
|
||||
|
||||
{{#if ../../owner}}
|
||||
<div class="item-controls">
|
||||
<div class="item-controls flexrow">
|
||||
{{#if section.canPrepare}}
|
||||
<a class="item-control item-toggle {{item.toggleClass}}" title="{{item.toggleTitle}}"><i class="fas fa-sun"></i></a>
|
||||
{{/if}}
|
||||
|
@ -110,7 +108,7 @@
|
|||
<li class="item flexrow"><p class="notes">{{localize "SW5E.FilterNoPowers"}}</p></li>
|
||||
{{else}}
|
||||
<li class="item flexrow inventory-header powerbook-header powerbook-empty">
|
||||
<div class="item-controls">
|
||||
<div class="item-controls flexrow">
|
||||
<a class="item-control item-create" title="{{localize 'SW5E.PowerCreate'}}" data-type="power"
|
||||
data-level="{{lvl}}"><i class="fas fa-plus"></i> {{localize "SW5E.PowerAdd"}}</a>
|
||||
</div>
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
</div>
|
||||
|
||||
{{#unless isVehicle}}
|
||||
<div class="form-group ">
|
||||
<label>{{localize "SW5E.MovementConfig"}}</label>
|
||||
<a class="configure-movement" title="{{localize 'SW5E.MovementConfig'}}"><i class="fas fa-cog"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="form-group {{#unless data.traits.senses}}inactive{{/unless}}">
|
||||
<label>{{localize "SW5E.Senses"}}</label>
|
||||
<input type="text" name="data.traits.senses" value="{{data.traits.senses}}" placeholder="{{ localize 'SW5E.None' }}"/>
|
||||
|
|
|
@ -58,8 +58,7 @@
|
|||
<li class="attribute">
|
||||
<h4 class="attribute-name box-title">{{localize 'SW5E.Speed'}}</h4>
|
||||
<div class="attribute-value">
|
||||
<input name="data.attributes.speed.value" type="text" placeholder="—"
|
||||
value="{{data.attributes.speed.value}}">
|
||||
<input name="data.attributes.speed" type="text" placeholder="—" value="{{data.attributes.speed}}"/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -139,16 +138,16 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{> 'systems/sw5e/templates/actors/parts/actor-traits.html'}}
|
||||
{{> 'systems/sw5e/templates/actors/oldActor/parts/actor-traits.html'}}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="tab features flexcol" data-group="primary" data-tab="features">
|
||||
{{> 'systems/sw5e/templates/actors/parts/actor-features.html' sections=features}}
|
||||
{{> 'systems/sw5e/templates/actors/oldActor/parts/actor-features.html' sections=features}}
|
||||
</div>
|
||||
|
||||
<div class="tab cargo flexcol" data-group="primary" data-tab="cargo">
|
||||
{{> 'systems/sw5e/templates/actors/parts/actor-inventory.html' sections=cargo}}
|
||||
{{> 'systems/sw5e/templates/actors/oldActor/parts/actor-inventory.html' sections=cargo}}
|
||||
</div>
|
||||
|
||||
<div class="tab biography flexcol" data-group="primary" data-tab="biography">
|
||||
|
|
38
templates/actors/parts/active-effects.html
Normal file
38
templates/actors/parts/active-effects.html
Normal file
|
@ -0,0 +1,38 @@
|
|||
<ol class="items-list effects-list">
|
||||
{{#each effects as |section sid|}}
|
||||
<li class="items-header flexrow" data-effect-type="{{section.type}}">
|
||||
<h3 class="item-name effect-name flexrow">{{localize section.label}}</h3>
|
||||
<div class="effect-source">Source</div>
|
||||
<div class="effect-source">Duration</div>
|
||||
<div class="item-controls effect-controls flexrow">
|
||||
<a class="effect-control" data-action="create" title="{{localize 'SW5E.EffectCreate'}}">
|
||||
<i class="fas fa-plus"></i> {{localize "SW5E.Add"}}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<ol class="item-list">
|
||||
{{#each section.effects as |effect|}}
|
||||
<li class="item effect flexrow" data-effect-id="{{effect.id}}">
|
||||
<div class="item-name effect-name flexrow">
|
||||
<img class="item-image" src="{{effect.data.icon}}"/>
|
||||
<h4>{{effect.data.label}}</h4>
|
||||
</div>
|
||||
<div class="effect-source">{{effect.sourceName}}</div>
|
||||
<div class="effect-duration">{{effect.duration.label}}</div>
|
||||
<div class="item-controls effect-controls flexrow">
|
||||
<a class="effect-control" data-action="toggle" title="{{localize 'SW5E.EffectToggle'}}">
|
||||
<i class="fas fa-circle-notch"></i>
|
||||
</a>
|
||||
<a class="effect-control" data-action="edit" title="{{localize 'SW5E.EffectEdit'}}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="effect-control" data-action="delete" title="{{localize 'SW5E.EffectDelete'}}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
{{/each}}
|
||||
</ol>
|
|
@ -1,44 +1,48 @@
|
|||
<form class="{{cssClass}}" autocomplete="off">
|
||||
<p class="notes">{{localize 'SW5E.FlagsInstructions'}}</p>
|
||||
<section class="form-body">
|
||||
<p class="notes">{{localize 'SW5E.FlagsInstructions'}}</p>
|
||||
|
||||
{{#each flags as |fs section|}}
|
||||
<h3 class="form-header">{{localize section}}</h3>
|
||||
{{#each fs as |flag key|}}
|
||||
{{#each flags as |fs section|}}
|
||||
<h3 class="form-header">{{localize section}}</h3>
|
||||
{{#each fs as |flag key|}}
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{localize flag.name}}</label>
|
||||
<div class="form-group">
|
||||
<label>{{localize flag.name}}</label>
|
||||
|
||||
{{#if flag.isCheckbox}}
|
||||
<input type="checkbox" name="{{key}}" data-dtype="Boolean" {{checked flag.value}}/>
|
||||
{{#if flag.isCheckbox}}
|
||||
<input type="checkbox" name="{{key}}" data-dtype="Boolean" {{checked flag.value}}/>
|
||||
|
||||
{{else if flag.isSelect}}
|
||||
<select name="{{key}}" data-dtype="{{flag.type}}">
|
||||
{{#select flag.value}}
|
||||
{{#each flag.choices as |v k|}}
|
||||
<option value="{{k}}">{{localize v}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
{{else if flag.isSelect}}
|
||||
<select name="{{key}}" data-dtype="{{flag.type}}">
|
||||
{{#select flag.value}}
|
||||
{{#each flag.choices as |v k|}}
|
||||
<option value="{{k}}">{{localize v}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
|
||||
{{else}}
|
||||
<input type="text" name="{{key}}" value="{{flag.value}}" placeholder="{{flag.placeholder}}" data-dtype="{{flag.type}}"/>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<input type="text" name="{{key}}" value="{{flag.value}}" placeholder="{{flag.placeholder}}" data-dtype="{{flag.type}}"/>
|
||||
{{/if}}
|
||||
|
||||
<p class="notes">{{localize flag.hint}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
<p class="notes">{{localize flag.hint}}</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
|
||||
<h3 class="form-header">{{localize "SW5E.Bonuses"}}</h3>
|
||||
<p class="notes">{{localize "SW5E.BonusesHint"}}</p>
|
||||
{{#each bonuses as |b|}}
|
||||
<div class="form-group">
|
||||
<label>{{localize b.label}}</label>
|
||||
<input type="text" name="{{b.name}}" value="{{b.value}}"/>
|
||||
</div>
|
||||
{{/each}}
|
||||
<h3 class="form-header">{{localize "SW5E.Bonuses"}}</h3>
|
||||
<p class="notes">{{localize "SW5E.BonusesHint"}}</p>
|
||||
{{#each bonuses as |b|}}
|
||||
<div class="form-group">
|
||||
<label>{{localize b.label}}</label>
|
||||
<input type="text" name="{{b.name}}" value="{{b.value}}"/>
|
||||
</div>
|
||||
{{/each}}
|
||||
</section>
|
||||
|
||||
<button type="submit" name="submit">
|
||||
<i class="far fa-save"></i> {{localize 'SW5E.FlagsSave'}}
|
||||
</button>
|
||||
<footer class="form-footer">
|
||||
<button type="submit" name="submit">
|
||||
<i class="far fa-save"></i> {{localize 'SW5E.FlagsSave'}}
|
||||
</button>
|
||||
</footer>
|
||||
</form>
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
<form id="cast-config-form">
|
||||
<p>{{ localize "SW5E.SpellCastHint" }} <strong>{{item.name}}</strong> {{ localize "SW5E.cast" }}.</p>
|
||||
|
||||
{{#unless canCast}}
|
||||
<p class="notification error">{{ localize "SW5E.SpellCastNoSlots" }}</p>
|
||||
{{/unless}}
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "SW5E.SpellCastUpcast" }}</label>
|
||||
<div class="form-fields">
|
||||
<select name="level">
|
||||
{{#select item.data.level}}
|
||||
{{#each castLevels as |l|}}
|
||||
<option value="{{l.level}}" {{#unless l.canCast}}disabled{{/unless}}>{{l.label}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{#if canUpcast}}
|
||||
<label class="checkbox">{{ localize "SW5E.SpellCastConsume" }} <input type="checkbox" name="consume" {{checked canCast}}/></label>
|
||||
{{/if}}
|
||||
|
||||
{{#if hasPlaceableTemplate}}
|
||||
<div class="form-group">
|
||||
<label class="checkbox">{{ localize "SW5E.PlaceTemplate" }}
|
||||
<input type="checkbox" name="placeTemplate" checked/>
|
||||
</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
34
templates/apps/movement-config.html
Normal file
34
templates/apps/movement-config.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<form autocomplete="off">
|
||||
<p class="notes">{{localize "SW5E.MovementConfigHint"}}</p>
|
||||
<div class="form-group">
|
||||
<label>{{localize "SW5E.MovementWalk"}}</label>
|
||||
<input name="data.attributes.movement.walk" type="number" step="0.1" value="{{movement.walk}}"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "SW5E.MovementBurrow"}}</label>
|
||||
<input name="data.attributes.movement.burrow" type="number" step="0.1" value="{{movement.burrow}}"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "SW5E.MovementClimb"}}</label>
|
||||
<input name="data.attributes.movement.climb" type="number" step="0.1" value="{{movement.climb}}"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "SW5E.MovementFly"}}</label>
|
||||
<input name="data.attributes.movement.fly" type="number" step="0.1" value="{{movement.fly}}"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "SW5E.MovementSwim"}}</label>
|
||||
<input name="data.attributes.movement.swim" type="number" step="0.1" value="{{movement.swim}}"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "SW5E.MovementUnits"}}</label>
|
||||
<select name="data.attributes.movement.units">
|
||||
{{selectOptions units selected=movement.units}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{localize "SW5E.MovementHover"}}</label>
|
||||
<input name="data.attributes.movement.hover" type="checkbox" {{checked movement.hover}}/>
|
||||
</div>
|
||||
<button type="submit" name="submit" value="1"><i class="far fa-save"></i> {{ localize "Submit"}}</button>
|
||||
</form>
|
|
@ -15,5 +15,5 @@
|
|||
<input type="text" name="custom" value="{{custom}}" data-dtype="String"/>
|
||||
</div>
|
||||
{{/if}}
|
||||
<button type="submit" name="submit" value="1"><i class="far fa-save"></i> {{ localize "SW5E.Save"}}</button>
|
||||
<button type="submit" name="submit" value="1"><i class="far fa-save"></i> {{ localize "SW5E.TraitSave"}}</button>
|
||||
</form>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="sw5e chat-card item-card" data-actor-id="{{actor._id}}" data-item-id="{{item._id}}" {{#if tokenId}}data-token-id="{{tokenId}}"{{/if}}>
|
||||
<header class="card-header flexrow">
|
||||
<img src="{{item.img}}" title="{{item.name}}" width="36" height="36"/>
|
||||
<h3>{{item.name}}</h3>
|
||||
<h3 class="item-name">{{item.name}}</h3>
|
||||
</header>
|
||||
|
||||
<div class="card-content">{{{data.description.value}}}</div>
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
{{!-- Item Sheet Navigation --}}
|
||||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<!-- <a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a> -->
|
||||
</nav>
|
||||
|
||||
|
@ -39,7 +40,13 @@
|
|||
|
||||
{{!-- Description Tab --}}
|
||||
<div class="tab description" data-group="primary" data-tab="description">
|
||||
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
|
||||
</div>
|
||||
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
{{!-- Item Sheet Navigation --}}
|
||||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<!-- <a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a> -->
|
||||
</nav>
|
||||
|
||||
|
@ -32,68 +33,42 @@
|
|||
|
||||
{{!-- Description Tab --}}
|
||||
<div class="tab description" data-group="primary" data-tab="description">
|
||||
<div class="background">{{editor content=data.flavorText.value target="data.flavorText.value" button=true editable=editable}}</div>
|
||||
<div class="background"><p><strong>Skill Proficiencies:</strong> {{{data.skillProficiencies.value}}}</p></div>
|
||||
<div class="background"><p><strong>Tool Proficiencies:</strong> {{{data.toolProficiencies.value}}}</p></div>
|
||||
<div class="background"><p><strong>Languages:</strong> {{{data.languages.value}}}</p></div>
|
||||
<div class="background"><p><strong>Equipment:</strong> {{{data.equipment.value}}}</p></div>
|
||||
<div class="background"><h3>{{{data.flavorName.value}}}</h3></div>
|
||||
<div class="background"><p>{{{data.flavorDescription.value}}}</p></div>
|
||||
<div class="smalltable"><p>{{{data.flavorOptions.value}}}</p></div>
|
||||
<div class="background"><h2>Feature: {{{data.featureName.value}}}</h2></div>
|
||||
<div class="background"><p>{{{data.featureText.value}}}</p></div>
|
||||
<h2>Background Feat</h2>
|
||||
<p>As a further embodiment of the experience and training of your background, you can choose from the following feats:</p>
|
||||
<div class="smalltable"><p>{{{data.featOptions.value}}}</p></div>
|
||||
<h3>Suggested Characteristics</h3>
|
||||
<div class="background"><p>{{{data.suggestedCharacteristics.value}}}</p></div>
|
||||
<div class="medtable"><p>{{{data.personalityTraitOptions.value}}}</p></div><p> </p>
|
||||
<div class="medtable"><p>{{{data.idealOptions.value}}}</p></div><p> </p>
|
||||
<div class="medtable"><p>{{{data.flawOptions.value}}}</p></div><p> </p>
|
||||
<div class="medtable"><p>{{{data.bondOptions.value}}}</p></div>
|
||||
<div class="background">{{editor content=data.flavorText.value target="data.flavorText.value" button=true editable=editable}}</div>
|
||||
<div class="background"><p><strong>Skill Proficiencies:</strong> {{{data.skillProficiencies.value}}}</p></div>
|
||||
<div class="background"><p><strong>Tool Proficiencies:</strong> {{{data.toolProficiencies.value}}}</p></div>
|
||||
<div class="background"><p><strong>Languages:</strong> {{{data.languages.value}}}</p></div>
|
||||
<div class="background"><p><strong>Equipment:</strong> {{{data.equipment.value}}}</p></div>
|
||||
<div class="background"><h3>{{{data.flavorName.value}}}</h3></div>
|
||||
<div class="background"><p>{{{data.flavorDescription.value}}}</p></div>
|
||||
<div class="smalltable"><p>{{{data.flavorOptions.value}}}</p></div>
|
||||
<div class="background"><h2>Feature: {{{data.featureName.value}}}</h2></div>
|
||||
<div class="background"><p>{{{data.featureText.value}}}</p></div>
|
||||
<h2>Background Feat</h2>
|
||||
<p>As a further embodiment of the experience and training of your background, you can choose from the following feats:</p>
|
||||
<div class="smalltable"><p>{{{data.featOptions.value}}}</p></div>
|
||||
<h3>Suggested Characteristics</h3>
|
||||
<div class="background"><p>{{{data.suggestedCharacteristics.value}}}</p></div>
|
||||
<div class="medtable"><p>{{{data.personalityTraitOptions.value}}}</p></div><p> </p>
|
||||
<div class="medtable"><p>{{{data.idealOptions.value}}}</p></div><p> </p>
|
||||
<div class="medtable"><p>{{{data.flawOptions.value}}}</p></div><p> </p>
|
||||
<div class="medtable"><p>{{{data.bondOptions.value}}}</p></div>
|
||||
|
||||
<script>
|
||||
let nullField = document.querySelectorAll('.background > div');
|
||||
<script>
|
||||
let nullField = document.querySelectorAll('.background > div');
|
||||
|
||||
nullField.forEach(function(element) {
|
||||
|
||||
if (element.value === null) {
|
||||
element.previousElementSibling.style.display = 'none';
|
||||
element.style.display = 'none';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<!-- {{> "systems/sw5e/templates/items/parts/item-description.html"}}
|
||||
|
||||
{{!-- Details Tab --}}
|
||||
<div class="tab details" data-group="primary" data-tab="details">
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.FeatureUsage" }}</h3>
|
||||
|
||||
{{!-- Item Activation Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-activation.html"}}
|
||||
|
||||
{{!-- Recharge Requirement --}}
|
||||
{{#if data.activation.type}}
|
||||
<div class="form-group recharge">
|
||||
<label>{{ localize "SW5E.FeatureActionRecharge" }}</label>
|
||||
<div class="form-fields">
|
||||
<span>{{ localize "SW5E.FeatureRechargeOn" }}</span>
|
||||
<input type="text" name="data.recharge.value" value="{{data.recharge.value}}"
|
||||
data-dtype="Number" placeholder="{{ localize 'SW5E.FeatureRechargeResult' }}"/>
|
||||
<label class="checkbox">
|
||||
{{ localize "SW5E.Charged" }}
|
||||
<input type="checkbox" name="data.recharge.charged" {{checked data.recharge.charged}}/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.FeatureAttack" }}</h3>
|
||||
|
||||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div> -->
|
||||
nullField.forEach(function(element) {
|
||||
if (element.value === null) {
|
||||
element.previousElementSibling.style.display = 'none';
|
||||
element.style.display = 'none';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
<form class="{{cssClass}} flexcol" autocomplete="off">
|
||||
|
||||
{{!-- Item Sheet Header --}}
|
||||
<header class="sheet-header flexrow">
|
||||
<img class="profile" src="{{item.img}}" title="{{item.name}}" data-edit="img"/>
|
||||
|
||||
<div class="header-details flexrow">
|
||||
<h1 class="charname">
|
||||
<input name="name" type="text" value="{{item.name}}" placeholder="{{ localize 'SW5E.CastName' }}"/>
|
||||
</h1>
|
||||
|
||||
<div class="item-subtitle">
|
||||
<h4 class="item-type">{{itemType}}</h4>
|
||||
<span class="item-status">{{itemStatus}}</span>
|
||||
</div>
|
||||
|
||||
<ul class="summary">
|
||||
<li>
|
||||
{{labels.level}}
|
||||
</li>
|
||||
<li>
|
||||
{{labels.school}}
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" name="data.source" value="{{data.source}}" placeholder="{{ localize 'SW5E.Source' }}"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{!-- Item Sheet Navigation --}}
|
||||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
<section class="sheet-body">
|
||||
|
||||
{{!-- Description Tab --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-description.html"}}
|
||||
|
||||
{{!-- Details Tab --}}
|
||||
<div class="tab details" data-group="primary" data-tab="details">
|
||||
<h3 class="form-header">{{ localize "SW5E.CastDetails" }}</h3>
|
||||
|
||||
{{!-- Cast Level --}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize "SW5E.CastLevel" }}</label>
|
||||
<select name="data.level" data-dtype="Number">
|
||||
{{#select data.level}}
|
||||
{{#each config.castLevels as |name lvl|}}
|
||||
<option value="{{lvl}}">{{name}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{!-- Cast School --}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize "SW5E.CastSchool" }}</label>
|
||||
<select name="data.school">
|
||||
{{#select data.school}}
|
||||
{{#each config.castSchools as |name sch|}}
|
||||
<option value="{{sch}}">{{name}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{!-- Cast Components --}}
|
||||
<div class="cast-components form-group stacked">
|
||||
<label>{{ localize "SW5E.CastComponents" }}</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="data.components.vocal" {{checked data.components.vocal}}/> {{ localize "SW5E.ComponentVerbal" }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="data.components.somatic" {{checked data.components.somatic}}/> {{ localize "SW5E.ComponentSomatic" }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="data.components.material" {{checked data.components.material}}/> {{ localize "SW5E.ComponentMaterial" }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="data.components.concentration" {{checked data.components.concentration}}/> {{ localize "SW5E.Concentration" }}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="data.components.ritual" {{checked data.components.ritual}}/> {{ localize "SW5E.Ritual" }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{{!-- Material Components --}}
|
||||
<div class="form-group stacked">
|
||||
<label>{{ localize "SW5E.CastMaterials" }}</label>
|
||||
<input class="materials" type="text" name="data.materials.value" value="{{data.materials.value}}"/>
|
||||
{{#if data.materials.value}}
|
||||
<div class="cast-materials flexrow">
|
||||
<label>{{ localize "SW5E.Supply" }}</label>
|
||||
<input type="text" name="data.materials.supply" value="{{data.materials.supply}}" data-dtype="Number" Placeholder="0"/>
|
||||
<label>{{ localize "SW5E.CostGP" }}</label>
|
||||
<input type="text" name="data.materials.cost" value="{{data.materials.cost}}" data-dtype="Number" Placeholder="-"/>
|
||||
<label>{{ localize "SW5E.Consumed" }}</label>
|
||||
<input type="checkbox" name="data.materials.consumed" {{checked data.materials.consumed}}/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{!-- Preparation Mode --}}
|
||||
<div class="form-group input-select">
|
||||
<label>{{ localize "SW5E.CastPreparationMode" }}</label>
|
||||
<div class="form-fields">
|
||||
<label class="checkbox prepared">
|
||||
{{ localize "SW5E.CastPrepared" }} <input type="checkbox" name="data.preparation.prepared" {{checked data.preparation.prepared}}/>
|
||||
</label>
|
||||
<select name="data.preparation.mode">
|
||||
{{#select data.preparation.mode}}
|
||||
<option value=""></option>
|
||||
{{#each config.castPreparationModes as |name key|}}
|
||||
<option value="{{key}}">{{name}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.CastingHeader" }}</h3>
|
||||
|
||||
{{!-- Item Activation Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-activation.html"}}
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.CastEffects" }}</h3>
|
||||
|
||||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
|
||||
{{!-- Cast Level Scaling --}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize "SW5E.LevelScaling" }}</label>
|
||||
<div class="form-fields">
|
||||
<select name="data.scaling.mode">
|
||||
{{#select data.scaling.mode}}
|
||||
{{#each config.castScalingModes as |name key|}}
|
||||
<option value="{{key}}">{{name}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
<input type="text" name="data.scaling.formula" value="{{data.scaling.formula}}" placeholder="{{ localize 'SW5E.ScalingFormula' }}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
|
@ -32,6 +32,7 @@
|
|||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
|
@ -69,5 +70,11 @@
|
|||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
|
@ -86,5 +87,10 @@
|
|||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
|
@ -142,5 +143,10 @@
|
|||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
|
@ -69,5 +70,11 @@
|
|||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
{{!-- Item Sheet Navigation --}}
|
||||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<!-- <a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a> -->
|
||||
</nav>
|
||||
|
||||
|
@ -36,36 +37,11 @@
|
|||
{{editor content=data.description.value target="data.description.value" button=true editable=editable}}
|
||||
|
||||
</div>
|
||||
<!-- {{> "systems/sw5e/templates/items/parts/item-description.html"}}
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Details Tab --}}
|
||||
<div class="tab details" data-group="primary" data-tab="details">
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.FeatureUsage" }}</h3>
|
||||
|
||||
{{!-- Item Activation Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-activation.html"}}
|
||||
|
||||
{{!-- Recharge Requirement --}}
|
||||
{{#if data.activation.type}}
|
||||
<div class="form-group recharge">
|
||||
<label>{{ localize "SW5E.FeatureActionRecharge" }}</label>
|
||||
<div class="form-fields">
|
||||
<span>{{ localize "SW5E.FeatureRechargeOn" }}</span>
|
||||
<input type="text" name="data.recharge.value" value="{{data.recharge.value}}"
|
||||
data-dtype="Number" placeholder="{{ localize 'SW5E.FeatureRechargeResult' }}"/>
|
||||
<label class="checkbox">
|
||||
{{ localize "SW5E.Charged" }}
|
||||
<input type="checkbox" name="data.recharge.charged" {{checked data.recharge.charged}}/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.FeatureAttack" }}</h3>
|
||||
|
||||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div> -->
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
{{!-- Item Sheet Navigation --}}
|
||||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<!-- <a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a> -->
|
||||
</nav>
|
||||
|
||||
|
@ -36,36 +37,11 @@
|
|||
{{editor content=data.description.value target="data.description.value" button=true editable=editable}}
|
||||
|
||||
</div>
|
||||
<!-- {{> "systems/sw5e/templates/items/parts/item-description.html"}}
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Details Tab --}}
|
||||
<div class="tab details" data-group="primary" data-tab="details">
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.FeatureUsage" }}</h3>
|
||||
|
||||
{{!-- Item Activation Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-activation.html"}}
|
||||
|
||||
{{!-- Recharge Requirement --}}
|
||||
{{#if data.activation.type}}
|
||||
<div class="form-group recharge">
|
||||
<label>{{ localize "SW5E.FeatureActionRecharge" }}</label>
|
||||
<div class="form-fields">
|
||||
<span>{{ localize "SW5E.FeatureRechargeOn" }}</span>
|
||||
<input type="text" name="data.recharge.value" value="{{data.recharge.value}}"
|
||||
data-dtype="Number" placeholder="{{ localize 'SW5E.FeatureRechargeResult' }}"/>
|
||||
<label class="checkbox">
|
||||
{{ localize "SW5E.Charged" }}
|
||||
<input type="checkbox" name="data.recharge.charged" {{checked data.recharge.charged}}/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.FeatureAttack" }}</h3>
|
||||
|
||||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div> -->
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
{{!-- Item Sheet Navigation --}}
|
||||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<!-- <a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a> -->
|
||||
</nav>
|
||||
|
||||
|
@ -36,36 +37,11 @@
|
|||
{{editor content=data.description.value target="data.description.value" button=true editable=editable}}
|
||||
|
||||
</div>
|
||||
<!-- {{> "systems/sw5e/templates/items/parts/item-description.html"}}
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Details Tab --}}
|
||||
<div class="tab details" data-group="primary" data-tab="details">
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.FeatureUsage" }}</h3>
|
||||
|
||||
{{!-- Item Activation Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-activation.html"}}
|
||||
|
||||
{{!-- Recharge Requirement --}}
|
||||
{{#if data.activation.type}}
|
||||
<div class="form-group recharge">
|
||||
<label>{{ localize "SW5E.FeatureActionRecharge" }}</label>
|
||||
<div class="form-fields">
|
||||
<span>{{ localize "SW5E.FeatureRechargeOn" }}</span>
|
||||
<input type="text" name="data.recharge.value" value="{{data.recharge.value}}"
|
||||
data-dtype="Number" placeholder="{{ localize 'SW5E.FeatureRechargeResult' }}"/>
|
||||
<label class="checkbox">
|
||||
{{ localize "SW5E.Charged" }}
|
||||
<input type="checkbox" name="data.recharge.charged" {{checked data.recharge.charged}}/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.FeatureAttack" }}</h3>
|
||||
|
||||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div> -->
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -25,8 +25,26 @@
|
|||
</div>
|
||||
</header>
|
||||
|
||||
{{!-- Item Sheet Navigation --}}
|
||||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
<!-- <a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a> -->
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
<section class="sheet-body">
|
||||
{{> "systems/sw5e/templates/items/parts/item-description.html"}}
|
||||
|
||||
{{!-- Description Tab --}}
|
||||
<div class="tab description" data-group="primary" data-tab="description">
|
||||
{{> "systems/sw5e/templates/items/parts/item-description.html"}}
|
||||
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -112,8 +112,9 @@
|
|||
<label>{{ localize "SW5E.LimitedUses" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="data.uses.value" value="{{data.uses.value}}" data-dtype="Number"/>
|
||||
<span class="sep"> {{ localize "SW5E.of" }} </span>
|
||||
<span class="sep">{{ localize "SW5E.of" }}</span>
|
||||
<input type="text" name="data.uses.max" value="{{data.uses.max}}" data-dtype="Number"/>
|
||||
<span class="sep">{{ localize "SW5E.per" }}</span>
|
||||
<select name="data.uses.per">
|
||||
{{#select data.uses.per}}
|
||||
<option value=""></option>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
|
@ -68,6 +69,30 @@
|
|||
</select>
|
||||
</div>
|
||||
|
||||
{{!-- Power Components --}}
|
||||
<div class="power-components form-group stacked">
|
||||
<label>{{ localize "SW5E.PowerComponents" }}</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="data.components.concentration" {{checked data.components.concentration}}/> {{ localize "SW5E.Concentration" }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{{!-- Material Components --}}
|
||||
<div class="form-group stacked">
|
||||
<label>{{ localize "SW5E.PowerMaterials" }}</label>
|
||||
<input class="materials" type="text" name="data.materials.value" value="{{data.materials.value}}"/>
|
||||
{{#if data.materials.value}}
|
||||
<div class="power-materials flexrow">
|
||||
<label>{{ localize "SW5E.Supply" }}</label>
|
||||
<input type="text" name="data.materials.supply" value="{{data.materials.supply}}" data-dtype="Number" Placeholder="0"/>
|
||||
<label>{{ localize "SW5E.CostGP" }}</label>
|
||||
<input type="text" name="data.materials.cost" value="{{data.materials.cost}}" data-dtype="Number" Placeholder="-"/>
|
||||
<label>{{ localize "SW5E.Consumed" }}</label>
|
||||
<input type="checkbox" name="data.materials.consumed" {{checked data.materials.consumed}}/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{!-- Preparation Mode --}}
|
||||
<div class="form-group input-select">
|
||||
<label>{{ localize "SW5E.PowerPreparationMode" }}</label>
|
||||
|
@ -76,24 +101,10 @@
|
|||
{{ localize "SW5E.PowerPrepared" }} <input type="checkbox" name="data.preparation.prepared" {{checked data.preparation.prepared}}/>
|
||||
</label>
|
||||
<select name="data.preparation.mode">
|
||||
{{#select data.preparation.mode}}
|
||||
<option value=""></option>
|
||||
{{#each config.powerPreparationModes as |name key|}}
|
||||
<option value="{{key}}">{{name}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
{{ selectOptions config.powerPreparationModes selected=data.preparation.mode }}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{!-- Concentration Mode --}}
|
||||
<div class="form-group input-select">
|
||||
<label>{{ localize "SW5E.PowerConcentrationMode" }}</label>
|
||||
<div class="form-fields">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="data.components.concentration" {{checked data.components.concentration}}/> {{ localize "SW5E.Concentrated" }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="form-header">{{ localize "SW5E.PowerCastingHeader" }}</h3>
|
||||
|
||||
|
@ -120,5 +131,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">Description</a>
|
||||
<a class="item" data-tab="species-traits">Species Traits</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
|
@ -110,8 +111,11 @@
|
|||
{{editor content=data.traits.value target="data.traits.value" button=true editable=editable}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</form>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<nav class="sheet-navigation tabs" data-group="primary">
|
||||
<a class="item active" data-tab="description">{{ localize "SW5E.Description" }}</a>
|
||||
<a class="item" data-tab="details">{{ localize "SW5E.Details" }}</a>
|
||||
<a class="item" data-tab="effects">{{ localize "SW5E.Effects" }}</a>
|
||||
</nav>
|
||||
|
||||
{{!-- Item Sheet Body --}}
|
||||
|
@ -110,5 +111,10 @@
|
|||
{{!-- Item Action Template --}}
|
||||
{{> "systems/sw5e/templates/items/parts/item-action.html"}}
|
||||
</div>
|
||||
|
||||
{{!-- Effects Tab --}}
|
||||
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||
{{> "systems/sw5e/templates/actors/parts/active-effects.html"}}
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue