forked from profectus/Profectus
Document challenge
This commit is contained in:
parent
165eba688e
commit
e6c7ad62a7
1 changed files with 64 additions and 5 deletions
|
@ -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" {
|
||||||
|
|
Loading…
Reference in a new issue