diff --git a/src/App.vue b/src/App.vue index 9613f04..6a365ef 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,18 +1,25 @@ - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/components/Layer.vue b/src/components/Layer.vue index c00cf8a..1857535 100644 --- a/src/components/Layer.vue +++ b/src/components/Layer.vue @@ -1,5 +1,6 @@ - + + ❌ ([]); + onErrorCaptured((err, instance, info) => { + console.warn(`Error caught in "${props.name}" layer`, err, instance, info); + errors.value.push( + err instanceof Error ? (err as Error) : new Error(JSON.stringify(err)) + ); + return false; + }); + return { component, minimizedComponent, showGoBack, updateNodes, unref, - goBack + goBack, + errors }; } }); diff --git a/src/data/common.tsx b/src/data/common.tsx index e7a9db0..786afa4 100644 --- a/src/data/common.tsx +++ b/src/data/common.tsx @@ -459,7 +459,7 @@ export function createFormulaPreview( const processedShowPreview = convertComputable(showPreview); const processedPreviewAmount = convertComputable(previewAmount); if (!formula.hasVariable()) { - throw new Error("Cannot create formula preview if the formula does not have a variable"); + console.error("Cannot create formula preview if the formula does not have a variable"); } return jsx(() => { if (unref(processedShowPreview)) { diff --git a/src/features/tabs/tabFamily.ts b/src/features/tabs/tabFamily.ts index 2ca544b..aa7fafc 100644 --- a/src/features/tabs/tabFamily.ts +++ b/src/features/tabs/tabFamily.ts @@ -151,8 +151,7 @@ export function createTabFamily( optionsFunc?: OptionsFunc ): TabFamily { if (Object.keys(tabs).length === 0) { - console.warn("Cannot create tab family with 0 tabs"); - throw new Error("Cannot create tab family with 0 tabs"); + console.error("Cannot create tab family with 0 tabs"); } const selected = persistent(Object.keys(tabs)[0], false); diff --git a/src/game/formulas/formulas.ts b/src/game/formulas/formulas.ts index 144dc0d..37410cb 100644 --- a/src/game/formulas/formulas.ts +++ b/src/game/formulas/formulas.ts @@ -2,7 +2,7 @@ import { Resource } from "features/resources/resource"; import { NonPersistent } from "game/persistence"; import Decimal, { DecimalSource, format } from "util/bignum"; import { Computable, ProcessedComputable, convertComputable } from "util/computed"; -import { ComputedRef, Ref, computed, ref, unref } from "vue"; +import { Ref, computed, ref, unref } from "vue"; import * as ops from "./operations"; import type { EvaluateFunction, @@ -104,7 +104,7 @@ export abstract class InternalFormula { if (inputs.length !== 1) { - throw new Error("Evaluate function is required if inputs is not length 1"); + console.error("Evaluate function is required if inputs is not length 1"); } return { inputs: inputs as T, @@ -250,7 +250,8 @@ export abstract class InternalFormula (value = func(value))); @@ -1329,14 +1335,15 @@ export default class Formula< ) { this.integralFormula = this; } else { - throw new Error("Cannot integrate formula without variable"); + console.error("Cannot integrate formula without variable"); + return Formula.constant(0); } } return this.integralFormula; } else { // "Inner" part of the formula if (this.applySubstitution == null) { - throw new Error("Cannot have two complex operations in an integrable formula"); + console.error("Cannot have two complex operations in an integrable formula"); } stack.push((variable: GenericFormula) => // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -1353,7 +1360,8 @@ export default class Formula< ) { return this; } else { - throw new Error("Cannot integrate formula without variable"); + console.error("Cannot integrate formula without variable"); + return Formula.constant(0); } } } @@ -1429,9 +1437,10 @@ export function calculateMaxAffordable( let affordable; if (unref(computedSpendResources)) { if (!formula.isIntegrable() || !formula.isIntegralInvertible()) { - throw new Error( + console.error( "Cannot calculate max affordable of formula with non-invertible integral" ); + return 0; } affordable = Decimal.floor( formula.invertIntegral(Decimal.add(resource.value, formula.evaluateIntegral())) @@ -1441,7 +1450,8 @@ export function calculateMaxAffordable( } } else { if (!formula.isInvertible()) { - throw new Error("Cannot calculate max affordable of non-invertible formula"); + console.error("Cannot calculate max affordable of non-invertible formula"); + return 0; } affordable = Decimal.floor(formula.invert(resource.value)); if (summedPurchases == null) { @@ -1501,9 +1511,10 @@ export function calculateCost( if (spendResources) { if (Decimal.gt(amountToBuy, summedPurchases)) { if (!formula.isIntegrable()) { - throw new Error( + console.error( "Cannot calculate cost with spending resources of non-integrable formula" ); + return 0; } cost = Decimal.sub(formula.evaluateIntegral(newValue), formula.evaluateIntegral()); } diff --git a/src/game/formulas/operations.ts b/src/game/formulas/operations.ts index 0e39d58..56b2af1 100644 --- a/src/game/formulas/operations.ts +++ b/src/game/formulas/operations.ts @@ -12,17 +12,20 @@ export function invertNeg(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.neg(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateNeg(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } return Formula.neg(lhs.getIntegralFormula(stack)); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function applySubstitutionNeg(value: GenericFormula) { @@ -35,24 +38,28 @@ export function invertAdd(value: DecimalSource, lhs: FormulaSource, rhs: Formula } else if (hasVariable(rhs)) { return rhs.invert(Decimal.sub(value, unrefFormulaSource(lhs))); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateAdd(stack: SubstitutionStack, lhs: FormulaSource, rhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.times(rhs, lhs.innermostVariable ?? 0).add(x); } else if (hasVariable(rhs)) { if (!rhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = rhs.getIntegralFormula(stack); return Formula.times(lhs, rhs.innermostVariable ?? 0).add(x); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function integrateInnerAdd( @@ -62,18 +69,21 @@ export function integrateInnerAdd( ) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.add(x, rhs); } else if (hasVariable(rhs)) { if (!rhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = rhs.getIntegralFormula(stack); return Formula.add(x, lhs); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertSub(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { @@ -82,24 +92,28 @@ export function invertSub(value: DecimalSource, lhs: FormulaSource, rhs: Formula } else if (hasVariable(rhs)) { return rhs.invert(Decimal.sub(unrefFormulaSource(lhs), value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateSub(stack: SubstitutionStack, lhs: FormulaSource, rhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.sub(x, Formula.times(rhs, lhs.innermostVariable ?? 0)); } else if (hasVariable(rhs)) { if (!rhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = rhs.getIntegralFormula(stack); return Formula.times(lhs, rhs.innermostVariable ?? 0).sub(x); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function integrateInnerSub( @@ -109,18 +123,21 @@ export function integrateInnerSub( ) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.sub(x, rhs); } else if (hasVariable(rhs)) { if (!rhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = rhs.getIntegralFormula(stack); return Formula.sub(x, lhs); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertMul(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { @@ -129,24 +146,28 @@ export function invertMul(value: DecimalSource, lhs: FormulaSource, rhs: Formula } else if (hasVariable(rhs)) { return rhs.invert(Decimal.div(value, unrefFormulaSource(lhs))); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateMul(stack: SubstitutionStack, lhs: FormulaSource, rhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.times(x, rhs); } else if (hasVariable(rhs)) { if (!rhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = rhs.getIntegralFormula(stack); return Formula.times(x, lhs); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function applySubstitutionMul( @@ -159,7 +180,8 @@ export function applySubstitutionMul( } else if (hasVariable(rhs)) { return Formula.div(value, lhs); } - throw new Error("Could not apply substitution due to no input being a variable"); + console.error("Could not apply substitution due to no input being a variable"); + return Formula.constant(0); } export function invertDiv(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { @@ -168,24 +190,28 @@ export function invertDiv(value: DecimalSource, lhs: FormulaSource, rhs: Formula } else if (hasVariable(rhs)) { return rhs.invert(Decimal.div(unrefFormulaSource(lhs), value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateDiv(stack: SubstitutionStack, lhs: FormulaSource, rhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.div(x, rhs); } else if (hasVariable(rhs)) { if (!rhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = rhs.getIntegralFormula(stack); return Formula.div(lhs, x); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function applySubstitutionDiv( @@ -198,32 +224,37 @@ export function applySubstitutionDiv( } else if (hasVariable(rhs)) { return Formula.mul(value, lhs); } - throw new Error("Could not apply substitution due to no input being a variable"); + console.error("Could not apply substitution due to no input being a variable"); + return Formula.constant(0); } export function invertRecip(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.recip(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateRecip(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.ln(x); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertLog10(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.pow10(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } function internalIntegrateLog10(lhs: DecimalSource) { @@ -235,13 +266,15 @@ function internalInvertIntegralLog10(value: DecimalSource, lhs: FormulaSource) { const numerator = ln10.times(value); return lhs.invert(numerator.div(numerator.div(Math.E).lambertw())); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateLog10(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } return new Formula({ inputs: [lhs.getIntegralFormula(stack)], @@ -249,7 +282,8 @@ export function integrateLog10(stack: SubstitutionStack, lhs: FormulaSource) { invert: internalInvertIntegralLog10 }); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertLog(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { @@ -258,7 +292,8 @@ export function invertLog(value: DecimalSource, lhs: FormulaSource, rhs: Formula } else if (hasVariable(rhs)) { return rhs.invert(Decimal.root(unrefFormulaSource(lhs), value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } function internalIntegrateLog(lhs: DecimalSource, rhs: DecimalSource) { @@ -270,13 +305,15 @@ function internalInvertIntegralLog(value: DecimalSource, lhs: FormulaSource, rhs const numerator = Decimal.ln(unrefFormulaSource(rhs)).times(value); return lhs.invert(numerator.div(numerator.div(Math.E).lambertw())); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateLog(stack: SubstitutionStack, lhs: FormulaSource, rhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } return new Formula({ inputs: [lhs.getIntegralFormula(stack), rhs], @@ -284,14 +321,16 @@ export function integrateLog(stack: SubstitutionStack, lhs: FormulaSource, rhs: invert: internalInvertIntegralLog }); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertLog2(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.pow(2, value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } function internalIntegrateLog2(lhs: DecimalSource) { @@ -303,13 +342,15 @@ function internalInvertIntegralLog2(value: DecimalSource, lhs: FormulaSource) { const numerator = Decimal.ln(2).times(value); return lhs.invert(numerator.div(numerator.div(Math.E).lambertw())); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateLog2(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } return new Formula({ inputs: [lhs.getIntegralFormula(stack)], @@ -317,14 +358,16 @@ export function integrateLog2(stack: SubstitutionStack, lhs: FormulaSource) { invert: internalInvertIntegralLog2 }); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertLn(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.exp(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } function internalIntegrateLn(lhs: DecimalSource) { @@ -335,13 +378,15 @@ function internalInvertIntegralLn(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.div(value, Decimal.div(value, Math.E).lambertw())); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateLn(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } return new Formula({ inputs: [lhs.getIntegralFormula(stack)], @@ -349,7 +394,8 @@ export function integrateLn(stack: SubstitutionStack, lhs: FormulaSource) { invert: internalInvertIntegralLn }); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertPow(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { @@ -358,43 +404,50 @@ export function invertPow(value: DecimalSource, lhs: FormulaSource, rhs: Formula } else if (hasVariable(rhs)) { return rhs.invert(Decimal.ln(value).div(Decimal.ln(unrefFormulaSource(lhs)))); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integratePow(stack: SubstitutionStack, lhs: FormulaSource, rhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); const pow = Formula.add(rhs, 1); return Formula.pow(x, pow).div(pow); } else if (hasVariable(rhs)) { if (!rhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = rhs.getIntegralFormula(stack); return Formula.pow(lhs, x).div(Formula.ln(lhs)); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertPow10(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.root(value, 10)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integratePow10(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.pow10(x).div(Formula.ln(10)); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertPowBase(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { @@ -403,25 +456,29 @@ export function invertPowBase(value: DecimalSource, lhs: FormulaSource, rhs: For } else if (hasVariable(rhs)) { return rhs.invert(Decimal.root(unrefFormulaSource(lhs), value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integratePowBase(stack: SubstitutionStack, lhs: FormulaSource, rhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.pow(rhs, x).div(Formula.ln(rhs)); } else if (hasVariable(rhs)) { if (!rhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = rhs.getIntegralFormula(stack); const denominator = Formula.add(lhs, 1); return Formula.pow(x, denominator).div(denominator); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertRoot(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { @@ -430,36 +487,42 @@ export function invertRoot(value: DecimalSource, lhs: FormulaSource, rhs: Formul } else if (hasVariable(rhs)) { return rhs.invert(Decimal.ln(unrefFormulaSource(lhs)).div(Decimal.ln(value))); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateRoot(stack: SubstitutionStack, lhs: FormulaSource, rhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.pow(x, Formula.recip(rhs).add(1)).times(rhs).div(Formula.add(rhs, 1)); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertExp(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.ln(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateExp(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.exp(x); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function tetrate( @@ -481,7 +544,8 @@ export function invertTetrate( return base.invert(Decimal.ssqrt(value)); } // Other params can't be inverted ATM - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function iteratedexp( @@ -509,7 +573,8 @@ export function invertIteratedExp( ); } // Other params can't be inverted ATM - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function iteratedLog( @@ -533,7 +598,8 @@ export function invertSlog(value: DecimalSource, lhs: FormulaSource, rhs: Formul ); } // Other params can't be inverted ATM - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function layeradd(value: DecimalSource, diff: DecimalSource, base: DecimalSource) { @@ -556,21 +622,24 @@ export function invertLayeradd( ); } // Other params can't be inverted ATM - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function invertLambertw(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.pow(Math.E, value).times(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function invertSsqrt(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.tetrate(value, 2)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function pentate(value: DecimalSource, height: DecimalSource, payload: DecimalSource) { @@ -582,226 +651,262 @@ export function invertSin(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.asin(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateSin(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.cos(x).neg(); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertCos(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.acos(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateCos(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.sin(x); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertTan(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.atan(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateTan(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.cos(x).ln().neg(); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertAsin(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.sin(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateAsin(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.asin(x) .times(x) .add(Formula.sqrt(Formula.sub(1, Formula.pow(x, 2)))); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertAcos(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.cos(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateAcos(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.acos(x) .times(x) .sub(Formula.sqrt(Formula.sub(1, Formula.pow(x, 2)))); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertAtan(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.tan(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateAtan(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.atan(x) .times(x) .sub(Formula.ln(Formula.pow(x, 2).add(1)).div(2)); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertSinh(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.asinh(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateSinh(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.cosh(x); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertCosh(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.acosh(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateCosh(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.sinh(x); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertTanh(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.atanh(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateTanh(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.cosh(x).ln(); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertAsinh(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.sinh(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateAsinh(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.asinh(x).times(x).sub(Formula.pow(x, 2).add(1).sqrt()); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertAcosh(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.cosh(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateAcosh(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.acosh(x) .times(x) .sub(Formula.add(x, 1).sqrt().times(Formula.sub(x, 1).sqrt())); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function invertAtanh(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.tanh(value)); } - throw new Error("Could not invert due to no input being a variable"); + console.error("Could not invert due to no input being a variable"); + return 0; } export function integrateAtanh(stack: SubstitutionStack, lhs: FormulaSource) { if (hasVariable(lhs)) { if (!lhs.isIntegrable()) { - throw new Error("Could not integrate due to variable not being integrable"); + console.error("Could not integrate due to variable not being integrable"); + return Formula.constant(0); } const x = lhs.getIntegralFormula(stack); return Formula.atanh(x) .times(x) .add(Formula.sub(1, Formula.pow(x, 2)).ln().div(2)); } - throw new Error("Could not integrate due to no input being a variable"); + console.error("Could not integrate due to no input being a variable"); + return Formula.constant(0); } export function createPassthroughBinaryFormula( diff --git a/src/game/layers.tsx b/src/game/layers.tsx index 6294592..bbb6079 100644 --- a/src/game/layers.tsx +++ b/src/game/layers.tsx @@ -225,7 +225,9 @@ export function createLayer( addingLayers[addingLayers.length - 1] == null || addingLayers[addingLayers.length - 1] !== id ) { - throw `Adding layers stack in invalid state. This should not happen\nStack: ${addingLayers}\nTrying to pop ${layer.id}`; + throw new Error( + `Adding layers stack in invalid state. This should not happen\nStack: ${addingLayers}\nTrying to pop ${layer.id}` + ); } addingLayers.pop(); diff --git a/src/game/persistence.ts b/src/game/persistence.ts index a61d9cf..e760411 100644 --- a/src/game/persistence.ts +++ b/src/game/persistence.ts @@ -116,12 +116,7 @@ function checkNaNAndWrite(persistent: Persistent, value: T) state.NaNPath = persistent[SaveDataPath]; state.NaNPersistent = persistent as Persistent; } - console.error( - `Attempted to save NaN value to`, - persistent[SaveDataPath]?.join("."), - persistent - ); - throw new Error("Attempted to set NaN value. See above for details"); + console.error(`Attempted to save NaN value to ${persistent[SaveDataPath]?.join(".")}`); } persistent[PersistentState].value = value; } @@ -292,8 +287,8 @@ globalBus.on("addLayer", (layer: GenericLayer, saveData: Record "." )}\` when it's already present at \`${value[SaveDataPath].join( "." - )}\`. This can cause unexpected behavior when loading saves between updates.`, - value + )}\`.`, + "This can cause unexpected behavior when loading saves between updates." ); } value[SaveDataPath] = newPath; @@ -368,9 +363,9 @@ globalBus.on("addLayer", (layer: GenericLayer, saveData: Record return; } console.error( - `Created persistent ref in ${layer.id} without registering it to the layer! Make sure to include everything persistent in the returned object`, - persistent, - "\nCreated at:\n" + persistent[StackTrace] + `Created persistent ref in ${layer.id} without registering it to the layer!`, + "Make sure to include everything persistent in the returned object.\n\nCreated at:\n" + + persistent[StackTrace] ); }); persistentRefs[layer.id].clear(); diff --git a/src/game/state.ts b/src/game/state.ts index db71c7b..9e672c3 100644 --- a/src/game/state.ts +++ b/src/game/state.ts @@ -1,5 +1,5 @@ import type { DecimalSource } from "util/bignum"; -import { shallowReactive } from "vue"; +import { reactive, shallowReactive } from "vue"; import type { Persistent } from "./persistence"; /** An object of global data that is not persistent. */ @@ -12,6 +12,8 @@ export interface Transient { NaNPath?: string[]; /** The ref that was being set to NaN. */ NaNPersistent?: Persistent; + /** List of errors that have occurred, to show the user. */ + errors: Error[]; } declare global { @@ -24,5 +26,6 @@ declare global { export default window.state = shallowReactive({ lastTenTicks: [], hasNaN: false, - NaNPath: [] + NaNPath: [], + errors: reactive([]) }); diff --git a/src/main.ts b/src/main.ts index 38c9ac3..9fb753b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,6 +2,7 @@ import "@fontsource/material-icons"; import App from "App.vue"; import projInfo from "data/projInfo.json"; import "game/notifications"; +import state from "game/state"; import { load } from "util/save"; import { useRegisterSW } from "virtual:pwa-register/vue"; import type { App as VueApp } from "vue"; @@ -23,11 +24,30 @@ declare global { } } +const error = console.error; +console.error = function (...args) { + if (import.meta.env.DEV) { + state.errors.push(new Error(args[0], { cause: args[1] })); + } + error(...args); +}; + +window.onerror = function (event, source, lineno, colno, error) { + state.errors.push(error instanceof Error ? error : new Error(JSON.stringify(error))); + return true; +}; +window.onunhandledrejection = function (event) { + state.errors.push( + event.reason instanceof Error ? event.reason : new Error(JSON.stringify(event.reason)) + ); +}; + document.title = projInfo.title; window.projInfo = projInfo; if (projInfo.id === "") { - throw new Error( - "Project ID is empty! Please select a unique ID for this project in /src/data/projInfo.json" + console.error( + "Project ID is empty!", + "Please select a unique ID for this project in /src/data/projInfo.json" ); } @@ -43,6 +63,9 @@ requestAnimationFrame(async () => { // Create Vue const vue = (window.vue = createApp(App)); + vue.config.errorHandler = function (err, instance, info) { + console.error(err, info, instance); + }; globalBus.emit("setupVue", vue); vue.mount("#app"); diff --git a/src/util/proxies.ts b/src/util/proxies.ts index f712470..ed5d4ac 100644 --- a/src/util/proxies.ts +++ b/src/util/proxies.ts @@ -40,7 +40,7 @@ export function createLazyProxy( function calculateObj(): T { if (!calculated) { if (calculating) { - throw new Error("Cyclical dependency detected. Cannot evaluate lazy proxy."); + console.error("Cyclical dependency detected. Cannot evaluate lazy proxy."); } calculating = true; Object.assign(obj, objectFunc.call(obj, obj));