zischenstand

This commit is contained in:
centron\schwoerer
2025-11-14 14:52:43 +01:00
parent 30aa03c6db
commit f054a31b20
8733 changed files with 900639 additions and 0 deletions

View File

@@ -0,0 +1,465 @@
import { GeneralSettings } from "../../interface/GeneralSettings.js";
import { ObjectsInteractionsFX as OIF } from "../../ObjectsInteractionsFX.js";
export class SettingsSkeleton extends FormApplication
{
static get defaultOptions() {
const DefaultOptions = super.defaultOptions;
const OverrideOptions = {
closeOnSubmit: false,
height: 'auto',
with: 600,
submitOnChange: true,
template: OIF.TEMPLATES.SETTINGS_SKELETON
};
const MergedOptions = foundry.utils.mergeObject(DefaultOptions, OverrideOptions);
return MergedOptions;
}
static Settings = {}
////////////////////////////////////////////////////////////
// Prototype function used to register custom settings
////////////////////////////////////////////////////////////
static _protoRegister(config, name, options)
{
if (SettingsSkeleton.Settings[config] == undefined)
{
SettingsSkeleton.Settings[config] = {};
}
// Prepare the internal foundry setting options
let FoundrySettingOptions = {
name: options['name'],
hint: options['hint'],
scope: options['scope'],
default: options['default'],
config: false
}
// Detect and convert custom type to be used by foundry
if (options['type'] != undefined)
{
// Sets a value inside option, named after the type name, as true
// This is used inside .hbs templates to select which option
// display/editor will be shown
options[options['type']] = true;
// Convert type to a foundry compatible
switch (options['type'])
{
case 'checkbox':
FoundrySettingOptions['type'] = Boolean;
break;
case 'slider':
FoundrySettingOptions['type'] = Number;
FoundrySettingOptions['range'] = options['range'];
break;
case 'string':
FoundrySettingOptions['type'] = String;
break;
case 'dropdown':
FoundrySettingOptions['type'] = String;
FoundrySettingOptions['choices'] = options['choices'];
break;
default:
console.error(`Cannot register setting, ${options['type']} is not a valid type`);
break;
}
}
// Register setting inside foundry
game.settings.register(OIF.ID, name, FoundrySettingOptions);
// Get currently set value to be displayed
options['value'] = game.settings.get(OIF.ID, name);
// Dropdown types are handled differently
if (options['type'] == 'dropdown')
{
// Iterate through the choices and set them as selected
// or not
options['choices'].forEach((element) => {
if (element.value == options['value'])
{
element['selected'] = true;
}
else
{
element['selected'] = false;
}
})
}
// Set the ID to be used as reference later
options['referenceID'] = name;
// Check there are required modules
let RequiredModule = options['requiredModule'];
if (RequiredModule != undefined)
{
// Check if the required module is active
if (!RequiredModule.active)
{
options['disabled'] = true;
options['disabledMessage'] = game.i18n.localize('OIF.Settings.ModuleRequired').replace("${module}", `"${RequiredModule.name}"`);
}
}
// Register inside OIF
SettingsSkeleton.Settings[config][name] = options;
}
////////////////////////////////////////////////////////////
// Prototype function used to get custom settings
////////////////////////////////////////////////////////////
static _protoGet(config, name)
{
let CurrentSetting = SettingsSkeleton.Settings[config][name];
switch (CurrentSetting['type'])
{
case 'checkbox':
return CurrentSetting['value'] && !CurrentSetting['disabled'];
break;
case 'slider':
case 'string':
case 'dropdown':
return CurrentSetting['disabled'] ? CurrentSetting['default'] : CurrentSetting['value'];
break;
default:
return CurrentSetting['value'];
break;
}
}
////////////////////////////////////////////////////////////
// Internal function that checks if restart is required
////////////////////////////////////////////////////////////
async _checkForRestart(config, id)
{
if (SettingsSkeleton.Settings[config][id]['restart'] != null && SettingsSkeleton.Settings[config][id]['restart'] != undefined && SettingsSkeleton.Settings[config][id]['restart'])
{
SettingsSkeleton.Settings[config][id]['requiresRestart'] = true;
SettingsSkeleton.Settings[config][id]['restartMessage'] = 'OIF.Settings.RestartRequired';
}
}
////////////////////////////////////////////////////////////
// Internal function for handling Checkbox interactions
////////////////////////////////////////////////////////////
async _handleCheckBoxInteraction(config, event)
{
let ClickedElement = $(event.currentTarget)[0];
game.settings.set(OIF.ID, ClickedElement.id, ClickedElement.checked);
SettingsSkeleton.Settings[config][ClickedElement.id]['value'] = ClickedElement.checked;
this._checkForRestart(config, ClickedElement.id);
SettingsSkeleton._protoUpdateSettings(config);
this.render();
}
////////////////////////////////////////////////////////////
// Internal function for handling Slider interactions
////////////////////////////////////////////////////////////
async _handleSliderInteraction(config, event)
{
let ClickedElement = $(event.currentTarget)[0];
game.settings.set(OIF.ID, ClickedElement.id, ClickedElement.value);
// Check value
let CurrentValue = Number(ClickedElement.value);
if (CurrentValue > ClickedElement.max)
{
CurrentValue = ClickedElement.max;
}
else if (CurrentValue < ClickedElement.min)
{
CurrentValue = ClickedElement.min;
}
SettingsSkeleton.Settings[config][ClickedElement.id]['value'] = CurrentValue;
this._checkForRestart(config, ClickedElement.id);
SettingsSkeleton._protoUpdateSettings(config);
this.render();
}
////////////////////////////////////////////////////////////
// Internal function for handling String interactions
////////////////////////////////////////////////////////////
async _handleStringInteraction(config, event)
{
let ClickedElement = $(event.currentTarget)[0];
game.settings.set(OIF.ID, ClickedElement.id, ClickedElement.value);
SettingsSkeleton.Settings[config]['value'] = ClickedElement.value;
this._checkForRestart(config, ClickedElement.id);
SettingsSkeleton._protoUpdateSettings(config);
this.render();
}
////////////////////////////////////////////////////////////
// Internal function for handling Dropdown interactions
////////////////////////////////////////////////////////////
async _handleDropdownInteraction(config, event)
{
let ClickedElement = $(event.currentTarget)[0];
game.settings.set(OIF.ID, ClickedElement.id, ClickedElement.value);
SettingsSkeleton.Settings[config][ClickedElement.id]['value'] = ClickedElement.value;
SettingsSkeleton.Settings[config][ClickedElement.id]['choices'].forEach(function(element)
{
element['selected'] = false;
if (element.value == ClickedElement.value)
{
element['selected'] = true;
}
});
this._checkForRestart(config, ClickedElement.id);
SettingsSkeleton._protoUpdateSettings(config);
this.render();
}
////////////////////////////////////////////////////////////
// Prototype function that updates settings
////////////////////////////////////////////////////////////
static _protoUpdateSettings(config)
{
for (const key in SettingsSkeleton.Settings[config])
{
// Check if current element has a dependecy
let CurrentSetting = SettingsSkeleton.Settings[config][key];
let CurrentDependencies = CurrentSetting['dependsOn'];
let CurrentUndependencies = CurrentSetting['excludesOn'];
// Check if setting requires a module to be used
let RequiredModule = CurrentSetting.requiredModule;
if (RequiredModule != null && RequiredModule != undefined)
{
// Check if required module isn't loaded
if (!RequiredModule.active)
{
CurrentSetting['disabled'] = true;
CurrentSetting['disabledMessage'] = game.i18n.localize('OIF.Settings.ModuleRequired').replace("${module}", `"${RequiredModule.name}"`);
continue;
}
}
// Check if setting has dependencies
let AllRequirementsMet = true;
if (CurrentDependencies != null && CurrentDependencies != undefined)
{
// Loop through all dependencies to check if the are Boolean
let BoolCheck = true;
for (let index = 0; index < CurrentDependencies.length; index++)
{
// Check if the current dependency is a Boolean
const CurrentDependency = CurrentDependencies[index];
if (SettingsSkeleton.Settings[config][CurrentDependency]['type'] != 'checkbox')
{
BoolCheck = false;
}
}
// If one of the dependencies isn't a Boolean, skip the rest
if (BoolCheck == false)
{
console.error(`OIF | ${config} | ${key} | Dependency isn't a Boolean`);
continue;
}
// Check if the dependecies exist
let CurrentRequirements = [];
for (let index = 0; index < CurrentDependencies.length; index++)
{
// Check if the current dependency exists
const CurrentDependency = CurrentDependencies[index];
let CurrentRequirement = SettingsSkeleton.Settings[config][CurrentDependency];
if (CurrentRequirement != null && CurrentRequirement != undefined)
{
CurrentRequirements.push(CurrentRequirement);
}
}
// Check if all dependencies are met
for (let index = 0; index < CurrentRequirements.length; index++)
{
// Check if the current dependency is met
const CurrentRequirement = CurrentRequirements[index];
if (!SettingsSkeleton._protoGet(config, CurrentRequirement.referenceID))
{
AllRequirementsMet = false;
}
}
// If all dependencies are met, enable the setting
if (AllRequirementsMet)
{
CurrentSetting['disabled'] = false;
CurrentSetting['disabledMessage'] = '';
}
// If not, disable the setting
else
{
// Create a string with all dependencies, so the user knows which ones are missing
// separeted all dependencies with a comma, except the last one, which is separeted with 'and'
let DependenciesString = '';
for (let index = 0; index < CurrentRequirements.length; index++)
{
const CurrentRequirement = CurrentRequirements[index];
if (index == CurrentRequirements.length - 1)
{
DependenciesString += ` and "${game.i18n.localize(CurrentRequirement['name'])}"`;
}
else
{
DependenciesString += `"${game.i18n.localize(CurrentRequirement['name'])}", `;
}
}
// Disable the setting and add a message
CurrentSetting['disabled'] = true;
CurrentSetting['disabledMessage'] = game.i18n.localize('OIF.Settings.OptionRequired').replace("${option}", DependenciesString);
}
}
// Check if setting has undependencies
if (CurrentUndependencies != null && CurrentUndependencies != undefined && AllRequirementsMet)
{
// Loop through all undependencies to check if the are Boolean
let BoolCheck = true;
for (let index = 0; index < CurrentUndependencies.length; index++)
{
// Check if the current undependency is a Boolean
const CurrentUndependency = CurrentUndependencies[index];
if (SettingsSkeleton.Settings[config][CurrentUndependency]['type'] != 'checkbox')
{
BoolCheck = false;
}
}
// If one of the undependencies isn't a Boolean, skip the rest
if (BoolCheck == false)
{
console.error(`OIF | ${config} | ${key} | Undependency isn't a Boolean`);
continue;
}
// Check if the undependecies exist
let CurrentUnrequirements = [];
for (let index = 0; index < CurrentUndependencies.length; index++)
{
// Check if the current undependency exists
const CurrentUndependency = CurrentUndependencies[index];
let CurrentRequirement = SettingsSkeleton.Settings[config][CurrentUndependency];
if (CurrentRequirement != null && CurrentRequirement != undefined)
{
CurrentUnrequirements.push(CurrentRequirement);
}
}
// Check if all undependencies are met
let AllUnrequirementsMet = true;
for (let index = 0; index < CurrentUnrequirements.length; index++)
{
// Check if the current undependency is met
const CurrentUnrequirement = CurrentUnrequirements[index];
if (SettingsSkeleton._protoGet(config, CurrentUnrequirement.referenceID))
{
AllUnrequirementsMet = false;
}
}
// If all undependencies are met, enable the setting
if (AllUnrequirementsMet)
{
CurrentSetting['disabled'] = false;
CurrentSetting['disabledMessage'] = '';
}
// If not, disable the setting
else
{
// Create a string with all undependencies, so the user knows which ones are missing
// separeted all undependencies with a comma, except the last one, which is separeted with 'and'
let UndependenciesString = '';
for (let index = 0; index < CurrentUnrequirements.length; index++)
{
const CurrentUnrequirement = CurrentUnrequirements[index];
if (index == CurrentUnrequirements.length - 1)
{
UndependenciesString += ` and "${game.i18n.localize(CurrentUnrequirement['name'])}"`;
}
else
{
UndependenciesString += `"${game.i18n.localize(CurrentUnrequirement['name'])}", `;
}
}
// Disable the setting and add a message
CurrentSetting['disabled'] = true;
CurrentSetting['disabledMessage'] = game.i18n.localize('OIF.Settings.OptionUnrequired').replace("${option}", UndependenciesString);
}
}
}
Hooks.call(OIF.HOOKS.CHANGE_SETTINGS, SettingsSkeleton.Settings[config]);
}
////////////////////////////////////////////////////////////
// Prototype function to get data for rendering .hbs
////////////////////////////////////////////////////////////
static _protoGetData(config, options)
{
// Only return settings that are in the 'world' scope
// for GMs
let ReturnedSettings = {};
for (const key in SettingsSkeleton.Settings[config])
{
let CurrentSetting = SettingsSkeleton.Settings[config][key];
if (CurrentSetting.scope == 'world')
{
if (game.user.isGM)
{
ReturnedSettings[key] = CurrentSetting;
}
else
{
continue;
}
}
else
{
ReturnedSettings[key] = CurrentSetting;
}
}
return { settings: ReturnedSettings }
}
////////////////////////////////////////////////////////////
// Activate listeners
////////////////////////////////////////////////////////////
activateListeners(html, config)
{
super.activateListeners(html);
// Checkbox
html.on('change', 'input[type=checkbox]', this._handleCheckBoxInteraction.bind(this, config));
// Slider
html.on('change', 'input[type=range]', this._handleSliderInteraction.bind(this, config));
html.on('change', 'input[type=number]', this._handleSliderInteraction.bind(this, config));
// String
html.on('change', 'input[type=text]', this._handleStringInteraction.bind(this, config));
// Dropdown
html.on('change', 'select', this._handleDropdownInteraction.bind(this, config));
}
}