Make challenges use the requirements system
This commit is contained in:
parent
eff5852b04
commit
cc1a2998e0
3 changed files with 32 additions and 58 deletions
|
@ -12,10 +12,10 @@ import {
|
||||||
Visibility
|
Visibility
|
||||||
} from "features/feature";
|
} from "features/feature";
|
||||||
import type { GenericReset } from "features/reset";
|
import type { GenericReset } from "features/reset";
|
||||||
import type { Resource } from "features/resources/resource";
|
|
||||||
import { globalBus } from "game/events";
|
import { globalBus } from "game/events";
|
||||||
import type { Persistent } from "game/persistence";
|
import type { Persistent } from "game/persistence";
|
||||||
import { persistent } from "game/persistence";
|
import { persistent } from "game/persistence";
|
||||||
|
import { maxRequirementsMet, Requirements } from "game/requirements";
|
||||||
import settings, { registerSettingField } from "game/settings";
|
import settings, { registerSettingField } from "game/settings";
|
||||||
import type { DecimalSource } from "util/bignum";
|
import type { DecimalSource } from "util/bignum";
|
||||||
import Decimal from "util/bignum";
|
import Decimal from "util/bignum";
|
||||||
|
@ -36,11 +36,10 @@ export interface ChallengeOptions {
|
||||||
visibility?: Computable<Visibility | boolean>;
|
visibility?: Computable<Visibility | boolean>;
|
||||||
canStart?: Computable<boolean>;
|
canStart?: Computable<boolean>;
|
||||||
reset?: GenericReset;
|
reset?: GenericReset;
|
||||||
canComplete?: Computable<boolean | DecimalSource>;
|
requirements: Requirements;
|
||||||
|
maximize?: Computable<boolean>;
|
||||||
completionLimit?: Computable<DecimalSource>;
|
completionLimit?: Computable<DecimalSource>;
|
||||||
mark?: Computable<boolean | string>;
|
mark?: Computable<boolean | string>;
|
||||||
resource?: Resource;
|
|
||||||
goal?: Computable<DecimalSource>;
|
|
||||||
classes?: Computable<Record<string, boolean>>;
|
classes?: Computable<Record<string, boolean>>;
|
||||||
style?: Computable<StyleValue>;
|
style?: Computable<StyleValue>;
|
||||||
display?: Computable<
|
display?: Computable<
|
||||||
|
@ -60,6 +59,7 @@ export interface ChallengeOptions {
|
||||||
|
|
||||||
export interface BaseChallenge {
|
export interface BaseChallenge {
|
||||||
id: string;
|
id: string;
|
||||||
|
canComplete: Ref<DecimalSource>;
|
||||||
completions: Persistent<DecimalSource>;
|
completions: Persistent<DecimalSource>;
|
||||||
completed: Ref<boolean>;
|
completed: Ref<boolean>;
|
||||||
maxed: Ref<boolean>;
|
maxed: Ref<boolean>;
|
||||||
|
@ -76,10 +76,10 @@ export type Challenge<T extends ChallengeOptions> = Replace<
|
||||||
{
|
{
|
||||||
visibility: GetComputableTypeWithDefault<T["visibility"], Visibility.Visible>;
|
visibility: GetComputableTypeWithDefault<T["visibility"], Visibility.Visible>;
|
||||||
canStart: GetComputableTypeWithDefault<T["canStart"], true>;
|
canStart: GetComputableTypeWithDefault<T["canStart"], true>;
|
||||||
canComplete: GetComputableTypeWithDefault<T["canComplete"], Ref<boolean>>;
|
requirements: GetComputableType<T["requirements"]>;
|
||||||
|
maximize: GetComputableType<T["maximize"]>;
|
||||||
completionLimit: GetComputableTypeWithDefault<T["completionLimit"], 1>;
|
completionLimit: GetComputableTypeWithDefault<T["completionLimit"], 1>;
|
||||||
mark: GetComputableTypeWithDefault<T["mark"], Ref<boolean>>;
|
mark: GetComputableTypeWithDefault<T["mark"], Ref<boolean>>;
|
||||||
goal: GetComputableType<T["goal"]>;
|
|
||||||
classes: GetComputableType<T["classes"]>;
|
classes: GetComputableType<T["classes"]>;
|
||||||
style: GetComputableType<T["style"]>;
|
style: GetComputableType<T["style"]>;
|
||||||
display: GetComputableType<T["display"]>;
|
display: GetComputableType<T["display"]>;
|
||||||
|
@ -91,7 +91,6 @@ export type GenericChallenge = Replace<
|
||||||
{
|
{
|
||||||
visibility: ProcessedComputable<Visibility | boolean>;
|
visibility: ProcessedComputable<Visibility | boolean>;
|
||||||
canStart: ProcessedComputable<boolean>;
|
canStart: ProcessedComputable<boolean>;
|
||||||
canComplete: ProcessedComputable<boolean | DecimalSource>;
|
|
||||||
completionLimit: ProcessedComputable<DecimalSource>;
|
completionLimit: ProcessedComputable<DecimalSource>;
|
||||||
mark: ProcessedComputable<boolean>;
|
mark: ProcessedComputable<boolean>;
|
||||||
}
|
}
|
||||||
|
@ -105,19 +104,6 @@ export function createChallenge<T extends ChallengeOptions>(
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(() => {
|
||||||
const challenge = optionsFunc();
|
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.id = getUniqueID("challenge-");
|
||||||
challenge.type = ChallengeType;
|
challenge.type = ChallengeType;
|
||||||
challenge[Component] = ChallengeComponent;
|
challenge[Component] = ChallengeComponent;
|
||||||
|
@ -137,13 +123,10 @@ export function createChallenge<T extends ChallengeOptions>(
|
||||||
const genericChallenge = challenge as GenericChallenge;
|
const genericChallenge = challenge as GenericChallenge;
|
||||||
if (genericChallenge.active.value) {
|
if (genericChallenge.active.value) {
|
||||||
if (
|
if (
|
||||||
unref(genericChallenge.canComplete) !== false &&
|
Decimal.gt(unref(genericChallenge.canComplete), 0) &&
|
||||||
!genericChallenge.maxed.value
|
!genericChallenge.maxed.value
|
||||||
) {
|
) {
|
||||||
let completions: boolean | DecimalSource = unref(genericChallenge.canComplete);
|
const completions = unref(genericChallenge.canComplete);
|
||||||
if (typeof completions === "boolean") {
|
|
||||||
completions = 1;
|
|
||||||
}
|
|
||||||
genericChallenge.completions.value = Decimal.min(
|
genericChallenge.completions.value = Decimal.min(
|
||||||
Decimal.add(genericChallenge.completions.value, completions),
|
Decimal.add(genericChallenge.completions.value, completions),
|
||||||
unref(genericChallenge.completionLimit)
|
unref(genericChallenge.completionLimit)
|
||||||
|
@ -163,18 +146,20 @@ export function createChallenge<T extends ChallengeOptions>(
|
||||||
genericChallenge.onEnter?.();
|
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) {
|
challenge.complete = function (remainInChallenge?: boolean) {
|
||||||
const genericChallenge = challenge as GenericChallenge;
|
const genericChallenge = challenge as GenericChallenge;
|
||||||
let completions: boolean | DecimalSource = unref(genericChallenge.canComplete);
|
const completions = unref(genericChallenge.canComplete);
|
||||||
if (
|
if (
|
||||||
genericChallenge.active.value &&
|
genericChallenge.active.value &&
|
||||||
completions !== false &&
|
Decimal.gt(completions, 0) &&
|
||||||
(completions === true || Decimal.neq(0, completions)) &&
|
|
||||||
!genericChallenge.maxed.value
|
!genericChallenge.maxed.value
|
||||||
) {
|
) {
|
||||||
if (typeof completions === "boolean") {
|
|
||||||
completions = 1;
|
|
||||||
}
|
|
||||||
genericChallenge.completions.value = Decimal.min(
|
genericChallenge.completions.value = Decimal.min(
|
||||||
Decimal.add(genericChallenge.completions.value, completions),
|
Decimal.add(genericChallenge.completions.value, completions),
|
||||||
unref(genericChallenge.completionLimit)
|
unref(genericChallenge.completionLimit)
|
||||||
|
@ -196,19 +181,6 @@ export function createChallenge<T extends ChallengeOptions>(
|
||||||
}
|
}
|
||||||
return unref(visibility);
|
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) {
|
if (challenge.mark == null) {
|
||||||
challenge.mark = computed(
|
challenge.mark = computed(
|
||||||
() =>
|
() =>
|
||||||
|
@ -219,11 +191,10 @@ export function createChallenge<T extends ChallengeOptions>(
|
||||||
|
|
||||||
processComputable(challenge as T, "canStart");
|
processComputable(challenge as T, "canStart");
|
||||||
setDefault(challenge, "canStart", true);
|
setDefault(challenge, "canStart", true);
|
||||||
processComputable(challenge as T, "canComplete");
|
processComputable(challenge as T, "maximize");
|
||||||
processComputable(challenge as T, "completionLimit");
|
processComputable(challenge as T, "completionLimit");
|
||||||
setDefault(challenge, "completionLimit", 1);
|
setDefault(challenge, "completionLimit", 1);
|
||||||
processComputable(challenge as T, "mark");
|
processComputable(challenge as T, "mark");
|
||||||
processComputable(challenge as T, "goal");
|
|
||||||
processComputable(challenge as T, "classes");
|
processComputable(challenge as T, "classes");
|
||||||
processComputable(challenge as T, "style");
|
processComputable(challenge as T, "style");
|
||||||
processComputable(challenge as T, "display");
|
processComputable(challenge as T, "display");
|
||||||
|
@ -278,9 +249,9 @@ export function setupAutoComplete(
|
||||||
): WatchStopHandle {
|
): WatchStopHandle {
|
||||||
const isActive = typeof autoActive === "function" ? computed(autoActive) : autoActive;
|
const isActive = typeof autoActive === "function" ? computed(autoActive) : autoActive;
|
||||||
return watch(
|
return watch(
|
||||||
[challenge.canComplete as Ref<boolean>, isActive as Ref<boolean>],
|
[challenge.canComplete as Ref<DecimalSource>, isActive as Ref<boolean>],
|
||||||
([canComplete, isActive]) => {
|
([canComplete, isActive]) => {
|
||||||
if (canComplete && isActive) {
|
if (Decimal.gt(canComplete, 0) && isActive) {
|
||||||
challenge.complete(!exitOnComplete);
|
challenge.complete(!exitOnComplete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,11 @@ export interface BaseRepeatable {
|
||||||
maxed: Ref<boolean>;
|
maxed: Ref<boolean>;
|
||||||
/** Whether or not this repeatable can be clicked. */
|
/** Whether or not this repeatable can be clicked. */
|
||||||
canClick: ProcessedComputable<boolean>;
|
canClick: ProcessedComputable<boolean>;
|
||||||
|
/**
|
||||||
|
* How much amount can be increased by, or 1 if unclickable.
|
||||||
|
* Capped at 1 if {@link RepeatableOptions.maximize} is false.
|
||||||
|
**/
|
||||||
|
amountToIncrease: Ref<DecimalSource>;
|
||||||
/** A function that gets called when this repeatable is clicked. */
|
/** A function that gets called when this repeatable is clicked. */
|
||||||
onClick: (event?: MouseEvent | TouchEvent) => void;
|
onClick: (event?: MouseEvent | TouchEvent) => void;
|
||||||
/** A symbol that helps identify features of the same type. */
|
/** A symbol that helps identify features of the same type. */
|
||||||
|
@ -170,6 +175,11 @@ export function createRepeatable<T extends RepeatableOptions>(
|
||||||
}
|
}
|
||||||
return currClasses;
|
return currClasses;
|
||||||
});
|
});
|
||||||
|
repeatable.amountToIncrease = computed(() =>
|
||||||
|
unref((repeatable as GenericRepeatable).maximize)
|
||||||
|
? maxRequirementsMet(repeatable.requirements)
|
||||||
|
: 1
|
||||||
|
);
|
||||||
repeatable.canClick = computed(() => requirementsMet(repeatable.requirements));
|
repeatable.canClick = computed(() => requirementsMet(repeatable.requirements));
|
||||||
const onClick = repeatable.onClick;
|
const onClick = repeatable.onClick;
|
||||||
repeatable.onClick = function (this: GenericRepeatable, event?: MouseEvent | TouchEvent) {
|
repeatable.onClick = function (this: GenericRepeatable, event?: MouseEvent | TouchEvent) {
|
||||||
|
@ -177,12 +187,7 @@ export function createRepeatable<T extends RepeatableOptions>(
|
||||||
if (!unref(genericRepeatable.canClick)) {
|
if (!unref(genericRepeatable.canClick)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
payRequirements(
|
payRequirements(repeatable.requirements, unref(repeatable.amountToIncrease));
|
||||||
repeatable.requirements,
|
|
||||||
unref(genericRepeatable.maximize)
|
|
||||||
? maxRequirementsMet(genericRepeatable.requirements)
|
|
||||||
: 1
|
|
||||||
);
|
|
||||||
genericRepeatable.amount.value = Decimal.add(genericRepeatable.amount.value, 1);
|
genericRepeatable.amount.value = Decimal.add(genericRepeatable.amount.value, 1);
|
||||||
onClick?.(event);
|
onClick?.(event);
|
||||||
};
|
};
|
||||||
|
@ -233,9 +238,7 @@ export function createRepeatable<T extends RepeatableOptions>(
|
||||||
<br />
|
<br />
|
||||||
{displayRequirements(
|
{displayRequirements(
|
||||||
genericRepeatable.requirements,
|
genericRepeatable.requirements,
|
||||||
unref(genericRepeatable.maximize)
|
unref(repeatable.amountToIncrease)
|
||||||
? maxRequirementsMet(genericRepeatable.requirements)
|
|
||||||
: 1
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -239,7 +239,7 @@ export function maxRequirementsMet(requirements: Requirements): DecimalSource {
|
||||||
}
|
}
|
||||||
const reqsMet = unref(requirements.requirementMet);
|
const reqsMet = unref(requirements.requirementMet);
|
||||||
if (typeof reqsMet === "boolean") {
|
if (typeof reqsMet === "boolean") {
|
||||||
return reqsMet ? Infinity : 0;
|
return reqsMet ? Decimal.dInf : 0;
|
||||||
} else if (Decimal.gt(reqsMet, 1) && unref(requirements.canMaximize) !== true) {
|
} else if (Decimal.gt(reqsMet, 1) && unref(requirements.canMaximize) !== true) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue