Second round de corrections et améliorations

This commit is contained in:
2026-04-19 18:55:34 +02:00
parent 783d4a16e6
commit d62d14c1da
33 changed files with 2225 additions and 390 deletions

View File

@@ -1,4 +1,5 @@
export { default as MGT2ActorSheet } from "./base-actor-sheet.mjs";
export { default as TravellerCharacterSheet } from "./character-sheet.mjs";
export { default as TravellerVehiculeSheet } from "./vehicule-sheet.mjs";
export { default as TravellerCreatureSheet } from "./creature-sheet.mjs";
export { default as TravellerItemSheet } from "./item-sheet.mjs";

View File

@@ -283,7 +283,7 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
const html = this.element;
if (!this.isEditable) return;
this._bindClassEvent(html, ".roll", "click", TravellerCharacterSheet.#onRoll);
this._bindClassEvent(html, "a[data-roll]", "click", TravellerCharacterSheet.#onRoll);
this._bindClassEvent(html, ".cfg-characteristic", "click", TravellerCharacterSheet.#onOpenCharacteristic);
this._bindClassEvent(html, ".item-create", "click", TravellerCharacterSheet.#onCreateItem);
this._bindClassEvent(html, ".item-edit", "click", TravellerCharacterSheet.#onEditItem);
@@ -374,26 +374,22 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
let sourceItem = this.actor.getEmbeddedDocument("Item", sourceItemData.id);
if (sourceItem) {
if (!targetItem) return false;
sourceItem = foundry.utils.deepClone(sourceItem);
if (sourceItem._id === targetId) return false;
if (sourceItem.id === targetId) return false;
if (targetItem.type === "item" || targetItem.type === "equipment") {
if (targetItem.system.subType === "software")
sourceItem.system.software.computerId = targetItem.system.software.computerId;
await sourceItem.update({ "system.software.computerId": targetItem.system.software.computerId });
else
sourceItem.system.container.id = targetItem.system.container.id;
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
await sourceItem.update({ "system.container.id": targetItem.system.container.id });
return true;
} else if (targetItem.type === "computer") {
sourceItem.system.software.computerId = targetId;
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
await sourceItem.update({ "system.software.computerId": targetId });
return true;
} else if (targetItem.type === "container") {
if (targetItem.system.locked && !game.user.isGM) {
ui.notifications.error("Verrouillé");
} else {
sourceItem.system.container.id = targetId;
this.actor.updateEmbeddedDocuments("Item", [sourceItem]);
await sourceItem.update({ "system.container.id": targetId });
return true;
}
}
@@ -466,20 +462,19 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
static async #onEquipItem(event, target) {
event.preventDefault();
const li = target.closest("[data-item-id]");
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId));
const item = this.actor.getEmbeddedDocument("Item", li?.dataset.itemId);
if (!item) return;
item.system.equipped = !item.system.equipped;
this.actor.updateEmbeddedDocuments("Item", [item]);
await item.update({ "system.equipped": !item.system.equipped });
}
static async #onItemStorageIn(event, target) {
event.preventDefault();
const li = target.closest("[data-item-id]");
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId));
const item = this.actor.getEmbeddedDocument("Item", li?.dataset.itemId);
if (!item) return;
if (item.type === "container") {
item.system.onHand = false;
await item.update({ "system.onHand": false });
} else {
const containers = this.actor.getContainers();
let container;
@@ -497,27 +492,24 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
ui.notifications.error("Objet verrouillé");
return;
}
item.system.container.id = container._id;
await item.update({ "system.container.id": container._id });
}
this.actor.updateEmbeddedDocuments("Item", [item]);
}
static async #onItemStorageOut(event, target) {
event.preventDefault();
const li = target.closest("[data-item-id]");
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId));
const item = this.actor.getEmbeddedDocument("Item", li?.dataset.itemId);
if (!item) return;
item.system.container.id = "";
this.actor.updateEmbeddedDocuments("Item", [item]);
await item.update({ "system.container.id": "" });
}
static async #onSoftwareEject(event, target) {
event.preventDefault();
const li = target.closest("[data-item-id]");
const item = foundry.utils.deepClone(this.actor.getEmbeddedDocument("Item", li?.dataset.itemId));
const item = this.actor.getEmbeddedDocument("Item", li?.dataset.itemId);
if (!item) return;
item.system.software.computerId = "";
this.actor.updateEmbeddedDocuments("Item", [item]);
await item.update({ "system.software.computerId": "" });
}
static async #onContainerCreate(event) {
@@ -543,23 +535,19 @@ export default class TravellerCharacterSheet extends MGT2ActorSheet {
);
if (containerItems.length > 0) {
for (let item of containerItems) {
let clone = foundry.utils.deepClone(item);
clone.system.container.id = "";
this.actor.updateEmbeddedDocuments("Item", [clone]);
}
const updates = containerItems.map(item => ({ _id: item.id, "system.container.id": "" }));
await this.actor.updateEmbeddedDocuments("Item", updates);
}
const cloneActor = foundry.utils.deepClone(this.actor);
cloneActor.system.containerView = "";
if (cloneActor.system.containerDropIn === container._id) {
cloneActor.system.containerDropIn = "";
const actorUpdate = { "system.containerView": "" };
if (this.actor.system.containerDropIn === container._id) {
actorUpdate["system.containerDropIn"] = "";
const remaining = containers.filter(x => x._id !== container._id);
if (remaining.length > 0) cloneActor.system.containerDropIn = remaining[0]._id;
if (remaining.length > 0) actorUpdate["system.containerDropIn"] = remaining[0]._id;
}
this.actor.deleteEmbeddedDocuments("Item", [container._id]);
this.actor.update(cloneActor);
await this.actor.deleteEmbeddedDocuments("Item", [container._id]);
await this.actor.update(actorUpdate);
}
static async #onRoll(event, target) {

View File

@@ -0,0 +1,231 @@
import MGT2ActorSheet from "./base-actor-sheet.mjs";
/** Convert Traveller dice notation (e.g. "2D", "4D+2", "3D6") to FoundryVTT formula */
function normalizeDice(formula) {
if (!formula) return "1d6";
return formula
.replace(/(\d*)D(\d*)([+-]\d+)?/gi, (_, count, sides, mod) => {
const n = count || "1";
const d = sides || "6";
return mod ? `${n}d${d}${mod}` : `${n}d${d}`;
});
}
export default class TravellerCreatureSheet extends MGT2ActorSheet {
/** @override */
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: [...super.DEFAULT_OPTIONS.classes, "creature", "nopad"],
position: {
width: 720,
height: 600,
},
window: {
...super.DEFAULT_OPTIONS.window,
title: "TYPES.Actor.creature",
},
actions: {
...super.DEFAULT_OPTIONS.actions,
rollAttack: TravellerCreatureSheet.#onRollAttack,
rollSkill: TravellerCreatureSheet.#onRollSkill,
addSkill: TravellerCreatureSheet.#onAddRow,
deleteSkill: TravellerCreatureSheet.#onDeleteRow,
addAttack: TravellerCreatureSheet.#onAddRow,
deleteAttack: TravellerCreatureSheet.#onDeleteRow,
addTrait: TravellerCreatureSheet.#onAddRow,
deleteTrait: TravellerCreatureSheet.#onDeleteRow,
},
}
/** @override */
static PARTS = {
sheet: {
template: "systems/mgt2/templates/actors/creature-sheet.html",
},
}
/** @override */
tabGroups = { primary: "skills" }
/** @override */
async _prepareContext() {
const context = await super._prepareContext();
const actor = this.document;
context.sizeLabel = this._getSizeLabel(actor.system.life.max);
context.sizeTraitLabel = this._getSizeTrait(actor.system.life.max);
context.config = CONFIG.MGT2;
return context;
}
_getSizeLabel(pdv) {
if (pdv <= 2) return "Souris / Rat";
if (pdv <= 5) return "Chat";
if (pdv <= 7) return "Blaireau / Chien";
if (pdv <= 13) return "Chimpanzé / Chèvre";
if (pdv <= 28) return "Humain";
if (pdv <= 35) return "Vache / Cheval";
if (pdv <= 49) return "Requin";
if (pdv <= 70) return "Rhinocéros";
if (pdv <= 90) return "Éléphant";
if (pdv <= 125) return "Carnosaure";
return "Sauropode / Baleine";
}
_getSizeTrait(pdv) {
if (pdv <= 2) return "Petit (4)";
if (pdv <= 5) return "Petit (3)";
if (pdv <= 7) return "Petit (2)";
if (pdv <= 13) return "Petit (1)";
if (pdv <= 28) return "—";
if (pdv <= 35) return "Grand (+1)";
if (pdv <= 49) return "Grand (+2)";
if (pdv <= 70) return "Grand (+3)";
if (pdv <= 90) return "Grand (+4)";
if (pdv <= 125) return "Grand (+5)";
return "Grand (+6)";
}
// ───────────────────────────────────────────────────────── Roll Handlers
/** Roll an attack (damage) with optional difficulty dialog */
static async #onRollAttack(event, target) {
const index = parseInt(target.dataset.index ?? 0);
const actor = this.document;
const attack = actor.system.attacks[index];
if (!attack) return;
const rollFormula = normalizeDice(attack.damage);
const roll = await new Roll(rollFormula).evaluate();
const total = roll.total;
await roll.toMessage({
speaker: ChatMessage.getSpeaker({ actor }),
flavor: `<strong>${actor.name}</strong> — ${attack.name}`,
rollMode: game.settings.get("core", "rollMode"),
});
}
/** Roll a skill check (2d6 + level vs difficulty) */
static async #onRollSkill(event, target) {
const index = parseInt(target.dataset.index ?? 0);
const actor = this.document;
const skill = actor.system.skills[index];
if (!skill) return;
const htmlContent = await renderTemplate(
"systems/mgt2/templates/actors/creature-roll-prompt.html",
{
skillName: skill.name,
skillLevel: skill.level,
config: CONFIG.MGT2
}
);
const result = await foundry.applications.api.DialogV2.wait({
window: { title: game.i18n.localize("MGT2.Creature.RollSkill") + " — " + skill.name },
content: htmlContent,
rejectClose: false,
buttons: [
{
action: "boon",
label: game.i18n.localize("MGT2.RollPrompt.Boon"),
callback: (event, button, dialog) => {
const fd = new foundry.applications.ux.FormDataExtended(dialog.element.querySelector("form")).object;
fd.diceModifier = "dl";
return fd;
}
},
{
action: "roll",
label: game.i18n.localize("MGT2.RollPrompt.Roll"),
icon: '<i class="fa-solid fa-dice"></i>',
default: true,
callback: (event, button, dialog) =>
new foundry.applications.ux.FormDataExtended(dialog.element.querySelector("form")).object
},
{
action: "bane",
label: game.i18n.localize("MGT2.RollPrompt.Bane"),
callback: (event, button, dialog) => {
const fd = new foundry.applications.ux.FormDataExtended(dialog.element.querySelector("form")).object;
fd.diceModifier = "dh";
return fd;
}
}
]
});
if (!result) return;
const dm = parseInt(result.dm ?? 0) + (skill.level ?? 0);
const modifier = result.diceModifier ?? "";
const difficultyTarget = parseInt(result.difficulty ?? 8);
const difficultyLabel = result.difficultyLabel ?? "";
const diceFormula = modifier ? `3d6${modifier}` : "2d6";
const fullFormula = dm !== 0 ? `${diceFormula} + ${dm}` : diceFormula;
const roll = await new Roll(fullFormula).evaluate();
const success = roll.total >= difficultyTarget;
const chatData = {
creatureName: actor.name,
creatureImg: actor.img,
rollLabel: skill.name.toUpperCase(),
formula: fullFormula,
total: roll.total,
tooltip: await roll.getTooltip(),
difficulty: difficultyTarget,
difficultyLabel,
success,
failure: !success,
modifiers: dm !== 0 ? [`DM ${dm >= 0 ? "+" : ""}${dm}`] : [],
};
const chatContent = await renderTemplate(
"systems/mgt2/templates/chat/creature-roll.html",
chatData
);
await ChatMessage.create({
content: chatContent,
speaker: ChatMessage.getSpeaker({ actor }),
rolls: [roll],
rollMode: game.settings.get("core", "rollMode"),
});
}
// ───────────────────────────────────────────────────────── CRUD Handlers
static async #onAddRow(event, target) {
const prop = target.dataset.prop;
if (!prop) return;
const actor = this.document;
const arr = foundry.utils.deepClone(actor.system[prop] ?? []);
arr.push(this._getDefaultRow(prop));
await actor.update({ [`system.${prop}`]: arr });
}
static async #onDeleteRow(event, target) {
const prop = target.dataset.prop;
const index = parseInt(target.dataset.index);
if (!prop || isNaN(index)) return;
const actor = this.document;
const arr = foundry.utils.deepClone(actor.system[prop] ?? []);
arr.splice(index, 1);
await actor.update({ [`system.${prop}`]: arr });
}
_getDefaultRow(prop) {
switch (prop) {
case "skills": return { name: "", level: 0, note: "" };
case "attacks": return { name: "", damage: "1D", description: "" };
case "traits": return { name: "", value: "", description: "" };
default: return {};
}
}
}

View File

@@ -69,7 +69,7 @@ export class ChatHelper {
speaker = game.user.character;
}
let rollTypeName = message.flags.mgt2.damage.rollTypeName ? message.flags.mgt2.damage.rollTypeName + " DAMAGE" : null;
let rollTypeName = message.flags.mgt2.damage.rollTypeName ? message.flags.mgt2.damage.rollTypeName + " " + game.i18n.localize("MGT2.Actor.Damage") : null;
const chatData = {
user: game.user.id,

View File

@@ -154,4 +154,27 @@ MGT2.Durations = Object.freeze({
Seconds: "MGT2.Durations.Seconds",
Minutes: "MGT2.Durations.Minutes",
Heures: "MGT2.Durations.Heures"
});
MGT2.CreatureBehaviorType = Object.freeze({
herbivore: "MGT2.CreatureBehaviorType.herbivore",
carnivore: "MGT2.CreatureBehaviorType.carnivore",
charognard: "MGT2.CreatureBehaviorType.charognard",
omnivore: "MGT2.CreatureBehaviorType.omnivore"
});
MGT2.CreatureBehaviorSubType = Object.freeze({
accumulateur: "MGT2.CreatureBehaviorSubType.accumulateur",
brouteur: "MGT2.CreatureBehaviorSubType.brouteur",
filtreur: "MGT2.CreatureBehaviorSubType.filtreur",
intermittent: "MGT2.CreatureBehaviorSubType.intermittent",
chasseur: "MGT2.CreatureBehaviorSubType.chasseur",
detourneur: "MGT2.CreatureBehaviorSubType.detourneux",
guetteur: "MGT2.CreatureBehaviorSubType.guetteur",
mangeur: "MGT2.CreatureBehaviorSubType.mangeur",
piegeur: "MGT2.CreatureBehaviorSubType.piegeur",
intimidateur: "MGT2.CreatureBehaviorSubType.intimidateur",
necrophage: "MGT2.CreatureBehaviorSubType.necrophage",
reducteur: "MGT2.CreatureBehaviorSubType.reducteur",
opportuniste: "MGT2.CreatureBehaviorSubType.opportuniste"
});

View File

@@ -1,6 +1,7 @@
import {
CharacterData,
VehiculeData,
CreatureData,
ItemData,
EquipmentData,
DiseaseData,
@@ -17,7 +18,7 @@ import {
import { MGT2 } from "./config.js";
import { TravellerActor, MGT2Combatant } from "./actors/actor.js";
import { TravellerItem } from "./item.js";
import { TravellerItemSheet, TravellerCharacterSheet, TravellerVehiculeSheet } from "./applications/sheets/_module.mjs";
import { TravellerItemSheet, TravellerCharacterSheet, TravellerVehiculeSheet, TravellerCreatureSheet } from "./applications/sheets/_module.mjs";
import { preloadHandlebarsTemplates } from "./templates.js";
//import { MGT2Helper } from "./helper.js";
import {ChatHelper} from "./chatHelper.js";
@@ -73,6 +74,10 @@ Hooks.once("init", async function () {
"characteristics.charm.value",
"characteristics.psionic.value",
"characteristics.other.value"]
},
creature: {
bar: ["life"],
value: ["life.value", "life.max", "speed", "armor", "psi"]
}
};
@@ -91,13 +96,15 @@ Hooks.once("init", async function () {
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet);
foundry.documents.collections.Actors.registerSheet("mgt2", TravellerCharacterSheet, { types: ["character"], makeDefault: true, label: "Traveller Sheet" });
foundry.documents.collections.Actors.registerSheet("mgt2", TravellerVehiculeSheet, { types: ["vehicule"], makeDefault: true, label: "Vehicule Sheet" });
foundry.documents.collections.Actors.registerSheet("mgt2", TravellerCreatureSheet, { types: ["creature"], makeDefault: true, label: "Creature Sheet" });
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet);
foundry.documents.collections.Items.registerSheet("mgt2", TravellerItemSheet, { makeDefault: true });
Object.assign(CONFIG.Actor.dataModels, {
"character": CharacterData,
"vehicule": VehiculeData
"vehicule": VehiculeData,
"creature": CreatureData
});
Object.assign(CONFIG.Item.dataModels, {

View File

@@ -109,18 +109,9 @@ export class MGT2Helper {
}
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;
}
const key = `MGT2.Difficulty.${difficulty}`;
const label = game.i18n.localize(key);
return label !== key ? label : null;
}
static getRangeDisplay(range) {

View File

@@ -0,0 +1,96 @@
const fields = foundry.data.fields;
export default class CreatureData extends foundry.abstract.TypeDataModel {
static defineSchema() {
return {
life: new fields.SchemaField({
value: new fields.NumberField({ required: true, initial: 10, min: 0, integer: true }),
max: new fields.NumberField({ required: true, initial: 10, min: 0, integer: true })
}),
speed: new fields.NumberField({ required: true, initial: 6, min: 0, integer: true }),
armor: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
psi: new fields.NumberField({ required: true, initial: 0, min: 0, integer: true }),
initiativeBonus: new fields.NumberField({ required: true, initial: 0, integer: true }),
skills: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({ required: true, blank: true, trim: true, initial: "" }),
level: new fields.NumberField({ required: true, initial: 0, integer: true }),
note: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
})
),
attacks: new fields.ArrayField(
new fields.SchemaField({
name: new fields.StringField({ required: true, blank: true, trim: true, initial: "" }),
damage: new fields.StringField({ required: true, blank: true, trim: true, initial: "1D" }),
description: 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, initial: "" }),
value: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
description: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
})
),
behavior: new fields.SchemaField({
type: new fields.StringField({ required: false, blank: true, trim: true, initial: "" }),
subtype: new fields.StringField({ required: false, blank: true, trim: true, initial: "" })
}),
biography: new fields.HTMLField({ required: false, blank: true, trim: true }),
notes: new fields.HTMLField({ required: false, blank: true, trim: true }),
};
}
/** @override */
prepareDerivedData() {
// Compute initiative bonus from Métabolisme traits
let bonus = 0;
for (const trait of this.traits) {
const nameLower = trait.name.toLowerCase();
if (nameLower.includes("métabolisme rapide") || nameLower.includes("metabolisme rapide")) {
const val = parseInt(trait.value);
if (!isNaN(val)) bonus += val;
} else if (nameLower.includes("métabolisme lent") || nameLower.includes("metabolisme lent")) {
const val = parseInt(trait.value);
if (!isNaN(val)) bonus -= val;
}
}
this.initiativeBonus = bonus;
// Compute armor from Armure trait if not set manually
if (this.armor === 0) {
for (const trait of this.traits) {
if (trait.name.toLowerCase().startsWith("armure")) {
const val = parseInt(trait.value);
if (!isNaN(val)) {
this.armor = val;
break;
}
}
}
}
// Compute PSI from Psionique trait
if (this.psi === 0) {
for (const trait of this.traits) {
if (trait.name.toLowerCase().startsWith("psionique")) {
const val = parseInt(trait.value);
if (!isNaN(val)) {
this.psi = val;
break;
}
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
// Actor DataModels
export { default as CharacterData } from "./character.mjs";
export { default as VehiculeData } from "./vehicule.mjs";
export { default as CreatureData } from "./creature.mjs";
// Item DataModels
export { default as ItemData } from "./items/item.mjs";

View File

@@ -20,6 +20,7 @@ export class RollPromptHelper {
return await DialogV2.wait({
window: { title: options.title ?? options.rollTypeName ?? game.i18n.localize("MGT2.RollPrompt.Roll") },
classes: ["mgt2-roll-dialog"],
content: htmlContent,
rejectClose: false,
buttons: [

View File

@@ -25,6 +25,9 @@ export const preloadHandlebarsTemplates = async function() {
"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/actors/creature-sheet.html",
"systems/mgt2/templates/actors/creature-roll-prompt.html",
"systems/mgt2/templates/chat/creature-roll.html",
"systems/mgt2/templates/editor-fullview.html"
];

View File

@@ -51,6 +51,7 @@
font-weight: 600
font-style: italic
text-align: center
color: var(--mgt2-color-form)
position: relative
& > a
&.roll
@@ -122,7 +123,7 @@
background-color: transparent
text-align: center
font-size: 1.5rem
width: 2rem
width: 3rem
height: 2rem
border: none
outline: none
@@ -183,13 +184,14 @@ ul
float: left
margin: 0
padding: 0
color: var(--mgt2-color-primary-light)
color: var(--mgt2-color-form)
input
display: block
border: none
font-weight: bold
font-family: "Roboto Condensed", sans-serif
background-color: #fff
color: var(--mgt2-color-form)
font-size: 0.8rem
border: 1px solid #fff
&:hover
@@ -203,6 +205,7 @@ ul
font-size: 0.7rem
text-transform: uppercase
text-wrap: nowrap
color: var(--mgt2-color-form)
i
margin-right: 0.25rem
.character-body
@@ -236,7 +239,7 @@ ul
display: flex
margin: 0
padding: 0
color: #4b4a44
color: var(--mgt2-color-form)
justify-content: space-between
align-items: center
width: 100%

View File

@@ -1,47 +1,331 @@
.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
// MGT2 Chat Roll Cards
// Light theme for ALL chat messages - matching character sheet
li.chat-message
background: #ffffff !important
border: 1px solid #ccbbbb !important
box-shadow: 0 2px 8px rgba(0,0,0,0.18) !important
color: #0A0405
padding: 0 !important
.message-header
background: #0A0405
border-bottom: 3px solid #EE4050
padding: 4px 10px
.message-sender
color: #EE4050
font-family: 'Barlow Condensed', sans-serif
font-weight: 700
font-size: 0.75rem
text-transform: uppercase
letter-spacing: 1.5px
margin: 0
.message-timestamp
color: #888888
font-size: 0.68rem
.message-delete i
color: #888888
&:hover .message-delete i
color: #EE4050
// Plain Foundry roll cards (without .mgt2-chat-roll wrapper)
.message-content > .dice-roll
background: #fdf8f8
padding: 6px 12px
.dice-formula
background: #f5eeee
border: 1px solid #ddc8c8
border-radius: 3px
color: #664444
font-size: 0.78rem
padding: 2px 10px
text-align: center
font-family: 'Barlow Condensed', sans-serif
display: inline-block
margin: 0 auto 4px auto
.dice-result
display: flex
flex-direction: column
align-items: center
gap: 3px
h4.dice-total
font-family: 'Barlow Condensed', sans-serif
font-size: 2.2rem !important
font-weight: 900
color: #0A0405 !important
margin: 2px 0
line-height: 1
text-align: center
background: transparent !important
width: 100% !important
display: block !important
.dice-tooltip
.part-header
background: #f5eeee
padding: 2px 8px
display: flex
justify-content: space-between
.part-formula, .part-total
color: #EE4050
font-weight: 700
font-size: 0.78rem
.dice-rolls
display: flex
flex-wrap: wrap
justify-content: center
gap: 4px
padding: 6px 8px
list-style: none
margin: 0
.roll.die
display: flex
align-items: center
justify-content: center
width: 26px
height: 26px
background: #f5eeee
border: 1.5px solid #ddc8c8
border-radius: 4px
color: #3a2020
font-weight: 700
font-size: 0.9rem
// Inner roll card structure
.mgt2-chat-roll
font-family: 'Barlow Condensed', sans-serif
// ── Header: characteristic name + roll type
.mgt2-roll-header
background: #0A0405 !important
border-left: 4px solid #EE4050 !important
padding: 5px 10px 4px 10px !important
.mgt2-roll-char-name
display: block
color: #ffffff
font-size: 1.2rem
font-weight: 800
text-transform: uppercase
letter-spacing: 1px
line-height: 1
.mgt2-roll-meta
display: flex
align-items: center
gap: 6px
margin-top: 2px
.mgt2-roll-type
color: #EE4050
font-size: 0.68rem
font-weight: 700
text-transform: uppercase
letter-spacing: 2px
.mgt2-roll-sep
color: #888888
.mgt2-roll-difficulty
color: #bbbbbb
font-size: 0.68rem
text-transform: uppercase
letter-spacing: 1px
// Modifier line
.mgt2-roll-modifier
background: #f5eeee
border-bottom: 1px solid #e8dada
padding: 3px 14px
color: #664444
font-size: 0.8rem
.mgt2-roll-modifiers
display: flex
gap: 6px
flex-wrap: wrap
background: #f5eeee
border-bottom: 1px solid #e8dada
padding: 3px 14px
.mgt2-roll-mod-tag
color: #664444
font-size: 0.8rem
// Dice block (Foundry .dice-roll structure preserved for tooltip click)
.dice-roll
background: #fdf8f8
padding: 4px 10px
cursor: pointer
.dice-flavor
color: #888
font-size: 0.73rem
text-align: center
margin-bottom: 3px
.dice-result
display: flex
flex-direction: column
align-items: center
gap: 3px
.dice-formula
background: #f5eeee
border: 1px solid #ddc8c8
border-radius: 3px
color: #664444
font-size: 0.78rem
padding: 2px 14px
font-family: 'DM Sans', sans-serif
text-align: center
// Tooltip (individual dice shown on click by Foundry)
.dice-tooltip
width: 100%
.wrapper
background: transparent
.tooltip-part
background: #fdf8f8
border: 1px solid #e8dada
border-radius: 4px
margin-bottom: 6px
overflow: hidden
.part-header
background: #f5eeee
padding: 3px 8px
.part-formula, .part-total
color: #EE4050
font-weight: 700
font-size: 0.8rem
.dice-rolls
display: flex
flex-wrap: wrap
justify-content: center
gap: 5px
padding: 8px 10px
list-style: none
margin: 0
.roll.die
display: flex
align-items: center
justify-content: center
width: 30px
height: 30px
background: #f5eeee
border: 1.5px solid #ddc8c8
border-radius: 5px
color: #3a2020
font-weight: 700
font-size: 1rem
font-family: 'Barlow Condensed', sans-serif
&.max
border-color: #EE4050
color: #EE4050
background: #fde8ea
box-shadow: 0 0 8px rgba(238,64,80,0.2)
&.min
border-color: #ccbbbb
color: #999999
// ── Total: prominent number
h4.dice-total
font-size: 1.6rem !important
font-weight: 900
color: #0A0405 !important
margin: 3px 0 2px 0
line-height: 1
font-family: 'Barlow Condensed', sans-serif
text-align: center
text-shadow: none
background: #f5eeee !important
border: 1px solid #ddc8c8 !important
border-radius: 4px !important
padding: 2px 14px !important
width: auto !important
display: inline-block !important
min-width: 60px
&.success
color: #1a8840 !important
background: rgba(82,232,122,0.1) !important
border-color: rgba(26,136,64,0.35) !important
&.failure
color: #EE4050 !important
background: rgba(238,64,80,0.07) !important
border-color: rgba(238,64,80,0.3) !important
// Outcome badge
.mgt2-outcome
text-align: center
font-size: 0.75rem
font-weight: 700
text-transform: uppercase
letter-spacing: 2px
padding: 3px 10px
i
margin-right: 5px
&.is-success
background: rgba(26,136,64,0.08)
color: #1a8840
border-top: 1px solid rgba(26,136,64,0.2)
&.is-failure
background: rgba(238,64,80,0.07)
color: #EE4050
border-top: 1px solid rgba(238,64,80,0.2)
// Action buttons
.mgt2-buttons
display: flex
justify-content: center
flex-wrap: wrap
gap: 4px
padding: 5px 10px
background: #f5eeee
border-top: 1px solid #ddc8c8
button
background: #ffffff
border: 1px solid #ccbbbb
color: #3a2020
border-radius: 3px
padding: 4px 14px
font-family: 'Barlow Condensed', sans-serif
font-size: 0.78rem
font-weight: 700
text-transform: uppercase
letter-spacing: 1px
cursor: pointer
transition: background 0.15s ease, box-shadow 0.15s ease
box-shadow: none
i
font-size: 1rem
padding: 0
margin: 0
&:hover
background: #EE4050
border-color: #EE4050
color: #fff
box-shadow: 0 0 8px rgba(238,64,80,0.25)

View File

@@ -0,0 +1,201 @@
//
// Creature Sheet Styles
//
.creature-sheet
// Header
.creature-header
display: flex
flex-direction: row
align-items: flex-start
gap: 0.75rem
padding: 0.5rem 0.75rem 0.5rem
background: var(--mgt2-bgcolor-form)
border-bottom: 2px solid var(--mgt2-color-primary)
.creature-header-img
flex: 0 0 80px
img.profile
width: 80px
height: 80px
object-fit: cover
border: 2px solid var(--mgt2-color-primary)
border-radius: 4px
cursor: pointer
.creature-header-body
flex: 1
display: flex
flex-direction: column
gap: 0.4rem
.creature-name
font-family: "Barlow Condensed", sans-serif
font-size: 1.6rem
font-weight: 700
font-style: italic
color: var(--mgt2-color-form)
background: transparent
border: none
border-bottom: 1px solid var(--mgt2-color-primary)
width: 100%
padding: 0
&:focus
outline: none
border-bottom-color: var(--mgt2-color-secondary)
// Stat boxes
.creature-stats-row
display: flex
flex-direction: row
flex-wrap: wrap
gap: 0.5rem
.creature-stat
display: flex
flex-direction: column
align-items: center
background: var(--mgt2-bgcolor-primary)
border: 1px solid var(--mgt2-color-primary)
border-radius: 4px
padding: 2px 6px
min-width: 4rem
label
font-family: "Barlow Condensed", sans-serif
font-size: 0.7rem
font-weight: 700
text-transform: uppercase
color: var(--mgt2-color-primary)
line-height: 1.2
.creature-stat-value
display: flex
align-items: center
gap: 2px
input
width: 2.5rem
text-align: center
background: transparent
border: none
color: var(--mgt2-bgcolor-form)
font-size: 1rem
font-weight: 700
padding: 0
&:focus
outline: none
border-bottom: 1px solid var(--mgt2-color-primary)
.stat-max
width: 2.5rem
.stat-unit
font-size: 0.7rem
color: var(--mgt2-bgcolor-form)
opacity: 0.7
.stat-readonly
font-size: 1rem
font-weight: 700
color: var(--mgt2-bgcolor-form)
min-width: 2.5rem
text-align: center
// Behavior row
.creature-behavior-row
display: flex
flex-direction: row
align-items: center
gap: 0.5rem
flex-wrap: wrap
label
font-size: 0.75rem
text-transform: uppercase
color: var(--mgt2-color-primary)
font-weight: 700
.behavior-select
background: var(--mgt2-bgcolor-form)
color: var(--mgt2-color-form)
border: 1px solid var(--mgt2-color-primary)
border-radius: 3px
font-size: 0.85rem
padding: 1px 4px
.behavior-sep
color: var(--mgt2-color-form)
opacity: 0.5
.creature-size-badge
margin-left: auto
font-size: 0.75rem
font-style: italic
color: var(--mgt2-color-secondary)
background: var(--mgt2-bgcolor-primary)
border: 1px solid var(--mgt2-color-secondary)
border-radius: 3px
padding: 1px 6px
// Body / Tabs
.creature-body
flex: 1
overflow-y: auto
padding: 0.5rem 0.75rem
.tab
display: none
&.active
display: block
// Info tab
.creature-info-tab
display: flex
flex-direction: column
gap: 0.5rem
.creature-description
width: 100%
background: var(--mgt2-bgcolor-form)
color: var(--mgt2-color-form)
border: 1px solid var(--mgt2-color-primary)
border-radius: 3px
padding: 4px
resize: vertical
font-size: 0.9rem
// Damage formula
.creature-damage
.damage-formula
font-family: "Barlow Condensed", sans-serif
font-weight: 700
color: var(--mgt2-color-primary)
font-size: 1rem
.trait-name
font-weight: 600
color: var(--mgt2-color-form)
.trait-value
font-style: italic
color: var(--mgt2-color-secondary)
//
// Chat card creature skill roll
//
.mgt2-creature-roll
.mgt2-roll-header
display: flex
flex-direction: row
align-items: center
gap: 0.6rem
.creature-chat-img
width: 36px
height: 36px
object-fit: cover
border: 1px solid var(--mgt2-color-primary)
border-radius: 3px
flex: 0 0 36px
.mgt2-roll-header-text
flex: 1

View File

@@ -1,4 +1,150 @@
.mgt2
.dialog-button
color: var(--mgt2-color-primary)
background-color: var(--mgt2-bgcolor-primary) !important
.mgt2
.dialog-button
color: var(--mgt2-color-primary)
background-color: var(--mgt2-bgcolor-primary) !important
// MGT2 Roll Dialog (DialogV2)
.mgt2-roll-dialog
background: #ffffff !important
border: 1px solid #ccbbbb !important
box-shadow: 0 8px 32px rgba(0,0,0,0.35) !important
border-radius: 6px !important
overflow: hidden !important
.window-header
background: #0A0405 !important
border-bottom: 3px solid #EE4050 !important
padding: 10px 14px !important
position: relative !important
.window-title
color: #ffffff !important
font-family: 'Barlow Condensed', sans-serif !important
font-weight: 700 !important
font-size: 1rem !important
text-transform: uppercase !important
letter-spacing: 2px !important
.window-content
background: #ffffff !important
color: #0A0405 !important
padding: 0 !important
.dialog-content, .standard-form
background: #ffffff !important
padding: 14px 18px 10px !important
// Form group rows
.form-group
display: flex !important
align-items: center !important
gap: 10px !important
margin-bottom: 8px !important
padding: 4px 0 !important
border-bottom: 1px solid #e8e0e0 !important
&:last-child
border-bottom: none !important
label
color: #0A0405 !important
font-family: 'Barlow Condensed', sans-serif !important
font-weight: 700 !important
font-size: 0.72rem !important
text-transform: uppercase !important
letter-spacing: 1.2px !important
flex: 0 0 110px !important
line-height: 1.2 !important
select, input[type="number"], input[type="text"]
flex: 1 !important
background: #ffffff !important
border: 1px solid #ccbbbb !important
color: #0A0405 !important
border-radius: 3px !important
padding: 5px 10px !important
font-family: 'Barlow Condensed', sans-serif !important
font-size: 0.9rem !important
transition: border-color 150ms ease !important
&:focus
border-color: #EE4050 !important
outline: none !important
background: rgba(238,64,80,0.04) !important
option
background: #ffffff !important
color: #0A0405 !important
// Fieldset / ÉTATS section
fieldset, .form-fields
background: #fdf8f8 !important
border: 1px solid #e0c8c8 !important
border-radius: 5px !important
padding: 10px 14px !important
margin-bottom: 8px !important
legend
color: #EE4050 !important
font-family: 'Barlow Condensed', sans-serif !important
font-weight: 700 !important
font-size: 0.72rem !important
text-transform: uppercase !important
letter-spacing: 2px !important
padding: 0 8px !important
background: #ffffff !important
// Checkboxes inside fieldset
.form-group
border-bottom: none !important
margin-bottom: 4px !important
padding: 2px 0 !important
label
color: #3a2020 !important
flex: 1 !important
input[type="checkbox"]
accent-color: #EE4050 !important
width: 14px !important
height: 14px !important
// Footer buttons
.dialog-buttons, .form-footer, footer
background: #f5eeee !important
border-top: 2px solid #EE4050 !important
padding: 10px 14px !important
display: flex !important
gap: 8px !important
justify-content: center !important
button
flex: 1 !important
max-width: 140px !important
background: #ffffff !important
border: 1px solid #ccbbbb !important
color: #3a2020 !important
border-radius: 4px !important
padding: 7px 14px !important
font-family: 'Barlow Condensed', sans-serif !important
font-size: 0.82rem !important
font-weight: 700 !important
text-transform: uppercase !important
letter-spacing: 1.5px !important
cursor: pointer !important
transition: all 150ms ease !important
&:hover
background: #fdf0f0 !important
border-color: #EE4050 !important
color: #EE4050 !important
// Primary action button (DialogV2: data-action="submit", autofocus)
&.default, &[data-action="submit"], &[autofocus]
background: #EE4050 !important
border-color: #EE4050 !important
color: #fff !important
box-shadow: 0 2px 12px rgba(238,64,80,0.3) !important
&:hover
background: #ff5060 !important
box-shadow: 0 4px 18px rgba(238,64,80,0.45) !important

View File

@@ -50,6 +50,7 @@
font-size: 13px
&.field-name
background-color: var(--mgt2-input-bgcolor)
color: var(--mgt2-color-primary)
font-size: 2rem
border: none
font-weight: 700
@@ -58,6 +59,7 @@
padding: 0
&.field-item-name
background-color: var(--mgt2-input-bgcolor)
color: var(--mgt2-color-primary)
height: auto
font-size: 2rem
font-weight: 700

View File

@@ -5,7 +5,7 @@
.itemsheet-header
display: flex
background-color: var(--mgt2-bgcolor-primary)
color: red
color: var(--mgt2-color-primary)
padding: 0.5rem
align-items: center
flex: 0 0 2rem

View File

@@ -1,40 +1,108 @@
.mgt2
.sheet-sidebar
.item
margin: 0 1rem
// ── Overflow fixes: allow sidebar nav to protrude OUTSIDE the window frame
// All ancestors of nav.sheet-sidebar need overflow:visible so absolute-positioned
// nav (left: 100% of character-body) can extend to the right of the window border.
// Layered !important beats Foundry's unlayered overflow:hidden per CSS cascade spec.
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
.mgt2.character, .mgt2.creature
overflow: visible !important
> .window-content
overflow: visible !important
.editable, .locked
overflow: visible !important
.mgt2.character .character-body
position: relative !important
overflow: visible !important
.mgt2.creature .creature-body
position: relative !important
overflow: visible !important
// Vertical sidebar tab navigation (outside window, right side)
.mgt2
nav.sheet-sidebar.tabs
position: absolute !important
left: 100% !important
top: 0 !important
bottom: 0 !important
width: 62px !important
flex: none !important
display: flex !important
flex-direction: column
// Rich dark gradient matching MGT2 theme
background: linear-gradient(180deg, #1e0507 0%, #110304 40%, #0a0202 100%)
border-top: 1px solid rgba(238,64,80,0.35)
border-right: 1px solid rgba(238,64,80,0.25)
border-bottom: 1px solid rgba(238,64,80,0.2)
border-left: 3px solid var(--mgt2-color-primary)
border-radius: 0 10px 10px 0
box-shadow: 6px 0 24px rgba(0,0,0,0.75), 0 0 0 0 transparent, inset 1px 0 16px rgba(238,64,80,0.05)
z-index: 10
overflow: hidden !important
padding: 2px 0
& > .item
position: relative
display: flex !important
flex-direction: column !important
justify-content: center !important
align-items: center !important
gap: 4px !important
min-height: 54px
padding: 8px 4px
color: rgba(238,64,80,0.45)
border-bottom: 1px solid rgba(238,64,80,0.07)
cursor: pointer
transition: background 180ms ease, color 180ms ease, box-shadow 180ms ease
user-select: none
text-decoration: none !important
// Left accent bar
&::before
content: ''
position: absolute
left: -3px
top: 18%
bottom: 18%
width: 3px
background: transparent
border-radius: 0 3px 3px 0
transition: background 180ms ease, top 180ms ease, bottom 180ms ease, box-shadow 180ms ease
&:hover
color: var(--mgt2-color-primary)
background: rgba(238,64,80,0.07)
&::before
background: rgba(238,64,80,0.5)
.tab-label
color: rgba(238,64,80,0.7)
&.active
color: var(--mgt2-color-primary)
background: linear-gradient(90deg, rgba(238,64,80,0.16) 0%, rgba(238,64,80,0.03) 100%)
box-shadow: inset 0 1px 0 rgba(238,64,80,0.12), inset 0 -1px 0 rgba(238,64,80,0.08)
&::before
background: var(--mgt2-color-primary)
top: 10%
bottom: 10%
box-shadow: 0 0 10px rgba(238,64,80,0.7), 0 0 20px rgba(238,64,80,0.3)
i
filter: drop-shadow(0 0 5px rgba(238,64,80,0.55))
.tab-label
color: rgba(238,64,80,0.85)
i
font-size: 1.15rem
pointer-events: none
line-height: 1
.tab-label
font-family: 'Barlow Condensed', sans-serif
font-size: 0.52rem
font-weight: 700
text-transform: uppercase
letter-spacing: 0.8px
color: rgba(238,64,80,0.3)
line-height: 1
pointer-events: none
transition: color 180ms ease

View File

@@ -63,7 +63,7 @@
&:not(:last-child)
margin-right: 0.4rem
i
color: black
color: var(--mgt2-color-form)
a[data-roll]
margin-right: 0.5rem
.heading

View File

@@ -12,4 +12,5 @@
@import 'components/_tabs'
@import 'components/_tab-sidebar'
@import 'components/_tables'
@import 'components/_tables'
@import 'components/_creature'

View File

@@ -11,6 +11,7 @@
&.sheet
.window-content
background: var(--mgt2-bgcolor-form)
color: var(--mgt2-color-form)
padding: 0
.nopad