Migration FOundry v13/v14
This commit is contained in:
3214
mgt2.bundle.js
3214
mgt2.bundle.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
8
package.json
Normal file
8
package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "foundryvtt-mgt2",
|
||||
"description": "Mongoose Traveller 2nd Edition for FoundryVTT",
|
||||
"scripts": {
|
||||
"build": "rollup -c rollup.config.mjs",
|
||||
"watch": "rollup -c rollup.config.mjs --watch"
|
||||
}
|
||||
}
|
||||
8
rollup.config.mjs
Normal file
8
rollup.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
input: 'src/module/core.js',
|
||||
output: {
|
||||
file: 'mgt2.bundle.js',
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
},
|
||||
};
|
||||
@@ -82,6 +82,10 @@ export class TravellerActor extends Actor {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
async recalculateWeight() {
|
||||
if (this.type === "character") {
|
||||
return ActorCharacter.recalculateWeight(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export class ActorCharacter {
|
||||
// Eject software
|
||||
for (let item of $this.items) {
|
||||
if (item.system.hasOwnProperty("software") && item.system.computerId === d._id) {
|
||||
let clone = duplicate(item);
|
||||
let clone = foundry.utils.deepClone(item);
|
||||
clone.system.software.computerId = "";
|
||||
itemToUpdates.push(clone);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export class ActorCharacter {
|
||||
if (itemToUpdates.length > 0)
|
||||
await $this.updateEmbeddedDocuments('Item', itemToUpdates);
|
||||
|
||||
await this.recalculateWeight();
|
||||
await this.recalculateWeight($this);
|
||||
}
|
||||
|
||||
static async onUpdateDescendantDocuments($this, parent, collection, documents, changes, options, userId) {
|
||||
@@ -90,7 +90,7 @@ export class ActorCharacter {
|
||||
for (let computer of computers) {
|
||||
let newProcessingUsed = computerChanges[computer._id].processingUsed;
|
||||
if (computer.system.processingUsed !== newProcessingUsed) {
|
||||
const cloneComputer = duplicate($this.getEmbeddedDocument("Item", computer._id));
|
||||
const cloneComputer = foundry.utils.deepClone($this.getEmbeddedDocument("Item", computer._id));
|
||||
cloneComputer.system.processingUsed = newProcessingUsed;
|
||||
cloneComputer.system.overload = cloneComputer.system.processingUsed > cloneComputer.system.processing;
|
||||
updatedComputers.push(cloneComputer);
|
||||
@@ -138,7 +138,7 @@ export class ActorCharacter {
|
||||
}
|
||||
|
||||
if (recalculEncumbrance || recalculWeight) {
|
||||
const cloneActor = duplicate($this);
|
||||
const cloneActor = foundry.utils.deepClone($this);
|
||||
|
||||
await this.recalculateArmor($this, cloneActor);
|
||||
|
||||
@@ -163,7 +163,7 @@ export class ActorCharacter {
|
||||
|
||||
static async recalculateArmor($this, cloneActor) {
|
||||
if (cloneActor === null || cloneActor === undefined)
|
||||
cloneActor = duplicate($this);
|
||||
cloneActor = foundry.utils.deepClone($this);
|
||||
|
||||
let armor = 0;
|
||||
for (let item of $this.items) {
|
||||
@@ -180,7 +180,7 @@ export class ActorCharacter {
|
||||
static async recalculateWeight($this, cloneActor) {
|
||||
|
||||
if (cloneActor === null || cloneActor === undefined)
|
||||
cloneActor = duplicate($this);
|
||||
cloneActor = foundry.utils.deepClone($this);
|
||||
|
||||
let updatedContainers = [];
|
||||
let containerChanges = {};
|
||||
@@ -241,8 +241,8 @@ export class ActorCharacter {
|
||||
let newWeight = containerChanges[container._id].weight;
|
||||
let newCount = containerChanges[container._id].count;
|
||||
if (container.system.weight !== newWeight || container.system.count !== newCount) {
|
||||
//const cloneContainer = duplicate();
|
||||
const cloneContainer = duplicate($this.getEmbeddedDocument("Item", container._id));
|
||||
//const cloneContainer = foundry.utils.deepClone();
|
||||
const cloneContainer = foundry.utils.deepClone($this.getEmbeddedDocument("Item", container._id));
|
||||
//foundry.utils.setProperty(cloneContainer, "system.weight", newWeight);
|
||||
cloneContainer.system.weight = newWeight;
|
||||
cloneContainer.system.count = newCount;
|
||||
|
||||
4
src/module/applications/sheets/_module.mjs
Normal file
4
src/module/applications/sheets/_module.mjs
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as MGT2ActorSheet } from "./base-actor-sheet.mjs";
|
||||
export { default as TravellerCharacterSheet } from "./character-sheet.mjs";
|
||||
export { default as TravellerVehiculeSheet } from "./vehicule-sheet.mjs";
|
||||
export { default as TravellerItemSheet } from "./item-sheet.mjs";
|
||||
104
src/module/applications/sheets/base-actor-sheet.mjs
Normal file
104
src/module/applications/sheets/base-actor-sheet.mjs
Normal file
@@ -0,0 +1,104 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class MGT2ActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||
|
||||
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this._sheetMode = this.constructor.SHEET_MODES.PLAY;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["mgt2", "sheet", "actor"],
|
||||
position: {
|
||||
width: 780,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
dragDrop: [{ dragSelector: ".drag-item-list", dropSelector: ".drop-item-list" }],
|
||||
actions: {
|
||||
toggleSheet: MGT2ActorSheet.#onToggleSheet,
|
||||
},
|
||||
}
|
||||
|
||||
get isPlayMode() {
|
||||
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY;
|
||||
return this._sheetMode === this.constructor.SHEET_MODES.PLAY;
|
||||
}
|
||||
|
||||
get isEditMode() {
|
||||
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY;
|
||||
return this._sheetMode === this.constructor.SHEET_MODES.EDIT;
|
||||
}
|
||||
|
||||
tabGroups = { primary: "stats" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const base = await super._prepareContext();
|
||||
const actor = this.document;
|
||||
return {
|
||||
...base,
|
||||
actor: actor,
|
||||
// Flat shorthands for template backward-compat (AppV1 style)
|
||||
name: actor.name,
|
||||
img: actor.img,
|
||||
cssClass: this.isEditable ? "editable" : "locked",
|
||||
system: actor.system,
|
||||
source: actor.toObject(),
|
||||
fields: actor.schema.fields,
|
||||
systemFields: actor.system.schema.fields,
|
||||
isEditable: this.isEditable,
|
||||
isEditMode: this.isEditMode,
|
||||
isPlayMode: this.isPlayMode,
|
||||
isGM: game.user.isGM,
|
||||
config: CONFIG.MGT2,
|
||||
};
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
this._activateTabGroups();
|
||||
}
|
||||
|
||||
_activateTabGroups() {
|
||||
for (const [group, activeTab] of Object.entries(this.tabGroups)) {
|
||||
const nav = this.element.querySelector(`nav[data-group="${group}"]`);
|
||||
if (!nav) continue;
|
||||
|
||||
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||
link.classList.toggle('active', link.dataset.tab === activeTab);
|
||||
link.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
this.tabGroups[group] = link.dataset.tab;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
|
||||
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach(content => {
|
||||
content.classList.toggle('active', content.dataset.tab === activeTab);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_canDragDrop(selector) {
|
||||
return this.isEditable;
|
||||
}
|
||||
|
||||
static async #onToggleSheet(event) {
|
||||
event.preventDefault();
|
||||
this._sheetMode = this.isPlayMode
|
||||
? this.constructor.SHEET_MODES.EDIT
|
||||
: this.constructor.SHEET_MODES.PLAY;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
848
src/module/applications/sheets/character-sheet.mjs
Normal file
848
src/module/applications/sheets/character-sheet.mjs
Normal file
@@ -0,0 +1,848 @@
|
||||
import MGT2ActorSheet from "./base-actor-sheet.mjs";
|
||||
import { MGT2 } from "../../config.js";
|
||||
import { MGT2Helper } from "../../helper.js";
|
||||
import { RollPromptHelper } from "../../roll-prompt.js";
|
||||
import { CharacterPrompts } from "../../actors/character-prompts.js";
|
||||
|
||||
export default class TravellerCharacterSheet extends MGT2ActorSheet {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "character", "nopad"],
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "TYPES.Actor.character",
|
||||
},
|
||||
actions: {
|
||||
...super.DEFAULT_OPTIONS.actions,
|
||||
createItem: TravellerCharacterSheet.#onCreateItem,
|
||||
editItem: TravellerCharacterSheet.#onEditItem,
|
||||
deleteItem: TravellerCharacterSheet.#onDeleteItem,
|
||||
equipItem: TravellerCharacterSheet.#onEquipItem,
|
||||
itemStorageIn: TravellerCharacterSheet.#onItemStorageIn,
|
||||
itemStorageOut: TravellerCharacterSheet.#onItemStorageOut,
|
||||
softwareEject: TravellerCharacterSheet.#onSoftwareEject,
|
||||
createContainer: TravellerCharacterSheet.#onContainerCreate,
|
||||
editContainer: TravellerCharacterSheet.#onContainerEdit,
|
||||
deleteContainer: TravellerCharacterSheet.#onContainerDelete,
|
||||
roll: TravellerCharacterSheet.#onRoll,
|
||||
openConfig: TravellerCharacterSheet.#onOpenConfig,
|
||||
openCharacteristic: TravellerCharacterSheet.#onOpenCharacteristic,
|
||||
traitCreate: TravellerCharacterSheet.#onTraitCreate,
|
||||
traitEdit: TravellerCharacterSheet.#onTraitEdit,
|
||||
traitDelete: TravellerCharacterSheet.#onTraitDelete,
|
||||
openEditor: TravellerCharacterSheet.#onOpenEditor,
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/mgt2/templates/actors/actor-sheet.html",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "inventory",
|
||||
characteristics: "core",
|
||||
inventory: "onhand",
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext();
|
||||
const actor = this.document;
|
||||
|
||||
context.settings = {
|
||||
weightUnit: "kg",
|
||||
usePronouns: game.settings.get("mgt2", "usePronouns"),
|
||||
useGender: game.settings.get("mgt2", "useGender"),
|
||||
showLife: game.settings.get("mgt2", "showLife"),
|
||||
};
|
||||
context.isGM = game.user.isGM;
|
||||
context.showTrash = false;
|
||||
context.initiative = actor.getInitiative();
|
||||
|
||||
this._prepareCharacterItems(context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
_prepareCharacterItems(context) {
|
||||
const actor = this.document;
|
||||
const settings = context.settings;
|
||||
const items = actor.items;
|
||||
|
||||
const weapons = [], armors = [], augments = [], computers = [], softwares = [];
|
||||
const miscItems = [], equipments = [], containerItems = [], careers = [];
|
||||
const skills = [], psionics = [], diseases = [], wounds = [], contacts = [];
|
||||
const actorContainers = [];
|
||||
|
||||
for (let i of items) {
|
||||
if (i.type === "container") {
|
||||
actorContainers.push(i);
|
||||
} else if (i.type === "computer") {
|
||||
computers.push(i);
|
||||
i._subItems = [];
|
||||
if (i.system.overload === true)
|
||||
i._overloadClass = "computer-overload";
|
||||
}
|
||||
}
|
||||
|
||||
actorContainers.sort(MGT2Helper.compareByName);
|
||||
|
||||
const containers = [{ name: "(tous)", _id: "" }].concat(actorContainers);
|
||||
const containerIndex = new Map();
|
||||
|
||||
for (let c of actorContainers) {
|
||||
containerIndex.set(c._id, c);
|
||||
|
||||
if (c.system.weight > 0) {
|
||||
const w = MGT2Helper.convertWeightForDisplay(c.system.weight) + " " + settings.weightUnit;
|
||||
c._display = c.name.length > 12 ? `${c.name.substring(0, 12)}... (${w})` : `${c.name} (${w})`;
|
||||
} else {
|
||||
c._display = c.name.length > 12 ? c.name.substring(0, 12) + "..." : c.name;
|
||||
}
|
||||
|
||||
if (c.system.onHand === true)
|
||||
c._subItems = [];
|
||||
}
|
||||
|
||||
const containerView = actor.system.containerView;
|
||||
let currentContainerView = containerView !== "" ? containerIndex.get(containerView) : null;
|
||||
|
||||
context.containerView = currentContainerView || null;
|
||||
context.containerWeight = currentContainerView
|
||||
? MGT2Helper.convertWeightForDisplay(currentContainerView.system.weight)
|
||||
: MGT2Helper.convertWeightForDisplay(0);
|
||||
context.containerShowAll = containerView === "";
|
||||
|
||||
for (let i of items) {
|
||||
const item = i.system;
|
||||
|
||||
if (item.hasOwnProperty("weight") && item.weight > 0) {
|
||||
i._weight = isNaN(item.quantity)
|
||||
? MGT2Helper.convertWeightForDisplay(item.weight) + " " + settings.weightUnit
|
||||
: MGT2Helper.convertWeightForDisplay(item.weight * item.quantity) + " " + settings.weightUnit;
|
||||
}
|
||||
|
||||
if (item.hasOwnProperty("container") && item.container.id !== "" && item.container.id !== undefined) {
|
||||
const container = containerIndex.get(item.container.id);
|
||||
if (container === undefined) {
|
||||
if (context.containerShowAll) {
|
||||
i._containerName = "#deleted#";
|
||||
containerItems.push(i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (container.system.locked && !game.user.isGM) continue;
|
||||
|
||||
if (container.system.onHand === true)
|
||||
container._subItems.push(i);
|
||||
|
||||
if (context.containerShowAll || actor.system.containerView === item.container.id) {
|
||||
i._containerName = container.name;
|
||||
containerItems.push(i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.hasOwnProperty("equipped")) {
|
||||
i._canEquip = true;
|
||||
i._toggleClass = item.equipped ? "active" : "";
|
||||
} else {
|
||||
i._canEquip = false;
|
||||
}
|
||||
|
||||
switch (i.type) {
|
||||
case "equipment":
|
||||
(i.system.subType === "augment" ? augments : equipments).push(i);
|
||||
break;
|
||||
|
||||
case "armor":
|
||||
if (i.system.options?.length > 0)
|
||||
i._subInfo = i.system.options.map(x => x.name).join(", ");
|
||||
armors.push(i);
|
||||
break;
|
||||
|
||||
case "computer":
|
||||
if (i.system.options?.length > 0)
|
||||
i._subInfo = i.system.options.map(x => x.name).join(", ");
|
||||
break;
|
||||
|
||||
case "item":
|
||||
if (i.system.subType === "software") {
|
||||
if (i.system.software.computerId && i.system.software.computerId !== "") {
|
||||
const computer = computers.find(x => x._id === i.system.software.computerId);
|
||||
if (computer !== undefined) computer._subItems.push(i);
|
||||
else softwares.push(i);
|
||||
} else {
|
||||
i._display = i.system.software.bandwidth > 0
|
||||
? `${i.name} (${i.system.software.bandwidth})`
|
||||
: i.name;
|
||||
softwares.push(i);
|
||||
}
|
||||
} else {
|
||||
miscItems.push(i);
|
||||
}
|
||||
break;
|
||||
|
||||
case "weapon":
|
||||
i._range = i.system.range.isMelee
|
||||
? game.i18n.localize("MGT2.Melee")
|
||||
: MGT2Helper.getRangeDisplay(i.system.range);
|
||||
if (i.system.traits?.length > 0)
|
||||
i._subInfo = i.system.traits.map(x => x.name).join(", ");
|
||||
weapons.push(i);
|
||||
break;
|
||||
|
||||
case "career":
|
||||
careers.push(i);
|
||||
break;
|
||||
|
||||
case "contact":
|
||||
contacts.push(i);
|
||||
break;
|
||||
|
||||
case "disease":
|
||||
(i.system.subType === "wound" ? wounds : diseases).push(i);
|
||||
break;
|
||||
|
||||
case "talent":
|
||||
if (i.system.subType === "skill") {
|
||||
skills.push(i);
|
||||
} else {
|
||||
if (MGT2Helper.hasValue(i.system.psionic, "reach"))
|
||||
i._reach = game.i18n.localize(`MGT2.PsionicReach.${i.system.psionic.reach}`);
|
||||
if (MGT2Helper.hasValue(i.system.roll, "difficulty"))
|
||||
i._difficulty = game.i18n.localize(`MGT2.Difficulty.${i.system.roll.difficulty}`);
|
||||
psionics.push(i);
|
||||
}
|
||||
break;
|
||||
|
||||
case "container":
|
||||
if (i.system.onHand === true)
|
||||
miscItems.push(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const byName = MGT2Helper.compareByName;
|
||||
const byEquipName = (a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||
|
||||
context.encumbranceNormal = MGT2Helper.convertWeightForDisplay(actor.system.inventory.encumbrance.normal);
|
||||
context.encumbranceHeavy = MGT2Helper.convertWeightForDisplay(actor.system.inventory.encumbrance.heavy);
|
||||
|
||||
const totalWeight = actor.system.inventory.weight;
|
||||
if (totalWeight > actor.system.inventory.encumbrance.heavy) {
|
||||
context.encumbranceClasses = "encumbrance-heavy";
|
||||
context.encumbrance = 2;
|
||||
} else if (totalWeight > actor.system.inventory.encumbrance.normal) {
|
||||
context.encumbranceClasses = "encumbrance-normal";
|
||||
context.encumbrance = 1;
|
||||
} else {
|
||||
context.encumbrance = 0;
|
||||
}
|
||||
|
||||
if (softwares.length > 0) { softwares.sort(byName); context.softwares = softwares; }
|
||||
augments.sort(byEquipName); context.augments = augments;
|
||||
armors.sort(byEquipName); context.armors = armors;
|
||||
computers.sort(byEquipName); context.computers = computers;
|
||||
context.careers = careers;
|
||||
contacts.sort(byName); context.contacts = contacts;
|
||||
containers.sort(byName); context.containers = containers;
|
||||
diseases.sort(byName); context.diseases = diseases;
|
||||
context.wounds = wounds;
|
||||
equipments.sort(byEquipName); context.equipments = equipments;
|
||||
miscItems.sort(byEquipName); context.items = miscItems;
|
||||
actorContainers.sort(byName); context.actorContainers = actorContainers;
|
||||
skills.sort(byName); context.skills = skills;
|
||||
psionics.sort(byName); context.psionics = psionics;
|
||||
weapons.sort(byEquipName); context.weapons = weapons;
|
||||
|
||||
if (containerItems.length > 0) {
|
||||
containerItems.sort((a, b) => {
|
||||
const r = a._containerName.localeCompare(b._containerName);
|
||||
return r !== 0 ? r : a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||
});
|
||||
}
|
||||
context.containerItems = containerItems;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Event Binding (AppV2 _onRender — replaces jQuery activateListeners)
|
||||
// Templates still use CSS class selectors, so we bind manually here.
|
||||
// =========================================================
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options);
|
||||
const html = this.element;
|
||||
if (!this.isEditable) return;
|
||||
|
||||
this._bindClassEvent(html, ".roll", "click", TravellerCharacterSheet.#onRoll);
|
||||
this._bindClassEvent(html, ".cfg-characteristic", "click", TravellerCharacterSheet.#onOpenCharacteristic);
|
||||
this._bindClassEvent(html, ".item-create", "click", TravellerCharacterSheet.#onCreateItem);
|
||||
this._bindClassEvent(html, ".item-edit", "click", TravellerCharacterSheet.#onEditItem);
|
||||
this._bindClassEvent(html, ".item-delete", "click", TravellerCharacterSheet.#onDeleteItem);
|
||||
this._bindClassEvent(html, ".item-equip", "click", TravellerCharacterSheet.#onEquipItem);
|
||||
this._bindClassEvent(html, ".item-storage-in", "click", TravellerCharacterSheet.#onItemStorageIn);
|
||||
this._bindClassEvent(html, ".item-storage-out", "click", TravellerCharacterSheet.#onItemStorageOut);
|
||||
this._bindClassEvent(html, ".software-eject", "click", TravellerCharacterSheet.#onSoftwareEject);
|
||||
this._bindClassEvent(html, ".container-create", "click", TravellerCharacterSheet.#onContainerCreate);
|
||||
this._bindClassEvent(html, ".container-edit", "click", TravellerCharacterSheet.#onContainerEdit);
|
||||
this._bindClassEvent(html, ".container-delete", "click", TravellerCharacterSheet.#onContainerDelete);
|
||||
this._bindClassEvent(html, ".traits-create", "click", TravellerCharacterSheet.#onTraitCreate);
|
||||
this._bindClassEvent(html, ".traits-edit", "click", TravellerCharacterSheet.#onTraitEdit);
|
||||
this._bindClassEvent(html, ".traits-delete", "click", TravellerCharacterSheet.#onTraitDelete);
|
||||
this._bindClassEvent(html, "[data-editor='open']", "click", TravellerCharacterSheet.#onOpenEditor);
|
||||
html.querySelector("[name='config']")?.addEventListener("click", (ev) => TravellerCharacterSheet.#onOpenConfig.call(this, ev, ev.currentTarget));
|
||||
}
|
||||
|
||||
/** Helper: bind a handler to all matching elements, with `this` set to the sheet instance */
|
||||
_bindClassEvent(html, selector, event, handler) {
|
||||
for (const el of html.querySelectorAll(selector)) {
|
||||
el.addEventListener(event, (ev) => handler.call(this, ev, ev.currentTarget));
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Drag & Drop
|
||||
// =========================================================
|
||||
|
||||
/** @override */
|
||||
async _onDrop(event) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
const dropData = MGT2Helper.getDataFromDropEvent(event);
|
||||
if (!dropData) return false;
|
||||
|
||||
const sourceItemData = await MGT2Helper.getItemDataFromDropData(dropData);
|
||||
|
||||
if (sourceItemData.type === "species") {
|
||||
const update = {
|
||||
system: {
|
||||
personal: {
|
||||
species: sourceItemData.name,
|
||||
speciesText: {
|
||||
description: sourceItemData.system.description,
|
||||
descriptionLong: sourceItemData.system.descriptionLong,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
update.system.personal.traits = this.actor.system.personal.traits.concat(sourceItemData.system.traits);
|
||||
|
||||
if (sourceItemData.system.modifiers?.length > 0) {
|
||||
update.system.characteristics = {};
|
||||
for (let modifier of sourceItemData.system.modifiers) {
|
||||
if (MGT2Helper.hasValue(modifier, "characteristic") && MGT2Helper.hasValue(modifier, "value")) {
|
||||
const c = this.actor.system.characteristics[modifier.characteristic];
|
||||
const updateValue = { value: c.value + modifier.value };
|
||||
if (c.showMax) updateValue.max = c.max + modifier.value;
|
||||
update.system.characteristics[modifier.characteristic] = updateValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.actor.update(update);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (["contact", "disease", "career", "talent"].includes(sourceItemData.type)) {
|
||||
let transferData = {};
|
||||
try { transferData = sourceItemData.toJSON(); } catch (e) { transferData = sourceItemData; }
|
||||
delete transferData._id;
|
||||
delete transferData.id;
|
||||
await this.actor.createEmbeddedDocuments("Item", [transferData]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!["armor", "weapon", "computer", "container", "item", "equipment"].includes(sourceItemData.type)) return false;
|
||||
|
||||
const target = event.target.closest(".table-row");
|
||||
let targetId = null;
|
||||
let targetItem = null;
|
||||
if (target !== null) {
|
||||
targetId = target.dataset.itemId;
|
||||
targetItem = this.actor.getEmbeddedDocument("Item", targetId);
|
||||
}
|
||||
|
||||
let sourceItem = this.actor.getEmbeddedDocument("Item", sourceItemData.id);
|
||||
if (sourceItem) {
|
||||
if (!targetItem) return false;
|
||||
sourceItem = foundry.utils.deepClone(sourceItem);
|
||||
if (sourceItem._id === targetId) return false;
|
||||
|
||||
if (targetItem.type === "item" || targetItem.type === "equipment") {
|
||||
if (targetItem.system.subType === "software")
|
||||
sourceItem.system.software.computerId = targetItem.system.software.computerId;
|
||||
else
|
||||
sourceItem.system.container.id = targetItem.system.container.id;
|
||||
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
|
||||
return true;
|
||||
} else if (targetItem.type === "computer") {
|
||||
sourceItem.system.software.computerId = targetId;
|
||||
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
|
||||
return true;
|
||||
} else if (targetItem.type === "container") {
|
||||
if (targetItem.system.locked && !game.user.isGM) {
|
||||
ui.notifications.error("Verrouillé");
|
||||
} else {
|
||||
sourceItem.system.container.id = targetId;
|
||||
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let transferData = {};
|
||||
try { transferData = sourceItemData.toJSON(); } catch (e) { transferData = sourceItemData; }
|
||||
delete transferData._id;
|
||||
delete transferData.id;
|
||||
|
||||
const recalcWeight = transferData.system.hasOwnProperty("weight");
|
||||
|
||||
if (transferData.system.hasOwnProperty("container")) transferData.system.container.id = "";
|
||||
if (transferData.type === "item" && transferData.system.subType === "software") transferData.system.software.computerId = "";
|
||||
if (transferData.type === "container") transferData.system.onHand = true;
|
||||
if (transferData.system.hasOwnProperty("equipment")) transferData.system.equipped = false;
|
||||
|
||||
if (targetItem !== null) {
|
||||
if (transferData.type === "item" && transferData.system.subType === "software") {
|
||||
if (targetItem.type === "item" && targetItem.system.subType === "software")
|
||||
transferData.system.software.computerId = targetItem.system.software.computerId;
|
||||
else if (targetItem.type === "computer")
|
||||
transferData.system.software.computerId = targetItem._id;
|
||||
} else if (["armor", "computer", "equipment", "item", "weapon"].includes(transferData.type)) {
|
||||
if (targetItem.type === "container") {
|
||||
if (!targetItem.system.locked || game.user.isGM)
|
||||
transferData.system.container.id = targetId;
|
||||
} else {
|
||||
transferData.system.container.id = targetItem.system.container.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.actor.createEmbeddedDocuments("Item", [transferData]);
|
||||
if (recalcWeight) await this.actor.recalculateWeight();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Actions (static private methods)
|
||||
// =========================================================
|
||||
|
||||
static async #onCreateItem(event, target) {
|
||||
event.preventDefault();
|
||||
const data = {
|
||||
name: target.dataset.createName,
|
||||
type: target.dataset.typeItem,
|
||||
};
|
||||
if (target.dataset.subtype) {
|
||||
data.system = { subType: target.dataset.subtype };
|
||||
}
|
||||
const cls = getDocumentClass("Item");
|
||||
return cls.create(data, { parent: this.actor });
|
||||
}
|
||||
|
||||
static async #onEditItem(event, target) {
|
||||
event.preventDefault();
|
||||
const li = target.closest("[data-item-id]");
|
||||
const item = this.actor.getEmbeddedDocument("Item", li?.dataset.itemId);
|
||||
if (item) item.sheet.render(true);
|
||||
}
|
||||
|
||||
static async #onDeleteItem(event, target) {
|
||||
event.preventDefault();
|
||||
const li = target.closest("[data-item-id]");
|
||||
if (!li?.dataset.itemId) return;
|
||||
this.actor.deleteEmbeddedDocuments("Item", [li.dataset.itemId]);
|
||||
}
|
||||
|
||||
static async #onEquipItem(event, target) {
|
||||
event.preventDefault();
|
||||
const li = target.closest("[data-item-id]");
|
||||
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId));
|
||||
if (!item) return;
|
||||
item.system.equipped = !item.system.equipped;
|
||||
this.actor.updateEmbeddedDocuments("Item", [item]);
|
||||
}
|
||||
|
||||
static async #onItemStorageIn(event, target) {
|
||||
event.preventDefault();
|
||||
const li = target.closest("[data-item-id]");
|
||||
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId));
|
||||
if (!item) return;
|
||||
|
||||
if (item.type === "container") {
|
||||
item.system.onHand = false;
|
||||
} else {
|
||||
const containers = this.actor.getContainers();
|
||||
let container;
|
||||
const dropInId = this.actor.system.containerDropIn;
|
||||
|
||||
if (!dropInId) {
|
||||
container = containers.length === 0
|
||||
? await getDocumentClass("Item").create({ name: "New container", type: "container" }, { parent: this.actor })
|
||||
: containers[0];
|
||||
} else {
|
||||
container = containers.find(x => x._id === dropInId);
|
||||
}
|
||||
|
||||
if (container?.system.locked && !game.user.isGM) {
|
||||
ui.notifications.error("Objet verrouillé");
|
||||
return;
|
||||
}
|
||||
item.system.container.id = container._id;
|
||||
}
|
||||
this.actor.updateEmbeddedDocuments("Item", [item]);
|
||||
}
|
||||
|
||||
static async #onItemStorageOut(event, target) {
|
||||
event.preventDefault();
|
||||
const li = target.closest("[data-item-id]");
|
||||
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId));
|
||||
if (!item) return;
|
||||
item.system.container.id = "";
|
||||
this.actor.updateEmbeddedDocuments("Item", [item]);
|
||||
}
|
||||
|
||||
static async #onSoftwareEject(event, target) {
|
||||
event.preventDefault();
|
||||
const li = target.closest("[data-item-id]");
|
||||
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId));
|
||||
if (!item) return;
|
||||
item.system.software.computerId = "";
|
||||
this.actor.updateEmbeddedDocuments("Item", [item]);
|
||||
}
|
||||
|
||||
static async #onContainerCreate(event) {
|
||||
event.preventDefault();
|
||||
const cls = getDocumentClass("Item");
|
||||
return cls.create({ name: "New container", type: "container" }, { parent: this.actor });
|
||||
}
|
||||
|
||||
static async #onContainerEdit(event) {
|
||||
event.preventDefault();
|
||||
const container = this.actor.getEmbeddedDocument("Item", this.actor.system.containerView);
|
||||
if (container) container.sheet.render(true);
|
||||
}
|
||||
|
||||
static async #onContainerDelete(event) {
|
||||
event.preventDefault();
|
||||
const containers = this.actor.getContainers();
|
||||
const container = containers.find(x => x._id === this.actor.system.containerView);
|
||||
if (!container) return;
|
||||
|
||||
const containerItems = this.actor.items.filter(
|
||||
x => x.system.hasOwnProperty("container") && x.system.container.id === container._id
|
||||
);
|
||||
|
||||
if (containerItems.length > 0) {
|
||||
for (let item of containerItems) {
|
||||
let clone = foundry.utils.deepClone(item);
|
||||
clone.system.container.id = "";
|
||||
this.actor.updateEmbeddedDocuments("Item", [clone]);
|
||||
}
|
||||
}
|
||||
|
||||
const cloneActor = foundry.utils.deepClone(this.actor);
|
||||
cloneActor.system.containerView = "";
|
||||
if (cloneActor.system.containerDropIn === container._id) {
|
||||
cloneActor.system.containerDropIn = "";
|
||||
const remaining = containers.filter(x => x._id !== container._id);
|
||||
if (remaining.length > 0) cloneActor.system.containerDropIn = remaining[0]._id;
|
||||
}
|
||||
|
||||
this.actor.deleteEmbeddedDocuments("Item", [container._id]);
|
||||
this.actor.update(cloneActor);
|
||||
}
|
||||
|
||||
static async #onRoll(event, target) {
|
||||
event.preventDefault();
|
||||
|
||||
const rollOptions = {
|
||||
rollTypeName: game.i18n.localize("MGT2.RollPrompt.Roll"),
|
||||
rollObjectName: "",
|
||||
characteristics: [{ _id: "", name: "" }],
|
||||
characteristic: "",
|
||||
skills: [],
|
||||
skill: "",
|
||||
fatigue: this.actor.system.states.fatigue,
|
||||
encumbrance: this.actor.system.states.encumbrance,
|
||||
difficulty: null,
|
||||
damageFormula: null,
|
||||
};
|
||||
|
||||
const cardButtons = [];
|
||||
for (const [key, label] of Object.entries(MGT2.Characteristics)) {
|
||||
const c = this.actor.system.characteristics[key];
|
||||
if (c.show) {
|
||||
rollOptions.characteristics.push({
|
||||
_id: key,
|
||||
name: game.i18n.localize(label) + MGT2Helper.getDisplayDM(c.dm),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (let item of this.actor.items) {
|
||||
if (item.type === "talent" && item.system.subType === "skill")
|
||||
rollOptions.skills.push({ _id: item._id, name: item.getRollDisplay() });
|
||||
}
|
||||
|
||||
rollOptions.skills.sort(MGT2Helper.compareByName);
|
||||
rollOptions.skills = [{ _id: "NP", name: game.i18n.localize("MGT2.Items.NotProficient") }].concat(rollOptions.skills);
|
||||
|
||||
let itemObj = null;
|
||||
let isInitiative = false;
|
||||
const rollType = target.dataset.roll;
|
||||
|
||||
if (rollType === "initiative") {
|
||||
rollOptions.rollTypeName = game.i18n.localize("MGT2.RollPrompt.InitiativeRoll");
|
||||
rollOptions.characteristic = this.actor.system.config.initiative;
|
||||
isInitiative = true;
|
||||
} else if (rollType === "characteristic") {
|
||||
rollOptions.characteristic = target.dataset.rollCharacteristic;
|
||||
rollOptions.rollTypeName = game.i18n.localize("MGT2.RollPrompt.CharacteristicRoll");
|
||||
rollOptions.rollObjectName = game.i18n.localize(`MGT2.Characteristics.${rollOptions.characteristic}.name`);
|
||||
} else {
|
||||
if (rollType === "skill") {
|
||||
rollOptions.skill = target.dataset.rollSkill;
|
||||
itemObj = this.actor.getEmbeddedDocument("Item", rollOptions.skill);
|
||||
rollOptions.rollTypeName = game.i18n.localize("MGT2.RollPrompt.SkillRoll");
|
||||
rollOptions.rollObjectName = itemObj.name;
|
||||
} else if (rollType === "psionic") {
|
||||
rollOptions.rollTypeName = game.i18n.localize("MGT2.RollPrompt.PsionicRoll");
|
||||
}
|
||||
|
||||
if (itemObj === null && target.dataset.itemId) {
|
||||
itemObj = this.actor.getEmbeddedDocument("Item", target.dataset.itemId);
|
||||
rollOptions.rollObjectName = itemObj.name;
|
||||
if (itemObj.type === "weapon") rollOptions.rollTypeName = game.i18n.localize("TYPES.Item.weapon");
|
||||
else if (itemObj.type === "armor") rollOptions.rollTypeName = game.i18n.localize("TYPES.Item.armor");
|
||||
else if (itemObj.type === "computer") rollOptions.rollTypeName = game.i18n.localize("TYPES.Item.computer");
|
||||
}
|
||||
|
||||
if (rollType === "psionic" && itemObj) {
|
||||
rollOptions.rollObjectName = itemObj.name;
|
||||
if (MGT2Helper.hasValue(itemObj.system.psionic, "duration")) {
|
||||
cardButtons.push({
|
||||
label: game.i18n.localize("MGT2.Items.Duration"),
|
||||
formula: itemObj.system.psionic.duration,
|
||||
message: {
|
||||
objectName: itemObj.name,
|
||||
flavor: "{0} ".concat(game.i18n.localize(`MGT2.Durations.${itemObj.system.psionic.durationUnit}`)),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (itemObj?.system.hasOwnProperty("damage")) {
|
||||
rollOptions.damageFormula = itemObj.system.damage;
|
||||
if (itemObj.type === "disease") {
|
||||
if (itemObj.system.subType === "disease")
|
||||
rollOptions.rollTypeName = game.i18n.localize("MGT2.DiseaseSubType.disease");
|
||||
else if (itemObj.system.subType === "poison")
|
||||
rollOptions.rollTypeName = game.i18n.localize("MGT2.DiseaseSubType.poison");
|
||||
}
|
||||
}
|
||||
|
||||
if (itemObj?.system.hasOwnProperty("roll")) {
|
||||
if (MGT2Helper.hasValue(itemObj.system.roll, "characteristic")) rollOptions.characteristic = itemObj.system.roll.characteristic;
|
||||
if (MGT2Helper.hasValue(itemObj.system.roll, "skill")) rollOptions.skill = itemObj.system.roll.skill;
|
||||
if (MGT2Helper.hasValue(itemObj.system.roll, "difficulty")) rollOptions.difficulty = itemObj.system.roll.difficulty;
|
||||
}
|
||||
}
|
||||
|
||||
const userRollData = await RollPromptHelper.roll(rollOptions);
|
||||
|
||||
const rollModifiers = [];
|
||||
const rollFormulaParts = [];
|
||||
|
||||
if (userRollData.diceModifier) {
|
||||
rollFormulaParts.push("3d6", userRollData.diceModifier);
|
||||
} else {
|
||||
rollFormulaParts.push("2d6");
|
||||
}
|
||||
|
||||
if (userRollData.characteristic) {
|
||||
const c = this.actor.system.characteristics[userRollData.characteristic];
|
||||
rollFormulaParts.push(MGT2Helper.getFormulaDM(c.dm));
|
||||
rollModifiers.push(game.i18n.localize(`MGT2.Characteristics.${userRollData.characteristic}.name`) + MGT2Helper.getDisplayDM(c.dm));
|
||||
}
|
||||
|
||||
if (userRollData.skill) {
|
||||
if (userRollData.skill === "NP") {
|
||||
rollFormulaParts.push("-3");
|
||||
rollModifiers.push(game.i18n.localize("MGT2.Items.NotProficient"));
|
||||
} else {
|
||||
const skillObj = this.actor.getEmbeddedDocument("Item", userRollData.skill);
|
||||
rollFormulaParts.push(MGT2Helper.getFormulaDM(skillObj.system.level));
|
||||
rollModifiers.push(skillObj.getRollDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
if (userRollData.psionic) {
|
||||
const psionicObj = this.actor.getEmbeddedDocument("Item", userRollData.psionic);
|
||||
rollFormulaParts.push(MGT2Helper.getFormulaDM(psionicObj.system.level));
|
||||
rollModifiers.push(psionicObj.getRollDisplay());
|
||||
}
|
||||
|
||||
if (userRollData.timeframes && userRollData.timeframes !== "" && userRollData.timeframes !== "Normal") {
|
||||
rollModifiers.push(game.i18n.localize(`MGT2.Timeframes.${userRollData.timeframes}`));
|
||||
rollFormulaParts.push(userRollData.timeframes === "Slower" ? "+2" : "-2");
|
||||
}
|
||||
|
||||
if (userRollData.encumbrance === true) {
|
||||
rollFormulaParts.push("-2");
|
||||
rollModifiers.push(game.i18n.localize("MGT2.Actor.Encumbrance") + " -2");
|
||||
}
|
||||
|
||||
if (userRollData.fatigue === true) {
|
||||
rollFormulaParts.push("-2");
|
||||
rollModifiers.push(game.i18n.localize("MGT2.Actor.Fatigue") + " -2");
|
||||
}
|
||||
|
||||
if (userRollData.customDM) {
|
||||
const s = userRollData.customDM.trim();
|
||||
if (/^[0-9]/.test(s)) rollFormulaParts.push("+");
|
||||
rollFormulaParts.push(s);
|
||||
}
|
||||
|
||||
if (MGT2Helper.hasValue(userRollData, "difficulty")) rollOptions.difficulty = userRollData.difficulty;
|
||||
|
||||
const rollFormula = rollFormulaParts.join("");
|
||||
if (!Roll.validate(rollFormula)) {
|
||||
ui.notifications.error(game.i18n.localize("MGT2.Errors.InvalidRollFormula"));
|
||||
return;
|
||||
}
|
||||
|
||||
let roll = await new Roll(rollFormula, this.actor.getRollData()).roll({ async: true, rollMode: userRollData.rollMode });
|
||||
|
||||
if (isInitiative && this.token?.combatant) {
|
||||
await this.token.combatant.update({ initiative: roll.total });
|
||||
}
|
||||
|
||||
const chatData = {
|
||||
user: game.user.id,
|
||||
speaker: this.actor ? ChatMessage.getSpeaker({ actor: this.actor }) : null,
|
||||
formula: roll._formula,
|
||||
tooltip: await roll.getTooltip(),
|
||||
total: Math.round(roll.total * 100) / 100,
|
||||
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
|
||||
showButtons: true,
|
||||
showLifeButtons: false,
|
||||
showRollRequest: false,
|
||||
rollTypeName: rollOptions.rollTypeName,
|
||||
rollObjectName: rollOptions.rollObjectName,
|
||||
rollModifiers: rollModifiers,
|
||||
showRollDamage: rollOptions.damageFormula !== null && rollOptions.damageFormula !== "",
|
||||
cardButtons: cardButtons,
|
||||
};
|
||||
|
||||
if (MGT2Helper.hasValue(rollOptions, "difficulty")) {
|
||||
chatData.rollDifficulty = rollOptions.difficulty;
|
||||
chatData.rollDifficultyLabel = MGT2Helper.getDifficultyDisplay(rollOptions.difficulty);
|
||||
if (roll.total >= MGT2Helper.getDifficultyValue(rollOptions.difficulty))
|
||||
chatData.rollSuccess = true;
|
||||
else
|
||||
chatData.rollFailure = true;
|
||||
}
|
||||
|
||||
const html = await renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
|
||||
chatData.content = html;
|
||||
|
||||
let flags = null;
|
||||
if (rollOptions.damageFormula) {
|
||||
flags = { mgt2: { damage: { formula: rollOptions.damageFormula, rollObjectName: rollOptions.rollObjectName, rollTypeName: rollOptions.rollTypeName } } };
|
||||
}
|
||||
if (cardButtons.length > 0) {
|
||||
if (!flags) flags = { mgt2: {} };
|
||||
flags.mgt2.buttons = cardButtons;
|
||||
}
|
||||
if (flags) chatData.flags = flags;
|
||||
|
||||
return roll.toMessage(chatData);
|
||||
}
|
||||
|
||||
static async #onOpenConfig(event) {
|
||||
event.preventDefault();
|
||||
const userConfig = await CharacterPrompts.openConfig(this.actor.system);
|
||||
if (userConfig) this.actor.update({ "system.config": userConfig });
|
||||
}
|
||||
|
||||
static async #onOpenCharacteristic(event, target) {
|
||||
event.preventDefault();
|
||||
const name = target.dataset.cfgCharacteristic;
|
||||
const c = this.actor.system.characteristics[name];
|
||||
|
||||
let showAll = false;
|
||||
for (const value of Object.values(this.actor.system.characteristics)) {
|
||||
if (!value.show) { showAll = true; break; }
|
||||
}
|
||||
|
||||
const userConfig = await CharacterPrompts.openCharacteristic(
|
||||
game.i18n.localize(`MGT2.Characteristics.${name}.name`),
|
||||
c.show, c.showMax, showAll
|
||||
);
|
||||
|
||||
if (userConfig) {
|
||||
const data = { system: { characteristics: {} } };
|
||||
data.system.characteristics[name] = { show: userConfig.show, showMax: userConfig.showMax };
|
||||
|
||||
if (userConfig.showAll === true) {
|
||||
for (const [key, value] of Object.entries(this.actor.system.characteristics)) {
|
||||
if (key !== name && !value.show)
|
||||
data.system.characteristics[key] = { show: true };
|
||||
}
|
||||
}
|
||||
this.actor.update(data);
|
||||
}
|
||||
}
|
||||
|
||||
static async #onTraitCreate(event) {
|
||||
event.preventDefault();
|
||||
let traits = this.actor.system.personal.traits;
|
||||
let newTraits;
|
||||
if (traits.length === 0) {
|
||||
newTraits = [{ name: "", description: "" }];
|
||||
} else {
|
||||
newTraits = [...traits, { name: "", description: "" }];
|
||||
}
|
||||
return this.actor.update({ system: { personal: { traits: newTraits } } });
|
||||
}
|
||||
|
||||
static async #onTraitEdit(event, target) {
|
||||
event.preventDefault();
|
||||
const element = target.closest("[data-traits-part]");
|
||||
const index = Number(element.dataset.traitsPart);
|
||||
const trait = this.actor.system.personal.traits[index];
|
||||
const result = await CharacterPrompts.openTraitEdit(trait);
|
||||
const traits = [...this.actor.system.personal.traits];
|
||||
traits[index] = { ...traits[index], name: result.name, description: result.description };
|
||||
return this.actor.update({ system: { personal: { traits: traits } } });
|
||||
}
|
||||
|
||||
static async #onTraitDelete(event, target) {
|
||||
event.preventDefault();
|
||||
const element = target.closest("[data-traits-part]");
|
||||
const index = Number(element.dataset.traitsPart);
|
||||
const traits = foundry.utils.deepClone(this.actor.system.personal.traits);
|
||||
const newTraits = Object.entries(traits)
|
||||
.filter(([key]) => Number(key) !== index)
|
||||
.map(([, value]) => value);
|
||||
return this.actor.update({ system: { personal: { traits: newTraits } } });
|
||||
}
|
||||
|
||||
static async #onOpenEditor(event) {
|
||||
event.preventDefault();
|
||||
await CharacterPrompts.openEditorFullView(
|
||||
this.actor.system.personal.species,
|
||||
this.actor.system.personal.speciesText.descriptionLong
|
||||
);
|
||||
}
|
||||
}
|
||||
253
src/module/applications/sheets/item-sheet.mjs
Normal file
253
src/module/applications/sheets/item-sheet.mjs
Normal file
@@ -0,0 +1,253 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
import { MGT2Helper } from "../../helper.js";
|
||||
|
||||
export default class TravellerItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["mgt2", "sheet", "item"],
|
||||
position: { width: 630 },
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
window: { resizable: true },
|
||||
actions: {
|
||||
careerEventCreate: TravellerItemSheet.#onCareerEventCreate,
|
||||
careerEventDelete: TravellerItemSheet.#onCareerEventDelete,
|
||||
optionCreate: TravellerItemSheet.#onOptionCreate,
|
||||
optionDelete: TravellerItemSheet.#onOptionDelete,
|
||||
modifierCreate: TravellerItemSheet.#onModifierCreate,
|
||||
modifierDelete: TravellerItemSheet.#onModifierDelete,
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
// template is dynamic — resolved in _prepareContext / _renderHTML
|
||||
template: "",
|
||||
},
|
||||
}
|
||||
|
||||
/** Resolve template dynamically based on item type */
|
||||
get template() {
|
||||
return `systems/mgt2/templates/items/${this.document.type}-sheet.html`;
|
||||
}
|
||||
|
||||
tabGroups = { primary: "tab1" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const item = this.document;
|
||||
const source = item.toObject();
|
||||
|
||||
const settings = {
|
||||
usePronouns: game.settings.get("mgt2", "usePronouns"),
|
||||
};
|
||||
|
||||
let containers = null;
|
||||
let computers = null;
|
||||
let hadContainer = false;
|
||||
|
||||
if (item.actor !== null) {
|
||||
hadContainer = true;
|
||||
containers = [{ name: "", _id: "" }].concat(item.actor.getContainers());
|
||||
computers = [{ name: "", _id: "" }].concat(item.actor.getComputers());
|
||||
}
|
||||
|
||||
let weight = null;
|
||||
if (item.system.hasOwnProperty("weight")) {
|
||||
weight = MGT2Helper.convertWeightForDisplay(item.system.weight);
|
||||
}
|
||||
|
||||
let skills = [];
|
||||
if (this.actor !== null) {
|
||||
for (let actorItem of this.actor.items) {
|
||||
if (actorItem.type === "talent" && actorItem.system.subType === "skill")
|
||||
skills.push({ _id: actorItem._id, name: actorItem.getRollDisplay() });
|
||||
}
|
||||
}
|
||||
skills.sort(MGT2Helper.compareByName);
|
||||
skills = [{ _id: "NP", name: game.i18n.localize("MGT2.Items.NotProficient") }].concat(skills);
|
||||
|
||||
return {
|
||||
item: item,
|
||||
document: item,
|
||||
cssClass: this.isEditable ? "editable" : "locked",
|
||||
system: item.system,
|
||||
source: source.system,
|
||||
fields: item.schema.fields,
|
||||
systemFields: item.system.schema.fields,
|
||||
isEditable: this.isEditable,
|
||||
isGM: game.user.isGM,
|
||||
config: CONFIG,
|
||||
settings: settings,
|
||||
containers: containers,
|
||||
computers: computers,
|
||||
hadContainer: hadContainer,
|
||||
weight: weight,
|
||||
unitlabels: { weight: MGT2Helper.getWeightLabel() },
|
||||
skills: skills,
|
||||
};
|
||||
}
|
||||
|
||||
/** @override — resolve the per-type template before rendering */
|
||||
async _renderHTML(context, options) {
|
||||
const templatePath = `systems/mgt2/templates/items/${this.document.type}-sheet.html`;
|
||||
const html = await renderTemplate(templatePath, context);
|
||||
return { sheet: html };
|
||||
}
|
||||
|
||||
/** @override — put rendered HTML into the window content */
|
||||
_replaceHTML(result, content, options) {
|
||||
content.innerHTML = result.sheet;
|
||||
this._activateTabGroups();
|
||||
this._bindItemEvents();
|
||||
}
|
||||
|
||||
/** Bind CSS class-based events (templates not yet migrated to data-action) */
|
||||
_bindItemEvents() {
|
||||
const html = this.element;
|
||||
if (!this.isEditable) return;
|
||||
const bind = (sel, handler) => {
|
||||
for (const el of html.querySelectorAll(sel)) {
|
||||
el.addEventListener("click", (ev) => handler.call(this, ev, ev.currentTarget));
|
||||
}
|
||||
};
|
||||
bind(".event-create", TravellerItemSheet.#onCareerEventCreate);
|
||||
bind(".event-delete", TravellerItemSheet.#onCareerEventDelete);
|
||||
bind(".options-create", TravellerItemSheet.#onOptionCreate);
|
||||
bind(".options-delete", TravellerItemSheet.#onOptionDelete);
|
||||
bind(".modifiers-create", TravellerItemSheet.#onModifierCreate);
|
||||
bind(".modifiers-delete", TravellerItemSheet.#onModifierDelete);
|
||||
}
|
||||
|
||||
_activateTabGroups() {
|
||||
for (const [group, activeTab] of Object.entries(this.tabGroups)) {
|
||||
const nav = this.element.querySelector(`nav[data-group="${group}"], .horizontal-tabs`);
|
||||
if (!nav) continue;
|
||||
|
||||
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||
link.classList.toggle('active', link.dataset.tab === activeTab);
|
||||
link.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
this.tabGroups[group] = link.dataset.tab;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
|
||||
this.element.querySelectorAll(`.itemsheet-panel [data-tab], [data-group="${group}"][data-tab]`).forEach(content => {
|
||||
content.classList.toggle('active', content.dataset.tab === activeTab);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @override — process form data before submit (weight/qty/cost conversions + container logic) */
|
||||
_prepareSubmitData(event, form, formData) {
|
||||
const data = foundry.utils.expandObject(formData.object);
|
||||
|
||||
if (data.hasOwnProperty("weight")) {
|
||||
data.system = data.system || {};
|
||||
data.system.weight = MGT2Helper.convertWeightFromInput(data.weight);
|
||||
delete data.weight;
|
||||
}
|
||||
|
||||
if (data.system?.hasOwnProperty("quantity")) {
|
||||
data.system.quantity = MGT2Helper.getIntegerFromInput(data.system.quantity);
|
||||
}
|
||||
|
||||
if (data.system?.hasOwnProperty("cost")) {
|
||||
data.system.cost = MGT2Helper.getIntegerFromInput(data.system.cost);
|
||||
}
|
||||
|
||||
// Container/equipped logic
|
||||
if (data.system?.hasOwnProperty("container") && this.document.system.hasOwnProperty("equipped")) {
|
||||
const equippedChange = this.document.system.equipped !== data.system.equipped;
|
||||
const containerChange = this.document.system.container?.id !== data.system.container?.id;
|
||||
|
||||
if (equippedChange && data.system.equipped === true) {
|
||||
data.system.container = { id: "" };
|
||||
} else if (containerChange && data.system.container?.id !== "" && this.document.system.container?.id === "") {
|
||||
data.system.equipped = false;
|
||||
}
|
||||
}
|
||||
|
||||
return foundry.utils.flattenObject(data);
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Actions
|
||||
// =========================================================
|
||||
|
||||
static async #onCareerEventCreate(event) {
|
||||
event.preventDefault();
|
||||
const events = this.document.system.events;
|
||||
let newEvents;
|
||||
if (!events || events.length === 0) {
|
||||
newEvents = [{ age: "", description: "" }];
|
||||
} else {
|
||||
newEvents = [...events, { age: "", description: "" }];
|
||||
}
|
||||
return this.document.update({ system: { events: newEvents } });
|
||||
}
|
||||
|
||||
static async #onCareerEventDelete(event, target) {
|
||||
event.preventDefault();
|
||||
const element = target.closest("[data-events-part]");
|
||||
const index = Number(element.dataset.eventsPart);
|
||||
const events = foundry.utils.deepClone(this.document.system.events);
|
||||
const newEvents = Object.entries(events)
|
||||
.filter(([key]) => Number(key) !== index)
|
||||
.map(([, val]) => val);
|
||||
return this.document.update({ system: { events: newEvents } });
|
||||
}
|
||||
|
||||
static async #onOptionCreate(event, target) {
|
||||
event.preventDefault();
|
||||
const property = target.dataset.property;
|
||||
const options = this.document.system[property];
|
||||
let newOptions;
|
||||
if (!options || options.length === 0) {
|
||||
newOptions = [{ name: "", description: "" }];
|
||||
} else {
|
||||
newOptions = [...options, { name: "", description: "" }];
|
||||
}
|
||||
return this.document.update({ [`system.${property}`]: newOptions });
|
||||
}
|
||||
|
||||
static async #onOptionDelete(event, target) {
|
||||
event.preventDefault();
|
||||
const element = target.closest("[data-options-part]");
|
||||
const property = element.dataset.property;
|
||||
const index = Number(element.dataset.optionsPart);
|
||||
const options = foundry.utils.deepClone(this.document.system[property]);
|
||||
const newOptions = Object.entries(options)
|
||||
.filter(([key]) => Number(key) !== index)
|
||||
.map(([, val]) => val);
|
||||
return this.document.update({ [`system.${property}`]: newOptions });
|
||||
}
|
||||
|
||||
static async #onModifierCreate(event) {
|
||||
event.preventDefault();
|
||||
const modifiers = this.document.system.modifiers;
|
||||
let newModifiers;
|
||||
if (!modifiers || modifiers.length === 0) {
|
||||
newModifiers = [{ characteristic: "Endurance", value: null }];
|
||||
} else {
|
||||
newModifiers = [...modifiers, { characteristic: "Endurance", value: null }];
|
||||
}
|
||||
return this.document.update({ system: { modifiers: newModifiers } });
|
||||
}
|
||||
|
||||
static async #onModifierDelete(event, target) {
|
||||
event.preventDefault();
|
||||
const element = target.closest("[data-modifiers-part]");
|
||||
const index = Number(element.dataset.modifiersPart);
|
||||
const modifiers = foundry.utils.deepClone(this.document.system.modifiers);
|
||||
const newModifiers = Object.entries(modifiers)
|
||||
.filter(([key]) => Number(key) !== index)
|
||||
.map(([, val]) => val);
|
||||
return this.document.update({ system: { modifiers: newModifiers } });
|
||||
}
|
||||
}
|
||||
24
src/module/applications/sheets/vehicule-sheet.mjs
Normal file
24
src/module/applications/sheets/vehicule-sheet.mjs
Normal file
@@ -0,0 +1,24 @@
|
||||
import MGT2ActorSheet from "./base-actor-sheet.mjs";
|
||||
|
||||
export default class TravellerVehiculeSheet extends MGT2ActorSheet {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "vehicule", "nopad"],
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "TYPES.Actor.vehicule",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/mgt2/templates/actors/vehicule-sheet.html",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "stats" }
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
CharacterData,
|
||||
VehiculeData,
|
||||
ItemData,
|
||||
EquipmentData,
|
||||
DiseaseData,
|
||||
@@ -11,13 +12,12 @@ import {
|
||||
WeaponData,
|
||||
ItemContainerData,
|
||||
SpeciesData
|
||||
} from "./datamodels.js";
|
||||
} from "./models/index.mjs";
|
||||
|
||||
import { MGT2 } from "./config.js";
|
||||
import { TravellerActor, MGT2Combatant } from "./actors/actor.js";
|
||||
import { TravellerItem } from "./item.js";
|
||||
import { TravellerItemSheet } from "./item-sheet.js";
|
||||
import { TravellerActorSheet } from "./actors/character-sheet.js";
|
||||
import { TravellerItemSheet, TravellerCharacterSheet, TravellerVehiculeSheet } from "./applications/sheets/_module.mjs";
|
||||
import { preloadHandlebarsTemplates } from "./templates.js";
|
||||
//import { MGT2Helper } from "./helper.js";
|
||||
import {ChatHelper} from "./chatHelper.js";
|
||||
@@ -88,14 +88,16 @@ Hooks.once("init", async function () {
|
||||
CONFIG.Actor.documentClass = TravellerActor;
|
||||
CONFIG.Item.documentClass = TravellerItem;
|
||||
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
Actors.registerSheet("mgt2", TravellerActorSheet, { types: ["character"], makeDefault: true, label: "Traveller Sheet" });
|
||||
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
|
||||
foundry.documents.collections.Actors.registerSheet("mgt2", TravellerCharacterSheet, { types: ["character"], makeDefault: true, label: "Traveller Sheet" });
|
||||
foundry.documents.collections.Actors.registerSheet("mgt2", TravellerVehiculeSheet, { types: ["vehicule"], makeDefault: true, label: "Vehicule Sheet" });
|
||||
|
||||
Items.unregisterSheet("core", ItemSheet);
|
||||
Items.registerSheet("mgt2", TravellerItemSheet, { makeDefault: true });
|
||||
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
|
||||
foundry.documents.collections.Items.registerSheet("mgt2", TravellerItemSheet, { makeDefault: true });
|
||||
|
||||
Object.assign(CONFIG.Actor.dataModels, {
|
||||
"character": CharacterData
|
||||
"character": CharacterData,
|
||||
"vehicule": VehiculeData
|
||||
});
|
||||
|
||||
Object.assign(CONFIG.Item.dataModels, {
|
||||
|
||||
@@ -1,485 +0,0 @@
|
||||
// https://foundryvtt.com/article/system-data-models/
|
||||
// https://foundryvtt.com/api/classes/foundry.data.fields.NumberField.html
|
||||
// https://foundryvtt.com/api/v10/classes/foundry.data.fields.DataField.html
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export class CharacterData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
// XP
|
||||
return {
|
||||
name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
personal: new fields.SchemaField({
|
||||
title: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
species: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
speciesText: new fields.SchemaField({
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true })
|
||||
}),
|
||||
age: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
gender: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
pronouns: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
homeworld: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
ucp: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
)
|
||||
}),
|
||||
biography: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
characteristics: new fields.SchemaField({
|
||||
strength: createCharacteristicField(true, true),
|
||||
dexterity: createCharacteristicField(true, true),
|
||||
endurance: createCharacteristicField(true, true),
|
||||
intellect: createCharacteristicField(true, false),
|
||||
education: createCharacteristicField(true, false),
|
||||
social: createCharacteristicField(true, false),
|
||||
morale: createCharacteristicField(true, false),
|
||||
luck: createCharacteristicField(true, false),
|
||||
sanity: createCharacteristicField(true, false),
|
||||
charm: createCharacteristicField(true, false),
|
||||
psionic: createCharacteristicField(true, false),
|
||||
other: createCharacteristicField(true, false)
|
||||
}),
|
||||
|
||||
health: new fields.SchemaField({
|
||||
radiations: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
study: new fields.SchemaField({
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
total: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
completed: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
finance: new fields.SchemaField({
|
||||
pension: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
credits: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
cashOnHand: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
debt: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
livingCost: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
monthlyShipPayments: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
notes: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
}),
|
||||
containerView: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
containerDropIn: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
notes: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
inventory: new fields.SchemaField({
|
||||
armor: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
weight: new fields.NumberField({ required: true, initial: 0, min: 0, integer: false }),
|
||||
encumbrance: new fields.SchemaField({
|
||||
normal: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
heavy: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
states: new fields.SchemaField({
|
||||
encumbrance: new fields.BooleanField({ required: false, initial: false }),
|
||||
fatigue: new fields.BooleanField({ required: false, initial: false }),
|
||||
unconscious: new fields.BooleanField({ required: false, initial: false }),
|
||||
surgeryRequired: new fields.BooleanField({ required: false, initial: false })
|
||||
}),
|
||||
|
||||
config: new fields.SchemaField({
|
||||
psionic: new fields.BooleanField({ required: false, initial: true }),
|
||||
initiative: new fields.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
damages: new fields.SchemaField({
|
||||
rank1: new fields.StringField({ required: false, blank: true, initial: "strength" }),
|
||||
rank2: new fields.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
rank3: new fields.StringField({ required: false, blank: true, initial: "endurance" })
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// export class CreatureData extends foundry.abstract.TypeDataModel {
|
||||
// static defineSchema() {
|
||||
// return {
|
||||
// name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
// TL: new fields.StringField({ required: true, blank: false, initial: "NA" }),
|
||||
// species: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
// //cost: new fields.NumberField({ required: true, integer: true }),
|
||||
// armor: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
// life: new fields.SchemaField({
|
||||
// value: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
// max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
// }),
|
||||
|
||||
// speed: new fields.StringField({ required: false, initial: "4m", blank: true, trim: true }),
|
||||
|
||||
// traits: new fields.ArrayField(
|
||||
// new fields.SchemaField({
|
||||
// name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
// description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
// })
|
||||
// ),
|
||||
|
||||
// description: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
// behaviour: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
// export class NPCData extends CreatureData {
|
||||
// static defineSchema() {
|
||||
// const schema = super.defineSchema();
|
||||
// // Species, Gender, Age
|
||||
// // STR, DEX, END, INT,. EDU, SOC, PSI, SKILL/Psy, equipment
|
||||
// // Status
|
||||
// schema.secret = new fields.HTMLField({ required: false, blank: true, trim: true });
|
||||
|
||||
// return schema;
|
||||
// }
|
||||
// }
|
||||
|
||||
export class VehiculeData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
|
||||
skillId: new fields.StringField({ required: false, initial: "", blank: true, trim: true }),
|
||||
speed: new fields.SchemaField({
|
||||
cruise: new fields.StringField({ required: false, initial: "Slow", blank: true }),
|
||||
maximum: new fields.StringField({ required: false, initial: "Medium", blank: true })
|
||||
}),
|
||||
agility: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
crew: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
passengers: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
cargo: new fields.NumberField({ required: false, min: 0, integer: false }),
|
||||
//hull
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
shipping: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
cost: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
armor: new fields.SchemaField({
|
||||
front: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
rear: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
sides: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
|
||||
skills: new fields.SchemaField({
|
||||
// Skill Level
|
||||
autopilot: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
// Communication Range
|
||||
// Navigation
|
||||
// Sensors
|
||||
// Camouflage / Recon
|
||||
// Stealth
|
||||
})
|
||||
// config: new fields.SchemaField({
|
||||
// })
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ItemBaseData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
const schema = {
|
||||
//name: new fields.StringField({ required: true, blank: true, trim: true, nullable: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
//type: new fields.StringField({ required: false, blank: false }),
|
||||
subType: new fields.StringField({ required: false, blank: false, nullable: true })
|
||||
};
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
class PhysicalItemData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.quantity = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true });
|
||||
schema.weight = new fields.NumberField({ required: true, initial: 0, min: 0, integer: false });
|
||||
schema.weightless = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.tl = new fields.StringField({ required: true, blank: false, initial: "TL12" });
|
||||
schema.container = new fields.SchemaField({
|
||||
//inContainer: new fields.BooleanField({ required: false, initial: false }),
|
||||
id: new fields.StringField({ required: false, blank: true })
|
||||
});
|
||||
|
||||
schema.roll = new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
|
||||
schema.trash = new fields.BooleanField({ required: false, initial: false });
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ItemData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "loot";
|
||||
schema.software = new fields.SchemaField({
|
||||
bandwidth: new fields.NumberField({ required: false, initial: 0, min: 0, max: 10, integer: true }),
|
||||
effect: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
computerId: new fields.StringField({ required: false, blank: true, initial: "" })
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class EquipmentData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
// augment, clothes
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
//schema.skillModifier = new fields.StringField({ required: false, blank: true });
|
||||
//schema.characteristicModifier = new fields.StringField({ required: false, blank: true });
|
||||
|
||||
schema.augment = new fields.SchemaField({
|
||||
improvement: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
|
||||
schema.subType.initial = "equipment"; // augment, clothing, trinket, toolkit, equipment
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class DiseaseData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "disease"; // disease;poison
|
||||
schema.difficulty = new fields.StringField({ required: true, initial: "Average" });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true });
|
||||
schema.interval = new fields.StringField({ required: false, blank: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class CareerData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.difficulty = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true });
|
||||
schema.interval = new fields.StringField({ required: false, blank: true });
|
||||
|
||||
schema.assignment = new fields.StringField({ required: false, blank: true });
|
||||
schema.terms = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.rank = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.events = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
age: new fields.NumberField({ required: false, integer: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class TalentData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.subType.initial = "skill";
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
schema.level = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
schema.skill = new fields.SchemaField({
|
||||
speciality: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
reduceEncumbrance: new fields.BooleanField({ required: false, initial: false })
|
||||
});
|
||||
|
||||
schema.psionic = new fields.SchemaField({
|
||||
reach: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
cost: new fields.NumberField({ required: false, initial: 1, min: 0, integer: true }),
|
||||
duration: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
durationUnit: new fields.StringField({ required: false })
|
||||
});
|
||||
|
||||
schema.roll = new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ContactData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.subType.initial = "skill";
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true })
|
||||
|
||||
schema.skill = new fields.SchemaField({
|
||||
speciality: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
|
||||
schema.status = new fields.StringField({ required: false, blank: true, trim: true, initial: "Alive" });
|
||||
schema.attitude = new fields.StringField({ required: false, blank: true, trim: true, initial: "Unknow" });
|
||||
schema.relation = new fields.StringField({ required: false, blank: true, trim: true, initial: "Contact" });
|
||||
schema.title = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.nickname = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.species = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.gender = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.pronouns = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.homeworld = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.location = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.occupation = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.notes = new fields.HTMLField({ required: false, blank: true, trim: true });
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class WeaponData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.range = new fields.SchemaField({
|
||||
isMelee: new fields.BooleanField({ required: false, initial: false }),
|
||||
value: new fields.NumberField({ required: false, integer: true, nullable: true }),
|
||||
unit: new fields.StringField({ required: false, blank: true, nullable: true })
|
||||
}),
|
||||
//schema.tons = new fields.NumberField({ required: false, initial: 0, min: 0, integer: false });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.magazine = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.magazineCost = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.traits = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ArmorData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.radiations = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.protection = new fields.StringField({ required: false, blank: false, trim: true });
|
||||
|
||||
// Some armours have a required skill. A Traveller suffers DM-1 to all checks taken in the armour per missing
|
||||
// skill level. For example, a Traveller with Vacc Suit skill 0 who is in a suit that requires Vacc Suit 2 would have
|
||||
// DM-2 to all their checks. Not having the skill at all inflicts the usual DM-3 unskilled penalty instead.
|
||||
schema.requireSkill = new fields.StringField({ required: false, blank: false });
|
||||
schema.requireSkillLevel = new fields.NumberField({ required: false, min: 0, integer: true });
|
||||
|
||||
//requirements: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
|
||||
// As powered armour, battle dress supports its own weight. While powered and active, the mass of battle dress
|
||||
// does not count against the encumbrance of the wearer and is effectively weightless.
|
||||
schema.powered = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
|
||||
// Characteristics Modifiers (Pirate of Drinax - ASLAN BATTLE DRESS STR/DEX, Slot)
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ComputerData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.processing = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.processingUsed = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.overload = new fields.BooleanField({ required: false, initial: false });
|
||||
//schema.softwares = new fields.ArrayField(new fields.StringField({ required: false, blank: true, trim: true }));
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class SoftwareData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.bandwidth = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.inUse = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.computer = new fields.StringField({ required: false, blank: true, nullable: true });
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class SpeciesData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
const schema = {
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
),
|
||||
modifiers: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
value: new fields.NumberField({ required: false, integer: true, nullable: true })
|
||||
})
|
||||
)
|
||||
};
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ItemContainerData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.onHand = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.location = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.count = new fields.NumberField({ required: false, initial: 0, integer: true });
|
||||
schema.weight = new fields.NumberField({ required: false, initial: 0, integer: false });
|
||||
schema.weightless = new fields.BooleanField({ required: false, initial: false });
|
||||
|
||||
schema.locked = new fields.BooleanField({ required: false, initial: false }); // GM only
|
||||
schema.lockedDescription = new fields.StringField({ required: false, blank: true, trim: true, nullable: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
function createCharacteristicField(show = true, showMax = false) {
|
||||
return new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
dm: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
show: new fields.BooleanField({ required: false, initial: show }),
|
||||
showMax: new fields.BooleanField({ required: false, initial: showMax })
|
||||
});
|
||||
}
|
||||
96
src/module/models/character.mjs
Normal file
96
src/module/models/character.mjs
Normal file
@@ -0,0 +1,96 @@
|
||||
import { createCharacteristicField } from "./items/base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class CharacterData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
personal: new fields.SchemaField({
|
||||
title: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
species: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
speciesText: new fields.SchemaField({
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true })
|
||||
}),
|
||||
age: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
gender: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
pronouns: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
homeworld: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
ucp: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
)
|
||||
}),
|
||||
biography: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
characteristics: new fields.SchemaField({
|
||||
strength: createCharacteristicField(true, true),
|
||||
dexterity: createCharacteristicField(true, true),
|
||||
endurance: createCharacteristicField(true, true),
|
||||
intellect: createCharacteristicField(true, false),
|
||||
education: createCharacteristicField(true, false),
|
||||
social: createCharacteristicField(true, false),
|
||||
morale: createCharacteristicField(true, false),
|
||||
luck: createCharacteristicField(true, false),
|
||||
sanity: createCharacteristicField(true, false),
|
||||
charm: createCharacteristicField(true, false),
|
||||
psionic: createCharacteristicField(true, false),
|
||||
other: createCharacteristicField(true, false)
|
||||
}),
|
||||
|
||||
health: new fields.SchemaField({
|
||||
radiations: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
study: new fields.SchemaField({
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
total: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
completed: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
finance: new fields.SchemaField({
|
||||
pension: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
credits: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
cashOnHand: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
debt: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
livingCost: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
monthlyShipPayments: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
notes: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
}),
|
||||
containerView: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
containerDropIn: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
notes: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
inventory: new fields.SchemaField({
|
||||
armor: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
weight: new fields.NumberField({ required: true, initial: 0, min: 0, integer: false }),
|
||||
encumbrance: new fields.SchemaField({
|
||||
normal: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
heavy: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
states: new fields.SchemaField({
|
||||
encumbrance: new fields.BooleanField({ required: false, initial: false }),
|
||||
fatigue: new fields.BooleanField({ required: false, initial: false }),
|
||||
unconscious: new fields.BooleanField({ required: false, initial: false }),
|
||||
surgeryRequired: new fields.BooleanField({ required: false, initial: false })
|
||||
}),
|
||||
|
||||
config: new fields.SchemaField({
|
||||
psionic: new fields.BooleanField({ required: false, initial: true }),
|
||||
initiative: new fields.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
damages: new fields.SchemaField({
|
||||
rank1: new fields.StringField({ required: false, blank: true, initial: "strength" }),
|
||||
rank2: new fields.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
rank3: new fields.StringField({ required: false, blank: true, initial: "endurance" })
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
16
src/module/models/index.mjs
Normal file
16
src/module/models/index.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
// Actor DataModels
|
||||
export { default as CharacterData } from "./character.mjs";
|
||||
export { default as VehiculeData } from "./vehicule.mjs";
|
||||
|
||||
// Item DataModels
|
||||
export { default as ItemData } from "./items/item.mjs";
|
||||
export { default as EquipmentData } from "./items/equipment.mjs";
|
||||
export { default as DiseaseData } from "./items/disease.mjs";
|
||||
export { default as CareerData } from "./items/career.mjs";
|
||||
export { default as TalentData } from "./items/talent.mjs";
|
||||
export { default as ContactData } from "./items/contact.mjs";
|
||||
export { default as WeaponData } from "./items/weapon.mjs";
|
||||
export { default as ArmorData } from "./items/armor.mjs";
|
||||
export { default as ComputerData } from "./items/computer.mjs";
|
||||
export { default as ItemContainerData } from "./items/container.mjs";
|
||||
export { default as SpeciesData } from "./items/species.mjs";
|
||||
23
src/module/models/items/armor.mjs
Normal file
23
src/module/models/items/armor.mjs
Normal file
@@ -0,0 +1,23 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ArmorData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.radiations = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.protection = new fields.StringField({ required: false, blank: false, trim: true });
|
||||
// A Traveller suffers DM-1 to all checks per missing skill level in the required skill.
|
||||
schema.requireSkill = new fields.StringField({ required: false, blank: false });
|
||||
schema.requireSkillLevel = new fields.NumberField({ required: false, min: 0, integer: true });
|
||||
// Powered armour supports its own weight and is effectively weightless for encumbrance.
|
||||
schema.powered = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
41
src/module/models/items/base-item.mjs
Normal file
41
src/module/models/items/base-item.mjs
Normal file
@@ -0,0 +1,41 @@
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export function createCharacteristicField(show = true, showMax = false) {
|
||||
return new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
dm: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
show: new fields.BooleanField({ required: false, initial: show }),
|
||||
showMax: new fields.BooleanField({ required: false, initial: showMax })
|
||||
});
|
||||
}
|
||||
|
||||
export class ItemBaseData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
return {
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
subType: new fields.StringField({ required: false, blank: false, nullable: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class PhysicalItemData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.quantity = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true });
|
||||
schema.weight = new fields.NumberField({ required: true, initial: 0, min: 0, integer: false });
|
||||
schema.weightless = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.tl = new fields.StringField({ required: true, blank: false, initial: "TL12" });
|
||||
schema.container = new fields.SchemaField({
|
||||
id: new fields.StringField({ required: false, blank: true })
|
||||
});
|
||||
schema.roll = new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
schema.trash = new fields.BooleanField({ required: false, initial: false });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
21
src/module/models/items/career.mjs
Normal file
21
src/module/models/items/career.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class CareerData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.difficulty = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true });
|
||||
schema.interval = new fields.StringField({ required: false, blank: true });
|
||||
schema.assignment = new fields.StringField({ required: false, blank: true });
|
||||
schema.terms = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.rank = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.events = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
age: new fields.NumberField({ required: false, integer: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
18
src/module/models/items/computer.mjs
Normal file
18
src/module/models/items/computer.mjs
Normal file
@@ -0,0 +1,18 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ComputerData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.processing = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.processingUsed = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.overload = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
27
src/module/models/items/contact.mjs
Normal file
27
src/module/models/items/contact.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ContactData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "skill";
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true });
|
||||
schema.skill = new fields.SchemaField({
|
||||
speciality: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
schema.status = new fields.StringField({ required: false, blank: true, trim: true, initial: "Alive" });
|
||||
schema.attitude = new fields.StringField({ required: false, blank: true, trim: true, initial: "Unknow" });
|
||||
schema.relation = new fields.StringField({ required: false, blank: true, trim: true, initial: "Contact" });
|
||||
schema.title = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.nickname = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.species = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.gender = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.pronouns = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.homeworld = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.location = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.occupation = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.notes = new fields.HTMLField({ required: false, blank: true, trim: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
16
src/module/models/items/container.mjs
Normal file
16
src/module/models/items/container.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ItemContainerData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.onHand = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.location = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.count = new fields.NumberField({ required: false, initial: 0, integer: true });
|
||||
schema.weight = new fields.NumberField({ required: false, initial: 0, integer: false });
|
||||
schema.weightless = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.locked = new fields.BooleanField({ required: false, initial: false }); // GM only
|
||||
schema.lockedDescription = new fields.StringField({ required: false, blank: true, trim: true, nullable: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
13
src/module/models/items/disease.mjs
Normal file
13
src/module/models/items/disease.mjs
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class DiseaseData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "disease"; // disease, poison
|
||||
schema.difficulty = new fields.StringField({ required: true, initial: "Average" });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true });
|
||||
schema.interval = new fields.StringField({ required: false, blank: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
14
src/module/models/items/equipment.mjs
Normal file
14
src/module/models/items/equipment.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class EquipmentData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.augment = new fields.SchemaField({
|
||||
improvement: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
schema.subType.initial = "equipment"; // augment, clothing, trinket, toolkit, equipment
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
15
src/module/models/items/item.mjs
Normal file
15
src/module/models/items/item.mjs
Normal file
@@ -0,0 +1,15 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class ItemData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "loot";
|
||||
schema.software = new fields.SchemaField({
|
||||
bandwidth: new fields.NumberField({ required: false, initial: 0, min: 0, max: 10, integer: true }),
|
||||
effect: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
computerId: new fields.StringField({ required: false, blank: true, initial: "" })
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
22
src/module/models/items/species.mjs
Normal file
22
src/module/models/items/species.mjs
Normal file
@@ -0,0 +1,22 @@
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class SpeciesData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
return {
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
),
|
||||
modifiers: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
value: new fields.NumberField({ required: false, integer: true, nullable: true })
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
27
src/module/models/items/talent.mjs
Normal file
27
src/module/models/items/talent.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ItemBaseData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class TalentData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "skill";
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.level = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.skill = new fields.SchemaField({
|
||||
speciality: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
reduceEncumbrance: new fields.BooleanField({ required: false, initial: false })
|
||||
});
|
||||
schema.psionic = new fields.SchemaField({
|
||||
reach: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
cost: new fields.NumberField({ required: false, initial: 1, min: 0, integer: true }),
|
||||
duration: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
durationUnit: new fields.StringField({ required: false })
|
||||
});
|
||||
schema.roll = new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
30
src/module/models/items/weapon.mjs
Normal file
30
src/module/models/items/weapon.mjs
Normal file
@@ -0,0 +1,30 @@
|
||||
import { PhysicalItemData } from "./base-item.mjs";
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class WeaponData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.range = new fields.SchemaField({
|
||||
isMelee: new fields.BooleanField({ required: false, initial: false }),
|
||||
value: new fields.NumberField({ required: false, integer: true, nullable: true }),
|
||||
unit: new fields.StringField({ required: false, blank: true, nullable: true })
|
||||
});
|
||||
schema.damage = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.magazine = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.magazineCost = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.traits = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
33
src/module/models/vehicule.mjs
Normal file
33
src/module/models/vehicule.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export default class VehiculeData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
skillId: new fields.StringField({ required: false, initial: "", blank: true, trim: true }),
|
||||
speed: new fields.SchemaField({
|
||||
cruise: new fields.StringField({ required: false, initial: "Slow", blank: true }),
|
||||
maximum: new fields.StringField({ required: false, initial: "Medium", blank: true })
|
||||
}),
|
||||
agility: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
crew: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
passengers: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
cargo: new fields.NumberField({ required: false, min: 0, integer: false }),
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
shipping: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
cost: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
armor: new fields.SchemaField({
|
||||
front: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
rear: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
sides: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
skills: new fields.SchemaField({
|
||||
autopilot: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user