Settings

From changing the layout to toggling the automation of a feature, a Setting is anything the user can freely control.

Requirements

  • Allow the player to easily change settings
  • Boolean, Number and String options
  • Extendable with different types
  • Multiple choice and Range settings
  • Set a default value
  • Lock settings and even options behind requirements
  • Save & Load

Usage

const settings = new Settings();
settings.add(
new MultipleChoiceSetting(SettingId.ExampleSetting, "Example setting", [
new SettingOption<number>("Option 1", 1),
new SettingOption<number>("Option 2", 2),
new SettingOption<number>("Option 3", 3),
], 2));
settings.setSetting(SettingId.ExampleSetting, 4); // Not a valid option, nothing happens
settings.setSetting(SettingId.ExampleSetting, 1);
// Passing <number> is optional.
const exampleSetting = settings.getSetting<number>(SettingId.ExampleSetting);
if (exampleSetting?.value === 1) {
// Do something
}

Implementation

The abstract Setting class is the base for all custom settings. It uses TypeScript Generics which allows the setting value to be any type you like.

src/ig-template/features/settings/Setting.ts
export abstract class Setting<T> {
id: SettingId;
displayName: string;
options: SettingOption<T>[];
defaultValue: T;
value: T;
requirement: Requirement;
set(value: T): void {
if (!this.canAccess) {
return;
}
if (this.validValue(value)) {
this.value = value;
} else {
console.warn(`${value} is not a valid value for setting ${this.id}. It could be that the option is not yet unlocked.`);
}
}
validValue(value: T): boolean {
const option = this.options.find((option) => option.value === value);
if (option == undefined || !option.canAccess) {
return false;
}
return true;
}
isSelected(value: T): boolean {
return this.value === value;
}
get canAccess(): boolean {
return this.requirement.isCompleted
}
}

An example extension is the RangeSetting, which allows its value to be any float between an min and max.

src/ig-template/features/settings/RangeSetting.ts
export class RangeSetting extends Setting<number> {
min: number;
max: number;
constructor(id: SettingId, displayName: string, min: number, max: number, defaultValue: number, requirement?: Requirement) {
// Pass the default value as an option
super(id, displayName, [new SettingOption<number>("Default", defaultValue)], defaultValue, requirement);
if (min >= max || max <= min) {
throw new RangeError(`Invalid range settings (min: ${min}, max: ${max}) for setting ${this.id}`)
}
this.min = min;
this.max = max;
if (!this.validValue(this.defaultValue)) {
throw new RangeError(`${this.defaultValue} is not a valid value for setting ${this.id}.`);
}
}
validValue(value: number): boolean {
return value >= this.min && value <= this.max;
}
}

See also