Second round de corrections et améliorations
This commit is contained in:
647
mgt2.bundle.js
647
mgt2.bundle.js
@@ -1,20 +1,20 @@
|
||||
const fields$d = foundry.data.fields;
|
||||
const fields$e = foundry.data.fields;
|
||||
|
||||
function createCharacteristicField(show = true, showMax = false) {
|
||||
return new fields$d.SchemaField({
|
||||
value: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
max: new fields$d.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
dm: new fields$d.NumberField({ required: false, initial: 0, integer: true }),
|
||||
show: new fields$d.BooleanField({ required: false, initial: show }),
|
||||
showMax: new fields$d.BooleanField({ required: false, initial: showMax })
|
||||
return new fields$e.SchemaField({
|
||||
value: new fields$e.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
max: new fields$e.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
dm: new fields$e.NumberField({ required: false, initial: 0, integer: true }),
|
||||
show: new fields$e.BooleanField({ required: false, initial: show }),
|
||||
showMax: new fields$e.BooleanField({ required: false, initial: showMax })
|
||||
});
|
||||
}
|
||||
|
||||
class ItemBaseData extends foundry.abstract.TypeDataModel {
|
||||
static defineSchema() {
|
||||
return {
|
||||
description: new fields$d.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
subType: new fields$d.StringField({ required: false, blank: false, nullable: true })
|
||||
description: new fields$e.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
subType: new fields$e.StringField({ required: false, blank: false, nullable: true })
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -22,57 +22,57 @@ class ItemBaseData extends foundry.abstract.TypeDataModel {
|
||||
class PhysicalItemData extends ItemBaseData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
schema.quantity = new fields$d.NumberField({ required: true, initial: 1, min: 0, integer: true });
|
||||
schema.weight = new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: false });
|
||||
schema.weightless = new fields$d.BooleanField({ required: false, initial: false });
|
||||
schema.cost = new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.tl = new fields$d.StringField({ required: true, blank: false, initial: "TL12" });
|
||||
schema.container = new fields$d.SchemaField({
|
||||
id: new fields$d.StringField({ required: false, blank: true })
|
||||
schema.quantity = new fields$e.NumberField({ required: true, initial: 1, min: 0, integer: true });
|
||||
schema.weight = new fields$e.NumberField({ required: true, initial: 0, min: 0, integer: false });
|
||||
schema.weightless = new fields$e.BooleanField({ required: false, initial: false });
|
||||
schema.cost = new fields$e.NumberField({ required: true, initial: 0, min: 0, integer: true });
|
||||
schema.tl = new fields$e.StringField({ required: true, blank: false, initial: "TL12" });
|
||||
schema.container = new fields$e.SchemaField({
|
||||
id: new fields$e.StringField({ required: false, blank: true })
|
||||
});
|
||||
schema.roll = new fields$d.SchemaField({
|
||||
characteristic: new fields$d.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields$d.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields$d.StringField({ required: false, blank: true, trim: true })
|
||||
schema.roll = new fields$e.SchemaField({
|
||||
characteristic: new fields$e.StringField({ required: false, blank: true, trim: true }),
|
||||
skill: new fields$e.StringField({ required: false, blank: true, trim: true }),
|
||||
difficulty: new fields$e.StringField({ required: false, blank: true, trim: true })
|
||||
});
|
||||
schema.trash = new fields$d.BooleanField({ required: false, initial: false });
|
||||
schema.trash = new fields$e.BooleanField({ required: false, initial: false });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
const fields$c = foundry.data.fields;
|
||||
const fields$d = foundry.data.fields;
|
||||
|
||||
class CharacterData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields$c.StringField({ required: false, blank: false, trim: true }),
|
||||
life: new fields$c.SchemaField({
|
||||
value: new fields$c.NumberField({ required: false, initial: 0, integer: true }),
|
||||
max: new fields$c.NumberField({ required: true, initial: 0, integer: true })
|
||||
name: new fields$d.StringField({ required: false, blank: false, trim: true }),
|
||||
life: new fields$d.SchemaField({
|
||||
value: new fields$d.NumberField({ required: false, initial: 0, integer: true }),
|
||||
max: new fields$d.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
personal: new fields$c.SchemaField({
|
||||
title: new fields$c.StringField({ required: false, blank: true, trim: true }),
|
||||
species: new fields$c.StringField({ required: false, blank: true, trim: true }),
|
||||
speciesText: new fields$c.SchemaField({
|
||||
description: new fields$c.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields$c.HTMLField({ required: false, blank: true, trim: true })
|
||||
personal: new fields$d.SchemaField({
|
||||
title: new fields$d.StringField({ required: false, blank: true, trim: true }),
|
||||
species: new fields$d.StringField({ required: false, blank: true, trim: true }),
|
||||
speciesText: new fields$d.SchemaField({
|
||||
description: new fields$d.StringField({ required: false, blank: true, trim: true, nullable: true }),
|
||||
descriptionLong: new fields$d.HTMLField({ required: false, blank: true, trim: true })
|
||||
}),
|
||||
age: new fields$c.StringField({ required: false, blank: true, trim: true }),
|
||||
gender: new fields$c.StringField({ required: false, blank: true, trim: true }),
|
||||
pronouns: new fields$c.StringField({ required: false, blank: true, trim: true }),
|
||||
homeworld: new fields$c.StringField({ required: false, blank: true, trim: true }),
|
||||
ucp: new fields$c.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
traits: new fields$c.ArrayField(
|
||||
new fields$c.SchemaField({
|
||||
name: new fields$c.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields$c.StringField({ required: false, blank: true, trim: true })
|
||||
age: new fields$d.StringField({ required: false, blank: true, trim: true }),
|
||||
gender: new fields$d.StringField({ required: false, blank: true, trim: true }),
|
||||
pronouns: new fields$d.StringField({ required: false, blank: true, trim: true }),
|
||||
homeworld: new fields$d.StringField({ required: false, blank: true, trim: true }),
|
||||
ucp: new fields$d.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
traits: new fields$d.ArrayField(
|
||||
new fields$d.SchemaField({
|
||||
name: new fields$d.StringField({ required: true, blank: true, trim: true }),
|
||||
description: new fields$d.StringField({ required: false, blank: true, trim: true })
|
||||
})
|
||||
)
|
||||
}),
|
||||
biography: new fields$c.HTMLField({ required: false, blank: true, trim: true }),
|
||||
biography: new fields$d.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
characteristics: new fields$c.SchemaField({
|
||||
characteristics: new fields$d.SchemaField({
|
||||
strength: createCharacteristicField(true, true),
|
||||
dexterity: createCharacteristicField(true, true),
|
||||
endurance: createCharacteristicField(true, true),
|
||||
@@ -87,87 +87,184 @@ class CharacterData extends foundry.abstract.TypeDataModel {
|
||||
other: createCharacteristicField(true, false)
|
||||
}),
|
||||
|
||||
health: new fields$c.SchemaField({
|
||||
radiations: new fields$c.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
health: new fields$d.SchemaField({
|
||||
radiations: new fields$d.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
study: new fields$c.SchemaField({
|
||||
skill: new fields$c.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
total: new fields$c.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
completed: new fields$c.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
study: new fields$d.SchemaField({
|
||||
skill: new fields$d.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
total: new fields$d.NumberField({ required: false, initial: 0, min: 0, integer: true }),
|
||||
completed: new fields$d.NumberField({ required: false, initial: 0, min: 0, integer: true })
|
||||
}),
|
||||
finance: new fields$c.SchemaField({
|
||||
pension: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
credits: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
cashOnHand: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
debt: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
livingCost: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
monthlyShipPayments: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
notes: new fields$c.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
finance: new fields$d.SchemaField({
|
||||
pension: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
credits: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
cashOnHand: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
debt: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
livingCost: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
monthlyShipPayments: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
notes: new fields$d.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
}),
|
||||
containerView: new fields$c.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
containerDropIn: new fields$c.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
notes: new fields$c.HTMLField({ required: false, blank: true, trim: true }),
|
||||
containerView: new fields$d.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
containerDropIn: new fields$d.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
notes: new fields$d.HTMLField({ required: false, blank: true, trim: true }),
|
||||
|
||||
inventory: new fields$c.SchemaField({
|
||||
armor: new fields$c.NumberField({ required: true, initial: 0, integer: true }),
|
||||
weight: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: false }),
|
||||
encumbrance: new fields$c.SchemaField({
|
||||
normal: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
heavy: new fields$c.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
inventory: new fields$d.SchemaField({
|
||||
armor: new fields$d.NumberField({ required: true, initial: 0, integer: true }),
|
||||
weight: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: false }),
|
||||
encumbrance: new fields$d.SchemaField({
|
||||
normal: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
heavy: new fields$d.NumberField({ required: true, initial: 0, min: 0, integer: true })
|
||||
})
|
||||
}),
|
||||
states: new fields$c.SchemaField({
|
||||
encumbrance: new fields$c.BooleanField({ required: false, initial: false }),
|
||||
fatigue: new fields$c.BooleanField({ required: false, initial: false }),
|
||||
unconscious: new fields$c.BooleanField({ required: false, initial: false }),
|
||||
surgeryRequired: new fields$c.BooleanField({ required: false, initial: false })
|
||||
states: new fields$d.SchemaField({
|
||||
encumbrance: new fields$d.BooleanField({ required: false, initial: false }),
|
||||
fatigue: new fields$d.BooleanField({ required: false, initial: false }),
|
||||
unconscious: new fields$d.BooleanField({ required: false, initial: false }),
|
||||
surgeryRequired: new fields$d.BooleanField({ required: false, initial: false })
|
||||
}),
|
||||
|
||||
config: new fields$c.SchemaField({
|
||||
psionic: new fields$c.BooleanField({ required: false, initial: true }),
|
||||
initiative: new fields$c.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
damages: new fields$c.SchemaField({
|
||||
rank1: new fields$c.StringField({ required: false, blank: true, initial: "strength" }),
|
||||
rank2: new fields$c.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
rank3: new fields$c.StringField({ required: false, blank: true, initial: "endurance" })
|
||||
config: new fields$d.SchemaField({
|
||||
psionic: new fields$d.BooleanField({ required: false, initial: true }),
|
||||
initiative: new fields$d.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
damages: new fields$d.SchemaField({
|
||||
rank1: new fields$d.StringField({ required: false, blank: true, initial: "strength" }),
|
||||
rank2: new fields$d.StringField({ required: false, blank: true, initial: "dexterity" }),
|
||||
rank3: new fields$d.StringField({ required: false, blank: true, initial: "endurance" })
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const fields$c = foundry.data.fields;
|
||||
|
||||
class VehiculeData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields$c.StringField({ required: false, blank: false, trim: true }),
|
||||
skillId: new fields$c.StringField({ required: false, initial: "", blank: true, trim: true }),
|
||||
speed: new fields$c.SchemaField({
|
||||
cruise: new fields$c.StringField({ required: false, initial: "Slow", blank: true }),
|
||||
maximum: new fields$c.StringField({ required: false, initial: "Medium", blank: true })
|
||||
}),
|
||||
agility: new fields$c.NumberField({ required: false, min: 0, integer: true }),
|
||||
crew: new fields$c.NumberField({ required: false, min: 0, integer: true }),
|
||||
passengers: new fields$c.NumberField({ required: false, min: 0, integer: true }),
|
||||
cargo: new fields$c.NumberField({ required: false, min: 0, integer: false }),
|
||||
life: new fields$c.SchemaField({
|
||||
value: new fields$c.NumberField({ required: true, initial: 0, integer: true }),
|
||||
max: new fields$c.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
shipping: new fields$c.NumberField({ required: false, min: 0, integer: true }),
|
||||
cost: new fields$c.NumberField({ required: false, min: 0, integer: true }),
|
||||
armor: new fields$c.SchemaField({
|
||||
front: new fields$c.NumberField({ required: true, initial: 0, integer: true }),
|
||||
rear: new fields$c.NumberField({ required: true, initial: 0, integer: true }),
|
||||
sides: new fields$c.NumberField({ required: true, initial: 0, integer: true })
|
||||
}),
|
||||
skills: new fields$c.SchemaField({
|
||||
autopilot: new fields$c.NumberField({ required: true, initial: 0, integer: true })
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const fields$b = foundry.data.fields;
|
||||
|
||||
class VehiculeData extends foundry.abstract.TypeDataModel {
|
||||
class CreatureData extends foundry.abstract.TypeDataModel {
|
||||
|
||||
static defineSchema() {
|
||||
return {
|
||||
name: new fields$b.StringField({ required: false, blank: false, trim: true }),
|
||||
skillId: new fields$b.StringField({ required: false, initial: "", blank: true, trim: true }),
|
||||
speed: new fields$b.SchemaField({
|
||||
cruise: new fields$b.StringField({ required: false, initial: "Slow", blank: true }),
|
||||
maximum: new fields$b.StringField({ required: false, initial: "Medium", blank: true })
|
||||
}),
|
||||
agility: new fields$b.NumberField({ required: false, min: 0, integer: true }),
|
||||
crew: new fields$b.NumberField({ required: false, min: 0, integer: true }),
|
||||
passengers: new fields$b.NumberField({ required: false, min: 0, integer: true }),
|
||||
cargo: new fields$b.NumberField({ required: false, min: 0, integer: false }),
|
||||
life: new fields$b.SchemaField({
|
||||
value: new fields$b.NumberField({ required: true, initial: 0, integer: true }),
|
||||
max: new fields$b.NumberField({ required: true, initial: 0, integer: true })
|
||||
value: new fields$b.NumberField({ required: true, initial: 10, min: 0, integer: true }),
|
||||
max: new fields$b.NumberField({ required: true, initial: 10, min: 0, integer: true })
|
||||
}),
|
||||
shipping: new fields$b.NumberField({ required: false, min: 0, integer: true }),
|
||||
cost: new fields$b.NumberField({ required: false, min: 0, integer: true }),
|
||||
armor: new fields$b.SchemaField({
|
||||
front: new fields$b.NumberField({ required: true, initial: 0, integer: true }),
|
||||
rear: new fields$b.NumberField({ required: true, initial: 0, integer: true }),
|
||||
sides: new fields$b.NumberField({ required: true, initial: 0, integer: true })
|
||||
|
||||
speed: new fields$b.NumberField({ required: true, initial: 6, min: 0, integer: true }),
|
||||
|
||||
armor: new fields$b.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
|
||||
psi: new fields$b.NumberField({ required: true, initial: 0, min: 0, integer: true }),
|
||||
|
||||
initiativeBonus: new fields$b.NumberField({ required: true, initial: 0, integer: true }),
|
||||
|
||||
skills: new fields$b.ArrayField(
|
||||
new fields$b.SchemaField({
|
||||
name: new fields$b.StringField({ required: true, blank: true, trim: true, initial: "" }),
|
||||
level: new fields$b.NumberField({ required: true, initial: 0, integer: true }),
|
||||
note: new fields$b.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
})
|
||||
),
|
||||
|
||||
attacks: new fields$b.ArrayField(
|
||||
new fields$b.SchemaField({
|
||||
name: new fields$b.StringField({ required: true, blank: true, trim: true, initial: "" }),
|
||||
damage: new fields$b.StringField({ required: true, blank: true, trim: true, initial: "1D" }),
|
||||
description: new fields$b.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
})
|
||||
),
|
||||
|
||||
traits: new fields$b.ArrayField(
|
||||
new fields$b.SchemaField({
|
||||
name: new fields$b.StringField({ required: true, blank: true, trim: true, initial: "" }),
|
||||
value: new fields$b.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
description: new fields$b.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
})
|
||||
),
|
||||
|
||||
behavior: new fields$b.SchemaField({
|
||||
type: new fields$b.StringField({ required: false, blank: true, trim: true, initial: "" }),
|
||||
subtype: new fields$b.StringField({ required: false, blank: true, trim: true, initial: "" })
|
||||
}),
|
||||
skills: new fields$b.SchemaField({
|
||||
autopilot: new fields$b.NumberField({ required: true, initial: 0, integer: true })
|
||||
})
|
||||
|
||||
biography: new fields$b.HTMLField({ required: false, blank: true, trim: true }),
|
||||
notes: new fields$b.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fields$a = foundry.data.fields;
|
||||
@@ -555,6 +652,29 @@ MGT2.Durations = Object.freeze({
|
||||
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"
|
||||
});
|
||||
|
||||
class ActorCharacter {
|
||||
static preCreate($this, data, options, user) {
|
||||
$this.updateSource({ prototypeToken: { actorLink: true } }); // QoL
|
||||
@@ -1374,18 +1494,9 @@ 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) {
|
||||
@@ -1494,13 +1605,13 @@ class MGT2Helper {
|
||||
}
|
||||
|
||||
const { DialogV2: DialogV2$1 } = foundry.applications.api;
|
||||
const { renderTemplate: renderTemplate$1 } = foundry.applications.handlebars;
|
||||
const { renderTemplate: renderTemplate$2 } = foundry.applications.handlebars;
|
||||
const { FormDataExtended: FormDataExtended$1 } = foundry.applications.ux;
|
||||
|
||||
class RollPromptHelper {
|
||||
|
||||
static async roll(options) {
|
||||
const htmlContent = await renderTemplate$1('systems/mgt2/templates/roll-prompt.html', {
|
||||
const htmlContent = await renderTemplate$2('systems/mgt2/templates/roll-prompt.html', {
|
||||
config: CONFIG.MGT2,
|
||||
characteristics: options.characteristics,
|
||||
characteristic: options.characteristic,
|
||||
@@ -1515,6 +1626,7 @@ class RollPromptHelper {
|
||||
|
||||
return await DialogV2$1.wait({
|
||||
window: { title: options.title ?? options.rollTypeName ?? game.i18n.localize("MGT2.RollPrompt.Roll") },
|
||||
classes: ["mgt2-roll-dialog"],
|
||||
content: htmlContent,
|
||||
rejectClose: false,
|
||||
buttons: [
|
||||
@@ -1551,11 +1663,11 @@ class RollPromptHelper {
|
||||
}
|
||||
|
||||
const { DialogV2 } = foundry.applications.api;
|
||||
const { renderTemplate } = foundry.applications.handlebars;
|
||||
const { renderTemplate: renderTemplate$1 } = foundry.applications.handlebars;
|
||||
const { FormDataExtended } = foundry.applications.ux;
|
||||
|
||||
async function _dialogWithForm(title, templatePath, templateData) {
|
||||
const htmlContent = await renderTemplate(templatePath, templateData);
|
||||
const htmlContent = await renderTemplate$1(templatePath, templateData);
|
||||
game.settings.get("mgt2", "theme");
|
||||
return await DialogV2.wait({
|
||||
window: { title },
|
||||
@@ -1603,7 +1715,7 @@ class CharacterPrompts {
|
||||
}
|
||||
|
||||
static async openEditorFullView(title, html) {
|
||||
const htmlContent = await renderTemplate("systems/mgt2/templates/editor-fullview.html", {
|
||||
const htmlContent = await renderTemplate$1("systems/mgt2/templates/editor-fullview.html", {
|
||||
config: CONFIG.MGT2,
|
||||
html
|
||||
});
|
||||
@@ -1903,7 +2015,7 @@ 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);
|
||||
@@ -1994,26 +2106,22 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -2086,20 +2194,19 @@ 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;
|
||||
@@ -2117,27 +2224,24 @@ 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) {
|
||||
@@ -2163,23 +2267,19 @@ 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) {
|
||||
@@ -2489,6 +2589,236 @@ class TravellerVehiculeSheet extends MGT2ActorSheet {
|
||||
tabGroups = { primary: "stats" }
|
||||
}
|
||||
|
||||
/** 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}`;
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
class TravellerItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
||||
@@ -2774,6 +3104,9 @@ 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"
|
||||
];
|
||||
|
||||
@@ -2849,7 +3182,7 @@ 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,
|
||||
@@ -3011,6 +3344,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"]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3029,13 +3366,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, {
|
||||
|
||||
Reference in New Issue
Block a user