diff --git a/src/features/challenges/challenge.tsx b/src/features/challenges/challenge.tsx index 92dce42..34f850c 100644 --- a/src/features/challenges/challenge.tsx +++ b/src/features/challenges/challenge.tsx @@ -12,10 +12,10 @@ import { Visibility } from "features/feature"; import type { GenericReset } from "features/reset"; -import type { Resource } from "features/resources/resource"; import { globalBus } from "game/events"; import type { Persistent } from "game/persistence"; import { persistent } from "game/persistence"; +import { maxRequirementsMet, Requirements } from "game/requirements"; import settings, { registerSettingField } from "game/settings"; import type { DecimalSource } from "util/bignum"; import Decimal from "util/bignum"; @@ -36,11 +36,10 @@ export interface ChallengeOptions { visibility?: Computable; canStart?: Computable; reset?: GenericReset; - canComplete?: Computable; + requirements: Requirements; + maximize?: Computable; completionLimit?: Computable; mark?: Computable; - resource?: Resource; - goal?: Computable; classes?: Computable>; style?: Computable; display?: Computable< @@ -60,6 +59,7 @@ export interface ChallengeOptions { export interface BaseChallenge { id: string; + canComplete: Ref; completions: Persistent; completed: Ref; maxed: Ref; @@ -76,10 +76,10 @@ export type Challenge = Replace< { visibility: GetComputableTypeWithDefault; canStart: GetComputableTypeWithDefault; - canComplete: GetComputableTypeWithDefault>; + requirements: GetComputableType; + maximize: GetComputableType; completionLimit: GetComputableTypeWithDefault; mark: GetComputableTypeWithDefault>; - goal: GetComputableType; classes: GetComputableType; style: GetComputableType; display: GetComputableType; @@ -91,7 +91,6 @@ export type GenericChallenge = Replace< { visibility: ProcessedComputable; canStart: ProcessedComputable; - canComplete: ProcessedComputable; completionLimit: ProcessedComputable; mark: ProcessedComputable; } @@ -105,19 +104,6 @@ export function createChallenge( return createLazyProxy(() => { const challenge = optionsFunc(); - if ( - challenge.canComplete == null && - (challenge.resource == null || challenge.goal == null) - ) { - console.warn( - "Cannot create challenge without a canComplete property or a resource and goal property", - challenge - ); - throw new Error( - "Cannot create challenge without a canComplete property or a resource and goal property" - ); - } - challenge.id = getUniqueID("challenge-"); challenge.type = ChallengeType; challenge[Component] = ChallengeComponent; @@ -137,13 +123,10 @@ export function createChallenge( const genericChallenge = challenge as GenericChallenge; if (genericChallenge.active.value) { if ( - unref(genericChallenge.canComplete) !== false && + Decimal.gt(unref(genericChallenge.canComplete), 0) && !genericChallenge.maxed.value ) { - let completions: boolean | DecimalSource = unref(genericChallenge.canComplete); - if (typeof completions === "boolean") { - completions = 1; - } + const completions = unref(genericChallenge.canComplete); genericChallenge.completions.value = Decimal.min( Decimal.add(genericChallenge.completions.value, completions), unref(genericChallenge.completionLimit) @@ -163,18 +146,20 @@ export function createChallenge( genericChallenge.onEnter?.(); } }; + challenge.canComplete = computed(() => + Decimal.max( + maxRequirementsMet((challenge as GenericChallenge).requirements), + unref((challenge as GenericChallenge).maximize) ? Decimal.dInf : 1 + ) + ); challenge.complete = function (remainInChallenge?: boolean) { const genericChallenge = challenge as GenericChallenge; - let completions: boolean | DecimalSource = unref(genericChallenge.canComplete); + const completions = unref(genericChallenge.canComplete); if ( genericChallenge.active.value && - completions !== false && - (completions === true || Decimal.neq(0, completions)) && + Decimal.gt(completions, 0) && !genericChallenge.maxed.value ) { - if (typeof completions === "boolean") { - completions = 1; - } genericChallenge.completions.value = Decimal.min( Decimal.add(genericChallenge.completions.value, completions), unref(genericChallenge.completionLimit) @@ -196,19 +181,6 @@ export function createChallenge( } return unref(visibility); }); - if (challenge.canComplete == null) { - challenge.canComplete = computed(() => { - const genericChallenge = challenge as GenericChallenge; - if ( - !genericChallenge.active.value || - genericChallenge.resource == null || - genericChallenge.goal == null - ) { - return false; - } - return Decimal.gte(genericChallenge.resource.value, unref(genericChallenge.goal)); - }); - } if (challenge.mark == null) { challenge.mark = computed( () => @@ -219,11 +191,10 @@ export function createChallenge( processComputable(challenge as T, "canStart"); setDefault(challenge, "canStart", true); - processComputable(challenge as T, "canComplete"); + processComputable(challenge as T, "maximize"); processComputable(challenge as T, "completionLimit"); setDefault(challenge, "completionLimit", 1); processComputable(challenge as T, "mark"); - processComputable(challenge as T, "goal"); processComputable(challenge as T, "classes"); processComputable(challenge as T, "style"); processComputable(challenge as T, "display"); @@ -278,9 +249,9 @@ export function setupAutoComplete( ): WatchStopHandle { const isActive = typeof autoActive === "function" ? computed(autoActive) : autoActive; return watch( - [challenge.canComplete as Ref, isActive as Ref], + [challenge.canComplete as Ref, isActive as Ref], ([canComplete, isActive]) => { - if (canComplete && isActive) { + if (Decimal.gt(canComplete, 0) && isActive) { challenge.complete(!exitOnComplete); } } diff --git a/src/features/repeatable.tsx b/src/features/repeatable.tsx index 96bea11..b442068 100644 --- a/src/features/repeatable.tsx +++ b/src/features/repeatable.tsx @@ -78,6 +78,11 @@ export interface BaseRepeatable { maxed: Ref; /** Whether or not this repeatable can be clicked. */ canClick: ProcessedComputable; + /** + * How much amount can be increased by, or 1 if unclickable. + * Capped at 1 if {@link RepeatableOptions.maximize} is false. + **/ + amountToIncrease: Ref; /** A function that gets called when this repeatable is clicked. */ onClick: (event?: MouseEvent | TouchEvent) => void; /** A symbol that helps identify features of the same type. */ @@ -170,6 +175,11 @@ export function createRepeatable( } return currClasses; }); + repeatable.amountToIncrease = computed(() => + unref((repeatable as GenericRepeatable).maximize) + ? maxRequirementsMet(repeatable.requirements) + : 1 + ); repeatable.canClick = computed(() => requirementsMet(repeatable.requirements)); const onClick = repeatable.onClick; repeatable.onClick = function (this: GenericRepeatable, event?: MouseEvent | TouchEvent) { @@ -177,12 +187,7 @@ export function createRepeatable( if (!unref(genericRepeatable.canClick)) { return; } - payRequirements( - repeatable.requirements, - unref(genericRepeatable.maximize) - ? maxRequirementsMet(genericRepeatable.requirements) - : 1 - ); + payRequirements(repeatable.requirements, unref(repeatable.amountToIncrease)); genericRepeatable.amount.value = Decimal.add(genericRepeatable.amount.value, 1); onClick?.(event); }; @@ -233,9 +238,7 @@ export function createRepeatable(
{displayRequirements( genericRepeatable.requirements, - unref(genericRepeatable.maximize) - ? maxRequirementsMet(genericRepeatable.requirements) - : 1 + unref(repeatable.amountToIncrease) )} )} diff --git a/src/game/requirements.tsx b/src/game/requirements.tsx index 8cd1cf7..9e775de 100644 --- a/src/game/requirements.tsx +++ b/src/game/requirements.tsx @@ -239,7 +239,7 @@ export function maxRequirementsMet(requirements: Requirements): DecimalSource { } const reqsMet = unref(requirements.requirementMet); if (typeof reqsMet === "boolean") { - return reqsMet ? Infinity : 0; + return reqsMet ? Decimal.dInf : 0; } else if (Decimal.gt(reqsMet, 1) && unref(requirements.canMaximize) !== true) { return 1; }