Changed how conversion gain modifiers work, so nextAt will be correct

This commit is contained in:
thepaperpilot 2022-03-29 21:26:01 -05:00
parent 84ed880533
commit f40765e4de

View file

@ -1,7 +1,10 @@
import { GenericLayer } from "game/layers";
import Decimal, { DecimalSource } from "util/bignum";
import { isFunction } from "util/common";
import {
Computable,
convertComputable,
DoNotCache,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
@ -20,7 +23,7 @@ export interface ConversionOptions {
buyMax?: Computable<boolean>;
roundUpCost?: Computable<boolean>;
convert?: VoidFunction;
modifyGainAmount?: (gain: DecimalSource) => DecimalSource;
gainModifier?: GainModifier;
}
export interface BaseConversion {
@ -47,6 +50,11 @@ export type GenericConversion = Replace<
}
>;
export interface GainModifier {
apply: (gain: DecimalSource) => DecimalSource;
revert: (gain: DecimalSource) => DecimalSource;
}
export function createConversion<T extends ConversionOptions>(
optionsFunc: () => T & ThisType<Conversion<T>>
): Conversion<T> {
@ -54,25 +62,33 @@ export function createConversion<T extends ConversionOptions>(
const conversion: T = optionsFunc();
if (conversion.currentGain == null) {
conversion.currentGain = computed(() =>
conversion.scaling.currentGain(conversion as GenericConversion)
);
conversion.currentGain = computed(() => {
let gain = conversion.gainModifier
? conversion.gainModifier.apply(
conversion.scaling.currentGain(conversion as GenericConversion)
)
: conversion.scaling.currentGain(conversion as GenericConversion);
gain = Decimal.floor(gain).max(0);
if (!conversion.buyMax) {
gain = gain.min(1);
}
return gain;
});
}
if (conversion.nextAt == null) {
conversion.nextAt = computed(() =>
conversion.scaling.nextAt(conversion as GenericConversion)
);
conversion.nextAt = computed(() => {
let next = conversion.scaling.nextAt(conversion as GenericConversion);
if (conversion.roundUpCost) next = Decimal.ceil(next);
return next;
});
}
if (conversion.convert == null) {
conversion.convert = function () {
conversion.gainResource.value = Decimal.add(
conversion.gainResource.value,
conversion.modifyGainAmount
? conversion.modifyGainAmount(
unref((conversion as GenericConversion).currentGain)
)
: unref((conversion as GenericConversion).currentGain)
unref((conversion as GenericConversion).currentGain)
);
// TODO just subtract cost?
conversion.baseResource.value = 0;
@ -107,25 +123,17 @@ export function createLinearScaling(
return 0;
}
let gain = Decimal.sub(conversion.baseResource.value, unref(base))
return Decimal.sub(conversion.baseResource.value, unref(base))
.sub(1)
.times(unref(coefficient))
.add(1)
.floor()
.max(0);
if (!conversion.buyMax) {
gain = gain.min(1);
}
return gain;
.add(1);
},
nextAt(conversion) {
let next = Decimal.add(unref(conversion.currentGain), 1)
.times(unref(coefficient))
.add(unref(base))
.max(unref(base));
if (conversion.roundUpCost) next = next.ceil();
return next;
let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1);
if (conversion.gainModifier) {
next = conversion.gainModifier.revert(next);
}
return Decimal.times(next, unref(coefficient)).add(unref(base)).max(unref(base));
}
};
}
@ -138,27 +146,21 @@ export function createPolynomialScaling(
): ScalingFunction {
return {
currentGain(conversion) {
let gain = Decimal.div(conversion.baseResource.value, unref(base))
.pow(unref(exponent))
.floor()
.max(0);
const gain = Decimal.div(conversion.baseResource.value, unref(base)).pow(
unref(exponent)
);
if (gain.isNan()) {
return new Decimal(0);
}
if (!conversion.buyMax) {
gain = gain.min(1);
}
return gain;
},
nextAt(conversion) {
let next = Decimal.add(unref(conversion.currentGain), 1)
.root(unref(exponent))
.times(unref(base))
.max(unref(base));
if (conversion.roundUpCost) next = next.ceil();
return next;
let next: DecimalSource = Decimal.add(unref(conversion.currentGain), 1);
if (conversion.gainModifier) {
next = conversion.gainModifier.revert(next);
}
return Decimal.root(next, unref(exponent)).times(unref(base)).max(unref(base));
}
};
}
@ -188,8 +190,10 @@ export function createIndependentConversion<S extends ConversionOptions>(
);
}
setDefault(conversion, "convert", function () {
conversion.gainResource.value = conversion.modifyGainAmount
? conversion.modifyGainAmount(unref((conversion as GenericConversion).currentGain))
conversion.gainResource.value = conversion.gainModifier
? conversion.gainModifier.apply(
unref((conversion as GenericConversion).currentGain)
)
: unref((conversion as GenericConversion).currentGain);
// TODO just subtract cost?
// Maybe by adding a cost function to scaling and nextAt just calls the cost function
@ -250,3 +254,34 @@ export function addHardcap(
currentGain: conversion => Decimal.min(scaling.currentGain(conversion), unref(cap))
};
}
export function createAdditiveModifier(addend: Computable<DecimalSource>): GainModifier {
const processedAddend = convertComputable(addend);
return {
apply: gain => Decimal.add(gain, unref(processedAddend)),
revert: gain => Decimal.sub(gain, unref(processedAddend))
};
}
export function createMultiplicativeModifier(multiplier: Computable<DecimalSource>): GainModifier {
const processedMultiplier = convertComputable(multiplier);
return {
apply: gain => Decimal.times(gain, unref(processedMultiplier)),
revert: gain => Decimal.div(gain, unref(processedMultiplier))
};
}
export function createExponentialModifier(exponent: Computable<DecimalSource>): GainModifier {
const processedExponent = convertComputable(exponent);
return {
apply: gain => Decimal.pow(gain, unref(processedExponent)),
revert: gain => Decimal.root(gain, unref(processedExponent))
};
}
export function createSequentialModifier(...modifiers: GainModifier[]): GainModifier {
return {
apply: gain => modifiers.reduce((gain, modifier) => modifier.apply(gain), gain),
revert: gain => modifiers.reduceRight((gain, modifier) => modifier.revert(gain), gain)
};
}