Compare commits
10 Commits
v0.1.4
...
783d4a16e6
| Author | SHA1 | Date | |
|---|---|---|---|
| 783d4a16e6 | |||
| fa1c02caa6 | |||
| 86b2cd5777 | |||
| e3002dd602 | |||
|
|
89b3e401a4 | ||
|
|
f1c4f43c66 | ||
|
|
78ad10918c | ||
|
|
770e80fd3d | ||
|
|
d63526831c | ||
|
|
7f10f617c4 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -27,5 +27,6 @@
|
|||||||
# Local History for Visual Studio Code
|
# Local History for Visual Studio Code
|
||||||
.history/
|
.history/
|
||||||
|
|
||||||
src/
|
|
||||||
mgt2.zip
|
mgt2.zip
|
||||||
|
assets/regles/
|
||||||
|
.github
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
* `system.json` no longer generates warnings ([#3](https://github.com/JDR-Ninja/foundryvtt-mgt2/issues/3))
|
||||||
|
|
||||||
## [0.1.4] (2024-05-25)
|
## [0.1.4] (2024-05-25)
|
||||||
|
|
||||||
### Correctifs
|
### Correctifs
|
||||||
|
|||||||
5282
mgt2.bundle.js
5282
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
91
src/module/actors/actor.js
Normal file
91
src/module/actors/actor.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { ActorCharacter } from "./character.js";
|
||||||
|
|
||||||
|
export class MGT2Combatant extends Combatant {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TravellerActor extends Actor {
|
||||||
|
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
if (this.type === "character") {
|
||||||
|
this.system.initiative = ActorCharacter.getInitiative(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _preCreate(data, options, user) {
|
||||||
|
if ( (await super._preCreate(data, options, user)) === false ) return false;
|
||||||
|
|
||||||
|
if (this.type === "character") {
|
||||||
|
ActorCharacter.preCreate(this, data, options, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onDeleteDescendantDocuments(parent, collection, documents, ids, options, userId) {
|
||||||
|
await super._onDeleteDescendantDocuments(parent, collection, documents, ids, options, userId);
|
||||||
|
|
||||||
|
if (this.type === "character") {
|
||||||
|
await ActorCharacter.onDeleteDescendantDocuments(this, parent, collection, documents, ids, options, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId) {
|
||||||
|
super._onUpdateDescendantDocuments(parent, collection, documents, changes, options, userId);
|
||||||
|
//console.log("_onUpdateDescendantDocuments");
|
||||||
|
|
||||||
|
if (this.type === "character") {
|
||||||
|
await ActorCharacter.onUpdateDescendantDocuments(this, parent, collection, documents, changes, options, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _preUpdate(changed, options, user) {
|
||||||
|
if ((await super._preUpdate(changed, options, user)) === false) return false;
|
||||||
|
|
||||||
|
if (this.type === "character") {
|
||||||
|
await ActorCharacter.preUpdate(this, changed, options, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getInitiative($this) {
|
||||||
|
if (this.type === "character") {
|
||||||
|
return ActorCharacter.getInitiative(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyDamage(amount) {
|
||||||
|
if (this.type === "character") {
|
||||||
|
ActorCharacter.applyDamage(this, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getContainers() {
|
||||||
|
if (this.type === "character") {
|
||||||
|
return ActorCharacter.getContainers(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getComputers() {
|
||||||
|
if (this.type === "character") {
|
||||||
|
return ActorCharacter.getComputers(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getSkills() {
|
||||||
|
if (this.type === "character") {
|
||||||
|
return ActorCharacter.getSkills(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async recalculateWeight() {
|
||||||
|
if (this.type === "character") {
|
||||||
|
return ActorCharacter.recalculateWeight(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
73
src/module/actors/character-prompts.js
Normal file
73
src/module/actors/character-prompts.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
const { DialogV2 } = foundry.applications.api;
|
||||||
|
const { renderTemplate } = foundry.applications.handlebars;
|
||||||
|
const { FormDataExtended } = foundry.applications.ux;
|
||||||
|
|
||||||
|
async function _dialogWithForm(title, templatePath, templateData) {
|
||||||
|
const htmlContent = await renderTemplate(templatePath, templateData);
|
||||||
|
const theme = game.settings.get("mgt2", "theme");
|
||||||
|
return await DialogV2.wait({
|
||||||
|
window: { title },
|
||||||
|
content: htmlContent,
|
||||||
|
rejectClose: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action: "submit",
|
||||||
|
label: game.i18n.localize("MGT2.Save"),
|
||||||
|
icon: '<i class="fa-solid fa-floppy-disk"></i>',
|
||||||
|
default: true,
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
return new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CharacterPrompts {
|
||||||
|
|
||||||
|
static async openConfig(system) {
|
||||||
|
return _dialogWithForm(
|
||||||
|
"Configuration",
|
||||||
|
"systems/mgt2/templates/actors/actor-config-sheet.html",
|
||||||
|
{ config: CONFIG.MGT2, system }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async openCharacteristic(name, show, showMax, showAll = false) {
|
||||||
|
return _dialogWithForm(
|
||||||
|
"Configuration: " + name,
|
||||||
|
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
|
||||||
|
{ name, show, showMax, showAll }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async openTraitEdit(data) {
|
||||||
|
const title = data.name ?? game.i18n.localize("MGT2.Actor.EditTrait");
|
||||||
|
return _dialogWithForm(
|
||||||
|
title,
|
||||||
|
"systems/mgt2/templates/actors/trait-sheet.html",
|
||||||
|
{ config: CONFIG.MGT2, data }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async openEditorFullView(title, html) {
|
||||||
|
const htmlContent = await renderTemplate("systems/mgt2/templates/editor-fullview.html", {
|
||||||
|
config: CONFIG.MGT2,
|
||||||
|
html
|
||||||
|
});
|
||||||
|
const theme = game.settings.get("mgt2", "theme");
|
||||||
|
await DialogV2.wait({
|
||||||
|
window: { title },
|
||||||
|
content: htmlContent,
|
||||||
|
rejectClose: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action: "close",
|
||||||
|
label: game.i18n.localize("MGT2.Close") || "Fermer",
|
||||||
|
default: true,
|
||||||
|
callback: () => null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
1078
src/module/actors/character-sheet.js
Normal file
1078
src/module/actors/character-sheet.js
Normal file
File diff suppressed because it is too large
Load Diff
452
src/module/actors/character.js
Normal file
452
src/module/actors/character.js
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
export class ActorCharacter {
|
||||||
|
static preCreate($this, data, options, user) {
|
||||||
|
$this.updateSource({ prototypeToken: { actorLink: true } }) // QoL
|
||||||
|
}
|
||||||
|
|
||||||
|
static prepareData(actorData) {
|
||||||
|
actorData.initiative = this.getInitiative(actorData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getInitiative($this) {
|
||||||
|
let c = $this.system.config.initiative;
|
||||||
|
return $this.system.characteristics[c].dm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async onDeleteDescendantDocuments($this, parent, collection, documents, ids, options, userId) {
|
||||||
|
const toDeleteIds = [];
|
||||||
|
const itemToUpdates = [];
|
||||||
|
|
||||||
|
for (let d of documents) {
|
||||||
|
if (d.type === "container") {
|
||||||
|
// Delete content
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.system.hasOwnProperty("container") && item.system.container.id === d._id)
|
||||||
|
toDeleteIds.push(item._id);
|
||||||
|
}
|
||||||
|
} else if (d.type === "computer") {
|
||||||
|
// Eject software
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.system.hasOwnProperty("software") && item.system.computerId === d._id) {
|
||||||
|
let clone = foundry.utils.deepClone(item);
|
||||||
|
clone.system.software.computerId = "";
|
||||||
|
itemToUpdates.push(clone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toDeleteIds.length > 0)
|
||||||
|
await $this.deleteEmbeddedDocuments("Item", toDeleteIds);
|
||||||
|
|
||||||
|
if (itemToUpdates.length > 0)
|
||||||
|
await $this.updateEmbeddedDocuments('Item', itemToUpdates);
|
||||||
|
|
||||||
|
await this.recalculateWeight($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async onUpdateDescendantDocuments($this, parent, collection, documents, changes, options, userId) {
|
||||||
|
await this.calculEncumbranceAndWeight($this, parent, collection, documents, changes, options, userId);
|
||||||
|
await this.calculComputers($this, parent, collection, documents, changes, options, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async calculComputers($this, parent, collection, documents, changes, options, userId) {
|
||||||
|
let change;
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
let recalculProcessing = false;
|
||||||
|
for (let d of documents) {
|
||||||
|
if (changes[i].hasOwnProperty("system")) {
|
||||||
|
change = changes[i];
|
||||||
|
if (d.type === "item" && d.system.subType === "software") {
|
||||||
|
if (change.system.software.hasOwnProperty("bandwidth") || change.system.software.hasOwnProperty("computerId")) {
|
||||||
|
recalculProcessing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recalculProcessing) {
|
||||||
|
let updatedComputers = [];
|
||||||
|
let computerChanges = {};
|
||||||
|
let computers = [];
|
||||||
|
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.system.trash === true) continue;
|
||||||
|
if (item.type === "computer") {
|
||||||
|
computers.push(item);
|
||||||
|
computerChanges[item._id] = { processingUsed: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.type !== "item" && item.system.subType !== "software") continue;
|
||||||
|
|
||||||
|
if (item.system.software.hasOwnProperty("computerId") && item.system.software.computerId !== "") {
|
||||||
|
computerChanges[item.system.software.computerId].processingUsed += item.system.software.bandwidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let computer of computers) {
|
||||||
|
let newProcessingUsed = computerChanges[computer._id].processingUsed;
|
||||||
|
if (computer.system.processingUsed !== newProcessingUsed) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedComputers.length > 0) {
|
||||||
|
await $this.updateEmbeddedDocuments('Item', updatedComputers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async calculEncumbranceAndWeight($this, parent, collection, documents, changes, options, userId) {
|
||||||
|
let recalculEncumbrance = false;
|
||||||
|
let recalculWeight = false;
|
||||||
|
|
||||||
|
let change;
|
||||||
|
let i = 0;
|
||||||
|
for (let d of documents) {
|
||||||
|
if (changes[i].hasOwnProperty("system")) {
|
||||||
|
change = changes[i];
|
||||||
|
|
||||||
|
if (d.type === "armor" ||
|
||||||
|
d.type === "computer" ||
|
||||||
|
d.type === "gear" ||
|
||||||
|
d.type === "item" ||
|
||||||
|
d.type === "weapon") {
|
||||||
|
if (change.system.hasOwnProperty("quantity") ||
|
||||||
|
change.system.hasOwnProperty("weight") ||
|
||||||
|
change.system.hasOwnProperty("weightless") ||
|
||||||
|
change.system.hasOwnProperty("container") ||
|
||||||
|
change.system.hasOwnProperty("equipped") ||
|
||||||
|
d.type === "armor") {
|
||||||
|
recalculWeight = true;
|
||||||
|
}
|
||||||
|
} else if (d.type === "talent" && d.system.subType === "skill") {
|
||||||
|
if (change.system.level || (change.system?.hasOwnProperty("skill") && change.system?.skill.hasOwnProperty("reduceEncumbrance"))) {
|
||||||
|
recalculEncumbrance = true;
|
||||||
|
}
|
||||||
|
} else if (d.type === "container" && (change.system.hasOwnProperty("onHand") || change.system.hasOwnProperty("weightless"))) {
|
||||||
|
recalculWeight = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recalculEncumbrance || recalculWeight) {
|
||||||
|
const updateData = {};
|
||||||
|
|
||||||
|
this.recalculateArmor($this, updateData);
|
||||||
|
|
||||||
|
if (recalculEncumbrance) {
|
||||||
|
const str = $this.system.characteristics.strength.value;
|
||||||
|
const end = $this.system.characteristics.endurance.value;
|
||||||
|
let sumSkill = 0;
|
||||||
|
$this.items.filter(x => x.type === "talent" && x.system.subType === "skill" && x.system.skill.reduceEncumbrance === true).forEach(x => sumSkill += x.system.level);
|
||||||
|
let normal = str + end + sumSkill;
|
||||||
|
let heavy = normal * 2;
|
||||||
|
|
||||||
|
updateData["system.states.encumbrance"] = $this.system.inventory.weight > normal;
|
||||||
|
updateData["system.inventory.encumbrance.normal"] = normal;
|
||||||
|
updateData["system.inventory.encumbrance.heavy"] = heavy;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recalculWeight)
|
||||||
|
await this.recalculateWeight($this, updateData);
|
||||||
|
else if (Object.keys(updateData).length > 0)
|
||||||
|
await $this.update(updateData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static recalculateArmor($this, updateData) {
|
||||||
|
if (updateData === null || updateData === undefined)
|
||||||
|
updateData = {};
|
||||||
|
|
||||||
|
let armor = 0;
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.type === "armor") {
|
||||||
|
if (item.system.equipped === true && !isNaN(item.system.protection)) {
|
||||||
|
armor += (+item.system.protection || 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData["system.inventory.armor"] = armor;
|
||||||
|
return updateData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async recalculateWeight($this, updateData) {
|
||||||
|
|
||||||
|
if (updateData === null || updateData === undefined)
|
||||||
|
updateData = {};
|
||||||
|
|
||||||
|
let updatedContainers = [];
|
||||||
|
let containerChanges = {};
|
||||||
|
|
||||||
|
let containers = [];
|
||||||
|
|
||||||
|
// List all containers
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.system.trash === true) continue;
|
||||||
|
|
||||||
|
if (item.type === "container") {
|
||||||
|
containers.push(item);
|
||||||
|
containerChanges[item._id] = { count: 0, weight: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let onHandWeight = 0;
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.type === "container") continue;
|
||||||
|
if (item.system.hasOwnProperty("weightless") && item.system.weightless === true) continue;
|
||||||
|
|
||||||
|
let itemWeight = 0;
|
||||||
|
if (item.system.hasOwnProperty("weight")) {
|
||||||
|
let itemQty = item.system.quantity;
|
||||||
|
if (!isNaN(itemQty) && itemQty > 0) {
|
||||||
|
itemWeight = item.system.weight;
|
||||||
|
if (itemWeight > 0) {
|
||||||
|
itemWeight *= itemQty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === "armor") {
|
||||||
|
if (item.system.equipped === true) {
|
||||||
|
if (item.system.powered === true)
|
||||||
|
itemWeight = 0;
|
||||||
|
else
|
||||||
|
itemWeight *= 0.25; // mass of armor that is being worn by 75% OPTIONAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.system.container && item.system.container.id && item.system.container.id !== "") {
|
||||||
|
// bad deleted container id
|
||||||
|
if (containerChanges.hasOwnProperty(item.system.container.id)) {
|
||||||
|
containerChanges[item.system.container.id].weight += Math.round(itemWeight * 10) / 10;
|
||||||
|
containerChanges[item.system.container.id].count += item.system.quantity;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onHandWeight += Math.round(itemWeight * 10) / 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check containers new weight
|
||||||
|
for (let container of containers) {
|
||||||
|
let newWeight = containerChanges[container._id].weight;
|
||||||
|
let newCount = containerChanges[container._id].count;
|
||||||
|
if (container.system.weight !== newWeight || container.system.count !== newCount) {
|
||||||
|
updatedContainers.push({
|
||||||
|
_id: container._id,
|
||||||
|
"system.weight": newWeight,
|
||||||
|
"system.count": newCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (container.system.onHand === true &&
|
||||||
|
(container.system.weight > 0 || container.system.weightless !== true)) {
|
||||||
|
onHandWeight += container.system.weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData["system.inventory.weight"] = onHandWeight;
|
||||||
|
updateData["system.states.encumbrance"] = onHandWeight > $this.system.inventory.encumbrance.normal;
|
||||||
|
|
||||||
|
await $this.update(updateData);
|
||||||
|
|
||||||
|
if (updatedContainers.length > 0) {
|
||||||
|
await $this.updateEmbeddedDocuments('Item', updatedContainers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async preUpdate($this, changed, options, user) {
|
||||||
|
// Calc encumbrance
|
||||||
|
|
||||||
|
const newStr = foundry.utils.getProperty(changed, "system.characteristics.strength.value") ?? $this.system.characteristics.strength.value;
|
||||||
|
const newEnd = foundry.utils.getProperty(changed, "system.characteristics.endurance.value") ?? $this.system.characteristics.endurance.value;
|
||||||
|
if ((newStr !== $this.system.characteristics.strength.value) || (newEnd !== $this.system.characteristics.endurance.value)) {
|
||||||
|
let sumSkill = 0;
|
||||||
|
$this.items.filter(x => x.type === "talent" && x.system.subType === "skill" && x.system.skill.reduceEncumbrance === true).forEach(x => sumSkill += x.system.level);
|
||||||
|
let normal = newStr + newEnd + sumSkill;
|
||||||
|
let heavy = normal * 2;
|
||||||
|
foundry.utils.setProperty(changed, "system.inventory.encumbrance.normal", normal);
|
||||||
|
foundry.utils.setProperty(changed, "system.inventory.encumbrance.heavy", heavy);
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log(foundry.utils.getProperty(changed, "system.characteristics.strength.value"));
|
||||||
|
const characteristicModified = this.computeCharacteristics(changed);
|
||||||
|
const strengthValue = foundry.utils.getProperty(changed, "system.characteristics.strength.value") ?? $this.system.characteristics.strength.value;
|
||||||
|
const strengthMax = foundry.utils.getProperty(changed, "system.characteristics.strength.max") ?? $this.system.characteristics.strength.max;
|
||||||
|
const dexterityValue = foundry.utils.getProperty(changed, "system.characteristics.dexterity.value") ?? $this.system.characteristics.dexterity.value;
|
||||||
|
const dexterityMax = foundry.utils.getProperty(changed, "system.characteristics.dexterity.max") ?? $this.system.characteristics.dexterity.max;
|
||||||
|
const enduranceValue = foundry.utils.getProperty(changed, "system.characteristics.endurance.value") ?? $this.system.characteristics.endurance.value;
|
||||||
|
const enduranceMax = foundry.utils.getProperty(changed, "system.characteristics.endurance.max") ?? $this.system.characteristics.endurance.max;
|
||||||
|
const lifeValue = strengthValue + dexterityValue + enduranceValue;
|
||||||
|
const lifeMax = strengthMax + dexterityMax + enduranceMax;
|
||||||
|
|
||||||
|
if ($this.system.life.value !== lifeValue)
|
||||||
|
foundry.utils.setProperty(changed, "system.life.value", lifeValue);
|
||||||
|
if ($this.system.life.max !== lifeMax)
|
||||||
|
foundry.utils.setProperty(changed, "system.life.max", lifeMax);
|
||||||
|
|
||||||
|
if (characteristicModified && $this.system.personal.ucp === undefined || $this.system.personal.ucp === "") {
|
||||||
|
// calc
|
||||||
|
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Apply changes in Actor size to Token width/height
|
||||||
|
// if ( "size" in (this.system.traits || {}) ) {
|
||||||
|
// const newSize = foundry.utils.getProperty(changed, "system.traits.size");
|
||||||
|
// if ( newSize && (newSize !== this.system.traits?.size) ) {
|
||||||
|
// let size = CONFIG.DND5E.tokenSizes[newSize];
|
||||||
|
// if ( !foundry.utils.hasProperty(changed, "prototypeToken.width") ) {
|
||||||
|
// changed.prototypeToken ||= {};
|
||||||
|
// changed.prototypeToken.height = size;
|
||||||
|
// changed.prototypeToken.width = size;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// static applyHealing($this, amount) {
|
||||||
|
// if (isNaN(amount) || amount === 0) return;
|
||||||
|
|
||||||
|
// const strength = $this.system.characteristics.strength;
|
||||||
|
// const dexterity = $this.system.characteristics.dexterity;
|
||||||
|
// const endurance = $this.system.characteristics.endurance;
|
||||||
|
|
||||||
|
// const data = {
|
||||||
|
// strength: { value: strength.value },
|
||||||
|
// dexterity: { value: dexterity.value },
|
||||||
|
// endurance: { value: endurance.value }
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// $this.update({ system: { characteristics: data } });
|
||||||
|
// }
|
||||||
|
|
||||||
|
static applyDamage($this, amount) {
|
||||||
|
if (isNaN(amount) || amount === 0) return;
|
||||||
|
const rank1 = $this.system.config.damages.rank1;
|
||||||
|
const rank2 = $this.system.config.damages.rank2;
|
||||||
|
const rank3 = $this.system.config.damages.rank3;
|
||||||
|
|
||||||
|
const data = {};
|
||||||
|
data[rank1] = { value: $this.system.characteristics[rank1].value };
|
||||||
|
data[rank2] = { value: $this.system.characteristics[rank2].value };
|
||||||
|
data[rank3] = { value: $this.system.characteristics[rank3].value };
|
||||||
|
|
||||||
|
if (amount < 0) amount = Math.abs(amount);
|
||||||
|
|
||||||
|
for (const [key, rank] of Object.entries(data)) {
|
||||||
|
if (rank.value > 0) {
|
||||||
|
if (rank.value >= amount) {
|
||||||
|
rank.value -= amount;
|
||||||
|
amount = 0;
|
||||||
|
} else {
|
||||||
|
amount -= rank.value;
|
||||||
|
rank.value = 0;
|
||||||
|
}
|
||||||
|
rank.dm = this.getModifier(rank.value);
|
||||||
|
if (amount <= 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this.update({ system: { characteristics: data } });
|
||||||
|
}
|
||||||
|
|
||||||
|
static getContainers($this) {
|
||||||
|
const containers = [];
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.type == "container") {
|
||||||
|
containers.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
containers.sort(this.compareByName);
|
||||||
|
|
||||||
|
return containers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getComputers($this) {
|
||||||
|
const containers = [];
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.type == "computer") {
|
||||||
|
containers.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
containers.sort(this.compareByName);
|
||||||
|
|
||||||
|
return containers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSkills($this) {
|
||||||
|
const skills = [];
|
||||||
|
for (let item of $this.items) {
|
||||||
|
if (item.type === "talent" && item.system.subType === "skill") {
|
||||||
|
skills.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skills.sort(this.compareByName);
|
||||||
|
|
||||||
|
return skills;
|
||||||
|
}
|
||||||
|
|
||||||
|
static computeCharacteristics(changed) {
|
||||||
|
let modified = this.computeCharacteristic(changed, "strength");
|
||||||
|
|
||||||
|
if (this.computeCharacteristic(changed, "dexterity") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "endurance") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "intellect") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "education") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "social") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "morale") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "luck") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "sanity") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "charm") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "psionic") && !modified) modified = true;
|
||||||
|
if (this.computeCharacteristic(changed, "other") && !modified) modified = true;
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
static computeCharacteristic(changed, name) {
|
||||||
|
//if (isNaN(c.value) || c.value <= 0) c.value = 0;
|
||||||
|
//c.dm = this._getModifier(c.value)
|
||||||
|
const path = `system.characteristics.${name}`;
|
||||||
|
const newValue = foundry.utils.getProperty(changed, path + ".value");// || this.system.characteristics[name].value;
|
||||||
|
if (newValue) {
|
||||||
|
const dm = this.getModifier(newValue);
|
||||||
|
foundry.utils.setProperty(changed, path + ".dm", dm);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getModifier(value) {
|
||||||
|
if (isNaN(value) || value <= 0) return -3;
|
||||||
|
if (value >= 1 && value <= 2) return -2;
|
||||||
|
if (value >= 3 && value <= 5) return -1;
|
||||||
|
if (value >= 6 && value <= 8) return 0;
|
||||||
|
if (value >= 9 && value <= 11) return 1;
|
||||||
|
if (value >= 12 && value <= 14) return 2;
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static compareByName(a, b) {
|
||||||
|
if (!a.hasOwnProperty("name") || !b.hasOwnProperty("name")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/module/actors/vehicule-sheet.js
Normal file
18
src/module/actors/vehicule-sheet.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export class VehiculeActorSheet extends ActorSheet {
|
||||||
|
static get defaultOptions() {
|
||||||
|
const options = super.defaultOptions;
|
||||||
|
|
||||||
|
//if (game.user.isGM || options.editable)
|
||||||
|
// options.dragDrop.push({ dragSelector: ".drag-item-list", dropSelector: ".drop-item-list" });
|
||||||
|
|
||||||
|
return foundry.utils.mergeObject(options, {
|
||||||
|
classes: ["mgt2", game.settings.get("mgt2", "theme"), "actor", "vehicule", "nopad"],
|
||||||
|
template: "systems/mgt2/templates/actors/vehicule-sheet.html",
|
||||||
|
width: 780,
|
||||||
|
//height: 600,
|
||||||
|
tabs: [
|
||||||
|
{ navSelector: ".sheet-sidebar", contentSelector: "form" }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
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";
|
||||||
107
src/module/applications/sheets/base-actor-sheet.mjs
Normal file
107
src/module/applications/sheets/base-actor-sheet.mjs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
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 = { sidebar: "health" }
|
||||||
|
|
||||||
|
/** @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);
|
||||||
|
// Inject theme class dynamically (can't use game.settings in static DEFAULT_OPTIONS)
|
||||||
|
const theme = game.settings.get("mgt2", "theme");
|
||||||
|
if (theme) this.element.classList.add(theme);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
847
src/module/applications/sheets/character-sheet.mjs
Normal file
847
src/module/applications/sheets/character-sheet.mjs
Normal file
@@ -0,0 +1,847 @@
|
|||||||
|
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 = {
|
||||||
|
sidebar: "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({ 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,
|
||||||
|
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 foundry.applications.handlebars.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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
258
src/module/applications/sheets/item-sheet.mjs
Normal file
258
src/module/applications/sheets/item-sheet.mjs
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Dynamic PARTS: template resolved per item type */
|
||||||
|
get PARTS() {
|
||||||
|
const type = this.document?.type ?? "item";
|
||||||
|
return {
|
||||||
|
sheet: {
|
||||||
|
template: `systems/mgt2/templates/items/${type}-sheet.html`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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.MGT2,
|
||||||
|
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 foundry.applications.handlebars.renderTemplate(templatePath, context);
|
||||||
|
return { sheet: html };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override — put rendered HTML into the window content */
|
||||||
|
_replaceHTML(result, content, options) {
|
||||||
|
content.innerHTML = result.sheet;
|
||||||
|
// Inject theme class dynamically (can't use game.settings in static DEFAULT_OPTIONS)
|
||||||
|
const theme = game.settings.get("mgt2", "theme");
|
||||||
|
if (theme) this.element.classList.add(theme);
|
||||||
|
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" }
|
||||||
|
}
|
||||||
99
src/module/chatHelper.js
Normal file
99
src/module/chatHelper.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { MGT2Helper } from "./helper.js";
|
||||||
|
|
||||||
|
export class ChatHelper {
|
||||||
|
|
||||||
|
static setupCardListeners(message, element, messageData) {
|
||||||
|
if (!message || !element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.querySelectorAll('button[data-action="rollDamage"]').forEach(el => {
|
||||||
|
el.addEventListener('click', async event => {
|
||||||
|
await this._processRollDamageButtonEvent(message, event);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
element.querySelectorAll('button[data-action="damage"]').forEach(el => {
|
||||||
|
el.addEventListener('click', async event => {
|
||||||
|
await this._applyChatCardDamage(message, event);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
element.querySelectorAll('button[data-action="healing"]').forEach(el => {
|
||||||
|
el.addEventListener('click', async event => {
|
||||||
|
ui.notifications.warn("healing");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
element.querySelectorAll('button[data-index]').forEach(el => {
|
||||||
|
el.addEventListener('click', async event => {
|
||||||
|
await this._processRollButtonEvent(message, event);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async _processRollButtonEvent(message, event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
let buttons = message.flags.mgt2.buttons;
|
||||||
|
const index = event.target.dataset.index;
|
||||||
|
const button = buttons[index];
|
||||||
|
let roll = await new Roll(button.formula, {}).roll();
|
||||||
|
|
||||||
|
const chatData = {
|
||||||
|
user: game.user.id,
|
||||||
|
speaker: message.speaker,
|
||||||
|
formula: roll._formula,
|
||||||
|
tooltip: await roll.getTooltip(),
|
||||||
|
total: Math.round(roll.total * 100) / 100,
|
||||||
|
rollObjectName: button.message.objectName,
|
||||||
|
rollMessage: MGT2Helper.format(button.message.flavor, Math.round(roll.total * 100) / 100),
|
||||||
|
};
|
||||||
|
|
||||||
|
const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
|
||||||
|
chatData.content = html;
|
||||||
|
return roll.toMessage(chatData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async _processRollDamageButtonEvent(message, event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
let rollFormula = message.flags.mgt2.damage.formula;
|
||||||
|
|
||||||
|
let roll = await new Roll(rollFormula, {}).roll();
|
||||||
|
|
||||||
|
let speaker;
|
||||||
|
let selectTokens = canvas.tokens.controlled;
|
||||||
|
if (selectTokens.length > 0) {
|
||||||
|
speaker = selectTokens[0].actor;
|
||||||
|
} else {
|
||||||
|
speaker = game.user.character;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rollTypeName = message.flags.mgt2.damage.rollTypeName ? message.flags.mgt2.damage.rollTypeName + " DAMAGE" : null;
|
||||||
|
|
||||||
|
const chatData = {
|
||||||
|
user: game.user.id,
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: speaker }),
|
||||||
|
formula: roll._formula,
|
||||||
|
tooltip: await roll.getTooltip(),
|
||||||
|
total: Math.round(roll.total * 100) / 100,
|
||||||
|
showButtons: true,
|
||||||
|
hasDamage: true,
|
||||||
|
rollTypeName: rollTypeName,
|
||||||
|
rollObjectName: message.flags.mgt2.damage.rollObjectName
|
||||||
|
};
|
||||||
|
|
||||||
|
const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
|
||||||
|
chatData.content = html;
|
||||||
|
|
||||||
|
return roll.toMessage(chatData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _applyChatCardDamage(message, event) {
|
||||||
|
const roll = message.rolls[0];
|
||||||
|
return Promise.all(canvas.tokens.controlled.map(t => {
|
||||||
|
const a = t.actor;
|
||||||
|
return a.applyDamage(roll.total);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
157
src/module/config.js
Normal file
157
src/module/config.js
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
export const MGT2 = {};
|
||||||
|
|
||||||
|
MGT2.MetricRange = Object.freeze({
|
||||||
|
meter: "MGT2.MetricRange.meter",
|
||||||
|
kilometer: "MGT2.MetricRange.kilometer"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.MetricWeight = Object.freeze({
|
||||||
|
kilogram: "MGT2.MetricWeight.kilogram",
|
||||||
|
ton: "MGT2.MetricWeight.ton"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.Difficulty = Object.freeze({
|
||||||
|
NA: "MGT2.Difficulty.NA",
|
||||||
|
Simple: "MGT2.Difficulty.Simple",
|
||||||
|
Easy: "MGT2.Difficulty.Easy",
|
||||||
|
Routine: "MGT2.Difficulty.Routine",
|
||||||
|
Average: "MGT2.Difficulty.Average",
|
||||||
|
Difficult: "MGT2.Difficulty.Difficult",
|
||||||
|
VeryDifficult: "MGT2.Difficulty.VeryDifficult",
|
||||||
|
Formidable: "MGT2.Difficulty.Formidable",
|
||||||
|
Impossible: "MGT2.Difficulty.Impossible"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.ItemSubType = Object.freeze({
|
||||||
|
loot: "MGT2.ItemSubType.loot",
|
||||||
|
software: "MGT2.ItemSubType.software"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.EquipmentSubType = Object.freeze({
|
||||||
|
augment: "MGT2.EquipmentSubType.augment",
|
||||||
|
clothing: "MGT2.EquipmentSubType.clothing",
|
||||||
|
equipment: "MGT2.EquipmentSubType.equipment",
|
||||||
|
trinket: "MGT2.EquipmentSubType.trinket",
|
||||||
|
toolkit: "MGT2.EquipmentSubType.toolkit"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.TalentSubType = Object.freeze({
|
||||||
|
skill: "MGT2.TalentSubType.skill",
|
||||||
|
psionic: "MGT2.TalentSubType.psionic"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.DiseaseSubType = Object.freeze({
|
||||||
|
disease: "MGT2.DiseaseSubType.disease",
|
||||||
|
poison: "MGT2.DiseaseSubType.poison",
|
||||||
|
wound: "MGT2.DiseaseSubType.wound"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.PsionicReach = Object.freeze({
|
||||||
|
NA: "MGT2.PsionicReach.NA",
|
||||||
|
Personal: "MGT2.PsionicReach.Personal",
|
||||||
|
Close: "MGT2.PsionicReach.Close",
|
||||||
|
Short: "MGT2.PsionicReach.Short",
|
||||||
|
Medium: "MGT2.PsionicReach.Medium",
|
||||||
|
Long: "MGT2.PsionicReach.Long",
|
||||||
|
VeryLong: "MGT2.PsionicReach.VeryLong",
|
||||||
|
Distant: "MGT2.PsionicReach.Distant",
|
||||||
|
VeryDistant: "MGT2.PsionicReach.VeryDistant",
|
||||||
|
Continental: "MGT2.PsionicReach.Continental",
|
||||||
|
Planetary: "MGT2.PsionicReach.Planetary"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.ContactRelations = Object.freeze({
|
||||||
|
Allie: "MGT2.Contact.Relation.Allie",
|
||||||
|
Contact: "MGT2.Contact.Relation.Contact",
|
||||||
|
Rival: "MGT2.Contact.Relation.Rival",
|
||||||
|
Enemy: "MGT2.Contact.Relation.Enemy"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.ContactStatus = Object.freeze({
|
||||||
|
Alive: "MGT2.Contact.Status.Alive",
|
||||||
|
Unknow: "MGT2.Contact.Status.Unknow",
|
||||||
|
Dead: "MGT2.Contact.Status.Dead"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.Attitudes = Object.freeze({
|
||||||
|
Unknow: "MGT2.Contact.Attitude.Unknow",
|
||||||
|
Hostile: "MGT2.Contact.Attitude.Hostile",
|
||||||
|
Unfriendly: "MGT2.Contact.Attitude.Unfriendly",
|
||||||
|
Indifferent: "MGT2.Contact.Attitude.Indifferent",
|
||||||
|
Friendly: "MGT2.Contact.Attitude.Friendly",
|
||||||
|
Helpful: "MGT2.Contact.Attitude.Helpful",
|
||||||
|
Complicated: "MGT2.Contact.Attitude.Complicated"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.Characteristics = Object.freeze({
|
||||||
|
strength: "MGT2.Characteristics.strength.name",
|
||||||
|
dexterity: "MGT2.Characteristics.dexterity.name",
|
||||||
|
endurance: "MGT2.Characteristics.endurance.name",
|
||||||
|
intellect: "MGT2.Characteristics.intellect.name",
|
||||||
|
education: "MGT2.Characteristics.education.name",
|
||||||
|
social: "MGT2.Characteristics.social.name",
|
||||||
|
morale: "MGT2.Characteristics.morale.name",
|
||||||
|
luck: "MGT2.Characteristics.luck.name",
|
||||||
|
sanity: "MGT2.Characteristics.sanity.name",
|
||||||
|
charm: "MGT2.Characteristics.charm.name",
|
||||||
|
psionic: "MGT2.Characteristics.psionic.name",
|
||||||
|
other: "MGT2.Characteristics.other.name"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.InitiativeCharacteristics = Object.freeze({
|
||||||
|
dexterity: "MGT2.Characteristics.dexterity.name",
|
||||||
|
intellect: "MGT2.Characteristics.intellect.name"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.DamageCharacteristics = Object.freeze({
|
||||||
|
strength: "MGT2.Characteristics.strength.name",
|
||||||
|
dexterity: "MGT2.Characteristics.dexterity.name",
|
||||||
|
endurance: "MGT2.Characteristics.endurance.name"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.TL = Object.freeze({
|
||||||
|
NA: "MGT2.TL.NA",
|
||||||
|
Unknow: "MGT2.TL.Unknow",
|
||||||
|
NotIdentified: "MGT2.TL.NotIdentified",
|
||||||
|
TL00: "MGT2.TL.L00",
|
||||||
|
TL01: "MGT2.TL.L01",
|
||||||
|
TL02: "MGT2.TL.L02",
|
||||||
|
TL03: "MGT2.TL.L03",
|
||||||
|
TL04: "MGT2.TL.L04",
|
||||||
|
TL05: "MGT2.TL.L05",
|
||||||
|
TL06: "MGT2.TL.L06",
|
||||||
|
TL07: "MGT2.TL.L07",
|
||||||
|
TL08: "MGT2.TL.L08",
|
||||||
|
TL09: "MGT2.TL.L09",
|
||||||
|
TL10: "MGT2.TL.L10",
|
||||||
|
TL11: "MGT2.TL.L11",
|
||||||
|
TL12: "MGT2.TL.L12",
|
||||||
|
TL13: "MGT2.TL.L13",
|
||||||
|
TL14: "MGT2.TL.L14",
|
||||||
|
TL15: "MGT2.TL.L15"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.Timeframes = Object.freeze({
|
||||||
|
Normal: "MGT2.Timeframes.Normal",
|
||||||
|
Slower: "MGT2.Timeframes.Slower",
|
||||||
|
Faster: "MGT2.Timeframes.Faster"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.SpeedBands = Object.freeze({
|
||||||
|
Stoppped: "MGT2.SpeedBands.Stoppped",
|
||||||
|
Idle: "MGT2.SpeedBands.Idle",
|
||||||
|
VerySlow: "MGT2.SpeedBands.VerySlow",
|
||||||
|
Slow: "MGT2.SpeedBands.Slow",
|
||||||
|
Medium: "MGT2.SpeedBands.Medium",
|
||||||
|
High: "MGT2.SpeedBands.High.",
|
||||||
|
Fast: "MGT2.SpeedBands.Fast",
|
||||||
|
VeryFast: "MGT2.SpeedBands.VeryFast",
|
||||||
|
Subsonic: "MGT2.SpeedBands.Subsonic",
|
||||||
|
Hypersonic: "MGT2.SpeedBands.Hypersonic"
|
||||||
|
});
|
||||||
|
|
||||||
|
MGT2.Durations = Object.freeze({
|
||||||
|
Seconds: "MGT2.Durations.Seconds",
|
||||||
|
Minutes: "MGT2.Durations.Minutes",
|
||||||
|
Heures: "MGT2.Durations.Heures"
|
||||||
|
});
|
||||||
1
src/module/constants.js
Normal file
1
src/module/constants.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const ATTRIBUTE_TYPES = ["String", "Number", "Boolean", "Formula", "Resource"];
|
||||||
126
src/module/core.js
Normal file
126
src/module/core.js
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import {
|
||||||
|
CharacterData,
|
||||||
|
VehiculeData,
|
||||||
|
ItemData,
|
||||||
|
EquipmentData,
|
||||||
|
DiseaseData,
|
||||||
|
CareerData,
|
||||||
|
TalentData,
|
||||||
|
ContactData,
|
||||||
|
ArmorData,
|
||||||
|
ComputerData,
|
||||||
|
WeaponData,
|
||||||
|
ItemContainerData,
|
||||||
|
SpeciesData
|
||||||
|
} from "./models/index.mjs";
|
||||||
|
|
||||||
|
import { MGT2 } from "./config.js";
|
||||||
|
import { TravellerActor, MGT2Combatant } from "./actors/actor.js";
|
||||||
|
import { TravellerItem } from "./item.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";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Foundry VTT Initialization */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
import { registerSettings } from "./settings.js";
|
||||||
|
|
||||||
|
function registerHandlebarsHelpers() {
|
||||||
|
Handlebars.registerHelper('showDM', function (dm) {
|
||||||
|
if (dm === 0) return "0";
|
||||||
|
if (dm > 0) return `+${dm}`;
|
||||||
|
if (dm < 0) return `${dm}`;
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.once("init", async function () {
|
||||||
|
CONFIG.MGT2 = MGT2;
|
||||||
|
CONFIG.Combat.initiative = {
|
||||||
|
formula: "2d6 + @initiative",
|
||||||
|
decimals: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
CONFIG.Actor.trackableAttributes = {
|
||||||
|
character: {
|
||||||
|
bar: ["life",
|
||||||
|
"characteristics.strength",
|
||||||
|
"characteristics.dexterity",
|
||||||
|
"characteristics.endurance",
|
||||||
|
"characteristics.intellect",
|
||||||
|
"characteristics.education",
|
||||||
|
"characteristics.social",
|
||||||
|
"characteristics.morale",
|
||||||
|
"characteristics.luck",
|
||||||
|
"characteristics.sanity",
|
||||||
|
"characteristics.charm",
|
||||||
|
"characteristics.psionic",
|
||||||
|
"characteristics.other"
|
||||||
|
],
|
||||||
|
value: ["life.value",
|
||||||
|
"health.radiations",
|
||||||
|
"characteristics.strength.value",
|
||||||
|
"characteristics.dexterity.value",
|
||||||
|
"characteristics.endurance.value",
|
||||||
|
"characteristics.intellect.value",
|
||||||
|
"characteristics.education.value",
|
||||||
|
"characteristics.social.value",
|
||||||
|
"characteristics.morale.value",
|
||||||
|
"characteristics.luck.value",
|
||||||
|
"characteristics.sanity.value",
|
||||||
|
"characteristics.charm.value",
|
||||||
|
"characteristics.psionic.value",
|
||||||
|
"characteristics.other.value"]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
game.mgt2 = {
|
||||||
|
TravellerActor,
|
||||||
|
TravellerItem
|
||||||
|
};
|
||||||
|
|
||||||
|
registerHandlebarsHelpers();
|
||||||
|
registerSettings();
|
||||||
|
|
||||||
|
CONFIG.Combatant.documentClass = MGT2Combatant;
|
||||||
|
CONFIG.Actor.documentClass = TravellerActor;
|
||||||
|
CONFIG.Item.documentClass = TravellerItem;
|
||||||
|
|
||||||
|
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" });
|
||||||
|
|
||||||
|
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,
|
||||||
|
"vehicule": VehiculeData
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(CONFIG.Item.dataModels, {
|
||||||
|
"item": ItemData,
|
||||||
|
"equipment": EquipmentData,
|
||||||
|
"disease": DiseaseData,
|
||||||
|
"career": CareerData,
|
||||||
|
"talent": TalentData,
|
||||||
|
"contact": ContactData,
|
||||||
|
"weapon": WeaponData,
|
||||||
|
"computer": ComputerData,
|
||||||
|
"armor": ArmorData,
|
||||||
|
"container": ItemContainerData,
|
||||||
|
"species": SpeciesData
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Hooks.on("renderChatMessageHTML", (message, element, messageData) => {
|
||||||
|
ChatHelper.setupCardListeners(message, element, messageData);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Preload template partials
|
||||||
|
await preloadHandlebarsTemplates();
|
||||||
|
});
|
||||||
|
|
||||||
|
export { MGT2 };
|
||||||
230
src/module/helper.js
Normal file
230
src/module/helper.js
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
export class MGT2Helper {
|
||||||
|
static POUNDS_CONVERT = 2.20462262185;
|
||||||
|
|
||||||
|
static decimalSeparator;
|
||||||
|
static badDecimalSeparator;
|
||||||
|
|
||||||
|
static {
|
||||||
|
this.decimalSeparator = Number(1.1).toLocaleString().charAt(1);
|
||||||
|
this.badDecimalSeparator = (this.decimalSeparator === "." ? "," : ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
static format = function() {
|
||||||
|
var s = arguments[0];
|
||||||
|
for (var i = 0; i < arguments.length - 1; i++) {
|
||||||
|
var reg = new RegExp("\\{" + i + "\\}", "gm");
|
||||||
|
s = s.replace(reg, arguments[i + 1]);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static hasValue(object, property) {
|
||||||
|
return object !== undefined && object.hasOwnProperty(property) && object[property] !== null && object[property] !== undefined && object[property] !== "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static getItemsWeight(items) {
|
||||||
|
let weight = 0;
|
||||||
|
for (let i of items) {
|
||||||
|
let item = i.hasOwnProperty("system") ? i.system : i;
|
||||||
|
if (item.hasOwnProperty("weightless") && item.weightless === true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.hasOwnProperty("weight")) {
|
||||||
|
let itemQty = item.quantity
|
||||||
|
if (!isNaN(itemQty) && itemQty > 0) {
|
||||||
|
let itemWeight = item.weight;
|
||||||
|
if (itemWeight > 0) {
|
||||||
|
weight += itemWeight * itemQty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
static generateUID() {
|
||||||
|
let result = '';
|
||||||
|
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
|
||||||
|
for (let i = 0; i < 36; i++) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * characters.length);
|
||||||
|
result += characters.charAt(randomIndex);
|
||||||
|
if (i === 8 || i === 12 || i === 16 || i === 20)
|
||||||
|
result += "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static compareByName(a, b) {
|
||||||
|
if (!a.hasOwnProperty("name") || !b.hasOwnProperty("name")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDisplayDM(dm) {
|
||||||
|
if (dm === 0) return " (0)";
|
||||||
|
if (dm > 0) return ` (+${dm})`;
|
||||||
|
if (dm < 0) return ` (${dm})`;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
static getFormulaDM(dm) {
|
||||||
|
if (dm === 0) return "+0";
|
||||||
|
if (dm > 0) return `+${dm}`;
|
||||||
|
if (dm < 0) return `${dm}`;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDiceResults(roll) {
|
||||||
|
const results = [];
|
||||||
|
for (const die of roll.dice) {
|
||||||
|
results.push(die.results);
|
||||||
|
}
|
||||||
|
return results.flat(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDiceTotal(roll) {
|
||||||
|
let total = 0;
|
||||||
|
for (const die of roll.dice) {
|
||||||
|
total += die.total;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDifficultyValue(difficulty) {
|
||||||
|
switch(difficulty) {
|
||||||
|
case "Simple": return 2;
|
||||||
|
case "Easy": return 4;
|
||||||
|
case "Routine": return 6;
|
||||||
|
case "Average": return 8;
|
||||||
|
case "Difficult": return 10;
|
||||||
|
case "VeryDifficult": return 12;
|
||||||
|
case "Formidable": return 14;
|
||||||
|
case "Impossible": return 16;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDifficultyDisplay(difficulty) {
|
||||||
|
switch(difficulty) {
|
||||||
|
case "Simple": return game.i18n.localize("MGT2.Difficulty.Simple") + " (2+)";
|
||||||
|
case "Easy": return game.i18n.localize("MGT2.Difficulty.Easy") + " (4+)";
|
||||||
|
case "Routine": return game.i18n.localize("MGT2.Difficulty.Routine") + " (6+)";
|
||||||
|
case "Average": return game.i18n.localize("MGT2.Difficulty.Average") + " (8+)";
|
||||||
|
case "Difficult": return game.i18n.localize("MGT2.Difficulty.Difficult") + " (10+)";
|
||||||
|
case "VeryDifficult": return game.i18n.localize("MGT2.Difficulty.VeryDifficult") + " (12+)";
|
||||||
|
case "Formidable": return game.i18n.localize("MGT2.Difficulty.Formidable") + " (14+)";
|
||||||
|
case "Impossible": return game.i18n.localize("MGT2.Difficulty.Impossible") + " (16+)";
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRangeDisplay(range) {
|
||||||
|
let value = Number(range.value);
|
||||||
|
|
||||||
|
if (isNaN(value)) return null;
|
||||||
|
|
||||||
|
let label;
|
||||||
|
//if (game.settings.get("mgt2", "useDistanceMetric") === true) {
|
||||||
|
if (range.unit !== null && range.unit !== undefined && range.unit !== "")
|
||||||
|
label = game.i18n.localize(`MGT2.MetricRange.${range.unit}`).toLowerCase();
|
||||||
|
else
|
||||||
|
label = "";
|
||||||
|
//} else {
|
||||||
|
// TODO
|
||||||
|
//}
|
||||||
|
|
||||||
|
return `${value}${label}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getWeightLabel() {
|
||||||
|
//const label = game.settings.get("mgt2", "useWeightMetric") === true ? "MGT2.MetricSystem.Weight.kg" : "MGT2.ImperialSystem.Weight.lb";
|
||||||
|
//return game.i18n.localize(label);
|
||||||
|
return game.i18n.localize("MGT2.MetricSystem.Weight.kg");
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDistanceLabel() {
|
||||||
|
//const label = game.settings.get("mgt2", "useDistanceMetric") === true ? "MGT2.MetricSystem.Distance.km" : "MGT2.ImperialSystem.Distance.mi";
|
||||||
|
//return game.i18n.localize(label);
|
||||||
|
return game.i18n.localize("MGT2.MetricSystem.Distance.km");
|
||||||
|
}
|
||||||
|
|
||||||
|
static getIntegerFromInput(data) {
|
||||||
|
return Math.trunc(this.getNumberFromInput(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getNumberFromInput(data) {
|
||||||
|
if (data === undefined || data === null) return 0;
|
||||||
|
|
||||||
|
if (typeof data === "string") {
|
||||||
|
let converted = Number(data.replace(/\s+/g, '').replace(this.badDecimalSeparator, this.decimalSeparator).trim());
|
||||||
|
if (isNaN(converted))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
let converted = Number(data);
|
||||||
|
|
||||||
|
if (isNaN(converted))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static convertWeightForDisplay(weight) {
|
||||||
|
//if (game.settings.get("mgt2", "useWeightMetric") === true || weight === 0)
|
||||||
|
return weight;
|
||||||
|
|
||||||
|
// Metric to Imperial
|
||||||
|
//const pounds = weight * this.POUNDS_CONVERT;
|
||||||
|
//return Math.round(pounds * 10) / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
static convertWeightFromInput(weight) {
|
||||||
|
//if (game.settings.get("mgt2", "useWeightMetric") === true || weight === 0)
|
||||||
|
return Math.round(weight * 10) / 10;
|
||||||
|
|
||||||
|
// Imperial to Metric
|
||||||
|
//const kg = this.POUNDS_CONVERT / weight;
|
||||||
|
//return Math.round(kg * 10) / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDataFromDropEvent(event) {
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
return JSON.parse(event.dataTransfer?.getData("text/plain"));
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if ( data.type !== "Item" ) return false;
|
||||||
|
//const item = await Item.implementation.fromDropData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getItemDataFromDropData(dropData) {
|
||||||
|
//console.log("getItemDataFromDropData");
|
||||||
|
let item;
|
||||||
|
if (game.modules.get("monks-enhanced-journal")?.active && dropData.itemId && dropData.uuid.includes("JournalEntry")) {
|
||||||
|
const journalEntry = await fromUuid(dropData.uuid);
|
||||||
|
} else if (dropData.hasOwnProperty("uuid")) {
|
||||||
|
item = await fromUuid(dropData.uuid);
|
||||||
|
} else {
|
||||||
|
let uuid = `${dropData.type}.${dropData.data._id}`;
|
||||||
|
item = await fromUuid(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
throw new Error(game.i18n.localize("Errors.CouldNotFindItem").replace("_ITEM_ID_", dropData.uuid));
|
||||||
|
}
|
||||||
|
if (item.pack) {
|
||||||
|
const pack = game.packs.get(item.pack);
|
||||||
|
item = await pack?.getDocument(item._id);
|
||||||
|
}
|
||||||
|
return deepClone(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
402
src/module/item-sheet.js
Normal file
402
src/module/item-sheet.js
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
import { MGT2Helper } from "./helper.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend the basic ItemSheet with some very simple modifications
|
||||||
|
* @extends {ItemSheet}
|
||||||
|
*/
|
||||||
|
export class TravellerItemSheet extends ItemSheet {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
static get defaultOptions() {
|
||||||
|
const options = super.defaultOptions;
|
||||||
|
return foundry.utils.mergeObject(options, {
|
||||||
|
classes: ["mgt2", game.settings.get("mgt2", "theme"), "sheet"],
|
||||||
|
width: 630,
|
||||||
|
tabs: [{ navSelector: ".horizontal-tabs", contentSelector: ".itemsheet-panel", initial: "tab1" }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
get template() {
|
||||||
|
const path = "systems/mgt2/templates/items";
|
||||||
|
return `${path}/${this.item.type}-sheet.html`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
async getData(options) {
|
||||||
|
const context = await super.getData(options);
|
||||||
|
//console.log('-=getData=-');
|
||||||
|
//console.log(context);
|
||||||
|
const item = context.item;
|
||||||
|
|
||||||
|
const source = item.toObject();
|
||||||
|
context.config = CONFIG.MGT2;
|
||||||
|
|
||||||
|
const settings = {};
|
||||||
|
settings.usePronouns = game.settings.get("mgt2", "usePronouns");
|
||||||
|
|
||||||
|
let containers = null;
|
||||||
|
let computers = null;;
|
||||||
|
let hadContainer;
|
||||||
|
if (context.item.actor != null) {
|
||||||
|
hadContainer = true;
|
||||||
|
containers = [{ "name": "", "_id": "" }].concat(context.item.actor.getContainers());
|
||||||
|
computers = [{ "name": "", "_id": "" }].concat(context.item.actor.getComputers());
|
||||||
|
} else {
|
||||||
|
hadContainer = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let weight = null;
|
||||||
|
if (item.system.hasOwnProperty("weight")) {
|
||||||
|
weight = MGT2Helper.convertWeightForDisplay(item.system.weight);
|
||||||
|
}
|
||||||
|
let unitlabels = {
|
||||||
|
weight: MGT2Helper.getWeightLabel()
|
||||||
|
};
|
||||||
|
let skills = [];
|
||||||
|
|
||||||
|
if (this.actor !== null) {
|
||||||
|
for (let item of this.actor.items) {
|
||||||
|
if (item.type === "talent") {
|
||||||
|
if (item.system.subType === "skill")
|
||||||
|
skills.push({ _id: item._id, name: item.getRollDisplay() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skills.sort(MGT2Helper.compareByName);
|
||||||
|
skills = [{ _id: "NP", name: game.i18n.localize("MGT2.Items.NotProficient") }].concat(skills);
|
||||||
|
|
||||||
|
foundry.utils.mergeObject(context, {
|
||||||
|
source: source.system,
|
||||||
|
system: item.system,
|
||||||
|
settings: settings,
|
||||||
|
containers: containers,
|
||||||
|
computers: computers,
|
||||||
|
hadContainer: hadContainer,
|
||||||
|
weight: weight,
|
||||||
|
unitlabels: unitlabels,
|
||||||
|
editable: this.isEditable,
|
||||||
|
isGM: game.user.isGM,
|
||||||
|
skills: skills,
|
||||||
|
config: CONFIG
|
||||||
|
//rollData: this.item.getRollData(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
activateListeners(html) {
|
||||||
|
super.activateListeners(html);
|
||||||
|
|
||||||
|
// Everything below here is only needed if the sheet is editable
|
||||||
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
|
//let handler = ev => this._onDropCustom(ev);
|
||||||
|
|
||||||
|
//console.log(html);
|
||||||
|
// itemsheet-panel
|
||||||
|
//html.addEventListener("dragstart", this._onDropCustom, false);
|
||||||
|
html.find('div.itemsheet-panel').each((i, li) => {
|
||||||
|
// //if (li.classList.contains("inventory-header")) return;
|
||||||
|
//li.setAttribute("draggable", true);
|
||||||
|
//li.addEventListener("drop", handler, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//html.find('div.dropitem').each((i, li) => {
|
||||||
|
// //if (li.classList.contains("inventory-header")) return;
|
||||||
|
// li.setAttribute("draggable", true);
|
||||||
|
// li.addEventListener("dragstart", handler, false);
|
||||||
|
//});
|
||||||
|
|
||||||
|
// if (this.item.type == "weapon") {
|
||||||
|
// html.find('.trait-create').click(this._onTraitCreate.bind(this));
|
||||||
|
// html.find('.trait-delete').click(this._onTraitDelete.bind(this));
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (this.item.type == "career") {
|
||||||
|
html.find('.event-create').click(this._onCareerEventCreate.bind(this));
|
||||||
|
html.find('.event-delete').click(this._onCareerEventDelete.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (this.item.type == "armor" ||
|
||||||
|
this.item.type == "computer" ||
|
||||||
|
this.item.type == "species" ||
|
||||||
|
this.item.type == "weapon") {
|
||||||
|
html.find('.options-create').click(this._onOptionCreate.bind(this));
|
||||||
|
html.find('.options-delete').click(this._onOptionDelete.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.item.type == "species") {
|
||||||
|
html.find('.modifiers-create').click(this._onModifierEventCreate.bind(this));
|
||||||
|
html.find('.modifiers-delete').click(this._onModifierEventDelete.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onModifierEventCreate(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
await this._onSubmit(event);
|
||||||
|
|
||||||
|
let modifiers = this.item.system.modifiers;
|
||||||
|
let index;
|
||||||
|
if (modifiers.length === 0) {
|
||||||
|
modifiers = {};
|
||||||
|
modifiers["0"] = { characteristic: "Endurance", value: null };
|
||||||
|
} else {
|
||||||
|
index = Math.max(...Object.keys(modifiers));
|
||||||
|
index++;
|
||||||
|
modifiers[index] = { characteristic: "Endurance", value: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
let update = {
|
||||||
|
system: {
|
||||||
|
modifiers: modifiers
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.item.update(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onModifierEventDelete(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
await this._onSubmit(event);
|
||||||
|
const element = event.currentTarget.closest(".modifiers-part");
|
||||||
|
const modifiers = foundry.utils.deepClone(this.item.system.modifiers);
|
||||||
|
let index = Number(element.dataset.modifiersPart);
|
||||||
|
|
||||||
|
const newModifiers = [];
|
||||||
|
let entries = Object.entries(modifiers);
|
||||||
|
if (entries.length > 1) {
|
||||||
|
for (const [key, value] of entries) {
|
||||||
|
if (key != index)
|
||||||
|
newModifiers.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let update = {
|
||||||
|
system: {
|
||||||
|
modifiers: newModifiers
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.item.update(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onCareerEventCreate(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
await this._onSubmit(event);
|
||||||
|
|
||||||
|
let events = this.item.system.events;
|
||||||
|
let index;
|
||||||
|
if (events.length === 0) {
|
||||||
|
events = {};
|
||||||
|
events["0"] = { age: "", description: "" };
|
||||||
|
} else {
|
||||||
|
index = Math.max(...Object.keys(events));
|
||||||
|
index++;
|
||||||
|
events[index] = { age: "", description: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
let update = {
|
||||||
|
system: {
|
||||||
|
events: events
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.item.update(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onCareerEventDelete(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
await this._onSubmit(event);
|
||||||
|
const element = event.currentTarget.closest(".events-part");
|
||||||
|
const events = foundry.utils.deepClone(this.item.system.events);
|
||||||
|
let index = Number(element.dataset.eventsPart);
|
||||||
|
|
||||||
|
const newEvents = [];
|
||||||
|
let entries = Object.entries(events);
|
||||||
|
if (entries.length > 1) {
|
||||||
|
for (const [key, value] of entries) {
|
||||||
|
if (key != index)
|
||||||
|
newEvents.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let update = {
|
||||||
|
system: {
|
||||||
|
events: newEvents
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.item.update(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onOptionCreate(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
await this._onSubmit(event);
|
||||||
|
|
||||||
|
//const subType = event.currentTarget.dataset.subType;
|
||||||
|
const property = event.currentTarget.dataset.property;
|
||||||
|
|
||||||
|
//let options = this.item.system[subType][property];
|
||||||
|
let options = this.item.system[property];
|
||||||
|
let index;
|
||||||
|
if (options.length === 0) {
|
||||||
|
options = {};
|
||||||
|
options["0"] = { name: "", description: "" };
|
||||||
|
} else {
|
||||||
|
index = Math.max(...Object.keys(options));
|
||||||
|
index++;
|
||||||
|
options[index] = { name: "", description: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
let update = {};
|
||||||
|
//update[`system.${subType}.${property}`] = options;
|
||||||
|
update[`system.${property}`] = options;
|
||||||
|
return this.item.update(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onOptionDelete(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
await this._onSubmit(event);
|
||||||
|
const element = event.currentTarget.closest(".options-part");
|
||||||
|
//const subType = element.dataset.subType;
|
||||||
|
const property = element.dataset.property;
|
||||||
|
//const options = foundry.utils.deepClone(this.item.system[subType][property]);
|
||||||
|
const options = foundry.utils.deepClone(this.item.system[property]);
|
||||||
|
let index = Number(element.dataset.optionsPart);
|
||||||
|
|
||||||
|
const newOptions = [];
|
||||||
|
let entries = Object.entries(options);
|
||||||
|
if (entries.length > 1) {
|
||||||
|
for (const [key, value] of entries) {
|
||||||
|
if (key != index)
|
||||||
|
newOptions.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let update = {};
|
||||||
|
//update[`system.${subType}.${property}`] = newOptions;
|
||||||
|
update[`system.${property}`] = newOptions;
|
||||||
|
return this.item.update(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
// async _onTraitCreate(event) {
|
||||||
|
// event.preventDefault();
|
||||||
|
// await this._onSubmit(event);
|
||||||
|
// const traits = this.item.system.traits;
|
||||||
|
// return this.item.update({ "system.traits.parts": traits.parts.concat([["", ""]]) });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async _onTraitDelete(event) {
|
||||||
|
// event.preventDefault();
|
||||||
|
// await this._onSubmit(event);
|
||||||
|
// const element = event.currentTarget.closest(".traits-part");
|
||||||
|
// const traits = foundry.utils.deepClone(this.item.system.traits);
|
||||||
|
// traits.parts.splice(Number(element.dataset.traitsPart), 1);
|
||||||
|
// return this.item.update({ "system.traits.parts": traits.parts });
|
||||||
|
// }
|
||||||
|
|
||||||
|
_getSubmitData(updateData = {}) {
|
||||||
|
const formData = foundry.utils.expandObject(super._getSubmitData(updateData));
|
||||||
|
|
||||||
|
// Gestion des containers
|
||||||
|
if (formData.hasOwnProperty("system") && formData.system.hasOwnProperty("container") &&
|
||||||
|
(this.item.system.hasOwnProperty("equipped"))) {
|
||||||
|
//*console.log('-=_getSubmitData=-');
|
||||||
|
//console.log(this.item.system.onHand);
|
||||||
|
//console.log(formData.system.onHand);
|
||||||
|
//const onHandChange = this.item.system.onHand !== formData.system.onHand;
|
||||||
|
const equippedChange = this.item.system.equipped !== formData.system.equipped;
|
||||||
|
const containerChange = this.item.system.container.id !== formData.system.container.id;
|
||||||
|
// Maintenant équipé
|
||||||
|
if (equippedChange) {
|
||||||
|
if (formData.system.equipped === true) {
|
||||||
|
//formData.system.onHand = true;
|
||||||
|
//console.log("clear container");
|
||||||
|
formData.system.container = {
|
||||||
|
//inContainer: false,
|
||||||
|
id: ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*else if (onHandChange) {
|
||||||
|
// Maintenant à portée
|
||||||
|
if (formData.system.onHand === true) {
|
||||||
|
//console.log("clear container");
|
||||||
|
formData.system.container = {
|
||||||
|
inContainer: false,
|
||||||
|
id: ""
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
formData.system.equipped = false;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
else if (containerChange) {
|
||||||
|
// Mise en storage
|
||||||
|
if (formData.system.container.id !== "" && (this.item.system.container.id === "" || this.item.system.container.id === null)) {
|
||||||
|
//console.log("put in container");
|
||||||
|
//formData.system.onHand = false;
|
||||||
|
formData.system.equipped = false;
|
||||||
|
//formData.system.container.inContainer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (this.item.type == "weapon") {
|
||||||
|
// const traits = formData.system?.traits;
|
||||||
|
// if (traits)
|
||||||
|
// traits.parts = Object.values(traits?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (this.item.type == "career") {
|
||||||
|
// const events = formData.system?.events;
|
||||||
|
// if (events)
|
||||||
|
// events.parts = Object.values(events?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// else if (this.item.type == "equipment") {
|
||||||
|
// if (this.item.system.subType == "armor") {
|
||||||
|
// // const armor = formData.system?.armor;
|
||||||
|
// // if (armor)
|
||||||
|
// // //options.parts = Object.values(options?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||||
|
// // console.log(armor.options);
|
||||||
|
// // armor.options = Object.values(armor?.options || {})
|
||||||
|
// // .map(d => [d.name || "", d.description || ""]);
|
||||||
|
// // console.log(armor.options);
|
||||||
|
// } else if (this.item.system.subType == "computer") {
|
||||||
|
// const computer = formData.system?.computer;
|
||||||
|
// if (computer)
|
||||||
|
// //options.parts = Object.values(options?.parts || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||||
|
// computer.options = Object.values(computer?.options || {}).map(d => [d[0] || "", d[1] || ""]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (formData.hasOwnProperty("weight")) {
|
||||||
|
formData.system.weight = MGT2Helper.convertWeightFromInput(formData.weight);
|
||||||
|
delete formData.weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.system.hasOwnProperty("quantity")) {
|
||||||
|
formData.system.quantity = MGT2Helper.getIntegerFromInput(formData.system.quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.system.hasOwnProperty("cost")) {
|
||||||
|
formData.system.cost = MGT2Helper.getIntegerFromInput(formData.system.cost);
|
||||||
|
}
|
||||||
|
//console.log("before flatten");
|
||||||
|
//console.log(formData);
|
||||||
|
//console.log("after flatten");
|
||||||
|
// let x = foundry.utils.flattenObject(formData);;
|
||||||
|
// console.log(x);
|
||||||
|
// return x;
|
||||||
|
return foundry.utils.flattenObject(formData);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/module/item.js
Normal file
61
src/module/item.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
export class TravellerItem extends Item {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
prepareDerivedData() {
|
||||||
|
super.prepareDerivedData();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async _preUpdate(changed, options, user) {
|
||||||
|
if ((await super._preUpdate(changed, options, user)) === false) return false;
|
||||||
|
|
||||||
|
if (this.type === "computer") {
|
||||||
|
// Overload
|
||||||
|
const newProcessing = foundry.utils.getProperty(changed, "system.processing") ?? this.system.processing;
|
||||||
|
if (newProcessing !== this.system.processing) {
|
||||||
|
let overload = this.system.processingUsed > newProcessing;
|
||||||
|
foundry.utils.setProperty(changed, "system.overload", overload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Qty max 1
|
||||||
|
if (this.type === "computer" || this.type === "container" || (this.type === "item" && this.system.subType === "software")) {
|
||||||
|
const newQty = foundry.utils.getProperty(changed, "system.quantity") ?? this.system.quantity;
|
||||||
|
if (newQty !== this.system.quantity && newQty > 1) {
|
||||||
|
foundry.utils.setProperty(changed, "system.quantity", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No Weight
|
||||||
|
if (this.type === "item" && this.system.subType === "software") {
|
||||||
|
const newWeight = foundry.utils.getProperty(changed, "system.weight") ?? this.system.weight;
|
||||||
|
if (newWeight !== this.system.weight && newWeight > 0) {
|
||||||
|
foundry.utils.setProperty(changed, "system.weight", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRollDisplay() {
|
||||||
|
if (this.type === "talent") {
|
||||||
|
if (this.system.subType === "skill") {
|
||||||
|
let label;
|
||||||
|
if (this.system.skill.speciality !== "" && this.system.skill.speciality !== undefined) {
|
||||||
|
label = `${this.name} (${this.system.skill.speciality})`;
|
||||||
|
} else {
|
||||||
|
label = this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.system.level > 0)
|
||||||
|
label += ` (+${this.system.level})`;
|
||||||
|
else if (this.system.level < 0)
|
||||||
|
label += ` (${this.system.level})`;
|
||||||
|
|
||||||
|
return label;
|
||||||
|
} else if (this.system.subType === "psionic") {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
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 })
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/module/roll-prompt.js
Normal file
56
src/module/roll-prompt.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const { DialogV2 } = foundry.applications.api;
|
||||||
|
const { renderTemplate } = foundry.applications.handlebars;
|
||||||
|
const { FormDataExtended } = foundry.applications.ux;
|
||||||
|
|
||||||
|
export class RollPromptHelper {
|
||||||
|
|
||||||
|
static async roll(options) {
|
||||||
|
const htmlContent = await renderTemplate('systems/mgt2/templates/roll-prompt.html', {
|
||||||
|
config: CONFIG.MGT2,
|
||||||
|
characteristics: options.characteristics,
|
||||||
|
characteristic: options.characteristic,
|
||||||
|
skills: options.skills,
|
||||||
|
skill: options.skill,
|
||||||
|
fatigue: options.fatigue,
|
||||||
|
encumbrance: options.encumbrance,
|
||||||
|
difficulty: options.difficulty
|
||||||
|
});
|
||||||
|
|
||||||
|
const theme = game.settings.get("mgt2", "theme");
|
||||||
|
|
||||||
|
return await DialogV2.wait({
|
||||||
|
window: { title: options.title ?? options.rollTypeName ?? game.i18n.localize("MGT2.RollPrompt.Roll") },
|
||||||
|
content: htmlContent,
|
||||||
|
rejectClose: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
action: "boon",
|
||||||
|
label: game.i18n.localize("MGT2.RollPrompt.Boon"),
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
const formData = new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||||
|
formData.diceModifier = "dl";
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: "submit",
|
||||||
|
label: game.i18n.localize("MGT2.RollPrompt.Roll"),
|
||||||
|
icon: '<i class="fa-solid fa-dice"></i>',
|
||||||
|
default: true,
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
return new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: "bane",
|
||||||
|
label: game.i18n.localize("MGT2.RollPrompt.Bane"),
|
||||||
|
callback: (event, button, dialog) => {
|
||||||
|
const formData = new FormDataExtended(dialog.element.querySelector('form')).object;
|
||||||
|
formData.diceModifier = "dh";
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/module/settings.js
Normal file
86
src/module/settings.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
export const registerSettings = function () {
|
||||||
|
|
||||||
|
game.settings.register("mgt2", "theme", {
|
||||||
|
name: "MGT2.Settings.theme.name",
|
||||||
|
hint: "MGT2.Settings.theme.hint",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
default: "black-and-red",
|
||||||
|
type: String,
|
||||||
|
choices: {
|
||||||
|
"black-and-red": "MGT2.Themes.BlackAndRed",
|
||||||
|
"mwamba": "MGT2.Themes.Mwamba",
|
||||||
|
"blue": "MGT2.Themes.Blue"
|
||||||
|
},
|
||||||
|
requiresReload: true
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register('mgt2', 'usePronouns', {
|
||||||
|
name: "MGT2.Settings.usePronouns.name",
|
||||||
|
hint: "MGT2.Settings.usePronouns.hint",
|
||||||
|
default: false,
|
||||||
|
scope: 'world',
|
||||||
|
type: Boolean,
|
||||||
|
config: true,
|
||||||
|
requiresReload: false
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register('mgt2', 'useGender', {
|
||||||
|
name: "MGT2.Settings.useGender.name",
|
||||||
|
hint: "MGT2.Settings.useGender.hint",
|
||||||
|
default: false,
|
||||||
|
scope: 'world',
|
||||||
|
type: Boolean,
|
||||||
|
config: true,
|
||||||
|
requiresReload: false
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register('mgt2', 'showLife', {
|
||||||
|
name: "MGT2.Settings.showLife.name",
|
||||||
|
hint: "MGT2.Settings.showLife.hint",
|
||||||
|
default: false,
|
||||||
|
scope: 'world',
|
||||||
|
type: Boolean,
|
||||||
|
config: true,
|
||||||
|
requiresReload: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// game.settings.register('mgt2', 'useWeightMetric', {
|
||||||
|
// name: "MGT2.Settings.useWeightMetric.name",
|
||||||
|
// hint: "MGT2.Settings.useWeightMetric.hint",
|
||||||
|
// default: true,
|
||||||
|
// scope: 'world',
|
||||||
|
// type: Boolean,
|
||||||
|
// config: true,
|
||||||
|
// requiresReload: true
|
||||||
|
// });
|
||||||
|
|
||||||
|
// game.settings.register('mgt2', 'useDistanceMetric', {
|
||||||
|
// name: "MGT2.Settings.useDistanceMetric.name",
|
||||||
|
// hint: "MGT2.Settings.useDistanceMetric.hint",
|
||||||
|
// default: true,
|
||||||
|
// scope: 'world',
|
||||||
|
// type: Boolean,
|
||||||
|
// config: true,
|
||||||
|
// requiresReload: true
|
||||||
|
// });
|
||||||
|
|
||||||
|
// game.settings.register('mgt2', 'showTrash', {
|
||||||
|
// name: "Show Trash tab to Player",
|
||||||
|
// hint: "Player can see the Trash tab and recover item",
|
||||||
|
// default: false,
|
||||||
|
// scope: 'world',
|
||||||
|
// type: Boolean,
|
||||||
|
// config: true,
|
||||||
|
// requiresReload: false
|
||||||
|
// });
|
||||||
|
|
||||||
|
/*game.settings.register('mgt2', 'containerDropIn', {
|
||||||
|
name: "Test",
|
||||||
|
hint: "Mon hint",
|
||||||
|
default: true,
|
||||||
|
scope: 'client',
|
||||||
|
type: Boolean,
|
||||||
|
config: true
|
||||||
|
});*/
|
||||||
|
};
|
||||||
32
src/module/templates.js
Normal file
32
src/module/templates.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Define a set of template paths to pre-load
|
||||||
|
* Pre-loaded templates are compiled and cached for fast access when rendering
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
export const preloadHandlebarsTemplates = async function() {
|
||||||
|
|
||||||
|
const templatePaths = [
|
||||||
|
"systems/mgt2/templates/items/armor-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/career-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/computer-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/contact-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/container-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/disease-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/equipment-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/item-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/species-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/talent-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/weapon-sheet.html",
|
||||||
|
"systems/mgt2/templates/items/parts/sheet-configuration.html",
|
||||||
|
"systems/mgt2/templates/items/parts/sheet-physical-item.html",
|
||||||
|
"systems/mgt2/templates/roll-prompt.html",
|
||||||
|
"systems/mgt2/templates/chat/roll.html",
|
||||||
|
//"systems/mgt2/templates/chat/roll-characteristic.html",
|
||||||
|
"systems/mgt2/templates/actors/actor-config-sheet.html",
|
||||||
|
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
|
||||||
|
"systems/mgt2/templates/actors/trait-sheet.html",
|
||||||
|
"systems/mgt2/templates/editor-fullview.html"
|
||||||
|
];
|
||||||
|
|
||||||
|
return loadTemplates(templatePaths);
|
||||||
|
};
|
||||||
276
src/sass/components/_character.sass
Normal file
276
src/sass/components/_character.sass
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
.characteristics-panel
|
||||||
|
.tab
|
||||||
|
padding: 4px
|
||||||
|
|
||||||
|
.species
|
||||||
|
font-size: 13px
|
||||||
|
margin: 4px 1rem 0 1rem
|
||||||
|
text-align: justify
|
||||||
|
a
|
||||||
|
margin-right: 1rem
|
||||||
|
|
||||||
|
.mgt2
|
||||||
|
.characteristics
|
||||||
|
flex-basis: 138px
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 0
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.characteristics-header
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
background: var(--mgt2-bgcolor-primary)
|
||||||
|
font-family: "Rubik", monospace
|
||||||
|
font-style: normal
|
||||||
|
font-size: 1rem
|
||||||
|
line-height: 2rem
|
||||||
|
text-transform: uppercase
|
||||||
|
|
||||||
|
.characteristic-row
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.characteristic-minmax
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
flex-wrap: nowrap
|
||||||
|
|
||||||
|
.characteristic-dm
|
||||||
|
span
|
||||||
|
&.label
|
||||||
|
font-size: 0.8rem
|
||||||
|
font-weight: 600
|
||||||
|
|
||||||
|
.characteristic-label
|
||||||
|
font-family: "Barlow Condensed", sans-serif
|
||||||
|
font-size: 1.2rem
|
||||||
|
font-weight: 600
|
||||||
|
font-style: italic
|
||||||
|
text-align: center
|
||||||
|
position: relative
|
||||||
|
& > a
|
||||||
|
&.roll
|
||||||
|
color: black
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
a
|
||||||
|
&.cfg-characteristic
|
||||||
|
display: none
|
||||||
|
font-size: 12px
|
||||||
|
position: absolute
|
||||||
|
right: 0
|
||||||
|
top: 0
|
||||||
|
&:hover
|
||||||
|
a
|
||||||
|
&.cfg-characteristic
|
||||||
|
display: block
|
||||||
|
|
||||||
|
.characteristic-input
|
||||||
|
color: var(--mgt2-bgcolor-primary)
|
||||||
|
text-align: center
|
||||||
|
font-size: 1.5rem
|
||||||
|
font-weight: 500
|
||||||
|
width: 2.4rem
|
||||||
|
height: 2rem
|
||||||
|
box-sizing: border-box
|
||||||
|
border: none
|
||||||
|
outline: none
|
||||||
|
background: linear-gradient(45deg, #0000 5.66px, #000 0 calc(5.66px + 2px), #0000 0 calc(100% - 5.66px - 2px), #000 0 calc(100% - 5.66px), #0000 0), linear-gradient(-45deg, #0000 5.66px, #000 0 calc(5.66px + 2px), #0000 0 calc(100% - 5.66px - 2px), #000 0 calc(100% - 5.66px), #0000 0), linear-gradient(90deg, #000 4px, #0000 0) -2px 50%/100% calc(100% - 16px) repeat-x, linear-gradient(#000 4px, #0000 0) 50% -2px/calc(100% - 16px) 100% repeat-y
|
||||||
|
|
||||||
|
.characteristic-dm
|
||||||
|
color: var(--mgt2-color-dm)
|
||||||
|
background-color: transparent
|
||||||
|
text-align: center
|
||||||
|
font-size: 1rem
|
||||||
|
width: 1.5rem
|
||||||
|
height: 1.4rem
|
||||||
|
padding: 0
|
||||||
|
outline: none
|
||||||
|
border: none
|
||||||
|
margin: 0
|
||||||
|
position: absolute
|
||||||
|
right: 0
|
||||||
|
background-color: var(--mgt2-bgcolor-dm)
|
||||||
|
border-radius: 9px
|
||||||
|
margin-right: 2px
|
||||||
|
&:focus
|
||||||
|
outline: none
|
||||||
|
box-shadow: none
|
||||||
|
|
||||||
|
.characteristic-dm-minmax
|
||||||
|
&:focus
|
||||||
|
outline: none
|
||||||
|
box-shadow: none
|
||||||
|
|
||||||
|
.minmaxwrapper
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
flex-wrap: nowrap
|
||||||
|
justify-content: center
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
margin: 0 0.5rem
|
||||||
|
box-sizing: border-box
|
||||||
|
background: linear-gradient(45deg, #0000 7.07px, #000 0 calc(7.07px + 2px), #0000 0 calc(100% - 7.07px - 2px), #000 0 calc(100% - 7.07px), #0000 0), linear-gradient(-45deg, #0000 7.07px, #000 0 calc(7.07px + 2px), #0000 0 calc(100% - 7.07px - 2px), #000 0 calc(100% - 7.07px), #0000 0), linear-gradient(90deg, #000 4px, #0000 0) -2px 50%/100% calc(100% - 20px) repeat-x, linear-gradient(#000 4px, #0000 0) 50% -2px/calc(100% - 20px) 100% repeat-y
|
||||||
|
input
|
||||||
|
display: inline-block
|
||||||
|
color: black
|
||||||
|
background-color: transparent
|
||||||
|
text-align: center
|
||||||
|
font-size: 1.5rem
|
||||||
|
width: 2rem
|
||||||
|
height: 2rem
|
||||||
|
border: none
|
||||||
|
outline: none
|
||||||
|
&:focus
|
||||||
|
outline: none
|
||||||
|
box-shadow: none
|
||||||
|
&:first-child
|
||||||
|
margin-left: 5px
|
||||||
|
&:last-child
|
||||||
|
margin-right: 5px
|
||||||
|
span
|
||||||
|
font-size: 1.5rem
|
||||||
|
font-weight: 500
|
||||||
|
.computer-overload,
|
||||||
|
.computer-overload i
|
||||||
|
color: var(--mgt2-color-warning) !important
|
||||||
|
ul
|
||||||
|
&.softwares
|
||||||
|
list-style: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
display: inline-block
|
||||||
|
color: var(--mgt2-color-software)
|
||||||
|
background-color: var(--mgt2-bgcolor-software)
|
||||||
|
padding: 3px 7px
|
||||||
|
border-radius: 8px
|
||||||
|
a
|
||||||
|
display: none
|
||||||
|
&:first-child
|
||||||
|
margin: 0 0.5rem
|
||||||
|
&:hover
|
||||||
|
a
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
.character-header
|
||||||
|
display: flex
|
||||||
|
margin-top: 8px
|
||||||
|
margin-right: 8px
|
||||||
|
flex-direction: row
|
||||||
|
flex-wrap: nowrap
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 0
|
||||||
|
justify-content: flex-start
|
||||||
|
align-items: flex-start
|
||||||
|
.character-header-img
|
||||||
|
flex-basis: 138px
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 0
|
||||||
|
text-align: center
|
||||||
|
.character-summary
|
||||||
|
flex: 0 0 100%
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
list-style: none
|
||||||
|
border-top: 5px double var(--mgt2-bgcolor-primary)
|
||||||
|
li
|
||||||
|
float: left
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
color: var(--mgt2-color-primary-light)
|
||||||
|
input
|
||||||
|
display: block
|
||||||
|
border: none
|
||||||
|
font-weight: bold
|
||||||
|
font-family: "Roboto Condensed", sans-serif
|
||||||
|
background-color: #fff
|
||||||
|
font-size: 0.8rem
|
||||||
|
border: 1px solid #fff
|
||||||
|
&:hover
|
||||||
|
border: 1px solid #111
|
||||||
|
.character-header-body
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
flex-wrap: nowrap
|
||||||
|
justify-content: flex-start
|
||||||
|
legend
|
||||||
|
font-size: 0.7rem
|
||||||
|
text-transform: uppercase
|
||||||
|
text-wrap: nowrap
|
||||||
|
i
|
||||||
|
margin-right: 0.25rem
|
||||||
|
.character-body
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
align-content: flex-start
|
||||||
|
flex-wrap: nowrap
|
||||||
|
.tab
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
|
||||||
|
.lifes
|
||||||
|
height: 100%
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
div
|
||||||
|
font-size: 2rem
|
||||||
|
.character-states
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
list-style: none
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
justify-content: flex-start
|
||||||
|
align-items: flex-start
|
||||||
|
width: 100%
|
||||||
|
float: right
|
||||||
|
li
|
||||||
|
display: flex
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
color: #4b4a44
|
||||||
|
justify-content: space-between
|
||||||
|
align-items: center
|
||||||
|
width: 100%
|
||||||
|
font-size: 0.7rem
|
||||||
|
line-height: 1.1rem
|
||||||
|
|
||||||
|
.encumbrance-normal
|
||||||
|
color: var(--mgt2-encumbrance-normal)!important
|
||||||
|
|
||||||
|
.encumbrance-heavy
|
||||||
|
color: var(--mgt2-encumbrance-heavy)!important
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.character-body
|
||||||
|
height: 100%
|
||||||
|
overflow: hidden
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
width: 100%
|
||||||
|
justify-content: flex-start
|
||||||
|
align-items: flex-start
|
||||||
|
border-top: 3px solid black
|
||||||
|
|
||||||
|
.actor-footer
|
||||||
|
bottom: 0
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
background-color: var(--mgt2-bgcolor-primary)
|
||||||
|
width: 100%
|
||||||
|
margin: 0 -8px
|
||||||
|
height: 1.5rem
|
||||||
|
justify-content: space-between
|
||||||
|
align-items: center
|
||||||
|
padding: 0 1rem
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 0
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
47
src/sass/components/_chat-sidebar.sass
Normal file
47
src/sass/components/_chat-sidebar.sass
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
.chat-sidebar,
|
||||||
|
.mgt2-buttons button
|
||||||
|
background: rgba(0, 0, 0, 0.1)
|
||||||
|
border: 1px solid var(--color-border-light-2)
|
||||||
|
border-radius: 3px
|
||||||
|
box-shadow: 0 0 2px #FFF inset
|
||||||
|
//.chat-message
|
||||||
|
// &.message
|
||||||
|
// color: #0A0405
|
||||||
|
// background-color: #fff
|
||||||
|
// background-image: none
|
||||||
|
.dice-formula,
|
||||||
|
.dice-total
|
||||||
|
background-color: #fff
|
||||||
|
.mgt2-buttons
|
||||||
|
display: flex
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
flex-wrap: nowrap
|
||||||
|
color: #0A0405
|
||||||
|
margin-top: 5px
|
||||||
|
button
|
||||||
|
i
|
||||||
|
font-size: 1.1rem
|
||||||
|
padding: 0
|
||||||
|
margin: 0
|
||||||
|
.roll-info
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
.roll-type-group
|
||||||
|
flex-direction: row
|
||||||
|
flex-wrap: wrap
|
||||||
|
justify-content: space-between
|
||||||
|
display: flex
|
||||||
|
.roll-type-name
|
||||||
|
font-size: 11px
|
||||||
|
text-transform: uppercase
|
||||||
|
color: #515151
|
||||||
|
.roll-object-name
|
||||||
|
font-weight: 400
|
||||||
|
font-size: 1.4rem
|
||||||
|
.roll-success
|
||||||
|
font-size: 1.2rem
|
||||||
|
font-weight: bold
|
||||||
|
text-transform: uppercase
|
||||||
|
margin-top: 1rem
|
||||||
|
text-align: center
|
||||||
4
src/sass/components/_dialog.sass
Normal file
4
src/sass/components/_dialog.sass
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.mgt2
|
||||||
|
.dialog-button
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
background-color: var(--mgt2-bgcolor-primary) !important
|
||||||
84
src/sass/components/_forms.sass
Normal file
84
src/sass/components/_forms.sass
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
.mgt2
|
||||||
|
&.sheet
|
||||||
|
textarea
|
||||||
|
color: var(--mgt2-input-color)
|
||||||
|
background-color: var(--mgt2-input-bgcolor)
|
||||||
|
font-family: "Roboto", sans-serif
|
||||||
|
font-size: 13px
|
||||||
|
font-stretch: 100%
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus
|
||||||
|
outline: none
|
||||||
|
box-shadow: none
|
||||||
|
|
||||||
|
.checkbox-small
|
||||||
|
flex: none!important
|
||||||
|
width: auto!important
|
||||||
|
height: auto!important
|
||||||
|
margin: 0!important
|
||||||
|
|
||||||
|
.header
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
background: var(--mgt2-bgcolor-primary)
|
||||||
|
font-size: 14px
|
||||||
|
font-family: "Roboto Condensed", sans-serif
|
||||||
|
font-weight: bold
|
||||||
|
padding-left: 5px
|
||||||
|
margin-bottom: 4px
|
||||||
|
line-height: 30px
|
||||||
|
text-transform: uppercase
|
||||||
|
|
||||||
|
.field-groups
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
flex-wrap: nowrap
|
||||||
|
align-items: center
|
||||||
|
justify-content: space-between
|
||||||
|
|
||||||
|
.field-group
|
||||||
|
label
|
||||||
|
text-transform: uppercase
|
||||||
|
font-weight: 700
|
||||||
|
font-size: 14px
|
||||||
|
font-family: "Roboto Condensed", sans-serif
|
||||||
|
font-optical-sizing: auto
|
||||||
|
input
|
||||||
|
&.field
|
||||||
|
background-color: var(--mgt2-input-bgcolor)
|
||||||
|
font-size: 13px
|
||||||
|
&.field-name
|
||||||
|
background-color: var(--mgt2-input-bgcolor)
|
||||||
|
font-size: 2rem
|
||||||
|
border: none
|
||||||
|
font-weight: 700
|
||||||
|
font-family: "Roboto Condensed", sans-serif
|
||||||
|
margin-bottom: 0.5rem
|
||||||
|
padding: 0
|
||||||
|
&.field-item-name
|
||||||
|
background-color: var(--mgt2-input-bgcolor)
|
||||||
|
height: auto
|
||||||
|
font-size: 2rem
|
||||||
|
font-weight: 700
|
||||||
|
font-family: "Roboto Condensed", sans-serif
|
||||||
|
|
||||||
|
.fields
|
||||||
|
display: flex
|
||||||
|
|
||||||
|
.editor
|
||||||
|
min-height: 3rem
|
||||||
|
border: 1px solid var(--mgt2-editor-border)
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.sheet-body
|
||||||
|
margin-left: 140px
|
||||||
|
padding-bottom: 1.5rem
|
||||||
|
|
||||||
|
label
|
||||||
|
&.mgt2-checkbox
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
align-items: center
|
||||||
|
input
|
||||||
|
margin: 0 0.3rem 0 0
|
||||||
38
src/sass/components/_item.sass
Normal file
38
src/sass/components/_item.sass
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
.itemsheet
|
||||||
|
display: flex
|
||||||
|
flex-wrap: nowrap
|
||||||
|
flex-direction: row
|
||||||
|
.itemsheet-header
|
||||||
|
display: flex
|
||||||
|
background-color: var(--mgt2-bgcolor-primary)
|
||||||
|
color: red
|
||||||
|
padding: 0.5rem
|
||||||
|
align-items: center
|
||||||
|
flex: 0 0 2rem
|
||||||
|
label
|
||||||
|
writing-mode: tb-rl
|
||||||
|
transform: rotate(-180deg)
|
||||||
|
font-weight: 700
|
||||||
|
font-size: 20px
|
||||||
|
letter-spacing: 5px
|
||||||
|
font-family: "Rubik Mono One", monospace
|
||||||
|
font-style: normal
|
||||||
|
text-transform: uppercase
|
||||||
|
.itemsheet-maincol
|
||||||
|
flex: 0 0 130px
|
||||||
|
padding: 0 1rem 0 0
|
||||||
|
.itemsheet-panel
|
||||||
|
display: flex
|
||||||
|
flex: inherit
|
||||||
|
padding: 1rem
|
||||||
|
img
|
||||||
|
&.profile-img
|
||||||
|
width: 100px
|
||||||
|
height: 100px
|
||||||
|
.itemsheet input,
|
||||||
|
.itemsheet select
|
||||||
|
color: var(--mgt2-input-color)
|
||||||
|
background-color: var(--mgt2-input-bgcolor)
|
||||||
|
display: block
|
||||||
|
width: 100%
|
||||||
|
font-size: 13px
|
||||||
40
src/sass/components/_tab-sidebar.sass
Normal file
40
src/sass/components/_tab-sidebar.sass
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
.mgt2
|
||||||
|
.sheet-sidebar
|
||||||
|
.item
|
||||||
|
margin: 0 1rem
|
||||||
|
|
||||||
|
nav[data-group="sidebar"].tabs
|
||||||
|
position: absolute
|
||||||
|
left: 100%
|
||||||
|
top: 172px
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
z-index: -1
|
||||||
|
& > .item
|
||||||
|
height: 40px
|
||||||
|
position: relative
|
||||||
|
display: flex
|
||||||
|
justify-content: end
|
||||||
|
align-items: center
|
||||||
|
padding-right: 0.75rem
|
||||||
|
background: var(--mgt2-bgcolor-primary)
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
border: 1px solid transparent
|
||||||
|
font-size: 1rem
|
||||||
|
transition: all 250ms ease
|
||||||
|
margin-left: 0
|
||||||
|
&.active
|
||||||
|
text-shadow: none
|
||||||
|
margin: 0
|
||||||
|
border-color: var(--mgt2-color-primary)
|
||||||
|
&::after
|
||||||
|
border-left: none
|
||||||
|
inset: 0.25rem 0.25rem 0.25rem 0
|
||||||
|
&::after
|
||||||
|
content: ""
|
||||||
|
position: absolute
|
||||||
|
inset: 0.25rem
|
||||||
|
border: 1px solid var(--mgt2-color-primary)
|
||||||
|
pointer-events: none
|
||||||
|
i
|
||||||
|
margin-left: 0.8rem
|
||||||
172
src/sass/components/_tables.sass
Normal file
172
src/sass/components/_tables.sass
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
.container-controls
|
||||||
|
display: inline-block
|
||||||
|
margin-left: 1rem
|
||||||
|
a
|
||||||
|
&:not(:last-child)
|
||||||
|
margin-right: 0.5rem
|
||||||
|
|
||||||
|
.table-container
|
||||||
|
display: flex
|
||||||
|
flex-flow: column nowrap
|
||||||
|
width: 100%
|
||||||
|
margin: 0 auto
|
||||||
|
.table-row
|
||||||
|
display: flex
|
||||||
|
flex-flow: row nowrap
|
||||||
|
width: 100%
|
||||||
|
position: relative
|
||||||
|
align-items: flex-start
|
||||||
|
&.heading
|
||||||
|
background-color: var(--mgt2-bgcolor-primary)
|
||||||
|
align-items: center
|
||||||
|
.row-item
|
||||||
|
text-transform: uppercase
|
||||||
|
font-size: 12px
|
||||||
|
&:first-child
|
||||||
|
font-weight: bold
|
||||||
|
font-size: 13px
|
||||||
|
letter-spacing: 3px
|
||||||
|
i
|
||||||
|
margin-right: 0.5rem
|
||||||
|
&.color-1
|
||||||
|
.row-item
|
||||||
|
background-color: var(--mgt2-bgcolor-primary)
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
&.color-2
|
||||||
|
.row-item
|
||||||
|
background-color: var(--mgt2-bgcolor-form)
|
||||||
|
color: var(--mgt2-bgcolor-primary)
|
||||||
|
div
|
||||||
|
&.row-item
|
||||||
|
padding-left: 5px
|
||||||
|
&:last-child
|
||||||
|
padding-right: 5px
|
||||||
|
&:hover
|
||||||
|
&:not(.heading)
|
||||||
|
background-color: var(--mgt2-row-hover)
|
||||||
|
.table-row-mb-4
|
||||||
|
margin-bottom: 4px
|
||||||
|
.row-item
|
||||||
|
display: flex
|
||||||
|
flex-grow: 1
|
||||||
|
font-size: 14px
|
||||||
|
line-height: 25px
|
||||||
|
align-items: center
|
||||||
|
transition: all 0.15s ease-in-out
|
||||||
|
overflow: hidden !important
|
||||||
|
text-overflow: ellipsis
|
||||||
|
text-wrap: nowrap
|
||||||
|
&.item-controls
|
||||||
|
justify-content: right
|
||||||
|
padding-right: 4px
|
||||||
|
a
|
||||||
|
&:not(:last-child)
|
||||||
|
margin-right: 0.4rem
|
||||||
|
i
|
||||||
|
color: black
|
||||||
|
a[data-roll]
|
||||||
|
margin-right: 0.5rem
|
||||||
|
.heading
|
||||||
|
&.color-1
|
||||||
|
.row-item
|
||||||
|
i
|
||||||
|
color: var(--mgt2-color-primary) !important
|
||||||
|
&.color-2
|
||||||
|
.row-item
|
||||||
|
i
|
||||||
|
color: var(--mgt2-bgcolor-primary) !important
|
||||||
|
.table-subrow
|
||||||
|
border-left: 2px var(--mgt2-subrow-color) dashed
|
||||||
|
color: var(--mgt2-subrow-color)
|
||||||
|
.row-item
|
||||||
|
font-size: 0.8em
|
||||||
|
line-height: 20px
|
||||||
|
&:first-child
|
||||||
|
padding-left: 1rem
|
||||||
|
& > i
|
||||||
|
margin-right: 4px
|
||||||
|
.row-item-center
|
||||||
|
justify-content: center
|
||||||
|
text-align: center
|
||||||
|
.row-item-left
|
||||||
|
justify-content: left
|
||||||
|
.row-item-right
|
||||||
|
justify-content: right
|
||||||
|
.row-item-space-between
|
||||||
|
justify-content: space-between
|
||||||
|
.row-item-2
|
||||||
|
flex-basis: 4rem
|
||||||
|
.row-item-5
|
||||||
|
flex-basis: 5%
|
||||||
|
.row-item-10
|
||||||
|
flex-basis: 10%
|
||||||
|
.row-item-12
|
||||||
|
flex-basis: 4rem
|
||||||
|
.row-item-15
|
||||||
|
flex-basis: 5rem
|
||||||
|
.row-item-20
|
||||||
|
flex-basis: 20%
|
||||||
|
.row-item-25
|
||||||
|
flex-basis: 25%
|
||||||
|
.row-item-30
|
||||||
|
flex-basis: 30%
|
||||||
|
.row-item-35
|
||||||
|
flex-basis: 35%
|
||||||
|
.row-item-40
|
||||||
|
flex-basis: 40%
|
||||||
|
.row-item-45
|
||||||
|
flex-basis: 45%
|
||||||
|
.row-item-50
|
||||||
|
flex-basis: 50%
|
||||||
|
.row-item-65
|
||||||
|
flex-basis: 50%
|
||||||
|
.row-item-85
|
||||||
|
flex-basis: 50%
|
||||||
|
.row-item-storage
|
||||||
|
flex-wrap: wrap
|
||||||
|
flex-grow: 0
|
||||||
|
flex-basis: 20%
|
||||||
|
font-size: 0.7rem
|
||||||
|
line-height: 0.8rem
|
||||||
|
.item-control
|
||||||
|
&.item-equip
|
||||||
|
i
|
||||||
|
color: var(--mgt2-row-inactive-icon)
|
||||||
|
&.active
|
||||||
|
i
|
||||||
|
color: var(--mgt2-color-form)
|
||||||
|
.row-description
|
||||||
|
flex-basis: 100%
|
||||||
|
font-size: 14px
|
||||||
|
padding: 4px 0
|
||||||
|
justify-content: left
|
||||||
|
transition: all 0.15s ease-in-out
|
||||||
|
.row-sub-container
|
||||||
|
display: flex
|
||||||
|
flex-flow: column nowrap
|
||||||
|
flex: 1
|
||||||
|
.row-item
|
||||||
|
padding: 8px 0
|
||||||
|
border-bottom: 1px solid var(--mgt2-bgcolor-primary)
|
||||||
|
.table-row:last-child,
|
||||||
|
.row-sub-container .row-item:last-child
|
||||||
|
border-bottom: 0
|
||||||
|
.table-container
|
||||||
|
&.editable
|
||||||
|
.table-row
|
||||||
|
margin-top: 4px
|
||||||
|
.table-container
|
||||||
|
&.editable
|
||||||
|
.table-row:last-child
|
||||||
|
margin-bottom: 4px
|
||||||
|
.item-options
|
||||||
|
position: absolute
|
||||||
|
top: 0.7rem
|
||||||
|
font-size: 0.7em
|
||||||
|
left: 1.6rem
|
||||||
|
text-transform: uppercase
|
||||||
|
font-family: "DM Sans", sans-serif
|
||||||
|
font-optical-sizing: auto
|
||||||
|
font-weight: 600
|
||||||
|
font-style: normal
|
||||||
|
color: var(--mgt2-subrow-color)
|
||||||
58
src/sass/components/_tabs.sass
Normal file
58
src/sass/components/_tabs.sass
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
.mgt2
|
||||||
|
nav
|
||||||
|
&.horizontal-tabs
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
background: var(--mgt2-bgcolor-primary)
|
||||||
|
font-style: normal
|
||||||
|
font-weight: 700
|
||||||
|
font-size: 14px
|
||||||
|
line-height: 30px
|
||||||
|
text-transform: uppercase
|
||||||
|
justify-content: space-around
|
||||||
|
align-items: center
|
||||||
|
font-family: "Roboto Condensed", sans-serif
|
||||||
|
a
|
||||||
|
&.item
|
||||||
|
position: relative
|
||||||
|
flex: 1 1 auto
|
||||||
|
i
|
||||||
|
margin-right: 0.5rem
|
||||||
|
& > a
|
||||||
|
&.item
|
||||||
|
&::after
|
||||||
|
content: ""
|
||||||
|
position: absolute
|
||||||
|
inset: 0.25rem 0.25rem 0.25rem 0.25rem
|
||||||
|
border: 1px solid var( --mgt2-color-primary-active)
|
||||||
|
pointer-events: none
|
||||||
|
&.active
|
||||||
|
&::after
|
||||||
|
border-bottom: none
|
||||||
|
border-top: 2px solid var( --mgt2-color-primary-active)
|
||||||
|
border-left: 2px solid var( --mgt2-color-primary-active)
|
||||||
|
border-right: 2px solid var( --mgt2-color-primary-active)
|
||||||
|
inset: 0.25rem 0.25rem 0 0.25rem
|
||||||
|
.active
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
text-decoration: none
|
||||||
|
text-shadow: none
|
||||||
|
border-bottom: none
|
||||||
|
|
||||||
|
|
||||||
|
.tab[data-tab].fullsize
|
||||||
|
height: calc(100% - 3rem)
|
||||||
|
|
||||||
|
.subTab
|
||||||
|
flex-flow: column
|
||||||
|
height: 100%
|
||||||
|
display: flex
|
||||||
|
justify-content: flex-start
|
||||||
|
align-items: stretch
|
||||||
|
.tab-scroll
|
||||||
|
overflow-y: auto
|
||||||
|
height: 100%
|
||||||
|
.subTabs
|
||||||
|
height: 100%
|
||||||
|
flex-direction: column
|
||||||
|
&.active
|
||||||
|
display: flex !important
|
||||||
15
src/sass/mgt2.sass
Normal file
15
src/sass/mgt2.sass
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
@import 'utils/typography'
|
||||||
|
@import 'utils/colors'
|
||||||
|
@import 'utils/global'
|
||||||
|
@import 'utils/window'
|
||||||
|
@import 'utils/flex'
|
||||||
|
|
||||||
|
@import 'components/_forms'
|
||||||
|
@import 'components/_dialog'
|
||||||
|
@import 'components/_character'
|
||||||
|
@import 'components/_item'
|
||||||
|
@import 'components/_chat-sidebar'
|
||||||
|
|
||||||
|
@import 'components/_tabs'
|
||||||
|
@import 'components/_tab-sidebar'
|
||||||
|
@import 'components/_tables'
|
||||||
63
src/sass/utils/_colors.sass
Normal file
63
src/sass/utils/_colors.sass
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
$primary-color: #3498db
|
||||||
|
$secondary-color: #2ecc71
|
||||||
|
$background-color: #ecf0f1
|
||||||
|
$text-color: #34495e
|
||||||
|
|
||||||
|
.black-and-red
|
||||||
|
--mgt2-color-form: #0A0405
|
||||||
|
--mgt2-bgcolor-form: #fff
|
||||||
|
--mgt2-color-primary: #EE4050
|
||||||
|
--mgt2-color-primary-active: #AF2F3C
|
||||||
|
--mgt2-bgcolor-primary: #0A0405
|
||||||
|
--mgt2-color-primary-light: #4b4a44
|
||||||
|
--mgt2-color-warning: #EE4050
|
||||||
|
--mgt2-color-dm: #fff
|
||||||
|
--mgt2-bgcolor-dm: #0A0405
|
||||||
|
--mgt2-color-software: #fff
|
||||||
|
--mgt2-bgcolor-software: #0A0405
|
||||||
|
--mgt2-input-color: #0A0405
|
||||||
|
--mgt2-input-bgcolor: #fff
|
||||||
|
--mgt2-editor-border: #C6C6C6
|
||||||
|
--mgt2-row-hover: #F2F2F2
|
||||||
|
--mgt2-subrow-color: #727272
|
||||||
|
--mgt2-row-inactive-icon: #b5b3a4
|
||||||
|
--mgt2-encumbrance-normal: #D94826
|
||||||
|
--mgt2-encumbrance-heavy: #D82727
|
||||||
|
|
||||||
|
.mwamba
|
||||||
|
--mgt2-color-form: #0A0405
|
||||||
|
--mgt2-bgcolor-form: #fff
|
||||||
|
--mgt2-color-primary: #2A9932
|
||||||
|
--mgt2-color-primary-active: #40ED4E
|
||||||
|
--mgt2-bgcolor-primary: #0A0405
|
||||||
|
--mgt2-color-primary-light: #4b4a44
|
||||||
|
--mgt2-color-warning: #EE4050
|
||||||
|
--mgt2-color-dm: #fff
|
||||||
|
--mgt2-bgcolor-dm: #0A0405
|
||||||
|
--mgt2-color-software: #fff
|
||||||
|
--mgt2-bgcolor-software: #0A0405
|
||||||
|
--mgt2-input-color: #0A0405
|
||||||
|
--mgt2-input-bgcolor: #fff
|
||||||
|
--mgt2-editor-border: #C6C6C6
|
||||||
|
--mgt2-row-hover: #F2F2F2
|
||||||
|
--mgt2-subrow-color: #727272
|
||||||
|
--mgt2-row-inactive-icon: #b5b3a4
|
||||||
|
|
||||||
|
.blue
|
||||||
|
--mgt2-color-form: #0A0405
|
||||||
|
--mgt2-bgcolor-form: #fff
|
||||||
|
--mgt2-color-primary: #91AAC8
|
||||||
|
--mgt2-color-primary-active: #BCDCFF
|
||||||
|
--mgt2-bgcolor-primary: #0A0405
|
||||||
|
--mgt2-color-primary-light: #4b4a44
|
||||||
|
--mgt2-color-warning: #EE4050
|
||||||
|
--mgt2-color-dm: #fff
|
||||||
|
--mgt2-bgcolor-dm: #0A0405
|
||||||
|
--mgt2-color-software: #fff
|
||||||
|
--mgt2-bgcolor-software: #0A0405
|
||||||
|
--mgt2-input-color: #0A0405
|
||||||
|
--mgt2-input-bgcolor: #fff
|
||||||
|
--mgt2-editor-border: #C6C6C6
|
||||||
|
--mgt2-row-hover: #F2F2F2
|
||||||
|
--mgt2-subrow-color: #727272
|
||||||
|
--mgt2-row-inactive-icon: #b5b3a4
|
||||||
18
src/sass/utils/_flex.sass
Normal file
18
src/sass/utils/_flex.sass
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.mgt2
|
||||||
|
.flex-fix
|
||||||
|
flex-grow: 0 !important
|
||||||
|
flex-shrink: 0 !important
|
||||||
|
.flex-basis-10
|
||||||
|
flex-basis: 10%
|
||||||
|
.flex-basis-20
|
||||||
|
flex-basis: 20%
|
||||||
|
.flex-basis-30
|
||||||
|
flex-basis: 30%
|
||||||
|
.flex-basis-40
|
||||||
|
flex-basis: 40%
|
||||||
|
.flex-basis-50
|
||||||
|
flex-basis: 50%
|
||||||
|
.flex-basis-60
|
||||||
|
flex-basis: 60%
|
||||||
|
.flex-basis-70
|
||||||
|
flex-basis: 70%
|
||||||
39
src/sass/utils/_global.sass
Normal file
39
src/sass/utils/_global.sass
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
.upcase
|
||||||
|
text-transform: uppercase
|
||||||
|
|
||||||
|
.w1-10
|
||||||
|
width: calc(100% / 10)
|
||||||
|
|
||||||
|
.w2-10
|
||||||
|
width: calc(100% / 10 * 2)
|
||||||
|
|
||||||
|
.w3-10
|
||||||
|
width: calc(100% / 10 * 3)
|
||||||
|
|
||||||
|
.w4-10
|
||||||
|
width: calc(100% / 10 * 4)
|
||||||
|
|
||||||
|
.w5-10
|
||||||
|
width: calc(100% / 10 * 5)
|
||||||
|
|
||||||
|
.h100
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.w100
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.mgt2
|
||||||
|
a:hover
|
||||||
|
text-shadow: none
|
||||||
|
|
||||||
|
.w-100
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.mb-1
|
||||||
|
margin-bottom: 8px
|
||||||
|
|
||||||
|
.mt-1, .mt-05
|
||||||
|
margin-top: 8px
|
||||||
|
|
||||||
|
.mt-2
|
||||||
|
margin-top: 14px
|
||||||
3
src/sass/utils/_typography.sass
Normal file
3
src/sass/utils/_typography.sass
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap')
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap')
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap')
|
||||||
4
src/sass/utils/_variables.sass
Normal file
4
src/sass/utils/_variables.sass
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
$myFont: Helvetica, sans-serif
|
||||||
|
$myColor: red
|
||||||
|
$myFontSize: 18px
|
||||||
|
$myWidth: 680px
|
||||||
18
src/sass/utils/_window.sass
Normal file
18
src/sass/utils/_window.sass
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.mgt2
|
||||||
|
&.sheet
|
||||||
|
header
|
||||||
|
&.window-header
|
||||||
|
color: var(--mgt2-color-primary)
|
||||||
|
background-color: var(--mgt2-bgcolor-primary)
|
||||||
|
h4
|
||||||
|
&.window-title
|
||||||
|
font-weight: bold
|
||||||
|
text-transform: uppercase
|
||||||
|
&.sheet
|
||||||
|
.window-content
|
||||||
|
background: var(--mgt2-bgcolor-form)
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
.nopad
|
||||||
|
.window-content
|
||||||
|
padding: 0
|
||||||
11
src/todo.md
Normal file
11
src/todo.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# BUGS
|
||||||
|
|
||||||
|
|
||||||
|
# Chose à faire
|
||||||
|
|
||||||
|
- Enlever les styles inlines
|
||||||
|
|
||||||
|
Actors
|
||||||
|
- NPC
|
||||||
|
- Creature
|
||||||
|
- Container
|
||||||
1026
styles/mgt2.min.css
vendored
1026
styles/mgt2.min.css
vendored
File diff suppressed because one or more lines are too long
1
styles/mgt2.min.css.map
Normal file
1
styles/mgt2.min.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":["../src/sass/utils/_typography.sass","../src/sass/utils/_colors.sass","../src/sass/utils/_global.sass","../src/sass/utils/_window.sass","../src/sass/utils/_flex.sass","../src/sass/components/_forms.sass","../src/sass/components/_dialog.sass","../src/sass/components/_character.sass","../src/sass/components/_item.sass","../src/sass/components/_chat-sidebar.sass","../src/sass/components/_tabs.sass","../src/sass/components/_tab-sidebar.sass","../src/sass/components/_tables.sass"],"names":[],"mappings":"AAAQ,sMACA,qGACA,0HCGR,eACI,2BACA,0BACA,8BACA,qCACA,gCACA,oCACA,8BACA,sBACA,2BACA,4BACA,iCACA,4BACA,2BACA,8BACA,0BACA,6BACA,kCACA,mCACA,kCAEJ,QACI,2BACA,0BACA,8BACA,qCACA,gCACA,oCACA,8BACA,sBACA,2BACA,4BACA,iCACA,4BACA,2BACA,8BACA,0BACA,6BACA,kCAEJ,MACI,2BACA,0BACA,8BACA,qCACA,gCACA,oCACA,8BACA,sBACA,2BACA,4BACA,iCACA,4BACA,2BACA,8BACA,0BACA,6BACA,kCC9DJ,QACC,yBAED,OACC,UAED,OACC,UAED,OACC,UAED,OACC,UAED,OACC,UAED,MACC,YAED,MACC,WAGA,cACC,iBAED,aACC,WAED,YACC,kBAED,yBACC,eAED,YACC,gBCnCC,iCACC,gCACA,6CAED,4BACC,iBACA,yBAEF,4BACC,oCACA,UAGF,uBACC,UChBD,gBACC,uBACA,yBACD,qBACC,eACD,qBACC,eACD,qBACC,eACD,qBACC,eACD,qBACC,eACD,qBACC,eACD,qBACC,eCfA,qBACC,8BACA,2CACA,gCACA,eACA,kBAEF,0DAGC,aACA,gBAED,sBACC,qBACA,sBACA,uBACA,oBAED,cACC,gCACA,uCACA,eACA,0CACA,iBACA,iBACA,kBACA,iBACA,yBAED,oBACC,aACA,mBACA,iBACA,mBACA,8BAGA,yBACC,yBACA,gBACA,eACA,0CACA,yBAED,kBACC,2CACA,eACD,uBACC,2CACA,eACA,YACA,gBACA,0CACA,oBACA,UACD,4BACC,2CACA,YACA,eACA,gBACA,0CAEF,cACC,aAED,cACC,gBACA,2CACA,YAED,kBACC,kBACA,sBAGA,0BACC,aACA,mBACA,mBACA,gCACC,mBClFH,qBACC,gCACA,wDCFD,4BACC,YAEF,SACC,eACA,uBACA,mBACA,WACC,kBAGD,uBACC,iBACA,YACA,cACA,kBAED,8BACC,gCACA,uCACA,8BACA,kBACA,eACA,iBACA,yBAED,0BACC,aACA,mBACA,mBACA,uBACA,kBAED,6BACC,aACA,mBACA,mBACA,uBACA,iBAIC,oCACC,gBACA,gBAEH,4BACC,0CACA,iBACA,gBACA,kBACA,kBACA,kBAEC,mCACC,WACA,kBACA,OAED,iDACC,aACA,eACA,kBACA,QACA,MAGA,uDACC,cAEJ,4BACC,kCACA,kBACA,iBACA,gBACA,aACA,YACA,sBACA,YACA,aACA,2fAED,yBACC,2BACA,+BACA,kBACA,eACA,aACA,cACA,UACA,aACA,YACA,SACA,kBACA,QACA,wCACA,kBACA,iBACA,+BACC,aACA,gBAGD,sCACC,aACA,gBAEF,qBACC,aACA,mBACA,iBACA,uBACA,uBACA,mBACA,eACA,sBACA,2fACA,2BACC,qBACA,WACA,+BACA,kBACA,iBACA,WACA,YACA,YACA,aACA,iCACC,aACA,gBACD,uCACC,gBACD,sCACC,iBACF,0BACC,iBACA,gBACH,wCAEC,2CAEA,aACC,gBACA,SACA,UACA,gBACC,qBACA,iCACA,8CACA,gBACA,kBACA,kBACC,aACA,8BACC,eAED,wBACC,qBAEL,kBACC,aACA,eACA,iBACA,mBACA,iBACA,YACA,cACA,2BACA,uBACD,sBACC,iBACA,YACA,cACA,kBACD,mBACC,cACA,SACA,UACA,gBACA,kDACA,sBACC,WACA,SACA,UACA,sCACD,yBACC,cACA,YACA,iBACA,0CACA,sBACA,gBACA,sBACA,+BACC,sBACH,uBACC,aACA,sBACA,iBACA,2BACA,8BACC,gBACA,yBACA,iBACA,gCACC,oBACH,gBACC,aACA,mBACA,yBACA,iBACA,qBACC,WAGF,OACC,YACA,aACA,mBACA,uBACA,mBACA,WACC,eACF,kBACC,SACA,UACA,gBACA,aACA,sBACA,2BACA,uBACA,WACA,YACA,qBACC,aACA,SACA,UACA,cACA,8BACA,mBACA,WACA,gBACA,mBAEF,oBACC,gDAED,mBACC,+CACA,iBAED,gBACC,YACA,gBACA,aACA,mBACA,WACA,2BACA,uBACA,0BAED,cACC,SACA,gCACA,6CACA,WACA,cACA,cACA,8BACA,mBACA,eACA,YACA,cACA,aACA,mBCnRD,WACC,aACA,iBACA,mBACD,kBACC,aACA,6CACA,UACA,cACA,mBACA,cACA,wBACC,mBACA,0BACA,gBACA,eACA,mBACA,uCACA,kBACA,yBACF,mBACC,eACA,mBACD,iBACC,aACA,aACA,aAEC,iCACC,YACA,aACH,mCAEC,8BACA,2CACA,cACA,WACA,eCrCD,mCAEC,0BACA,6CACA,kBACA,8BAMD,0BAEC,sBACD,cACC,aACA,uBACA,mBACA,iBACA,cACA,eAEC,uBACC,iBACA,UACA,SACH,WACC,aACA,sBACD,iBACC,mBACA,eACA,8BACA,aACD,gBACC,eACA,yBACA,cACD,kBACC,gBACA,iBACD,cACC,iBACA,iBACA,yBACA,gBACA,kBC5CC,0BACC,gCACA,uCACA,kBACA,gBACA,eACA,iBACA,yBACA,6BACA,mBACA,0CAEC,iCACC,kBACA,cACA,mCACC,mBAGD,wCACC,WACA,kBACA,kCACA,kDACA,oBAEA,+CACC,mBACA,sDACA,uDACA,wDACA,6BACJ,kCACC,gCACA,qBACA,iBACA,mBAGJ,wBACC,yBAED,QACC,iBACA,YACA,aACA,2BACA,oBACD,YACC,gBACA,YACD,SACC,YACA,sBACA,gBACC,wBCvDA,2BACC,cAEF,mCACC,kBACA,UACA,UACA,aACA,sBACA,WACA,yCACC,YACA,kBACA,aACA,oBACA,mBACA,qBACA,uCACA,gCACA,+BACA,eACA,0BACA,cACA,gDACC,iBACA,SACA,uCACA,uDACC,iBACA,6BACF,gDACC,WACA,kBACA,aACA,2CACA,oBACD,2CACC,kBCvCJ,oBACC,qBACA,iBAEC,uCACC,mBAEH,iBACC,aACA,wBACA,WACA,cACD,WACC,aACA,qBACA,WACA,kBACA,uBACA,mBACC,6CACA,mBACA,6BACC,yBACA,eACA,yCACC,iBACA,eACA,mBACA,2CACC,mBAEF,qCACC,6CACA,gCAED,qCACC,0CACA,kCAED,gCACC,iBACA,2CACC,kBAEH,+BACC,uCACH,gBACC,kBACD,UACC,aACA,YACA,eACA,iBACA,mBACA,gCACA,2BACA,uBACA,iBACA,wBACC,sBACA,kBAEC,2CACC,mBACH,YACC,WACD,uBACC,mBAIC,6BACC,2CAGD,6BACC,6CACJ,cACC,gDACA,+BACA,wBACC,eACA,iBACA,oCACC,kBACD,0BACC,iBACH,iBACC,uBACA,kBACD,eACC,qBACD,gBACC,sBACD,wBACC,8BACD,YACC,gBACD,YACC,cACD,aACC,eACD,aACC,gBACD,aACC,gBACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,kBACC,eACA,YACA,eACA,gBACA,kBAGC,2BACC,oCAEA,kCACC,6BACJ,iBACC,gBACA,eACA,cACA,qBACA,gCACD,mBACC,aACA,wBACA,OACA,6BACC,cACA,oDACF,8DAEC,gBAGC,qCACC,eAGD,gDACC,kBACH,cACC,kBACA,UACA,eACA,YACA,yBACA,iCACA,yBACA,gBACA,kBACA","file":"mgt2.min.css"}
|
||||||
@@ -39,7 +39,9 @@
|
|||||||
"minimum": "11",
|
"minimum": "11",
|
||||||
"verified": "12.324"
|
"verified": "12.324"
|
||||||
},
|
},
|
||||||
"gridDistance": 1.5,
|
"grid": {
|
||||||
"gridUnits": "m",
|
"distance": 1.5,
|
||||||
|
"units": "m"
|
||||||
|
},
|
||||||
"primaryTokenAttribute": "life"
|
"primaryTokenAttribute": "life"
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} flexcol" autocomplete="off" style="align-content: flex-start;align-items: baseline;overflow: hidden;height: 100%;">
|
<div class="{{cssClass}} flexcol" style="align-content: flex-start;align-items: baseline;overflow: hidden;height: 100%;">
|
||||||
<nav class="sheet-sidebar tabs" data-group="sidebar">
|
<nav class="sheet-sidebar tabs" data-group="sidebar">
|
||||||
<!-- <a class="item tab-select" data-tab="personal" title="Personal"><i class="fa-solid fa-id-card"></i></a> -->
|
<!-- <a class="item tab-select" data-tab="personal" title="Personal"><i class="fa-solid fa-id-card"></i></a> -->
|
||||||
<a class="item tab-select" data-tab="health" title="{{ localize 'MGT2.Actor.Health' }}"><i class="fa-solid fa-heart-pulse"></i></a>
|
<a class="item tab-select" data-tab="health" title="{{ localize 'MGT2.Actor.Health' }}"><i class="fa-solid fa-heart-pulse"></i></a>
|
||||||
@@ -745,4 +745,4 @@
|
|||||||
<div><a name="config" title="Config" style="margin-right: 0.5rem;"><i class="fa-solid fa-gear"></i></a></div>
|
<div><a name="config" title="Config" style="margin-right: 0.5rem;"><i class="fa-solid fa-gear"></i></a></div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header">
|
<div class="itemsheet-header">
|
||||||
<label>{{localize 'MGT2.TYPES.Item.armor'}}</label>
|
<label>{{localize 'MGT2.TYPES.Item.armor'}}</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,4 +71,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Career' }}</label></div>
|
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Career' }}</label></div>
|
||||||
<div class="itemsheet-panel">
|
<div class="itemsheet-panel">
|
||||||
<div class="itemsheet-maincol">
|
<div class="itemsheet-maincol">
|
||||||
@@ -47,4 +47,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header">
|
<div class="itemsheet-header">
|
||||||
<label>{{localize 'MGT2.EquipmentSubType.computer'}}</label>
|
<label>{{localize 'MGT2.EquipmentSubType.computer'}}</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,4 +62,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Contact' }}</label></div>
|
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Contact' }}</label></div>
|
||||||
<div class="itemsheet-panel">
|
<div class="itemsheet-panel">
|
||||||
<div class="itemsheet-maincol">
|
<div class="itemsheet-maincol">
|
||||||
@@ -81,4 +81,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Container' }}</label></div>
|
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Container' }}</label></div>
|
||||||
<div class="itemsheet-panel">
|
<div class="itemsheet-panel">
|
||||||
<div class="itemsheet-maincol">
|
<div class="itemsheet-maincol">
|
||||||
@@ -52,4 +52,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header">
|
<div class="itemsheet-header">
|
||||||
{{#if (eq system.subType "disease")}}
|
{{#if (eq system.subType "disease")}}
|
||||||
<label>{{localize 'MGT2.DiseaseSubType.disease'}}</label>
|
<label>{{localize 'MGT2.DiseaseSubType.disease'}}</label>
|
||||||
@@ -42,4 +42,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header">
|
<div class="itemsheet-header">
|
||||||
{{#if (eq system.subType "augment")}}
|
{{#if (eq system.subType "augment")}}
|
||||||
<label>{{localize 'MGT2.EquipmentSubType.augment'}}</label>
|
<label>{{localize 'MGT2.EquipmentSubType.augment'}}</label>
|
||||||
@@ -54,4 +54,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header">
|
<div class="itemsheet-header">
|
||||||
{{#if (eq system.subType "loot")}}
|
{{#if (eq system.subType "loot")}}
|
||||||
<label>{{localize 'MGT2.ItemSubType.loot'}}</label>
|
<label>{{localize 'MGT2.ItemSubType.loot'}}</label>
|
||||||
@@ -57,4 +57,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header"><label>{{ localize 'MGT2.Specie' }}</label></div>
|
<div class="itemsheet-header"><label>{{ localize 'MGT2.Specie' }}</label></div>
|
||||||
<div class="itemsheet-panel">
|
<div class="itemsheet-panel">
|
||||||
<div class="itemsheet-maincol">
|
<div class="itemsheet-maincol">
|
||||||
@@ -67,4 +67,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} flexrow itemsheet" autocomplete="off">
|
<div class="{{cssClass}} flexrow itemsheet">
|
||||||
<div class="itemsheet-header">
|
<div class="itemsheet-header">
|
||||||
{{#if (eq system.subType "skill")}}
|
{{#if (eq system.subType "skill")}}
|
||||||
<label>{{localize 'MGT2.TalentSubType.skill'}}</label>
|
<label>{{localize 'MGT2.TalentSubType.skill'}}</label>
|
||||||
@@ -76,4 +76,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="{{cssClass}} itemsheet" autocomplete="off">
|
<div class="{{cssClass}} itemsheet">
|
||||||
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Weapon' }}</label></div>
|
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Weapon' }}</label></div>
|
||||||
<div class="itemsheet-panel">
|
<div class="itemsheet-panel">
|
||||||
<div class="itemsheet-maincol">
|
<div class="itemsheet-maincol">
|
||||||
@@ -86,4 +86,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ localize 'MGT2.RollPrompt.CharacteristicDM' }}</label>
|
<label>{{ localize 'MGT2.RollPrompt.CharacteristicDM' }}</label>
|
||||||
<select name="characteristic">
|
<select name="characteristic">
|
||||||
{{selectOptions characteristics selected=characteristic nameAttr="_id" labelAttr="name"}}
|
{{selectOptions characteristics selected=characteristic valueAttr="_id" labelAttr="name"}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ localize 'MGT2.RollPrompt.SkillDM' }}</label>
|
<label>{{ localize 'MGT2.RollPrompt.SkillDM' }}</label>
|
||||||
<select name="skill">
|
<select name="skill">
|
||||||
<option></option>
|
<option></option>
|
||||||
{{selectOptions skills selected=skill nameAttr="_id" labelAttr="name"}}
|
{{selectOptions skills selected=skill valueAttr="_id" labelAttr="name"}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|||||||
Reference in New Issue
Block a user