Skip to content

Instantly share code, notes, and snippets.

@Alpvax
Last active January 23, 2019 19:34
Show Gist options
  • Save Alpvax/cb0bf68dcc3b82d2b128cb713df41bfb to your computer and use it in GitHub Desktop.
Save Alpvax/cb0bf68dcc3b82d2b128cb713df41bfb to your computer and use it in GitHub Desktop.
Attribute
export const enum ModifyType {BASE_ADD, BASE_MULTIPLY, STACKING_MULTIPLY};
interface Modifier<T extends ModifyType> {
amount: number;
modType: T;
};
interface ChangeListener {
(attributeInstance: Attribute, oldValue: number): void;
}
export default class Attribute {
private baseValue: number;
private readonly modifiers: {
[mType in ModifyType]: Array<Modifier<mType>>;
};
private readonly listeners: ChangeListener[];
private cachedValue: number;
private dirty: boolean;
private min: number | void;
private max: number | void;
constructor(base: number, min?: number, max?: number) {
this.baseValue = base;
this.min = min;
this.max = max;
this.modifiers = [[], [], []];
this.dirty = true;
this.listeners = [];
}
private markDirty(): void {
this.dirty = true;
this.listeners.forEach((l) => l(this, this.cachedValue));
}
setRange(min: number, max?: number, base?: number): void {
if (max != undefined && min >= max) {
throw Error(`Cannot set minimum attribute value higher than maximum: {min: ${min}, max: ${max}}`);
}
this.min = min;
if (max != undefined) {
this.max = max;
}
if (base != undefined) {
this.baseValue = base;
this.markDirty();
} else if (this.min > this.value || this.max < this.value) {
this.markDirty()
}
}
/*
* @param {function} listener A function called whenever the value is changed. Will recieve the attribute instance, and the old value as arguments.
* The new value can be calculated and retrieved with `attribute.value`
* @returns the same `listener` passed in for convenience.
*/
onChange(listener): ChangeListener {
this.listeners.push(listener);
return listener;
}
offChange(listener): void {
const index = this.listeners.indexOf(listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
}
get value(): number {
if(this.dirty) {
let val = this.baseValue;
let basemult = 1;
let mult = 1;
this.modifiers[ModifyType.BASE_ADD].forEach((m) => val += m.amount);
this.modifiers[ModifyType.BASE_MULTIPLY].forEach((m) => basemult += m.amount);
this.modifiers[ModifyType.STACKING_MULTIPLY].forEach((m) => mult *= m.amount);
this.cachedValue = val * basemult * mult;
this.dirty = false;
}
return this.cachedValue;
}
/*
* @param {Object} modifier An object containing a "key" property and one or more of the following keys with numerical values:
* @param {number} [modifier.baseAdd] Number to add to the base value before further calculations.
* @param {number} [modifier.baseMult] Number to multiply the modified base value by. Does not stack, so 2 `2x` multipliers will be a `3x` multiplier in total.
* @param {number} [modifier.stackingMult] Number to multiple the final value by. Stacks, so 2 `2x` multipliers will be a `4x` multiplier in total
* @param {number} [baseAdd] If `modifier` was just the key, this will be the value used for the modifier baseAdd property
* @param {number} [baseMult] If `modifier` was just the key, this will be the value used for the modifier baseMult property
* @param {number} [stackingMult] If `modifier` was just the key, this will be the value used for the modifier stackingMult property
*
* @example //Will increase the value by 3
* attribute.addModifier({key: "exampleModifierKey", baseAdd: 3});
* //Will multiply the (modified) base value by 2 (does not stack, so 2 *2 multipliers will be a x3 multiplier in total)
* attribute.addModifier({key: "exampleModifierKey2", baseMult: 2});
* //Will multiply the final value by 4 (stacks, so 2 *2 multipliers will be a *4 multiplier in total)
* attribute.addModifier({key: "exampleModifierKey3", stackingMult: 4});
*/
addModifier<T extends ModifyType>(modifier: Modifier<T>): void {
(this.modifiers[modifier.modType] as Modifier<T>[]).push(modifier);
this.markDirty();
}
removeModifier<T extends ModifyType>(modifier: Modifier<T>): boolean {
let list = this.modifiers[modifier.modType] as Modifier<T>[];
const index = list.indexOf(modifier);
let flag = index > -1;
if (flag) {
list.splice(index, 1);
this.markDirty();
}
return flag;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment