Add eslint rule for strict boolean expressions and fix linting issues

This commit is contained in:
thepaperpilot 2022-12-20 21:26:25 -06:00
parent 31c53e0379
commit 0c0de62a83
42 changed files with 168 additions and 111 deletions

View file

@ -11,7 +11,8 @@ module.exports = {
"@vue/eslint-config-prettier" "@vue/eslint-config-prettier"
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2020 ecmaVersion: 2020,
project: "tsconfig.json"
}, },
ignorePatterns: ["src/lib"], ignorePatterns: ["src/lib"],
rules: { rules: {
@ -19,7 +20,14 @@ module.exports = {
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"vue/script-setup-uses-vars": "warn", "vue/script-setup-uses-vars": "warn",
"vue/no-mutating-props": "off", "vue/no-mutating-props": "off",
"vue/multi-word-component-names": "off" "vue/multi-word-component-names": "off",
"@typescript-eslint/strict-boolean-expressions": [
"error",
{
allowNullableObject: true,
allowNullableBoolean: true
}
]
}, },
globals: { globals: {
defineProps: "readonly", defineProps: "readonly",

View file

@ -32,7 +32,7 @@ const theme = computed(() => themes[settings.theme].variables as CSSProperties);
const showTPS = toRef(settings, "showTPS"); const showTPS = toRef(settings, "showTPS");
const gameComponent = computed(() => { const gameComponent = computed(() => {
return coerceComponent(jsx(() => <>{gameComponents.map(render)}</>)); return coerceComponent(jsx(() => (<>{gameComponents.map(render)}</>)));
}); });
</script> </script>

View file

@ -80,7 +80,7 @@ const isOpen = ref(false);
const timePlayed = computed(() => formatTime(player.timePlayed)); const timePlayed = computed(() => formatTime(player.timePlayed));
const infoComponent = computed(() => { const infoComponent = computed(() => {
return coerceComponent(jsx(() => <>{infoComponents.map(render)}</>)); return coerceComponent(jsx(() => (<>{infoComponents.map(render)}</>)));
}); });
defineExpose({ defineExpose({

View file

@ -12,9 +12,7 @@
</Context> </Context>
</div> </div>
<button v-if="unref(minimizable)" class="minimize" @click="setMinimized(true)"> <button v-if="unref(minimizable)" class="minimize" @click="setMinimized(true)"></button>
</button>
</div> </div>
</template> </template>
@ -114,7 +112,6 @@ export default defineComponent({
} }
} }
return { return {
component, component,
minimizedComponent, minimizedComponent,
@ -124,7 +121,7 @@ export default defineComponent({
goBack, goBack,
setMinimized, setMinimized,
minimized, minimized,
minWidth, minWidth
}; };
} }
}); });

View file

@ -40,7 +40,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { FeatureNode } from "game/layers"; import type { FeatureNode } from "game/layers";
import { computed, ref, toRefs } from "vue"; import { computed, ref, toRefs, unref } from "vue";
import Context from "./Context.vue"; import Context from "./Context.vue";
const _props = defineProps<{ const _props = defineProps<{
@ -51,7 +51,7 @@ const emit = defineEmits<{
(e: "update:modelValue", value: boolean): void; (e: "update:modelValue", value: boolean): void;
}>(); }>();
const isOpen = computed(() => props.modelValue || isAnimating.value); const isOpen = computed(() => unref(props.modelValue) || isAnimating.value);
function close() { function close() {
emit("update:modelValue", false); emit("update:modelValue", false);
} }

View file

@ -62,7 +62,7 @@ const savesManager = ref<ComponentPublicInstance<typeof SavesManager> | null>(nu
const path = computed(() => state.NaNPath?.join(".")); const path = computed(() => state.NaNPath?.join("."));
const property = computed(() => state.NaNPath?.slice(-1)[0]); const property = computed(() => state.NaNPath?.slice(-1)[0]);
const previous = computed<DecimalSource | null>(() => { const previous = computed<DecimalSource | null>(() => {
if (state.NaNReceiver && property.value) { if (state.NaNReceiver && property.value != null) {
return state.NaNReceiver[property.value] as DecimalSource; return state.NaNReceiver[property.value] as DecimalSource;
} }
return null; return null;
@ -77,21 +77,21 @@ const isPaused = computed({
}); });
function setZero() { function setZero() {
if (state.NaNReceiver && property.value) { if (state.NaNReceiver && property.value != null) {
state.NaNReceiver[property.value] = new Decimal(0); state.NaNReceiver[property.value] = new Decimal(0);
state.hasNaN = false; state.hasNaN = false;
} }
} }
function setOne() { function setOne() {
if (state.NaNReceiver && property.value) { if (state.NaNReceiver && property.value != null) {
state.NaNReceiver[property.value] = new Decimal(1); state.NaNReceiver[property.value] = new Decimal(1);
state.hasNaN = false; state.hasNaN = false;
} }
} }
function ignore() { function ignore() {
if (state.NaNReceiver && property.value) { if (state.NaNReceiver && property.value != null) {
state.NaNReceiver[property.value] = new Decimal(NaN); state.NaNReceiver[property.value] = new Decimal(NaN);
state.hasNaN = false; state.hasNaN = false;
} }

View file

@ -81,7 +81,7 @@ const themes = Object.keys(rawThemes).map(theme => ({
})); }));
const settingFieldsComponent = computed(() => { const settingFieldsComponent = computed(() => {
return coerceComponent(jsx(() => <>{settingFields.map(render)}</>)); return coerceComponent(jsx(() => (<>{settingFields.map(render)}</>)));
}); });
const { showTPS, theme, usingLog, alignUnits } = toRefs(settings); const { showTPS, theme, usingLog, alignUnits } = toRefs(settings);

View file

@ -107,11 +107,11 @@ const isEditing = ref(false);
const isConfirming = ref(false); const isConfirming = ref(false);
const newName = ref(""); const newName = ref("");
watch(isEditing, () => (newName.value = save.value.name || "")); watch(isEditing, () => (newName.value = save.value.name ?? ""));
const isActive = computed(() => save.value && save.value.id === player.id); const isActive = computed(() => save.value != null && save.value.id === player.id);
const currentTime = computed(() => const currentTime = computed(() =>
isActive.value ? player.time : (save.value && save.value.time) || 0 isActive.value ? player.time : (save.value != null && save.value.time) ?? 0
); );
const progressDisplay = computeComponent( const progressDisplay = computeComponent(

View file

@ -281,7 +281,7 @@ function newFromPreset(preset: string) {
function editSave(id: string, newName: string) { function editSave(id: string, newName: string) {
const currSave = saves.value[id]; const currSave = saves.value[id];
if (currSave) { if (currSave != null) {
currSave.name = newName; currSave.name = newName;
if (player.id === id) { if (player.id === id) {
player.name = newName; player.name = newName;

View file

@ -26,7 +26,7 @@ const emit = defineEmits<{
const value = computed({ const value = computed({
get() { get() {
return String(unref(props.modelValue) || 0); return String(unref(props.modelValue) ?? 0);
}, },
set(value: string) { set(value: string) {
emit("update:modelValue", Number(value)); emit("update:modelValue", Number(value));

View file

@ -55,7 +55,7 @@ onMounted(() => {
const value = computed({ const value = computed({
get() { get() {
return unref(props.modelValue) || ""; return unref(props.modelValue) ?? "";
}, },
set(value: string) { set(value: string) {
emit("update:modelValue", value); emit("update:modelValue", value);

View file

@ -19,7 +19,7 @@ const emit = defineEmits<{
(e: "update:modelValue", value: boolean): void; (e: "update:modelValue", value: boolean): void;
}>(); }>();
const component = computed(() => coerceComponent(unref(props.title) || "<span></span>", "span")); const component = computed(() => coerceComponent(unref(props.title) ?? "<span></span>", "span"));
const value = computed({ const value = computed({
get() { get() {

View file

@ -1,6 +1,8 @@
<template> <template>
<span style="white-space: nowrap"> <span style="white-space: nowrap">
<span style="font-size: larger; font-family: initial">&radic;</span <span style="font-size: larger; font-family: initial">&radic;</span>
><div style="display: inline-block; border-top: 1px solid; padding-left: .2em"><slot /></div> <div style="display: inline-block; border-top: 1px solid; padding-left: 0.2em">
<slot />
</div>
</span> </span>
</template> </template>

View file

@ -1,4 +1,5 @@
import Collapsible from "components/layout/Collapsible.vue"; import Collapsible from "components/layout/Collapsible.vue";
import "data/layers/styles/day-gradients.css";
import { createBar } from "features/bars/bar"; import { createBar } from "features/bars/bar";
import { GenericBuyable } from "features/buyable"; import { GenericBuyable } from "features/buyable";
import type { Clickable, ClickableOptions, GenericClickable } from "features/clickables/clickable"; import type { Clickable, ClickableOptions, GenericClickable } from "features/clickables/clickable";
@ -16,14 +17,12 @@ import { GenericMilestone } from "features/milestones/milestone";
import { displayResource, Resource, trackTotal } from "features/resources/resource"; import { displayResource, Resource, trackTotal } from "features/resources/resource";
import type { GenericTree, GenericTreeNode, TreeNode, TreeNodeOptions } from "features/trees/tree"; import type { GenericTree, GenericTreeNode, TreeNode, TreeNodeOptions } from "features/trees/tree";
import { createTreeNode } from "features/trees/tree"; import { createTreeNode } from "features/trees/tree";
import { BaseLayer, Layer } from "game/layers";
import type { Modifier } from "game/modifiers"; import type { Modifier } from "game/modifiers";
import type { Persistent } from "game/persistence"; import type { Persistent } from "game/persistence";
import { DefaultValue, persistent } from "game/persistence"; import { DefaultValue, persistent } from "game/persistence";
import player from "game/player"; import player from "game/player";
import settings from "game/settings"; import settings from "game/settings";
import { DecimalSource, formatSmall } from "util/bignum"; import Decimal, { DecimalSource, format, formatSmall } from "util/bignum";
import Decimal, { format } from "util/bignum";
import { formatWhole } from "util/break_eternity"; import { formatWhole } from "util/break_eternity";
import { Direction, WithRequired } from "util/common"; import { Direction, WithRequired } from "util/common";
import type { import type {
@ -34,10 +33,8 @@ import type {
} from "util/computed"; } from "util/computed";
import { convertComputable, processComputable } from "util/computed"; import { convertComputable, processComputable } from "util/computed";
import { getFirstFeature, render, renderColJSX, renderJSX, VueFeature } from "util/vue"; import { getFirstFeature, render, renderColJSX, renderJSX, VueFeature } from "util/vue";
import { Ref, watchEffect } from "vue"; import { computed, Ref, unref, watchEffect } from "vue";
import { computed, unref } from "vue";
import "./common.css"; import "./common.css";
import "data/layers/styles/day-gradients.css";
import { main } from "./projEntry"; import { main } from "./projEntry";
/** An object that configures a {@link ResetButton} */ /** An object that configures a {@link ResetButton} */
@ -138,7 +135,7 @@ export function createResetButton<T extends ClickableOptions & ResetButtonOption
)} )}
</b>{" "} </b>{" "}
{resetButton.conversion.gainResource.displayName} {resetButton.conversion.gainResource.displayName}
{unref(resetButton.showNextAt) ? ( {unref(resetButton.showNextAt) != null ? (
<div> <div>
<br /> <br />
{unref(resetButton.conversion.buyMax) ? "Next:" : "Req:"}{" "} {unref(resetButton.conversion.buyMax) ? "Next:" : "Req:"}{" "}
@ -167,7 +164,7 @@ export function createResetButton<T extends ClickableOptions & ResetButtonOption
const onClick = resetButton.onClick; const onClick = resetButton.onClick;
resetButton.onClick = function () { resetButton.onClick = function () {
if (!unref(resetButton.canClick)) { if (unref(resetButton.canClick) === false) {
return; return;
} }
resetButton.conversion.convert(); resetButton.conversion.convert();
@ -311,7 +308,7 @@ export function createCollapsibleModifierSections(
</span> </span>
{s.title} {s.title}
{s.subtitle ? <span class="subtitle"> ({s.subtitle})</span> : null} {s.subtitle != null ? <span class="subtitle"> ({s.subtitle})</span> : null}
</h3> </h3>
); );
@ -338,7 +335,8 @@ export function createCollapsibleModifierSections(
{hasPreviousSection ? <br /> : null} {hasPreviousSection ? <br /> : null}
<div <div
style={{ style={{
"--unit": settings.alignUnits && s.unit ? "'" + s.unit + "'" : "" "--unit":
settings.alignUnits && s.unit != null ? "'" + s.unit + "'" : ""
}} }}
> >
{header} {header}
@ -455,7 +453,7 @@ export function setUpDailyProgressTracker(options: {
animation: options.background.duration + " " + options.background.gradient + " linear infinite", animation: options.background.duration + " " + options.background.gradient + " linear infinite",
}, },
/* eslint-enable prettier/prettier */ /* eslint-enable prettier/prettier */
textStyle: options.textColor ? { color: options.textColor } : undefined, textStyle: options.textColor != null ? { color: options.textColor } : undefined,
progress: progressFunc, progress: progressFunc,
display: jsx(() => display: jsx(() =>
main.day.value === options.day || main.day.value === options.day ||

View file

@ -539,10 +539,10 @@ export const main = createLayer("main", function (this: BaseLayer) {
display: jsx(() => ( display: jsx(() => (
<> <>
{player.devSpeed === 0 ? <div>Game Paused</div> : null} {player.devSpeed === 0 ? <div>Game Paused</div> : null}
{player.devSpeed && player.devSpeed !== 1 ? ( {player.devSpeed != null && player.devSpeed !== 1 ? (
<div>Dev Speed: {format(player.devSpeed)}x</div> <div>Dev Speed: {format(player.devSpeed)}x</div>
) : null} ) : null}
{player.offlineTime ? ( {player.offlineTime != null && player.offlineTime !== 0 ? (
<div>Offline Time: {formatTime(player.offlineTime)}</div> <div>Offline Time: {formatTime(player.offlineTime)}</div>
) : null} ) : null}
<Spacer /> <Spacer />

View file

@ -109,7 +109,7 @@ export function createAchievement<T extends AchievementOptions>(
) { ) {
genericAchievement.earned.value = true; genericAchievement.earned.value = true;
genericAchievement.onComplete?.(); genericAchievement.onComplete?.();
if (genericAchievement.display) { if (genericAchievement.display != null) {
const Display = coerceComponent(unref(genericAchievement.display)); const Display = coerceComponent(unref(genericAchievement.display));
toast.info( toast.info(
<div> <div>

View file

@ -1,5 +1,11 @@
import BarComponent from "features/bars/Bar.vue"; import BarComponent from "features/bars/Bar.vue";
import type { CoercableComponent, GenericComponent, OptionsFunc, Replace, StyleValue } from "features/feature"; import type {
CoercableComponent,
GenericComponent,
OptionsFunc,
Replace,
StyleValue
} from "features/feature";
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature"; import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
import type { DecimalSource } from "util/bignum"; import type { DecimalSource } from "util/bignum";
import { Direction } from "util/common"; import { Direction } from "util/common";

View file

@ -210,7 +210,7 @@ function drag(e: MouseEvent | TouchEvent) {
hasDragged.value = true; hasDragged.value = true;
} }
if (dragging.value) { if (dragging.value != null) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} }

View file

@ -234,17 +234,17 @@ const title = computed(() => getNodeProperty(props.nodeType.value.title, unref(p
const label = computed(() => getNodeProperty(props.nodeType.value.label, unref(props.node))); const label = computed(() => getNodeProperty(props.nodeType.value.label, unref(props.node)));
const size = computed(() => getNodeProperty(props.nodeType.value.size, unref(props.node))); const size = computed(() => getNodeProperty(props.nodeType.value.size, unref(props.node)));
const progress = computed( const progress = computed(
() => getNodeProperty(props.nodeType.value.progress, unref(props.node)) || 0 () => getNodeProperty(props.nodeType.value.progress, unref(props.node)) ?? 0
); );
const backgroundColor = computed(() => themes[settings.theme].variables["--background"]); const backgroundColor = computed(() => themes[settings.theme].variables["--background"]);
const outlineColor = computed( const outlineColor = computed(
() => () =>
getNodeProperty(props.nodeType.value.outlineColor, unref(props.node)) || getNodeProperty(props.nodeType.value.outlineColor, unref(props.node)) ??
themes[settings.theme].variables["--outline"] themes[settings.theme].variables["--outline"]
); );
const fillColor = computed( const fillColor = computed(
() => () =>
getNodeProperty(props.nodeType.value.fillColor, unref(props.node)) || getNodeProperty(props.nodeType.value.fillColor, unref(props.node)) ??
themes[settings.theme].variables["--raised-background"] themes[settings.theme].variables["--raised-background"]
); );
const progressColor = computed(() => const progressColor = computed(() =>
@ -252,7 +252,7 @@ const progressColor = computed(() =>
); );
const titleColor = computed( const titleColor = computed(
() => () =>
getNodeProperty(props.nodeType.value.titleColor, unref(props.node)) || getNodeProperty(props.nodeType.value.titleColor, unref(props.node)) ??
themes[settings.theme].variables["--foreground"] themes[settings.theme].variables["--foreground"]
); );
const progressDisplay = computed(() => const progressDisplay = computed(() =>

View file

@ -1,5 +1,11 @@
import ClickableComponent from "features/clickables/Clickable.vue"; import ClickableComponent from "features/clickables/Clickable.vue";
import type { CoercableComponent, GenericComponent, OptionsFunc, Replace, StyleValue } from "features/feature"; import type {
CoercableComponent,
GenericComponent,
OptionsFunc,
Replace,
StyleValue
} from "features/feature";
import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature"; import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature";
import type { Resource } from "features/resources/resource"; import type { Resource } from "features/resources/resource";
import { DefaultValue, Persistent } from "game/persistence"; import { DefaultValue, Persistent } from "game/persistence";
@ -170,18 +176,18 @@ export function createBuyable<T extends BuyableOptions>(
} }
if (currDisplay != null && buyable.cost != null && buyable.resource != null) { if (currDisplay != null && buyable.cost != null && buyable.resource != null) {
const genericBuyable = buyable as GenericBuyable; const genericBuyable = buyable as GenericBuyable;
const Title = coerceComponent(currDisplay.title || "", "h3"); const Title = coerceComponent(currDisplay.title ?? "", "h3");
const Description = coerceComponent(currDisplay.description || ""); const Description = coerceComponent(currDisplay.description ?? "");
const EffectDisplay = coerceComponent(currDisplay.effectDisplay || ""); const EffectDisplay = coerceComponent(currDisplay.effectDisplay ?? "");
return ( return (
<span> <span>
{currDisplay.title ? ( {currDisplay.title == null ? null : (
<div> <div>
<Title /> <Title />
</div> </div>
) : null} )}
{currDisplay.description ? <Description /> : null} {currDisplay.description == null ? null : <Description />}
{currDisplay.showAmount === false ? null : ( {currDisplay.showAmount === false ? null : (
<div> <div>
<br /> <br />
@ -195,15 +201,15 @@ export function createBuyable<T extends BuyableOptions>(
)} )}
</div> </div>
)} )}
{currDisplay.effectDisplay ? ( {currDisplay.effectDisplay == null ? null : (
<div> <div>
<br /> <br />
Currently: <EffectDisplay /> Currently: <EffectDisplay />
</div> </div>
) : null} )}
{genericBuyable.cost && !genericBuyable.maxed.value ? ( {genericBuyable.cost != null && !genericBuyable.maxed.value ? (
<div> <div>
Cost: {format(unref(genericBuyable.cost) || 0)}{" "} Cost: {format(unref(genericBuyable.cost))}{" "}
{buyable.resource.displayName} {buyable.resource.displayName}
</div> </div>
) : null} ) : null}

View file

@ -134,25 +134,25 @@ export default defineComponent({
comp.value = coerceComponent( comp.value = coerceComponent(
jsx(() => ( jsx(() => (
<span> <span>
{currDisplay.title ? ( {currDisplay.title != null ? (
<div> <div>
<Title /> <Title />
</div> </div>
) : null} ) : null}
<Description /> <Description />
{currDisplay.goal ? ( {currDisplay.goal != null ? (
<div> <div>
<br /> <br />
Goal: <Goal /> Goal: <Goal />
</div> </div>
) : null} ) : null}
{currDisplay.reward ? ( {currDisplay.reward != null ? (
<div> <div>
<br /> <br />
Reward: <Reward /> Reward: <Reward />
</div> </div>
) : null} ) : null}
{currDisplay.effectDisplay ? ( {currDisplay.effectDisplay != null ? (
<div> <div>
Currently: <EffectDisplay /> Currently: <EffectDisplay />
</div> </div>

View file

@ -126,7 +126,10 @@ export function createChallenge<T extends ChallengeOptions>(
challenge.toggle = function () { challenge.toggle = function () {
const genericChallenge = challenge as GenericChallenge; const genericChallenge = challenge as GenericChallenge;
if (genericChallenge.active.value) { if (genericChallenge.active.value) {
if (unref(genericChallenge.canComplete) && !genericChallenge.maxed.value) { if (
unref(genericChallenge.canComplete) !== false &&
!genericChallenge.maxed.value
) {
let completions: boolean | DecimalSource = unref(genericChallenge.canComplete); let completions: boolean | DecimalSource = unref(genericChallenge.canComplete);
if (typeof completions === "boolean") { if (typeof completions === "boolean") {
completions = 1; completions = 1;
@ -264,11 +267,14 @@ export function setupAutoComplete(
exitOnComplete = true exitOnComplete = true
): WatchStopHandle { ): WatchStopHandle {
const isActive = typeof autoActive === "function" ? computed(autoActive) : autoActive; const isActive = typeof autoActive === "function" ? computed(autoActive) : autoActive;
return watch([challenge.canComplete, isActive], ([canComplete, isActive]) => { return watch(
[challenge.canComplete as Ref<boolean>, isActive as Ref<boolean>],
([canComplete, isActive]) => {
if (canComplete && isActive) { if (canComplete && isActive) {
challenge.complete(!exitOnComplete); challenge.complete(!exitOnComplete);
} }
}); }
);
} }
export function createActiveChallenge( export function createActiveChallenge(

View file

@ -98,7 +98,7 @@ export default defineComponent({
comp.value = coerceComponent( comp.value = coerceComponent(
jsx(() => ( jsx(() => (
<span> <span>
{currDisplay.title ? ( {currDisplay.title != null ? (
<div> <div>
<Title /> <Title />
</div> </div>

View file

@ -1,5 +1,11 @@
import ClickableComponent from "features/clickables/Clickable.vue"; import ClickableComponent from "features/clickables/Clickable.vue";
import type { CoercableComponent, GenericComponent, OptionsFunc, Replace, StyleValue } from "features/feature"; import type {
CoercableComponent,
GenericComponent,
OptionsFunc,
Replace,
StyleValue
} from "features/feature";
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature"; import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
import type { BaseLayer } from "game/layers"; import type { BaseLayer } from "game/layers";
import type { Unsubscribe } from "nanoevents"; import type { Unsubscribe } from "nanoevents";
@ -84,7 +90,7 @@ export function createClickable<T extends ClickableOptions>(
if (clickable.onClick) { if (clickable.onClick) {
const onClick = clickable.onClick.bind(clickable); const onClick = clickable.onClick.bind(clickable);
clickable.onClick = function (e) { clickable.onClick = function (e) {
if (unref(clickable.canClick)) { if (unref(clickable.canClick) !== false) {
onClick(e); onClick(e);
} }
}; };
@ -92,7 +98,7 @@ export function createClickable<T extends ClickableOptions>(
if (clickable.onHold) { if (clickable.onHold) {
const onHold = clickable.onHold.bind(clickable); const onHold = clickable.onHold.bind(clickable);
clickable.onHold = function () { clickable.onHold = function () {
if (unref(clickable.canClick)) { if (unref(clickable.canClick) !== false) {
onHold(); onHold();
} }
}; };
@ -136,7 +142,8 @@ export function setupAutoClick(
clickable: GenericClickable, clickable: GenericClickable,
autoActive: Computable<boolean> = true autoActive: Computable<boolean> = true
): Unsubscribe { ): Unsubscribe {
const isActive = typeof autoActive === "function" ? computed(autoActive) : autoActive; const isActive: ProcessedComputable<boolean> =
typeof autoActive === "function" ? computed(autoActive) : autoActive;
return layer.on("update", () => { return layer.on("update", () => {
if (unref(isActive) && unref(clickable.canClick)) { if (unref(isActive) && unref(clickable.canClick)) {
clickable.onClick?.(); clickable.onClick?.();

View file

@ -151,7 +151,7 @@ export function createConversion<T extends ConversionOptions>(
: conversion.scaling.currentGain(conversion as GenericConversion); : conversion.scaling.currentGain(conversion as GenericConversion);
gain = Decimal.floor(gain).max(0); gain = Decimal.floor(gain).max(0);
if (!unref(conversion.buyMax)) { if (unref(conversion.buyMax) === false) {
gain = gain.min(1); gain = gain.min(1);
} }
return gain; return gain;
@ -163,14 +163,15 @@ export function createConversion<T extends ConversionOptions>(
if (conversion.currentAt == null) { if (conversion.currentAt == null) {
conversion.currentAt = computed(() => { conversion.currentAt = computed(() => {
let current = conversion.scaling.currentAt(conversion as GenericConversion); let current = conversion.scaling.currentAt(conversion as GenericConversion);
if (conversion.roundUpCost) current = Decimal.ceil(current); if (unref((conversion as GenericConversion).roundUpCost))
current = Decimal.ceil(current);
return current; return current;
}); });
} }
if (conversion.nextAt == null) { if (conversion.nextAt == null) {
conversion.nextAt = computed(() => { conversion.nextAt = computed(() => {
let next = conversion.scaling.nextAt(conversion as GenericConversion); let next = conversion.scaling.nextAt(conversion as GenericConversion);
if (conversion.roundUpCost) next = Decimal.ceil(next); if (unref((conversion as GenericConversion).roundUpCost)) next = Decimal.ceil(next);
return next; return next;
}); });
} }
@ -405,7 +406,7 @@ export function createIndependentConversion<S extends ConversionOptions>(
: conversion.scaling.currentGain(conversion as GenericConversion); : conversion.scaling.currentGain(conversion as GenericConversion);
gain = Decimal.floor(gain).max(conversion.gainResource.value); gain = Decimal.floor(gain).max(conversion.gainResource.value);
if (!unref(conversion.buyMax)) { if (unref(conversion.buyMax) === false) {
gain = gain.min(Decimal.add(conversion.gainResource.value, 1)); gain = gain.min(Decimal.add(conversion.gainResource.value, 1));
} }
return gain; return gain;
@ -418,7 +419,7 @@ export function createIndependentConversion<S extends ConversionOptions>(
conversion.gainResource.value conversion.gainResource.value
).max(0); ).max(0);
if (!unref(conversion.buyMax)) { if (unref(conversion.buyMax) === false) {
gain = gain.min(1); gain = gain.min(1);
} }
return gain; return gain;
@ -511,8 +512,10 @@ export function addSoftcap(
): ScalingFunction { ): ScalingFunction {
return { return {
...scaling, ...scaling,
currentAt: conversion => softcap(scaling.currentAt(conversion), unref(cap), Decimal.recip(unref(power))), currentAt: conversion =>
nextAt: conversion => softcap(scaling.nextAt(conversion), unref(cap), Decimal.recip(unref(power))), softcap(scaling.currentAt(conversion), unref(cap), Decimal.recip(unref(power))),
nextAt: conversion =>
softcap(scaling.nextAt(conversion), unref(cap), Decimal.recip(unref(power))),
currentGain: conversion => currentGain: conversion =>
softcap(scaling.currentGain(conversion), unref(cap), unref(power)) softcap(scaling.currentGain(conversion), unref(cap), unref(power))
}; };

View file

@ -102,7 +102,7 @@ export function findFeatures(obj: Record<string, unknown>, ...types: symbol[]):
const handleObject = (obj: Record<string, unknown>) => { const handleObject = (obj: Record<string, unknown>) => {
Object.keys(obj).forEach(key => { Object.keys(obj).forEach(key => {
const value = obj[key]; const value = obj[key];
if (value && typeof value === "object") { if (value != null && typeof value === "object") {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
if (types.includes((value as Record<string, any>).type)) { if (types.includes((value as Record<string, any>).type)) {
objects.push(value); objects.push(value);
@ -127,7 +127,7 @@ export function excludeFeatures(obj: Record<string, unknown>, ...types: symbol[]
const handleObject = (obj: Record<string, unknown>) => { const handleObject = (obj: Record<string, unknown>) => {
Object.keys(obj).forEach(key => { Object.keys(obj).forEach(key => {
const value = obj[key]; const value = obj[key];
if (value && typeof value === "object") { if (value != null && typeof value === "object") {
if ( if (
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
typeof (value as Record<string, any>).type == "symbol" && typeof (value as Record<string, any>).type == "symbol" &&

View file

@ -74,12 +74,12 @@ export default defineComponent({
jsx(() => ( jsx(() => (
<span> <span>
<Requirement /> <Requirement />
{currDisplay.effectDisplay ? ( {currDisplay.effectDisplay != null ? (
<div> <div>
<EffectDisplay /> <EffectDisplay />
</div> </div>
) : null} ) : null}
{currDisplay.optionsDisplay ? ( {currDisplay.optionsDisplay != null ? (
<div class="equal-spaced"> <div class="equal-spaced">
<OptionsDisplay /> <OptionsDisplay />
</div> </div>

View file

@ -1,5 +1,11 @@
import Select from "components/fields/Select.vue"; import Select from "components/fields/Select.vue";
import type { CoercableComponent, GenericComponent, OptionsFunc, Replace, StyleValue } from "features/feature"; import type {
CoercableComponent,
GenericComponent,
OptionsFunc,
Replace,
StyleValue
} from "features/feature";
import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature"; import { Component, GatherProps, getUniqueID, jsx, setDefault, Visibility } from "features/feature";
import MilestoneComponent from "features/milestones/Milestone.vue"; import MilestoneComponent from "features/milestones/Milestone.vue";
import { globalBus } from "game/events"; import { globalBus } from "game/events";
@ -92,7 +98,7 @@ export function createMilestone<T extends MilestoneOptions>(
const genericMilestone = milestone as GenericMilestone; const genericMilestone = milestone as GenericMilestone;
earned.value = true; earned.value = true;
genericMilestone.onComplete?.(); genericMilestone.onComplete?.();
if (genericMilestone.display && unref(genericMilestone.showPopups) === true) { if (genericMilestone.display != null && unref(genericMilestone.showPopups) === true) {
const display = unref(genericMilestone.display); const display = unref(genericMilestone.display);
const Display = coerceComponent( const Display = coerceComponent(
isCoercableComponent(display) ? display : display.requirement isCoercableComponent(display) ? display : display.requirement
@ -162,7 +168,10 @@ export function createMilestone<T extends MilestoneOptions>(
) { ) {
genericMilestone.earned.value = true; genericMilestone.earned.value = true;
genericMilestone.onComplete?.(); genericMilestone.onComplete?.();
if (genericMilestone.display && unref(genericMilestone.showPopups) === true) { if (
genericMilestone.display != null &&
unref(genericMilestone.showPopups) === true
) {
const display = unref(genericMilestone.display); const display = unref(genericMilestone.display);
const Display = coerceComponent( const Display = coerceComponent(
isCoercableComponent(display) ? display : display.requirement isCoercableComponent(display) ? display : display.requirement

View file

@ -43,7 +43,7 @@ export function createReset<T extends ResetOptions>(
reset.reset = function () { reset.reset = function () {
const handleObject = (obj: unknown) => { const handleObject = (obj: unknown) => {
if (obj && typeof obj === "object") { if (obj != null && typeof obj === "object") {
if (DefaultValue in obj) { if (DefaultValue in obj) {
const persistent = obj as NonPersistent; const persistent = obj as NonPersistent;
persistent.value = persistent[DefaultValue]; persistent.value = persistent[DefaultValue];

View file

@ -88,7 +88,7 @@ export function createTreeNode<T extends TreeNodeOptions>(
if (treeNode.onClick) { if (treeNode.onClick) {
const onClick = treeNode.onClick.bind(treeNode); const onClick = treeNode.onClick.bind(treeNode);
treeNode.onClick = function () { treeNode.onClick = function () {
if (unref(treeNode.canClick)) { if (unref(treeNode.canClick) !== false) {
onClick(); onClick();
} }
}; };
@ -96,7 +96,7 @@ export function createTreeNode<T extends TreeNodeOptions>(
if (treeNode.onHold) { if (treeNode.onHold) {
const onHold = treeNode.onHold.bind(treeNode); const onHold = treeNode.onHold.bind(treeNode);
treeNode.onHold = function () { treeNode.onHold = function () {
if (unref(treeNode.canClick)) { if (unref(treeNode.canClick) !== false) {
onHold(); onHold();
} }
}; };

View file

@ -96,13 +96,13 @@ export default defineComponent({
component.value = coerceComponent( component.value = coerceComponent(
jsx(() => ( jsx(() => (
<span> <span>
{currDisplay.title ? ( {currDisplay.title != null ? (
<div> <div>
<Title /> <Title />
</div> </div>
) : null} ) : null}
<Description /> <Description />
{currDisplay.effectDisplay ? ( {currDisplay.effectDisplay != null ? (
<div> <div>
Currently: <EffectDisplay /> Currently: <EffectDisplay />
</div> </div>

View file

@ -187,8 +187,11 @@ export function setupAutoPurchase(
autoActive: Computable<boolean>, autoActive: Computable<boolean>,
upgrades: GenericUpgrade[] = [] upgrades: GenericUpgrade[] = []
): void { ): void {
upgrades = upgrades || findFeatures(layer, UpgradeType); upgrades =
const isAutoActive = isFunction(autoActive) ? computed(autoActive) : autoActive; upgrades.length === 0 ? (findFeatures(layer, UpgradeType) as GenericUpgrade[]) : upgrades;
const isAutoActive: ProcessedComputable<boolean> = isFunction(autoActive)
? computed(autoActive)
: autoActive;
layer.on("update", () => { layer.on("update", () => {
if (unref(isAutoActive)) { if (unref(isAutoActive)) {
upgrades.forEach(upgrade => upgrade.purchase()); upgrades.forEach(upgrade => upgrade.purchase());

View file

@ -78,7 +78,7 @@ export function createAdditiveModifier<T extends AdditiveModifierOptions>(
? undefined ? undefined
: jsx(() => ( : jsx(() => (
<div class="modifier-container"> <div class="modifier-container">
{unref(processedDescription) ? ( {unref(processedDescription) != null ? (
<span class="modifier-description"> <span class="modifier-description">
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
{renderJSX(unref(processedDescription)!)} {renderJSX(unref(processedDescription)!)}
@ -126,7 +126,7 @@ export function createMultiplicativeModifier<T extends MultiplicativeModifierOpt
? undefined ? undefined
: jsx(() => ( : jsx(() => (
<div class="modifier-container"> <div class="modifier-container">
{unref(processedDescription) ? ( {unref(processedDescription) != null ? (
<span class="modifier-description"> <span class="modifier-description">
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
{renderJSX(unref(processedDescription)!)} {renderJSX(unref(processedDescription)!)}
@ -195,7 +195,7 @@ export function createExponentialModifier<T extends ExponentialModifierOptions>(
? undefined ? undefined
: jsx(() => ( : jsx(() => (
<div class="modifier-container"> <div class="modifier-container">
{unref(processedDescription) ? ( {unref(processedDescription) != null ? (
<span class="modifier-description"> <span class="modifier-description">
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
{renderJSX(unref(processedDescription)!)} {renderJSX(unref(processedDescription)!)}

View file

@ -85,7 +85,7 @@ function getStackTrace() {
?.split("\n") ?.split("\n")
.slice(3, 5) .slice(3, 5)
.map(line => line.trim()) .map(line => line.trim())
.join("\n") || "" .join("\n") ?? ""
); );
} }
@ -134,7 +134,7 @@ export function persistent<T extends State>(defaultValue: T | Ref<T>): Persisten
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isPersistent(value: any): value is Persistent { export function isPersistent(value: any): value is Persistent {
return value && typeof value === "object" && PersistentState in value; return value != null && typeof value === "object" && PersistentState in value;
} }
/** /**
@ -168,7 +168,7 @@ globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>
let foundPersistent = false; let foundPersistent = false;
Object.keys(obj).forEach(key => { Object.keys(obj).forEach(key => {
let value = obj[key]; let value = obj[key];
if (value && typeof value === "object") { if (value != null && typeof value === "object") {
if (ProxyState in value) { if (ProxyState in value) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
value = (value as any)[ProxyState] as object; value = (value as any)[ProxyState] as object;

View file

@ -70,7 +70,7 @@ const state = reactive<PlayerData>({
modVersion: "", modVersion: "",
layers: {}, layers: {},
autoPause: true, autoPause: true
}); });
/** Convert a player save data object into a JSON string. Unwraps refs. */ /** Convert a player save data object into a JSON string. Unwraps refs. */

View file

@ -35,7 +35,7 @@ const state = reactive<Partial<Settings>>({
unthrottled: false, unthrottled: false,
usingLog: false, usingLog: false,
alignUnits: false, alignUnits: false
}); });
watch( watch(
@ -70,7 +70,7 @@ export const hardResetSettings = (window.hardResetSettings = () => {
theme: Themes.Nordic, theme: Themes.Nordic,
usingLog: false, usingLog: false,
alignUnits: false, alignUnits: false
}; };
globalBus.emit("loadSettings", settings); globalBus.emit("loadSettings", settings);
Object.assign(state, settings); Object.assign(state, settings);

View file

@ -33,7 +33,7 @@ declare global {
formatSmall: (x: DecimalSource, precision?: number) => string; formatSmall: (x: DecimalSource, precision?: number) => string;
formatLimit: (list: [DecimalSource, string][], unit: string) => string; formatLimit: (list: [DecimalSource, string][], unit: string) => string;
invertOOM: (x: DecimalSource) => Decimal; invertOOM: (x: DecimalSource) => Decimal;
formatGain: (x: DecimalSource) => string formatGain: (x: DecimalSource) => string;
} }
} }
window.Decimal = Decimal; window.Decimal = Decimal;
@ -47,6 +47,6 @@ window.toPlaces = toPlaces;
window.formatSmall = formatSmall; window.formatSmall = formatSmall;
window.formatLimit = formatLimit; window.formatLimit = formatLimit;
window.invertOOM = invertOOM; window.invertOOM = invertOOM;
window.formatGain = formatGain window.formatGain = formatGain;
export default Decimal; export default Decimal;

View file

@ -196,7 +196,11 @@ export function invertOOM(x: DecimalSource): Decimal {
return x; return x;
} }
export function formatLimit(list: [DecimalSource, string][], unit: string, gainMultiplier: DecimalSource = Decimal.dOne): string { export function formatLimit(
list: [DecimalSource, string][],
unit: string,
gainMultiplier: DecimalSource = Decimal.dOne
): string {
let num = list[0][0]; let num = list[0][0];
let str = list[0][1]; let str = list[0][1];
for (let i = 1; i < list.length; i++) { for (let i = 1; i < list.length; i++) {

View file

@ -39,7 +39,7 @@ export function createLazyProxy<T extends object, S extends T>(
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const val = (calculateObj() as any)[key]; const val = (calculateObj() as any)[key];
if (val && typeof val === "object" && NonPersistent in val) { if (val != null && typeof val === "object" && NonPersistent in val) {
return val[NonPersistent]; return val[NonPersistent];
} }
return val; return val;

View file

@ -86,7 +86,7 @@ export function getUniqueID(): string {
i = 0; i = 0;
do { do {
id = `${projInfo.id}-${i++}`; id = `${projInfo.id}-${i++}`;
} while (localStorage.getItem(id)); } while (localStorage.getItem(id) != null);
return id; return id;
} }
@ -107,7 +107,12 @@ export async function loadSave(playerObj: Partial<PlayerData>): Promise<void> {
getInitialLayers(playerObj).forEach(layer => addLayer(layer, playerObj)); getInitialLayers(playerObj).forEach(layer => addLayer(layer, playerObj));
playerObj = setupInitialStore(playerObj); playerObj = setupInitialStore(playerObj);
if (playerObj.offlineProd && playerObj.time && playerObj.devSpeed !== 0) { if (
playerObj.offlineProd &&
playerObj.time != null &&
playerObj.time &&
playerObj.devSpeed !== 0
) {
if (playerObj.offlineTime == undefined) playerObj.offlineTime = 0; if (playerObj.offlineTime == undefined) playerObj.offlineTime = 0;
playerObj.offlineTime += Math.min( playerObj.offlineTime += Math.min(
playerObj.offlineTime + (Date.now() - playerObj.time) / 1000, playerObj.offlineTime + (Date.now() - playerObj.time) / 1000,

View file

@ -229,7 +229,10 @@ export function computeOptionalComponent(
const comp = shallowRef<Component | "" | null>(null); const comp = shallowRef<Component | "" | null>(null);
watchEffect(() => { watchEffect(() => {
const currComponent = unwrapRef(component); const currComponent = unwrapRef(component);
comp.value = !currComponent ? null : coerceComponent(currComponent, defaultWrapper); comp.value =
currComponent == "" || currComponent == null
? null
: coerceComponent(currComponent, defaultWrapper);
}); });
return comp; return comp;
} }