Added remaining options and fixed secure loading of dice models (#413)

This commit is contained in:
WBHarry 2025-07-26 04:12:22 +02:00 committed by GitHub
parent dddee78356
commit bf31ced3df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 167 additions and 100 deletions

View file

@ -1561,7 +1561,11 @@
"foreground": "Foreground", "foreground": "Foreground",
"background": "Background", "background": "Background",
"outline": "Outline", "outline": "Outline",
"edge": "Edge" "edge": "Edge",
"texture": "Texture",
"colorset": "Theme",
"material": "Material",
"system": "Dice Preset"
} }
}, },
"variantRules": { "variantRules": {

View file

@ -59,8 +59,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.settingFields = this.settings; context.settingFields = this.settings;
context.diceSoNiceTextures = game.dice3d?.exports?.TEXTURELIST ?? {}; context.showDiceSoNice = game.modules.get('dice-so-nice')?.active;
context.diceSoNiceColorsets = game.dice3d?.exports?.COLORSETS ?? {}; if (game.dice3d) {
context.diceSoNiceTextures = game.dice3d.exports.TEXTURELIST;
context.diceSoNiceColorsets = game.dice3d.exports.COLORSETS;
context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).map(key => ({
key: key,
name: `DICESONICE.Material${key.capitalize()}`
}));
context.diceSoNiceSystems = [];
for (const [key, system] of game.dice3d.DiceFactory.systems.entries()) {
context.diceSoNiceSystems.push({ key, name: system.name });
}
}
context.diceTab = { context.diceTab = {
key: this.tabGroups.diceSoNice, key: this.tabGroups.diceSoNice,
source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice], source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice],

View file

@ -369,42 +369,28 @@ export const diceSetNumbers = {
flat: 'Flat' flat: 'Flat'
}; };
export const getDiceSoNicePresets = () => { export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => {
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
const getPreset = async (type, faces) => {
const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces);
if (!system.modelLoaded) {
await system.loadModel(game.dice3d.DiceFactory.loaderGLTF);
}
return {
modelFile: system.modelFile,
appearance: {
...system.appearance,
...type
}
};
};
return { return {
hope: { hope: await getPreset(diceSoNice.hope, hopeFaces),
...diceSoNice.hope, fear: await getPreset(diceSoNice.fear, fearFaces),
colorset: 'inspired', advantage: await getPreset(diceSoNice.advantage, advantageFaces),
texture: 'bloodmoon', disadvantage: await getPreset(diceSoNice.disadvantage, disadvantageFaces)
material: 'metal',
font: 'Arial Black',
system: 'standard'
},
fear: {
...diceSoNice.fear,
colorset: 'bloodmoon',
texture: 'bloodmoon',
material: 'metal',
font: 'Arial Black',
system: 'standard'
},
advantage: {
...diceSoNice.advantage,
colorset: 'bloodmoon',
texture: 'bloodmoon',
material: 'metal',
font: 'Arial Black',
system: 'standard'
},
disadvantage: {
...diceSoNice.disadvantage,
colorset: 'bloodmoon',
texture: 'bloodmoon',
material: 'metal',
font: 'Arial Black',
system: 'standard'
}
}; };
}; };

View file

@ -21,25 +21,41 @@ export default class DhAppearance extends foundry.abstract.DataModel {
foreground: new fields.ColorField({ required: true, initial: '#ffffff' }), foreground: new fields.ColorField({ required: true, initial: '#ffffff' }),
background: new fields.ColorField({ required: true, initial: '#ffe760' }), background: new fields.ColorField({ required: true, initial: '#ffe760' }),
outline: new fields.ColorField({ required: true, initial: '#000000' }), outline: new fields.ColorField({ required: true, initial: '#000000' }),
edge: new fields.ColorField({ required: true, initial: '#ffffff' }) edge: new fields.ColorField({ required: true, initial: '#ffffff' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}), }),
fear: new fields.SchemaField({ fear: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#000000' }), foreground: new fields.ColorField({ required: true, initial: '#000000' }),
background: new fields.ColorField({ required: true, initial: '#0032b1' }), background: new fields.ColorField({ required: true, initial: '#0032b1' }),
outline: new fields.ColorField({ required: true, initial: '#ffffff' }), outline: new fields.ColorField({ required: true, initial: '#ffffff' }),
edge: new fields.ColorField({ required: true, initial: '#000000' }) edge: new fields.ColorField({ required: true, initial: '#000000' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}), }),
advantage: new fields.SchemaField({ advantage: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#ffffff' }), foreground: new fields.ColorField({ required: true, initial: '#ffffff' }),
background: new fields.ColorField({ required: true, initial: '#008000' }), background: new fields.ColorField({ required: true, initial: '#008000' }),
outline: new fields.ColorField({ required: true, initial: '#000000' }), outline: new fields.ColorField({ required: true, initial: '#000000' }),
edge: new fields.ColorField({ required: true, initial: '#ffffff' }) edge: new fields.ColorField({ required: true, initial: '#ffffff' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}), }),
disadvantage: new fields.SchemaField({ disadvantage: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#000000' }), foreground: new fields.ColorField({ required: true, initial: '#000000' }),
background: new fields.ColorField({ required: true, initial: '#b30000' }), background: new fields.ColorField({ required: true, initial: '#b30000' }),
outline: new fields.ColorField({ required: true, initial: '#ffffff' }), outline: new fields.ColorField({ required: true, initial: '#ffffff' }),
edge: new fields.ColorField({ required: true, initial: '#000000' }) edge: new fields.ColorField({ required: true, initial: '#000000' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}) })
}), }),
showGenericStatusEffects: new fields.BooleanField({ showGenericStatusEffects: new fields.BooleanField({

View file

@ -1,5 +1,4 @@
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
import DHRoll from './dhRoll.mjs'; import DHRoll from './dhRoll.mjs';
export default class D20Roll extends DHRoll { export default class D20Roll extends DHRoll {
@ -141,8 +140,8 @@ export default class D20Roll extends DHRoll {
return modifiers; return modifiers;
} }
static postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
const data = super.postEvaluate(roll, config); const data = await super.postEvaluate(roll, config);
if (config.targets?.length) { if (config.targets?.length) {
config.targets.forEach(target => { config.targets.forEach(target => {
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion; const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
@ -185,7 +184,7 @@ export default class D20Roll extends DHRoll {
static async reroll(rollString, _target, message) { static async reroll(rollString, _target, message) {
let parsedRoll = game.system.api.dice.D20Roll.fromData(rollString); let parsedRoll = game.system.api.dice.D20Roll.fromData(rollString);
parsedRoll = await parsedRoll.reroll(); parsedRoll = await parsedRoll.reroll();
const newRoll = game.system.api.dice.D20Roll.postEvaluate(parsedRoll, { const newRoll = await game.system.api.dice.D20Roll.postEvaluate(parsedRoll, {
targets: message.system.targets, targets: message.system.targets,
roll: { roll: {
advantage: message.system.roll.advantage?.type, advantage: message.system.roll.advantage?.type,

View file

@ -16,14 +16,19 @@ export default class DamageRoll extends DHRoll {
for (const roll of config.roll) await roll.roll.evaluate(); for (const roll of config.roll) await roll.roll.evaluate();
} }
roll._evaluated = true; roll._evaluated = true;
const parts = config.roll.map(r => this.postEvaluate(r)); const parts = [];
for (let r of config.roll) {
const part = this.postEvaluate(r);
parts.push(part);
}
config.roll = this.unifyDamageRoll(parts); config.roll = this.unifyDamageRoll(parts);
} }
static postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
return { return {
...roll, ...roll,
...super.postEvaluate(roll.roll, config), ...(await super.postEvaluate(roll.roll, config)),
damageTypes: [...(roll.damageTypes ?? [])], damageTypes: [...(roll.damageTypes ?? [])],
roll: roll.roll, roll: roll.roll,
type: config.type, type: config.type,

View file

@ -47,7 +47,7 @@ export default class DHRoll extends Roll {
static async buildEvaluate(roll, config = {}, message = {}) { static async buildEvaluate(roll, config = {}, message = {}) {
if (config.evaluate !== false) await roll.evaluate(); if (config.evaluate !== false) await roll.evaluate();
config.roll = this.postEvaluate(roll, config); config.roll = await this.postEvaluate(roll, config);
} }
static async buildPost(roll, config, message) { static async buildPost(roll, config, message) {
@ -67,7 +67,7 @@ export default class DHRoll extends Roll {
} else config.message = await this.toMessage(roll, config); } else config.message = await this.toMessage(roll, config);
} }
static postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
return { return {
total: roll.total, total: roll.total,
formula: roll.formula, formula: roll.formula,

View file

@ -166,8 +166,8 @@ export default class DualityRoll extends D20Roll {
return modifiers; return modifiers;
} }
static postEvaluate(roll, config = {}) { static async postEvaluate(roll, config = {}) {
const data = super.postEvaluate(roll, config); const data = await super.postEvaluate(roll, config);
data.hope = { data.hope = {
dice: roll.dHope.denomination, dice: roll.dHope.denomination,
@ -198,7 +198,13 @@ export default class DualityRoll extends D20Roll {
if (roll._rallyIndex && roll.data?.parent) if (roll._rallyIndex && roll.data?.parent)
roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]); roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]);
setDiceSoNiceForDualityRoll(roll, data.advantage.type); await setDiceSoNiceForDualityRoll(
roll,
data.advantage.type,
data.hope.dice,
data.fear.dice,
data.advantage.dice
);
return data; return data;
} }
@ -220,10 +226,10 @@ export default class DualityRoll extends D20Roll {
options: { appearance: {} } options: { appearance: {} }
}; };
const diceSoNicePresets = getDiceSoNicePresets(); const diceSoNicePresets = await getDiceSoNicePresets();
const type = target.dataset.type; const type = target.dataset.type;
if (diceSoNicePresets[type]) { if (diceSoNicePresets[type]) {
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets[type] }; diceSoNiceRoll.dice[0].options = diceSoNicePresets[type];
} }
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
@ -231,7 +237,7 @@ export default class DualityRoll extends D20Roll {
await parsedRoll.evaluate(); await parsedRoll.evaluate();
const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, { const newRoll = await game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, {
targets: message.system.targets, targets: message.system.targets,
roll: { roll: {
advantage: message.system.roll.advantage?.type, advantage: message.system.roll.advantage?.type,

View file

@ -52,14 +52,13 @@ export const getCommandTarget = () => {
return target; return target;
}; };
export const setDiceSoNiceForDualityRoll = (rollResult, advantageState) => { export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, hopeFaces, fearFaces, advantageFaces) => {
const diceSoNicePresets = getDiceSoNicePresets(); const diceSoNicePresets = await getDiceSoNicePresets(hopeFaces, fearFaces, advantageFaces, advantageFaces);
rollResult.dice[0].options = { appearance: diceSoNicePresets.hope }; rollResult.dice[0].options = diceSoNicePresets.hope;
rollResult.dice[1].options = { appearance: diceSoNicePresets.fear }; //diceSoNicePresets.fear; rollResult.dice[1].options = diceSoNicePresets.fear;
if (rollResult.dice[2] && advantageState) { if (rollResult.dice[2] && advantageState) {
rollResult.dice[2].options = { rollResult.dice[2].options =
appearance: advantageState === 1 ? diceSoNicePresets.advantage : diceSoNicePresets.disadvantage advantageState === 1 ? diceSoNicePresets.advantage : diceSoNicePresets.disadvantage;
};
} }
}; };

View file

@ -443,6 +443,10 @@
} }
.field-section { .field-section {
display: flex;
flex-direction: column;
gap: 10px;
.split-section { .split-section {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
@ -455,9 +459,18 @@
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 10px; gap: 10px;
&.full-width {
grid-template-columns: 1fr 1fr 1fr 1fr;
select {
grid-column: span 3;
}
}
label { label {
align-self: center; align-self: center;
text-align: center; text-align: center;
white-space: nowrap;
} }
} }
} }

View file

@ -7,47 +7,74 @@
{{formGroup settingFields.schema.fields.dualityColorScheme value=settingFields._source.dualityColorScheme localize=true}} {{formGroup settingFields.schema.fields.dualityColorScheme value=settingFields._source.dualityColorScheme localize=true}}
<fieldset> {{#if showDiceSoNice}}
<legend>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title"}}</legend> <fieldset>
<div class="title-hint">{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}</div> <legend>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title"}}</legend>
<div class="title-hint">{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}</div>
<section class='tab-navigation'> <section class='tab-navigation'>
<div class='navigation-container'> <div class='navigation-container'>
<div class="navigation-inner-container"> <div class="navigation-inner-container">
<line-div></line-div> <line-div></line-div>
<nav class='feature-tab sheet-tabs tabs' data-group='diceSoNice'> <nav class='feature-tab sheet-tabs tabs' data-group='diceSoNice'>
{{#each tabs as |tab|}} {{#each tabs as |tab|}}
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'> <a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
{{localize tab.label}} {{localize tab.label}}
</a> </a>
{{/each}} {{/each}}
</nav> </nav>
<line-div></line-div> <line-div></line-div>
</div>
</div>
</section>
<div class="field-section">
<div class="label-container full-width">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.system"}}</label>
<select name="{{concat "diceSoNice." diceTab.key ".system"}}">
{{selectOptions diceSoNiceSystems selected=diceTab.source.system valueAttr="key" labelAttr="name" localize=true}}
</select>
</div>
<div class="split-section">
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label>
{{formInput diceTab.fields.foreground value=diceTab.source.foreground localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label>
{{formInput diceTab.fields.background value=diceTab.source.background localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label>
{{formInput diceTab.fields.outline value=diceTab.source.outline localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput diceTab.fields.edge value=diceTab.source.edge localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.colorset"}}</label>
<select name="{{concat "diceSoNice." diceTab.key ".colorset"}}">
{{selectOptions diceSoNiceColorsets selected=diceTab.source.colorset labelAttr="description" localize=true}}
</select>
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.texture"}}</label>
<select name="{{concat "diceSoNice." diceTab.key ".texture"}}">
{{selectOptions diceSoNiceTextures selected=diceTab.source.texture labelAttr="name" localize=true}}
</select>
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.material"}}</label>
<select name="{{concat "diceSoNice." diceTab.key ".material"}}">
{{selectOptions diceSoNiceMaterials selected=diceTab.source.material valueAttr="key" labelAttr="name" localize=true}}
</select>
</div>
</div> </div>
</div> </div>
</section> </fieldset>
{{/if}}
<div class="field-section">
<div class="split-section">
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label>
{{formInput diceTab.fields.foreground value=diceTab.source.foreground localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label>
{{formInput diceTab.fields.background value=diceTab.source.background localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label>
{{formInput diceTab.fields.outline value=diceTab.source.outline localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput diceTab.fields.edge value=diceTab.source.edge localize=true}}
</div>
</div>
</div>
</fieldset>
<footer class="form-footer"> <footer class="form-footer">
<button data-action="reset"> <button data-action="reset">