diff --git a/src/features/achievements/achievement.tsx b/src/features/achievements/achievement.tsx index c79d1ad..ee1ea1d 100644 --- a/src/features/achievements/achievement.tsx +++ b/src/features/achievements/achievement.tsx @@ -2,7 +2,6 @@ import AchievementComponent from "features/achievements/Achievement.vue"; import { CoercableComponent, Component, - findFeatures, GatherProps, getUniqueID, Replace, @@ -10,7 +9,6 @@ import { StyleValue, Visibility } from "features/feature"; -import { globalBus } from "game/events"; import "game/notifications"; import { Persistent, makePersistent, PersistentState } from "game/persistence"; import { @@ -22,15 +20,16 @@ import { } from "util/computed"; import { createLazyProxy } from "util/proxies"; import { coerceComponent } from "util/vue"; -import { Unsubscribe } from "nanoevents"; -import { Ref, unref } from "vue"; +import { Ref, unref, watchEffect } from "vue"; import { useToast } from "vue-toastification"; +const toast = useToast(); + export const AchievementType = Symbol("Achievement"); export interface AchievementOptions { visibility?: Computable; - shouldEarn?: Computable; + shouldEarn?: () => boolean; display?: Computable; mark?: Computable; image?: Computable; @@ -52,7 +51,6 @@ export type Achievement = Replace< T & BaseAchievement, { visibility: GetComputableTypeWithDefault; - shouldEarn: GetComputableType; display: GetComputableType; mark: GetComputableType; image: GetComputableType; @@ -85,7 +83,6 @@ export function createAchievement( processComputable(achievement as T, "visibility"); setDefault(achievement, "visibility", Visibility.Visible); - processComputable(achievement as T, "shouldEarn"); processComputable(achievement as T, "display"); processComputable(achievement as T, "mark"); processComputable(achievement as T, "image"); @@ -97,29 +94,18 @@ export function createAchievement( return { visibility, display, earned, image, style, classes, mark, id }; }; - return achievement as unknown as Achievement; - }); -} - -const toast = useToast(); - -const listeners: Record = {}; -globalBus.on("addLayer", layer => { - const achievements: GenericAchievement[] = ( - findFeatures(layer, AchievementType) as GenericAchievement[] - ).filter(ach => ach.shouldEarn != null); - if (achievements.length) { - listeners[layer.id] = layer.on("postUpdate", () => { - achievements.forEach(achievement => { + if (achievement.shouldEarn) { + const genericAchievement = achievement as GenericAchievement; + watchEffect(() => { if ( - unref(achievement.visibility) === Visibility.Visible && - !unref(achievement.earned) && - unref(achievement.shouldEarn) + !genericAchievement.earned.value && + unref(genericAchievement.visibility) === Visibility.Visible && + genericAchievement.shouldEarn?.() ) { - achievement[PersistentState].value = true; - achievement.onComplete?.(); - if (achievement.display) { - const Display = coerceComponent(unref(achievement.display)); + genericAchievement.earned.value = true; + genericAchievement.onComplete?.(); + if (genericAchievement.display) { + const Display = coerceComponent(unref(genericAchievement.display)); toast.info(

Achievement earned!

@@ -133,11 +119,8 @@ globalBus.on("addLayer", layer => { } } }); - }); - } -}); -globalBus.on("removeLayer", layer => { - // unsubscribe from postUpdate - listeners[layer.id]?.(); - listeners[layer.id] = undefined; -}); + } + + return achievement as unknown as Achievement; + }); +} diff --git a/src/features/milestones/milestone.tsx b/src/features/milestones/milestone.tsx index 359b192..c22693c 100644 --- a/src/features/milestones/milestone.tsx +++ b/src/features/milestones/milestone.tsx @@ -2,7 +2,6 @@ import Select from "components/fields/Select.vue"; import { CoercableComponent, Component, - findFeatures, GatherProps, getUniqueID, jsx, @@ -26,10 +25,11 @@ import { } from "util/computed"; import { createLazyProxy } from "util/proxies"; import { coerceComponent, isCoercableComponent } from "util/vue"; -import { Unsubscribe } from "nanoevents"; -import { computed, Ref, unref } from "vue"; +import { computed, Ref, unref, watchEffect } from "vue"; import { useToast } from "vue-toastification"; +const toast = useToast(); + export const MilestoneType = Symbol("Milestone"); export enum MilestoneDisplay { @@ -42,7 +42,7 @@ export enum MilestoneDisplay { export interface MilestoneOptions { visibility?: Computable; - shouldEarn: Computable; + shouldEarn?: () => boolean; style?: Computable; classes?: Computable>; display?: Computable< @@ -68,7 +68,6 @@ export type Milestone = Replace< T & BaseMilestone, { visibility: GetComputableTypeWithDefault; - shouldEarn: GetComputableType; style: GetComputableType; classes: GetComputableType; display: GetComputableType; @@ -124,7 +123,6 @@ export function createMilestone( } }); - processComputable(milestone as T, "shouldEarn"); processComputable(milestone as T, "style"); processComputable(milestone as T, "classes"); processComputable(milestone as T, "display"); @@ -134,52 +132,40 @@ export function createMilestone( return { visibility, display, style, classes, earned, id }; }; + 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( + <> +

Milestone earned!

+
+ {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore */} + +
+ + ); + } + } + }); + } + return milestone as unknown as Milestone; }); } -const toast = useToast(); - -const listeners: Record = {}; -globalBus.on("addLayer", layer => { - const milestones: GenericMilestone[] = ( - findFeatures(layer, MilestoneType) as GenericMilestone[] - ).filter(milestone => milestone.shouldEarn != null); - listeners[layer.id] = layer.on("postUpdate", () => { - milestones.forEach(milestone => { - if ( - unref(milestone.visibility) === Visibility.Visible && - !milestone.earned.value && - unref(milestone.shouldEarn) - ) { - milestone[PersistentState].value = true; - milestone.onComplete?.(); - if (milestone.display) { - const display = unref(milestone.display); - const Display = coerceComponent( - isCoercableComponent(display) ? display : display.requirement - ); - toast( - <> -

Milestone earned!

-
- {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} - {/* @ts-ignore */} - -
- - ); - } - } - }); - }); -}); -globalBus.on("removeLayer", layer => { - // unsubscribe from postUpdate - listeners[layer.id]?.(); - listeners[layer.id] = undefined; -}); - declare module "game/settings" { interface Settings { msDisplay: MilestoneDisplay;