Profectus / features/achievements/achievement
features/achievements/achievement ​
This is a feature that represents some task the player can achieve a single time. This could be used to show the player their next goal, or be a part of a list of accomplishments to try to achieve. Typically these are things the player is expected to do during normal progression, in which case the achievement should either have no reward or a QoL reward, or something players must go out of their way to accomplish, in which case they'd have a mechanical reward.
In addition to being renderable on its own, the display will also be used to display a notification when the achievement is earned. If no display is specified, it will generate it from the requirements if they exist.
There are two ways to handle granting the achievement: the requirements systen, or via manually granting it. The first case would look like this, where the achievement will be earned when a total of 10 points are earned:
const points = createResource<DecimalSource>(10);
const total = trackTotal(points);
const totalPoints = createResource(total, "total points");
const achievement = createAchievement(() => ({
requirements: createCostRequirement(() => ({
cost: 10,
resource: totalPoints,
requiresPay: false
})),
display: {
requirement: "Gain 10 total points",
effectDisplay: "1.1x points gain"
}
}));
The other method of granting achievements is through granting them manually. This is for cases where the achievement happens during an "event" or specific moment in time, where the requirements system ultimately just gets in the way. For example, here's an achievement that gets earned for failing a challenge, which would be hard to implement using the requirements system because no reward is granted:
const achievement = createAchievement(() => ({
display: "Fail a challenge"
}));
const challenge = createChallenge(() => ({
requirements: ...,
onExit() {
if (challenge.completed.value === false) {
achievement.complete();
}
}
}));
You may be interested in giving achievements some sort of effect to the player. You can make new features visible or enable modifiers based on an achievement's earned
property like so:
const points = createResource<DecimalSource>(10);
const pointGainModifier = createSequentialModifier(() => [
...,
createMultiplicativeModifier(() => ({ multiplier: 2, enabled: ach.earned }))
]);
const pointGain = computed(() => pointGainModifier.apply(1));
layer.on("update", diff => {
points.value = Decimal.add(points.value, Decimal.times(pointGain.value, diff));
});
Another common reward is a multiplier to some resource that increases based on the number of completed achievements. That could look like this:
const numCompletedAchievements = computed(() => [
ach1,
ach2,
ach3
].filter(ach => ach.earned.value).length);
const pointGainModifier = createSequentialModifier(() => [
...,
createMultiplicativeModifier(() => ({
multiplier: () => Decimal.pow(1.1, numCompletedAchievements.value),
enabled: () => numCompletedAchievements.value > 0
}))
]);