fix: Correct critical bugs and complete Creature/Group DataModel implementation
- Fix TypeError: controls.find is not a function in hooks.mjs - Fix undefined 'npc' variable in applications.mjs - Fix CONFIG.VERMINE.model undefined by checking game.system.template existence - Fix TypeError: html.find(...).forEach is not a function in roll.mjs - Fix Cannot set properties of undefined (setting 'initial') in actor.mjs - Fix Cannot read properties of undefined (reading 'difficulty') in actor.mjs - Fix ActiveEffect application phase 'initial' already completed by adding combatStatus to base template - Fix Missing helper: 'select' in roll-dialog.hbs (removed invalid Handlebars select block) - Add SIZE_LEVELS labels to creatureSizeLevels config - Add SIZE_LEVELS translations to fr.json - Add combatStatus to base actor template - Convert all .html templates to .hbs for Foundry v14 compatibility - Update item-sheet.mjs to use .hbs extension - Update handlebars-manager.mjs to use .hbs for all partials Complete Vermine2047 Creature and Group sheet implementation: - Creature: Pattern, Size, Role, Pack with computed values - Group: Totem, Reserve, Morale, Objectives, Members management - All templates functional with proper styling Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
+244
-4
@@ -11,6 +11,12 @@ export class VermineActor extends Actor {
|
||||
// the following, in order: data reset (to clear active effects),
|
||||
// prepareBaseData(), prepareEmbeddedDocuments() (including active effects),
|
||||
// prepareDerivedData().
|
||||
|
||||
// Initialize wound data to prevent undefined errors with active effects
|
||||
if (!this.system.minorWound) this.system.minorWound = { value: 0, min: 0, max: 5, threshold: 1 };
|
||||
if (!this.system.majorWound) this.system.majorWound = { value: 0, min: 0, max: 4, threshold: 4 };
|
||||
if (!this.system.deadlyWound) this.system.deadlyWound = { value: 0, min: 0, max: 2, threshold: 8 };
|
||||
|
||||
super.prepareData();
|
||||
|
||||
}
|
||||
@@ -48,10 +54,14 @@ export class VermineActor extends Actor {
|
||||
case "npc":
|
||||
this._prepareNpcData(actorData);
|
||||
break;
|
||||
|
||||
case "group":
|
||||
this._prepareGroupData(actorData);
|
||||
break;
|
||||
case "creature":
|
||||
this._prepareCreatureData(actorData);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,6 +85,9 @@ export class VermineActor extends Actor {
|
||||
|
||||
}
|
||||
prepareCombatStatus() {
|
||||
// Ensure combatStatus exists (defined in base template)
|
||||
if (!this.system.combatStatus) return;
|
||||
|
||||
//combat initiative reaction difficulty
|
||||
switch (parseInt(this.system.combatStatus.difficulty)) {
|
||||
case 5: this.system.combatStatus.label = "Offensif";
|
||||
@@ -98,8 +111,235 @@ export class VermineActor extends Actor {
|
||||
|
||||
// Make modifications to data here. For example:
|
||||
const systemData = actorData.system;
|
||||
systemData.xp = (systemData.cr * systemData.cr) * 100;
|
||||
this.prepareCombatStatus()
|
||||
|
||||
// Set wound thresholds based on threat level
|
||||
this._setNpcThresholds();
|
||||
|
||||
// Set reserve max values based on role
|
||||
this._setNpcAttributes();
|
||||
|
||||
this.prepareCombatStatus();
|
||||
|
||||
// Prepare abilities with labels
|
||||
for (let [k, v] of Object.entries(systemData.abilities)) {
|
||||
v.label = game.i18n.localize(CONFIG.VERMINE.abilities[k]) ?? k;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set NPC wound thresholds based on threat level
|
||||
*/
|
||||
_setNpcThresholds() {
|
||||
const health = this.system.abilities?.health?.value || 1;
|
||||
const threatLevel = this.system.threat?.value || 1;
|
||||
const threatConfig = CONFIG.VERMINE.npcThreatLevels[threatLevel] || {};
|
||||
|
||||
// Use threat-based wounds or fall back to health-based
|
||||
this.system.minorWound.threshold = threatConfig.minorWound || health;
|
||||
this.system.majorWound.threshold = threatConfig.majorWound || (health + 3);
|
||||
this.system.deadlyWound.threshold = threatConfig.deadlyWound || (health + 7 < 11 ? health + 7 : 10);
|
||||
|
||||
// Set max wounds based on threat level
|
||||
this.system.minorWound.max = threatConfig.minorWound || 4;
|
||||
this.system.majorWound.max = threatConfig.majorWound || 3;
|
||||
this.system.deadlyWound.max = threatConfig.deadlyWound || 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set NPC attributes from role level
|
||||
*/
|
||||
_setNpcAttributes() {
|
||||
const roleLevel = this.system.role?.value || 1;
|
||||
const roleConfig = CONFIG.VERMINE.npcRoleLevels[roleLevel] || {};
|
||||
|
||||
// Set effort and self_control based on role
|
||||
this.system.attributes.effort.max = roleConfig.pools || 0;
|
||||
this.system.attributes.self_control.max = roleConfig.reaction_bonus || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare Group type specific data.
|
||||
*/
|
||||
_prepareGroupData(actorData) {
|
||||
if (actorData.type !== 'group') return;
|
||||
|
||||
this.prepareCombatStatus();
|
||||
|
||||
// Initialize group-specific data if not present
|
||||
this._initGroupData();
|
||||
|
||||
// Calculate reserve max based on group level
|
||||
this._calculateGroupReserve();
|
||||
|
||||
// Update morale level based on dice value
|
||||
this._updateGroupMorale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize group data with defaults
|
||||
*/
|
||||
_initGroupData() {
|
||||
if (this.type !== 'group') return;
|
||||
|
||||
const system = this.system;
|
||||
|
||||
// Initialize objectives if not present
|
||||
if (!system.objectives) {
|
||||
system.objectives = { major: [], minor: [] };
|
||||
}
|
||||
|
||||
// Initialize groupAbilities if not present
|
||||
if (!system.groupAbilities) {
|
||||
system.groupAbilities = [];
|
||||
}
|
||||
|
||||
// Initialize reserve if not present
|
||||
if (!system.reserve) {
|
||||
system.reserve = { value: 0, min: 0, max: 10 };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate group reserve max based on level
|
||||
* Rules: Group level determines reserve size
|
||||
*/
|
||||
_calculateGroupReserve() {
|
||||
if (this.type !== 'group') return;
|
||||
|
||||
const level = this.system.level?.value || 1;
|
||||
// Reserve max is based on group level (simplified: level * 1D for now)
|
||||
// Can be customized based on specific rules
|
||||
this.system.reserve.max = Math.min(10, level * 2);
|
||||
|
||||
// Ensure value doesn't exceed max
|
||||
if (this.system.reserve.value > this.system.reserve.max) {
|
||||
this.system.reserve.value = this.system.reserve.max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update group morale level based on dice value
|
||||
* Rules: 7D+ = Haut, 6-3D = Normal, 2D- = Bas, 0D = Crise
|
||||
*/
|
||||
_updateGroupMorale() {
|
||||
if (this.type !== 'group') return;
|
||||
|
||||
const moraleValue = this.system.morale?.value || 0;
|
||||
const moraleLevel = this.system.morale?.level;
|
||||
|
||||
// If level is already explicitly set, keep it
|
||||
if (moraleLevel && moraleLevel !== "high") return;
|
||||
|
||||
// Determine morale level based on dice value
|
||||
if (moraleValue >= 7) {
|
||||
this.system.morale.level = "high";
|
||||
} else if (moraleValue >= 3) {
|
||||
this.system.morale.level = "normal";
|
||||
} else if (moraleValue >= 1) {
|
||||
this.system.morale.level = "low";
|
||||
} else {
|
||||
this.system.morale.level = "crisis";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare Creature type specific data.
|
||||
* Calculates computed values from pattern, size, role, and pack.
|
||||
*/
|
||||
_prepareCreatureData(actorData) {
|
||||
if (actorData.type !== 'creature') return;
|
||||
|
||||
this.prepareCombatStatus();
|
||||
|
||||
// Calculate computed values from pattern, size, role, and pack
|
||||
this._calculateCreatureComputedValues();
|
||||
|
||||
// Set wound thresholds from creature characteristics
|
||||
this._calculateCreatureWoundThresholds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate creature computed values from pattern, size, role, and pack.
|
||||
* Rules: Attack = pattern + size + pack + role.reaction
|
||||
* Damage = pattern.damage + size.vigor + pack.damage
|
||||
* Reaction = role.reaction + role.reaction_bonus
|
||||
*/
|
||||
_calculateCreatureComputedValues() {
|
||||
if (this.type !== 'creature') return;
|
||||
|
||||
const patternLevel = this.system.pattern?.value || 1;
|
||||
const sizeLevel = this.system.size?.value || 1;
|
||||
const roleLevel = this.system.role?.value || 1;
|
||||
const packLevel = this.system.pack?.value || 0;
|
||||
|
||||
// Get config values
|
||||
const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel] || {};
|
||||
const sizeConfig = CONFIG.VERMINE.creatureSizeLevels[sizeLevel] || {};
|
||||
const roleConfig = CONFIG.VERMINE.creatureRoleLevels[roleLevel] || {};
|
||||
const packConfig = CONFIG.VERMINE.creaturePackLevels[packLevel] || {};
|
||||
|
||||
// Calculate computed values
|
||||
this.system.computed = this.system.computed || {};
|
||||
|
||||
// Attack: pattern + size + pack + role.reaction
|
||||
this.system.computed.attack = (patternConfig.attack || 0) +
|
||||
(sizeConfig.attack || 0) +
|
||||
(packConfig.attack || 0) +
|
||||
(roleConfig.reaction || 0);
|
||||
|
||||
// Damage: pattern + size.vigor + pack
|
||||
this.system.computed.damage = (patternConfig.damage || 0) +
|
||||
(sizeConfig.vigor || 0) +
|
||||
(packConfig.damage || 0);
|
||||
|
||||
// Vigor: size.vigor + pack.damage
|
||||
this.system.computed.vigor = (sizeConfig.vigor || 0) + (packConfig.damage || 0);
|
||||
|
||||
// Reaction: role.reaction + role.reaction_bonus
|
||||
this.system.computed.reaction = (roleConfig.reaction || 0) + (roleConfig.reaction_bonus || 0);
|
||||
this.system.computed.reactionBonus = roleConfig.reaction_bonus || 0;
|
||||
|
||||
// Pools (reserves)
|
||||
this.system.computed.pools = roleConfig.pools || 0;
|
||||
|
||||
// Gear and hindrance
|
||||
this.system.computed.gear = roleConfig.gear || 9;
|
||||
this.system.computed.gearHindrance = roleConfig.gear_hindrance || 0;
|
||||
|
||||
// Protection
|
||||
this.system.computed.protection = roleConfig.protection || 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate creature wound thresholds from pattern, size, and pack.
|
||||
* Rules: Thresholds are sum of minorWound, majorWound, deadlyWound from all sources
|
||||
*/
|
||||
_calculateCreatureWoundThresholds() {
|
||||
if (this.type !== 'creature') return;
|
||||
|
||||
const patternLevel = this.system.pattern?.value || 1;
|
||||
const sizeLevel = this.system.size?.value || 1;
|
||||
const packLevel = this.system.pack?.value || 0;
|
||||
|
||||
const patternConfig = CONFIG.VERMINE.creaturePatternLevels[patternLevel] || {};
|
||||
const sizeConfig = CONFIG.VERMINE.creatureSizeLevels[sizeLevel] || {};
|
||||
const packConfig = CONFIG.VERMINE.creaturePackLevels[packLevel] || {};
|
||||
|
||||
// Calculate wound thresholds (sum of all sources)
|
||||
this.system.minorWound.threshold = (patternConfig.minorWound || 0) +
|
||||
(sizeConfig.minorWound || 0) +
|
||||
(packConfig.minorWound || 0);
|
||||
this.system.majorWound.threshold = (patternConfig.majorWound || 0) +
|
||||
(sizeConfig.majorWound || 0) +
|
||||
(packConfig.majorWound || 0);
|
||||
this.system.deadlyWound.threshold = (patternConfig.deadlyWound || 0) +
|
||||
(sizeConfig.deadlyWound || 0) +
|
||||
(packConfig.deadlyWound || 0);
|
||||
|
||||
// Set max wounds
|
||||
this.system.minorWound.max = Math.min(5, this.system.minorWound.threshold + 2);
|
||||
this.system.majorWound.max = Math.min(4, this.system.majorWound.threshold + 1);
|
||||
this.system.deadlyWound.max = Math.min(2, this.system.deadlyWound.threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user