fix: Dice So Nice import v14 compat, override Foundry text colors for readability
Release Creation / build (release) Failing after 1m28s

- hooks.mjs: replace static dice-so-nice import with dynamic import
  using game.modules.get('dice-so-nice').id (path removed in v14)
- hooks.mjs: fix permission condition (|| -> &&), jQuery -> vanilla JS
- less/base.less: override --color-text-* and --button-text-color
  for both .themed.theme-dark (AppV2) and body.theme-dark (legacy apps)
- target #settings-config buttons + labels + hints for dark grays
This commit is contained in:
2026-07-12 22:05:45 +02:00
parent dcc24b47ec
commit 90de66d668
44 changed files with 516 additions and 4305 deletions
+5 -320
View File
@@ -43,295 +43,27 @@ export default class VermineActor extends Actor {
* Prepare Character type specific data
*/
_prepareCharacterData(actorData) {
if (actorData.type !== 'character') return;
this._setAgeType();
this._setCharacterEffort();
this._setCharacterSelfControl();
this._setCharacterThresholds();
// Make modifications to data here. For example:
const systemData = actorData.system;
// Loop through ability scores, and add their modifiers to our sheet output.
for (let [key, ability] of Object.entries(systemData.abilities)) {
// Calculate the modifier using d20 rules.
ability.mod = Math.floor((ability.value - 10) / 2);
}
this.prepareCombatStatus();
// Character derived data is computed by VermineCharacterData.prepareDerivedData()
}
prepareCombatStatus() {
// Ensure combatStatus exists (defined in base template)
if (!this.system.combatStatus) {
this.system.combatStatus = { difficulty: "9", label: "Passif" };
return;
}
// Ensure difficulty exists
if (!this.system.combatStatus.difficulty) {
this.system.combatStatus.difficulty = "9";
}
//combat initiative reaction difficulty
const difficulty = parseInt(this.system.combatStatus.difficulty) || 9;
// Only update if values are different to avoid triggering unnecessary updates
const currentLabel = this.system.combatStatus.label;
let newLabel = "Passif";
switch (difficulty) {
case 5: newLabel = "Offensif"; break;
case 7: newLabel = "Actif"; break;
case 9: newLabel = "Passif"; break;
}
// Only update if label changed
if (currentLabel !== newLabel) {
this.system.combatStatus.label = newLabel;
}
// Only update difficulty if it was undefined or invalid
if (!this.system.combatStatus.difficulty || isNaN(parseInt(this.system.combatStatus.difficulty))) {
this.system.combatStatus.difficulty = "9";
}
}
/**
* Prepare NPC type specific data.
*/
_prepareNpcData(actorData) {
if (actorData.type !== 'npc') return;
// Make modifications to data here. For example:
const systemData = actorData.system;
// 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;
// NPC derived data is computed by VermineNpcData.prepareDerivedData()
}
/**
* 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";
}
// Group derived data is computed by VermineGroupData.prepareDerivedData()
}
/**
* 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);
// Creature derived data is computed by VermineCreatureData.prepareDerivedData()
}
/**
@@ -376,53 +108,6 @@ export default class VermineActor extends Actor {
// Process additional NPC data here.
}
_setCharacterSelfControl() {
this.system.attributes.self_control.max = 0 + Object.values(this.system.abilities).filter(i => i.category === "mental" || i.category === "social").map((i) => i.value).reduce((acc, curr) => acc + curr, 0) + this.modFromAgeSelfControl;
}
_setCharacterEffort() {
this.system.attributes.effort.max = 0 + Object.values(this.system.abilities).filter(i => i.category === "physical" || i.category === "manual").map((i) => i.value).reduce((acc, curr) => acc + curr, 0) + this.modFromAgeEffort;
}
_setCharacterThresholds() {
const health = this.system.abilities.health.value;
this.system.minorWound.threshold = health;
this.system.majorWound.threshold = health + 3;
this.system.deadlyWound.threshold = (health + 7 < 11) ? health + 7 : 10;
this.system.minorWound.max = 4 + this.modFromAgeWounds.l;
this.system.majorWound.max = 3 + this.modFromAgeWounds.h;
this.system.deadlyWound.max = 2 + this.modFromAgeWounds.d;
}
_setAgeType() {
Object.keys(CONFIG.VERMINE.AgeTypes).forEach((type) => {
if (this.system.identity.age >= parseInt(CONFIG.VERMINE.AgeTypes[type].beginning, 10)) {
this.system.identity.ageType = type;
}
});
}
get ageType() {
return this.system.identity.ageType;
}
get modFromAgeSelfControl() {
return this.ageType == 1 ? -1 : 0;
}
get modFromAgeEffort() {
if (this.ageType == 1) return -1;
if (this.ageType == 3) return -2;
return 0;
}
get modFromAgeWounds() {
if (this.ageType == 1) return { l: 0, h: 0, d: -1 };
if (this.ageType == 3) return { l: -1, h: -1, d: -1 };
return { l: 0, h: 0, d: 0 };
}
// Character derived-data methods removed — handled by VermineCharacterData.prepareDerivedData()
}
+1 -1
View File
@@ -38,7 +38,7 @@ export default class VermineItem extends Item {
}
prepareAbilityData() {
console.log('ability data', this)
// ability data
const actorType = (this.actor !== null) ? this.actor.type : 'character';
if (this.system.type == "") {