Achievement Templates
WARNING: EVERY GAME IS DIFFERENT!
Here you'll see examples of some typical achievements. You can use them as inspiration to create your own achievement. But keep in mind the following:
EVERY GAME IS DIFFERENT! The memory has a different behavior from game to game.
So do NOT take these templates as a rule. They are here just for educational purposes.
In these examples we use the following convention 0xLEVEL is the memory address with the level ID; 0xLIFE is the address used for the character life; 0xTIME for time, 0xITEM for getting an item, etc...
- Collecting an Item N Times
- Finish Level N
- Finish Level N Before Time Reaches T
- Finish Level N In Under Time T When There Is No In-Game Timer
- Finish Level N without Dying (or getting hit, using a weapon, etc.)
- Finish Level N with Item
- Collect an Item in a Specific Level
- Collected 100 Percent of Something
- Circumvent the Problem of a Counter Incrementing Twice in the Same Frame
- Check for a Specific Value Changing to Another Specific Value Ten Times
- Conditional Resets
- "Pause Until:" Using
PauseIf
to Prevent Achievement Processing Until Some Condition is Met
Collecting an Item N times
There are some situations where you want to award an achievement for collecting an item (like a coin or a ring) N times. You'll need to find in the memory the address responsible to count how much of the item you have. Once you have the address, this logic usually does the job:
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | Mem | 0xCOUNT | > | Delta | 0xCOUNT | (N) |
But you most likely will want to be more specific, for example "get item N times without dying". Then you should use another condition with a ResetIf
, like this:
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | Mem | 0xCOUNT | > | Delta | 0xCOUNT | (N) | |
2 | ResetIf | Mem | 0xLIVES | < | Delta | 0xLIVES |
The ResetIf
condition could be "while in level X", "without using a bomb", etc.
Finish Level N
In this example we want to award when the player finish the level N
and goes to level N+1
.
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | Delta | 0xLEVEL | = | Value | N | (0) | |
2 | Mem | 0xLEVEL | = | Value | N+1 | (0) |
Requirements
- 1: On Level
N
in previous frame. - 2: Now at level
N+1
on current frame.
This logic is true only on the exact frame the level advances from N
to N+1
, which makes it safe from being triggered by a level select cheat or loading a password to Level N+1
. For robust achievement logic, you'll want other conditions too, such as checking the player is in-game or there is no demo active.
Finish Level N Before Time Reaches T
In this example we consider a game where the time decreases (e.g.: Super Mario Bros). Adapt it accordingly the time on your game has a different behavior.
We want to award if the player finishes the level N
while time is greater than T
.
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | Delta | 0xLEVEL | = | Value | N | ||
2 | Trigger | Mem | 0xLEVEL | = | Value | N+1 | |
3 | Mem | 0xTIME | > | Value | T |
Requirements
- 1-2: Functionally the same as conditions 1-2 from the Finish Level N template, but with a
Trigger
on the final line so that the challenge icon shows to the player while the challenge is active. - 3: Ensure the
TIME
address has a value greater than the failure time. When the time is at or below this value, a non-Trigger-flagged condition is now false and the challenge icon will disappear, indicating a failure to the user.
Notes
- Keep in mind that besides knowing how the time behaves in game you also need to research how it behaves in memory.
Finish Level N In Under Time T When There Is No In-Game Timer
In this example we consider a game where there is no in-game timer to use for a speed run.
We want to award if the player finishes the level N
before a time T
in the level, calculated as number of frames, has passed.
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | Delta | 0xLEVEL | = | Value | N | ||
2 | Trigger | Mem | 0xLEVEL | = | Value | N+1 | |
3 | PauseIf | Mem | 0xLEVEL | = | Value | N | (T*FRAMERATE) |
Requirements
- 1-2: Functionally the same as conditions 1-2 from the Finish Level N template, but with a
Trigger
on the final line so that the challenge icon shows to the player while the challenge is active. - 3: The hit target
T*FRAMERATE
should be set equal to the number of frames that equal the time at which the challenge fails. For a system that runs at 60 frames per second,T*FRAMERATE = TimeInSeconds x 60
. When the player is in the level for that many frames, thePauseIf
will become true and locked achievement processing in the core group until a Reset occurs (see Notes, below).
Notes
- A player should be able to re-try this challenge. You will need a Reset to clear the pause lock at an appropriate time, such as dying. You can use a
ResetIf
in an alt group (an activePauseIf
will prevent a reset in the core group from working), or you can attach aResetNextIf
right before thePauseIf
. - You may want some other conditions on the
PauseIf
. You can link more usingAndNext
flag. If a game has a flag that indicates the player has control of the character, this may be a good choice. Experiment!
Finish Level N without Dying
(or getting hit, using a weapon, etc.)
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | AndNext | Mem | 0xLEVEL | = | Value | N | |
2 | Mem | 0xLVL_STATE | = | Value | LVL_N_INTRO | (1) | |
3 | Delta | 0xLEVEL | = | Value | N | ||
4 | Trigger | Mem | 0xLEVEL | = | Value | N+1 | |
5 | ResetIf | Mem | 0xLEVEL | = | Value | TITLE | |
6 | AndNext | Mem | 0xLEVEL | = | Value | N | |
7 | ResetIf | Mem | 0xLIVES | < | Delta | 0xLIVES |
Requirements
- 1-2 Supposes there is a
LEVEL_STATE
address that has values like "In Level Intro Screen," "Playing Level," etc and sets a checkpoint hit when you are in the correct level's intro. This is a stand in for some "checkpoint" where you want the challenge to start. You will need to determine the proper conditions and timing and set up your own appropriate start checkpoint. - 3-4: Functionally the same as conditions 1-2 from the Finish Level N template, but with a Trigger on the final line so that the challenge icon shows to the player while the challenge is active.
- 5: Resets the start checkpoint if the player has a game over or otherwise is able to quit back to the title screen early. Prevents the checkpoint hit from sticking around when it shouldn't.
- 6-7: A Reset for when the
LEVEL
address has the value of levelN
and theLIVES
address is less than it was on the previous frame, indicating the player lost a life. Clears the starting checkpoint hit and so fails the challenge.
Notes
- The requirements in 6-7 can also be modified to reset hit count if a weapon was used, damage taken, or anything else that you want the player to NOT do. -The requirements in 1-2 may vary widely depending on the game.
Finish Level N with Item
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | Delta | 0xLEVEL | = | Value | N | (0) | |
2 | Trigger | Mem | 0xLEVEL | = | Value | N+1 | (0) |
3 | Mem | 0xITEM | = | Value | TRUE | (0) |
Requirements
- 1-2: Functionally the same as conditions 1-2 from the Finish Level N template, but with a
Trigger
on the final line so that the challenge icon shows to the player while the challenge is active. - 3: Value that is true when the player has the correct item. The lack of a
Trigger
flag here allows the icon to appear while the player has the item, and disappear when the player does not have it.
Collect an Item In a Specific Level
This template is for battery save or password protection for items. It avoids awarding "get item X" by loading a password or save where the player already has the item. It checks that an item is collected in the level/room ID it is supposed to be collected, and only allows it be earned at that time.
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | Mem | 0xLEVEL | = | Value | COLLECT_LEVEL | (0) | |
2 | Mem | 0xLEVEL | = | Delta | 0xLEVEL | (0) | |
3 | Delta | 0xITEM | = | Value | FALSE | (0) | |
4 | Mem | 0xITEM | = | Value | TRUE | (0) |
Requirements
- 1-2: Must be in the correct Level (
COLLECT_LEVEL
) for at least two frames during the frame at which the item status changes, preventing the following conditions from triggering the logic on load. - 3: Did not have the item last frame
- 4: Have the item on the current frame.
Notes
- Requirements 1 and 2 together mean you have to have been in the level for at least two frames. The frame at which you load the data (from password or save) will be considered false, so any change in the item status at this time won't cause the trigger.
- It does not necessarily have to be level/Room ID. There are other ways to approach this problem, for example a unique Mem/Val that only occurs on collection. Etc.
Collected 100 Percent of Something
This is limited battery save/password protection for when a player will collect 100% of something like clearing each stage in Super Mario world, or getting a 100% collection rate in Super Metroid. It's necessary so that a player cannot just load a save at 100% and get the achievement for free. (As usual there are other ways to approach this problem too.)
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | Delta | 0xCollectPercent | < | Value | 100% value | ||
2 | Mem | 0xCollectPercent | = | Value | 100% value | ||
3 | Mem | 0xGuardConditions | = | Value | When to Check for Increase |
Using the correct condition or conditions for requirement 3+ is especially important. You need to find an address or addresses that represents a unique time in the game where this percent increases. You'd not want the achievement to trigger when the player is loading their in game save file, which means this achievement must evaluate to False
at that time.
Examples
- Check for an in-game state and look for it being active for multiple frames, similar to the level check in Collect an Item in a specific Level:
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
3 | Mem | 0xGAME_STATE | = | Value | IN_GAME | (0) | |
4 | Mem | 0xGAME_STATE | = | Delta | 0xGAME_STATE | (0) |
- In Super Mario World this change happens while the player sees the world map after completing a stage. So you construct your conditions for this timing in a way to distinguish that from loading a save. Here the player state would be
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
3 | Mem | 0xPlayerState | = | Value | Returned to map from completed |
Circumvent the Problem of a Counter Incrementing Twice in the Same Frame
In the Collecting an Item N times we are counting how many times the counter goes up. But in some games there are situations where the counter goes up twice in the same frame, and the hit counter is incremented only by one. This behavior, obviously, ruins our logic. We're going to see a way to circumvent this issue.
The technique used here relies on two other ones:
Here's the trick:
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | SubSource | Delta | 0xCOUNT | ||||
2 | AddHits | Mem | 0xCOUNT | = | Value | 0x02 | |
3 | Mem | 0xCOUNT | > | Delta | 0xCOUNT | (N) | |
4 | ResetIf | Mem | 0xLIVES | < | Delta | 0xLIVES |
It can look a bit confusing at a first sight, but maybe using a real example it can be more clear. Check the Circumvent the Problem of a Counter Incrementing Twice in the Same Frame.
Check for a Specific Value Changing to Another Specific Value Ten Times
In this example we want to detect a value changing from V1
to V2
ten times:
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | AndNext | Delta | 0xADDRESS | = | Value | 0xV1 | |
2 | Mem | 0xADDRESS | = | Value | 0xV2 | (10) |
Requirements
- 1: If value in
0xADDRESS
in the previous frame is0xV1
- 2: AND If the current value in
0xADDRESS
is0xV2
, increases the hitcount (up to 10).
Conditional Resets
Conditional resets can be used for many things.
Lets say you want to have a reset if a player enters a certain X and Y zone of a level:
CORE
- The Core is whatever condition(s) you need for your achievement to be true. It can also include normal reset behavior.
ALT1
ID | Flag | Type | Memory | Cmp | Type | Mem/Val |
---|---|---|---|---|---|---|
1 | ResetIf | Mem | 0xLEVEL | = | Value | LEVELID |
2 | PauseIf | Mem | 0xX-COORDS | > | Value | RESET-X-ZONE |
3 | PauseIf | Mem | 0xX-COORDS | < | Value | RESET-X-ZONE |
4 | PauseIf | Mem | 0xY-COORDS | > | Value | RESET-Y-ZONE |
5 | PauseIf | Mem | 0xY-COORDS | < | Value | RESET-Y-ZONE |
- The reset will only happen if all of the
PauseIf
conditions are not true. - The pause is local to the alt but the reset resets the entire achievement.
ALT2
ID | Flag | Type | Memory | Cmp | Type | Mem/Val |
---|---|---|---|---|---|---|
1 | Mem | 0x1 | = | Mem | 0x1 |
- Any true condition. (to satisfy alt behavior having an always true alt is needed)
Notes
- You can use multiple conditional resets, each one in their own alt group to have far greater control of reset behavior.
Pause Until-Using PauseIf
to Prevent Achievement Processing Until Some Condition is Met
Sometimes it will be very difficult to identify an on-going timing for when to process hit counts or other hit-related events. Or, there are a lot of conditions for when something might look valid, but not be valid. This situation may lead to convoluted logic in order to guard against invalid hit counts. One method to mitigate this is a Pause Until
:
Basic Construction of the "Pause Until"
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
1 | AndNext | Delta | 0xSTATE | = | Value | VALUE_A | 0 (0) |
2 | ResetNextIf | Mem | 0xSTATE | = | Value | VALUE_B | 1 (0) |
3 | PauseIf | Value | 1 | = | Value | 1 | 0 (0) |
Explanation of Conditions:
- 1-2: This transition from
VALUE_A
toVALUE_B
may mark the 'start of game/level/sequence' definitively. However,VALUE_B
may not definitively identify the 'in-game/in-level/in-sequence' that you care about for the challenge. TheResetNextIf
here will latch to true upon this transition and suppress the "always-true"PauseIf
in the following condition, allowing the rest of the logic to be processed. The single hit target here is important for this to function properly. - 3: This
PauseIf
condition will always be true. That is, unless theResetNextIf
before it is latched to 'true' but receiving a hit towards its target of 1 hit! That's what makes this a "Pause Until" structure: The group is paused until theResetNextIf
occurs.
An Example of Logic towards an Achievement
Suppose our actual goal is to "Complete Game/Level/Sequence in under X units of time without losing health."
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
4 | Delta | 0xCOMPLETE_FLAG | = | Value | 0 | 0 (0) | |
5 | Trigger | Mem | 0xCOMPLETE_FLAG | = | Value | 1 | 0 (0) |
In place of conditions 4 and 5 here, you would use whatever logic definitively identifies the completion of the challenge you are constructing. This is just one example of a set of conditions you might have to identify the completion of the challenge.
ID | Flag | Type | Memory | Cmp | Type | Mem/Val | Hits |
---|---|---|---|---|---|---|---|
6 | ResetIf | Mem | 0xHEALTH | < | Delta | 0xHEALTH | 0 (0) |
7 | ResetIf | Value | 0 | = | Value | 0x0 | NUM_FRAMES_EQUAL_TO_TIME_X (0) |
Here, either ResetIf
statement will remove the hit from the ResetNextIf
and result in the achievement becoming Paused, preventing the timing hits from accruing until the game/level/sequence start transition happens again. Because the group is paused until the challenge starts, the timer reset will never falsely accrue hits outside of the challenge.
Something To Keep In Mind
There will rarely be a time where this structure is needed. Usually the developer should be able to find some conditions to join with the timer or other counter (using the AndNext
flag, etc) in order to restrict them to a specific window, along with any other resets needed to clear the counter hits to zero for other failure cases.