Ajouter les sources
This commit is contained in:
87
src/module/actors/actor.js
Normal file
87
src/module/actors/actor.js
Normal file
@@ -0,0 +1,87 @@
|
||||
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 [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
153
src/module/actors/character-prompts.js
Normal file
153
src/module/actors/character-prompts.js
Normal file
@@ -0,0 +1,153 @@
|
||||
class EditorFullViewDialog extends Dialog {
|
||||
constructor(dialogData = {}, options = {}) {
|
||||
super(dialogData, options);
|
||||
this.options.classes = ["mgt2", game.settings.get("mgt2", "theme"), "sheet"];
|
||||
this.options.resizable = true;
|
||||
}
|
||||
|
||||
static async create(title, html) {
|
||||
const htmlContent = await renderTemplate("systems/mgt2/templates/editor-fullview.html", {
|
||||
config: CONFIG.MGT2,
|
||||
html: html
|
||||
});
|
||||
|
||||
const results = new Promise(resolve => {
|
||||
new this({
|
||||
title: title,
|
||||
content: htmlContent,
|
||||
buttons: {
|
||||
//close: { label: game.i18n.localize("MGT2.Close") }
|
||||
}
|
||||
}).render(true);
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
class ActorConfigDialog extends Dialog {
|
||||
constructor(dialogData = {}, options = {}) {
|
||||
super(dialogData, options);
|
||||
this.options.classes = ["mgt2", game.settings.get("mgt2", "theme"), "sheet"];
|
||||
}
|
||||
|
||||
static async create(system) {
|
||||
const htmlContent = await renderTemplate("systems/mgt2/templates/actors/actor-config-sheet.html", {
|
||||
config: CONFIG.MGT2,
|
||||
system: system
|
||||
});
|
||||
|
||||
const results = new Promise(resolve => {
|
||||
new this({
|
||||
title: "Configuration",
|
||||
content: htmlContent,
|
||||
buttons: {
|
||||
submit: {
|
||||
label: game.i18n.localize("MGT2.Save"),
|
||||
icon: '<i class="fa-solid fa-floppy-disk"></i>',
|
||||
callback: (html) => {
|
||||
const formData = new FormDataExtended(html[0].querySelector('form')).object;
|
||||
resolve(formData);
|
||||
},
|
||||
}
|
||||
}
|
||||
}).render(true);
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
class ActorCharacteristicDialog extends Dialog {
|
||||
// https://foundryvtt.wiki/en/development/api/dialog
|
||||
constructor(dialogData = {}, options = {}) {
|
||||
super(dialogData, options);
|
||||
this.options.classes = ["mgt2", game.settings.get("mgt2", "theme"), "sheet"];
|
||||
}
|
||||
|
||||
static async create(name, show, showMax, showAll = false) {
|
||||
const htmlContent = await renderTemplate("systems/mgt2/templates/actors/actor-config-characteristic-sheet.html", {
|
||||
name: name,
|
||||
show: show,
|
||||
showMax: showMax,
|
||||
showAll: showAll
|
||||
});
|
||||
|
||||
const results = new Promise(resolve => {
|
||||
new this({
|
||||
title: "Configuration: " + name,
|
||||
content: htmlContent,
|
||||
buttons: {
|
||||
submit: {
|
||||
label: game.i18n.localize("MGT2.Save"),
|
||||
icon: '<i class="fa-solid fa-floppy-disk"></i>',
|
||||
callback: (html) => {
|
||||
const formData = new FormDataExtended(html[0].querySelector('form')).object;
|
||||
resolve(formData);
|
||||
},
|
||||
}
|
||||
}
|
||||
}).render(true);
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
class TraitEditDialog extends Dialog {
|
||||
constructor(dialogData = {}, options = {}) {
|
||||
super(dialogData, options);
|
||||
this.options.classes = ["mgt2", game.settings.get("mgt2", "theme"), "sheet"];
|
||||
}
|
||||
|
||||
static async create(data) {
|
||||
const htmlContent = await renderTemplate("systems/mgt2/templates/actors/trait-sheet.html", {
|
||||
config: CONFIG.MGT2,
|
||||
data: data
|
||||
});
|
||||
const title = data.hasOwnProperty("name") && data.name !== undefined ? data.name : game.i18n.localize("MGT2.Actor.EditTrait");
|
||||
const results = new Promise(resolve => {
|
||||
new this({
|
||||
title: title,
|
||||
content: htmlContent,
|
||||
buttons: {
|
||||
submit: {
|
||||
label: game.i18n.localize("MGT2.Save"),
|
||||
icon: '<i class="fa-solid fa-floppy-disk"></i>',
|
||||
callback: (html) => {
|
||||
const formData = new FormDataExtended(html[0].querySelector('form')).object;
|
||||
resolve(formData);
|
||||
},
|
||||
}
|
||||
//cancel: { label: "Cancel" }
|
||||
}
|
||||
// close: (html) => {
|
||||
// console.log("This always is logged no matter which option is chosen");
|
||||
// const formData = new FormDataExtended(html[0].querySelector('form')).object;
|
||||
// resolve(formData);
|
||||
// }
|
||||
}).render(true);
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
export class CharacterPrompts {
|
||||
|
||||
static async openConfig(system) {
|
||||
return await ActorConfigDialog.create(system);
|
||||
}
|
||||
|
||||
static async openCharacteristic(name, hide, showMax, showAll = false) {
|
||||
return await ActorCharacteristicDialog.create(name, hide, showMax, showAll);
|
||||
}
|
||||
|
||||
static async openTraitEdit(data) {
|
||||
return await TraitEditDialog.create(data);
|
||||
}
|
||||
|
||||
static async openEditorFullView(title, html) {
|
||||
return await EditorFullViewDialog.create(title, html);
|
||||
}
|
||||
}
|
||||
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
455
src/module/actors/character.js
Normal file
455
src/module/actors/character.js
Normal file
@@ -0,0 +1,455 @@
|
||||
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 = duplicate(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();
|
||||
}
|
||||
|
||||
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 = duplicate($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 cloneActor = duplicate($this);
|
||||
|
||||
await this.recalculateArmor($this, cloneActor);
|
||||
|
||||
if (recalculEncumbrance) {
|
||||
//console.log("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;
|
||||
|
||||
cloneActor.system.states.encumbrance = $this.system.inventory.weight > normal;
|
||||
cloneActor.system.encumbrance.normal = normal;
|
||||
cloneActor.system.encumbrance.heavy = heavy;
|
||||
}
|
||||
|
||||
if (recalculWeight)
|
||||
await this.recalculateWeight($this, cloneActor);
|
||||
}
|
||||
}
|
||||
|
||||
static async recalculateArmor($this, cloneActor) {
|
||||
if (cloneActor === null || cloneActor === undefined)
|
||||
cloneActor = duplicate($this);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cloneActor.system.inventory.armor = armor;
|
||||
}
|
||||
|
||||
static async recalculateWeight($this, cloneActor) {
|
||||
|
||||
if (cloneActor === null || cloneActor === undefined)
|
||||
cloneActor = duplicate($this);
|
||||
|
||||
let updatedContainers = [];
|
||||
let containerChanges = {};
|
||||
|
||||
//console.log("recalculWeight");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//cloneActor.system.inventory.weight = onHandWeight.toFixed(1);
|
||||
|
||||
// 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) {
|
||||
//const cloneContainer = duplicate();
|
||||
const cloneContainer = duplicate($this.getEmbeddedDocument("Item", container._id));
|
||||
//foundry.utils.setProperty(cloneContainer, "system.weight", newWeight);
|
||||
cloneContainer.system.weight = newWeight;
|
||||
cloneContainer.system.count = newCount;
|
||||
updatedContainers.push(cloneContainer);
|
||||
|
||||
if (container.system.onHand === true &&
|
||||
(container.system.weight > 0 || container.system.weightless !== true)) {
|
||||
onHandWeight += container.system.weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cloneActor.system.inventory.weight = onHandWeight;
|
||||
cloneActor.system.states.encumbrance = onHandWeight > $this.system.inventory.encumbrance.normal;
|
||||
|
||||
|
||||
await $this.update(cloneActor);
|
||||
|
||||
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" }
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
126
src/module/chatHelper.js
Normal file
126
src/module/chatHelper.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import { MGT2Helper } from "./helper.js";
|
||||
|
||||
export class ChatHelper {
|
||||
|
||||
|
||||
// _injectContent(message, type, html) {
|
||||
|
||||
// _setupCardListeners(message, html);
|
||||
|
||||
// }
|
||||
|
||||
|
||||
static setupCardListeners(message, html, messageData) {
|
||||
if (!message || !html) {
|
||||
return;
|
||||
}
|
||||
// if (SettingsUtility.getSettingValue(SETTING_NAMES.MANUAL_DAMAGE_MODE) > 0) {
|
||||
// html.find('.card-buttons').find(`[data-action='rsr-${ROLL_TYPE.DAMAGE}']`).click(async event => {
|
||||
// await _processDamageButtonEvent(message, event);
|
||||
// });
|
||||
// }
|
||||
html.find('button[data-action="rollDamage"]').click(async event => {
|
||||
//ui.notifications.warn("rollDamage");
|
||||
await this._processRollDamageButtonEvent(message, event);
|
||||
});
|
||||
|
||||
html.find('button[data-action="damage"]').click(async event => {
|
||||
//ui.notifications.warn("damage");
|
||||
await this._applyChatCardDamage(message, event);
|
||||
//await _processApplyButtonEvent(message, event);
|
||||
});
|
||||
|
||||
html.find('button[data-action="healing"]').click(async event => {
|
||||
ui.notifications.warn("healing");
|
||||
//await _processApplyTotalButtonEvent(message, event);
|
||||
});
|
||||
|
||||
html.find('button[data-index]').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({ async: true });
|
||||
//console.log(message);
|
||||
|
||||
const chatData = {
|
||||
user: game.user.id,
|
||||
speaker: message.speaker,
|
||||
formula: roll._formula,
|
||||
tooltip: await roll.getTooltip(),
|
||||
total: Math.round(roll.total * 100) / 100,
|
||||
//formula: isPrivate ? "???" : roll._formula,
|
||||
//tooltip: isPrivate ? "" : await roll.getTooltip(),
|
||||
//total: isPrivate ? "?" : Math.round(roll.total * 100) / 100,
|
||||
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
|
||||
rollObjectName: button.message.objectName,
|
||||
rollMessage: MGT2Helper.format(button.message.flavor, Math.round(roll.total * 100) / 100),
|
||||
};
|
||||
|
||||
const html = await 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({ async: true });
|
||||
|
||||
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,
|
||||
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
|
||||
showButtons: true,
|
||||
hasDamage: true,
|
||||
rollTypeName: rollTypeName,
|
||||
rollObjectName: message.flags.mgt2.damage.rollObjectName
|
||||
};
|
||||
|
||||
const html = await renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
|
||||
chatData.content = html;
|
||||
|
||||
return roll.toMessage(chatData);
|
||||
}
|
||||
|
||||
async _processDamageButtonEvent(message, event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
//message.flags[MODULE_SHORT].manualDamage = false
|
||||
//message.flags[MODULE_SHORT].renderDamage = true;
|
||||
// current user/actor
|
||||
|
||||
await ItemUtility.runItemAction(null, message, ROLL_TYPE.DAMAGE);
|
||||
}
|
||||
|
||||
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"];
|
||||
124
src/module/core.js
Normal file
124
src/module/core.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
CharacterData,
|
||||
ItemData,
|
||||
EquipmentData,
|
||||
DiseaseData,
|
||||
CareerData,
|
||||
TalentData,
|
||||
ContactData,
|
||||
ArmorData,
|
||||
ComputerData,
|
||||
WeaponData,
|
||||
ItemContainerData,
|
||||
SpeciesData
|
||||
} from "./datamodels.js";
|
||||
|
||||
import { MGT2 } from "./config.js";
|
||||
import { TravellerActor, MGT2Combatant } from "./actors/actor.js";
|
||||
import { TravellerItem } from "./item.js";
|
||||
import { TravellerItemSheet } from "./item-sheet.js";
|
||||
import { TravellerActorSheet } from "./actors/character-sheet.js";
|
||||
import { 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;
|
||||
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
Actors.registerSheet("mgt2", TravellerActorSheet, { types: ["character"], makeDefault: true, label: "Traveller Sheet" });
|
||||
|
||||
Items.unregisterSheet("core", ItemSheet);
|
||||
Items.registerSheet("mgt2", TravellerItemSheet, { makeDefault: true });
|
||||
|
||||
Object.assign(CONFIG.Actor.dataModels, {
|
||||
"character": CharacterData
|
||||
});
|
||||
|
||||
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("renderChatMessage", (message, html, messageData) => {
|
||||
ChatHelper.setupCardListeners(message, html, messageData);
|
||||
});
|
||||
|
||||
// Preload template partials
|
||||
await preloadHandlebarsTemplates();
|
||||
});
|
||||
|
||||
export { MGT2 };
|
||||
485
src/module/datamodels.js
Normal file
485
src/module/datamodels.js
Normal file
@@ -0,0 +1,485 @@
|
||||
// https://foundryvtt.com/article/system-data-models/
|
||||
// https://foundryvtt.com/api/classes/foundry.data.fields.NumberField.html
|
||||
// https://foundryvtt.com/api/v10/classes/foundry.data.fields.DataField.html
|
||||
const fields = foundry.data.fields;
|
||||
|
||||
export class CharacterData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
// XP
|
||||
return {
|
||||
name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
personal: new fields.SchemaField({
|
||||
title: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
species: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
speciesText: new fields.SchemaField({
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true })
|
||||
}),
|
||||
age: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
gender: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
pronouns: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
homeworld: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
ucp: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
)
|
||||
}),
|
||||
biography: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
characteristics: new fields.SchemaField({
|
||||
strength: createCharacteristicField(true, true),
|
||||
dexterity: createCharacteristicField(true, true),
|
||||
endurance: createCharacteristicField(true, true),
|
||||
intellect: createCharacteristicField(true, false),
|
||||
education: createCharacteristicField(true, false),
|
||||
social: createCharacteristicField(true, false),
|
||||
morale: createCharacteristicField(true, false),
|
||||
luck: createCharacteristicField(true, false),
|
||||
sanity: createCharacteristicField(true, false),
|
||||
charm: createCharacteristicField(true, false),
|
||||
psionic: createCharacteristicField(true, false),
|
||||
other: createCharacteristicField(true, false)
|
||||
}),
|
||||
|
||||
health: new fields.SchemaField({
|
||||
radiations: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
study: new fields.SchemaField({
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
total: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
completed: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
finance: new fields.SchemaField({
|
||||
pension: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
credits: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
cashOnHand: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
debt: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
livingCost: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
monthlyShipPayments: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
notes: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
}),
|
||||
containerView: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
containerDropIn: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
notes: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
inventory: new fields.SchemaField({
|
||||
armor: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
weight: new fields.NumberField({ required: true, initial: 0, min: 0, integer: false }),
|
||||
encumbrance: new fields.SchemaField({
|
||||
normal: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
heavy: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
states: new fields.SchemaField({
|
||||
encumbrance: new fields.BooleanField({ required: false, initial: false }),
|
||||
fatigue: new fields.BooleanField({ required: false, initial: false }),
|
||||
unconscious: new fields.BooleanField({ required: false, initial: false }),
|
||||
surgeryRequired: new fields.BooleanField({ required: false, initial: false })
|
||||
}),
|
||||
|
||||
config: new fields.SchemaField({
|
||||
psionic: new fields.BooleanField({ required: false, initial: true }),
|
||||
initiative: new fields.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
damages: new fields.SchemaField({
|
||||
rank1: new fields.StringField({ required: false, blank: true, initial: "strength" }),
|
||||
rank2: new fields.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
rank3: new fields.StringField({ required: false, blank: true, initial: "endurance" })
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// export class CreatureData extends foundry.abstract.TypeDataModel {
|
||||
// static defineSchema() {
|
||||
// return {
|
||||
// name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
// TL: new fields.StringField({ required: true, blank: false, initial: "NA" }),
|
||||
// species: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
// //cost: new fields.NumberField({ required: true, integer: true }),
|
||||
// armor: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
// life: new fields.SchemaField({
|
||||
// value: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
// max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
// }),
|
||||
|
||||
// speed: new fields.StringField({ required: false, initial: "4m", blank: true, trim: true }),
|
||||
|
||||
// traits: new fields.ArrayField(
|
||||
// new fields.SchemaField({
|
||||
// name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
// description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
// })
|
||||
// ),
|
||||
|
||||
// description: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
// behaviour: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
// export class NPCData extends CreatureData {
|
||||
// static defineSchema() {
|
||||
// const schema = super.defineSchema();
|
||||
// // Species, Gender, Age
|
||||
// // STR, DEX, END, INT,. EDU, SOC, PSI, SKILL/Psy, equipment
|
||||
// // Status
|
||||
// schema.secret = new fields.HTMLField({ required: false, blank: true, trim: true });
|
||||
|
||||
// return schema;
|
||||
// }
|
||||
// }
|
||||
|
||||
export class VehiculeData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
|
||||
skillId: new fields.StringField({ required: false, initial: "", blank: true, trim: true }),
|
||||
speed: new fields.SchemaField({
|
||||
cruise: new fields.StringField({ required: false, initial: "Slow", blank: true }),
|
||||
maximum: new fields.StringField({ required: false, initial: "Medium", blank: true })
|
||||
}),
|
||||
agility: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
crew: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
passengers: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
cargo: new fields.NumberField({ required: false, min: 0, integer: false }),
|
||||
//hull
|
||||
life: new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
shipping: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
cost: new fields.NumberField({ required: false, min: 0, integer: true }),
|
||||
armor: new fields.SchemaField({
|
||||
front: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
rear: new fields.NumberField({ required: true, initial: 0, integer: true }),
|
||||
sides: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
|
||||
skills: new fields.SchemaField({
|
||||
// Skill Level
|
||||
autopilot: new fields.NumberField({ required: true, initial: 0, integer: true })
|
||||
// Communication Range
|
||||
// Navigation
|
||||
// Sensors
|
||||
// Camouflage / Recon
|
||||
// Stealth
|
||||
})
|
||||
// config: new fields.SchemaField({
|
||||
// })
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ItemBaseData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
const schema = {
|
||||
//name: new fields.StringField({ required: true, blank: true, trim: true, nullable: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
//type: new fields.StringField({ required: false, blank: false }),
|
||||
subType: new fields.StringField({ required: false, blank: false, nullable: true })
|
||||
};
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
class PhysicalItemData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.quantity = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true });
|
||||
schema.weight = new fields.NumberField({ required: true, initial: 0, min: 0, integer: false });
|
||||
schema.weightless = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.tl = new fields.StringField({ required: true, blank: false, initial: "TL12" });
|
||||
schema.container = new fields.SchemaField({
|
||||
//inContainer: new fields.BooleanField({ required: false, initial: false }),
|
||||
id: new fields.StringField({ required: false, blank: true })
|
||||
});
|
||||
|
||||
schema.roll = new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
|
||||
schema.trash = new fields.BooleanField({ required: false, initial: false });
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ItemData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "loot";
|
||||
schema.software = new fields.SchemaField({
|
||||
bandwidth: new fields.NumberField({ required: false, initial: 0, min: 0, max: 10, integer: true }),
|
||||
effect: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
computerId: new fields.StringField({ required: false, blank: true, initial: "" })
|
||||
});
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class EquipmentData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
// augment, clothes
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
//schema.skillModifier = new fields.StringField({ required: false, blank: true });
|
||||
//schema.characteristicModifier = new fields.StringField({ required: false, blank: true });
|
||||
|
||||
schema.augment = new fields.SchemaField({
|
||||
improvement: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
|
||||
schema.subType.initial = "equipment"; // augment, clothing, trinket, toolkit, equipment
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class DiseaseData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.subType.initial = "disease"; // disease;poison
|
||||
schema.difficulty = new fields.StringField({ required: true, initial: "Average" });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true });
|
||||
schema.interval = new fields.StringField({ required: false, blank: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class CareerData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.difficulty = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true });
|
||||
schema.interval = new fields.StringField({ required: false, blank: true });
|
||||
|
||||
schema.assignment = new fields.StringField({ required: false, blank: true });
|
||||
schema.terms = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.rank = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.events = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
age: new fields.NumberField({ required: false, integer: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class TalentData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.subType.initial = "skill";
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
schema.level = new fields.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
schema.skill = new fields.SchemaField({
|
||||
speciality: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
reduceEncumbrance: new fields.BooleanField({ required: false, initial: false })
|
||||
});
|
||||
|
||||
schema.psionic = new fields.SchemaField({
|
||||
reach: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
cost: new fields.NumberField({ required: false, initial: 1, min: 0, integer: true }),
|
||||
duration: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
durationUnit: new fields.StringField({ required: false })
|
||||
});
|
||||
|
||||
schema.roll = new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ContactData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.subType.initial = "skill";
|
||||
schema.cost = new fields.NumberField({ required: true, initial: 1, min: 0, integer: true })
|
||||
|
||||
schema.skill = new fields.SchemaField({
|
||||
speciality: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
|
||||
schema.status = new fields.StringField({ required: false, blank: true, trim: true, initial: "Alive" });
|
||||
schema.attitude = new fields.StringField({ required: false, blank: true, trim: true, initial: "Unknow" });
|
||||
schema.relation = new fields.StringField({ required: false, blank: true, trim: true, initial: "Contact" });
|
||||
schema.title = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.nickname = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.species = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.gender = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.pronouns = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.homeworld = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.location = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.occupation = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.notes = new fields.HTMLField({ required: false, blank: true, trim: true });
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class WeaponData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.range = new fields.SchemaField({
|
||||
isMelee: new fields.BooleanField({ required: false, initial: false }),
|
||||
value: new fields.NumberField({ required: false, integer: true, nullable: true }),
|
||||
unit: new fields.StringField({ required: false, blank: true, nullable: true })
|
||||
}),
|
||||
//schema.tons = new fields.NumberField({ required: false, initial: 0, min: 0, integer: false });
|
||||
schema.damage = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.magazine = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.magazineCost = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.traits = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ArmorData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.equipped = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.radiations = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.protection = new fields.StringField({ required: false, blank: false, trim: true });
|
||||
|
||||
// Some armours have a required skill. A Traveller suffers DM-1 to all checks taken in the armour per missing
|
||||
// skill level. For example, a Traveller with Vacc Suit skill 0 who is in a suit that requires Vacc Suit 2 would have
|
||||
// DM-2 to all their checks. Not having the skill at all inflicts the usual DM-3 unskilled penalty instead.
|
||||
schema.requireSkill = new fields.StringField({ required: false, blank: false });
|
||||
schema.requireSkillLevel = new fields.NumberField({ required: false, min: 0, integer: true });
|
||||
|
||||
//requirements: new fields.StringField({ required: false, blank: false, trim: true }),
|
||||
|
||||
// As powered armour, battle dress supports its own weight. While powered and active, the mass of battle dress
|
||||
// does not count against the encumbrance of the wearer and is effectively weightless.
|
||||
schema.powered = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
|
||||
// Characteristics Modifiers (Pirate of Drinax - ASLAN BATTLE DRESS STR/DEX, Slot)
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ComputerData extends PhysicalItemData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.processing = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.processingUsed = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.overload = new fields.BooleanField({ required: false, initial: false });
|
||||
//schema.softwares = new fields.ArrayField(new fields.StringField({ required: false, blank: true, trim: true }));
|
||||
schema.options = new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
);
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class SoftwareData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.bandwidth = new fields.NumberField({ required: false, initial: 0, min: 0, integer: true });
|
||||
schema.inUse = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.computer = new fields.StringField({ required: false, blank: true, nullable: true });
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class SpeciesData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
const schema = {
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields.HTMLField({ required: false, blank: true, trim: true }),
|
||||
traits: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
name: new fields.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
),
|
||||
modifiers: new fields.ArrayField(
|
||||
new fields.SchemaField({
|
||||
characteristic: new fields.StringField({ required: false, blank: true, trim: true }),
|
||||
value: new fields.NumberField({ required: false, integer: true, nullable: true })
|
||||
})
|
||||
)
|
||||
};
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
export class ItemContainerData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.onHand = new fields.BooleanField({ required: false, initial: false });
|
||||
schema.location = new fields.StringField({ required: false, blank: true, trim: true });
|
||||
schema.count = new fields.NumberField({ required: false, initial: 0, integer: true });
|
||||
schema.weight = new fields.NumberField({ required: false, initial: 0, integer: false });
|
||||
schema.weightless = new fields.BooleanField({ required: false, initial: false });
|
||||
|
||||
schema.locked = new fields.BooleanField({ required: false, initial: false }); // GM only
|
||||
schema.lockedDescription = new fields.StringField({ required: false, blank: true, trim: true, nullable: true });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
function createCharacteristicField(show = true, showMax = false) {
|
||||
return new fields.SchemaField({
|
||||
value: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
max: new fields.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
dm: new fields.NumberField({ required: false, initial: 0, integer: true }),
|
||||
show: new fields.BooleanField({ required: false, initial: show }),
|
||||
showMax: new fields.BooleanField({ required: false, initial: showMax })
|
||||
});
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
103
src/module/roll-prompt.js
Normal file
103
src/module/roll-prompt.js
Normal file
@@ -0,0 +1,103 @@
|
||||
class RollPromptDialog extends Dialog {
|
||||
constructor(dialogData = {}, options = {}) {
|
||||
super(dialogData, options);
|
||||
this.options.classes = ["mgt2", game.settings.get("mgt2", "theme"), "sheet", "dialog"];
|
||||
}
|
||||
|
||||
static async create(options) {
|
||||
|
||||
const htmlContent = await renderTemplate('systems/mgt2/templates/roll-prompt.html', {
|
||||
config: CONFIG.MGT2,
|
||||
//formula: formula,
|
||||
characteristics: options.characteristics,
|
||||
characteristic: options.characteristic,
|
||||
skills: options.skills,
|
||||
skill: options.skill,
|
||||
fatigue: options.fatigue,
|
||||
encumbrance: options.encumbrance,
|
||||
difficulty: options.difficulty
|
||||
});
|
||||
|
||||
const results = new Promise(resolve => {
|
||||
new this({
|
||||
title: options.title,
|
||||
content: htmlContent,
|
||||
buttons: {
|
||||
boon: {
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Boon"),
|
||||
callback: (html) => {
|
||||
const formData = new FormDataExtended(html[0].querySelector('form')).object;
|
||||
formData.diceModifier = "dl";
|
||||
resolve(formData);
|
||||
}
|
||||
},
|
||||
submit: {
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Roll"),
|
||||
icon: '<i class="fa-solid fa-dice"></i>',
|
||||
callback: (html) => {
|
||||
const formData = new FormDataExtended(html[0].querySelector('form')).object;
|
||||
resolve(formData);
|
||||
},
|
||||
},
|
||||
bane: {
|
||||
label: game.i18n.localize("MGT2.RollPrompt.Bane"),
|
||||
//icon: '<i class="fa-solid fa-dice"></i>',
|
||||
callback: (html) => {
|
||||
const formData = new FormDataExtended(html[0].querySelector('form')).object;
|
||||
formData.diceModifier = "dh";
|
||||
resolve(formData);
|
||||
}
|
||||
}
|
||||
}
|
||||
//close: () => { resolve(false) }
|
||||
}).render(true);
|
||||
});
|
||||
|
||||
//console.log(Promise.resolve(results));
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
export class RollPromptHelper {
|
||||
|
||||
static async roll(options) {
|
||||
return await RollPromptDialog.create(options);
|
||||
}
|
||||
|
||||
static async promptForFruitTraits() {
|
||||
const htmlContent = await renderTemplate('systems/mgt2/templateschat/chat/roll-prompt.html');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const dialog = new Dialog({
|
||||
title: "Fruit Traits",
|
||||
content: htmlContent,
|
||||
buttons: {
|
||||
submit: {
|
||||
label: "Roll",
|
||||
icon: '<i class="fa-solid fa-dice"></i>',
|
||||
callback: (html) => {
|
||||
const formData = new FormDataExtended(html[0].querySelector('form'))
|
||||
.toObject();
|
||||
|
||||
//verifyFruitInputs(formData);
|
||||
|
||||
resolve(formData);
|
||||
},
|
||||
},
|
||||
skip: {
|
||||
label: "Cancel",
|
||||
callback: () => resolve(null),
|
||||
}
|
||||
},
|
||||
render: (html) => {
|
||||
//html.on('click', 'button[data-preset]', handleFruitPreset);
|
||||
},
|
||||
close: () => {
|
||||
reject('User closed dialog without making a selection.');
|
||||
},
|
||||
});
|
||||
|
||||
dialog.render(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
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
|
||||
});*/
|
||||
};
|
||||
22
src/module/templates.js
Normal file
22
src/module/templates.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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/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"
|
||||
//"systems/mgt2/templates/actors/parts/actor-characteristic.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
|
||||
&.window-app
|
||||
.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
|
||||
Reference in New Issue
Block a user