Complete the rewrite
Renderables no longer get wrapped in computed refs, because JSX.Elements don't like that (desyncs with the DOM) Relatedly, a lot of display functions got fairly simplified, removing unnecessary local components Added `MaybeGetter` utility type for something that may be a getter function or a static value (but not a ref) Made Achievement.vue use a Renderable for the display. The object of components can still be passed to `createAchievement` Made Challenge.vue use a Renderable for the display. The object of components can still be passed to `createChallenge` Fixed some issues introduced by the rewrite that broke particles systems
This commit is contained in:
parent
ffd9529e9a
commit
ff14443102
37 changed files with 504 additions and 483 deletions
|
@ -15,15 +15,16 @@
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import "components/common/fields.css";
|
import "components/common/fields.css";
|
||||||
|
import { MaybeGetter } from "util/computed";
|
||||||
import { render, Renderable } from "util/vue";
|
import { render, Renderable } from "util/vue";
|
||||||
import { MaybeRef, ref, toRef, unref, watch } from "vue";
|
import { ref, toRef, unref, watch } from "vue";
|
||||||
import VueNextSelect from "vue-next-select";
|
import VueNextSelect from "vue-next-select";
|
||||||
import "vue-next-select/dist/index.css";
|
import "vue-next-select/dist/index.css";
|
||||||
|
|
||||||
export type SelectOption = { label: string; value: unknown };
|
export type SelectOption = { label: string; value: unknown };
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title?: MaybeRef<Renderable>;
|
title?: MaybeGetter<Renderable>;
|
||||||
modelValue?: unknown;
|
modelValue?: unknown;
|
||||||
options: SelectOption[];
|
options: SelectOption[];
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
|
|
@ -27,12 +27,13 @@
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import "components/common/fields.css";
|
import "components/common/fields.css";
|
||||||
|
import { MaybeGetter } from "util/computed";
|
||||||
import { render, Renderable } from "util/vue";
|
import { render, Renderable } from "util/vue";
|
||||||
import { computed, MaybeRef, onMounted, shallowRef, unref } from "vue";
|
import { computed, onMounted, shallowRef, unref } from "vue";
|
||||||
import VueTextareaAutosize from "vue-textarea-autosize";
|
import VueTextareaAutosize from "vue-textarea-autosize";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title?: MaybeRef<Renderable>;
|
title?: MaybeGetter<Renderable>;
|
||||||
modelValue?: string;
|
modelValue?: string;
|
||||||
textArea?: boolean;
|
textArea?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
|
|
@ -7,11 +7,12 @@
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import "components/common/fields.css";
|
import "components/common/fields.css";
|
||||||
|
import { MaybeGetter } from "util/computed";
|
||||||
import { render, Renderable } from "util/vue";
|
import { render, Renderable } from "util/vue";
|
||||||
import { computed, MaybeRef } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title?: MaybeRef<Renderable>;
|
title?: MaybeGetter<Renderable>;
|
||||||
modelValue?: boolean;
|
modelValue?: boolean;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
|
@ -8,14 +8,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { MaybeGetter } from "util/computed";
|
||||||
import { render, Renderable } from "util/vue";
|
import { render, Renderable } from "util/vue";
|
||||||
import type { MaybeRef, Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import Col from "./Column.vue";
|
import Col from "./Column.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
collapsed: Ref<boolean>;
|
collapsed: Ref<boolean>;
|
||||||
display: MaybeRef<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
content: MaybeRef<Renderable>;
|
content: MaybeGetter<Renderable>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const Display = () => render(props.display);
|
const Display = () => render(props.display);
|
||||||
|
|
|
@ -17,7 +17,7 @@ import settings from "game/settings";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import Decimal, { format, formatSmall, formatTime } from "util/bignum";
|
import Decimal, { format, formatSmall, formatTime } from "util/bignum";
|
||||||
import { WithRequired } from "util/common";
|
import { WithRequired } from "util/common";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { render, Renderable, renderCol } from "util/vue";
|
import { render, Renderable, renderCol } from "util/vue";
|
||||||
import type { ComputedRef, MaybeRef, MaybeRefOrGetter } from "vue";
|
import type { ComputedRef, MaybeRef, MaybeRefOrGetter } from "vue";
|
||||||
import { computed, ref, unref } from "vue";
|
import { computed, ref, unref } from "vue";
|
||||||
|
@ -43,7 +43,7 @@ export interface ResetButtonOptions extends ClickableOptions {
|
||||||
* The content to display on the button.
|
* The content to display on the button.
|
||||||
* By default, this includes the reset description, and amount of currency to be gained.
|
* By default, this includes the reset description, and amount of currency to be gained.
|
||||||
*/
|
*/
|
||||||
display?: MaybeRefOrGetter<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
/**
|
/**
|
||||||
* Whether or not this button can currently be clicked.
|
* Whether or not this button can currently be clicked.
|
||||||
* Defaults to checking the current gain amount is greater than {@link minimumGain}
|
* Defaults to checking the current gain amount is greater than {@link minimumGain}
|
||||||
|
@ -126,38 +126,36 @@ export function createResetButton<T extends ClickableOptions & ResetButtonOption
|
||||||
Decimal.gte(unref(conversion.actualGain), unref(resetButton.minimumGain))
|
Decimal.gte(unref(conversion.actualGain), unref(resetButton.minimumGain))
|
||||||
),
|
),
|
||||||
display:
|
display:
|
||||||
processGetter(display) ??
|
display ??
|
||||||
computed(
|
((): JSX.Element => (
|
||||||
(): JSX.Element => (
|
<span>
|
||||||
<span>
|
{unref(resetButton.resetDescription)}
|
||||||
{unref(resetButton.resetDescription)}
|
<b>
|
||||||
<b>
|
{displayResource(
|
||||||
|
conversion.gainResource,
|
||||||
|
Decimal.max(
|
||||||
|
unref(conversion.actualGain),
|
||||||
|
unref(resetButton.minimumGain)
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</b>{" "}
|
||||||
|
{conversion.gainResource.displayName}
|
||||||
|
{unref(resetButton.showNextAt) != null ? (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
{unref(conversion.buyMax) ? "Next:" : "Req:"}{" "}
|
||||||
{displayResource(
|
{displayResource(
|
||||||
conversion.gainResource,
|
conversion.baseResource,
|
||||||
Decimal.max(
|
!unref<boolean>(conversion.buyMax) &&
|
||||||
unref(conversion.actualGain),
|
Decimal.gte(unref(conversion.actualGain), 1)
|
||||||
unref(resetButton.minimumGain)
|
? unref(conversion.currentAt)
|
||||||
)
|
: unref(conversion.nextAt)
|
||||||
)}
|
)}{" "}
|
||||||
</b>{" "}
|
{conversion.baseResource.displayName}
|
||||||
{conversion.gainResource.displayName}
|
</div>
|
||||||
{unref(resetButton.showNextAt) != null ? (
|
) : null}
|
||||||
<div>
|
</span>
|
||||||
<br />
|
)),
|
||||||
{unref(conversion.buyMax) ? "Next:" : "Req:"}{" "}
|
|
||||||
{displayResource(
|
|
||||||
conversion.baseResource,
|
|
||||||
!unref<boolean>(conversion.buyMax) &&
|
|
||||||
Decimal.gte(unref(conversion.actualGain), 1)
|
|
||||||
? unref(conversion.currentAt)
|
|
||||||
: unref(conversion.nextAt)
|
|
||||||
)}{" "}
|
|
||||||
{conversion.baseResource.displayName}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
),
|
|
||||||
onClick: function (e?: MouseEvent | TouchEvent) {
|
onClick: function (e?: MouseEvent | TouchEvent) {
|
||||||
if (unref(resetButton.canClick) === false) {
|
if (unref(resetButton.canClick) === false) {
|
||||||
return;
|
return;
|
||||||
|
@ -211,7 +209,7 @@ export function createLayerTreeNode<T extends LayerTreeNodeOptions>(optionsFunc:
|
||||||
return {
|
return {
|
||||||
...(props as Omit<typeof props, keyof LayerTreeNodeOptions>),
|
...(props as Omit<typeof props, keyof LayerTreeNodeOptions>),
|
||||||
layerID,
|
layerID,
|
||||||
display: processGetter(display) ?? layerID,
|
display: display ?? layerID,
|
||||||
append: processGetter(append) ?? true,
|
append: processGetter(append) ?? true,
|
||||||
onClick() {
|
onClick() {
|
||||||
if (unref<boolean>(layerTreeNode.append)) {
|
if (unref<boolean>(layerTreeNode.append)) {
|
||||||
|
@ -244,7 +242,7 @@ export interface Section {
|
||||||
/** The unit of measurement for the base. **/
|
/** The unit of measurement for the base. **/
|
||||||
unit?: string;
|
unit?: string;
|
||||||
/** The label to call the base amount. Defaults to "Base". **/
|
/** The label to call the base amount. Defaults to "Base". **/
|
||||||
baseText?: MaybeRefOrGetter<Renderable>;
|
baseText?: MaybeGetter<Renderable>;
|
||||||
/** Whether or not this section should be currently visible to the player. **/
|
/** Whether or not this section should be currently visible to the player. **/
|
||||||
visible?: MaybeRefOrGetter<boolean>;
|
visible?: MaybeRefOrGetter<boolean>;
|
||||||
/** Determines if numbers larger or smaller than the base should be displayed as red. */
|
/** Determines if numbers larger or smaller than the base should be displayed as red. */
|
||||||
|
@ -258,12 +256,12 @@ export interface Section {
|
||||||
*/
|
*/
|
||||||
export function createCollapsibleModifierSections(
|
export function createCollapsibleModifierSections(
|
||||||
sectionsFunc: () => Section[]
|
sectionsFunc: () => Section[]
|
||||||
): [MaybeRef<Renderable>, Persistent<Record<number, boolean>>] {
|
): [() => Renderable, Persistent<Record<number, boolean>>] {
|
||||||
const sections: Section[] = [];
|
const sections: Section[] = [];
|
||||||
const processed:
|
const processed:
|
||||||
| {
|
| {
|
||||||
base: MaybeRef<DecimalSource | undefined>[];
|
base: MaybeRef<DecimalSource | undefined>[];
|
||||||
baseText: (MaybeRef<Renderable> | undefined)[];
|
baseText: (MaybeGetter<Renderable> | undefined)[];
|
||||||
visible: MaybeRef<boolean | undefined>[];
|
visible: MaybeRef<boolean | undefined>[];
|
||||||
title: MaybeRef<string | undefined>[];
|
title: MaybeRef<string | undefined>[];
|
||||||
subtitle: MaybeRef<string | undefined>[];
|
subtitle: MaybeRef<string | undefined>[];
|
||||||
|
@ -274,7 +272,7 @@ export function createCollapsibleModifierSections(
|
||||||
if (!calculated) {
|
if (!calculated) {
|
||||||
sections.push(...sectionsFunc());
|
sections.push(...sectionsFunc());
|
||||||
processed.base = sections.map(s => processGetter(s.base));
|
processed.base = sections.map(s => processGetter(s.base));
|
||||||
processed.baseText = sections.map(s => processGetter(s.baseText));
|
processed.baseText = sections.map(s => s.baseText);
|
||||||
processed.visible = sections.map(s => processGetter(s.visible));
|
processed.visible = sections.map(s => processGetter(s.visible));
|
||||||
processed.title = sections.map(s => processGetter(s.title));
|
processed.title = sections.map(s => processGetter(s.title));
|
||||||
processed.subtitle = sections.map(s => processGetter(s.subtitle));
|
processed.subtitle = sections.map(s => processGetter(s.subtitle));
|
||||||
|
@ -284,7 +282,7 @@ export function createCollapsibleModifierSections(
|
||||||
}
|
}
|
||||||
|
|
||||||
const collapsed = persistent<Record<number, boolean>>({}, false);
|
const collapsed = persistent<Record<number, boolean>>({}, false);
|
||||||
const jsxFunc = computed(() => {
|
const jsxFunc = () => {
|
||||||
const sections = calculateSections();
|
const sections = calculateSections();
|
||||||
|
|
||||||
let firstVisibleSection = true;
|
let firstVisibleSection = true;
|
||||||
|
@ -364,7 +362,7 @@ export function createCollapsibleModifierSections(
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return <>{sectionJSX}</>;
|
return <>{sectionJSX}</>;
|
||||||
});
|
};
|
||||||
return [jsxFunc, collapsed];
|
return [jsxFunc, collapsed];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,9 @@ import settings from "game/settings";
|
||||||
import { DecimalSource } from "lib/break_eternity";
|
import { DecimalSource } from "lib/break_eternity";
|
||||||
import Decimal, { format, formatWhole } from "util/bignum";
|
import Decimal, { format, formatWhole } from "util/bignum";
|
||||||
import { Direction } from "util/common";
|
import { Direction } from "util/common";
|
||||||
import { render, Renderable, renderCol, renderRow } from "util/vue";
|
import { Renderable, render, renderCol, renderRow } from "util/vue";
|
||||||
import { ComputedRef, Ref, computed, ref, unref } from "vue";
|
import { ComputedRef, Ref, computed, ref, unref } from "vue";
|
||||||
|
import { addMark } from "wrappers/marks/mark";
|
||||||
import { addTooltip } from "wrappers/tooltips/tooltip";
|
import { addTooltip } from "wrappers/tooltips/tooltip";
|
||||||
import f from "./f";
|
import f from "./f";
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ const layer = createLayer(id, () => {
|
||||||
title: "Lore",
|
title: "Lore",
|
||||||
titleStyle: { color: "#FE0000" },
|
titleStyle: { color: "#FE0000" },
|
||||||
display: "DEEP LORE!",
|
display: "DEEP LORE!",
|
||||||
bodyStyle: { "backgroundColor": "#0000EE" },
|
bodyStyle: { backgroundColor: "#0000EE" },
|
||||||
color: "rgb(75, 220, 19)"
|
color: "rgb(75, 220, 19)"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -121,11 +122,14 @@ const layer = createLayer(id, () => {
|
||||||
})),
|
})),
|
||||||
completionLimit: 3,
|
completionLimit: 3,
|
||||||
display: {
|
display: {
|
||||||
description: (): Renderable => <>
|
description: (): Renderable => (
|
||||||
Makes the game 0% harder<br/>{formatWhole(funChallenge.completions.value)}/{
|
<>
|
||||||
funChallenge.completionLimit
|
Makes the game 0% harder
|
||||||
} completions
|
<br />
|
||||||
</>,
|
{formatWhole(funChallenge.completions.value)}/{funChallenge.completionLimit}{" "}
|
||||||
|
completions
|
||||||
|
</>
|
||||||
|
),
|
||||||
goal: "Have 20 points I guess",
|
goal: "Have 20 points I guess",
|
||||||
reward: "Says hi",
|
reward: "Says hi",
|
||||||
effectDisplay: format(funEffect.value) + "x"
|
effectDisplay: format(funEffect.value) + "x"
|
||||||
|
@ -147,7 +151,10 @@ const layer = createLayer(id, () => {
|
||||||
height: "400px"
|
height: "400px"
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const funEffect = computed(() => Decimal.add(points.value, 1).tetrate(0.02));
|
addMark(funChallenge, () => ({
|
||||||
|
mark: funChallenge.maxed
|
||||||
|
}));
|
||||||
|
const funEffect = computed((): Decimal => Decimal.add(points.value, 1).tetrate(0.02));
|
||||||
|
|
||||||
const generatorUpgrade = createUpgrade(() => ({
|
const generatorUpgrade = createUpgrade(() => ({
|
||||||
display: {
|
display: {
|
||||||
|
@ -280,7 +287,7 @@ const layer = createLayer(id, () => {
|
||||||
spentOnBuyables.value = Decimal.sub(spentOnBuyables.value, cost);
|
spentOnBuyables.value = Decimal.sub(spentOnBuyables.value, cost);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const buyablesDisplay = computed(() => (
|
const buyablesDisplay = () => (
|
||||||
<Column>
|
<Column>
|
||||||
<Row>
|
<Row>
|
||||||
<Toggle
|
<Toggle
|
||||||
|
@ -321,7 +328,7 @@ const layer = createLayer(id, () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
));
|
);
|
||||||
|
|
||||||
const longBoi = createBar(() => ({
|
const longBoi = createBar(() => ({
|
||||||
fillStyle: { backgroundColor: "#FFFFFF" },
|
fillStyle: { backgroundColor: "#FFFFFF" },
|
||||||
|
@ -442,12 +449,11 @@ const layer = createLayer(id, () => {
|
||||||
resetDescription: "Melt your points into "
|
resetDescription: "Melt your points into "
|
||||||
}));
|
}));
|
||||||
const resetButtonTooltip = addTooltip(resetButton, () => ({
|
const resetButtonTooltip = addTooltip(resetButton, () => ({
|
||||||
display: computed(() =>
|
display: () =>
|
||||||
createModifierSection({
|
createModifierSection({
|
||||||
title: "Modifiers",
|
title: "Modifiers",
|
||||||
modifier: conversionModifier
|
modifier: conversionModifier
|
||||||
})
|
}),
|
||||||
),
|
|
||||||
pinnable: true,
|
pinnable: true,
|
||||||
direction: Direction.Down,
|
direction: Direction.Down,
|
||||||
style: { width: "400px", textAlign: "left" }
|
style: { width: "400px", textAlign: "left" }
|
||||||
|
|
|
@ -36,7 +36,13 @@ const layer = createLayer(id, () => {
|
||||||
const clickable = createClickable(() => ({
|
const clickable = createClickable(() => ({
|
||||||
display: {
|
display: {
|
||||||
title: "Clicky clicky!",
|
title: "Clicky clicky!",
|
||||||
description: () => <>Current state:<br/>{clickableState.value}</>
|
description: () => (
|
||||||
|
<>
|
||||||
|
Current state:
|
||||||
|
<br />
|
||||||
|
{clickableState.value}
|
||||||
|
</>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
initialState: "Start",
|
initialState: "Start",
|
||||||
canClick() {
|
canClick() {
|
||||||
|
|
|
@ -18,12 +18,13 @@ import "components/common/features.css";
|
||||||
import Node from "components/Node.vue";
|
import Node from "components/Node.vue";
|
||||||
import type { Visibility } from "features/feature";
|
import type { Visibility } from "features/feature";
|
||||||
import { isHidden, isVisible } from "features/feature";
|
import { isHidden, isVisible } from "features/feature";
|
||||||
|
import { MaybeGetter } from "util/computed";
|
||||||
import { render, Renderable } from "util/vue";
|
import { render, Renderable } from "util/vue";
|
||||||
import { MaybeRef, unref, type CSSProperties } from "vue";
|
import { MaybeRef, unref, type CSSProperties } from "vue";
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
id: string;
|
id: string;
|
||||||
components: MaybeRef<Renderable>[];
|
components: MaybeGetter<Renderable>[];
|
||||||
wrappers: ((el: () => Renderable) => Renderable)[];
|
wrappers: ((el: () => Renderable) => Renderable)[];
|
||||||
visibility?: MaybeRef<Visibility | boolean>;
|
visibility?: MaybeRef<Visibility | boolean>;
|
||||||
style?: MaybeRef<CSSProperties>;
|
style?: MaybeRef<CSSProperties>;
|
||||||
|
|
|
@ -29,35 +29,7 @@ const props = defineProps<{
|
||||||
small: Achievement["small"];
|
small: Achievement["small"];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const Component = () => {
|
const Component = () => props.display == null ? <></> : render(props.display);
|
||||||
if (props.display == null) {
|
|
||||||
return null;
|
|
||||||
} else if (
|
|
||||||
isRef(props.display) ||
|
|
||||||
typeof props.display === "string" ||
|
|
||||||
isJSXElement(props.display)
|
|
||||||
) {
|
|
||||||
return render(props.display);
|
|
||||||
} else {
|
|
||||||
const { requirement, effectDisplay, optionsDisplay } = props.display;
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{requirement ?
|
|
||||||
render(requirement, el => <h3>{el}</h3>) :
|
|
||||||
displayRequirements(props.requirements ?? [])}
|
|
||||||
{effectDisplay ? (
|
|
||||||
<div>
|
|
||||||
{render(effectDisplay, el => <b>{el}</b>)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{optionsDisplay != null ? (
|
|
||||||
<div class="equal-spaced">
|
|
||||||
{render(optionsDisplay)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</span>);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
} from "game/requirements";
|
} from "game/requirements";
|
||||||
import settings, { registerSettingField } from "game/settings";
|
import settings, { registerSettingField } from "game/settings";
|
||||||
import { camelToTitle } from "util/common";
|
import { camelToTitle } from "util/common";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import {
|
import {
|
||||||
isJSXElement,
|
isJSXElement,
|
||||||
|
@ -24,7 +24,7 @@ import {
|
||||||
vueFeatureMixin,
|
vueFeatureMixin,
|
||||||
VueFeatureOptions
|
VueFeatureOptions
|
||||||
} from "util/vue";
|
} from "util/vue";
|
||||||
import { computed, isRef, MaybeRef, MaybeRefOrGetter, unref, watchEffect } from "vue";
|
import { computed, MaybeRef, MaybeRefOrGetter, unref, watchEffect } from "vue";
|
||||||
import { useToast } from "vue-toastification";
|
import { useToast } from "vue-toastification";
|
||||||
import Achievement from "./Achievement.vue";
|
import Achievement from "./Achievement.vue";
|
||||||
|
|
||||||
|
@ -50,14 +50,15 @@ export interface AchievementOptions extends VueFeatureOptions {
|
||||||
requirements?: Requirements;
|
requirements?: Requirements;
|
||||||
/** The display to use for this achievement. */
|
/** The display to use for this achievement. */
|
||||||
display?:
|
display?:
|
||||||
| MaybeRefOrGetter<Renderable>
|
| Renderable
|
||||||
|
| (() => Renderable)
|
||||||
| {
|
| {
|
||||||
/** Description of the requirement(s) for this achievement. If unspecified then the requirements will be displayed automatically based on {@link requirements}. */
|
/** Description of the requirement(s) for this achievement. If unspecified then the requirements will be displayed automatically based on {@link requirements}. */
|
||||||
requirement?: MaybeRefOrGetter<Renderable>;
|
requirement?: MaybeGetter<Renderable>;
|
||||||
/** Description of what will change (if anything) for achieving this. */
|
/** Description of what will change (if anything) for achieving this. */
|
||||||
effectDisplay?: MaybeRefOrGetter<Renderable>;
|
effectDisplay?: MaybeGetter<Renderable>;
|
||||||
/** Any additional things to display on this achievement, such as a toggle for it's effect. */
|
/** Any additional things to display on this achievement, such as a toggle for it's effect. */
|
||||||
optionsDisplay?: MaybeRefOrGetter<Renderable>;
|
optionsDisplay?: MaybeGetter<Renderable>;
|
||||||
};
|
};
|
||||||
/** Toggles a smaller design for the feature. */
|
/** Toggles a smaller design for the feature. */
|
||||||
small?: MaybeRefOrGetter<boolean>;
|
small?: MaybeRefOrGetter<boolean>;
|
||||||
|
@ -76,13 +77,7 @@ export interface Achievement extends VueFeature {
|
||||||
/** A function that is called when the achievement is completed. */
|
/** A function that is called when the achievement is completed. */
|
||||||
onComplete?: VoidFunction;
|
onComplete?: VoidFunction;
|
||||||
/** The display to use for this achievement. */
|
/** The display to use for this achievement. */
|
||||||
display?:
|
display?: MaybeGetter<Renderable>;
|
||||||
| MaybeRef<Renderable>
|
|
||||||
| {
|
|
||||||
requirement?: MaybeRef<Renderable>;
|
|
||||||
effectDisplay?: MaybeRef<Renderable>;
|
|
||||||
optionsDisplay?: MaybeRef<Renderable>;
|
|
||||||
};
|
|
||||||
/** Toggles a smaller design for the feature. */
|
/** Toggles a smaller design for the feature. */
|
||||||
small?: MaybeRef<boolean>;
|
small?: MaybeRef<boolean>;
|
||||||
/** An image to display as the background for this achievement. */
|
/** An image to display as the background for this achievement. */
|
||||||
|
@ -105,7 +100,15 @@ export function createAchievement<T extends AchievementOptions>(optionsFunc?: ()
|
||||||
const earned = persistent<boolean>(false, false);
|
const earned = persistent<boolean>(false, false);
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(() => {
|
||||||
const options = optionsFunc?.() ?? ({} as T);
|
const options = optionsFunc?.() ?? ({} as T);
|
||||||
const { requirements, display, small, image, showPopups, onComplete, ...props } = options;
|
const {
|
||||||
|
requirements,
|
||||||
|
display: _display,
|
||||||
|
small,
|
||||||
|
image,
|
||||||
|
showPopups,
|
||||||
|
onComplete,
|
||||||
|
...props
|
||||||
|
} = options;
|
||||||
|
|
||||||
const vueFeature = vueFeatureMixin("achievement", options, () => (
|
const vueFeature = vueFeatureMixin("achievement", options, () => (
|
||||||
<Achievement
|
<Achievement
|
||||||
|
@ -117,12 +120,35 @@ export function createAchievement<T extends AchievementOptions>(optionsFunc?: ()
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let display: MaybeGetter<Renderable> | undefined = undefined;
|
||||||
|
if (typeof _display === "object" && !isJSXElement(_display)) {
|
||||||
|
const { requirement, effectDisplay, optionsDisplay } = _display;
|
||||||
|
display = () => (
|
||||||
|
<span>
|
||||||
|
{requirement == null
|
||||||
|
? displayRequirements(requirements ?? [])
|
||||||
|
: render(requirement, el => <h3>{el}</h3>)}
|
||||||
|
{effectDisplay == null ? null : (
|
||||||
|
<div>
|
||||||
|
{render(effectDisplay, el => (
|
||||||
|
<b>{el}</b>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{optionsDisplay != null ? (
|
||||||
|
<div class="equal-spaced">{render(optionsDisplay)}</div>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if (_display != null) {
|
||||||
|
display = _display;
|
||||||
|
}
|
||||||
|
|
||||||
const achievement = {
|
const achievement = {
|
||||||
type: AchievementType,
|
type: AchievementType,
|
||||||
...(props as Omit<typeof props, keyof VueFeature | keyof AchievementOptions>),
|
...(props as Omit<typeof props, keyof VueFeature | keyof AchievementOptions>),
|
||||||
...vueFeature,
|
...vueFeature,
|
||||||
visibility: computed(() => {
|
visibility: computed(() => {
|
||||||
const display = unref((achievement as Achievement).display);
|
|
||||||
switch (settings.msDisplay) {
|
switch (settings.msDisplay) {
|
||||||
default:
|
default:
|
||||||
case AchievementDisplay.All:
|
case AchievementDisplay.All:
|
||||||
|
@ -131,9 +157,9 @@ export function createAchievement<T extends AchievementOptions>(optionsFunc?: ()
|
||||||
if (
|
if (
|
||||||
unref(earned) &&
|
unref(earned) &&
|
||||||
!(
|
!(
|
||||||
display != null &&
|
_display != null &&
|
||||||
typeof display === "object" &&
|
typeof _display === "object" &&
|
||||||
"optionsDisplay" in display
|
!isJSXElement(_display)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return Visibility.None;
|
return Visibility.None;
|
||||||
|
@ -153,19 +179,7 @@ export function createAchievement<T extends AchievementOptions>(optionsFunc?: ()
|
||||||
small: processGetter(small),
|
small: processGetter(small),
|
||||||
image: processGetter(image),
|
image: processGetter(image),
|
||||||
showPopups: processGetter(showPopups) ?? true,
|
showPopups: processGetter(showPopups) ?? true,
|
||||||
display:
|
display,
|
||||||
display == null
|
|
||||||
? undefined
|
|
||||||
: isRef(display) ||
|
|
||||||
typeof display === "string" ||
|
|
||||||
typeof display === "function" ||
|
|
||||||
isJSXElement(display)
|
|
||||||
? processGetter(display)
|
|
||||||
: {
|
|
||||||
requirement: processGetter(display.requirement),
|
|
||||||
effectDisplay: processGetter(display.effectDisplay),
|
|
||||||
optionsDisplay: processGetter(display.optionsDisplay)
|
|
||||||
},
|
|
||||||
requirements:
|
requirements:
|
||||||
requirements == null
|
requirements == null
|
||||||
? undefined
|
? undefined
|
||||||
|
@ -181,19 +195,18 @@ export function createAchievement<T extends AchievementOptions>(optionsFunc?: ()
|
||||||
earned.value = true;
|
earned.value = true;
|
||||||
achievement.onComplete?.();
|
achievement.onComplete?.();
|
||||||
if (achievement.display != null && unref(achievement.showPopups) === true) {
|
if (achievement.display != null && unref(achievement.showPopups) === true) {
|
||||||
const display = achievement.display;
|
let display = achievement.display;
|
||||||
let Display;
|
if (typeof _display === "object" && !isJSXElement(_display)) {
|
||||||
if (isRef(display) || typeof display === "string" || isJSXElement(display)) {
|
if (_display.requirement != null) {
|
||||||
Display = () => render(display);
|
display = _display.requirement;
|
||||||
} else if (display.requirement != null) {
|
} else {
|
||||||
Display = () => render(display.requirement!);
|
display = displayRequirements(requirements ?? []);
|
||||||
} else {
|
}
|
||||||
Display = () => displayRequirements(achievement.requirements ?? []);
|
|
||||||
}
|
}
|
||||||
toast.info(
|
toast.info(
|
||||||
<div>
|
<div>
|
||||||
<h3>Achievement earned!</h3>
|
<h3>Achievement earned!</h3>
|
||||||
<div>{Display()}</div>
|
<div>{render(display)}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Bar from "features/bars/Bar.vue";
|
import Bar from "features/bars/Bar.vue";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import { Direction } from "util/common";
|
import { Direction } from "util/common";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
||||||
import { CSSProperties, MaybeRef, MaybeRefOrGetter } from "vue";
|
import { CSSProperties, MaybeRef, MaybeRefOrGetter } from "vue";
|
||||||
|
@ -30,7 +30,7 @@ export interface BarOptions extends VueFeatureOptions {
|
||||||
/** The progress value of the bar, from 0 to 1. */
|
/** The progress value of the bar, from 0 to 1. */
|
||||||
progress: MaybeRefOrGetter<DecimalSource>;
|
progress: MaybeRefOrGetter<DecimalSource>;
|
||||||
/** The display to use for this bar. */
|
/** The display to use for this bar. */
|
||||||
display?: MaybeRefOrGetter<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An object that represents a feature that displays some sort of progress or completion or resource with a cap. */
|
/** An object that represents a feature that displays some sort of progress or completion or resource with a cap. */
|
||||||
|
@ -52,7 +52,7 @@ export interface Bar extends VueFeature {
|
||||||
/** The progress value of the bar, from 0 to 1. */
|
/** The progress value of the bar, from 0 to 1. */
|
||||||
progress: MaybeRef<DecimalSource>;
|
progress: MaybeRef<DecimalSource>;
|
||||||
/** The display to use for this bar. */
|
/** The display to use for this bar. */
|
||||||
display?: MaybeRef<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
/** A symbol that helps identify features of the same type. */
|
/** A symbol that helps identify features of the same type. */
|
||||||
type: typeof BarType;
|
type: typeof BarType;
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ export function createBar<T extends BarOptions>(optionsFunc: () => T) {
|
||||||
textStyle: processGetter(textStyle),
|
textStyle: processGetter(textStyle),
|
||||||
fillStyle: processGetter(fillStyle),
|
fillStyle: processGetter(fillStyle),
|
||||||
progress: processGetter(progress),
|
progress: processGetter(progress),
|
||||||
display: processGetter(display)
|
display
|
||||||
} satisfies Bar;
|
} satisfies Bar;
|
||||||
|
|
||||||
return bar;
|
return bar;
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import "components/common/features.css";
|
import "components/common/features.css";
|
||||||
import { getHighNotifyStyle, getNotifyStyle } from "game/notifications";
|
import { getHighNotifyStyle, getNotifyStyle } from "game/notifications";
|
||||||
import { displayRequirements } from "game/requirements";
|
|
||||||
import { render } from "util/vue";
|
import { render } from "util/vue";
|
||||||
import type { Component } from "vue";
|
import type { Component } from "vue";
|
||||||
import { computed, unref } from "vue";
|
import { computed, unref } from "vue";
|
||||||
|
@ -61,34 +60,7 @@ const notifyStyle = computed(() => {
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
const Component = () => {
|
const Component = () => props.display == null ? <></> : render(props.display);
|
||||||
if (props.display == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (typeof props.display === "object" && "description" in props.display) {
|
|
||||||
const { title, description, goal, reward, effectDisplay } = props.display;
|
|
||||||
return <span>
|
|
||||||
{title != null ? (<div>{render(title, el => <h3>{el}</h3>)}</div>) : null}
|
|
||||||
{render(description, el => <div>{el}</div>)}
|
|
||||||
<div>
|
|
||||||
<br />
|
|
||||||
Goal: {goal == null ? displayRequirements(props.requirements) : render(goal, el => <h3>{el}</h3>)}
|
|
||||||
</div>
|
|
||||||
{reward != null ? (
|
|
||||||
<div>
|
|
||||||
<br />
|
|
||||||
Reward: {render(reward)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{effectDisplay != null ? (
|
|
||||||
<div>
|
|
||||||
Currently: {render(effectDisplay)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</span>;
|
|
||||||
}
|
|
||||||
return render(props.display);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -4,13 +4,20 @@ import type { Reset } from "features/reset";
|
||||||
import { globalBus } from "game/events";
|
import { globalBus } from "game/events";
|
||||||
import type { Persistent } from "game/persistence";
|
import type { Persistent } from "game/persistence";
|
||||||
import { persistent } from "game/persistence";
|
import { persistent } from "game/persistence";
|
||||||
import { Requirements, maxRequirementsMet } from "game/requirements";
|
import { Requirements, displayRequirements, maxRequirementsMet } from "game/requirements";
|
||||||
import settings, { registerSettingField } from "game/settings";
|
import settings, { registerSettingField } from "game/settings";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import Decimal from "util/bignum";
|
import Decimal from "util/bignum";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { Renderable, VueFeature, VueFeatureOptions, vueFeatureMixin } from "util/vue";
|
import {
|
||||||
|
Renderable,
|
||||||
|
VueFeature,
|
||||||
|
VueFeatureOptions,
|
||||||
|
isJSXElement,
|
||||||
|
render,
|
||||||
|
vueFeatureMixin
|
||||||
|
} from "util/vue";
|
||||||
import type { MaybeRef, MaybeRefOrGetter, Ref, WatchStopHandle } from "vue";
|
import type { MaybeRef, MaybeRefOrGetter, Ref, WatchStopHandle } from "vue";
|
||||||
import { computed, unref, watch } from "vue";
|
import { computed, unref, watch } from "vue";
|
||||||
import Challenge from "./Challenge.vue";
|
import Challenge from "./Challenge.vue";
|
||||||
|
@ -32,18 +39,19 @@ export interface ChallengeOptions extends VueFeatureOptions {
|
||||||
completionLimit?: MaybeRefOrGetter<DecimalSource>;
|
completionLimit?: MaybeRefOrGetter<DecimalSource>;
|
||||||
/** The display to use for this challenge. */
|
/** The display to use for this challenge. */
|
||||||
display?:
|
display?:
|
||||||
| MaybeRefOrGetter<Renderable>
|
| Renderable
|
||||||
|
| (() => Renderable)
|
||||||
| {
|
| {
|
||||||
/** A header to appear at the top of the display. */
|
/** A header to appear at the top of the display. */
|
||||||
title?: MaybeRefOrGetter<Renderable>;
|
title?: MaybeGetter<Renderable>;
|
||||||
/** The main text that appears in the display. */
|
/** The main text that appears in the display. */
|
||||||
description: MaybeRefOrGetter<Renderable>;
|
description: MaybeGetter<Renderable>;
|
||||||
/** A description of the current goal for this challenge. If unspecified then the requirements will be displayed automatically based on {@link requirements}. */
|
/** A description of the current goal for this challenge. If unspecified then the requirements will be displayed automatically based on {@link requirements}. */
|
||||||
goal?: MaybeRefOrGetter<Renderable>;
|
goal?: MaybeGetter<Renderable>;
|
||||||
/** A description of what will change upon completing this challenge. */
|
/** A description of what will change upon completing this challenge. */
|
||||||
reward?: MaybeRefOrGetter<Renderable>;
|
reward?: MaybeGetter<Renderable>;
|
||||||
/** A description of the current effect of this challenge. */
|
/** A description of the current effect of this challenge. */
|
||||||
effectDisplay?: MaybeRefOrGetter<Renderable>;
|
effectDisplay?: MaybeGetter<Renderable>;
|
||||||
};
|
};
|
||||||
/** A function that is called when the challenge is completed. */
|
/** A function that is called when the challenge is completed. */
|
||||||
onComplete?: VoidFunction;
|
onComplete?: VoidFunction;
|
||||||
|
@ -70,20 +78,7 @@ export interface Challenge extends VueFeature {
|
||||||
/** The maximum number of times the challenge can be completed. */
|
/** The maximum number of times the challenge can be completed. */
|
||||||
completionLimit?: MaybeRef<DecimalSource>;
|
completionLimit?: MaybeRef<DecimalSource>;
|
||||||
/** The display to use for this challenge. */
|
/** The display to use for this challenge. */
|
||||||
display?:
|
display?: MaybeGetter<Renderable>;
|
||||||
| MaybeRef<Renderable>
|
|
||||||
| {
|
|
||||||
/** A header to appear at the top of the display. */
|
|
||||||
title?: MaybeRef<Renderable>;
|
|
||||||
/** The main text that appears in the display. */
|
|
||||||
description: MaybeRef<Renderable>;
|
|
||||||
/** A description of the current goal for this challenge. If unspecified then the requirements will be displayed automatically based on {@link requirements}. */
|
|
||||||
goal?: MaybeRef<Renderable>;
|
|
||||||
/** A description of what will change upon completing this challenge. */
|
|
||||||
reward?: MaybeRef<Renderable>;
|
|
||||||
/** A description of the current effect of this challenge. */
|
|
||||||
effectDisplay?: MaybeRef<Renderable>;
|
|
||||||
};
|
|
||||||
/** The current amount of times this challenge can be completed. */
|
/** The current amount of times this challenge can be completed. */
|
||||||
canComplete: Ref<DecimalSource>;
|
canComplete: Ref<DecimalSource>;
|
||||||
/** The current number of times this challenge has been completed. */
|
/** The current number of times this challenge has been completed. */
|
||||||
|
@ -118,7 +113,7 @@ export function createChallenge<T extends ChallengeOptions>(optionsFunc: () => T
|
||||||
requirements,
|
requirements,
|
||||||
canStart,
|
canStart,
|
||||||
completionLimit,
|
completionLimit,
|
||||||
display,
|
display: _display,
|
||||||
reset,
|
reset,
|
||||||
onComplete,
|
onComplete,
|
||||||
onEnter,
|
onEnter,
|
||||||
|
@ -139,6 +134,41 @@ export function createChallenge<T extends ChallengeOptions>(optionsFunc: () => T
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let display: MaybeGetter<Renderable> | undefined = undefined;
|
||||||
|
if (typeof _display === "object" && !isJSXElement(_display)) {
|
||||||
|
const { title, description, goal, reward, effectDisplay } = _display;
|
||||||
|
display = () => (
|
||||||
|
<span>
|
||||||
|
{title != null ? (
|
||||||
|
<div>
|
||||||
|
{render(title, el => (
|
||||||
|
<h3>{el}</h3>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{render(description, el => (
|
||||||
|
<div>{el}</div>
|
||||||
|
))}
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
Goal:{" "}
|
||||||
|
{goal == null
|
||||||
|
? displayRequirements(challenge.requirements)
|
||||||
|
: render(goal, el => <h3>{el}</h3>)}
|
||||||
|
</div>
|
||||||
|
{reward != null ? (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
Reward: {render(reward)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{effectDisplay != null ? <div>Currently: {render(effectDisplay)}</div> : null}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if (_display != null) {
|
||||||
|
display = _display;
|
||||||
|
}
|
||||||
|
|
||||||
const challenge = {
|
const challenge = {
|
||||||
type: ChallengeType,
|
type: ChallengeType,
|
||||||
...(props as Omit<typeof props, keyof VueFeature | keyof ChallengeOptions>),
|
...(props as Omit<typeof props, keyof VueFeature | keyof ChallengeOptions>),
|
||||||
|
@ -157,18 +187,7 @@ export function createChallenge<T extends ChallengeOptions>(optionsFunc: () => T
|
||||||
onComplete,
|
onComplete,
|
||||||
onEnter,
|
onEnter,
|
||||||
onExit,
|
onExit,
|
||||||
display:
|
display,
|
||||||
display == null
|
|
||||||
? undefined
|
|
||||||
: typeof display === "object" && "description" in display
|
|
||||||
? {
|
|
||||||
title: processGetter(display.title),
|
|
||||||
description: processGetter(display.description),
|
|
||||||
goal: processGetter(display.goal),
|
|
||||||
reward: processGetter(display.reward),
|
|
||||||
effectDisplay: processGetter(display.effectDisplay)
|
|
||||||
}
|
|
||||||
: processGetter(display),
|
|
||||||
toggle: function () {
|
toggle: function () {
|
||||||
if (active.value) {
|
if (active.value) {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -5,11 +5,10 @@ import { persistent } from "game/persistence";
|
||||||
import Decimal, { DecimalSource } from "lib/break_eternity";
|
import Decimal, { DecimalSource } from "lib/break_eternity";
|
||||||
import { Unsubscribe } from "nanoevents";
|
import { Unsubscribe } from "nanoevents";
|
||||||
import { Direction } from "util/common";
|
import { Direction } from "util/common";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { render, Renderable, VueFeature, vueFeatureMixin } from "util/vue";
|
import { isJSXElement, render, Renderable, VueFeature, vueFeatureMixin } from "util/vue";
|
||||||
import { computed, MaybeRef, MaybeRefOrGetter, Ref, ref, unref } from "vue";
|
import { computed, MaybeRef, MaybeRefOrGetter, Ref, ref, unref } from "vue";
|
||||||
import { JSX } from "vue/jsx-runtime";
|
|
||||||
import { Bar, BarOptions, createBar } from "../bars/bar";
|
import { Bar, BarOptions, createBar } from "../bars/bar";
|
||||||
import { type Clickable, ClickableOptions } from "./clickable";
|
import { type Clickable, ClickableOptions } from "./clickable";
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ export interface Action extends VueFeature {
|
||||||
/** Whether or not the action may be performed. */
|
/** Whether or not the action may be performed. */
|
||||||
canClick: MaybeRef<boolean>;
|
canClick: MaybeRef<boolean>;
|
||||||
/** The display to use for this action. */
|
/** The display to use for this action. */
|
||||||
display?: MaybeRef<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
/** A function that is called when the action is clicked. */
|
/** A function that is called when the action is clicked. */
|
||||||
onClick: (amount: DecimalSource) => void;
|
onClick: (amount: DecimalSource) => void;
|
||||||
/** Whether or not the player is holding down the action. Actions will be considered clicked as soon as the cooldown completes when being held down. */
|
/** Whether or not the player is holding down the action. Actions will be considered clicked as soon as the cooldown completes when being held down. */
|
||||||
|
@ -62,8 +61,16 @@ export function createAction<T extends ActionOptions>(optionsFunc?: () => T) {
|
||||||
const progress = persistent<DecimalSource>(0);
|
const progress = persistent<DecimalSource>(0);
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(() => {
|
||||||
const options = optionsFunc?.() ?? ({} as T);
|
const options = optionsFunc?.() ?? ({} as T);
|
||||||
const { style, duration, canClick, autoStart, display, barOptions, onClick, ...props } =
|
const {
|
||||||
options;
|
style,
|
||||||
|
duration,
|
||||||
|
canClick,
|
||||||
|
autoStart,
|
||||||
|
display: _display,
|
||||||
|
barOptions,
|
||||||
|
onClick,
|
||||||
|
...props
|
||||||
|
} = options;
|
||||||
|
|
||||||
const processedCanClick = processGetter(canClick) ?? true;
|
const processedCanClick = processGetter(canClick) ?? true;
|
||||||
const processedStyle = processGetter(style);
|
const processedStyle = processGetter(style);
|
||||||
|
@ -78,29 +85,24 @@ export function createAction<T extends ActionOptions>(optionsFunc?: () => T) {
|
||||||
...(barOptions as Omit<typeof barOptions, keyof VueFeature>)
|
...(barOptions as Omit<typeof barOptions, keyof VueFeature>)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let Component: () => JSX.Element;
|
let display: MaybeGetter<Renderable>;
|
||||||
if (typeof display === "object" && "description" in display) {
|
if (typeof _display === "object" && !isJSXElement(_display)) {
|
||||||
const title = processGetter(display.title);
|
display = () => (
|
||||||
const description = processGetter(display.description);
|
<span>
|
||||||
|
{_display.title != null ? (
|
||||||
const Title = () => (title == null ? <></> : render(title, el => <h3>{el}</h3>));
|
<div>
|
||||||
const Description = () => render(description, el => <div>{el}</div>);
|
{render(_display.title, el => (
|
||||||
|
<h3>{el}</h3>
|
||||||
Component = () => {
|
))}
|
||||||
return (
|
</div>
|
||||||
<span>
|
) : null}
|
||||||
{title != null ? (
|
{render(_display.description, el => (
|
||||||
<div>
|
<div>{el}</div>
|
||||||
<Title />
|
))}
|
||||||
</div>
|
</span>
|
||||||
) : null}
|
);
|
||||||
<Description />
|
} else if (_display != null) {
|
||||||
</span>
|
display = _display;
|
||||||
);
|
|
||||||
};
|
|
||||||
} else if (display != null) {
|
|
||||||
const processedDisplay = processGetter(display);
|
|
||||||
Component = () => render(processedDisplay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const action = {
|
const action = {
|
||||||
|
@ -135,14 +137,14 @@ export function createAction<T extends ActionOptions>(optionsFunc?: () => T) {
|
||||||
unref(processedCanClick) && Decimal.gte(progress.value, unref(action.duration))
|
unref(processedCanClick) && Decimal.gte(progress.value, unref(action.duration))
|
||||||
),
|
),
|
||||||
autoStart: processGetter(autoStart) ?? false,
|
autoStart: processGetter(autoStart) ?? false,
|
||||||
display: computed(() => (
|
display: () => (
|
||||||
<>
|
<>
|
||||||
<div style="flex-grow: 1" />
|
<div style="flex-grow: 1" />
|
||||||
{display == null ? null : <Component />}
|
{display == null ? null : render(display)}
|
||||||
<div style="flex-grow: 1" />
|
<div style="flex-grow: 1" />
|
||||||
{render(progressBar)}
|
{render(progressBar)}
|
||||||
</>
|
</>
|
||||||
)),
|
),
|
||||||
progressBar,
|
progressBar,
|
||||||
onClick: function () {
|
onClick: function () {
|
||||||
if (unref(action.canClick) === false) {
|
if (unref(action.canClick) === false) {
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import Clickable from "features/clickables/Clickable.vue";
|
import Clickable from "features/clickables/Clickable.vue";
|
||||||
import type { BaseLayer } from "game/layers";
|
import type { BaseLayer } from "game/layers";
|
||||||
import type { Unsubscribe } from "nanoevents";
|
import type { Unsubscribe } from "nanoevents";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { render, Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
import {
|
||||||
|
isJSXElement,
|
||||||
|
render,
|
||||||
|
Renderable,
|
||||||
|
VueFeature,
|
||||||
|
vueFeatureMixin,
|
||||||
|
VueFeatureOptions
|
||||||
|
} from "util/vue";
|
||||||
import { computed, MaybeRef, MaybeRefOrGetter, unref } from "vue";
|
import { computed, MaybeRef, MaybeRefOrGetter, unref } from "vue";
|
||||||
|
|
||||||
/** A symbol used to identify {@link Clickable} features. */
|
/** A symbol used to identify {@link Clickable} features. */
|
||||||
|
@ -17,12 +24,13 @@ export interface ClickableOptions extends VueFeatureOptions {
|
||||||
canClick?: MaybeRefOrGetter<boolean>;
|
canClick?: MaybeRefOrGetter<boolean>;
|
||||||
/** The display to use for this clickable. */
|
/** The display to use for this clickable. */
|
||||||
display?:
|
display?:
|
||||||
| MaybeRefOrGetter<Renderable>
|
| Renderable
|
||||||
|
| (() => Renderable)
|
||||||
| {
|
| {
|
||||||
/** A header to appear at the top of the display. */
|
/** A header to appear at the top of the display. */
|
||||||
title?: MaybeRefOrGetter<Renderable>;
|
title?: MaybeGetter<Renderable>;
|
||||||
/** The main text that appears in the display. */
|
/** The main text that appears in the display. */
|
||||||
description: MaybeRefOrGetter<Renderable>;
|
description: MaybeGetter<Renderable>;
|
||||||
};
|
};
|
||||||
/** A function that is called when the clickable is clicked. */
|
/** A function that is called when the clickable is clicked. */
|
||||||
onClick?: (e?: MouseEvent | TouchEvent) => void;
|
onClick?: (e?: MouseEvent | TouchEvent) => void;
|
||||||
|
@ -39,7 +47,7 @@ export interface Clickable extends VueFeature {
|
||||||
/** Whether or not the clickable may be clicked. */
|
/** Whether or not the clickable may be clicked. */
|
||||||
canClick: MaybeRef<boolean>;
|
canClick: MaybeRef<boolean>;
|
||||||
/** The display to use for this clickable. */
|
/** The display to use for this clickable. */
|
||||||
display?: MaybeRef<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
/** A symbol that helps identify features of the same type. */
|
/** A symbol that helps identify features of the same type. */
|
||||||
type: typeof ClickableType;
|
type: typeof ClickableType;
|
||||||
}
|
}
|
||||||
|
@ -53,26 +61,20 @@ export function createClickable<T extends ClickableOptions>(optionsFunc?: () =>
|
||||||
const options = optionsFunc?.() ?? ({} as T);
|
const options = optionsFunc?.() ?? ({} as T);
|
||||||
const { canClick, display: _display, onClick: onClick, onHold: onHold, ...props } = options;
|
const { canClick, display: _display, onClick: onClick, onHold: onHold, ...props } = options;
|
||||||
|
|
||||||
let display: MaybeRef<Renderable> | undefined = undefined;
|
let display: MaybeGetter<Renderable> | undefined = undefined;
|
||||||
if (typeof _display === "object" && "description" in _display) {
|
if (typeof _display === "object" && !isJSXElement(_display)) {
|
||||||
const title = processGetter(_display.title);
|
display = () => (
|
||||||
const description = processGetter(_display.description);
|
|
||||||
|
|
||||||
const Title = () => (title == null ? <></> : render(title, el => <h3>{el}</h3>));
|
|
||||||
const Description = () => render(description, el => <div>{el}</div>);
|
|
||||||
|
|
||||||
display = computed(() => (
|
|
||||||
<span>
|
<span>
|
||||||
{title != null ? (
|
{_display.title != null ? (
|
||||||
<div>
|
<div>
|
||||||
<Title />
|
{render(_display.title, el => <h3>{el}</h3>)}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<Description />
|
{render(_display.description, el => <div>{el}</div>)}
|
||||||
</span>
|
</span>
|
||||||
));
|
);
|
||||||
} else if (_display != null) {
|
} else if (_display != null) {
|
||||||
display = processGetter(_display);
|
display = _display;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clickable = {
|
const clickable = {
|
||||||
|
|
|
@ -11,11 +11,11 @@ import {
|
||||||
} from "game/requirements";
|
} from "game/requirements";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import Decimal, { formatWhole } from "util/bignum";
|
import Decimal, { formatWhole } from "util/bignum";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { isJSXElement, render, Renderable, VueFeature, vueFeatureMixin } from "util/vue";
|
import { isJSXElement, render, Renderable, VueFeature, vueFeatureMixin } from "util/vue";
|
||||||
import type { MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
import type { MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
||||||
import { computed, isRef, unref } from "vue";
|
import { computed, unref } from "vue";
|
||||||
import { ClickableOptions } from "./clickable";
|
import { ClickableOptions } from "./clickable";
|
||||||
|
|
||||||
/** A symbol used to identify {@link Repeatable} features. */
|
/** A symbol used to identify {@link Repeatable} features. */
|
||||||
|
@ -31,14 +31,15 @@ export interface RepeatableOptions extends ClickableOptions {
|
||||||
initialAmount?: DecimalSource;
|
initialAmount?: DecimalSource;
|
||||||
/** The display to use for this repeatable. */
|
/** The display to use for this repeatable. */
|
||||||
display?:
|
display?:
|
||||||
| MaybeRefOrGetter<Renderable>
|
| Renderable
|
||||||
|
| (() => Renderable)
|
||||||
| {
|
| {
|
||||||
/** A header to appear at the top of the display. */
|
/** A header to appear at the top of the display. */
|
||||||
title?: MaybeRefOrGetter<Renderable>;
|
title?: MaybeGetter<Renderable>;
|
||||||
/** The main text that appears in the display. */
|
/** The main text that appears in the display. */
|
||||||
description: MaybeRefOrGetter<Renderable>;
|
description: MaybeGetter<Renderable>;
|
||||||
/** A description of the current effect of this repeatable, based off its amount. */
|
/** A description of the current effect of this repeatable, based off its amount. */
|
||||||
effectDisplay?: MaybeRefOrGetter<Renderable>;
|
effectDisplay?: MaybeGetter<Renderable>;
|
||||||
/** Whether or not to show the current amount of this repeatable at the bottom of the display. */
|
/** Whether or not to show the current amount of this repeatable at the bottom of the display. */
|
||||||
showAmount?: boolean;
|
showAmount?: boolean;
|
||||||
};
|
};
|
||||||
|
@ -53,7 +54,7 @@ export interface Repeatable extends VueFeature {
|
||||||
/** The initial amount this repeatable has on a new save / after reset. */
|
/** The initial amount this repeatable has on a new save / after reset. */
|
||||||
initialAmount?: DecimalSource;
|
initialAmount?: DecimalSource;
|
||||||
/** The display to use for this repeatable. */
|
/** The display to use for this repeatable. */
|
||||||
display?: MaybeRef<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
/** Whether or not the repeatable may be clicked. */
|
/** Whether or not the repeatable may be clicked. */
|
||||||
canClick: Ref<boolean>;
|
canClick: Ref<boolean>;
|
||||||
/** A function that is called when the repeatable is clicked. */
|
/** A function that is called when the repeatable is clicked. */
|
||||||
|
@ -119,25 +120,19 @@ export function createRepeatable<T extends RepeatableOptions>(optionsFunc: () =>
|
||||||
}
|
}
|
||||||
|
|
||||||
let display;
|
let display;
|
||||||
if (typeof _display === "object" && !isRef(_display) && !isJSXElement(_display)) {
|
if (typeof _display === "object" && !isJSXElement(_display)) {
|
||||||
const title = processGetter(_display.title);
|
const { title, description, effectDisplay, showAmount } = _display;
|
||||||
const description = processGetter(_display.description);
|
|
||||||
const effectDisplay = processGetter(_display.effectDisplay);
|
|
||||||
const showAmount = processGetter(_display.showAmount);
|
|
||||||
|
|
||||||
const Title = title == null ? null : () => render(title, el => <h3>{el}</h3>);
|
display = () => (
|
||||||
const Description = () => render(description, el => <>{el}</>);
|
|
||||||
const EffectDisplay =
|
|
||||||
effectDisplay == null ? null : () => render(effectDisplay, el => <>{el}</>);
|
|
||||||
|
|
||||||
display = computed(() => (
|
|
||||||
<span>
|
<span>
|
||||||
{Title == null ? null : (
|
{title == null ? null : (
|
||||||
<div>
|
<div>
|
||||||
<Title />
|
{render(title, el => (
|
||||||
|
<h3>{el}</h3>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Description />
|
{render(description)}
|
||||||
{showAmount === false ? null : (
|
{showAmount === false ? null : (
|
||||||
<div>
|
<div>
|
||||||
<br />
|
<br />
|
||||||
|
@ -147,10 +142,10 @@ export function createRepeatable<T extends RepeatableOptions>(optionsFunc: () =>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{EffectDisplay == null ? null : (
|
{effectDisplay == null ? null : (
|
||||||
<div>
|
<div>
|
||||||
<br />
|
<br />
|
||||||
Currently: <EffectDisplay />
|
Currently: {render(effectDisplay)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{unref(repeatable.maxed) ? null : (
|
{unref(repeatable.maxed) ? null : (
|
||||||
|
@ -160,12 +155,9 @@ export function createRepeatable<T extends RepeatableOptions>(optionsFunc: () =>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
));
|
);
|
||||||
} else if (_display != null) {
|
} else if (_display != null) {
|
||||||
const processedDisplay = processGetter(_display);
|
display = _display;
|
||||||
display = computed(() => render(processedDisplay));
|
|
||||||
} else {
|
|
||||||
display = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
amount[DefaultValue] = initialAmount ?? 0;
|
amount[DefaultValue] = initialAmount ?? 0;
|
||||||
|
|
|
@ -10,9 +10,16 @@ import {
|
||||||
requirementsMet
|
requirementsMet
|
||||||
} from "game/requirements";
|
} from "game/requirements";
|
||||||
import { isFunction } from "util/common";
|
import { isFunction } from "util/common";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { Renderable, VueFeature, VueFeatureOptions, render, vueFeatureMixin } from "util/vue";
|
import {
|
||||||
|
Renderable,
|
||||||
|
VueFeature,
|
||||||
|
VueFeatureOptions,
|
||||||
|
isJSXElement,
|
||||||
|
render,
|
||||||
|
vueFeatureMixin
|
||||||
|
} from "util/vue";
|
||||||
import type { MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
import type { MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
||||||
import { computed, unref } from "vue";
|
import { computed, unref } from "vue";
|
||||||
import Clickable from "./Clickable.vue";
|
import Clickable from "./Clickable.vue";
|
||||||
|
@ -27,14 +34,15 @@ export const UpgradeType = Symbol("Upgrade");
|
||||||
export interface UpgradeOptions extends VueFeatureOptions, ClickableOptions {
|
export interface UpgradeOptions extends VueFeatureOptions, ClickableOptions {
|
||||||
/** The display to use for this upgrade. */
|
/** The display to use for this upgrade. */
|
||||||
display?:
|
display?:
|
||||||
| MaybeRefOrGetter<Renderable>
|
| Renderable
|
||||||
|
| (() => Renderable)
|
||||||
| {
|
| {
|
||||||
/** A header to appear at the top of the display. */
|
/** A header to appear at the top of the display. */
|
||||||
title?: MaybeRefOrGetter<Renderable>;
|
title?: MaybeGetter<Renderable>;
|
||||||
/** The main text that appears in the display. */
|
/** The main text that appears in the display. */
|
||||||
description: MaybeRefOrGetter<Renderable>;
|
description: MaybeGetter<Renderable>;
|
||||||
/** A description of the current effect of the achievement. Useful when the effect changes dynamically. */
|
/** A description of the current effect of the achievement. Useful when the effect changes dynamically. */
|
||||||
effectDisplay?: MaybeRefOrGetter<Renderable>;
|
effectDisplay?: MaybeGetter<Renderable>;
|
||||||
};
|
};
|
||||||
/** The requirements to purchase this upgrade. */
|
/** The requirements to purchase this upgrade. */
|
||||||
requirements: Requirements;
|
requirements: Requirements;
|
||||||
|
@ -47,7 +55,7 @@ export interface Upgrade extends VueFeature {
|
||||||
/** The requirements to purchase this upgrade. */
|
/** The requirements to purchase this upgrade. */
|
||||||
requirements: Requirements;
|
requirements: Requirements;
|
||||||
/** The display to use for this upgrade. */
|
/** The display to use for this upgrade. */
|
||||||
display?: MaybeRef<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
/** Whether or not this upgrade has been purchased. */
|
/** Whether or not this upgrade has been purchased. */
|
||||||
bought: Persistent<boolean>;
|
bought: Persistent<boolean>;
|
||||||
/** Whether or not the upgrade can currently be purchased. */
|
/** Whether or not the upgrade can currently be purchased. */
|
||||||
|
@ -92,30 +100,23 @@ export function createUpgrade<T extends UpgradeOptions>(optionsFunc: () => T) {
|
||||||
requirements.push(createVisibilityRequirement(vueFeature.visibility));
|
requirements.push(createVisibilityRequirement(vueFeature.visibility));
|
||||||
}
|
}
|
||||||
|
|
||||||
let display: MaybeRef<Renderable> | undefined = undefined;
|
let display;
|
||||||
if (typeof _display === "object" && "description" in _display) {
|
if (typeof _display === "object" && !isJSXElement(_display)) {
|
||||||
const title = processGetter(_display.title);
|
const { title, description, effectDisplay } = _display;
|
||||||
const description = processGetter(_display.description);
|
|
||||||
const effectDisplay = processGetter(_display.effectDisplay);
|
|
||||||
|
|
||||||
const Title = () => (title == null ? <></> : render(title, el => <h3>{el}</h3>));
|
display = () => (
|
||||||
const Description = () => render(description, el => <div>{el}</div>);
|
|
||||||
const EffectDisplay = () =>
|
|
||||||
effectDisplay == null ? <></> : render(effectDisplay, el => <>{el}</>);
|
|
||||||
|
|
||||||
display = computed(() => (
|
|
||||||
<span>
|
<span>
|
||||||
{title != null ? (
|
{title != null ? (
|
||||||
<div>
|
<div>
|
||||||
<Title />
|
{render(title, el => (
|
||||||
</div>
|
<h3>{el}</h3>
|
||||||
) : null}
|
))}
|
||||||
<Description />
|
|
||||||
{effectDisplay != null ? (
|
|
||||||
<div>
|
|
||||||
Currently: <EffectDisplay />
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{render(description, el => (
|
||||||
|
<div>{el}</div>
|
||||||
|
))}
|
||||||
|
{effectDisplay != null ? <div>Currently: {render(effectDisplay)}</div> : null}
|
||||||
{bought.value ? null : (
|
{bought.value ? null : (
|
||||||
<>
|
<>
|
||||||
<br />
|
<br />
|
||||||
|
@ -123,9 +124,9 @@ export function createUpgrade<T extends UpgradeOptions>(optionsFunc: () => T) {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
));
|
);
|
||||||
} else if (_display != null) {
|
} else if (_display != null) {
|
||||||
display = processGetter(_display);
|
display = _display;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upgrade = {
|
const upgrade = {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import type { BaseLayer } from "game/layers";
|
||||||
import { createBooleanRequirement } from "game/requirements";
|
import { createBooleanRequirement } from "game/requirements";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import Decimal from "util/bignum";
|
import Decimal from "util/bignum";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { Renderable } from "util/vue";
|
import { Renderable } from "util/vue";
|
||||||
import { computed, MaybeRef, MaybeRefOrGetter, unref } from "vue";
|
import { computed, MaybeRef, MaybeRefOrGetter, unref } from "vue";
|
||||||
|
@ -310,7 +310,7 @@ export function setupPassiveGeneration(
|
||||||
export function createCanConvertRequirement(
|
export function createCanConvertRequirement(
|
||||||
conversion: Conversion,
|
conversion: Conversion,
|
||||||
minGainAmount: MaybeRefOrGetter<DecimalSource> = 1,
|
minGainAmount: MaybeRefOrGetter<DecimalSource> = 1,
|
||||||
display?: MaybeRefOrGetter<Renderable>
|
display?: MaybeGetter<Renderable>
|
||||||
) {
|
) {
|
||||||
const computedMinGainAmount = processGetter(minGainAmount);
|
const computedMinGainAmount = processGetter(minGainAmount);
|
||||||
return createBooleanRequirement(
|
return createBooleanRequirement(
|
||||||
|
|
|
@ -47,9 +47,12 @@ export function findFeatures(obj: object, ...types: symbol[]): unknown[] {
|
||||||
const handleObject = (obj: object) => {
|
const handleObject = (obj: object) => {
|
||||||
Object.keys(obj).forEach(key => {
|
Object.keys(obj).forEach(key => {
|
||||||
const value: unknown = obj[key as keyof typeof obj];
|
const value: unknown = obj[key as keyof typeof obj];
|
||||||
if (value != null && typeof value === "object") {
|
if (
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
value != null &&
|
||||||
if (types.includes((value as Record<string, any>).type)) {
|
typeof value === "object" &&
|
||||||
|
(value as Record<string, unknown>).__v_isVNode !== true
|
||||||
|
) {
|
||||||
|
if (types.includes((value as Record<string, unknown>).type as symbol)) {
|
||||||
objects.push(value);
|
objects.push(value);
|
||||||
} else if (!(value instanceof Decimal) && !isRef(value)) {
|
} else if (!(value instanceof Decimal) && !isRef(value)) {
|
||||||
handleObject(value as Record<string, unknown>);
|
handleObject(value as Record<string, unknown>);
|
||||||
|
@ -66,7 +69,7 @@ export function getFirstFeature<T extends VueFeature>(
|
||||||
filter: (feature: T) => boolean
|
filter: (feature: T) => boolean
|
||||||
): {
|
): {
|
||||||
firstFeature: Ref<T | undefined>;
|
firstFeature: Ref<T | undefined>;
|
||||||
collapsedContent: MaybeRef<Renderable>;
|
collapsedContent: () => Renderable;
|
||||||
hasCollapsedContent: Ref<boolean>;
|
hasCollapsedContent: Ref<boolean>;
|
||||||
} {
|
} {
|
||||||
const filteredFeatures = computed(() =>
|
const filteredFeatures = computed(() =>
|
||||||
|
@ -74,7 +77,7 @@ export function getFirstFeature<T extends VueFeature>(
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
firstFeature: computed(() => filteredFeatures.value[0]),
|
firstFeature: computed(() => filteredFeatures.value[0]),
|
||||||
collapsedContent: computed(() => renderCol(...filteredFeatures.value.slice(1))),
|
collapsedContent: () => renderCol(...filteredFeatures.value.slice(1)),
|
||||||
hasCollapsedContent: computed(() => filteredFeatures.value.length > 1)
|
hasCollapsedContent: computed(() => filteredFeatures.value.length > 1)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -90,13 +93,13 @@ 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 != null && typeof value === "object") {
|
if (
|
||||||
if (
|
value != null &&
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
typeof value === "object" &&
|
||||||
typeof (value as Record<string, any>).type === "symbol" &&
|
(value as Record<string, unknown>).__v_isVNode !== true
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
) {
|
||||||
!types.includes((value as Record<string, any>).type)
|
const type = (value as Record<string, unknown>).type;
|
||||||
) {
|
if (typeof type === "symbol" && !types.includes(type)) {
|
||||||
objects.push(value);
|
objects.push(value);
|
||||||
} else if (!(value instanceof Decimal) && !isRef(value)) {
|
} else if (!(value instanceof Decimal) && !isRef(value)) {
|
||||||
handleObject(value as Record<string, unknown>);
|
handleObject(value as Record<string, unknown>);
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import Column from "components/layout/Column.vue";
|
||||||
|
import Row from "components/layout/Row.vue";
|
||||||
|
import Clickable from "features/clickables/Clickable.vue";
|
||||||
import { getUniqueID, Visibility } from "features/feature";
|
import { getUniqueID, Visibility } from "features/feature";
|
||||||
import type { Persistent, State } from "game/persistence";
|
import type { Persistent, State } from "game/persistence";
|
||||||
import { persistent } from "game/persistence";
|
import { persistent } from "game/persistence";
|
||||||
import { isFunction } from "util/common";
|
import { isFunction } from "util/common";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { isJSXElement, render, Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
import {
|
||||||
|
isJSXElement,
|
||||||
|
render,
|
||||||
|
Renderable,
|
||||||
|
VueFeature,
|
||||||
|
vueFeatureMixin,
|
||||||
|
VueFeatureOptions
|
||||||
|
} from "util/vue";
|
||||||
import type { CSSProperties, MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
import type { CSSProperties, MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
||||||
import { computed, isRef, unref } from "vue";
|
import { computed, unref } from "vue";
|
||||||
import Column from "components/layout/Column.vue";
|
|
||||||
import Row from "components/layout/Row.vue";
|
|
||||||
import Clickable from "features/clickables/Clickable.vue";
|
|
||||||
|
|
||||||
/** A symbol used to identify {@link Grid} features. */
|
/** A symbol used to identify {@link Grid} features. */
|
||||||
export const GridType = Symbol("Grid");
|
export const GridType = Symbol("Grid");
|
||||||
|
@ -39,7 +46,7 @@ export interface GridCell extends VueFeature {
|
||||||
/** The persistent state of this cell. */
|
/** The persistent state of this cell. */
|
||||||
state: State;
|
state: State;
|
||||||
/** The main text that appears in the display. */
|
/** The main text that appears in the display. */
|
||||||
display: MaybeRef<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** A function that is called when the cell is clicked. */
|
/** A function that is called when the cell is clicked. */
|
||||||
onClick?: (e?: MouseEvent | TouchEvent) => void;
|
onClick?: (e?: MouseEvent | TouchEvent) => void;
|
||||||
/** A function that is called when the cell is held down. */
|
/** A function that is called when the cell is held down. */
|
||||||
|
@ -65,10 +72,13 @@ export interface GridOptions extends VueFeatureOptions {
|
||||||
/** A getter for the CSS classes for a cell. */
|
/** A getter for the CSS classes for a cell. */
|
||||||
getClasses?: CellMaybeRefOrGetter<Record<string, boolean>>;
|
getClasses?: CellMaybeRefOrGetter<Record<string, boolean>>;
|
||||||
/** A getter for the display component for a cell. */
|
/** A getter for the display component for a cell. */
|
||||||
getDisplay: CellMaybeRefOrGetter<Renderable> | {
|
getDisplay:
|
||||||
getTitle?: CellMaybeRefOrGetter<Renderable>;
|
| Renderable
|
||||||
getDescription: CellMaybeRefOrGetter<Renderable>
|
| ((row: number, col: number, state: State) => Renderable)
|
||||||
};
|
| {
|
||||||
|
getTitle?: Renderable | ((row: number, col: number, state: State) => Renderable);
|
||||||
|
getDescription: Renderable | ((row: number, col: number, state: State) => Renderable);
|
||||||
|
};
|
||||||
/** A function that is called when a cell is clicked. */
|
/** A function that is called when a cell is clicked. */
|
||||||
onClick?: (row: number, col: number, state: State, e?: MouseEvent | TouchEvent) => void;
|
onClick?: (row: number, col: number, state: State, e?: MouseEvent | TouchEvent) => void;
|
||||||
/** A function that is called when a cell is held down. */
|
/** A function that is called when a cell is held down. */
|
||||||
|
@ -96,7 +106,7 @@ export interface Grid extends VueFeature {
|
||||||
/** A getter for the CSS classes for a cell. */
|
/** A getter for the CSS classes for a cell. */
|
||||||
getClasses?: ProcessedCellRefOrGetter<Record<string, boolean>>;
|
getClasses?: ProcessedCellRefOrGetter<Record<string, boolean>>;
|
||||||
/** A getter for the display component for a cell. */
|
/** A getter for the display component for a cell. */
|
||||||
getDisplay: ProcessedCellRefOrGetter<Renderable>;
|
getDisplay: Renderable | ((row: number, col: number, state: State) => Renderable);
|
||||||
/** Get the auto-generated ID for identifying a specific cell of this grid that appears in the DOM. Will not persist between refreshes or updates. */
|
/** Get the auto-generated ID for identifying a specific cell of this grid that appears in the DOM. Will not persist between refreshes or updates. */
|
||||||
getID: (row: number, col: number, state: State) => string;
|
getID: (row: number, col: number, state: State) => string;
|
||||||
/** Get the persistent state of the given cell. */
|
/** Get the persistent state of the given cell. */
|
||||||
|
@ -214,7 +224,7 @@ function getCellHandler(grid: Grid, row: number, col: number): GridCell {
|
||||||
return grid.getState(row, col);
|
return grid.getState(row, col);
|
||||||
}
|
}
|
||||||
case "id":
|
case "id":
|
||||||
return target.id = target.id ?? getUniqueID("gridcell");
|
return (target.id = target.id ?? getUniqueID("gridcell"));
|
||||||
case "components":
|
case "components":
|
||||||
return [
|
return [
|
||||||
computed(() => (
|
computed(() => (
|
||||||
|
@ -227,7 +237,7 @@ function getCellHandler(grid: Grid, row: number, col: number): GridCell {
|
||||||
))
|
))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof key === "symbol") {
|
if (typeof key === "symbol") {
|
||||||
return (grid as any)[key];
|
return (grid as any)[key];
|
||||||
}
|
}
|
||||||
|
@ -264,12 +274,10 @@ function getCellHandler(grid: Grid, row: number, col: number): GridCell {
|
||||||
return (grid as any)[key];
|
return (grid as any)[key];
|
||||||
},
|
},
|
||||||
set(target, key, value) {
|
set(target, key, value) {
|
||||||
console.log("!!?", key, value)
|
|
||||||
if (typeof key !== "string") {
|
if (typeof key !== "string") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
key = `set${key.slice(0, 1).toUpperCase() + key.slice(1)}`;
|
key = `set${key.slice(0, 1).toUpperCase() + key.slice(1)}`;
|
||||||
console.log(key, grid[key])
|
|
||||||
if (key in grid && isFunction((grid as any)[key]) && (grid as any)[key].length <= 3) {
|
if (key in grid && isFunction((grid as any)[key]) && (grid as any)[key].length <= 3) {
|
||||||
(grid as any)[key].call(grid, row, col, value);
|
(grid as any)[key].call(grid, row, col, value);
|
||||||
return true;
|
return true;
|
||||||
|
@ -334,20 +342,23 @@ export function createGrid<T extends GridOptions>(optionsFunc: () => T) {
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let getDisplay;
|
let getDisplay;
|
||||||
if (typeof _getDisplay === "object" && !isRef(_getDisplay) && !isJSXElement(_getDisplay)) {
|
if (typeof _getDisplay === "object" && !isJSXElement(_getDisplay)) {
|
||||||
const { getTitle, getDescription } = _getDisplay;
|
const { getTitle, getDescription } = _getDisplay;
|
||||||
const getProcessedTitle = convertCellMaybeRefOrGetter(getTitle);
|
getDisplay = function (row: number, col: number, state: State) {
|
||||||
const getProcessedDescription = convertCellMaybeRefOrGetter(getDescription);
|
const title = typeof getTitle === "function" ? getTitle(row, col, state) : getTitle;
|
||||||
getDisplay = function(row: number, col: number, state: State) {
|
const description =
|
||||||
const title = typeof getProcessedTitle === "function" ? getProcessedTitle(row, col, state) : unref(getProcessedTitle);
|
typeof getDescription === "function"
|
||||||
const description = typeof getProcessedDescription === "function" ? getProcessedDescription(row, col, state) : unref(getProcessedDescription);
|
? getDescription(row, col, state)
|
||||||
return <>
|
: getDescription;
|
||||||
{title}
|
return (
|
||||||
{description}
|
<>
|
||||||
</>;
|
{title}
|
||||||
}
|
{description}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
getDisplay = convertCellMaybeRefOrGetter(_getDisplay);
|
getDisplay = _getDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
const grid = {
|
const grid = {
|
||||||
|
@ -357,10 +368,13 @@ export function createGrid<T extends GridOptions>(optionsFunc: () => T) {
|
||||||
<Column>
|
<Column>
|
||||||
{new Array(unref(grid.rows)).fill(0).map((_, row) => (
|
{new Array(unref(grid.rows)).fill(0).map((_, row) => (
|
||||||
<Row>
|
<Row>
|
||||||
{new Array(unref(grid.cols)).fill(0).map((_, col) =>
|
{new Array(unref(grid.cols))
|
||||||
render(grid.cells[row][col]))}
|
.fill(0)
|
||||||
</Row>))}
|
.map((_, col) => render(grid.cells[row][col]))}
|
||||||
</Column>)),
|
</Row>
|
||||||
|
))}
|
||||||
|
</Column>
|
||||||
|
)),
|
||||||
cellState,
|
cellState,
|
||||||
cells: new Proxy({} as GridCell[][], {
|
cells: new Proxy({} as GridCell[][], {
|
||||||
get(target, key: PropertyKey) {
|
get(target, key: PropertyKey) {
|
||||||
|
@ -422,8 +436,10 @@ export function createGrid<T extends GridOptions>(optionsFunc: () => T) {
|
||||||
cols: processGetter(cols),
|
cols: processGetter(cols),
|
||||||
getVisibility: convertCellMaybeRefOrGetter(getVisibility ?? true),
|
getVisibility: convertCellMaybeRefOrGetter(getVisibility ?? true),
|
||||||
getCanClick: convertCellMaybeRefOrGetter(getCanClick ?? true),
|
getCanClick: convertCellMaybeRefOrGetter(getCanClick ?? true),
|
||||||
getStartState: typeof getStartState === "function" && getStartState.length > 0 ?
|
getStartState:
|
||||||
getStartState : processGetter(getStartState),
|
typeof getStartState === "function" && getStartState.length > 0
|
||||||
|
? getStartState
|
||||||
|
: processGetter(getStartState),
|
||||||
getStyle: convertCellMaybeRefOrGetter(getStyle),
|
getStyle: convertCellMaybeRefOrGetter(getStyle),
|
||||||
getClasses: convertCellMaybeRefOrGetter(getClasses),
|
getClasses: convertCellMaybeRefOrGetter(getClasses),
|
||||||
getDisplay,
|
getDisplay,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Infobox from "features/infoboxes/Infobox.vue";
|
import Infobox from "features/infoboxes/Infobox.vue";
|
||||||
import type { Persistent } from "game/persistence";
|
import type { Persistent } from "game/persistence";
|
||||||
import { persistent } from "game/persistence";
|
import { persistent } from "game/persistence";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
||||||
import { CSSProperties, MaybeRef, MaybeRefOrGetter } from "vue";
|
import { CSSProperties, MaybeRef, MaybeRefOrGetter } from "vue";
|
||||||
|
@ -20,9 +20,9 @@ export interface InfoboxOptions extends VueFeatureOptions {
|
||||||
/** CSS to apply to the body of the infobox. */
|
/** CSS to apply to the body of the infobox. */
|
||||||
bodyStyle?: MaybeRefOrGetter<CSSProperties>;
|
bodyStyle?: MaybeRefOrGetter<CSSProperties>;
|
||||||
/** A header to appear at the top of the display. */
|
/** A header to appear at the top of the display. */
|
||||||
title: MaybeRefOrGetter<Renderable>;
|
title: MaybeGetter<Renderable>;
|
||||||
/** The main text that appears in the display. */
|
/** The main text that appears in the display. */
|
||||||
display: MaybeRefOrGetter<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An object that represents a feature that displays information in a collapsible way. */
|
/** An object that represents a feature that displays information in a collapsible way. */
|
||||||
|
@ -34,9 +34,9 @@ export interface Infobox extends VueFeature {
|
||||||
/** CSS to apply to the body of the infobox. */
|
/** CSS to apply to the body of the infobox. */
|
||||||
bodyStyle?: MaybeRef<CSSProperties>;
|
bodyStyle?: MaybeRef<CSSProperties>;
|
||||||
/** A header to appear at the top of the display. */
|
/** A header to appear at the top of the display. */
|
||||||
title: MaybeRef<Renderable>;
|
title: MaybeGetter<Renderable>;
|
||||||
/** The main text that appears in the display. */
|
/** The main text that appears in the display. */
|
||||||
display: MaybeRef<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** Whether or not this infobox is collapsed. */
|
/** Whether or not this infobox is collapsed. */
|
||||||
collapsed: Persistent<boolean>;
|
collapsed: Persistent<boolean>;
|
||||||
/** A symbol that helps identify features of the same type. */
|
/** A symbol that helps identify features of the same type. */
|
||||||
|
@ -70,8 +70,8 @@ export function createInfobox<T extends InfoboxOptions>(optionsFunc: () => T) {
|
||||||
color: processGetter(color) ?? "--layer-color",
|
color: processGetter(color) ?? "--layer-color",
|
||||||
titleStyle: processGetter(titleStyle),
|
titleStyle: processGetter(titleStyle),
|
||||||
bodyStyle: processGetter(bodyStyle),
|
bodyStyle: processGetter(bodyStyle),
|
||||||
title: processGetter(title),
|
title,
|
||||||
display: processGetter(display)
|
display
|
||||||
} satisfies Infobox;
|
} satisfies Infobox;
|
||||||
|
|
||||||
return infobox;
|
return infobox;
|
||||||
|
|
|
@ -41,6 +41,7 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
app.value?.destroy();
|
app.value?.destroy();
|
||||||
|
app.value = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
let isDirty = true;
|
let isDirty = true;
|
||||||
|
|
|
@ -3,8 +3,9 @@ import type { EmitterConfigV3 } from "@pixi/particle-emitter";
|
||||||
import { Emitter, upgradeConfig } from "@pixi/particle-emitter";
|
import { Emitter, upgradeConfig } from "@pixi/particle-emitter";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
import { VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
||||||
import { Ref, shallowRef } from "vue";
|
import { Ref, shallowRef, unref } from "vue";
|
||||||
import Particles from "./Particles.vue";
|
import Particles from "./Particles.vue";
|
||||||
|
import { processGetter } from "util/computed";
|
||||||
|
|
||||||
/** A symbol used to identify {@link Particles} features. */
|
/** A symbol used to identify {@link Particles} features. */
|
||||||
export const ParticlesType = Symbol("Particles");
|
export const ParticlesType = Symbol("Particles");
|
||||||
|
@ -47,7 +48,10 @@ export interface Particles extends VueFeature {
|
||||||
export function createParticles<T extends ParticlesOptions>(optionsFunc?: () => T) {
|
export function createParticles<T extends ParticlesOptions>(optionsFunc?: () => T) {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(() => {
|
||||||
const options = optionsFunc?.() ?? ({} as T);
|
const options = optionsFunc?.() ?? ({} as T);
|
||||||
const { onContainerResized, onHotReload, ...props } = options;
|
const { onContainerResized, onHotReload, style: _style, ...props } = options;
|
||||||
|
|
||||||
|
const style = processGetter(_style);
|
||||||
|
options.style = () => ({ position: "static", ...(unref(style) ?? {}) });
|
||||||
|
|
||||||
let emittersToAdd: {
|
let emittersToAdd: {
|
||||||
resolve: (value: Emitter | PromiseLike<Emitter>) => void;
|
resolve: (value: Emitter | PromiseLike<Emitter>) => void;
|
||||||
|
@ -57,6 +61,7 @@ export function createParticles<T extends ParticlesOptions>(optionsFunc?: () =>
|
||||||
function onInit(app: Application) {
|
function onInit(app: Application) {
|
||||||
emittersToAdd.forEach(({ resolve, config }) => resolve(new Emitter(app.stage, config)));
|
emittersToAdd.forEach(({ resolve, config }) => resolve(new Emitter(app.stage, config)));
|
||||||
emittersToAdd = [];
|
emittersToAdd = [];
|
||||||
|
particles.app.value = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
const particles = {
|
const particles = {
|
||||||
|
|
|
@ -19,15 +19,16 @@ import Sticky from "components/layout/Sticky.vue";
|
||||||
import type { Resource } from "features/resources/resource";
|
import type { Resource } from "features/resources/resource";
|
||||||
import ResourceVue from "features/resources/Resource.vue";
|
import ResourceVue from "features/resources/Resource.vue";
|
||||||
import Decimal from "util/bignum";
|
import Decimal from "util/bignum";
|
||||||
|
import { MaybeGetter } from "util/computed";
|
||||||
import { Renderable } from "util/vue";
|
import { Renderable } from "util/vue";
|
||||||
import { computed, MaybeRefOrGetter, ref, StyleValue, toValue } from "vue";
|
import { computed, ref, StyleValue, toValue } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
resource: Resource;
|
resource: Resource;
|
||||||
color?: string;
|
color?: string;
|
||||||
classes?: Record<string, boolean>;
|
classes?: Record<string, boolean>;
|
||||||
style?: StyleValue;
|
style?: StyleValue;
|
||||||
effectDisplay?: MaybeRefOrGetter<Renderable>;
|
effectDisplay?: MaybeGetter<Renderable>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const displayRef = ref<Element | null>(null);
|
const displayRef = ref<Element | null>(null);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { render, Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
||||||
import { MaybeRef, MaybeRefOrGetter } from "vue";
|
|
||||||
import { JSX } from "vue/jsx-runtime";
|
|
||||||
|
|
||||||
/** A symbol used to identify {@link Tab} features. */
|
/** A symbol used to identify {@link Tab} features. */
|
||||||
export const TabType = Symbol("Tab");
|
export const TabType = Symbol("Tab");
|
||||||
|
@ -12,7 +10,7 @@ export const TabType = Symbol("Tab");
|
||||||
*/
|
*/
|
||||||
export interface TabOptions extends VueFeatureOptions {
|
export interface TabOptions extends VueFeatureOptions {
|
||||||
/** The display to use for this tab. */
|
/** The display to use for this tab. */
|
||||||
display: MaybeRefOrGetter<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +19,7 @@ export interface TabOptions extends VueFeatureOptions {
|
||||||
*/
|
*/
|
||||||
export interface Tab extends VueFeature {
|
export interface Tab extends VueFeature {
|
||||||
/** The display to use for this tab. */
|
/** The display to use for this tab. */
|
||||||
display: MaybeRef<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** A symbol that helps identify features of the same type. */
|
/** A symbol that helps identify features of the same type. */
|
||||||
type: typeof TabType;
|
type: typeof TabType;
|
||||||
}
|
}
|
||||||
|
@ -38,8 +36,8 @@ export function createTab<T extends TabOptions>(optionsFunc: () => T) {
|
||||||
const tab = {
|
const tab = {
|
||||||
type: TabType,
|
type: TabType,
|
||||||
...(props as Omit<typeof props, keyof VueFeature | keyof TabOptions>),
|
...(props as Omit<typeof props, keyof VueFeature | keyof TabOptions>),
|
||||||
...vueFeatureMixin("tab", options, (): JSX.Element => render(tab.display)),
|
...vueFeatureMixin("tab", options, display),
|
||||||
display: processGetter(display)
|
display
|
||||||
} satisfies Tab;
|
} satisfies Tab;
|
||||||
|
|
||||||
return tab;
|
return tab;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import TabButton from "features/tabs/TabButton.vue";
|
||||||
import TabFamily from "features/tabs/TabFamily.vue";
|
import TabFamily from "features/tabs/TabFamily.vue";
|
||||||
import type { Persistent } from "game/persistence";
|
import type { Persistent } from "game/persistence";
|
||||||
import { persistent } from "game/persistence";
|
import { persistent } from "game/persistence";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
||||||
import type { CSSProperties, MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
import type { CSSProperties, MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
||||||
|
@ -20,9 +20,9 @@ export const TabFamilyType = Symbol("TabFamily");
|
||||||
*/
|
*/
|
||||||
export interface TabButtonOptions extends VueFeatureOptions {
|
export interface TabButtonOptions extends VueFeatureOptions {
|
||||||
/** The tab to display when this button is clicked. */
|
/** The tab to display when this button is clicked. */
|
||||||
tab: Tab | MaybeRefOrGetter<Renderable>;
|
tab: Tab | MaybeGetter<Renderable>;
|
||||||
/** The label on this button. */
|
/** The label on this button. */
|
||||||
display: MaybeRefOrGetter<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** The color of the glow effect to display when this button is active. */
|
/** The color of the glow effect to display when this button is active. */
|
||||||
glowColor?: MaybeRefOrGetter<string>;
|
glowColor?: MaybeRefOrGetter<string>;
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,9 @@ export interface TabButtonOptions extends VueFeatureOptions {
|
||||||
*/
|
*/
|
||||||
export interface TabButton extends VueFeature {
|
export interface TabButton extends VueFeature {
|
||||||
/** The tab to display when this button is clicked. */
|
/** The tab to display when this button is clicked. */
|
||||||
tab: Tab | MaybeRef<Renderable>;
|
tab: Tab | MaybeGetter<Renderable>;
|
||||||
/** The label on this button. */
|
/** The label on this button. */
|
||||||
display: MaybeRef<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** The color of the glow effect to display when this button is active. */
|
/** The color of the glow effect to display when this button is active. */
|
||||||
glowColor?: MaybeRef<string>;
|
glowColor?: MaybeRef<string>;
|
||||||
/** A symbol that helps identify features of the same type. */
|
/** A symbol that helps identify features of the same type. */
|
||||||
|
@ -64,7 +64,7 @@ export interface TabFamily extends VueFeature {
|
||||||
/** All the tabs within this family. */
|
/** All the tabs within this family. */
|
||||||
tabs: Record<string, TabButton>;
|
tabs: Record<string, TabButton>;
|
||||||
/** The currently active tab, if any. */
|
/** The currently active tab, if any. */
|
||||||
activeTab: Ref<Tab | MaybeRef<Renderable> | null>;
|
activeTab: Ref<Tab | MaybeGetter<Renderable> | null>;
|
||||||
/** The name of the tab that is currently active. */
|
/** The name of the tab that is currently active. */
|
||||||
selected: Persistent<string>;
|
selected: Persistent<string>;
|
||||||
/** A symbol that helps identify features of the same type. */
|
/** A symbol that helps identify features of the same type. */
|
||||||
|
@ -106,16 +106,17 @@ export function createTabFamily<T extends TabFamilyOptions>(
|
||||||
const tabButton = {
|
const tabButton = {
|
||||||
type: TabButtonType,
|
type: TabButtonType,
|
||||||
...(props as Omit<typeof props, keyof VueFeature | keyof TabButtonOptions>),
|
...(props as Omit<typeof props, keyof VueFeature | keyof TabButtonOptions>),
|
||||||
...vueFeatureMixin("tabButton", options, () =>
|
...vueFeatureMixin("tabButton", options, () => (
|
||||||
<TabButton
|
<TabButton
|
||||||
display={tabButton.display}
|
display={tabButton.display}
|
||||||
glowColor={tabButton.glowColor}
|
glowColor={tabButton.glowColor}
|
||||||
active={unref(tabButton.tab) === unref(tabFamily.activeTab)}
|
active={unref(tabButton.tab) === unref(tabFamily.activeTab)}
|
||||||
onSelectTab={() => tabFamily.selected.value = tab}
|
onSelectTab={() => (tabFamily.selected.value = tab)}
|
||||||
/>),
|
/>
|
||||||
tab: processGetter(buttonTab),
|
)),
|
||||||
|
tab: buttonTab,
|
||||||
glowColor: processGetter(glowColor),
|
glowColor: processGetter(glowColor),
|
||||||
display: processGetter(display)
|
display
|
||||||
} satisfies TabButton;
|
} satisfies TabButton;
|
||||||
|
|
||||||
parsedTabs[tab] = tabButton;
|
parsedTabs[tab] = tabButton;
|
||||||
|
@ -124,7 +125,7 @@ export function createTabFamily<T extends TabFamilyOptions>(
|
||||||
buttonContainerClasses: processGetter(buttonContainerClasses),
|
buttonContainerClasses: processGetter(buttonContainerClasses),
|
||||||
buttonContainerStyle: processGetter(buttonContainerStyle),
|
buttonContainerStyle: processGetter(buttonContainerStyle),
|
||||||
selected,
|
selected,
|
||||||
activeTab: computed((): Tab | MaybeRef<Renderable> | null => {
|
activeTab: computed((): Tab | MaybeGetter<Renderable> | null => {
|
||||||
if (
|
if (
|
||||||
selected.value in tabFamily.tabs &&
|
selected.value in tabFamily.tabs &&
|
||||||
isVisible(tabFamily.tabs[selected.value].visibility ?? true)
|
isVisible(tabFamily.tabs[selected.value].visibility ?? true)
|
||||||
|
|
|
@ -7,11 +7,11 @@ import TreeNode from "features/trees/TreeNode.vue";
|
||||||
import { noPersist } from "game/persistence";
|
import { noPersist } from "game/persistence";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import Decimal, { format, formatWhole } from "util/bignum";
|
import Decimal, { format, formatWhole } from "util/bignum";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
import { Renderable, VueFeature, vueFeatureMixin, VueFeatureOptions } from "util/vue";
|
||||||
import type { MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
import type { MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
||||||
import { computed, ref, shallowRef, unref } from "vue";
|
import { ref, shallowRef, unref } from "vue";
|
||||||
|
|
||||||
/** A symbol used to identify {@link TreeNode} features. */
|
/** A symbol used to identify {@link TreeNode} features. */
|
||||||
export const TreeNodeType = Symbol("TreeNode");
|
export const TreeNodeType = Symbol("TreeNode");
|
||||||
|
@ -27,7 +27,7 @@ export interface TreeNodeOptions extends VueFeatureOptions {
|
||||||
/** The background color for this node. */
|
/** The background color for this node. */
|
||||||
color?: MaybeRefOrGetter<string>;
|
color?: MaybeRefOrGetter<string>;
|
||||||
/** The label to display on this tree node. */
|
/** The label to display on this tree node. */
|
||||||
display?: MaybeRefOrGetter<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
/** The color of the glow effect shown to notify the user there's something to do with this node. */
|
/** The color of the glow effect shown to notify the user there's something to do with this node. */
|
||||||
glowColor?: MaybeRefOrGetter<string>;
|
glowColor?: MaybeRefOrGetter<string>;
|
||||||
/** A reset object attached to this node, used for propagating resets through the tree. */
|
/** A reset object attached to this node, used for propagating resets through the tree. */
|
||||||
|
@ -47,7 +47,7 @@ export interface TreeNode extends VueFeature {
|
||||||
/** The background color for this node. */
|
/** The background color for this node. */
|
||||||
color?: MaybeRef<string>;
|
color?: MaybeRef<string>;
|
||||||
/** The label to display on this tree node. */
|
/** The label to display on this tree node. */
|
||||||
display?: MaybeRef<Renderable>;
|
display?: MaybeGetter<Renderable>;
|
||||||
/** The color of the glow effect shown to notify the user there's something to do with this node. */
|
/** The color of the glow effect shown to notify the user there's something to do with this node. */
|
||||||
glowColor?: MaybeRef<string>;
|
glowColor?: MaybeRef<string>;
|
||||||
/** A reset object attached to this node, used for propagating resets through the tree. */
|
/** A reset object attached to this node, used for propagating resets through the tree. */
|
||||||
|
@ -84,7 +84,7 @@ export function createTreeNode<T extends TreeNodeOptions>(optionsFunc?: () => T)
|
||||||
)),
|
)),
|
||||||
canClick: processGetter(canClick) ?? true,
|
canClick: processGetter(canClick) ?? true,
|
||||||
color: processGetter(color),
|
color: processGetter(color),
|
||||||
display: processGetter(display),
|
display,
|
||||||
glowColor: processGetter(glowColor),
|
glowColor: processGetter(glowColor),
|
||||||
onClick:
|
onClick:
|
||||||
onClick == null
|
onClick == null
|
||||||
|
@ -265,9 +265,9 @@ export function createResourceTooltip(
|
||||||
resource: Resource,
|
resource: Resource,
|
||||||
requiredResource: Resource | null = null,
|
requiredResource: Resource | null = null,
|
||||||
requirement: MaybeRefOrGetter<DecimalSource> = 0
|
requirement: MaybeRefOrGetter<DecimalSource> = 0
|
||||||
): Ref<string> {
|
): () => string {
|
||||||
const req = processGetter(requirement);
|
const req = processGetter(requirement);
|
||||||
return computed(() => {
|
return () => {
|
||||||
if (requiredResource == null || Decimal.gte(resource.value, unref(req))) {
|
if (requiredResource == null || Decimal.gte(resource.value, unref(req))) {
|
||||||
return displayResource(resource) + " " + resource.displayName;
|
return displayResource(resource) + " " + resource.displayName;
|
||||||
}
|
}
|
||||||
|
@ -280,5 +280,5 @@ export function createResourceTooltip(
|
||||||
? formatWhole(requiredResource.value)
|
? formatWhole(requiredResource.value)
|
||||||
: format(requiredResource.value, requiredResource.precision)
|
: format(requiredResource.value, requiredResource.precision)
|
||||||
})`;
|
})`;
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,7 +275,18 @@ export function makeDraggable<T, S extends MakeDraggableOptions<T>>(
|
||||||
const position = persistent<NodePosition>({ x: 0, y: 0 });
|
const position = persistent<NodePosition>({ x: 0, y: 0 });
|
||||||
const draggable = createLazyProxy(() => {
|
const draggable = createLazyProxy(() => {
|
||||||
const options = optionsFunc();
|
const options = optionsFunc();
|
||||||
const { id, nodeBeingDragged, hasDragged, dragDelta, startDrag, endDrag, onMouseDown, onMouseUp, initialPosition, ...props } = options;
|
const {
|
||||||
|
id,
|
||||||
|
nodeBeingDragged,
|
||||||
|
hasDragged,
|
||||||
|
dragDelta,
|
||||||
|
startDrag,
|
||||||
|
endDrag,
|
||||||
|
onMouseDown,
|
||||||
|
onMouseUp,
|
||||||
|
initialPosition,
|
||||||
|
...props
|
||||||
|
} = options;
|
||||||
|
|
||||||
position[DefaultValue] = initialPosition ?? position[DefaultValue];
|
position[DefaultValue] = initialPosition ?? position[DefaultValue];
|
||||||
|
|
||||||
|
@ -323,7 +334,7 @@ export function makeDraggable<T, S extends MakeDraggableOptions<T>>(
|
||||||
|
|
||||||
runAfterEvaluation(element, el => {
|
runAfterEvaluation(element, el => {
|
||||||
draggable.id; // Ensure draggable gets evaluated
|
draggable.id; // Ensure draggable gets evaluated
|
||||||
(el as VueFeature & { draggable: Draggable<T> }).draggable = draggable;
|
(el as VueFeature & { draggable: Draggable<T> }).draggable = draggable;
|
||||||
element.wrappers.push(el => (
|
element.wrappers.push(el => (
|
||||||
<Draggable
|
<Draggable
|
||||||
mouseDown={draggable.onMouseDown}
|
mouseDown={draggable.onMouseDown}
|
||||||
|
|
|
@ -5,9 +5,9 @@ import { persistent } from "game/persistence";
|
||||||
import player from "game/player";
|
import player from "game/player";
|
||||||
import type { Emitter } from "nanoevents";
|
import type { Emitter } from "nanoevents";
|
||||||
import { createNanoEvents } from "nanoevents";
|
import { createNanoEvents } from "nanoevents";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { render, Renderable } from "util/vue";
|
import { Renderable } from "util/vue";
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
type CSSProperties,
|
type CSSProperties,
|
||||||
|
@ -108,7 +108,7 @@ export interface LayerOptions {
|
||||||
* The layout of this layer's features.
|
* The layout of this layer's features.
|
||||||
* When the layer is open in {@link game/player.PlayerData.tabs}, this is the content that is displayed.
|
* When the layer is open in {@link game/player.PlayerData.tabs}, this is the content that is displayed.
|
||||||
*/
|
*/
|
||||||
display: MaybeRefOrGetter<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** An object of classes that should be applied to the display. */
|
/** An object of classes that should be applied to the display. */
|
||||||
classes?: MaybeRefOrGetter<Record<string, boolean>>;
|
classes?: MaybeRefOrGetter<Record<string, boolean>>;
|
||||||
/** Styles that should be applied to the display. */
|
/** Styles that should be applied to the display. */
|
||||||
|
@ -127,7 +127,7 @@ export interface LayerOptions {
|
||||||
* The layout of this layer's features.
|
* The layout of this layer's features.
|
||||||
* When the layer is open in {@link game/player.PlayerData.tabs}, but the tab is {@link Layer.minimized} this is the content that is displayed.
|
* When the layer is open in {@link game/player.PlayerData.tabs}, but the tab is {@link Layer.minimized} this is the content that is displayed.
|
||||||
*/
|
*/
|
||||||
minimizedDisplay?: MaybeRefOrGetter<Renderable>;
|
minimizedDisplay?: MaybeGetter<Renderable>;
|
||||||
/**
|
/**
|
||||||
* Whether or not to force the go back button to be hidden.
|
* Whether or not to force the go back button to be hidden.
|
||||||
* If true, go back will be hidden regardless of {@link data/projInfo.allowGoBack}.
|
* If true, go back will be hidden regardless of {@link data/projInfo.allowGoBack}.
|
||||||
|
@ -169,7 +169,7 @@ export interface Layer extends BaseLayer {
|
||||||
* The layout of this layer's features.
|
* The layout of this layer's features.
|
||||||
* When the layer is open in {@link game/player.PlayerData.tabs}, this is the content that is displayed.
|
* When the layer is open in {@link game/player.PlayerData.tabs}, this is the content that is displayed.
|
||||||
*/
|
*/
|
||||||
display: MaybeRef<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** An object of classes that should be applied to the display. */
|
/** An object of classes that should be applied to the display. */
|
||||||
classes?: MaybeRef<Record<string, boolean>>;
|
classes?: MaybeRef<Record<string, boolean>>;
|
||||||
/** Styles that should be applied to the display. */
|
/** Styles that should be applied to the display. */
|
||||||
|
@ -188,7 +188,7 @@ export interface Layer extends BaseLayer {
|
||||||
* The layout of this layer's features.
|
* The layout of this layer's features.
|
||||||
* When the layer is open in {@link game/player.PlayerData.tabs}, but the tab is {@link Layer.minimized} this is the content that is displayed.
|
* When the layer is open in {@link game/player.PlayerData.tabs}, but the tab is {@link Layer.minimized} this is the content that is displayed.
|
||||||
*/
|
*/
|
||||||
minimizedDisplay?: MaybeRef<Renderable>;
|
minimizedDisplay?: MaybeGetter<Renderable>;
|
||||||
/**
|
/**
|
||||||
* Whether or not to force the go back button to be hidden.
|
* Whether or not to force the go back button to be hidden.
|
||||||
* If true, go back will be hidden regardless of {@link data/projInfo.allowGoBack}.
|
* If true, go back will be hidden regardless of {@link data/projInfo.allowGoBack}.
|
||||||
|
@ -261,7 +261,7 @@ export function createLayer<T extends LayerOptions>(
|
||||||
...baseLayer,
|
...baseLayer,
|
||||||
...(props as Omit<typeof props, keyof LayerOptions>),
|
...(props as Omit<typeof props, keyof LayerOptions>),
|
||||||
color: processGetter(color),
|
color: processGetter(color),
|
||||||
display: processGetter(display),
|
display,
|
||||||
classes: processGetter(classes),
|
classes: processGetter(classes),
|
||||||
style: computed((): CSSProperties => {
|
style: computed((): CSSProperties => {
|
||||||
let width = unref(layer.minWidth);
|
let width = unref(layer.minWidth);
|
||||||
|
@ -293,7 +293,7 @@ export function createLayer<T extends LayerOptions>(
|
||||||
forceHideGoBack: processGetter(forceHideGoBack),
|
forceHideGoBack: processGetter(forceHideGoBack),
|
||||||
minWidth: processGetter(minWidth) ?? 600,
|
minWidth: processGetter(minWidth) ?? 600,
|
||||||
minimizable: processGetter(minimizable) ?? true,
|
minimizable: processGetter(minimizable) ?? true,
|
||||||
minimizedDisplay: processGetter(minimizedDisplay)
|
minimizedDisplay
|
||||||
} satisfies Layer;
|
} satisfies Layer;
|
||||||
|
|
||||||
return layer;
|
return layer;
|
||||||
|
@ -370,21 +370,21 @@ export function reloadLayer(layer: Layer): void {
|
||||||
*/
|
*/
|
||||||
export function setupLayerModal(layer: Layer): {
|
export function setupLayerModal(layer: Layer): {
|
||||||
openModal: VoidFunction;
|
openModal: VoidFunction;
|
||||||
modal: Ref<JSX.Element>;
|
modal: () => JSX.Element;
|
||||||
} {
|
} {
|
||||||
const showModal = ref(false);
|
const showModal = ref(false);
|
||||||
return {
|
return {
|
||||||
openModal: () => (showModal.value = true),
|
openModal: () => (showModal.value = true),
|
||||||
modal: computed(() => (
|
modal: () => (
|
||||||
<Modal
|
<Modal
|
||||||
modelValue={showModal.value}
|
modelValue={showModal.value}
|
||||||
onUpdate:modelValue={value => (showModal.value = value)}
|
onUpdate:modelValue={value => (showModal.value = value)}
|
||||||
v-slots={{
|
v-slots={{
|
||||||
header: () => <h2>{unref(layer.name)}</h2>,
|
header: () => <h2>{unref(layer.name)}</h2>,
|
||||||
body: () => render(layer.display)
|
body: typeof layer.display ? layer.display : () => layer.display
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import settings from "game/settings";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import Decimal, { formatSmall } from "util/bignum";
|
import Decimal, { formatSmall } from "util/bignum";
|
||||||
import type { RequiredKeys, WithRequired } from "util/common";
|
import type { RequiredKeys, WithRequired } from "util/common";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { render, Renderable } from "util/vue";
|
import { render, Renderable } from "util/vue";
|
||||||
import { computed, MaybeRef, MaybeRefOrGetter, unref } from "vue";
|
import { computed, MaybeRef, MaybeRefOrGetter, unref } from "vue";
|
||||||
|
@ -32,7 +32,7 @@ export interface Modifier {
|
||||||
* A description of this modifier.
|
* A description of this modifier.
|
||||||
* @see {@link createModifierSection}.
|
* @see {@link createModifierSection}.
|
||||||
*/
|
*/
|
||||||
description?: MaybeRef<Renderable>;
|
description?: MaybeGetter<Renderable>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Utility type that represents the output of all modifiers that represent a single operation. */
|
/** Utility type that represents the output of all modifiers that represent a single operation. */
|
||||||
|
@ -46,7 +46,7 @@ export interface AdditiveModifierOptions {
|
||||||
/** The amount to add to the input value. */
|
/** The amount to add to the input value. */
|
||||||
addend: MaybeRefOrGetter<DecimalSource>;
|
addend: MaybeRefOrGetter<DecimalSource>;
|
||||||
/** Description of what this modifier is doing. */
|
/** Description of what this modifier is doing. */
|
||||||
description?: MaybeRefOrGetter<Renderable>;
|
description?: MaybeGetter<Renderable>;
|
||||||
/** A MaybeRefOrGetter that will be processed and passed directly into the returned modifier. */
|
/** A MaybeRefOrGetter that will be processed and passed directly into the returned modifier. */
|
||||||
enabled?: MaybeRefOrGetter<boolean>;
|
enabled?: MaybeRefOrGetter<boolean>;
|
||||||
/** Determines if numbers larger or smaller than 0 should be displayed as red. */
|
/** Determines if numbers larger or smaller than 0 should be displayed as red. */
|
||||||
|
@ -64,7 +64,6 @@ export function createAdditiveModifier<T extends AdditiveModifierOptions, S = Op
|
||||||
const { addend, description, enabled, smallerIsBetter } = optionsFunc();
|
const { addend, description, enabled, smallerIsBetter } = optionsFunc();
|
||||||
|
|
||||||
const processedAddend = processGetter(addend);
|
const processedAddend = processGetter(addend);
|
||||||
const processedDescription = processGetter(description);
|
|
||||||
const processedEnabled = enabled == null ? undefined : processGetter(enabled);
|
const processedEnabled = enabled == null ? undefined : processGetter(enabled);
|
||||||
return {
|
return {
|
||||||
apply: (gain: DecimalSource) => Decimal.add(gain, unref(processedAddend)),
|
apply: (gain: DecimalSource) => Decimal.add(gain, unref(processedAddend)),
|
||||||
|
@ -72,13 +71,11 @@ export function createAdditiveModifier<T extends AdditiveModifierOptions, S = Op
|
||||||
getFormula: (gain: FormulaSource) => Formula.add(gain, processedAddend),
|
getFormula: (gain: FormulaSource) => Formula.add(gain, processedAddend),
|
||||||
enabled: processedEnabled,
|
enabled: processedEnabled,
|
||||||
description:
|
description:
|
||||||
processedDescription == null
|
description == null
|
||||||
? undefined
|
? undefined
|
||||||
: computed(() => (
|
: () => (
|
||||||
<div class="modifier-container">
|
<div class="modifier-container">
|
||||||
<span class="modifier-description">
|
<span class="modifier-description">{render(description)}</span>
|
||||||
{render(processedDescription)}
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
class="modifier-amount"
|
class="modifier-amount"
|
||||||
style={
|
style={
|
||||||
|
@ -95,7 +92,7 @@ export function createAdditiveModifier<T extends AdditiveModifierOptions, S = Op
|
||||||
{formatSmall(unref(processedAddend))}
|
{formatSmall(unref(processedAddend))}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))
|
)
|
||||||
};
|
};
|
||||||
}) as S;
|
}) as S;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +102,7 @@ export interface MultiplicativeModifierOptions {
|
||||||
/** The amount to multiply the input value by. */
|
/** The amount to multiply the input value by. */
|
||||||
multiplier: MaybeRefOrGetter<DecimalSource>;
|
multiplier: MaybeRefOrGetter<DecimalSource>;
|
||||||
/** Description of what this modifier is doing. */
|
/** Description of what this modifier is doing. */
|
||||||
description?: MaybeRefOrGetter<Renderable> | undefined;
|
description?: MaybeGetter<Renderable> | undefined;
|
||||||
/** A MaybeRefOrGetter that will be processed and passed directly into the returned modifier. */
|
/** A MaybeRefOrGetter that will be processed and passed directly into the returned modifier. */
|
||||||
enabled?: MaybeRefOrGetter<boolean> | undefined;
|
enabled?: MaybeRefOrGetter<boolean> | undefined;
|
||||||
/** Determines if numbers larger or smaller than 1 should be displayed as red. */
|
/** Determines if numbers larger or smaller than 1 should be displayed as red. */
|
||||||
|
@ -124,7 +121,6 @@ export function createMultiplicativeModifier<
|
||||||
const { multiplier, description, enabled, smallerIsBetter } = optionsFunc();
|
const { multiplier, description, enabled, smallerIsBetter } = optionsFunc();
|
||||||
|
|
||||||
const processedMultiplier = processGetter(multiplier);
|
const processedMultiplier = processGetter(multiplier);
|
||||||
const processedDescription = processGetter(description);
|
|
||||||
const processedEnabled = enabled == null ? undefined : processGetter(enabled);
|
const processedEnabled = enabled == null ? undefined : processGetter(enabled);
|
||||||
return {
|
return {
|
||||||
apply: (gain: DecimalSource) => Decimal.times(gain, unref(processedMultiplier)),
|
apply: (gain: DecimalSource) => Decimal.times(gain, unref(processedMultiplier)),
|
||||||
|
@ -132,13 +128,11 @@ export function createMultiplicativeModifier<
|
||||||
getFormula: (gain: FormulaSource) => Formula.times(gain, processedMultiplier),
|
getFormula: (gain: FormulaSource) => Formula.times(gain, processedMultiplier),
|
||||||
enabled: processedEnabled,
|
enabled: processedEnabled,
|
||||||
description:
|
description:
|
||||||
processedDescription == null
|
description == null
|
||||||
? undefined
|
? undefined
|
||||||
: computed(() => (
|
: () => (
|
||||||
<div class="modifier-container">
|
<div class="modifier-container">
|
||||||
<span class="modifier-description">
|
<span class="modifier-description">{render(description)}</span>
|
||||||
{render(processedDescription)}
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
class="modifier-amount"
|
class="modifier-amount"
|
||||||
style={
|
style={
|
||||||
|
@ -154,7 +148,7 @@ export function createMultiplicativeModifier<
|
||||||
×{formatSmall(unref(processedMultiplier))}
|
×{formatSmall(unref(processedMultiplier))}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))
|
)
|
||||||
};
|
};
|
||||||
}) as S;
|
}) as S;
|
||||||
}
|
}
|
||||||
|
@ -164,7 +158,7 @@ export interface ExponentialModifierOptions {
|
||||||
/** The amount to raise the input value to the power of. */
|
/** The amount to raise the input value to the power of. */
|
||||||
exponent: MaybeRefOrGetter<DecimalSource>;
|
exponent: MaybeRefOrGetter<DecimalSource>;
|
||||||
/** Description of what this modifier is doing. */
|
/** Description of what this modifier is doing. */
|
||||||
description?: MaybeRefOrGetter<Renderable> | undefined;
|
description?: MaybeGetter<Renderable> | undefined;
|
||||||
/** A MaybeRefOrGetter that will be processed and passed directly into the returned modifier. */
|
/** A MaybeRefOrGetter that will be processed and passed directly into the returned modifier. */
|
||||||
enabled?: MaybeRefOrGetter<boolean> | undefined;
|
enabled?: MaybeRefOrGetter<boolean> | undefined;
|
||||||
/** Add 1 before calculating, then remove it afterwards. This prevents low numbers from becoming lower. */
|
/** Add 1 before calculating, then remove it afterwards. This prevents low numbers from becoming lower. */
|
||||||
|
@ -186,7 +180,6 @@ export function createExponentialModifier<
|
||||||
optionsFunc();
|
optionsFunc();
|
||||||
|
|
||||||
const processedExponent = processGetter(exponent);
|
const processedExponent = processGetter(exponent);
|
||||||
const processedDescription = processGetter(description);
|
|
||||||
const processedEnabled = enabled == null ? undefined : processGetter(enabled);
|
const processedEnabled = enabled == null ? undefined : processGetter(enabled);
|
||||||
return {
|
return {
|
||||||
apply: (gain: DecimalSource) => {
|
apply: (gain: DecimalSource) => {
|
||||||
|
@ -217,12 +210,12 @@ export function createExponentialModifier<
|
||||||
: Formula.pow(gain, processedExponent),
|
: Formula.pow(gain, processedExponent),
|
||||||
enabled: processedEnabled,
|
enabled: processedEnabled,
|
||||||
description:
|
description:
|
||||||
processedDescription == null
|
description == null
|
||||||
? undefined
|
? undefined
|
||||||
: computed(() => (
|
: () => (
|
||||||
<div class="modifier-container">
|
<div class="modifier-container">
|
||||||
<span class="modifier-description">
|
<span class="modifier-description">
|
||||||
{render(processedDescription)}
|
{render(description)}
|
||||||
{supportLowNumbers ? " (+1 effective)" : null}
|
{supportLowNumbers ? " (+1 effective)" : null}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
|
@ -240,7 +233,7 @@ export function createExponentialModifier<
|
||||||
^{formatSmall(unref(processedExponent))}
|
^{formatSmall(unref(processedExponent))}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))
|
)
|
||||||
};
|
};
|
||||||
}) as S;
|
}) as S;
|
||||||
}
|
}
|
||||||
|
@ -286,14 +279,13 @@ export function createSequentialModifier<
|
||||||
? computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0)
|
? computed(() => modifiers.filter(m => unref(m.enabled) !== false).length > 0)
|
||||||
: undefined,
|
: undefined,
|
||||||
description: modifiers.some(m => m.description != null)
|
description: modifiers.some(m => m.description != null)
|
||||||
? computed(() =>
|
? () =>
|
||||||
(
|
(
|
||||||
modifiers
|
modifiers
|
||||||
.filter(m => unref(m.enabled) !== false)
|
.filter(m => unref(m.enabled) !== false)
|
||||||
.map(m => unref(m.description))
|
.map(m => unref(m.description))
|
||||||
.filter(d => d) as MaybeRef<Renderable>[]
|
.filter(d => d) as MaybeGetter<Renderable>[]
|
||||||
).map(m => render(m))
|
).map(m => render(m))
|
||||||
)
|
|
||||||
: undefined
|
: undefined
|
||||||
};
|
};
|
||||||
}) as S;
|
}) as S;
|
||||||
|
@ -312,7 +304,7 @@ export interface ModifierSectionOptions {
|
||||||
/** The unit of the value being modified, if any. */
|
/** The unit of the value being modified, if any. */
|
||||||
unit?: string;
|
unit?: string;
|
||||||
/** The label to use for the base value. Defaults to "Base". */
|
/** The label to use for the base value. Defaults to "Base". */
|
||||||
baseText?: MaybeRefOrGetter<Renderable>;
|
baseText?: MaybeGetter<Renderable>;
|
||||||
/** Determines if numbers larger or smaller than the base should be displayed as red. */
|
/** Determines if numbers larger or smaller than the base should be displayed as red. */
|
||||||
smallerIsBetter?: boolean;
|
smallerIsBetter?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -332,7 +324,6 @@ export function createModifierSection({
|
||||||
smallerIsBetter
|
smallerIsBetter
|
||||||
}: ModifierSectionOptions) {
|
}: ModifierSectionOptions) {
|
||||||
const total = modifier.apply(base ?? 1);
|
const total = modifier.apply(base ?? 1);
|
||||||
const processedBaseText = processGetter(baseText);
|
|
||||||
return (
|
return (
|
||||||
<div style={{ "--unit": settings.alignUnits && unit != null ? "'" + unit + "'" : "" }}>
|
<div style={{ "--unit": settings.alignUnits && unit != null ? "'" + unit + "'" : "" }}>
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -341,7 +332,7 @@ export function createModifierSection({
|
||||||
</h3>
|
</h3>
|
||||||
<br />
|
<br />
|
||||||
<div class="modifier-container">
|
<div class="modifier-container">
|
||||||
<span class="modifier-description">{render(processedBaseText ?? "Base")}</span>
|
<span class="modifier-description">{render(baseText ?? "Base")}</span>
|
||||||
<span class="modifier-amount">
|
<span class="modifier-amount">
|
||||||
{formatSmall(base ?? 1)}
|
{formatSmall(base ?? 1)}
|
||||||
{unit}
|
{unit}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { isVisible, Visibility } from "features/feature";
|
import { isVisible, Visibility } from "features/feature";
|
||||||
import { displayResource, Resource } from "features/resources/resource";
|
import { displayResource, Resource } from "features/resources/resource";
|
||||||
import Decimal, { DecimalSource } from "lib/break_eternity";
|
import Decimal, { DecimalSource } from "lib/break_eternity";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy } from "util/proxies";
|
import { createLazyProxy } from "util/proxies";
|
||||||
import { joinJSX, render, Renderable } from "util/vue";
|
import { joinJSX, Renderable } from "util/vue";
|
||||||
import { computed, MaybeRef, MaybeRefOrGetter, unref } from "vue";
|
import { computed, MaybeRef, MaybeRefOrGetter, unref } from "vue";
|
||||||
import Formula, { calculateCost, calculateMaxAffordable } from "./formulas/formulas";
|
import Formula, { calculateCost, calculateMaxAffordable } from "./formulas/formulas";
|
||||||
import type { GenericFormula, InvertibleIntegralFormula } from "./formulas/types";
|
import type { GenericFormula, InvertibleIntegralFormula } from "./formulas/types";
|
||||||
|
@ -262,16 +262,16 @@ export function createVisibilityRequirement(
|
||||||
*/
|
*/
|
||||||
export function createBooleanRequirement(
|
export function createBooleanRequirement(
|
||||||
requirement: MaybeRefOrGetter<boolean>,
|
requirement: MaybeRefOrGetter<boolean>,
|
||||||
display?: MaybeRefOrGetter<Renderable>
|
display?: MaybeGetter<Renderable>
|
||||||
): Requirement {
|
): Requirement {
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(() => {
|
||||||
const processedDisplay = processGetter(display);
|
const partialDisplay =
|
||||||
|
display == null ? undefined : typeof display === "function" ? display : () => display;
|
||||||
return {
|
return {
|
||||||
requirementMet: processGetter(requirement),
|
requirementMet: processGetter(requirement),
|
||||||
partialDisplay: processedDisplay == null ? undefined : () => render(processedDisplay),
|
partialDisplay,
|
||||||
display:
|
display: display == null ? undefined : () => <>Req: {partialDisplay}</>,
|
||||||
processedDisplay == null ? undefined : () => <>Req: {render(processedDisplay)}</>,
|
visibility: display == null ? Visibility.None : Visibility.Visible,
|
||||||
visibility: processedDisplay == null ? Visibility.None : Visibility.Visible,
|
|
||||||
requiresPay: false
|
requiresPay: false
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,10 +2,10 @@ import projInfo from "data/projInfo.json";
|
||||||
import { Themes } from "data/themes";
|
import { Themes } from "data/themes";
|
||||||
import { globalBus } from "game/events";
|
import { globalBus } from "game/events";
|
||||||
import LZString from "lz-string";
|
import LZString from "lz-string";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter } from "util/computed";
|
||||||
import { decodeSave, hardReset } from "util/save";
|
import { decodeSave, hardReset } from "util/save";
|
||||||
import { Renderable } from "util/vue";
|
import { Renderable } from "util/vue";
|
||||||
import { MaybeRef, MaybeRefOrGetter, reactive, watch } from "vue";
|
import { reactive, watch } from "vue";
|
||||||
|
|
||||||
/** The player's settings object. */
|
/** The player's settings object. */
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
|
@ -101,22 +101,22 @@ export function loadSettings(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A list of fields to append to the settings modal. */
|
/** A list of fields to append to the settings modal. */
|
||||||
export const settingFields: MaybeRef<Renderable>[] = reactive([]);
|
export const settingFields: MaybeGetter<Renderable>[] = reactive([]);
|
||||||
/** Register a field to be displayed in the settings modal. */
|
/** Register a field to be displayed in the settings modal. */
|
||||||
export function registerSettingField(component: MaybeRefOrGetter<Renderable>) {
|
export function registerSettingField(component: MaybeGetter<Renderable>) {
|
||||||
settingFields.push(processGetter(component));
|
settingFields.push(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A list of components to show in the info modal. */
|
/** A list of components to show in the info modal. */
|
||||||
export const infoComponents: MaybeRef<Renderable>[] = reactive([]);
|
export const infoComponents: MaybeGetter<Renderable>[] = reactive([]);
|
||||||
/** Register a component to be displayed in the info modal. */
|
/** Register a component to be displayed in the info modal. */
|
||||||
export function registerInfoComponent(component: MaybeRefOrGetter<Renderable>) {
|
export function registerInfoComponent(component: MaybeGetter<Renderable>) {
|
||||||
infoComponents.push(processGetter(component));
|
infoComponents.push(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A list of components to add to the root of the page. */
|
/** A list of components to add to the root of the page. */
|
||||||
export const gameComponents: MaybeRef<Renderable>[] = reactive([]);
|
export const gameComponents: MaybeGetter<Renderable>[] = reactive([]);
|
||||||
/** Register a component to be displayed at the root of the page. */
|
/** Register a component to be displayed at the root of the page. */
|
||||||
export function registerGameComponent(component: MaybeRefOrGetter<Renderable>) {
|
export function registerGameComponent(component: MaybeGetter<Renderable>) {
|
||||||
gameComponents.push(processGetter(component));
|
gameComponents.push(component);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { isFunction } from "util/common";
|
||||||
import type { ComputedRef } from "vue";
|
import type { ComputedRef } from "vue";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
export type MaybeGetter<T> = T | (() => T);
|
||||||
|
|
||||||
export function processGetter<T>(obj: T): T extends () => infer S ? ComputedRef<S> : T {
|
export function processGetter<T>(obj: T): T extends () => infer S ? ComputedRef<S> : T {
|
||||||
if (isFunction(obj)) {
|
if (isFunction(obj)) {
|
||||||
return computed(obj) as ReturnType<typeof processGetter<T>>;
|
return computed(obj) as ReturnType<typeof processGetter<T>>;
|
||||||
|
|
|
@ -6,9 +6,9 @@ import Col from "components/layout/Column.vue";
|
||||||
import Row from "components/layout/Row.vue";
|
import Row from "components/layout/Row.vue";
|
||||||
import { getUniqueID, Visibility } from "features/feature";
|
import { getUniqueID, Visibility } from "features/feature";
|
||||||
import VueFeatureComponent from "features/VueFeature.vue";
|
import VueFeatureComponent from "features/VueFeature.vue";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import type { CSSProperties, MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
import type { CSSProperties, MaybeRef, MaybeRefOrGetter, Ref } from "vue";
|
||||||
import { isRef, onUnmounted, ref, unref } from "vue";
|
import { isRef, onUnmounted, ref, toValue } from "vue";
|
||||||
import { JSX } from "vue/jsx-runtime";
|
import { JSX } from "vue/jsx-runtime";
|
||||||
import { camelToKebab } from "./common";
|
import { camelToKebab } from "./common";
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export interface VueFeature {
|
||||||
/** CSS to apply to this feature. */
|
/** CSS to apply to this feature. */
|
||||||
style?: MaybeRef<CSSProperties>;
|
style?: MaybeRef<CSSProperties>;
|
||||||
/** The components to render inside the vue feature */
|
/** The components to render inside the vue feature */
|
||||||
components: MaybeRef<Renderable>[];
|
components: MaybeGetter<Renderable>[];
|
||||||
/** The components to render wrapped around the vue feature */
|
/** The components to render wrapped around the vue feature */
|
||||||
wrappers: ((el: () => Renderable) => Renderable)[];
|
wrappers: ((el: () => Renderable) => Renderable)[];
|
||||||
/** Used to identify Vue Features */
|
/** Used to identify Vue Features */
|
||||||
|
@ -45,14 +45,14 @@ export interface VueFeature {
|
||||||
export function vueFeatureMixin(
|
export function vueFeatureMixin(
|
||||||
featureName: string,
|
featureName: string,
|
||||||
options: VueFeatureOptions,
|
options: VueFeatureOptions,
|
||||||
component?: MaybeRefOrGetter<Renderable>
|
component?: MaybeGetter<Renderable>
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
id: getUniqueID(featureName),
|
id: getUniqueID(featureName),
|
||||||
visibility: processGetter(options.visibility),
|
visibility: processGetter(options.visibility),
|
||||||
classes: processGetter(options.classes),
|
classes: processGetter(options.classes),
|
||||||
style: processGetter(options.style),
|
style: processGetter(options.style),
|
||||||
components: component == null ? [] : [processGetter(component)],
|
components: component == null ? [] : [component],
|
||||||
wrappers: [] as ((el: () => Renderable) => Renderable)[],
|
wrappers: [] as ((el: () => Renderable) => Renderable)[],
|
||||||
[VueFeature]: true
|
[VueFeature]: true
|
||||||
} satisfies VueFeature;
|
} satisfies VueFeature;
|
||||||
|
@ -60,15 +60,15 @@ export function vueFeatureMixin(
|
||||||
|
|
||||||
export function render(object: VueFeature, wrapper?: (el: Renderable) => Renderable): JSX.Element;
|
export function render(object: VueFeature, wrapper?: (el: Renderable) => Renderable): JSX.Element;
|
||||||
export function render<T extends Renderable>(
|
export function render<T extends Renderable>(
|
||||||
object: MaybeRef<Renderable>,
|
object: MaybeGetter<Renderable>,
|
||||||
wrapper?: (el: Renderable) => T
|
wrapper?: (el: Renderable) => T
|
||||||
): T;
|
): T;
|
||||||
export function render(
|
export function render(
|
||||||
object: VueFeature | MaybeRef<Renderable>,
|
object: VueFeature | MaybeGetter<Renderable>,
|
||||||
wrapper?: (el: Renderable) => Renderable
|
wrapper?: (el: Renderable) => Renderable
|
||||||
): Renderable;
|
): Renderable;
|
||||||
export function render(
|
export function render(
|
||||||
object: VueFeature | MaybeRef<Renderable>,
|
object: VueFeature | MaybeGetter<Renderable>,
|
||||||
wrapper?: (el: Renderable) => Renderable
|
wrapper?: (el: Renderable) => Renderable
|
||||||
) {
|
) {
|
||||||
if (typeof object === "object" && VueFeature in object) {
|
if (typeof object === "object" && VueFeature in object) {
|
||||||
|
@ -85,20 +85,24 @@ export function render(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = unref(object);
|
object = toValue(object);
|
||||||
return wrapper?.(object) ?? object;
|
return wrapper?.(object) ?? object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderRow(...objects: (VueFeature | MaybeRef<Renderable>)[]): JSX.Element {
|
export function renderRow(
|
||||||
|
...objects: (VueFeature | MaybeGetter<Renderable>)[]
|
||||||
|
): JSX.Element {
|
||||||
return <Row>{objects.map(obj => render(obj))}</Row>;
|
return <Row>{objects.map(obj => render(obj))}</Row>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderCol(...objects: (VueFeature | MaybeRef<Renderable>)[]): JSX.Element {
|
export function renderCol(
|
||||||
|
...objects: (VueFeature | MaybeGetter<Renderable>)[]
|
||||||
|
): JSX.Element {
|
||||||
return <Col>{objects.map(obj => render(obj))}</Col>;
|
return <Col>{objects.map(obj => render(obj))}</Col>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function joinJSX(
|
export function joinJSX(
|
||||||
objects: (VueFeature | MaybeRef<Renderable>)[],
|
objects: (VueFeature | MaybeGetter<Renderable>)[],
|
||||||
joiner: JSX.Element
|
joiner: JSX.Element
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
return objects.reduce<JSX.Element>(
|
return objects.reduce<JSX.Element>(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { type OptionsFunc } from "features/feature";
|
|
||||||
import { processGetter } from "util/computed";
|
import { processGetter } from "util/computed";
|
||||||
import { createLazyProxy, runAfterEvaluation } from "util/proxies";
|
import { createLazyProxy, runAfterEvaluation } from "util/proxies";
|
||||||
import type { VueFeature } from "util/vue";
|
import type { VueFeature } from "util/vue";
|
||||||
|
@ -23,9 +22,9 @@ export interface Mark {
|
||||||
* @param element The renderable feature to display the tooltip on.
|
* @param element The renderable feature to display the tooltip on.
|
||||||
* @param options Mark options.
|
* @param options Mark options.
|
||||||
*/
|
*/
|
||||||
export function addMark<T extends MarkOptions>(
|
export function addMark(
|
||||||
element: VueFeature,
|
element: VueFeature,
|
||||||
optionsFunc: OptionsFunc<T, Mark>
|
optionsFunc: () => MarkOptions
|
||||||
): asserts element is VueFeature & { mark: Mark } {
|
): asserts element is VueFeature & { mark: Mark } {
|
||||||
const mark = createLazyProxy(() => {
|
const mark = createLazyProxy(() => {
|
||||||
const options = optionsFunc();
|
const options = optionsFunc();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { isVisible, type OptionsFunc } from "features/feature";
|
import { isVisible } from "features/feature";
|
||||||
import { deletePersistent, persistent } from "game/persistence";
|
import { deletePersistent, persistent } from "game/persistence";
|
||||||
import { Direction } from "util/common";
|
import { Direction } from "util/common";
|
||||||
import { processGetter } from "util/computed";
|
import { MaybeGetter, processGetter } from "util/computed";
|
||||||
import { createLazyProxy, runAfterEvaluation } from "util/proxies";
|
import { createLazyProxy, runAfterEvaluation } from "util/proxies";
|
||||||
import { Renderable, vueFeatureMixin, type VueFeature, type VueFeatureOptions } from "util/vue";
|
import { Renderable, vueFeatureMixin, type VueFeature, type VueFeatureOptions } from "util/vue";
|
||||||
import { MaybeRef, MaybeRefOrGetter, type Ref } from "vue";
|
import { MaybeRef, MaybeRefOrGetter, type Ref } from "vue";
|
||||||
|
@ -21,7 +21,7 @@ export interface TooltipOptions extends VueFeatureOptions {
|
||||||
/** Whether or not this tooltip can be pinned, meaning it'll stay visible even when not hovered. */
|
/** Whether or not this tooltip can be pinned, meaning it'll stay visible even when not hovered. */
|
||||||
pinnable?: boolean;
|
pinnable?: boolean;
|
||||||
/** The text to display inside the tooltip. */
|
/** The text to display inside the tooltip. */
|
||||||
display: MaybeRefOrGetter<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** The direction in which to display the tooltip */
|
/** The direction in which to display the tooltip */
|
||||||
direction?: MaybeRefOrGetter<Direction>;
|
direction?: MaybeRefOrGetter<Direction>;
|
||||||
/** The x offset of the tooltip, in px. */
|
/** The x offset of the tooltip, in px. */
|
||||||
|
@ -35,7 +35,7 @@ export interface Tooltip extends VueFeature {
|
||||||
/** Whether or not this tooltip can be pinned, meaning it'll stay visible even when not hovered. */
|
/** Whether or not this tooltip can be pinned, meaning it'll stay visible even when not hovered. */
|
||||||
pinnable?: boolean;
|
pinnable?: boolean;
|
||||||
/** The text to display inside the tooltip. */
|
/** The text to display inside the tooltip. */
|
||||||
display: MaybeRef<Renderable>;
|
display: MaybeGetter<Renderable>;
|
||||||
/** The direction in which to display the tooltip */
|
/** The direction in which to display the tooltip */
|
||||||
direction?: MaybeRef<Direction>;
|
direction?: MaybeRef<Direction>;
|
||||||
/** The x offset of the tooltip, in px. */
|
/** The x offset of the tooltip, in px. */
|
||||||
|
@ -51,9 +51,9 @@ export interface Tooltip extends VueFeature {
|
||||||
* @param element The renderable feature to display the tooltip on.
|
* @param element The renderable feature to display the tooltip on.
|
||||||
* @param options Tooltip options.
|
* @param options Tooltip options.
|
||||||
*/
|
*/
|
||||||
export function addTooltip<T extends TooltipOptions>(
|
export function addTooltip(
|
||||||
element: VueFeature,
|
element: VueFeature,
|
||||||
optionsFunc: OptionsFunc<T, Tooltip>
|
optionsFunc: () => TooltipOptions
|
||||||
): asserts element is VueFeature & { tooltip: Tooltip } {
|
): asserts element is VueFeature & { tooltip: Tooltip } {
|
||||||
const pinned = persistent<boolean>(false, false);
|
const pinned = persistent<boolean>(false, false);
|
||||||
const tooltip = createLazyProxy(() => {
|
const tooltip = createLazyProxy(() => {
|
||||||
|
@ -69,7 +69,7 @@ export function addTooltip<T extends TooltipOptions>(
|
||||||
...vueFeatureMixin("tooltip", options),
|
...vueFeatureMixin("tooltip", options),
|
||||||
pinnable: pinnable ?? true,
|
pinnable: pinnable ?? true,
|
||||||
pinned: pinnable === false ? undefined : pinned,
|
pinned: pinnable === false ? undefined : pinned,
|
||||||
display: processGetter(display),
|
display,
|
||||||
direction: processGetter(direction ?? Direction.Up),
|
direction: processGetter(direction ?? Direction.Up),
|
||||||
xoffset: processGetter(xoffset),
|
xoffset: processGetter(xoffset),
|
||||||
yoffset: processGetter(yoffset)
|
yoffset: processGetter(yoffset)
|
||||||
|
|
|
@ -9,15 +9,16 @@ import {
|
||||||
} from "game/modifiers";
|
} from "game/modifiers";
|
||||||
import Decimal, { DecimalSource } from "util/bignum";
|
import Decimal, { DecimalSource } from "util/bignum";
|
||||||
import { WithRequired } from "util/common";
|
import { WithRequired } from "util/common";
|
||||||
|
import { MaybeGetter } from "util/computed";
|
||||||
|
import { render, Renderable } from "util/vue";
|
||||||
import { beforeAll, describe, expect, test } from "vitest";
|
import { beforeAll, describe, expect, test } from "vitest";
|
||||||
import { MaybeRefOrGetter, Ref, ref, unref } from "vue";
|
import { MaybeRefOrGetter, Ref, ref, unref } from "vue";
|
||||||
import "../utils";
|
import "../utils";
|
||||||
import { render, Renderable } from "util/vue";
|
|
||||||
|
|
||||||
export type ModifierConstructorOptions = {
|
export type ModifierConstructorOptions = {
|
||||||
[S in "addend" | "multiplier" | "exponent"]: MaybeRefOrGetter<DecimalSource>;
|
[S in "addend" | "multiplier" | "exponent"]: MaybeRefOrGetter<DecimalSource>;
|
||||||
} & {
|
} & {
|
||||||
description?: MaybeRefOrGetter<Renderable>;
|
description?: MaybeGetter<Renderable>;
|
||||||
enabled?: MaybeRefOrGetter<boolean>;
|
enabled?: MaybeRefOrGetter<boolean>;
|
||||||
smallerIsBetter?: boolean;
|
smallerIsBetter?: boolean;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue