Migrate to FoundryVTT v13 AppV2 +

│ DataModels
│
│ - Reorganize DataModels into src/module/models/ (one .mjs per type)
│ - Create AppV2 actor/item sheets (HandlebarsApplicationMixin)…
This commit is contained in:
2026-04-19 10:54:43 +02:00
parent e3002dd602
commit 86b2cd5777
30 changed files with 445 additions and 1679 deletions

68
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,68 @@
# Copilot Instructions for foundryvtt-mgt2
## Project Overview
This is a [Foundry VTT](https://foundryvtt.com) game system implementing Mongoose Publishing Traveller 2nd Edition (MGT2). It runs entirely in the browser within the FoundryVTT platform — there is no Node.js runtime or backend server to develop against.
The distributed entry point is `mgt2.bundle.js` (pre-bundled from `src/`). CSS is compiled from `src/sass/` to `styles/mgt2.min.css`. No build tooling (package.json, webpack, etc.) is committed to the repo, so bundling is done externally.
## Architecture
### Entry Point & Initialization
`src/module/core.js` is the main module. It runs in the FoundryVTT `init` hook and:
- Registers all Actor/Item document classes
- Registers all TypeDataModel schemas (`datamodels.js`)
- Registers sheets (actor and item)
- Preloads Handlebars templates
### Document Classes
| Class | File | Purpose |
|---|---|---|
| `TravellerActor` | `actors/actor.js` | Extends `Actor`; delegates character logic to `ActorCharacter` |
| `ActorCharacter` | `actors/character.js` | Static methods for character-specific actor logic |
| `TravellerItem` | `item.js` | Extends `Item` |
| `MGT2Combatant` | `actors/actor.js` | Extends `Combatant` |
### Sheets
- **`TravellerActorSheet`** (`actors/character-sheet.js`) — character sheet. Uses `templates/actors/actor-sheet.html`. `getData()` returns `context.data` (not the full context).
- **`TravellerItemSheet`** (`item-sheet.js`) — item sheet. Template is dynamically resolved as `templates/items/{item.type}-sheet.html`.
- **`VehiculeSheet`** (`actors/vehicule-sheet.js`) — vehicle actor sheet.
### Data Models
All schemas are TypeDataModels defined in `src/module/datamodels.js`. Actor types: `character`, `vehicule`. Item types: `contact`, `career`, `disease`, `item`, `equipment`, `talent`, `armor`, `weapon`, `computer`, `container`, `species`.
The `CharacterData` model uses a helper `createCharacteristicField()` for the 12 characteristics (strength, dexterity, endurance, intellect, education, social, morale, luck, sanity, charm, psionic, other). Each characteristic has `.value`, `.max`, and `.dm` fields.
### Config Constants
`src/module/config.js` exports `MGT2` — a set of frozen objects mapping internal keys to i18n strings (e.g., `MGT2.Difficulty.Average`). Accessible at runtime via `CONFIG.MGT2`.
### Templates
Handlebars templates live in `templates/`. Paths always use the FoundryVTT-relative prefix `systems/mgt2/templates/...`. Partial templates (used across sheets) live in `templates/items/parts/`.
### Styles & Theming
SASS source is in `src/sass/`. The active theme is a CSS class applied to every sheet element — e.g. `["mgt2", game.settings.get("mgt2", "theme"), "sheet", ...]`. Available themes: `black-and-red`, `mwamba`, `blue`.
### Localization
All user-facing strings go through `game.i18n.localize()`. Keys follow the pattern `MGT2.Section.Key`. String definitions are in `lang/en.json` and `lang/fr.json`.
### Chat Cards
`ChatHelper` (`chatHelper.js`) hooks into `renderChatMessage` to attach button listeners. Chat card templates are in `templates/chat/`.
## Key Conventions
- **`getData()` returns `context.data`** — the character sheet's `getData()` calls `super.getData()` then returns `context.data`, not `context` itself. Keep this pattern for character sheet overrides.
- **Actor logic delegation** — `TravellerActor` is thin; type-specific logic lives in static methods on `ActorCharacter`. Follow this pattern for new actor types.
- **Settings keys** — always namespaced as `game.settings.get("mgt2", "settingName")`. Settings are registered in `settings.js`.
- **`vehicule` spelling** — the actor type and related files use the French spelling `vehicule` (not `vehicle`). Do not rename.
- **Template paths** — always use the full FoundryVTT path `systems/mgt2/templates/...` in JS; never relative paths.
- **i18n keys in config** — `config.js` stores i18n key strings (not translated text). Actual translation happens at render time via `game.i18n.localize()`.
- **`life` is the primary token attribute** — configured in `system.json` as `primaryTokenAttribute: "life"`.

1
.gitignore vendored
View File

@@ -28,3 +28,4 @@
.history/ .history/
mgt2.zip mgt2.zip
assets/regles/

Binary file not shown.

View File

@@ -695,12 +695,11 @@ class ActorCharacter {
} }
if (recalculEncumbrance || recalculWeight) { if (recalculEncumbrance || recalculWeight) {
const cloneActor = foundry.utils.deepClone($this); const updateData = {};
await this.recalculateArmor($this, cloneActor); this.recalculateArmor($this, updateData);
if (recalculEncumbrance) { if (recalculEncumbrance) {
//console.log("recalculEncumbrance");
const str = $this.system.characteristics.strength.value; const str = $this.system.characteristics.strength.value;
const end = $this.system.characteristics.endurance.value; const end = $this.system.characteristics.endurance.value;
let sumSkill = 0; let sumSkill = 0;
@@ -708,19 +707,21 @@ class ActorCharacter {
let normal = str + end + sumSkill; let normal = str + end + sumSkill;
let heavy = normal * 2; let heavy = normal * 2;
cloneActor.system.states.encumbrance = $this.system.inventory.weight > normal; updateData["system.states.encumbrance"] = $this.system.inventory.weight > normal;
cloneActor.system.encumbrance.normal = normal; updateData["system.inventory.encumbrance.normal"] = normal;
cloneActor.system.encumbrance.heavy = heavy; updateData["system.inventory.encumbrance.heavy"] = heavy;
} }
if (recalculWeight) if (recalculWeight)
await this.recalculateWeight($this, cloneActor); await this.recalculateWeight($this, updateData);
else if (Object.keys(updateData).length > 0)
await $this.update(updateData);
} }
} }
static async recalculateArmor($this, cloneActor) { static recalculateArmor($this, updateData) {
if (cloneActor === null || cloneActor === undefined) if (updateData === null || updateData === undefined)
cloneActor = foundry.utils.deepClone($this); updateData = {};
let armor = 0; let armor = 0;
for (let item of $this.items) { for (let item of $this.items) {
@@ -731,18 +732,18 @@ class ActorCharacter {
} }
} }
cloneActor.system.inventory.armor = armor; updateData["system.inventory.armor"] = armor;
return updateData;
} }
static async recalculateWeight($this, cloneActor) { static async recalculateWeight($this, updateData) {
if (cloneActor === null || cloneActor === undefined) if (updateData === null || updateData === undefined)
cloneActor = foundry.utils.deepClone($this); updateData = {};
let updatedContainers = []; let updatedContainers = [];
let containerChanges = {}; let containerChanges = {};
//console.log("recalculWeight");
let containers = []; let containers = [];
// List all containers // List all containers
@@ -791,19 +792,16 @@ class ActorCharacter {
} }
} }
//cloneActor.system.inventory.weight = onHandWeight.toFixed(1);
// Check containers new weight // Check containers new weight
for (let container of containers) { for (let container of containers) {
let newWeight = containerChanges[container._id].weight; let newWeight = containerChanges[container._id].weight;
let newCount = containerChanges[container._id].count; let newCount = containerChanges[container._id].count;
if (container.system.weight !== newWeight || container.system.count !== newCount) { if (container.system.weight !== newWeight || container.system.count !== newCount) {
//const cloneContainer = foundry.utils.deepClone(); updatedContainers.push({
const cloneContainer = foundry.utils.deepClone($this.getEmbeddedDocument("Item", container._id)); _id: container._id,
//foundry.utils.setProperty(cloneContainer, "system.weight", newWeight); "system.weight": newWeight,
cloneContainer.system.weight = newWeight; "system.count": newCount,
cloneContainer.system.count = newCount; });
updatedContainers.push(cloneContainer);
if (container.system.onHand === true && if (container.system.onHand === true &&
(container.system.weight > 0 || container.system.weightless !== true)) { (container.system.weight > 0 || container.system.weightless !== true)) {
@@ -812,11 +810,10 @@ class ActorCharacter {
} }
} }
cloneActor.system.inventory.weight = onHandWeight; updateData["system.inventory.weight"] = onHandWeight;
cloneActor.system.states.encumbrance = onHandWeight > $this.system.inventory.encumbrance.normal; updateData["system.states.encumbrance"] = onHandWeight > $this.system.inventory.encumbrance.normal;
await $this.update(updateData);
await $this.update(cloneActor);
if (updatedContainers.length > 0) { if (updatedContainers.length > 0) {
await $this.updateEmbeddedDocuments('Item', updatedContainers); await $this.updateEmbeddedDocuments('Item', updatedContainers);
@@ -1198,7 +1195,7 @@ class MGT2ActorSheet extends HandlebarsApplicationMixin$1(foundry.applications.s
return this._sheetMode === this.constructor.SHEET_MODES.EDIT; return this._sheetMode === this.constructor.SHEET_MODES.EDIT;
} }
tabGroups = { primary: "stats" } tabGroups = { sidebar: "health" }
/** @override */ /** @override */
async _prepareContext() { async _prepareContext() {
@@ -1226,6 +1223,9 @@ class MGT2ActorSheet extends HandlebarsApplicationMixin$1(foundry.applications.s
/** @override */ /** @override */
_onRender(context, options) { _onRender(context, options) {
super._onRender(context, options); super._onRender(context, options);
// Inject theme class dynamically (can't use game.settings in static DEFAULT_OPTIONS)
const theme = game.settings.get("mgt2", "theme");
if (theme) this.element.classList.add(theme);
this._activateTabGroups(); this._activateTabGroups();
} }
@@ -1493,17 +1493,15 @@ class MGT2Helper {
} }
} }
class RollPromptDialog extends Dialog { const { DialogV2: DialogV2$1 } = foundry.applications.api;
constructor(dialogData = {}, options = {}) { const { renderTemplate: renderTemplate$1 } = foundry.applications.handlebars;
super(dialogData, options); const { FormDataExtended: FormDataExtended$1 } = foundry.applications.ux;
this.options.classes = ["mgt2", game.settings.get("mgt2", "theme"), "sheet", "dialog"];
}
static async create(options) { class RollPromptHelper {
const htmlContent = await renderTemplate('systems/mgt2/templates/roll-prompt.html', { static async roll(options) {
const htmlContent = await renderTemplate$1('systems/mgt2/templates/roll-prompt.html', {
config: CONFIG.MGT2, config: CONFIG.MGT2,
//formula: formula,
characteristics: options.characteristics, characteristics: options.characteristics,
characteristic: options.characteristic, characteristic: options.characteristic,
skills: options.skills, skills: options.skills,
@@ -1513,241 +1511,116 @@ class RollPromptDialog extends Dialog {
difficulty: options.difficulty difficulty: options.difficulty
}); });
const results = new Promise(resolve => { game.settings.get("mgt2", "theme");
new this({
title: options.title, return await DialogV2$1.wait({
window: { title: options.title ?? options.rollTypeName ?? game.i18n.localize("MGT2.RollPrompt.Roll") },
content: htmlContent, content: htmlContent,
buttons: { rejectClose: false,
boon: { buttons: [
{
action: "boon",
label: game.i18n.localize("MGT2.RollPrompt.Boon"), label: game.i18n.localize("MGT2.RollPrompt.Boon"),
callback: (html) => { callback: (event, button, dialog) => {
const formData = new FormDataExtended(html[0].querySelector('form')).object; const formData = new FormDataExtended$1(dialog.element.querySelector('form')).object;
formData.diceModifier = "dl"; formData.diceModifier = "dl";
resolve(formData); return formData;
} }
}, },
submit: { {
action: "submit",
label: game.i18n.localize("MGT2.RollPrompt.Roll"), label: game.i18n.localize("MGT2.RollPrompt.Roll"),
icon: '<i class="fa-solid fa-dice"></i>', icon: '<i class="fa-solid fa-dice"></i>',
callback: (html) => { default: true,
const formData = new FormDataExtended(html[0].querySelector('form')).object; callback: (event, button, dialog) => {
resolve(formData); return new FormDataExtended$1(dialog.element.querySelector('form')).object;
}
}, },
}, {
bane: { action: "bane",
label: game.i18n.localize("MGT2.RollPrompt.Bane"), label: game.i18n.localize("MGT2.RollPrompt.Bane"),
//icon: '<i class="fa-solid fa-dice"></i>', callback: (event, button, dialog) => {
callback: (html) => { const formData = new FormDataExtended$1(dialog.element.querySelector('form')).object;
const formData = new FormDataExtended(html[0].querySelector('form')).object;
formData.diceModifier = "dh"; formData.diceModifier = "dh";
resolve(formData); return formData;
} }
} }
} ]
//close: () => { resolve(false) }
}).render(true);
}); });
//console.log(Promise.resolve(results));
return results;
} }
} }
class RollPromptHelper { const { DialogV2 } = foundry.applications.api;
const { renderTemplate } = foundry.applications.handlebars;
const { FormDataExtended } = foundry.applications.ux;
static async roll(options) { async function _dialogWithForm(title, templatePath, templateData) {
return await RollPromptDialog.create(options); const htmlContent = await renderTemplate(templatePath, templateData);
} game.settings.get("mgt2", "theme");
return await DialogV2.wait({
static async promptForFruitTraits() { window: { title },
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, content: htmlContent,
buttons: { rejectClose: false,
submit: { buttons: [
label: "Roll", {
icon: '<i class="fa-solid fa-dice"></i>', action: "submit",
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);
});
}
}
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"), label: game.i18n.localize("MGT2.Save"),
icon: '<i class="fa-solid fa-floppy-disk"></i>', icon: '<i class="fa-solid fa-floppy-disk"></i>',
callback: (html) => { default: true,
const formData = new FormDataExtended(html[0].querySelector('form')).object; callback: (event, button, dialog) => {
resolve(formData); return new FormDataExtended(dialog.element.querySelector('form')).object;
},
} }
} }
}).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;
}
} }
class CharacterPrompts { class CharacterPrompts {
static async openConfig(system) { static async openConfig(system) {
return await ActorConfigDialog.create(system); return _dialogWithForm(
"Configuration",
"systems/mgt2/templates/actors/actor-config-sheet.html",
{ config: CONFIG.MGT2, system }
);
} }
static async openCharacteristic(name, hide, showMax, showAll = false) { static async openCharacteristic(name, show, showMax, showAll = false) {
return await ActorCharacteristicDialog.create(name, hide, showMax, showAll); return _dialogWithForm(
"Configuration: " + name,
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
{ name, show, showMax, showAll }
);
} }
static async openTraitEdit(data) { static async openTraitEdit(data) {
return await TraitEditDialog.create(data); const title = data.name ?? game.i18n.localize("MGT2.Actor.EditTrait");
return _dialogWithForm(
title,
"systems/mgt2/templates/actors/trait-sheet.html",
{ config: CONFIG.MGT2, data }
);
} }
static async openEditorFullView(title, html) { static async openEditorFullView(title, html) {
return await EditorFullViewDialog.create(title, html); const htmlContent = await renderTemplate("systems/mgt2/templates/editor-fullview.html", {
config: CONFIG.MGT2,
html
});
game.settings.get("mgt2", "theme");
await DialogV2.wait({
window: { title },
content: htmlContent,
rejectClose: false,
buttons: [
{
action: "close",
label: game.i18n.localize("MGT2.Close") || "Fermer",
default: true,
callback: () => null
}
]
});
} }
} }
@@ -1792,7 +1665,7 @@ class TravellerCharacterSheet extends MGT2ActorSheet {
/** @override */ /** @override */
tabGroups = { tabGroups = {
primary: "inventory", sidebar: "inventory",
characteristics: "core", characteristics: "core",
inventory: "onhand", inventory: "onhand",
} }
@@ -2468,7 +2341,7 @@ class TravellerCharacterSheet extends MGT2ActorSheet {
return; return;
} }
let roll = await new Roll(rollFormula, this.actor.getRollData()).roll({ async: true, rollMode: userRollData.rollMode }); let roll = await new Roll(rollFormula, this.actor.getRollData()).roll({ rollMode: userRollData.rollMode });
if (isInitiative && this.token?.combatant) { if (isInitiative && this.token?.combatant) {
await this.token.combatant.update({ initiative: roll.total }); await this.token.combatant.update({ initiative: roll.total });
@@ -2480,7 +2353,6 @@ class TravellerCharacterSheet extends MGT2ActorSheet {
formula: roll._formula, formula: roll._formula,
tooltip: await roll.getTooltip(), tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100, total: Math.round(roll.total * 100) / 100,
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
showButtons: true, showButtons: true,
showLifeButtons: false, showLifeButtons: false,
showRollRequest: false, showRollRequest: false,
@@ -2500,7 +2372,7 @@ class TravellerCharacterSheet extends MGT2ActorSheet {
chatData.rollFailure = true; chatData.rollFailure = true;
} }
const html = await renderTemplate("systems/mgt2/templates/chat/roll.html", chatData); const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html; chatData.content = html;
let flags = null; let flags = null;
@@ -2640,12 +2512,14 @@ class TravellerItemSheet extends HandlebarsApplicationMixin(foundry.applications
}, },
} }
/** @override */ /** Dynamic PARTS: template resolved per item type */
static PARTS = { get PARTS() {
const type = this.document?.type ?? "item";
return {
sheet: { sheet: {
// template is dynamic — resolved in _prepareContext / _renderHTML template: `systems/mgt2/templates/items/${type}-sheet.html`,
template: "",
}, },
};
} }
/** Resolve template dynamically based on item type */ /** Resolve template dynamically based on item type */
@@ -2699,7 +2573,7 @@ class TravellerItemSheet extends HandlebarsApplicationMixin(foundry.applications
systemFields: item.system.schema.fields, systemFields: item.system.schema.fields,
isEditable: this.isEditable, isEditable: this.isEditable,
isGM: game.user.isGM, isGM: game.user.isGM,
config: CONFIG, config: CONFIG.MGT2,
settings: settings, settings: settings,
containers: containers, containers: containers,
computers: computers, computers: computers,
@@ -2713,13 +2587,16 @@ class TravellerItemSheet extends HandlebarsApplicationMixin(foundry.applications
/** @override — resolve the per-type template before rendering */ /** @override — resolve the per-type template before rendering */
async _renderHTML(context, options) { async _renderHTML(context, options) {
const templatePath = `systems/mgt2/templates/items/${this.document.type}-sheet.html`; const templatePath = `systems/mgt2/templates/items/${this.document.type}-sheet.html`;
const html = await renderTemplate(templatePath, context); const html = await foundry.applications.handlebars.renderTemplate(templatePath, context);
return { sheet: html }; return { sheet: html };
} }
/** @override — put rendered HTML into the window content */ /** @override — put rendered HTML into the window content */
_replaceHTML(result, content, options) { _replaceHTML(result, content, options) {
content.innerHTML = result.sheet; content.innerHTML = result.sheet;
// Inject theme class dynamically (can't use game.settings in static DEFAULT_OPTIONS)
const theme = game.settings.get("mgt2", "theme");
if (theme) this.element.classList.add(theme);
this._activateTabGroups(); this._activateTabGroups();
this._bindItemEvents(); this._bindItemEvents();
} }
@@ -2878,6 +2755,17 @@ class TravellerItemSheet extends HandlebarsApplicationMixin(foundry.applications
const preloadHandlebarsTemplates = async function() { const preloadHandlebarsTemplates = async function() {
const templatePaths = [ const templatePaths = [
"systems/mgt2/templates/items/armor-sheet.html",
"systems/mgt2/templates/items/career-sheet.html",
"systems/mgt2/templates/items/computer-sheet.html",
"systems/mgt2/templates/items/contact-sheet.html",
"systems/mgt2/templates/items/container-sheet.html",
"systems/mgt2/templates/items/disease-sheet.html",
"systems/mgt2/templates/items/equipment-sheet.html",
"systems/mgt2/templates/items/item-sheet.html",
"systems/mgt2/templates/items/species-sheet.html",
"systems/mgt2/templates/items/talent-sheet.html",
"systems/mgt2/templates/items/weapon-sheet.html",
"systems/mgt2/templates/items/parts/sheet-configuration.html", "systems/mgt2/templates/items/parts/sheet-configuration.html",
"systems/mgt2/templates/items/parts/sheet-physical-item.html", "systems/mgt2/templates/items/parts/sheet-physical-item.html",
"systems/mgt2/templates/roll-prompt.html", "systems/mgt2/templates/roll-prompt.html",
@@ -2887,7 +2775,6 @@ const preloadHandlebarsTemplates = async function() {
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html", "systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
"systems/mgt2/templates/actors/trait-sheet.html", "systems/mgt2/templates/actors/trait-sheet.html",
"systems/mgt2/templates/editor-fullview.html" "systems/mgt2/templates/editor-fullview.html"
//"systems/mgt2/templates/actors/parts/actor-characteristic.html"
]; ];
return loadTemplates(templatePaths); return loadTemplates(templatePaths);
@@ -2895,43 +2782,33 @@ const preloadHandlebarsTemplates = async function() {
class ChatHelper { class ChatHelper {
static setupCardListeners(message, element, messageData) {
// _injectContent(message, type, html) { if (!message || !element) {
// _setupCardListeners(message, html);
// }
static setupCardListeners(message, html, messageData) {
if (!message || !html) {
return; return;
} }
// if (SettingsUtility.getSettingValue(SETTING_NAMES.MANUAL_DAMAGE_MODE) > 0) { element.querySelectorAll('button[data-action="rollDamage"]').forEach(el => {
// html.find('.card-buttons').find(`[data-action='rsr-${ROLL_TYPE.DAMAGE}']`).click(async event => { el.addEventListener('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); await this._processRollDamageButtonEvent(message, event);
}); });
});
html.find('button[data-action="damage"]').click(async event => { element.querySelectorAll('button[data-action="damage"]').forEach(el => {
//ui.notifications.warn("damage"); el.addEventListener('click', async event => {
await this._applyChatCardDamage(message, event); await this._applyChatCardDamage(message, event);
//await _processApplyButtonEvent(message, event); });
}); });
html.find('button[data-action="healing"]').click(async event => { element.querySelectorAll('button[data-action="healing"]').forEach(el => {
el.addEventListener('click', async event => {
ui.notifications.warn("healing"); ui.notifications.warn("healing");
//await _processApplyTotalButtonEvent(message, event); });
}); });
html.find('button[data-index]').click(async event => { element.querySelectorAll('button[data-index]').forEach(el => {
el.addEventListener('click', async event => {
await this._processRollButtonEvent(message, event); await this._processRollButtonEvent(message, event);
}); });
});
} }
static async _processRollButtonEvent(message, event) { static async _processRollButtonEvent(message, event) {
@@ -2940,8 +2817,7 @@ class ChatHelper {
let buttons = message.flags.mgt2.buttons; let buttons = message.flags.mgt2.buttons;
const index = event.target.dataset.index; const index = event.target.dataset.index;
const button = buttons[index]; const button = buttons[index];
let roll = await new Roll(button.formula, {}).roll({ async: true }); let roll = await new Roll(button.formula, {}).roll();
//console.log(message);
const chatData = { const chatData = {
user: game.user.id, user: game.user.id,
@@ -2949,15 +2825,11 @@ class ChatHelper {
formula: roll._formula, formula: roll._formula,
tooltip: await roll.getTooltip(), tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100, 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, rollObjectName: button.message.objectName,
rollMessage: MGT2Helper.format(button.message.flavor, Math.round(roll.total * 100) / 100), rollMessage: MGT2Helper.format(button.message.flavor, Math.round(roll.total * 100) / 100),
}; };
const html = await renderTemplate("systems/mgt2/templates/chat/roll.html", chatData); const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html; chatData.content = html;
return roll.toMessage(chatData); return roll.toMessage(chatData);
} }
@@ -2967,7 +2839,7 @@ class ChatHelper {
event.stopPropagation(); event.stopPropagation();
let rollFormula = message.flags.mgt2.damage.formula; let rollFormula = message.flags.mgt2.damage.formula;
let roll = await new Roll(rollFormula, {}).roll({ async: true }); let roll = await new Roll(rollFormula, {}).roll();
let speaker; let speaker;
let selectTokens = canvas.tokens.controlled; let selectTokens = canvas.tokens.controlled;
@@ -2985,30 +2857,18 @@ class ChatHelper {
formula: roll._formula, formula: roll._formula,
tooltip: await roll.getTooltip(), tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100, total: Math.round(roll.total * 100) / 100,
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
showButtons: true, showButtons: true,
hasDamage: true, hasDamage: true,
rollTypeName: rollTypeName, rollTypeName: rollTypeName,
rollObjectName: message.flags.mgt2.damage.rollObjectName rollObjectName: message.flags.mgt2.damage.rollObjectName
}; };
const html = await renderTemplate("systems/mgt2/templates/chat/roll.html", chatData); const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html; chatData.content = html;
return roll.toMessage(chatData); 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) { static _applyChatCardDamage(message, event) {
const roll = message.rolls[0]; const roll = message.rolls[0];
return Promise.all(canvas.tokens.controlled.map(t => { return Promise.all(canvas.tokens.controlled.map(t => {
@@ -3193,8 +3053,8 @@ Hooks.once("init", async function () {
}); });
Hooks.on("renderChatMessage", (message, html, messageData) => { Hooks.on("renderChatMessageHTML", (message, element, messageData) => {
ChatHelper.setupCardListeners(message, html, messageData); ChatHelper.setupCardListeners(message, element, messageData);
}); });
// Preload template partials // Preload template partials

File diff suppressed because one or more lines are too long

View File

@@ -1,153 +1,73 @@
class EditorFullViewDialog extends Dialog { const { DialogV2 } = foundry.applications.api;
constructor(dialogData = {}, options = {}) { const { renderTemplate } = foundry.applications.handlebars;
super(dialogData, options); const { FormDataExtended } = foundry.applications.ux;
this.options.classes = ["mgt2", game.settings.get("mgt2", "theme"), "sheet"];
this.options.resizable = true;
}
static async create(title, html) { async function _dialogWithForm(title, templatePath, templateData) {
const htmlContent = await renderTemplate("systems/mgt2/templates/editor-fullview.html", { const htmlContent = await renderTemplate(templatePath, templateData);
config: CONFIG.MGT2, const theme = game.settings.get("mgt2", "theme");
html: html return await DialogV2.wait({
}); window: { title },
const results = new Promise(resolve => {
new this({
title: title,
content: htmlContent, content: htmlContent,
buttons: { rejectClose: false,
//close: { label: game.i18n.localize("MGT2.Close") } buttons: [
} {
}).render(true); action: "submit",
});
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"), label: game.i18n.localize("MGT2.Save"),
icon: '<i class="fa-solid fa-floppy-disk"></i>', icon: '<i class="fa-solid fa-floppy-disk"></i>',
callback: (html) => { default: true,
const formData = new FormDataExtended(html[0].querySelector('form')).object; callback: (event, button, dialog) => {
resolve(formData); return new FormDataExtended(dialog.element.querySelector('form')).object;
},
} }
} }
}).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 { export class CharacterPrompts {
static async openConfig(system) { static async openConfig(system) {
return await ActorConfigDialog.create(system); return _dialogWithForm(
"Configuration",
"systems/mgt2/templates/actors/actor-config-sheet.html",
{ config: CONFIG.MGT2, system }
);
} }
static async openCharacteristic(name, hide, showMax, showAll = false) { static async openCharacteristic(name, show, showMax, showAll = false) {
return await ActorCharacteristicDialog.create(name, hide, showMax, showAll); return _dialogWithForm(
"Configuration: " + name,
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
{ name, show, showMax, showAll }
);
} }
static async openTraitEdit(data) { static async openTraitEdit(data) {
return await TraitEditDialog.create(data); const title = data.name ?? game.i18n.localize("MGT2.Actor.EditTrait");
return _dialogWithForm(
title,
"systems/mgt2/templates/actors/trait-sheet.html",
{ config: CONFIG.MGT2, data }
);
} }
static async openEditorFullView(title, html) { static async openEditorFullView(title, html) {
return await EditorFullViewDialog.create(title, html); const htmlContent = await renderTemplate("systems/mgt2/templates/editor-fullview.html", {
config: CONFIG.MGT2,
html
});
const theme = game.settings.get("mgt2", "theme");
await DialogV2.wait({
window: { title },
content: htmlContent,
rejectClose: false,
buttons: [
{
action: "close",
label: game.i18n.localize("MGT2.Close") || "Fermer",
default: true,
callback: () => null
}
]
});
} }
} }

View File

@@ -138,12 +138,11 @@ export class ActorCharacter {
} }
if (recalculEncumbrance || recalculWeight) { if (recalculEncumbrance || recalculWeight) {
const cloneActor = foundry.utils.deepClone($this); const updateData = {};
await this.recalculateArmor($this, cloneActor); this.recalculateArmor($this, updateData);
if (recalculEncumbrance) { if (recalculEncumbrance) {
//console.log("recalculEncumbrance");
const str = $this.system.characteristics.strength.value; const str = $this.system.characteristics.strength.value;
const end = $this.system.characteristics.endurance.value; const end = $this.system.characteristics.endurance.value;
let sumSkill = 0; let sumSkill = 0;
@@ -151,19 +150,21 @@ export class ActorCharacter {
let normal = str + end + sumSkill; let normal = str + end + sumSkill;
let heavy = normal * 2; let heavy = normal * 2;
cloneActor.system.states.encumbrance = $this.system.inventory.weight > normal; updateData["system.states.encumbrance"] = $this.system.inventory.weight > normal;
cloneActor.system.encumbrance.normal = normal; updateData["system.inventory.encumbrance.normal"] = normal;
cloneActor.system.encumbrance.heavy = heavy; updateData["system.inventory.encumbrance.heavy"] = heavy;
} }
if (recalculWeight) if (recalculWeight)
await this.recalculateWeight($this, cloneActor); await this.recalculateWeight($this, updateData);
else if (Object.keys(updateData).length > 0)
await $this.update(updateData);
} }
} }
static async recalculateArmor($this, cloneActor) { static recalculateArmor($this, updateData) {
if (cloneActor === null || cloneActor === undefined) if (updateData === null || updateData === undefined)
cloneActor = foundry.utils.deepClone($this); updateData = {};
let armor = 0; let armor = 0;
for (let item of $this.items) { for (let item of $this.items) {
@@ -174,18 +175,18 @@ export class ActorCharacter {
} }
} }
cloneActor.system.inventory.armor = armor; updateData["system.inventory.armor"] = armor;
return updateData;
} }
static async recalculateWeight($this, cloneActor) { static async recalculateWeight($this, updateData) {
if (cloneActor === null || cloneActor === undefined) if (updateData === null || updateData === undefined)
cloneActor = foundry.utils.deepClone($this); updateData = {};
let updatedContainers = []; let updatedContainers = [];
let containerChanges = {}; let containerChanges = {};
//console.log("recalculWeight");
let containers = []; let containers = [];
// List all containers // List all containers
@@ -205,7 +206,7 @@ export class ActorCharacter {
let itemWeight = 0; let itemWeight = 0;
if (item.system.hasOwnProperty("weight")) { if (item.system.hasOwnProperty("weight")) {
let itemQty = item.system.quantity let itemQty = item.system.quantity;
if (!isNaN(itemQty) && itemQty > 0) { if (!isNaN(itemQty) && itemQty > 0) {
itemWeight = item.system.weight; itemWeight = item.system.weight;
if (itemWeight > 0) { if (itemWeight > 0) {
@@ -234,19 +235,16 @@ export class ActorCharacter {
} }
} }
//cloneActor.system.inventory.weight = onHandWeight.toFixed(1);
// Check containers new weight // Check containers new weight
for (let container of containers) { for (let container of containers) {
let newWeight = containerChanges[container._id].weight; let newWeight = containerChanges[container._id].weight;
let newCount = containerChanges[container._id].count; let newCount = containerChanges[container._id].count;
if (container.system.weight !== newWeight || container.system.count !== newCount) { if (container.system.weight !== newWeight || container.system.count !== newCount) {
//const cloneContainer = foundry.utils.deepClone(); updatedContainers.push({
const cloneContainer = foundry.utils.deepClone($this.getEmbeddedDocument("Item", container._id)); _id: container._id,
//foundry.utils.setProperty(cloneContainer, "system.weight", newWeight); "system.weight": newWeight,
cloneContainer.system.weight = newWeight; "system.count": newCount,
cloneContainer.system.count = newCount; });
updatedContainers.push(cloneContainer);
if (container.system.onHand === true && if (container.system.onHand === true &&
(container.system.weight > 0 || container.system.weightless !== true)) { (container.system.weight > 0 || container.system.weightless !== true)) {
@@ -255,11 +253,10 @@ export class ActorCharacter {
} }
} }
cloneActor.system.inventory.weight = onHandWeight; updateData["system.inventory.weight"] = onHandWeight;
cloneActor.system.states.encumbrance = onHandWeight > $this.system.inventory.encumbrance.normal; updateData["system.states.encumbrance"] = onHandWeight > $this.system.inventory.encumbrance.normal;
await $this.update(updateData);
await $this.update(cloneActor);
if (updatedContainers.length > 0) { if (updatedContainers.length > 0) {
await $this.updateEmbeddedDocuments('Item', updatedContainers); await $this.updateEmbeddedDocuments('Item', updatedContainers);

View File

@@ -38,7 +38,7 @@ export default class MGT2ActorSheet extends HandlebarsApplicationMixin(foundry.a
return this._sheetMode === this.constructor.SHEET_MODES.EDIT; return this._sheetMode === this.constructor.SHEET_MODES.EDIT;
} }
tabGroups = { primary: "stats" } tabGroups = { sidebar: "health" }
/** @override */ /** @override */
async _prepareContext() { async _prepareContext() {
@@ -66,6 +66,9 @@ export default class MGT2ActorSheet extends HandlebarsApplicationMixin(foundry.a
/** @override */ /** @override */
_onRender(context, options) { _onRender(context, options) {
super._onRender(context, options); super._onRender(context, options);
// Inject theme class dynamically (can't use game.settings in static DEFAULT_OPTIONS)
const theme = game.settings.get("mgt2", "theme");
if (theme) this.element.classList.add(theme);
this._activateTabGroups(); this._activateTabGroups();
} }

View File

@@ -45,7 +45,7 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
/** @override */ /** @override */
tabGroups = { tabGroups = {
primary: "inventory", sidebar: "inventory",
characteristics: "core", characteristics: "core",
inventory: "onhand", inventory: "onhand",
} }
@@ -721,7 +721,7 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
return; return;
} }
let roll = await new Roll(rollFormula, this.actor.getRollData()).roll({ async: true, rollMode: userRollData.rollMode }); let roll = await new Roll(rollFormula, this.actor.getRollData()).roll({ rollMode: userRollData.rollMode });
if (isInitiative && this.token?.combatant) { if (isInitiative && this.token?.combatant) {
await this.token.combatant.update({ initiative: roll.total }); await this.token.combatant.update({ initiative: roll.total });
@@ -733,7 +733,6 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
formula: roll._formula, formula: roll._formula,
tooltip: await roll.getTooltip(), tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100, total: Math.round(roll.total * 100) / 100,
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
showButtons: true, showButtons: true,
showLifeButtons: false, showLifeButtons: false,
showRollRequest: false, showRollRequest: false,
@@ -753,7 +752,7 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
chatData.rollFailure = true; chatData.rollFailure = true;
} }
const html = await renderTemplate("systems/mgt2/templates/chat/roll.html", chatData); const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html; chatData.content = html;
let flags = null; let flags = null;

View File

@@ -22,12 +22,14 @@ export default class TravellerItemSheet extends HandlebarsApplicationMixin(found
}, },
} }
/** @override */ /** Dynamic PARTS: template resolved per item type */
static PARTS = { get PARTS() {
const type = this.document?.type ?? "item";
return {
sheet: { sheet: {
// template is dynamic — resolved in _prepareContext / _renderHTML template: `systems/mgt2/templates/items/${type}-sheet.html`,
template: "",
}, },
};
} }
/** Resolve template dynamically based on item type */ /** Resolve template dynamically based on item type */
@@ -81,7 +83,7 @@ export default class TravellerItemSheet extends HandlebarsApplicationMixin(found
systemFields: item.system.schema.fields, systemFields: item.system.schema.fields,
isEditable: this.isEditable, isEditable: this.isEditable,
isGM: game.user.isGM, isGM: game.user.isGM,
config: CONFIG, config: CONFIG.MGT2,
settings: settings, settings: settings,
containers: containers, containers: containers,
computers: computers, computers: computers,
@@ -95,13 +97,16 @@ export default class TravellerItemSheet extends HandlebarsApplicationMixin(found
/** @override — resolve the per-type template before rendering */ /** @override — resolve the per-type template before rendering */
async _renderHTML(context, options) { async _renderHTML(context, options) {
const templatePath = `systems/mgt2/templates/items/${this.document.type}-sheet.html`; const templatePath = `systems/mgt2/templates/items/${this.document.type}-sheet.html`;
const html = await renderTemplate(templatePath, context); const html = await foundry.applications.handlebars.renderTemplate(templatePath, context);
return { sheet: html }; return { sheet: html };
} }
/** @override — put rendered HTML into the window content */ /** @override — put rendered HTML into the window content */
_replaceHTML(result, content, options) { _replaceHTML(result, content, options) {
content.innerHTML = result.sheet; content.innerHTML = result.sheet;
// Inject theme class dynamically (can't use game.settings in static DEFAULT_OPTIONS)
const theme = game.settings.get("mgt2", "theme");
if (theme) this.element.classList.add(theme);
this._activateTabGroups(); this._activateTabGroups();
this._bindItemEvents(); this._bindItemEvents();
} }

View File

@@ -2,43 +2,33 @@ import { MGT2Helper } from "./helper.js";
export class ChatHelper { export class ChatHelper {
static setupCardListeners(message, element, messageData) {
// _injectContent(message, type, html) { if (!message || !element) {
// _setupCardListeners(message, html);
// }
static setupCardListeners(message, html, messageData) {
if (!message || !html) {
return; return;
} }
// if (SettingsUtility.getSettingValue(SETTING_NAMES.MANUAL_DAMAGE_MODE) > 0) { element.querySelectorAll('button[data-action="rollDamage"]').forEach(el => {
// html.find('.card-buttons').find(`[data-action='rsr-${ROLL_TYPE.DAMAGE}']`).click(async event => { el.addEventListener('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); await this._processRollDamageButtonEvent(message, event);
}); });
});
html.find('button[data-action="damage"]').click(async event => { element.querySelectorAll('button[data-action="damage"]').forEach(el => {
//ui.notifications.warn("damage"); el.addEventListener('click', async event => {
await this._applyChatCardDamage(message, event); await this._applyChatCardDamage(message, event);
//await _processApplyButtonEvent(message, event); });
}); });
html.find('button[data-action="healing"]').click(async event => { element.querySelectorAll('button[data-action="healing"]').forEach(el => {
el.addEventListener('click', async event => {
ui.notifications.warn("healing"); ui.notifications.warn("healing");
//await _processApplyTotalButtonEvent(message, event); });
}); });
html.find('button[data-index]').click(async event => { element.querySelectorAll('button[data-index]').forEach(el => {
el.addEventListener('click', async event => {
await this._processRollButtonEvent(message, event); await this._processRollButtonEvent(message, event);
}); });
});
} }
static async _processRollButtonEvent(message, event) { static async _processRollButtonEvent(message, event) {
@@ -47,8 +37,7 @@ export class ChatHelper {
let buttons = message.flags.mgt2.buttons; let buttons = message.flags.mgt2.buttons;
const index = event.target.dataset.index; const index = event.target.dataset.index;
const button = buttons[index]; const button = buttons[index];
let roll = await new Roll(button.formula, {}).roll({ async: true }); let roll = await new Roll(button.formula, {}).roll();
//console.log(message);
const chatData = { const chatData = {
user: game.user.id, user: game.user.id,
@@ -56,15 +45,11 @@ export class ChatHelper {
formula: roll._formula, formula: roll._formula,
tooltip: await roll.getTooltip(), tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100, 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, rollObjectName: button.message.objectName,
rollMessage: MGT2Helper.format(button.message.flavor, Math.round(roll.total * 100) / 100), rollMessage: MGT2Helper.format(button.message.flavor, Math.round(roll.total * 100) / 100),
}; };
const html = await renderTemplate("systems/mgt2/templates/chat/roll.html", chatData); const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html; chatData.content = html;
return roll.toMessage(chatData); return roll.toMessage(chatData);
} }
@@ -74,7 +59,7 @@ export class ChatHelper {
event.stopPropagation(); event.stopPropagation();
let rollFormula = message.flags.mgt2.damage.formula; let rollFormula = message.flags.mgt2.damage.formula;
let roll = await new Roll(rollFormula, {}).roll({ async: true }); let roll = await new Roll(rollFormula, {}).roll();
let speaker; let speaker;
let selectTokens = canvas.tokens.controlled; let selectTokens = canvas.tokens.controlled;
@@ -92,30 +77,18 @@ export class ChatHelper {
formula: roll._formula, formula: roll._formula,
tooltip: await roll.getTooltip(), tooltip: await roll.getTooltip(),
total: Math.round(roll.total * 100) / 100, total: Math.round(roll.total * 100) / 100,
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
showButtons: true, showButtons: true,
hasDamage: true, hasDamage: true,
rollTypeName: rollTypeName, rollTypeName: rollTypeName,
rollObjectName: message.flags.mgt2.damage.rollObjectName rollObjectName: message.flags.mgt2.damage.rollObjectName
}; };
const html = await renderTemplate("systems/mgt2/templates/chat/roll.html", chatData); const html = await foundry.applications.handlebars.renderTemplate("systems/mgt2/templates/chat/roll.html", chatData);
chatData.content = html; chatData.content = html;
return roll.toMessage(chatData); 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) { static _applyChatCardDamage(message, event) {
const roll = message.rolls[0]; const roll = message.rolls[0];
return Promise.all(canvas.tokens.controlled.map(t => { return Promise.all(canvas.tokens.controlled.map(t => {

View File

@@ -115,8 +115,8 @@ Hooks.once("init", async function () {
}); });
Hooks.on("renderChatMessage", (message, html, messageData) => { Hooks.on("renderChatMessageHTML", (message, element, messageData) => {
ChatHelper.setupCardListeners(message, html, messageData); ChatHelper.setupCardListeners(message, element, messageData);
}); });
// Preload template partials // Preload template partials

View File

@@ -1,14 +1,12 @@
class RollPromptDialog extends Dialog { const { DialogV2 } = foundry.applications.api;
constructor(dialogData = {}, options = {}) { const { renderTemplate } = foundry.applications.handlebars;
super(dialogData, options); const { FormDataExtended } = foundry.applications.ux;
this.options.classes = ["mgt2", game.settings.get("mgt2", "theme"), "sheet", "dialog"];
}
static async create(options) { export class RollPromptHelper {
static async roll(options) {
const htmlContent = await renderTemplate('systems/mgt2/templates/roll-prompt.html', { const htmlContent = await renderTemplate('systems/mgt2/templates/roll-prompt.html', {
config: CONFIG.MGT2, config: CONFIG.MGT2,
//formula: formula,
characteristics: options.characteristics, characteristics: options.characteristics,
characteristic: options.characteristic, characteristic: options.characteristic,
skills: options.skills, skills: options.skills,
@@ -18,86 +16,41 @@ class RollPromptDialog extends Dialog {
difficulty: options.difficulty difficulty: options.difficulty
}); });
const results = new Promise(resolve => { const theme = game.settings.get("mgt2", "theme");
new this({
title: options.title, return await DialogV2.wait({
window: { title: options.title ?? options.rollTypeName ?? game.i18n.localize("MGT2.RollPrompt.Roll") },
content: htmlContent, content: htmlContent,
buttons: { rejectClose: false,
boon: { buttons: [
{
action: "boon",
label: game.i18n.localize("MGT2.RollPrompt.Boon"), label: game.i18n.localize("MGT2.RollPrompt.Boon"),
callback: (html) => { callback: (event, button, dialog) => {
const formData = new FormDataExtended(html[0].querySelector('form')).object; const formData = new FormDataExtended(dialog.element.querySelector('form')).object;
formData.diceModifier = "dl"; formData.diceModifier = "dl";
resolve(formData); return formData;
} }
}, },
submit: { {
action: "submit",
label: game.i18n.localize("MGT2.RollPrompt.Roll"), label: game.i18n.localize("MGT2.RollPrompt.Roll"),
icon: '<i class="fa-solid fa-dice"></i>', icon: '<i class="fa-solid fa-dice"></i>',
callback: (html) => { default: true,
const formData = new FormDataExtended(html[0].querySelector('form')).object; callback: (event, button, dialog) => {
resolve(formData); return new FormDataExtended(dialog.element.querySelector('form')).object;
}
}, },
}, {
bane: { action: "bane",
label: game.i18n.localize("MGT2.RollPrompt.Bane"), label: game.i18n.localize("MGT2.RollPrompt.Bane"),
//icon: '<i class="fa-solid fa-dice"></i>', callback: (event, button, dialog) => {
callback: (html) => { const formData = new FormDataExtended(dialog.element.querySelector('form')).object;
const formData = new FormDataExtended(html[0].querySelector('form')).object;
formData.diceModifier = "dh"; formData.diceModifier = "dh";
resolve(formData); return 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);
}); });
} }
} }

View File

@@ -6,6 +6,17 @@
export const preloadHandlebarsTemplates = async function() { export const preloadHandlebarsTemplates = async function() {
const templatePaths = [ const templatePaths = [
"systems/mgt2/templates/items/armor-sheet.html",
"systems/mgt2/templates/items/career-sheet.html",
"systems/mgt2/templates/items/computer-sheet.html",
"systems/mgt2/templates/items/contact-sheet.html",
"systems/mgt2/templates/items/container-sheet.html",
"systems/mgt2/templates/items/disease-sheet.html",
"systems/mgt2/templates/items/equipment-sheet.html",
"systems/mgt2/templates/items/item-sheet.html",
"systems/mgt2/templates/items/species-sheet.html",
"systems/mgt2/templates/items/talent-sheet.html",
"systems/mgt2/templates/items/weapon-sheet.html",
"systems/mgt2/templates/items/parts/sheet-configuration.html", "systems/mgt2/templates/items/parts/sheet-configuration.html",
"systems/mgt2/templates/items/parts/sheet-physical-item.html", "systems/mgt2/templates/items/parts/sheet-physical-item.html",
"systems/mgt2/templates/roll-prompt.html", "systems/mgt2/templates/roll-prompt.html",
@@ -15,7 +26,6 @@ export const preloadHandlebarsTemplates = async function() {
"systems/mgt2/templates/actors/actor-config-characteristic-sheet.html", "systems/mgt2/templates/actors/actor-config-characteristic-sheet.html",
"systems/mgt2/templates/actors/trait-sheet.html", "systems/mgt2/templates/actors/trait-sheet.html",
"systems/mgt2/templates/editor-fullview.html" "systems/mgt2/templates/editor-fullview.html"
//"systems/mgt2/templates/actors/parts/actor-characteristic.html"
]; ];
return loadTemplates(templatePaths); return loadTemplates(templatePaths);

View File

@@ -8,7 +8,7 @@
&.window-title &.window-title
font-weight: bold font-weight: bold
text-transform: uppercase text-transform: uppercase
&.window-app &.sheet
.window-content .window-content
background: var(--mgt2-bgcolor-form) background: var(--mgt2-bgcolor-form)
padding: 0 padding: 0

1026
styles/mgt2.min.css vendored

File diff suppressed because one or more lines are too long

1
styles/mgt2.min.css.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["../src/sass/utils/_typography.sass","../src/sass/utils/_colors.sass","../src/sass/utils/_global.sass","../src/sass/utils/_window.sass","../src/sass/utils/_flex.sass","../src/sass/components/_forms.sass","../src/sass/components/_dialog.sass","../src/sass/components/_character.sass","../src/sass/components/_item.sass","../src/sass/components/_chat-sidebar.sass","../src/sass/components/_tabs.sass","../src/sass/components/_tab-sidebar.sass","../src/sass/components/_tables.sass"],"names":[],"mappings":"AAAQ,sMACA,qGACA,0HCGR,eACI,2BACA,0BACA,8BACA,qCACA,gCACA,oCACA,8BACA,sBACA,2BACA,4BACA,iCACA,4BACA,2BACA,8BACA,0BACA,6BACA,kCACA,mCACA,kCAEJ,QACI,2BACA,0BACA,8BACA,qCACA,gCACA,oCACA,8BACA,sBACA,2BACA,4BACA,iCACA,4BACA,2BACA,8BACA,0BACA,6BACA,kCAEJ,MACI,2BACA,0BACA,8BACA,qCACA,gCACA,oCACA,8BACA,sBACA,2BACA,4BACA,iCACA,4BACA,2BACA,8BACA,0BACA,6BACA,kCC9DJ,QACC,yBAED,OACC,UAED,OACC,UAED,OACC,UAED,OACC,UAED,OACC,UAED,MACC,YAED,MACC,WAGA,cACC,iBAED,aACC,WAED,YACC,kBAED,yBACC,eAED,YACC,gBCnCC,iCACC,gCACA,6CAED,4BACC,iBACA,yBAEF,4BACC,oCACA,UAGF,uBACC,UChBD,gBACC,uBACA,yBACD,qBACC,eACD,qBACC,eACD,qBACC,eACD,qBACC,eACD,qBACC,eACD,qBACC,eACD,qBACC,eCfA,qBACC,8BACA,2CACA,gCACA,eACA,kBAEF,0DAGC,aACA,gBAED,sBACC,qBACA,sBACA,uBACA,oBAED,cACC,gCACA,uCACA,eACA,0CACA,iBACA,iBACA,kBACA,iBACA,yBAED,oBACC,aACA,mBACA,iBACA,mBACA,8BAGA,yBACC,yBACA,gBACA,eACA,0CACA,yBAED,kBACC,2CACA,eACD,uBACC,2CACA,eACA,YACA,gBACA,0CACA,oBACA,UACD,4BACC,2CACA,YACA,eACA,gBACA,0CAEF,cACC,aAED,cACC,gBACA,2CACA,YAED,kBACC,kBACA,sBAGA,0BACC,aACA,mBACA,mBACA,gCACC,mBClFH,qBACC,gCACA,wDCFD,4BACC,YAEF,SACC,eACA,uBACA,mBACA,WACC,kBAGD,uBACC,iBACA,YACA,cACA,kBAED,8BACC,gCACA,uCACA,8BACA,kBACA,eACA,iBACA,yBAED,0BACC,aACA,mBACA,mBACA,uBACA,kBAED,6BACC,aACA,mBACA,mBACA,uBACA,iBAIC,oCACC,gBACA,gBAEH,4BACC,0CACA,iBACA,gBACA,kBACA,kBACA,kBAEC,mCACC,WACA,kBACA,OAED,iDACC,aACA,eACA,kBACA,QACA,MAGA,uDACC,cAEJ,4BACC,kCACA,kBACA,iBACA,gBACA,aACA,YACA,sBACA,YACA,aACA,2fAED,yBACC,2BACA,+BACA,kBACA,eACA,aACA,cACA,UACA,aACA,YACA,SACA,kBACA,QACA,wCACA,kBACA,iBACA,+BACC,aACA,gBAGD,sCACC,aACA,gBAEF,qBACC,aACA,mBACA,iBACA,uBACA,uBACA,mBACA,eACA,sBACA,2fACA,2BACC,qBACA,WACA,+BACA,kBACA,iBACA,WACA,YACA,YACA,aACA,iCACC,aACA,gBACD,uCACC,gBACD,sCACC,iBACF,0BACC,iBACA,gBACH,wCAEC,2CAEA,aACC,gBACA,SACA,UACA,gBACC,qBACA,iCACA,8CACA,gBACA,kBACA,kBACC,aACA,8BACC,eAED,wBACC,qBAEL,kBACC,aACA,eACA,iBACA,mBACA,iBACA,YACA,cACA,2BACA,uBACD,sBACC,iBACA,YACA,cACA,kBACD,mBACC,cACA,SACA,UACA,gBACA,kDACA,sBACC,WACA,SACA,UACA,sCACD,yBACC,cACA,YACA,iBACA,0CACA,sBACA,gBACA,sBACA,+BACC,sBACH,uBACC,aACA,sBACA,iBACA,2BACA,8BACC,gBACA,yBACA,iBACA,gCACC,oBACH,gBACC,aACA,mBACA,yBACA,iBACA,qBACC,WAGF,OACC,YACA,aACA,mBACA,uBACA,mBACA,WACC,eACF,kBACC,SACA,UACA,gBACA,aACA,sBACA,2BACA,uBACA,WACA,YACA,qBACC,aACA,SACA,UACA,cACA,8BACA,mBACA,WACA,gBACA,mBAEF,oBACC,gDAED,mBACC,+CACA,iBAED,gBACC,YACA,gBACA,aACA,mBACA,WACA,2BACA,uBACA,0BAED,cACC,SACA,gCACA,6CACA,WACA,cACA,cACA,8BACA,mBACA,eACA,YACA,cACA,aACA,mBCnRD,WACC,aACA,iBACA,mBACD,kBACC,aACA,6CACA,UACA,cACA,mBACA,cACA,wBACC,mBACA,0BACA,gBACA,eACA,mBACA,uCACA,kBACA,yBACF,mBACC,eACA,mBACD,iBACC,aACA,aACA,aAEC,iCACC,YACA,aACH,mCAEC,8BACA,2CACA,cACA,WACA,eCrCD,mCAEC,0BACA,6CACA,kBACA,8BAMD,0BAEC,sBACD,cACC,aACA,uBACA,mBACA,iBACA,cACA,eAEC,uBACC,iBACA,UACA,SACH,WACC,aACA,sBACD,iBACC,mBACA,eACA,8BACA,aACD,gBACC,eACA,yBACA,cACD,kBACC,gBACA,iBACD,cACC,iBACA,iBACA,yBACA,gBACA,kBC5CC,0BACC,gCACA,uCACA,kBACA,gBACA,eACA,iBACA,yBACA,6BACA,mBACA,0CAEC,iCACC,kBACA,cACA,mCACC,mBAGD,wCACC,WACA,kBACA,kCACA,kDACA,oBAEA,+CACC,mBACA,sDACA,uDACA,wDACA,6BACJ,kCACC,gCACA,qBACA,iBACA,mBAGJ,wBACC,yBAED,QACC,iBACA,YACA,aACA,2BACA,oBACD,YACC,gBACA,YACD,SACC,YACA,sBACA,gBACC,wBCvDA,2BACC,cAEF,mCACC,kBACA,UACA,UACA,aACA,sBACA,WACA,yCACC,YACA,kBACA,aACA,oBACA,mBACA,qBACA,uCACA,gCACA,+BACA,eACA,0BACA,cACA,gDACC,iBACA,SACA,uCACA,uDACC,iBACA,6BACF,gDACC,WACA,kBACA,aACA,2CACA,oBACD,2CACC,kBCvCJ,oBACC,qBACA,iBAEC,uCACC,mBAEH,iBACC,aACA,wBACA,WACA,cACD,WACC,aACA,qBACA,WACA,kBACA,uBACA,mBACC,6CACA,mBACA,6BACC,yBACA,eACA,yCACC,iBACA,eACA,mBACA,2CACC,mBAEF,qCACC,6CACA,gCAED,qCACC,0CACA,kCAED,gCACC,iBACA,2CACC,kBAEH,+BACC,uCACH,gBACC,kBACD,UACC,aACA,YACA,eACA,iBACA,mBACA,gCACA,2BACA,uBACA,iBACA,wBACC,sBACA,kBAEC,2CACC,mBACH,YACC,WACD,uBACC,mBAIC,6BACC,2CAGD,6BACC,6CACJ,cACC,gDACA,+BACA,wBACC,eACA,iBACA,oCACC,kBACD,0BACC,iBACH,iBACC,uBACA,kBACD,eACC,qBACD,gBACC,sBACD,wBACC,8BACD,YACC,gBACD,YACC,cACD,aACC,eACD,aACC,gBACD,aACC,gBACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,aACC,eACD,kBACC,eACA,YACA,eACA,gBACA,kBAGC,2BACC,oCAEA,kCACC,6BACJ,iBACC,gBACA,eACA,cACA,qBACA,gCACD,mBACC,aACA,wBACA,OACA,6BACC,cACA,oDACF,8DAEC,gBAGC,qCACC,eAGD,gDACC,kBACH,cACC,kBACA,UACA,eACA,YACA,yBACA,iCACA,yBACA,gBACA,kBACA","file":"mgt2.min.css"}

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} flexcol" autocomplete="off" style="align-content: flex-start;align-items: baseline;overflow: hidden;height: 100%;"> <div class="{{cssClass}} flexcol" style="align-content: flex-start;align-items: baseline;overflow: hidden;height: 100%;">
<nav class="sheet-sidebar tabs" data-group="sidebar"> <nav class="sheet-sidebar tabs" data-group="sidebar">
<!-- <a class="item tab-select" data-tab="personal" title="Personal"><i class="fa-solid fa-id-card"></i></a> --> <!-- <a class="item tab-select" data-tab="personal" title="Personal"><i class="fa-solid fa-id-card"></i></a> -->
<a class="item tab-select" data-tab="health" title="{{ localize 'MGT2.Actor.Health' }}"><i class="fa-solid fa-heart-pulse"></i></a> <a class="item tab-select" data-tab="health" title="{{ localize 'MGT2.Actor.Health' }}"><i class="fa-solid fa-heart-pulse"></i></a>
@@ -745,4 +745,4 @@
<div><a name="config" title="Config" style="margin-right: 0.5rem;"><i class="fa-solid fa-gear"></i></a></div> <div><a name="config" title="Config" style="margin-right: 0.5rem;"><i class="fa-solid fa-gear"></i></a></div>
</section> </section>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="itemsheet-header">
<label>{{localize 'MGT2.TYPES.Item.armor'}}</label> <label>{{localize 'MGT2.TYPES.Item.armor'}}</label>
</div> </div>
@@ -71,4 +71,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Career' }}</label></div> <div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Career' }}</label></div>
<div class="itemsheet-panel"> <div class="itemsheet-panel">
<div class="itemsheet-maincol"> <div class="itemsheet-maincol">
@@ -47,4 +47,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="itemsheet-header">
<label>{{localize 'MGT2.EquipmentSubType.computer'}}</label> <label>{{localize 'MGT2.EquipmentSubType.computer'}}</label>
</div> </div>
@@ -62,4 +62,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Contact' }}</label></div> <div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Contact' }}</label></div>
<div class="itemsheet-panel"> <div class="itemsheet-panel">
<div class="itemsheet-maincol"> <div class="itemsheet-maincol">
@@ -81,4 +81,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Container' }}</label></div> <div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Container' }}</label></div>
<div class="itemsheet-panel"> <div class="itemsheet-panel">
<div class="itemsheet-maincol"> <div class="itemsheet-maincol">
@@ -52,4 +52,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="itemsheet-header">
{{#if (eq system.subType "disease")}} {{#if (eq system.subType "disease")}}
<label>{{localize 'MGT2.DiseaseSubType.disease'}}</label> <label>{{localize 'MGT2.DiseaseSubType.disease'}}</label>
@@ -42,4 +42,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="itemsheet-header">
{{#if (eq system.subType "augment")}} {{#if (eq system.subType "augment")}}
<label>{{localize 'MGT2.EquipmentSubType.augment'}}</label> <label>{{localize 'MGT2.EquipmentSubType.augment'}}</label>
@@ -54,4 +54,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"> <div class="itemsheet-header">
{{#if (eq system.subType "loot")}} {{#if (eq system.subType "loot")}}
<label>{{localize 'MGT2.ItemSubType.loot'}}</label> <label>{{localize 'MGT2.ItemSubType.loot'}}</label>
@@ -57,4 +57,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label>{{ localize 'MGT2.Specie' }}</label></div> <div class="itemsheet-header"><label>{{ localize 'MGT2.Specie' }}</label></div>
<div class="itemsheet-panel"> <div class="itemsheet-panel">
<div class="itemsheet-maincol"> <div class="itemsheet-maincol">
@@ -67,4 +67,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} flexrow itemsheet" autocomplete="off"> <div class="{{cssClass}} flexrow itemsheet">
<div class="itemsheet-header"> <div class="itemsheet-header">
{{#if (eq system.subType "skill")}} {{#if (eq system.subType "skill")}}
<label>{{localize 'MGT2.TalentSubType.skill'}}</label> <label>{{localize 'MGT2.TalentSubType.skill'}}</label>
@@ -76,4 +76,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -1,4 +1,4 @@
<form class="{{cssClass}} itemsheet" autocomplete="off"> <div class="{{cssClass}} itemsheet">
<div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Weapon' }}</label></div> <div class="itemsheet-header"><label class="upcase">{{ localize 'MGT2.Items.Weapon' }}</label></div>
<div class="itemsheet-panel"> <div class="itemsheet-panel">
<div class="itemsheet-maincol"> <div class="itemsheet-maincol">
@@ -86,4 +86,4 @@
</div> </div>
</div> </div>
</div> </div>
</form> </div>

View File

@@ -2,14 +2,14 @@
<div class="form-group"> <div class="form-group">
<label>{{ localize 'MGT2.RollPrompt.CharacteristicDM' }}</label> <label>{{ localize 'MGT2.RollPrompt.CharacteristicDM' }}</label>
<select name="characteristic"> <select name="characteristic">
{{selectOptions characteristics selected=characteristic nameAttr="_id" labelAttr="name"}} {{selectOptions characteristics selected=characteristic valueAttr="_id" labelAttr="name"}}
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>{{ localize 'MGT2.RollPrompt.SkillDM' }}</label> <label>{{ localize 'MGT2.RollPrompt.SkillDM' }}</label>
<select name="skill"> <select name="skill">
<option></option> <option></option>
{{selectOptions skills selected=skill nameAttr="_id" labelAttr="name"}} {{selectOptions skills selected=skill valueAttr="_id" labelAttr="name"}}
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">