switch to using TypeScript

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

View file

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

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

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

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

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

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

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

View file

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

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

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