2022-03-04 03:39:48 +00:00
|
|
|
import Select from "components/fields/Select.vue";
|
2022-01-14 04:25:47 +00:00
|
|
|
import {
|
|
|
|
CoercableComponent,
|
|
|
|
Component,
|
2022-02-27 19:49:34 +00:00
|
|
|
GatherProps,
|
2022-01-14 04:25:47 +00:00
|
|
|
getUniqueID,
|
2022-02-27 22:41:39 +00:00
|
|
|
jsx,
|
2022-01-14 04:25:47 +00:00
|
|
|
Replace,
|
|
|
|
setDefault,
|
|
|
|
StyleValue,
|
|
|
|
Visibility
|
2022-03-04 03:39:48 +00:00
|
|
|
} from "features/feature";
|
|
|
|
import MilestoneComponent from "features/milestones/Milestone.vue";
|
|
|
|
import { globalBus } from "game/events";
|
|
|
|
import "game/notifications";
|
|
|
|
import { makePersistent, Persistent, PersistentState } from "game/persistence";
|
|
|
|
import settings, { registerSettingField } from "game/settings";
|
|
|
|
import { camelToTitle } from "util/common";
|
2022-01-14 04:25:47 +00:00
|
|
|
import {
|
|
|
|
Computable,
|
|
|
|
GetComputableType,
|
|
|
|
GetComputableTypeWithDefault,
|
|
|
|
processComputable,
|
|
|
|
ProcessedComputable
|
2022-03-04 03:39:48 +00:00
|
|
|
} from "util/computed";
|
|
|
|
import { createLazyProxy } from "util/proxies";
|
|
|
|
import { coerceComponent, isCoercableComponent } from "util/vue";
|
2022-03-11 14:54:05 +00:00
|
|
|
import { computed, Ref, unref, watchEffect } from "vue";
|
2022-01-14 04:25:47 +00:00
|
|
|
import { useToast } from "vue-toastification";
|
|
|
|
|
2022-03-11 14:54:05 +00:00
|
|
|
const toast = useToast();
|
|
|
|
|
2022-01-14 04:25:47 +00:00
|
|
|
export const MilestoneType = Symbol("Milestone");
|
|
|
|
|
|
|
|
export enum MilestoneDisplay {
|
|
|
|
All = "all",
|
|
|
|
//Last = "last",
|
|
|
|
Configurable = "configurable",
|
|
|
|
Incomplete = "incomplete",
|
|
|
|
None = "none"
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface MilestoneOptions {
|
|
|
|
visibility?: Computable<Visibility>;
|
2022-03-11 14:54:05 +00:00
|
|
|
shouldEarn?: () => boolean;
|
2022-01-14 04:25:47 +00:00
|
|
|
style?: Computable<StyleValue>;
|
|
|
|
classes?: Computable<Record<string, boolean>>;
|
|
|
|
display?: Computable<
|
|
|
|
| CoercableComponent
|
|
|
|
| {
|
|
|
|
requirement: CoercableComponent;
|
|
|
|
effectDisplay?: CoercableComponent;
|
|
|
|
optionsDisplay?: CoercableComponent;
|
|
|
|
}
|
|
|
|
>;
|
|
|
|
onComplete?: VoidFunction;
|
|
|
|
}
|
|
|
|
|
2022-03-09 01:40:51 +00:00
|
|
|
export interface BaseMilestone extends Persistent<boolean> {
|
2022-01-14 04:25:47 +00:00
|
|
|
id: string;
|
|
|
|
earned: Ref<boolean>;
|
|
|
|
type: typeof MilestoneType;
|
|
|
|
[Component]: typeof MilestoneComponent;
|
2022-02-27 19:49:34 +00:00
|
|
|
[GatherProps]: () => Record<string, unknown>;
|
2022-01-14 04:25:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export type Milestone<T extends MilestoneOptions> = Replace<
|
|
|
|
T & BaseMilestone,
|
|
|
|
{
|
|
|
|
visibility: GetComputableTypeWithDefault<T["visibility"], Visibility.Visible>;
|
|
|
|
style: GetComputableType<T["style"]>;
|
|
|
|
classes: GetComputableType<T["classes"]>;
|
|
|
|
display: GetComputableType<T["display"]>;
|
|
|
|
}
|
|
|
|
>;
|
|
|
|
|
|
|
|
export type GenericMilestone = Replace<
|
|
|
|
Milestone<MilestoneOptions>,
|
|
|
|
{
|
|
|
|
visibility: ProcessedComputable<Visibility>;
|
|
|
|
}
|
|
|
|
>;
|
|
|
|
|
|
|
|
export function createMilestone<T extends MilestoneOptions>(
|
2022-02-27 19:49:34 +00:00
|
|
|
optionsFunc: () => T & ThisType<Milestone<T>>
|
2022-01-14 04:25:47 +00:00
|
|
|
): Milestone<T> {
|
2022-02-27 19:49:34 +00:00
|
|
|
return createLazyProxy(() => {
|
|
|
|
const milestone: T & Partial<BaseMilestone> = optionsFunc();
|
|
|
|
makePersistent<boolean>(milestone, false);
|
|
|
|
milestone.id = getUniqueID("milestone-");
|
|
|
|
milestone.type = MilestoneType;
|
|
|
|
milestone[Component] = MilestoneComponent;
|
2022-01-14 04:25:47 +00:00
|
|
|
|
2022-02-27 19:49:34 +00:00
|
|
|
milestone.earned = milestone[PersistentState];
|
|
|
|
processComputable(milestone as T, "visibility");
|
|
|
|
setDefault(milestone, "visibility", Visibility.Visible);
|
|
|
|
const visibility = milestone.visibility as ProcessedComputable<Visibility>;
|
|
|
|
milestone.visibility = computed(() => {
|
|
|
|
const display = unref((milestone as GenericMilestone).display);
|
|
|
|
switch (settings.msDisplay) {
|
|
|
|
default:
|
|
|
|
case MilestoneDisplay.All:
|
|
|
|
return unref(visibility);
|
|
|
|
case MilestoneDisplay.Configurable:
|
|
|
|
if (
|
|
|
|
unref(milestone.earned) &&
|
|
|
|
!(
|
|
|
|
display != null &&
|
|
|
|
typeof display == "object" &&
|
|
|
|
"optionsDisplay" in (display as Record<string, unknown>)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return Visibility.None;
|
|
|
|
}
|
|
|
|
return unref(visibility);
|
|
|
|
case MilestoneDisplay.Incomplete:
|
|
|
|
if (unref(milestone.earned)) {
|
|
|
|
return Visibility.None;
|
|
|
|
}
|
|
|
|
return unref(visibility);
|
|
|
|
case MilestoneDisplay.None:
|
2022-01-14 04:25:47 +00:00
|
|
|
return Visibility.None;
|
2022-02-27 19:49:34 +00:00
|
|
|
}
|
|
|
|
});
|
2022-01-14 04:25:47 +00:00
|
|
|
|
2022-02-27 19:49:34 +00:00
|
|
|
processComputable(milestone as T, "style");
|
|
|
|
processComputable(milestone as T, "classes");
|
|
|
|
processComputable(milestone as T, "display");
|
2022-01-14 04:25:47 +00:00
|
|
|
|
2022-02-27 19:49:34 +00:00
|
|
|
milestone[GatherProps] = function (this: GenericMilestone) {
|
|
|
|
const { visibility, display, style, classes, earned, id } = this;
|
|
|
|
return { visibility, display, style, classes, earned, id };
|
|
|
|
};
|
|
|
|
|
2022-03-11 14:54:05 +00:00
|
|
|
if (milestone.shouldEarn) {
|
|
|
|
const genericMilestone = milestone as GenericMilestone;
|
|
|
|
watchEffect(() => {
|
|
|
|
if (
|
|
|
|
!genericMilestone.earned.value &&
|
|
|
|
unref(genericMilestone.visibility) === Visibility.Visible &&
|
|
|
|
genericMilestone.shouldEarn?.()
|
|
|
|
) {
|
|
|
|
genericMilestone.earned.value = true;
|
|
|
|
genericMilestone.onComplete?.();
|
|
|
|
if (genericMilestone.display) {
|
|
|
|
const display = unref(genericMilestone.display);
|
|
|
|
const Display = coerceComponent(
|
|
|
|
isCoercableComponent(display) ? display : display.requirement
|
|
|
|
);
|
|
|
|
toast(
|
|
|
|
<>
|
|
|
|
<h3>Milestone earned!</h3>
|
|
|
|
<div>
|
|
|
|
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
|
|
|
|
{/* @ts-ignore */}
|
|
|
|
<Display />
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-02-27 19:49:34 +00:00
|
|
|
return milestone as unknown as Milestone<T>;
|
|
|
|
});
|
2022-01-14 04:25:47 +00:00
|
|
|
}
|
|
|
|
|
2022-03-04 04:45:25 +00:00
|
|
|
declare module "game/settings" {
|
2022-01-14 04:25:47 +00:00
|
|
|
interface Settings {
|
|
|
|
msDisplay: MilestoneDisplay;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
globalBus.on("loadSettings", settings => {
|
|
|
|
setDefault(settings, "msDisplay", MilestoneDisplay.All);
|
|
|
|
});
|
2022-02-27 22:41:39 +00:00
|
|
|
|
|
|
|
const msDisplayOptions = Object.values(MilestoneDisplay).map(option => ({
|
|
|
|
label: camelToTitle(option),
|
|
|
|
value: option
|
|
|
|
}));
|
|
|
|
|
|
|
|
registerSettingField(
|
|
|
|
jsx(() => (
|
|
|
|
<Select
|
|
|
|
title="Show Milestones"
|
|
|
|
options={msDisplayOptions}
|
|
|
|
onUpdate:modelValue={value => (settings.msDisplay = value as MilestoneDisplay)}
|
|
|
|
modelValue={settings.msDisplay}
|
|
|
|
/>
|
|
|
|
))
|
|
|
|
);
|