switch to using TypeScript

This commit is contained in:
Johannes Loher 2020-12-23 16:52:20 +01:00
parent 1d120b273a
commit d163fd27fe
53 changed files with 2875 additions and 1614 deletions

20
src/ds4.scss Normal file
View file

@ -0,0 +1,20 @@
// Import utilities.
@import "scss/utils/typography";
@import "scss/utils/colors";
@import "scss/utils/mixins";
@import "scss/utils/variables";
/* Global styles */
@import "scss/global/window";
@import "scss/global/grid";
@import "scss/global/flex";
/* Styles limited to ds4 sheets */
.ds4 {
@import "scss/components/apps";
@import "scss/components/forms";
@import "scss/components/basic_property";
@import "scss/components/tabs";
@import "scss/components/items";
@import "scss/components/description";
}

BIN
src/fonts/Woodstamp.otf Normal file

Binary file not shown.

BIN
src/fonts/Woodstamp.woff Normal file

Binary file not shown.

39
src/lang/en.json Normal file
View file

@ -0,0 +1,39 @@
{
"DS4.Description": "Description",
"DS4.Details": "Details",
"DS4.AttackType": "Attack Type",
"DS4.AttackTypeAbbr": "AT",
"DS4.WeaponBonus": "Weapon Bonus",
"DS4.WeaponBonusAbbr": "WB",
"DS4.OpponentDefense": "Opponent Defense",
"DS4.OpponentDefenseAbbr": "OD",
"DS4.AttackTypeMelee": "Melee",
"DS4.AttackTypeRanged": "Ranged",
"DS4.AttackTypeMeleeRanged": "Melee / Ranged",
"DS4.Quantity": "Quantity",
"DS4.PriceGold": "Price (Gold)",
"DS4.ItemAvailability": "Availability",
"DS4.ItemAvailabilityHamlet": "Hamlet",
"DS4.ItemAvailabilityVilage": "Village",
"DS4.ItemAvailabilityCity": "City",
"DS4.ItemAvailabilityElves": "Elves",
"DS4.ItemAvailabilityDwarves": "Dwarves",
"DS4.ItemAvailabilityNone": "None",
"DS4.ItemTypeWeapon": "Weapon",
"DS4.ItemTypeArmor": "Armor",
"DS4.ItemTypeShield": "Shield",
"DS4.ItemTypeTrinket": "Trinket",
"DS4.ItemTypeEquipment": "Equipment",
"DS4.ArmorType": "Armor Type",
"DS4.ArmorMaterialType": "Material Type",
"DS4.ArmorValue": "Armor Value",
"DS4.ArmorTypeBody": "Body",
"DS4.ArmorTypeHelmet": "Helmet",
"DS4.ArmorTypeVambrace": "Vambrace",
"DS4.ArmorTypeGreaves": "Greaves",
"DS4.ArmorTypeVambraceGreaves": "Vambrace + Greaves",
"DS4.ArmorMaterialTypeCloth": "Cloth",
"DS4.ArmorMaterialTypeLeather": "Leather",
"DS4.ArmorMaterialTypeChain": "Chain",
"DS4.ArmorMaterialTypePlate": "Plate"
}

View file

@ -0,0 +1,110 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class DS4ActorSheet extends ActorSheet<{
/* TODO: add actual type for data */
}> {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["ds4", "sheet", "actor"],
template: "systems/ds4/templates/actor/actor-sheet.html",
width: 600,
height: 600,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
});
}
/* -------------------------------------------- */
/** @override */
getData() {
// TODO: replace ["..."] access with .
const data = super.getData();
data["dtypes"] = ["String", "Number", "Boolean"];
const innerData = data.data;
for (let attr of Object.values(data.data["attributes"])) {
attr["isCheckbox"] = attr["dtype"] === "Boolean";
}
console.log(data);
return data;
}
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Add Inventory Item
html.find(".item-create").click(this._onItemCreate.bind(this));
// Update Inventory Item
html.find(".item-edit").click((ev) => {
const li = $(ev.currentTarget).parents(".item");
const item = this.actor.getOwnedItem(li.data("itemId"));
item.sheet.render(true);
});
// Delete Inventory Item
html.find(".item-delete").click((ev) => {
const li = $(ev.currentTarget).parents(".item");
this.actor.deleteOwnedItem(li.data("itemId"));
li.slideUp(200, () => this.render(false));
});
// Rollable abilities.
html.find(".rollable").click(this._onRoll.bind(this));
}
/* -------------------------------------------- */
/**
* Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset
* @param {Event} event The originating click event
* @private
*/
_onItemCreate(event) {
event.preventDefault();
const header = event.currentTarget;
// Get the type of item to create.
const type = header.dataset.type;
// Grab any data associated with this control.
const data = duplicate(header.dataset);
// Initialize a default name.
const name = `New ${type.capitalize()}`;
// Prepare the item object.
const itemData = {
name: name,
type: type,
data: data,
};
// Remove the type from the dataset since it's in the itemData.type prop.
delete itemData.data["type"];
// Finally, create the item!
return this.actor.createOwnedItem(itemData);
}
/**
* Handle clickable rolls.
* @param {Event} event The originating click event
* @private
*/
_onRoll(event) {
event.preventDefault();
const element = event.currentTarget;
const dataset = element.dataset;
if (dataset.roll) {
let roll = new Roll(dataset.roll, this.actor.data.data);
let label = dataset.label ? `Rolling ${dataset.label}` : "";
roll.roll().toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
flavor: label,
});
}
}
}

38
src/module/actor/actor.ts Normal file
View file

@ -0,0 +1,38 @@
/**
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
* @extends {Actor}
*/
export class DS4Actor extends Actor {
/** @override */
prepareDerivedData() {
const data = this.data;
this._prepareCombatValues(data);
}
_prepareCombatValues(data) {
const hitPointsModifier = getProperty(data, "data.combatValues.hitPoints.modifier") || 0;
setProperty(
data,
"data.combatValues.hitPoints.max",
data.data.attributes.body.initial + data.data.traits.constitution.initial + 10 + hitPointsModifier
);
const defenseModifier = getProperty(data, "data.combatValues.defense.modifier") || 0;
setProperty(
data,
"data.combatValues.defense.value",
data.data.attributes.body.initial +
data.data.traits.constitution.initial +
this._getArmorValue() +
defenseModifier
);
}
_getArmorValue() {
return this.data["items"]
.filter((item) => ["armor", "shield"].includes(item.type))
.filter((item) => item.data.equipped)
.map((item) => item.data.armorValue)
.reduce((a, b) => a + b, 0);
}
}

68
src/module/config.ts Normal file
View file

@ -0,0 +1,68 @@
export const DS4 = {
// ASCII Artwork
ASCII: `_____________________________________________________________________________________________
____ _ _ _ _ ____ _____ ___ _ _ ____ _ _ __ _______ ____ ____ _ _
| _ \\| | | | \\ | |/ ___| ____/ _ \\| \\ | / ___|| | / \\\\ \\ / / ____| _ \\/ ___| | || |
| | | | | | | \\| | | _| _|| | | | \\| \\___ \\| | / _ \\\\ V /| _| | |_) \\___ \\ | || |_
| |_| | |_| | |\\ | |_| | |__| |_| | |\\ |___) | |___ / ___ \\| | | |___| _ < ___) | |__ _|
|____/ \\___/|_| \\_|\\____|_____\\___/|_| \\_|____/|_____/_/ \\_\\_| |_____|_| \\_\\____/ |_|
=============================================================================================`,
/**
* Define the set of acttack types that can be performed with weapon items
* @type {Object}
*/
attackTypes: {
melee: "DS4.AttackTypeMelee",
ranged: "DS4.AttackTypeRanged",
meleeRanged: "DS4.AttackTypeMeleeRanged",
},
/**
* Define the set of item availabilties
* @type {Object}
*/
itemAvailabilities: {
hamlet: "DS4.ItemAvailabilityHamlet",
village: "DS4.ItemAvailabilityVilage",
city: "DS4.ItemAvailabilityCity",
elves: "DS4.ItemAvailabilityElves",
dwarves: "DS4.ItemAvailabilityDwarves",
none: "DS4.ItemAvailabilityNone",
},
/**
* * Define the set of item types
* @type {Object}
*/
itemTypes: {
weapon: "DS4.ItemTypeWeapon",
armor: "DS4.ItemTypeArmor",
shield: "DS4.ItemTypeShield",
trinket: "DS4.ItemTypeTrinket",
equipment: "DS4.ItemTypeEquipment",
},
/**
* * Define the set of armor types, a character may only wear one item of each at any given time
* @type {Object}
*/
armorTypes: {
body: "DS4.ArmorTypeBody",
helment: "DS4.ArmorTypeHelmet",
vambrace: "DS4.ArmorTypeVambrace",
greaves: "DS4.ArmorTypeGreaves",
vambraceGreaves: "DS4.ArmorTypeVambraceGreaves",
},
/**
* * Define the set of armor materials, used to determine if a characer may wear the armor without additional penalties
* @type {Object}
*/
armorMaterialTypes: {
cloth: "DS4.ArmorMaterialTypeCloth",
leather: "DS4.ArmorMaterialTypeLeather",
chain: "DS4.ArmorMaterialTypeChain",
plate: "DS4.ArmorMaterialTypePlate",
},
};

63
src/module/ds4.ts Normal file
View file

@ -0,0 +1,63 @@
// Import Modules
import { DS4Actor } from "./actor/actor.js";
import { DS4ActorSheet } from "./actor/actor-sheet.js";
import { DS4Item } from "./item/item.js";
import { DS4ItemSheet } from "./item/item-sheet.js";
import { DS4 } from "./config.js";
Hooks.once("init", async function () {
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
game.ds4 = {
DS4Actor,
DS4Item,
DS4,
};
// Record configuration
CONFIG.DS4 = DS4;
// Define custom Entity classes
CONFIG.Actor.entityClass = DS4Actor;
CONFIG.Item.entityClass = DS4Item;
// Register sheet application classes
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet("ds4", DS4ActorSheet, { makeDefault: true });
Items.unregisterSheet("core", ItemSheet);
Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true });
registerHandlebarsPartials();
});
async function registerHandlebarsPartials() {
const templatePaths = ["systems/ds4/templates/item/partials/description.hbs"];
return loadTemplates(templatePaths);
}
/* -------------------------------------------- */
/* Foundry VTT Setup */
/* -------------------------------------------- */
/**
* This function runs after game data has been requested and loaded from the servers, so entities exist
*/
Hooks.once("setup", function () {
// Localize CONFIG objects once up-front
const toLocalize = ["attackTypes", "itemAvailabilities", "itemTypes", "armorTypes", "armorMaterialTypes"];
// Exclude some from sorting where the default order matters
const noSort = [];
// Localize and sort CONFIG objects
for (let o of toLocalize) {
const localized = Object.entries(CONFIG.DS4[o]).map((e) => {
return [e[0], game.i18n.localize(e[1] as string)];
});
if (!noSort.includes(o)) localized.sort((a, b) => a[1].localeCompare(b[1]));
CONFIG.DS4[o] = localized.reduce((obj, e) => {
obj[e[0]] = e[1];
return obj;
}, {});
}
});

View file

@ -0,0 +1,85 @@
/**
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
*/
export class DS4ItemSheet extends ItemSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
width: 530,
height: 400,
classes: ["ds4", "sheet", "item"],
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
});
}
/** @override */
get template() {
const path = "systems/ds4/templates/item";
return `${path}/${this.item.data.type}-sheet.hbs`;
}
/* -------------------------------------------- */
/** @override */
getData() {
const data = { ...super.getData(), config: CONFIG.DS4 };
console.log(data);
return data;
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetBody = (this.element as JQuery).find(".sheet-body"); // TODO: Why is the cast necessary?
const bodyHeight = position.height - 192;
//sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
html.find(".effect-create").click(this._onEffectCreate.bind(this));
html.find(".effect-edit").click((ev) => {
const li = $(ev.currentTarget).parents(".effect");
console.log(li.data("effectId"));
const effect = this.item.effects.get(li.data("effectId"));
effect.sheet.render(true);
});
html.find(".effect-delete").click(async (ev) => {
const li = $(ev.currentTarget).parents(".effect");
await this.item.deleteEmbeddedEntity("ActiveEffect", li.data("effectId"));
});
}
/**
* Handle creating a new ActiveEffect for the item using initial data defined in the HTML dataset
* @param {Event} event The originating click event
* @private
*/
async _onEffectCreate(event) {
event.preventDefault();
const label = `New Effect`;
const createData = {
label: label,
changes: [],
duration: {},
transfer: true,
};
const effect = await ActiveEffect.create(createData, this.item);
return effect.create({});
}
}

17
src/module/item/item.ts Normal file
View file

@ -0,0 +1,17 @@
/**
* Extend the basic Item with some very simple modifications.
* @extends {Item}
*/
export class DS4Item extends Item {
/**
* Augment the basic Item data model with additional dynamic data.
*/
prepareData() {
super.prepareData();
// Get the Item's data
const itemData = this.data;
const actorData = this.actor ? this.actor.data : {};
const data = itemData.data;
}
}

View file

@ -0,0 +1,16 @@
.window-content {
overflow-y: hidden;
padding: 5px;
form {
height: 100%;
overflow: hidden;
}
.tab {
height: 100%;
overflow-y: auto;
align-content: flex-start;
}
}

View file

@ -0,0 +1,13 @@
.basic-properties {
flex: 0 0 100%;
.basic-property {
.basic-property-label {
font-weight: bold;
}
.basic-property-select {
display: block;
width: 100%;
}
}
}

View file

@ -0,0 +1,39 @@
.side-properties {
flex: 0 0 150px;
margin: 5px 5px 5px 0;
padding-right: 5px;
border-right: 2px groove $c-border-groove;
.side-property {
margin: 2px 0;
display: flex;
flex-direction: row;
label {
flex: 2;
line-height: 26px;
font-weight: bold;
}
input,
select {
text-align: right;
flex: 1;
width: calc(100% - 2px);
}
}
}
.sheet-body .tab .editor {
height: 100%;
}
.tox {
.tox-editor-container {
background: $c-white;
}
.tox-edit-area {
padding: 0 8px;
}
}

View file

@ -0,0 +1,62 @@
.item-form {
font-family: $font-primary;
}
$header-top-margin: 5px;
header.sheet-header {
flex: 0 0 210px;
overflow: hidden;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: flex-start;
.profile-img {
flex: 0 0 100px;
height: 100px;
margin: $header-top-margin 10px $header-top-margin 0;
}
.header-fields {
flex: 1;
}
h1.charname {
height: 50px;
padding: 0px;
margin: $header-top-margin 10px $header-top-margin 0;
border-bottom: 0;
input {
width: 100%;
height: 100%;
margin: 0;
border: none;
background-color: transparent;
}
font-family: $font-heading;
display: block;
}
h2.item-type {
font-family: $font-heading;
display: block;
height: 50px;
padding: 0px;
flex: 0 0 0;
color: $c-light-grey;
border: none;
line-height: 50px;
margin: $header-top-margin 0;
text-align: right;
}
}
.sheet-tabs {
flex: 0;
}
.sheet-body,
.sheet-body .tab {
height: 100%;
}

View file

@ -0,0 +1,35 @@
.items-list {
list-style: none;
margin: 7px 0;
padding: 0;
overflow-y: auto;
.item-header {
font-weight: bold;
}
.item {
height: 30px;
line-height: 24px;
padding: 3px 0;
border-bottom: 1px solid #BBB;
.item-image {
flex: 0 0 24px;
margin-right: 5px;
}
img {
display: block;
}
}
.item-name {
margin: 0;
}
.item-controls {
flex: 0 0 86px;
text-align: right;
}
}

View file

@ -0,0 +1,14 @@
nav.tabs {
height: 40px;
border-top: 2px groove $c-border-groove;
border-bottom: 2px groove $c-border-groove;
.item {
line-height: 40px;
font-weight: bold;
}
.item.active {
text-decoration: none;
}
}

View file

@ -0,0 +1,60 @@
/* ----------------------------------------- */
/* Flexbox */
/* ----------------------------------------- */
.flexrow {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
> * {
flex: 1;
}
.flex1 {
flex: 1;
}
.flex2 {
flex: 2;
}
.flex3 {
flex: 3;
}
.flex4 {
flex: 4;
}
}
.flexcol {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
> * {
flex: 1;
}
.flex1 {
flex: 1;
}
.flex2 {
flex: 2;
}
.flex3 {
flex: 3;
}
.flex4 {
flex: 4;
}
}
.flex-center {
align-items: center;
justify-content: center;
text-align: center;
}
.flex-between {
justify-content: space-between;
}

View file

@ -0,0 +1,83 @@
.grid,
.grid-2col {
display: grid;
grid-column: span 2 / span 2;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
padding: 0;
}
.grid-1col {
grid-column: span 1 / span 1;
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.grid-3col {
grid-column: span 3 / span 3;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid-4col {
grid-column: span 4 / span 4;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.grid-5col {
grid-column: span 5 / span 5;
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.grid-6col {
grid-column: span 5 / span 5;
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.grid-7col {
grid-column: span 7 / span 7;
grid-template-columns: repeat(7, minmax(0, 1fr));
}
.grid-8col {
grid-column: span 8 / span 8;
grid-template-columns: repeat(8, minmax(0, 1fr));
}
.grid-9col {
grid-column: span 9 / span 9;
grid-template-columns: repeat(9, minmax(0, 1fr));
}
.grid-10col {
grid-column: span 10 / span 10;
grid-template-columns: repeat(10, minmax(0, 1fr));
}
.grid-11col {
grid-column: span 11 / span 11;
grid-template-columns: repeat(11, minmax(0, 1fr));
}
.grid-12col {
grid-column: span 12 / span 12;
grid-template-columns: repeat(12, minmax(0, 1fr));
}
.flex-group-center,
.flex-group-left,
.flex-group-right {
justify-content: center;
align-items: center;
text-align: center;
padding: 5px;
border: 1px solid #999;
}
.flex-group-left {
justify-content: flex-start;
text-align: left;
}
.flex-group-right {
justify-content: flex-end;
text-align: right;
}

View file

@ -0,0 +1,12 @@
.window-app {
font-family: $font-primary;
}
.rollable {
&:hover,
&:focus {
color: #000;
text-shadow: 0 0 10px red;
cursor: pointer;
}
}

View file

@ -0,0 +1,4 @@
$c-white: #fff;
$c-black: #000;
$c-light-grey: #777;
$c-border-groove: #eeede0;

View file

@ -0,0 +1,16 @@
@mixin element-invisible {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
border: 0;
padding: 0;
clip: rect(0 0 0 0);
overflow: hidden;
}
@mixin hide {
display: none;
}

View file

@ -0,0 +1,12 @@
@import url("https://fonts.googleapis.com/css2?family=Lora:wght@400;700&display=swap");
@font-face {
font-family: "Wood Stamp";
font-style: normal;
font-weight: normal;
src: local("Wood Stamp"), url("fonts/Woodstamp.woff") format("woff");
}
$font-primary: "Lora", sans-serif;
$font-secondary: "Lora", sans-serif;
$font-heading: "Wood Stamp", sans-serif;

View file

@ -0,0 +1,3 @@
$padding-sm: 5px;
$padding-md: 10px;
$padding-lg: 20px;

28
src/system.json Normal file
View file

@ -0,0 +1,28 @@
{
"name": "ds4",
"title": "Dungeonslayers 4",
"description": "The Dungeonslayers 4 system for FoundryVTT!",
"version": "0.1.0",
"minimumCoreVersion": "0.7.9",
"compatibleCoreVersion": "0.7.9",
"templateVersion": 2,
"author": "Johannes Loher",
"esmodules": ["module/ds4.js"],
"styles": ["ds4.css"],
"scripts": [],
"packs": [],
"languages": [
{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}
],
"gridDistance": 1,
"gridUnits": "m",
"primaryTokenAttribute": "hitPoints",
"url": "",
"manifest": "",
"download": "",
"license": ""
}

79
src/template.json Normal file
View file

@ -0,0 +1,79 @@
{
"Actor": {
"types": ["character"],
"templates": {},
"character": {
"templates": [],
"attributes": {
"body": {
"initial": 8
},
"mobility": {
"initial": 0
},
"mind": {
"initial": 0
}
},
"traits": {
"strength": {
"initial": 4
},
"constitution": {
"initial": 0
},
"agility": {
"initial": 0
},
"dexterity": {
"initial": 0
},
"intellect": {
"initial": 0
},
"aura": {
"initial": 0
}
}
}
},
"Item": {
"types": ["weapon", "armor", "shield", "trinket", "equipment"],
"templates": {
"base": {
"description": ""
},
"physical": {
"quantity": 1,
"price": 0,
"availability": "none"
},
"equipable": {
"equipped": false
}
},
"weapon": {
"templates": ["base", "physical", "equipable"],
"attackType": "melee",
"weaponBonus": 0,
"opponentDefense": 0,
"properties": {}
},
"armor": {
"templates": ["base", "physical", "equipable"],
"armorMaterialType": "cloth",
"armorType": "body",
"armorValue": 0
},
"shield": {
"templates": ["base", "physical", "equipable"],
"armorValue": 0
},
"trinket": {
"templates": ["base", "physical", "equipable"]
},
"equipment": {
"templates": ["base", "physical"]
}
}
}

View file

@ -0,0 +1,90 @@
<form class="{{cssClass}} flexcol" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100" />
<div class="header-fields">
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name" /></h1>
{{!-- The grid classes are defined in scss/global/_grid.scss. To use, use both the "grid" and "grid-Ncol"
class where "N" can be any number from 1 to 12 and will create that number of columns. --}}
<div class="resources grid grid-2col">
{{!-- "flex-group-center" is also defined in the _grid.scss file and it will add a small amount of
padding, a border, and will center all of its child elements content and text. --}}
<div class="resource flex-group-center">
<label for="data.health.value" class="resource-label">Health</label>
<div class="resource-content flexrow flex-center flex-between">
<input type="text" name="data.health.value" value="{{data.health.value}}" data-dtype="Number" />
<span> / </span>
<input type="text" name="data.health.max" value="{{data.health.max}}" data-dtype="Number" />
</div>
</div>
<div class="resource flex-group-center">
<label for="data.power.value" class="resource-label">Power</label>
<div class="resource-content flexrow flex-center flex-between">
<input type="text" name="data.power.value" value="{{data.power.value}}" data-dtype="Number" />
<span> / </span>
<input type="text" name="data.power.max" value="{{data.power.max}}" data-dtype="Number" />
</div>
</div>
</div>
{{!-- The grid classes are defined in scss/global/_grid.scss. To use, use both the "grid" and "grid-Ncol"
class where "N" can be any number from 1 to 12 and will create that number of columns. --}}
<div class="abilities grid grid-3col">
{{#each data.abilities as |ability key|}}
<div class="ability flexrow flex-group-center">
<label for="data.abilities.{{key}}.value" class="resource-label">{{key}}</label>
<input
type="text"
name="data.abilities.{{key}}.value"
value="{{ability.value}}"
data-dtype="Number"
/>
<span class="ability-mod rollable" data-roll="d20+@abilities.{{key}}.mod" data-label="{{key}}"
>{{numberFormat ability.mod decimals=0 sign=true}}</span
>
</div>
{{/each}}
</div>
</div>
</header>
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="description">Description</a>
<a class="item" data-tab="items">Items</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Biography Tab --}}
<div class="tab biography" data-group="primary" data-tab="description">
{{editor content=data.biography target="data.biography" button=true owner=owner editable=editable}}
</div>
{{!-- Owned Items Tab --}}
<div class="tab items" data-group="primary" data-tab="items">
<ol class="items-list">
<li class="item flexrow item-header">
<div class="item-image"></div>
<div class="item-name">Name</div>
<div class="item-controls">
<a class="item-control item-create" title="Create item" data-type="weapon"
><i class="fas fa-plus"></i> Add item</a
>
</div>
</li>
{{#each actor.items as |item id|}}
<li class="item flexrow" data-item-id="{{item._id}}">
<div class="item-image">
<img src="{{item.img}}" title="{{item.name}}" width="24" height="24" />
</div>
<h4 class="item-name">{{item.name}}</h4>
<div class="item-controls">
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}
</ol>
</div>
</section>
</form>

View file

@ -0,0 +1,54 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
<div class="header-fields flexrow">
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name" /></h1>
<h2 class="item-type">{{localize (lookup config.itemTypes item.type)}}</h2>
<div class="grid grid-3col basic-properties">
<div class="basic-property">
<label class="basic-property-label">{{localize "DS4.ArmorType"}}</label>
<select class="basic-property-select" name="data.armorType" data-type="String">
{{#select data.armorType}}
{{#each config.armorTypes as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
<div class="basic-property">
<label class="basic-property-label">{{localize "DS4.ArmorMaterialType"}}</label>
<select class="basic-property-select" name="data.armorMaterialType" data-type="String">
{{#select data.armorMaterialType}}
{{#each config.armorMaterialTypes as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
<div class="basic-property">
<label class="basic-property-label">{{localize "DS4.ArmorValue"}}</label>
<input class="basic-property-input" type="text" name="data.armorValue" value="{{data.armorValue}}"
placeholder="0" data-dtype="Number" />
</div>
</div>
</div>
</header>
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="description">{{localize "DS4.Description"}}</a>
<a class="item" data-tab="details">{{localize "DS4.Details"}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Description Tab --}}
{{> systems/ds4/templates/item/partials/description.hbs}}
{{!-- Attributes Tab --}}
<div class="tab details" data-group="primary" data-tab="details">
{{!-- As you add new fields, add them in here! --}}
</div>
</section>
</form>

View file

@ -0,0 +1,27 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
<div class="header-fields flexrow">
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name" /></h1>
<h2 class="item-type">{{localize (lookup config.itemTypes item.type)}}</h2>
</div>
</header>
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="description">{{localize "DS4.Description"}}</a>
<a class="item" data-tab="details">{{localize "DS4.Details"}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Description Tab --}}
{{> systems/ds4/templates/item/partials/description.hbs}}
{{!-- Attributes Tab --}}
<div class="tab details" data-group="primary" data-tab="details">
{{!-- As you add new fields, add them in here! --}}
</div>
</section>
</form>

View file

@ -0,0 +1,24 @@
<div class="tab flexrow" data-group="primary" data-tab="description">
<div class="side-properties">
<div class="side-property">
<label for="data.quantity">{{localize "DS4.Quantity"}}</label>
<input type="number" data-dtype="Number" name="data.quantity" value="{{data.quantity}}" />
</div>
<div class="side-property">
<label for="data.price">{{localize "DS4.PriceGold"}}</label>
<input type="number" data-dtype="Number" name="data.price" value="{{data.price}}" />
</div>
<div class="side-property">
<label for="data.price">{{localize "DS4.ItemAvailability"}}</label>
<select name="data.availability" data-type="String">
{{#select data.availability}}
{{#each config.itemAvailabilities as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
{{editor content=data.description target="data.description" button=true owner=owner editable=editable}}
</div>

View file

@ -0,0 +1,34 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
<div class="header-fields flexrow">
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name" /></h1>
<h2 class="item-type">{{localize (lookup config.itemTypes item.type)}}</h2>
<div class="grid grid-1col basic-properties">
<div class="basic-property">
<label class="basic-property-label">{{localize "DS4.ArmorValue"}}</label>
<input class="basic-property-input" type="text" name="data.armorValue" value="{{data.armorValue}}"
placeholder="0" data-dtype="Number" />
</div>
</div>
</div>
</header>
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="description">{{localize "DS4.Description"}}</a>
<a class="item" data-tab="details">{{localize "DS4.Details"}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Description Tab --}}
{{> systems/ds4/templates/item/partials/description.hbs}}
{{!-- Attributes Tab --}}
<div class="tab details" data-group="primary" data-tab="details">
{{!-- As you add new fields, add them in here! --}}
</div>
</section>
</form>

View file

@ -0,0 +1,45 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
<div class="header-fields flexrow">
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name" /></h1>
<h2 class="item-type">{{localize (lookup config.itemTypes item.type)}}</h2>
</div>
</header>
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="description">{{localize "DS4.Description"}}</a>
<a class="item" data-tab="details">{{localize "DS4.Details"}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Description Tab --}}
{{> systems/ds4/templates/item/partials/description.hbs}}
{{!-- Attributes Tab --}}
<div class="tab details" data-group="primary" data-tab="details">
<ol class="effects-list">
<li class="effect flexrow effect-header">
<div class="effect-image"></div>
<div class="effect-name">Name</div>
<div class="effect-controls">
<a class="effect-control effect-create" title="Create Effect"><i
class="fas fa-plus"></i> Add effect</a>
</div>
</li>
{{#each item.effects as |effect id|}}
<li class="effect flexrow" data-effect-id="{{effect._id}}">
<h4 class="effect-name">{{effect.label}}</h4>
<div class="effect-controls">
<a class="effect-control effect-edit" title="Edit Effect"><i class="fas fa-edit"></i></a>
<a class="effect-control effect-delete" title="Delete Effect"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}
</ol>
</div>
</section>
</form>

View file

@ -0,0 +1,49 @@
<form class="{{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
<div class="header-fields flexrow">
<h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeholder="Name" /></h1>
<h2 class="item-type">{{localize (lookup config.itemTypes item.type)}}</h2>
<div class="grid grid-3col basic-properties">
<div class="basic-property">
<label class="basic-property-label">{{localize "DS4.AttackType"}}</label>
<select class="basic-property-select" name="data.attackType" data-type="String">
{{#select data.attackType}}
{{#each config.attackTypes as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
<div class="basic-property">
<label class="basic-property-label">{{localize "DS4.WeaponBonus"}}</label>
<input class="basic-property-input" type="text" name="data.weaponBonus" value="{{data.weaponBonus}}"
placeholder="0" data-dtype="Number" />
</div>
<div class="basic-property">
<label class="basic-property-label">{{localize "DS4.OpponentDefense"}}</label>
<input class="basic-property-input" type="text" name="data.opponentDefense"
value="{{data.opponentDefense}}" placeholder="0" data-dtype="Number" />
</div>
</div>
</div>
</header>
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="description">{{localize "DS4.Description"}}</a>
<a class="item" data-tab="details">{{localize "DS4.Details"}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Description Tab --}}
{{> systems/ds4/templates/item/partials/description.hbs}}
{{!-- Attributes Tab --}}
<div class="tab details" data-group="primary" data-tab="details">
{{!-- As you add new fields, add them in here! --}}
</div>
</section>
</form>