Document challenge

This commit is contained in:
thepaperpilot 2023-04-02 23:33:49 -05:00
parent 165eba688e
commit e6c7ad62a7

View file

@ -36,47 +36,87 @@ import { createLazyProxy } from "util/proxies";
import type { Ref, WatchStopHandle } from "vue"; import type { Ref, WatchStopHandle } from "vue";
import { computed, unref, watch } from "vue"; import { computed, unref, watch } from "vue";
export const ChallengeType = Symbol("ChallengeType"); /** A symbol used to identify {@link Challenge} features. */
export const ChallengeType = Symbol("Challenge");
/**
* An object that configures a {@link Challenge}.
*/
export interface ChallengeOptions { export interface ChallengeOptions {
/** Whether this challenge should be visible. */
visibility?: Computable<Visibility | boolean>; visibility?: Computable<Visibility | boolean>;
/** Whether this challenge can be started. */
canStart?: Computable<boolean>; canStart?: Computable<boolean>;
/** The reset function for this challenge. */
reset?: GenericReset; reset?: GenericReset;
/** The requirement(s) to complete this challenge. */
requirements: Requirements; requirements: Requirements;
/** Whether or not completing this challenge should grant multiple completions if requirements met. Requires {@link requirements} to be a requirement or array of requirements with {@link Requirement.canMaximize} true. */
maximize?: Computable<boolean>; maximize?: Computable<boolean>;
/** The maximum number of times the challenge can be completed. */
completionLimit?: Computable<DecimalSource>; completionLimit?: Computable<DecimalSource>;
/** Shows a marker on the corner of the feature. */
mark?: Computable<boolean | string>; mark?: Computable<boolean | string>;
/** Dictionary of CSS classes to apply to this feature. */
classes?: Computable<Record<string, boolean>>; classes?: Computable<Record<string, boolean>>;
/** CSS to apply to this feature. */
style?: Computable<StyleValue>; style?: Computable<StyleValue>;
/** The display to use for this challenge. */
display?: Computable< display?: Computable<
| CoercableComponent | CoercableComponent
| { | {
/** A header to appear at the top of the display. */
title?: CoercableComponent; title?: CoercableComponent;
/** The main text that appears in the display. */
description: CoercableComponent; description: CoercableComponent;
/** A description of the current goal for this challenge. */
goal?: CoercableComponent; goal?: CoercableComponent;
/** A description of what will change upon completing this challenge. */
reward?: CoercableComponent; reward?: CoercableComponent;
/** A description of the current effect of this challenge. */
effectDisplay?: CoercableComponent; effectDisplay?: CoercableComponent;
} }
>; >;
/** A function that is called when the challenge is completed. */
onComplete?: VoidFunction; onComplete?: VoidFunction;
/** A function that is called when the challenge is exited. */
onExit?: VoidFunction; onExit?: VoidFunction;
/** A function that is called when the challenge is entered. */
onEnter?: VoidFunction; onEnter?: VoidFunction;
} }
/**
* The properties that are added onto a processed {@link ChallengeOptions} to create a {@link Challenge}.
*/
export interface BaseChallenge { export interface BaseChallenge {
/** An auto-generated ID for identifying challenges that appear in the DOM. Will not persist between refreshes or updates. */
id: string; id: string;
/** 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. */
completions: Persistent<DecimalSource>; completions: Persistent<DecimalSource>;
/** Whether or not this challenge has been completed. */
completed: Ref<boolean>; completed: Ref<boolean>;
/** Whether or not this challenge's completion count is at its limit. */
maxed: Ref<boolean>; maxed: Ref<boolean>;
/** Whether or not this challenge is currently active. */
active: Persistent<boolean>; active: Persistent<boolean>;
/** A function to enter or leave the challenge. */
toggle: VoidFunction; toggle: VoidFunction;
/**
* A function to complete this challenge.
* @param remainInChallenge - Optional parameter to specify if the challenge should remain active after completion.
*/
complete: (remainInChallenge?: boolean) => void; complete: (remainInChallenge?: boolean) => void;
/** A symbol that helps identify features of the same type. */
type: typeof ChallengeType; type: typeof ChallengeType;
/** The Vue component used to render this feature. */
[Component]: GenericComponent; [Component]: GenericComponent;
/** A function to gather the props the vue component requires for this feature. */
[GatherProps]: () => Record<string, unknown>; [GatherProps]: () => Record<string, unknown>;
} }
/** An object that represents a feature that can be entered and exited, and have one or more completions with scaling requirements. */
export type Challenge<T extends ChallengeOptions> = Replace< export type Challenge<T extends ChallengeOptions> = Replace<
T & BaseChallenge, T & BaseChallenge,
{ {
@ -92,6 +132,7 @@ export type Challenge<T extends ChallengeOptions> = Replace<
} }
>; >;
/** A type that matches any valid {@link Challenge} object. */
export type GenericChallenge = Replace< export type GenericChallenge = Replace<
Challenge<ChallengeOptions>, Challenge<ChallengeOptions>,
{ {
@ -102,6 +143,10 @@ export type GenericChallenge = Replace<
} }
>; >;
/**
* Lazily creates a challenge with the given options.
* @param optionsFunc Challenge options.
*/
export function createChallenge<T extends ChallengeOptions>( export function createChallenge<T extends ChallengeOptions>(
optionsFunc: OptionsFunc<T, BaseChallenge, GenericChallenge> optionsFunc: OptionsFunc<T, BaseChallenge, GenericChallenge>
): Challenge<T> { ): Challenge<T> {
@ -248,6 +293,12 @@ export function createChallenge<T extends ChallengeOptions>(
}); });
} }
/**
* This will automatically complete a challenge when it's requirements are met.
* @param challenge The challenge to auto-complete
* @param autoActive Whether or not auto-completing should currently occur
* @param exitOnComplete Whether or not to exit the challenge after auto-completion
*/
export function setupAutoComplete( export function setupAutoComplete(
challenge: GenericChallenge, challenge: GenericChallenge,
autoActive: Computable<boolean> = true, autoActive: Computable<boolean> = true,
@ -264,19 +315,27 @@ export function setupAutoComplete(
); );
} }
/**
* Utility for taking an array of challenges where only one may be active at a time, and giving a ref to the one currently active (or null if none are active)
* @param challenges The list of challenges that are mutually exclusive
*/
export function createActiveChallenge( export function createActiveChallenge(
challenges: GenericChallenge[] challenges: GenericChallenge[]
): Ref<GenericChallenge | undefined> { ): Ref<GenericChallenge | null> {
return computed(() => challenges.find(challenge => challenge.active.value)); return computed(() => challenges.find(challenge => challenge.active.value) ?? null);
} }
/**
* Utility for reporting if any challenge in a list is currently active. Intended for preventing entering a challenge if another is already active.
* @param challenges List of challenges that are mutually exclusive
*/
export function isAnyChallengeActive( export function isAnyChallengeActive(
challenges: GenericChallenge[] | Ref<GenericChallenge | undefined> challenges: GenericChallenge[] | Ref<GenericChallenge | null>
): Ref<boolean> { ): Ref<boolean> {
if (isArray(challenges)) { if (isArray(challenges)) {
challenges = createActiveChallenge(challenges); challenges = createActiveChallenge(challenges);
} }
return computed(() => (challenges as Ref<GenericChallenge | undefined>).value != null); return computed(() => (challenges as Ref<GenericChallenge | null>).value != null);
} }
declare module "game/settings" { declare module "game/settings" {