Initial import

This commit is contained in:
sladecraven 2021-05-28 18:16:02 +02:00
commit 2f5722e0df
45 changed files with 14637 additions and 0 deletions

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# Frostgrave - Fantasy Wargames in the Frozen City
> Amidst the frozen ruins of an ancient city, wizards battle in the hopes of discovering the treasures of a fallen empire. In this fantasy skirmish wargame, each player takes on the role of a wizard from one of ten schools of magic, and builds his band of followers.
This is a full-featured system to play Frostgrave and Ghost Archipelago wargames by Osprey Publishing. It has some automation: spellcasting, stat rolls.
This system **does not** include any copyright-protected material (i.e. Images, Spells, NPCs, Magic Items, Scenarios etc) and require at least one player to own a rulebook. You can buy **Frostgrave: Second Edition** at [Osprey Publishing Store](https://ospreypublishing.com/frostgrave) or get PDF at [DriveThruRPG](https://www.drivethrurpg.com/product/325026/Frostgrave-Second-Edition)
**Foundry VTT Manifest:** https://raw.githubusercontent.com/Phenomen/foundry-frostgrave/master/system.json

12518
css/frostgrave.css Normal file

File diff suppressed because it is too large Load Diff

50
gulpfile.js Normal file
View File

@ -0,0 +1,50 @@
const gulp = require('gulp');
const prefix = require('gulp-autoprefixer');
const sourcemaps = require('gulp-sourcemaps');
const sass = require('gulp-sass');
/* ----------------------------------------- */
/* Compile Sass
/* ----------------------------------------- */
// Small error handler helper function.
function handleError(err) {
console.log(err.toString());
this.emit('end');
}
const SYSTEM_SCSS = ["scss/**/*.scss"];
function compileScss() {
// Configure options for sass output. For example, 'expanded' or 'nested'
let options = {
outputStyle: 'expanded'
};
return gulp.src(SYSTEM_SCSS)
.pipe(
sass(options)
.on('error', handleError)
)
.pipe(prefix({
cascade: false
}))
.pipe(gulp.dest("./css"))
}
const css = gulp.series(compileScss);
/* ----------------------------------------- */
/* Watch Updates
/* ----------------------------------------- */
function watchUpdates() {
gulp.watch(SYSTEM_SCSS, css);
}
/* ----------------------------------------- */
/* Export Tasks
/* ----------------------------------------- */
exports.default = gulp.series(
compileScss,
watchUpdates
);
exports.css = css;

BIN
img/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

0
lang/en.json Normal file
View File

View File

0
lib/some-lib/some-lib.min.js vendored Normal file
View File

228
module/actor/actor-sheet.js Normal file
View File

@ -0,0 +1,228 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class frostgraveActorSheet extends ActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["frostgrave", "sheet", "actor"],
template: "systems/frostgrave/templates/actor/actor-sheet.html",
width: 650,
height: 650,
tabs: [{
navSelector: ".sheet-tabs",
contentSelector: ".sheet-body",
initial: "items",
}, ],
});
}
/* -------------------------------------------- */
/** @override */
getData() {
const data = super.getData();
data.dtypes = ["String", "Number", "Boolean"];
//for (let attr of Object.values(data.data.attributes)) {
// attr.isCheckbox = attr.dtype === "Boolean";
// }
// Prepare items.
if (this.actor.data.type == "character") {
this._prepareCharacterItems(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));
// Edit Inventory Item
html.find(".item-edit").click((ev) => {
const card = $(ev.currentTarget).parents(".item-card");
const item = this.actor.getOwnedItem(card.data("item-id"));
item.sheet.render(true);
});
// Delete Inventory Item
html.find(".item-delete").click((ev) => {
const card = $(ev.currentTarget).parents(".item-card");
this.actor.deleteOwnedItem(card.data("item-id"));
});
// 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 damage = parseInt(roll.roll().total) + parseInt(dataset.bonus);
let rollflavor = `${dataset.label} Roll: ` + roll.total;
if (dataset.label == "Combat" || dataset.label == "Shooting") {
let damageflavor = `<br>Damage: ` + damage;
rollflavor = rollflavor + damageflavor;
}
roll.toMessage({
speaker: ChatMessage.getSpeaker({
actor: this.actor
}),
flavor: rollflavor,
});
}
if (dataset.spell) {
let alignment = dataset.alignment;
let empowerment = this.actor.data.data.empowerment;
let alignmentmod;
let selfdamage;
let castresult;
if (alignment == "Native") {
alignmentmod = 0;
} else if (alignment == "Aligned") {
alignmentmod = 2;
} else if (alignment == "Neutral") {
alignmentmod = 4;
} else {
alignmentmod = 6;
};
let difficulty = dataset.bcn - dataset.improved + alignmentmod;
let roll = new Roll(`1d20+` + empowerment);
//let roll = new Roll(`(1d20+` + empowerment + `)ms>=` + difficulty);
roll.roll();
let rollresult = difficulty - roll.total;
if (rollresult >= 20) {
selfdamage = 5;
} else if (rollresult >= 10) {
selfdamage = 2;
} else if (rollresult >= 5) {
selfdamage = 1;
} else {
selfdamage = 0;
};
selfdamage = selfdamage + empowerment;
if (rollresult <= 0) {
castresult = '<strong style="color: green; font-size: 18px;">SUCCESS</strong>';
} else {
castresult = '<strong style="color: red; font-size: 18px;">FAILURE</strong>';
};
let rollflavor = `Casting <strong>${dataset.label}</strong> vs Difficulty <strong>` + difficulty + `</strong><br>` + castresult +
`<br>BCN: ${dataset.bcn} | Alignment: +` + alignmentmod + ` | Improved: -${dataset.improved}
<br>Empowerment: ` + empowerment + ` | Self Damage: ` + selfdamage;
roll.toMessage({
speaker: ChatMessage.getSpeaker({
actor: this.actor
}),
flavor: rollflavor,
});
}
}
/**
* Organize and classify Items for Character sheets.
*
* @param {Object} actorData The actor to prepare.
*
* @return {undefined}
*/
_prepareCharacterItems(sheetData) {
const actorData = sheetData.actor;
// Initialize containers.
const gear = [];
const features = [];
const spells = [];
// Iterate through items, allocating to containers
// let totalWeight = 0;
for (let i of sheetData.items) {
let item = i.data;
i.img = i.img || DEFAULT_TOKEN;
// Append to gear.
if (i.type === "item") {
gear.push(i);
}
// Append to features.
else if (i.type === "feature") {
features.push(i);
}
// Append to spells.
else if (i.type === "spell") {
spells.push(i);
}
}
// Assign and return
actorData.gear = gear;
actorData.features = features;
actorData.spells = spells;
}
}

33
module/actor/actor.js Normal file
View File

@ -0,0 +1,33 @@
/**
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
* @extends {Actor}
*/
export class frostgraveActor extends Actor {
/**
* Augment the basic actor data with additional dynamic data.
*/
prepareData() {
super.prepareData();
const actorData = this.data;
const data = actorData.data;
const flags = actorData.flags;
// Make separate methods for each Actor type (character, npc, etc.) to keep
// things organized.
if (actorData.type === 'character') this._prepareCharacterData(actorData);
}
/**
* Prepare Character type specific data
*/
_prepareCharacterData(actorData) {
const data = actorData.data;
// Make modifications to data here. For example:
data.exptotal = data.expscenario + data.expbanked;
}
}

236
module/actor/dist/actor-sheet.dev.js vendored Normal file
View File

@ -0,0 +1,236 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.frostgraveActorSheet = void 0;
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
var frostgraveActorSheet =
/*#__PURE__*/
function (_ActorSheet) {
_inherits(frostgraveActorSheet, _ActorSheet);
function frostgraveActorSheet() {
_classCallCheck(this, frostgraveActorSheet);
return _possibleConstructorReturn(this, _getPrototypeOf(frostgraveActorSheet).apply(this, arguments));
}
_createClass(frostgraveActorSheet, [{
key: "getData",
/* -------------------------------------------- */
/** @override */
value: function getData() {
var data = _get(_getPrototypeOf(frostgraveActorSheet.prototype), "getData", this).call(this);
data.dtypes = ["String", "Number", "Boolean"]; //for (let attr of Object.values(data.data.attributes)) {
// attr.isCheckbox = attr.dtype === "Boolean";
// }
// Prepare items.
if (this.actor.data.type == "character") {
this._prepareCharacterItems(data);
}
return data;
}
/** @override */
}, {
key: "activateListeners",
value: function activateListeners(html) {
var _this = this;
_get(_getPrototypeOf(frostgraveActorSheet.prototype), "activateListeners", this).call(this, 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)); // Edit Inventory Item
html.find(".item-edit").click(function (ev) {
var card = $(ev.currentTarget).parents(".item-card");
var item = _this.actor.getOwnedItem(card.data("item-id"));
item.sheet.render(true);
}); // Delete Inventory Item
html.find(".item-delete").click(function (ev) {
var card = $(ev.currentTarget).parents(".item-card");
_this.actor.deleteOwnedItem(card.data("item-id"));
}); // 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
*/
}, {
key: "_onItemCreate",
value: function _onItemCreate(event) {
event.preventDefault();
var header = event.currentTarget; // Get the type of item to create.
var type = header.dataset.type; // Grab any data associated with this control.
var data = duplicate(header.dataset); // Initialize a default name.
var name = "New ".concat(type.capitalize()); // Prepare the item object.
var 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
*/
}, {
key: "_onRoll",
value: function _onRoll(event) {
event.preventDefault();
var element = event.currentTarget;
var dataset = element.dataset;
if (dataset.roll) {
var roll = new Roll(dataset.roll, this.actor.data.data);
var damage = parseInt(roll.roll().total) + parseInt(dataset.bonus);
var rollflavor = "".concat(dataset.label, " Roll: ") + roll.total;
if (dataset.label == "Combat" || dataset.label == "Shooting") {
var damageflavor = "<br>Damage: " + damage;
rollflavor = rollflavor + damageflavor;
}
roll.toMessage({
speaker: ChatMessage.getSpeaker({
actor: this.actor
}),
flavor: rollflavor
});
}
}
/**
* Organize and classify Items for Character sheets.
*
* @param {Object} actorData The actor to prepare.
*
* @return {undefined}
*/
}, {
key: "_prepareCharacterItems",
value: function _prepareCharacterItems(sheetData) {
var actorData = sheetData.actor; // Initialize containers.
var gear = [];
var features = [];
var spells = []; // Iterate through items, allocating to containers
// let totalWeight = 0;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = sheetData.items[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var i = _step.value;
var item = i.data;
i.img = i.img || DEFAULT_TOKEN; // Append to gear.
if (i.type === "item") {
gear.push(i);
} // Append to features.
else if (i.type === "feature") {
features.push(i);
} // Append to spells.
else if (i.type === "spell") {
spells.push(i);
}
} // Assign and return
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
actorData.gear = gear;
actorData.features = features;
actorData.spells = spells;
}
}], [{
key: "defaultOptions",
/** @override */
get: function get() {
return mergeObject(_get(_getPrototypeOf(frostgraveActorSheet), "defaultOptions", this), {
classes: ["frostgrave", "sheet", "actor"],
template: "systems/frostgrave/templates/actor/actor-sheet.html",
width: 600,
height: 650,
tabs: [{
navSelector: ".sheet-tabs",
contentSelector: ".sheet-body",
initial: "items"
}]
});
}
}]);
return frostgraveActorSheet;
}(ActorSheet);
exports.frostgraveActorSheet = frostgraveActorSheet;

77
module/actor/dist/actor.dev.js vendored Normal file
View File

@ -0,0 +1,77 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.frostgraveActor = void 0;
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
/**
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
* @extends {Actor}
*/
var frostgraveActor =
/*#__PURE__*/
function (_Actor) {
_inherits(frostgraveActor, _Actor);
function frostgraveActor() {
_classCallCheck(this, frostgraveActor);
return _possibleConstructorReturn(this, _getPrototypeOf(frostgraveActor).apply(this, arguments));
}
_createClass(frostgraveActor, [{
key: "prepareData",
/**
* Augment the basic actor data with additional dynamic data.
*/
value: function prepareData() {
_get(_getPrototypeOf(frostgraveActor.prototype), "prepareData", this).call(this);
var actorData = this.data;
var data = actorData.data;
var flags = actorData.flags; // Make separate methods for each Actor type (character, npc, etc.) to keep
// things organized.
if (actorData.type === 'character') this._prepareCharacterData(actorData);
}
/**
* Prepare Character type specific data
*/
}, {
key: "_prepareCharacterData",
value: function _prepareCharacterData(actorData) {
var data = actorData.data; // Make modifications to data here. For example:
data.exptotal = data.expscenario + data.expbanked;
}
}]);
return frostgraveActor;
}(Actor);
exports.frostgraveActor = frostgraveActor;

57
module/frostgrave.js Normal file
View File

@ -0,0 +1,57 @@
// Import Modules
import { frostgraveActor } from "./actor/actor.js";
import { frostgraveActorSheet } from "./actor/actor-sheet.js";
import { frostgraveItem } from "./item/item.js";
import { frostgraveItemSheet } from "./item/item-sheet.js";
import { preloadHandlebarsTemplates } from "./templates.js";
Hooks.once("init", async function () {
game.frostgrave = {
frostgraveActor,
frostgraveItem,
};
/**
* Set an initiative formula for the system
* @type {String}
*/
CONFIG.Combat.initiative = {
formula: "1d20",
decimals: 2,
};
// Define custom Entity classes
CONFIG.Actor.entityClass = frostgraveActor;
CONFIG.Item.entityClass = frostgraveItem;
// Register sheet application classes
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet("frostgrave", frostgraveActorSheet, {
makeDefault: true,
});
Items.unregisterSheet("core", ItemSheet);
Items.registerSheet("frostgrave", frostgraveItemSheet, {
types: ["item", "feature", "spell"],
makeDefault: true,
});
// If you need to add Handlebars helpers, here are a few useful examples:
Handlebars.registerHelper("concat", function () {
var outStr = "";
for (var arg in arguments) {
if (typeof arguments[arg] != "object") {
outStr += arguments[arg];
}
}
return outStr;
});
Handlebars.registerHelper("toLowerCase", function (str) {
return str.toLowerCase();
});
// Preload Handlebars Templates
preloadHandlebarsTemplates();
});

117
module/item/dist/item-sheet.dev.js vendored Normal file
View File

@ -0,0 +1,117 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.frostgraveItemSheet = void 0;
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
/**
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
*/
var frostgraveItemSheet =
/*#__PURE__*/
function (_ItemSheet) {
_inherits(frostgraveItemSheet, _ItemSheet);
function frostgraveItemSheet() {
_classCallCheck(this, frostgraveItemSheet);
return _possibleConstructorReturn(this, _getPrototypeOf(frostgraveItemSheet).apply(this, arguments));
}
_createClass(frostgraveItemSheet, [{
key: "getData",
/* -------------------------------------------- */
/** @override */
value: function getData() {
var data = _get(_getPrototypeOf(frostgraveItemSheet.prototype), "getData", this).call(this);
return data;
}
/* -------------------------------------------- */
/** @override */
}, {
key: "setPosition",
value: function setPosition() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var position = _get(_getPrototypeOf(frostgraveItemSheet.prototype), "setPosition", this).call(this, options);
var sheetBody = this.element.find(".sheet-body");
var bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
}, {
key: "activateListeners",
value: function activateListeners(html) {
_get(_getPrototypeOf(frostgraveItemSheet.prototype), "activateListeners", this).call(this, html); // Everything below here is only needed if the sheet is editable
if (!this.options.editable) return; // Roll handlers, click handlers, etc. would go here.
}
}, {
key: "template",
/** @override */
get: function get() {
var path = "systems/frostgrave/templates/item"; // Return a single sheet for all item types.
// return `${path}/item-sheet.html`;
// Alternatively, you could use the following return statement to do a
// unique item sheet by type, like `weapon-sheet.html`.
return "".concat(path, "/item-").concat(this.item.data.type, "-sheet.html");
}
}], [{
key: "defaultOptions",
/** @override */
get: function get() {
return mergeObject(_get(_getPrototypeOf(frostgraveItemSheet), "defaultOptions", this), {
classes: ["frostgrave", "sheet", "item"],
width: 450,
height: 500,
tabs: [{
navSelector: ".sheet-tabs",
contentSelector: ".sheet-body",
initial: "attributes"
}]
});
}
}]);
return frostgraveItemSheet;
}(ItemSheet);
exports.frostgraveItemSheet = frostgraveItemSheet;

61
module/item/item-sheet.js Normal file
View File

@ -0,0 +1,61 @@
/**
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
*/
export class frostgraveItemSheet extends ItemSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["frostgrave", "sheet", "item"],
width: 450,
height: 500,
tabs: [{
navSelector: ".sheet-tabs",
contentSelector: ".sheet-body",
initial: "attributes",
}, ],
});
}
/** @override */
get template() {
const path = "systems/frostgrave/templates/item";
// Return a single sheet for all item types.
// return `${path}/item-sheet.html`;
// Alternatively, you could use the following return statement to do a
// unique item sheet by type, like `weapon-sheet.html`.
return `${path}/item-${this.item.data.type}-sheet.html`;
}
/* -------------------------------------------- */
/** @override */
getData() {
const data = super.getData();
return data;
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Roll handlers, click handlers, etc. would go here.
}
}

37
module/item/item.js Normal file
View File

@ -0,0 +1,37 @@
/**
* Extend the basic Item with some very simple modifications.
* @extends {Item}
*/
export class frostgraveItem 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;
}
/**
* Handle clickable rolls.
* @param {Event} event The originating click event
* @private
*/
async roll() {
// Basic template rendering data
const token = this.actor.token;
const item = this.data;
const actorData = this.actor ? this.actor.data.data : {};
const itemData = item.data;
let roll = new Roll("d20+@stats.fight.value", actorData);
let label = `Rolling ${item.name}`;
roll.roll().toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
flavor: label,
});
}
}

28
module/templates.js Normal file
View File

@ -0,0 +1,28 @@
/**
* Define a set of template paths to pre-load
* Pre-loaded templates are compiled and cached for fast access when rendering
* @return {Promise}
*/
export const preloadHandlebarsTemplates = async function() {
// Define template paths to load
const templatePaths = [
// Actor Sheet Partials
"systems/frostgrave/templates/actor/partials/actor-header.html",
"systems/frostgrave/templates/actor/partials/actor-stats.html",
"systems/frostgrave/templates/actor/partials/actor-tab-navigation.html",
"systems/frostgrave/templates/actor/partials/actor-tab-notes.html",
"systems/frostgrave/templates/actor/partials/actor-tab-experience.html",
"systems/frostgrave/templates/actor/partials/actor-tab-homebase.html",
"systems/frostgrave/templates/actor/partials/actor-tab-items.html",
"systems/frostgrave/templates/actor/partials/actor-tab-spells.html"
// Item Sheet Partials
];
// Load the template parts
return loadTemplates(templatePaths);
};

27
package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "frostgrave",
"version": "1.0.0",
"description": "CSS compiler for the Frostgrave system",
"scripts": {
"build": "gulp",
"compile": "gulp css",
"watch": "gulp",
"gulp": "gulp"
},
"browserslist": [
"last 3 versions"
],
"author": "Phenomen",
"license": "MIT",
"private": true,
"dependencies": {
"gulp": "^4.0.2",
"gulp-autoprefixer": "^7.0.1",
"gulp-sass": "^4.1.0",
"gulp-sourcemaps": "^2.6.5"
},
"devDependencies": {
"bulma": "^0.9.0",
"prettier": "2.1.1"
}
}

3
packs/armour.db Normal file
View File

@ -0,0 +1,3 @@
{"name":"Light Armour","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"","category":"Armour","subcategory":"Light","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/armor/leather.png","_id":"I51JRohC7U9QZQf4"}
{"name":"Shield","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>May not be carried with a two-handed weapon or staff.</p>","category":"Armour","subcategory":"Shield","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/armor/shield.png","_id":"XdYmXpKWziTu3TsF"}
{"name":"Heavy Armour","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>-1 Move.</p>","category":"Armour","subcategory":"Heavy","damage":null,"armour":2,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/armor/scale.png","_id":"nQ5smVtqMU2hduea"}

1
packs/potions.db Normal file
View File

@ -0,0 +1 @@
{"name":"Potion of Healing","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>This potion restores up to 5 lost points of Health. It may not take a figure above its normal starting Health.</p>","category":"Potion","subcategory":"Lesser Potion","damage":null,"armour":null,"range":null,"effect":null,"price":75,"sale":50,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/potions/minor-red.jpg","_id":"gOvnL5aOzyEOO0s8"}

15
packs/soldiers.db Normal file
View File

@ -0,0 +1,15 @@
{"_id":"59mfcvDarifBycpj","name":"Infantryman","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Standard","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":10,"min":0,"max":10,"actual":10,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":11,"effective":11,"bonus":0,"rollname":"Armour"},"will":{"actual":0,"effective":0,"bonus":0,"rollname":"Will"},"fight":{"actual":3,"effective":3,"bonus":2,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Infantryman","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"59mfcvDarifBycpj","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"LuxNzXlIF9ysiABj","flags":{},"name":"Two-Handed Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/glaive.png","data":{"description":"<p>Takes up two item slots.</p>","category":"Weapon","subcategory":"Two-Handed Weapon","damage":2,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000},{"_id":"hXUaA7s8tneGPJrX","flags":{},"name":"Light Armour","type":"item","img":"systems/dnd5e/icons/items/armor/leather.png","data":{"description":"","category":"Armour","subcategory":"Light","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000}]}
{"_id":"66cjlcR0OhmEODUn","name":"Barbarian","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":14,"min":0,"max":14,"actual":14,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":10,"effective":10,"bonus":0,"rollname":"Armour"},"will":{"actual":3,"effective":3,"bonus":0,"rollname":"Will"},"fight":{"actual":4,"effective":4,"bonus":2,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Barbarian","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"66cjlcR0OhmEODUn","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"XiJ0wozY8N1no1dS","flags":{},"name":"Two-Handed Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/glaive.png","data":{"description":"<p>Takes up two item slots.</p>","category":"Weapon","subcategory":"Two-Handed Weapon","damage":2,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000},{"_id":"8gwdilYKx2p4BpyL","flags":{},"name":"Dagger","type":"item","img":"systems/dnd5e/icons/items/weapons/dagger.jpg","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000}]}
{"_id":"7W70MYhN79feZ1qm","name":"Templar","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":5,"effective":5,"bonus":0,"rollname":"Movement"},"armour":{"actual":12,"effective":12,"bonus":0,"rollname":"Armour"},"will":{"actual":1,"effective":1,"bonus":0,"rollname":"Will"},"fight":{"actual":4,"effective":4,"bonus":2,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Templar","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"7W70MYhN79feZ1qm","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"Bk5pYrBix5EInRAp","flags":{},"name":"Two-Handed Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/glaive.png","data":{"description":"<p>Takes up two item slots.</p>","category":"Weapon","subcategory":"Two-Handed Weapon","damage":2,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000},{"_id":"IRRlD5kzTvP7FYB3","flags":{},"name":"Heavy Armour","type":"item","img":"systems/dnd5e/icons/items/armor/scale.png","data":{"description":"<p>-1 Move.</p>","category":"Armour","subcategory":"Heavy","damage":null,"armour":2,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000}]}
{"_id":"8HEq0kbGyL12VI8f","name":"War Hound","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Standard","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":8,"min":0,"max":8,"actual":8,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":8,"effective":8,"bonus":0,"rollname":"Movement"},"armour":{"actual":10,"effective":10,"bonus":0,"rollname":"Armour"},"will":{"actual":-2,"effective":-2,"bonus":0,"rollname":"Will"},"fight":{"actual":1,"effective":1,"bonus":0,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"War Houn","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"8HEq0kbGyL12VI8f","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"RFL5VaOrVXJIUlYD","flags":{},"name":"Natural Weapon","type":"item","img":"systems/dnd5e/icons/skills/blood_06.jpg","data":{"description":"","category":"Weapon","subcategory":"Natural Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100001}]}
{"_id":"J6BAXGZkPnveNEGA","name":"Thug","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Standard","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":10,"min":0,"max":10,"actual":10,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":10,"effective":10,"bonus":0,"rollname":"Armour"},"will":{"actual":-1,"effective":-1,"bonus":0,"rollname":"Will"},"fight":{"actual":2,"effective":2,"bonus":0,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Thug","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"J6BAXGZkPnveNEGA","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"rUXaMve5ztHWWI4j","flags":{},"name":"Hand Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/sword-short.jpg","data":{"description":"","category":"Weapon","subcategory":"Hand Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000}]}
{"_id":"Mqf91lCJN5ZnXkYX","name":"Crossbowman","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":10,"min":0,"max":10,"actual":10,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":11,"effective":11,"bonus":0,"rollname":"Armour"},"will":{"actual":0,"effective":0,"bonus":0,"rollname":"Will"},"fight":{"actual":1,"effective":1,"bonus":-1,"rollname":"Combat"},"shoot":{"actual":2,"effective":2,"bonus":2,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Crossbowman","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"Mqf91lCJN5ZnXkYX","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"c0SyRaxhnTZsxZfz","flags":{},"name":"Quiver","type":"item","img":"systems/dnd5e/icons/items/weapons/arrows.jpg","data":{"description":"","category":"Weapon","subcategory":"Quiver","damage":null,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000},{"_id":"YRC0nrUnc9r9SmgP","flags":{},"name":"Dagger","type":"item","img":"systems/dnd5e/icons/items/weapons/dagger.jpg","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":300000},{"_id":"iyJ2bOYo0ZifGv0W","flags":{},"name":"Light Armour","type":"item","img":"systems/dnd5e/icons/items/armor/leather.png","data":{"description":"","category":"Armour","subcategory":"Light","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":400000},{"_id":"9iaToNIZ1NHmIPZw","flags":{},"name":"Crossbow","type":"item","img":"systems/dnd5e/icons/items/weapons/crossbow-light.jpg","data":{"description":"<p>Load and fire as separate actions. May reload in place of movement. Must have a quiver.</p>","category":"Weapon","subcategory":"Crossbow","damage":2,"armour":null,"range":24,"effect":"","price":null,"sale":null,"ingredient":null},"sort":500000}]}
{"_id":"O6AOKK7hOg2qT6jm","name":"Apothecary","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"<p>Apothecaries start each game with a potion of healing (see page 91). An apothecary may spend an action to give a potion to a member of the same warband within 1”, provided neither are in combat. The figure receiving the potion counts as having drunk it, and effects are applied immediately. Note that this rule applies to any potion carried by an apothecary, not just their free healing potion.</p>","category":"Soldier","class":"Standard","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":10,"effective":10,"bonus":0,"rollname":"Armour"},"will":{"actual":3,"effective":3,"bonus":0,"rollname":"Will"},"fight":{"actual":1,"effective":1,"bonus":-1,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Apothecary","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"O6AOKK7hOg2qT6jm","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"fJtt9VWxGCy81it8","flags":{},"name":"Staff","type":"item","img":"systems/dnd5e/icons/items/weapons/quarterstaff.jpg","data":{"description":"<p>-1 damage modifier to opponent in hand-to-hand combat.</p>","category":"Weapon","subcategory":"Staff","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000},{"_id":"IbFxeJjojSbx2fVU","flags":{},"name":"Potion of Healing","type":"item","img":"systems/dnd5e/icons/items/potions/minor-red.jpg","data":{"description":"<p>This potion restores up to 5 lost points of Health. It may not take a figure above its normal starting Health.</p>","category":"Potion","subcategory":"Lesser Potion","damage":null,"armour":null,"range":null,"effect":null,"price":75,"sale":50,"ingredient":null},"sort":200000}]}
{"_id":"OM87vw6ePSelpCId","name":"Thief","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Standard","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":10,"min":0,"max":10,"actual":10,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":7,"effective":7,"bonus":0,"rollname":"Movement"},"armour":{"actual":10,"effective":10,"bonus":0,"rollname":"Armour"},"will":{"actual":0,"effective":0,"bonus":0,"rollname":"Will"},"fight":{"actual":1,"effective":1,"bonus":-1,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Thief","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"OM87vw6ePSelpCId","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"ZMKGWw9o0d6hcHt0","flags":{},"name":"Dagger","type":"item","img":"systems/dnd5e/icons/items/weapons/dagger.jpg","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000}]}
{"_id":"UeAoVRN8m6HGzNcr","name":"Man-at-Arms","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Standard","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":12,"effective":12,"bonus":0,"rollname":"Armour"},"will":{"actual":1,"effective":1,"bonus":0,"rollname":"Will"},"fight":{"actual":3,"effective":3,"bonus":0,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Man-at-Arms","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"UeAoVRN8m6HGzNcr","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"sAcPFyuxOvDTzyR0","flags":{},"name":"Hand Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/sword-short.jpg","data":{"description":"","category":"Weapon","subcategory":"Hand Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000},{"_id":"jpX8uChEmNw9rOqV","flags":{},"name":"Shield","type":"item","img":"systems/dnd5e/icons/items/armor/shield.png","data":{"description":"<p>May not be carried with a two-handed weapon or staff.</p>","category":"Armour","subcategory":"Shield","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000},{"_id":"rCgB3iLJQ2QKca26","flags":{},"name":"Light Armour","type":"item","img":"systems/dnd5e/icons/items/armor/leather.png","data":{"description":"","category":"Armour","subcategory":"Light","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":300000}]}
{"_id":"WCtuyqc3U6PPFcSW","name":"Archer","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":10,"min":0,"max":10,"actual":10,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":11,"effective":11,"bonus":0,"rollname":"Armour"},"will":{"actual":0,"effective":0,"bonus":0,"rollname":"Will"},"fight":{"actual":1,"effective":1,"bonus":-1,"rollname":"Combat"},"shoot":{"actual":2,"effective":2,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Archer","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"WCtuyqc3U6PPFcSW","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"c0SyRaxhnTZsxZfz","flags":{},"name":"Quiver","type":"item","img":"systems/dnd5e/icons/items/weapons/arrows.jpg","data":{"description":"","category":"Weapon","subcategory":"Quiver","damage":null,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000},{"_id":"YRC0nrUnc9r9SmgP","flags":{},"name":"Dagger","type":"item","img":"systems/dnd5e/icons/items/weapons/dagger.jpg","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":300000},{"_id":"iyJ2bOYo0ZifGv0W","flags":{},"name":"Light Armour","type":"item","img":"systems/dnd5e/icons/items/armor/leather.png","data":{"description":"","category":"Armour","subcategory":"Light","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":400000},{"_id":"e9C9c3glr2QDolHy","flags":{},"name":"Bow","type":"item","img":"systems/dnd5e/icons/items/weapons/bow-long.jpg","data":{"description":"<p>Load and fire as a single action. Must have a quiver.</p>","category":"Weapon","subcategory":"Bow","damage":0,"armour":null,"range":24,"effect":"","price":null,"sale":null,"ingredient":null},"sort":500000}]}
{"_id":"Wkw8NSLNYMOgqV5m","name":"Tracker","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":7,"effective":7,"bonus":0,"rollname":"Movement"},"armour":{"actual":11,"effective":11,"bonus":0,"rollname":"Armour"},"will":{"actual":1,"effective":1,"bonus":0,"rollname":"Will"},"fight":{"actual":1,"effective":1,"bonus":-1,"rollname":"Combat"},"shoot":{"actual":2,"effective":2,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Tracker","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"Wkw8NSLNYMOgqV5m","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"iyJ2bOYo0ZifGv0W","flags":{},"name":"Light Armour","type":"item","img":"systems/dnd5e/icons/items/armor/leather.png","data":{"description":"","category":"Armour","subcategory":"Light","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":400000},{"_id":"lAvA5pueyHVR3WMW","flags":{},"name":"Staff","type":"item","img":"systems/dnd5e/icons/items/weapons/quarterstaff.jpg","data":{"description":"<p>-1 damage modifier to opponent in hand-to-hand combat.</p>","category":"Weapon","subcategory":"Staff","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":500000},{"_id":"OkmenwhMeke8KN09","flags":{},"name":"Bow","type":"item","img":"systems/dnd5e/icons/items/weapons/bow-long.jpg","data":{"description":"<p>Load and fire as a single action. Must have a quiver.</p>","category":"Weapon","subcategory":"Bow","damage":0,"armour":null,"range":24,"effect":"","price":null,"sale":null,"ingredient":null},"sort":600000},{"_id":"d5dly98Cfb3JlFiC","flags":{},"name":"Quiver","type":"item","img":"systems/dnd5e/icons/items/weapons/arrows.jpg","data":{"description":"","category":"Weapon","subcategory":"Quiver","damage":null,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":700000}]}
{"_id":"ZmgMQnPDy9Bi2Ngk","name":"Ranger","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":7,"effective":7,"bonus":0,"rollname":"Movement"},"armour":{"actual":11,"effective":11,"bonus":0,"rollname":"Armour"},"will":{"actual":2,"effective":2,"bonus":0,"rollname":"Will"},"fight":{"actual":2,"effective":2,"bonus":0,"rollname":"Combat"},"shoot":{"actual":2,"effective":2,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Ranger","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"ZmgMQnPDy9Bi2Ngk","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"P01vFsHBeLCyg0ud","flags":{},"name":"Bow","type":"item","img":"systems/dnd5e/icons/items/weapons/bow-long.jpg","data":{"description":"<p>Load and fire as a single action. Must have a quiver.</p>","category":"Weapon","subcategory":"Bow","damage":0,"armour":null,"range":24,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000},{"_id":"ZNtz0eaQPtjobyOU","flags":{},"name":"Quiver","type":"item","img":"systems/dnd5e/icons/items/weapons/arrows.jpg","data":{"description":"","category":"Weapon","subcategory":"Quiver","damage":null,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000},{"_id":"I3iFuOLR6wVipzmS","flags":{},"name":"Hand Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/sword-short.jpg","data":{"description":"","category":"Weapon","subcategory":"Hand Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":300000},{"_id":"xkVx43rKXlRZ4TaI","flags":{},"name":"Light Armour","type":"item","img":"systems/dnd5e/icons/items/armor/leather.png","data":{"description":"","category":"Armour","subcategory":"Light","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":400000}]}
{"_id":"lxbURH4JHvOocB2S","name":"Marksman","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":5,"effective":5,"bonus":0,"rollname":"Movement"},"armour":{"actual":12,"effective":12,"bonus":0,"rollname":"Armour"},"will":{"actual":1,"effective":1,"bonus":0,"rollname":"Will"},"fight":{"actual":2,"effective":2,"bonus":0,"rollname":"Combat"},"shoot":{"actual":2,"effective":2,"bonus":2,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Marksman","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"lxbURH4JHvOocB2S","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"I8Yi9C7SjWvdUsLI","flags":{},"name":"Crossbow","type":"item","img":"systems/dnd5e/icons/items/weapons/crossbow-light.jpg","data":{"description":"<p>Load and fire as separate actions. May reload in place of movement. Must have a quiver.</p>","category":"Weapon","subcategory":"Crossbow","damage":2,"armour":null,"range":24,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000},{"_id":"jWFtKAph1heL4hNP","flags":{},"name":"Quiver","type":"item","img":"systems/dnd5e/icons/items/weapons/arrows.jpg","data":{"description":"","category":"Weapon","subcategory":"Quiver","damage":null,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000},{"_id":"rOBhs2E7ET3Tchto","flags":{},"name":"Hand Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/sword-short.jpg","data":{"description":"","category":"Weapon","subcategory":"Hand Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":300000},{"_id":"itROH2a1HQmWqZ8X","flags":{},"name":"Heavy Armour","type":"item","img":"systems/dnd5e/icons/items/armor/scale.png","data":{"description":"<p>-1 Move.</p>","category":"Armour","subcategory":"Heavy","damage":null,"armour":2,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":400000}]}
{"_id":"oKNOoUlm6Wy2mNOS","name":"Knight","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":5,"effective":5,"bonus":0,"rollname":"Movement"},"armour":{"actual":13,"effective":13,"bonus":0,"rollname":"Armour"},"will":{"actual":1,"effective":1,"bonus":0,"rollname":"Will"},"fight":{"actual":4,"effective":4,"bonus":0,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Knight","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"oKNOoUlm6Wy2mNOS","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"mAUQ2TXvlPwPPvTA","flags":{},"name":"Hand Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/sword-short.jpg","data":{"description":"","category":"Weapon","subcategory":"Hand Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000},{"_id":"1TOGTQI1QjbV3Ta0","flags":{},"name":"Dagger","type":"item","img":"systems/dnd5e/icons/items/weapons/dagger.jpg","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":200000},{"_id":"cO9Ied08uZvmkfgI","flags":{},"name":"Shield","type":"item","img":"systems/dnd5e/icons/items/armor/shield.png","data":{"description":"<p>May not be carried with a two-handed weapon or staff.</p>","category":"Armour","subcategory":"Shield","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":300000},{"_id":"0U48VCdeUp7GmLhX","flags":{},"name":"Heavy Armour","type":"item","img":"systems/dnd5e/icons/items/armor/scale.png","data":{"description":"<p>-1 Move.</p>","category":"Armour","subcategory":"Heavy","damage":null,"armour":2,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":400000}]}
{"_id":"sbTosGbxZxh9Ud3A","name":"Treasure Hunter","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Soldier","class":"Specialist","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":7,"effective":7,"bonus":0,"rollname":"Movement"},"armour":{"actual":11,"effective":11,"bonus":0,"rollname":"Armour"},"will":{"actual":2,"effective":2,"bonus":0,"rollname":"Will"},"fight":{"actual":3,"effective":3,"bonus":0,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Treasure Hunter","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"sbTosGbxZxh9Ud3A","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"YRC0nrUnc9r9SmgP","flags":{},"name":"Dagger","type":"item","img":"systems/dnd5e/icons/items/weapons/dagger.jpg","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":300000},{"_id":"iyJ2bOYo0ZifGv0W","flags":{},"name":"Light Armour","type":"item","img":"systems/dnd5e/icons/items/armor/leather.png","data":{"description":"","category":"Armour","subcategory":"Light","damage":null,"armour":1,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":400000},{"_id":"2vmwtjKlrOhD1nk1","flags":{},"name":"Hand Weapon","type":"item","img":"systems/dnd5e/icons/items/weapons/sword-short.jpg","data":{"description":"","category":"Weapon","subcategory":"Hand Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":500000}]}

2
packs/spellcasters.db Normal file
View File

@ -0,0 +1,2 @@
{"_id":"PWs2JVZEbxXhfuNA","name":"Wizard","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Wizard","class":"Chronomancer","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":14,"min":0,"max":14,"actual":14,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":10,"effective":10,"bonus":0,"rollname":"Armour"},"will":{"actual":4,"effective":4,"bonus":0,"rollname":"Will"},"fight":{"actual":2,"effective":2,"bonus":-1,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Wizard","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"PWs2JVZEbxXhfuNA","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"ckH4LBUucf03uMPX","flags":{},"name":"Dagger","type":"item","img":"systems/dnd5e/icons/items/weapons/dagger.jpg","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000}]}
{"_id":"rz6WKsk9iNxrdxTw","name":"Apprentice","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"character","data":{"baseresources":"","basetreasury":0,"biography":"","category":"Apprentice","class":"Chronomancer","empowerment":0,"expbanked":0,"expscenario":0,"exptotal":0,"level":0,"levellog":"<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>","mod":0,"health":{"value":12,"min":0,"max":12,"actual":12,"bonus":0,"rollname":"Health"},"stats":{"move":{"actual":6,"effective":6,"bonus":0,"rollname":"Movement"},"armour":{"actual":10,"effective":10,"bonus":0,"rollname":"Armour"},"will":{"actual":2,"effective":2,"bonus":0,"rollname":"Will"},"fight":{"actual":0,"effective":0,"bonus":-1,"rollname":"Combat"},"shoot":{"actual":0,"effective":0,"bonus":0,"rollname":"Shooting"}}},"sort":100001,"flags":{},"img":"icons/svg/mystery-man.svg","token":{"flags":{},"name":"Apprentice","displayName":0,"img":"icons/svg/mystery-man.svg","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"actorId":"rz6WKsk9iNxrdxTw","actorLink":false,"actorData":{},"disposition":-1,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[{"_id":"WYRMfbRnali6nzYU","flags":{},"name":"Dagger","type":"item","img":"systems/dnd5e/icons/items/weapons/dagger.jpg","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"sort":100000}]}

9
packs/weapons.db Normal file
View File

@ -0,0 +1,9 @@
{"_id":"3znr4S0pBxvsjHfT","name":"Staff","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>-1 damage modifier to opponent in hand-to-hand combat.</p>","category":"Weapon","subcategory":"Staff","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/weapons/quarterstaff.jpg"}
{"_id":"I5dzIblASNnXm6wP","name":"Hand Weapon","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"","category":"Weapon","subcategory":"Hand Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/weapons/sword-short.jpg"}
{"_id":"J2MgKDjl7PnzfFk7","name":"Quiver","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"","category":"Weapon","subcategory":"Quiver","damage":null,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/weapons/arrows.jpg"}
{"_id":"NCVtlHqckthPSqQT","name":"Bow","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>Load and fire as a single action. Must have a quiver.</p>","category":"Weapon","subcategory":"Bow","damage":0,"armour":null,"range":24,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/weapons/bow-long.jpg"}
{"_id":"bc8K2X35HyZ3ilLv","name":"Crossbow","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>Load and fire as separate actions. May reload in place of movement. Must have a quiver.</p>","category":"Weapon","subcategory":"Crossbow","damage":2,"armour":null,"range":24,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/weapons/crossbow-light.jpg"}
{"_id":"pIUgVtBi8HY0wWad","name":"Two-Handed Weapon","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>Takes up two item slots.</p>","category":"Weapon","subcategory":"Two-Handed Weapon","damage":2,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/weapons/glaive.png"}
{"_id":"tepMTpdLsYToTt6Q","name":"Unarmed","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>-2 Fight.</p>","category":"Weapon","subcategory":"Unarmed","damage":-2,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/skills/blood_11.jpg"}
{"_id":"xdAmMNWwT945pgHx","name":"Dagger","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"<p>First dagger does not take up an item slot.</p>","category":"Weapon","subcategory":"Dagger","damage":-1,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/items/weapons/dagger.jpg"}
{"name":"Natural Weapon","permission":{"default":0,"t4s6NRodQrHWaAMD":3},"type":"item","data":{"description":"","category":"Weapon","subcategory":"Natural Weapon","damage":0,"armour":null,"range":null,"effect":"","price":null,"sale":null,"ingredient":null},"flags":{},"img":"systems/dnd5e/icons/skills/blood_06.jpg","_id":"xiMhRBKLUKy5wHFY"}

View File

@ -0,0 +1,37 @@
header {
display: flex !important;
background-image: none;
background-color: $accent-color;
a {
color: white;
}
a:hover {
color: white;
text-shadow: 0 0 5px white;
}
}
.sheet-body,
.sheet-body .tab,
.sheet-body, .editor {
height: 100%;
}
.tox {
.tox-editor-container {
background: white;
}
.tox-edit-area {
padding: 4px 8px;
}
}
form .notes,
form .hint {
font-size: 1rem;
line-height: 1.4rem;
color: #363636;
}

View File

@ -0,0 +1,4 @@
img.is-cover {
object-fit: cover;
border-color: $accent-color;
}

View File

View File

@ -0,0 +1,5 @@
.tabs .item.active {
text-shadow: none;
border-bottom-color: $accent-color;
color: $accent-color;
}

View File

@ -0,0 +1,21 @@
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold;
border-bottom: none
}
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.2rem;
}

18
scss/frostgrave.scss Normal file
View File

@ -0,0 +1,18 @@
@charset "utf-8";
// Import utilities.
@import "utils/variables";
/* Global styles */
@import "global/window";
/* Styles limited to Frostgrave sheets */
.frostgrave {
@import "../node_modules/bulma/bulma.sass";
@import "components/forms";
@import "components/images";
@import "components/text";
@import "components/tabs";
@import "components/items";
}

48
scss/global/_window.scss Normal file
View File

@ -0,0 +1,48 @@
.window-app {
box-shadow: 0 0 20px $accent-color;
.window-content {
background-image: url("../img/bg.jpg");
background-repeat: no-repeat;
background-size: cover;
background-color: hsl(206, 70%, 96%);
}
}
.rollable {
&:hover,
&:focus {
cursor: pointer;
}
}
::-webkit-scrollbar-thumb {
outline: none;
border-radius: 3px;
background: $accent-color;
border: 1px solid white;
}
a:hover {
color: $accent-color;
text-shadow: 0 0 5px white;
}
input[type="text"]:focus,
input[type="password"]:focus,
input[type="datetime-local"]:focus {
box-shadow: 0 0 5px $accent-color;
}
.editor-edit {
display: none;
font-size: 1.25em;
position: absolute;
top: 5px;
right: 5px;
background: none;
border: none;
border-radius: 0px;
box-shadow: none;
}

1
scss/utils/_bulma.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
$accent-color: hsl(204, 86%, 53%);

65
system.json Normal file
View File

@ -0,0 +1,65 @@
{
"name": "frostgrave",
"title": "Frostgrave",
"description": "The Frostgrave system for Foundry VTT",
"version": "1.0.0",
"minimumCoreVersion": "0.6.5",
"compatibleCoreVersion": "0.6.6",
"templateVersion": 2,
"author": "Phenomen",
"esmodules": [
"module/frostgrave.js"
],
"styles": [
"css/frostgrave.css"
],
"scripts": [],
"packs": [{
"name": "spellcasters",
"label": "Spellcasters",
"system": "frostgrave",
"path": "./packs/spellcasters.db",
"entity": "Actor"
},
{
"name": "soldiers",
"label": "Soldiers",
"system": "frostgrave",
"path": "./packs/soldiers.db",
"entity": "Actor"
},
{
"name": "armour",
"label": "Armour",
"system": "frostgrave",
"path": "./packs/armour.db",
"entity": "Item"
},
{
"name": "weapons",
"label": "Weapons",
"system": "frostgrave",
"path": "./packs/weapons.db",
"entity": "Item"
},
{
"name": "potions",
"label": "Potions",
"system": "frostgrave",
"path": "./packs/potions.db",
"entity": "Item"
}
],
"languages": [{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}],
"gridDistance": 5,
"gridUnits": "ft",
"primaryTokenAttribute": "health",
"url": "https://github.com/Phenomen/foundry-frostgrave",
"manifest": "https://raw.githubusercontent.com/Phenomen/foundry-frostgrave/master/system.json",
"download": "https://github.com/Phenomen/foundry-frostgrave/archive/master.zip",
"license": "LICENSE.txt"
}

109
template.json Normal file
View File

@ -0,0 +1,109 @@
{
"Actor": {
"types": [
"character"
],
"templates": {
"base": {
"baseresources": "",
"basetreasury": 0,
"biography": "",
"category": "Wizard",
"class": "Chronomancer",
"empowerment": 0,
"expbanked": 0,
"expscenario": 0,
"exptotal": 0,
"level": 0,
"levellog": "<ul><li>1:</li><li>2:</li><li>3:</li><li>4:</li><li>5:</li><li>6:</li><li>7:</li><li>8:</li><li>9:</li><li>10:</li></ul>",
"mod": 0,
"health": {
"value": 14,
"min": 0,
"max": 14,
"actual": 14,
"bonus": 0,
"rollname": "Health"
},
"stats": {
"move": {
"actual": 6,
"effective": 6,
"bonus": 0,
"rollname": "Movement"
},
"armour": {
"actual": 10,
"effective": 10,
"bonus": 0,
"rollname": "Armour"
},
"will": {
"actual": 4,
"effective": 4,
"bonus": 0,
"rollname": "Will"
},
"fight": {
"actual": 2,
"effective": 2,
"bonus": 0,
"rollname": "Combat"
},
"shoot": {
"actual": 0,
"effective": 0,
"bonus": 0,
"rollname": "Shooting"
}
}
}
},
"character": {
"templates": [
"base"
]
}
},
"Item": {
"types": [
"item",
"feature",
"spell"
],
"templates": {
"base": {
"description": ""
}
},
"item": {
"templates": [
"base"
],
"category": "Other",
"subcategory": "None",
"damage": null,
"armour": null,
"range": null,
"effect": null,
"price": null,
"sale": null,
"ingredient": null
},
"feature": {
"templates": [
"base"
]
},
"spell": {
"templates": [
"base"
],
"bcn": 10,
"improved": 0,
"category": "Line of Sight",
"school": "Chronomancer",
"alignment": "Native"
}
}
}

View File

@ -0,0 +1,30 @@
<form class="{{cssClass}}" autocomplete="off">
{{!-- Actor Header --}}
{{> "systems/frostgrave/templates/actor/partials/actor-header.html"}}
{{!-- Actor Stats --}}
{{> "systems/frostgrave/templates/actor/partials/actor-stats.html"}}
{{!-- Tab Navigation --}}
{{> "systems/frostgrave/templates/actor/partials/actor-tab-navigation.html"}}
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Notes Tab --}}
{{> "systems/frostgrave/templates/actor/partials/actor-tab-notes.html"}}
{{!-- Experience Tab --}}
{{> "systems/frostgrave/templates/actor/partials/actor-tab-experience.html"}}
{{!-- Homebase Tab --}}
{{> "systems/frostgrave/templates/actor/partials/actor-tab-homebase.html"}}
{{!-- Items Tab --}}
{{> "systems/frostgrave/templates/actor/partials/actor-tab-items.html"}}
{{!-- Spells Tab --}}
{{> "systems/frostgrave/templates/actor/partials/actor-tab-spells.html"}}
</section>
</form>

View File

@ -0,0 +1,71 @@
<div class="columns">
<div class="column is-3">
<figure class="image is-square">
<img class="is-cover" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" />
</figure>
</div>
<div class="column">
<input class="input is-info is-medium" name="name" type="text" value="{{actor.name}}" placeholder="Name" />
<div class="columns pt-2">
<div class="column is-6">
<div class="select is-fullwidth">
<select name="data.category" data-dtype="String">
{{#select data.category}}
<option value="Wizard">Wizard</option>
<option value="Apprentice">Apprentice</option>
<option value="Soldier">Soldier</option>
<option value="Creature">Creature</option>
<option value="Base">Base</option>
{{/select}}
</select>
</div>
</div>
<div class="column is-6">
<div class="select is-fullwidth">
<select name="data.class" data-dtype="String">
{{#select data.class}}
{{#if (or (eq data.category "Wizard") (eq data.category "Apprentice"))}}
<option value="Chronomancer">Chronomancer</option>
<option value="Elementalist">Elementalist</option>
<option value="Enchanter">Enchanter</option>
<option value="Illusionist">Illusionist</option>
<option value="Necromancer">Necromancer</option>
<option value="Sigilist">Sigilist</option>
<option value="Soothsayer">Soothsayer</option>
<option value="Summoner">Summoner</option>
<option value="Thaumaturge">Thaumaturge</option>
<option value="Witch">Witch</option>
{{/if}}
{{#if (eq data.category "Soldier")}}
<option value="Standard">Standard</option>
<option value="Specialist">Specialist</option>
{{/if}}
{{#if (eq data.category "Creature")}}
<option value="Uncontrolled">Uncontrolled</option>
<option value="Controlled">Controlled</option>
{{/if}}
{{#if (eq data.category "Base")}}
<option value="Inn">Inn</option>
<option value="Temple">Temple</option>
<option value="Crypt">Crypt</option>
<option value="Tower">Tower</option>
<option value="Treasury">Treasury</option>
<option value="Brewery">Brewery</option>
<option value="Library">Library</option>
<option value="Laboratory">Laboratory</option>
{{/if}}
{{/select}}
</select>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,98 @@
{{#if (ne data.category "Base")}}
<div class="columns is-multiline is-variable is-1">
<div class="column is-half py-1">
<div class="columns is-variable is-1">
<div class="column is-5">
<p class="control">
<span class="help">&nbsp;</span>
<button class="button is-danger is-outlined is-fullwidth rollable is-capitalized"
data-roll="d20+@data.health.value" data-label="{{data.health.rollname}}"> <span class="icon">
<i class="fas fa-dice-d20"></i>
</span>
<span>
Health
</span> </button>
</p>
</div>
<div class="column">
<p class="control">
<span class="help">Actual</span>
<input class="input" type="text" name="data.health.actual" value="{{data.health.actual}}"
data-dtype="Number">
</p>
</div>
<div class="column">
<span class="help">Effective</span>
<p class="control">
<input class="input" type="text" name="data.health.max" value="{{data.health.max}}"
data-dtype="Number">
</p>
</div>
<div class="column">
<span class="help">Current</span>
<p class="control">
<input class="input is-danger" type="text" name="data.health.value" value="{{data.health.value}}"
data-dtype="Number">
</p>
</div>
</div>
</div>
{{#each data.stats as |stat key|}}
<div class="column is-half py-1">
<div class="columns is-variable is-1">
<div class="column">
<p class="control is-5">
<span class="help">&nbsp;
</p>
<button class="button is-info is-outlined is-fullwidth rollable is-capitalized"
data-roll="d20+@stats.{{key}}.effective" data-label="{{stat.rollname}}" data-bonus="{{stat.bonus}}">
<span class="icon">
<i class="fas fa-dice-d20"></i>
</span>
<span>
{{key}}
</span>
</button>
</p>
</div>
<div class="column">
<p class="control">
<span class="help">Actual
</p>
<input class="input" type="text" name="data.stats.{{key}}.actual" value="{{stat.actual}}"
data-dtype="Number">
</p>
</div>
<div class="column">
<span class="help">Effective</span>
<p class="control">
<input class="input" type="text" name="data.stats.{{key}}.effective" value="{{stat.effective}}"
data-dtype="Number">
</p>
</div>
{{#if (or (eq key "fight") (eq key "shoot"))}}
<div class="column">
<span class="help">Dmg. Bonus</span>
<p class="control">
<input class="input" type="text" name="data.stats.{{key}}.bonus" value="{{stat.bonus}}"
data-dtype="Number">
</p>
</div>
{{/if}}
</div>
</div>
{{/each}}
</div>
{{/if}}

View File

@ -0,0 +1,27 @@
<div class="tab progression container is-fluid" data-group="primary" data-tab="progression">
<div class="columns">
<div class="column">
<label class="label">Level</label>
<input class="input" name="data.level" type="text" value="{{data.level}}" data-dtype="Number" />
</div>
<div class="column">
<label class="label">Scenario Experience</label>
<input class="input" name="data.expscenario" type="text" value="{{data.expscenario}}" data-dtype="Number" />
</div>
<div class="column">
<label class="label">Banked Experience</label>
<input class="input" name="data.expbanked" type="text" value="{{data.expbanked}}" data-dtype="Number" />
</div>
</div>
<progress class="progress is-large is-info" value="{{data.exptotal}}" max="100"></progress>
<h2 class="mb-2">Leveling Log</h2>
{{editor content=data.levellog target="data.levellog" button=true owner=owner editable=editable}}
</div>

View File

@ -0,0 +1,33 @@
<div class="tab homebase container is-fluid" data-group="primary" data-tab="homebase">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Treasury (gc)</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.basetreasury" type="text" value="{{data.basetreasury}}"
data-dtype="Number" />
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Base Resources</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.baseresources" type="text" value="{{data.baseresources}}"
data-dtype="String" />
</p>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,113 @@
<div class="tab gear container is-fluid" data-group="primary" data-tab="items">
{{#each actor.gear as |item id|}}
<div class="box item-card my-2 py-2" data-item-id="{{item._id}}">
<div class="media">
<div class="media-left">
<figure class="image is-square">
<img class="is-cover" src="{{item.img}}" title="{{item.name}}">
</figure>
<nav class="media-center">
<div class="field has-addons">
<p class="control">
<button class="button is-small item-edit">
<span class="icon is-small">
<i class="fas fa-edit"></i>
</span>
</button>
</p>
<p class="control">
<button class="button is-small item-delete">
<span class="icon is-small">
<i class="fas fa-times"></i>
</span>
</button>
</p>
</div>
</nav>
</div>
<div class="media-content">
<div class="is-capitalized is-size-5 has-text-weight-bold">{{item.name}}</div>
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-dark">{{item.data.category}}</span>
{{#if (ne item.data.subcategory "None")}}
<span class="tag is-light">{{item.data.subcategory}}</span>
{{/if}}
</div>
</div>
{{#if (ne item.data.damage null)}}
<div class="control">
<div class="tags has-addons">
<span class="tag is-info">Damage</span>
<span class="tag is-info is-light">{{item.data.damage}}</span>
</div>
</div>
{{/if}}
{{#if (ne item.data.range null)}}
<div class="control">
<div class="tags has-addons">
<span class="tag is-info">Range</span>
<span class="tag is-info is-light">{{item.data.range}}</span>
</div>
</div>
{{/if}}
{{#if (ne item.data.armour null)}}
<div class="control">
<div class="tags has-addons">
<span class="tag is-info">Armour</span>
<span class="tag is-info is-light">{{item.data.armour}}</span>
</div>
</div>
{{/if}}
{{#if (and (ne item.data.effect "") (ne item.data.effect null))}}
<div class="control">
<div class="tags has-addons">
<span class="tag is-primary">Effect</span>
<span class="tag is-primary is-light">{{item.data.effect}}</span>
</div>
</div>
{{/if}}
{{#if (ne item.data.price null)}}
<div class="control">
<div class="tags has-addons">
<span class="tag is-warning">Price</span>
<span class="tag is-warning is-light">{{item.data.price}}</span>
</div>
</div>
{{/if}}
{{#if (ne item.data.sale null)}}
<div class="control">
<div class="tags has-addons">
<span class="tag is-warning">Sale</span>
<span class="tag is-warning is-light">{{item.data.sale}}</span>
</div>
</div>
{{/if}}
</div>
<div class="media-content">{{formatHTMLMessage item.data.description}}</div>
</div>
</div>
</div>
{{/each}}
<button class="button is-outlined is-fullwidth item-create mb-4" data-type="item">
<span class="icon is-small">
<i class="fas fa-plus"></i>
</span>
<span>Add Item</span>
</button>
</div>

View File

@ -0,0 +1,19 @@
<div class="sheet-tabs tabs is-medium is-centered" data-group="primary">
<ul>
<li><a class="item" data-tab="items">{{#if (eq data.category "Base")}}Vault{{else}}Items{{/if}}</a></li>
{{#if (eq data.category "Base")}}
<li><a class="item" data-tab="homebase">Homebase</a></li>
{{/if}}
{{#if (or (eq data.category "Wizard") (eq data.category "Apprentice"))}}
<li><a class="item" data-tab="spells">Spells</a></li>
{{/if}}
{{#if (eq data.category "Wizard")}}
<li><a class="item" data-tab="progression">Experience</a></li>
{{/if}}
<li><a class="item" data-tab="notes">Notes</a></li>
</ul>
</div>

View File

@ -0,0 +1,3 @@
<div class="tab notes container" data-group="primary" data-tab="notes">
{{editor content=data.biography target="data.biography" button=true owner=owner editable=editable}}
</div>

View File

@ -0,0 +1,113 @@
<div class="tab spells container is-fluid" data-group="primary" data-tab="spells">
<div class="field is-horizontal">
<div class="field-body">
<div class="field is-expanded">
<div class="field has-addons">
<p class="control">
<a class="button is-static">
Empowerment
</a>
</p>
<p class="control is-expanded">
<input class="input" type="text" name="data.empowerment" value="{{data.empowerment}}"
data-dtype="Number">
</p>
</div>
</div>
</div>
</div>
{{#each actor.spells as |item id|}}
<div class="box item-card my-2 py-2" data-item-id="{{item._id}}">
<div class="media">
<div class="media-left">
<figure class="image is-square">
<img class="is-cover" src="{{item.img}}" title="{{item.name}}">
</figure>
<nav class="media-center">
<div class="field has-addons">
<p class="control">
<button class="button is-small item-edit">
<span class="icon is-small">
<i class="fas fa-edit"></i>
</span>
</button>
</p>
<p class="control">
<button class="button is-small item-delete">
<span class="icon is-small">
<i class="fas fa-times"></i>
</span>
</button>
</p>
</div>
</nav>
</div>
<div class="media-content">
<div class="is-capitalized is-size-5 has-text-weight-bold">{{item.name}}</div>
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<span class="tag is-dark">{{item.data.school}}</span>
<span class="tag is-light">{{item.data.category}}</span>
</div>
</div>
<div class="control">
<span class="tag is-primary is-light">{{item.data.alignment}}</span>
</div>
<div class="control">
<div class="tags has-addons">
<span class="tag is-info">BCN</span>
<span class="tag is-info is-light">{{item.data.bcn}}</span>
</div>
</div>
{{#if (ne item.data.improved 0)}}
<div class="control">
<div class="tags has-addons">
<span class="tag is-info">Improved</span>
<span class="tag is-info is-light">{{item.data.improved}}</span>
</div>
</div>
{{/if}}
</div>
<div class="media-content">{{formatHTMLMessage item.data.description}}</div>
<button class="button is-info is-outlined is-fullwidth rollable is-capitalized mt-2"
data-spell="1d20" data-label="{{name}}" data-bcn="{{item.data.bcn}}"
data-improved="{{item.data.improved}}" data-alignment="{{item.data.alignment}}"
data-empowerment="{{data.empowerment}}" data-description="{{item.data.description}}">
<span class="icon">
<i class="fas fa-dice-d20"></i>
</span>
<span>
Cast {{name}}
</span>
</button>
</div>
</div>
</div>
{{/each}}
<button class="button is-outlined is-fullwidth item-create mb-4" data-type="spell">
<span class="icon is-small">
<i class="fas fa-plus"></i>
</span>
<span>Add Spell</span>
</button>
</div>

View File

View File

@ -0,0 +1,198 @@
<form class="{{cssClass}}" autocomplete="off">
<div class="columns">
<div class="column is-2">
<figure class="image is-square">
<img class="is-cover" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</figure>
</div>
<div class="column">
<input class="input is-info is-medium" name="name" type="text" value="{{item.name}}" placeholder="Name" />
<div class="columns pt-2">
<div class="column is-6">
<div class="select is-fullwidth">
<select name="data.category" data-dtype="String">
{{#select data.category}}
<option value="Weapon">Weapon</option>
<option value="Armour">Armour</option>
<option value="Potion">Potion</option>
<option value="Scroll">Scroll</option>
<option value="Grimoire">Grimoire</option>
<option value="Other">Other</option>
{{/select}}
</select>
</div>
</div>
<div class="column is-6">
<div class="select is-fullwidth">
<select name="data.subcategory" data-dtype="String">
{{#select data.subcategory}}
<option value="None">None</option>
{{#if (eq data.category "Weapon")}}
<option value="Dagger">Dagger</option>
<option value="Hand Weapon">Hand Weapon</option>
<option value="Two-Handed Weapon">Two-Handed Weapon</option>
<option value="Staff">Staff</option>
<option value="Bow">Bow</option>
<option value="Crossbow">Crossbow</option>
<option value="Quiver">Quiver</option>
<option value="Unarmed">Unarmed</option>
<option value="Natural Weapon">Natural Weapon</option>
{{/if}}
{{#if (eq data.category "Armour")}}
<option value="Light">Light</option>
<option value="Heavy">Heavy</option>
<option value="Shield">Shield</option>
<option value="Natural">Natural</option>
{{/if}}
{{#if (eq data.category "Potion")}}
<option value="Lesser Potion">Lesser Potion</option>
<option value="Greater Potion">Greater Potion</option>
{{/if}}
{{/select}}
</select>
</div>
</div>
</div>
</div>
</div>
{{!-- Sheet Tab Navigation --}}
<div class="sheet-tabs tabs is-medium is-centered" data-group="primary">
<ul>
<li><a class="item" data-tab="attributes">Attributes</a></li>
<li><a class="item" data-tab="notes">Notes</a></li>
</ul>
</div>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Attributes Tab --}}
<div class="tab attributes container" data-group="primary" data-tab="attributes">
{{#if (eq data.category "Weapon")}}
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Damage Modifier</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.damage" type="text" value="{{data.damage}}" data-dtype="Number" />
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Maximum Range</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.range" type="text" value="{{data.range}}" data-dtype="Number" />
</p>
</div>
</div>
</div>
{{/if}}
{{#if (eq data.category "Armour")}}
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Armour</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.armour" type="text" value="{{data.armour}}" data-dtype="Number" />
</p>
</div>
</div>
</div>
{{/if}}
{{#if (or (eq data.category "Weapon") (eq data.category "Armour"))}}
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Magic Effect</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.effect" type="text" value="{{data.effect}}" data-dtype="String" />
</p>
</div>
</div>
</div>
{{/if}}
{{#if (or (eq data.category "Weapon") (eq data.category "Armour") (eq data.category "Potion"))}}
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Purchase Price</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.price" type="text" value="{{data.price}}" data-dtype="Number" />
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Sale Price</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.sale" type="text" value="{{data.sale}}" data-dtype="Number" />
</p>
</div>
</div>
</div>
{{#if (eq data.subcategory "Greater Potion")}}
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Ingredient Price</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" name="data.ingredient" type="text" value="{{data.ingredient}}" data-dtype="Number" />
</p>
</div>
</div>
</div>
{{/if}}
{{/if}}
</div>
{{!-- Notes Tab --}}
<div class="tab notes container" data-group="primary" data-tab="notes">
{{editor content=data.description target="data.description" button=true owner=owner editable=editable}}
</div>
</section>
</form>

View File

@ -0,0 +1,114 @@
<form class="{{cssClass}}" autocomplete="off">
<div class="columns">
<div class="column is-2">
<figure class="image is-square">
<img class="is-cover" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</figure>
</div>
<div class="column">
<input class="input is-info is-medium" name="name" type="text" value="{{item.name}}" placeholder="Name" />
</div>
</div>
<div class="columns is-variable is-1">
<div class="column">
<span class="help">School</span>
<div class="select is-fullwidth">
<select name="data.school" data-dtype="String">
{{#select data.school}}
<option value="Chronomancer">Chronomancer</option>
<option value="Elementalist">Elementalist</option>
<option value="Enchanter">Enchanter</option>
<option value="Illusionist">Illusionist</option>
<option value="Necromancer">Necromancer</option>
<option value="Sigilist">Sigilist</option>
<option value="Soothsayer">Soothsayer</option>
<option value="Summoner">Summoner</option>
<option value="Thaumaturge">Thaumaturge</option>
<option value="Witch">Witch</option>
{{/select}}
</select>
</div>
</div>
<div class="column">
<span class="help">Category</span>
<div class="select is-fullwidth">
<select name="data.category" data-dtype="String">
{{#select data.category}}
<option value="Area Effect">Area Effect</option>
<option value="Line of Sight">Line of Sight</option>
<option value="Out of Game (A)">Out of Game (A)</option>
<option value="Out of Game (B)">Out of Game (B)</option>
<option value="Self Only">Self Only</option>
<option value="Touch">Touch</option>
{{/select}}
</select>
</div>
</div>
<div class="column">
<span class="help">Alignment</span>
<div class="select is-fullwidth">
<select name="data.alignment" data-dtype="String">
{{#select data.alignment}}
<option value="Native">Native</option>
<option value="Aligned">Aligned</option>
<option value="Neutral">Neutral</option>
<option value="Opposed">Opposed</option>
{{/select}}
</select>
</div>
</div>
</div>
<div class="columns is-variable is-1">
<div class="column">
<p class="control">
<span class="help">Base Casting Number</span>
<input class="input" type="text" name="data.bcn" value="{{data.bcn}}" data-dtype="Number">
</p>
</div>
<div class="column">
<p class="control">
<span class="help">Improved</span>
<input class="input" type="text" name="data.improved" value="{{data.improved}}" data-dtype="Number">
</p>
</div>
</div>
</div>
{{!-- Sheet Tab Navigation --}}
<div class="sheet-tabs tabs is-medium is-centered" data-group="primary">
<ul>
<li><a class="item" data-tab="notes">Description</a></li>
</ul>
</div>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Description Tab --}}
<div class="tab notes container" data-group="primary" data-tab="notes">
{{editor content=data.description target="data.description" button=true owner=owner editable=editable}}
</div>
</section>
</form>