Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions docs/developer-docs/flags/resetif.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Discover how to effectively use the ResetIf flag to reset hit count

# `ResetIf`

The `ResetIf` can be chosen in the Flag column of the Achievement Editor
The `ResetIf` can be chosen in the Flag column of the Achievement Editor.

If the associated condition is true, **all** [hit counts](/developer-docs/hit-counts) in the achievement will be reset to 0. This includes hit counts in other groups (more about it in [Alt Groups](/developer-docs/alt-groups) below).

Expand All @@ -19,4 +19,30 @@ A `ResetIf` condition with a hit count will only trigger when the hit count targ

**Note**: If another `ResetIf` condition is met, all conditions, including the `ResetIf` with the hit count will have their hit counts reset to 0.

It is very common to use a condition with a hit count of 1 as a start marker for an achievement, and use a `ResetIf` to cancel the achievement before the end condition is met. A common example is a damageless achievement: "From start of (level/battle), reset if damage taken, trigger at end of (level/battle)". When the start condition is true, a hit count is captured. If it's still set when the end condition is true, the achievement triggers. If the `ResetIf` condition triggers, the hit count on the start condition is set back to zero, which makes the condition false when evaluating the state when the end condition is true, and the achievement will not trigger.
It is very common to use a condition with a hit count of 1 as a start marker for an achievement, and use a `ResetIf` to cancel the achievement before the end condition is met. A common example is a damageless achievement:

"From start of (level/battle), reset if damage taken, trigger at end of (level/battle)".

When the start condition is true, a hit count is captured. If it's still set when the end condition is true, the achievement triggers. If the `ResetIf` condition triggers, the hit count on the start condition is set back to zero, which makes the condition false when evaluating the state when the end condition is true, and the achievement will not trigger.

## Example

```
1: ResetIf Mem 8-bit 0x0007536c != Value 3 (0)
2: Mem 8-bit 0x00065980 != Value 1 (0)
3: Mem 8-bit 0x000a35a0 != Value 2 (0)
4: Mem 8-bit 0x000a35a0 != Value 4 (0)
5: Mem 8-bit 0x000a35a0 != Value 7 (0)
6: AndNext Mem 32-bit 0x000b0c00 = Value 1 (0)
7: Delta 32-bit 0x000b0c00 = Value 2 (2)
Comment thread
pilzkopf777 marked this conversation as resolved.
Outdated
8: AndNext Mem 32-bit 0x001eed30 = Value 1 (0)
9: ResetIf Delta 32-bit 0x001eed30 = Value 0 (0)
```

In this achievement we want players to witness a special event twice in one round. The event is tracked using the address 0x000b0c00 - once the value jumps from 2 to 1 the event took place once.

Every time the event started the hit target in condition gains one hit.

Should the player lose or end the current session we wish to reset the amount of events witnessed. If we did not do that a player could see the event once, end the current session, start again and witness it only one more time in order to unlock the achievement.

Condition 1 checks if the player is not in a session. If that condition is true they are in the main menu and thus the hit targets are cleared.
21 changes: 21 additions & 0 deletions docs/developer-docs/flags/resetnextif.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,24 @@ The `ResetNextIf` Flag is a special version of the `ResetIf` Flag that only rese
`AndNext` and `OrNext` are combining logical conditions and will have their hit counts reset, as well as whatever condition follows them.

All other condition flags, including `AddHits` and `SubHits`, will cause processing of the `ResetNextIf` behavior to stop.

## Example

```
1: Mem 8-bit 0x00065980 != Value 1 (0)
2: Mem 8-bit 0x000a35a0 = Value 3 (0)
3: Mem 8-bit 0x0007536c = Value 3 (0)
4: Trigger Mem 32-bit 0x001bf958 >= Value 1500000 (0)
5: Trigger Delta 32-bit 0x001bf958 <= Value 1500000 (0)
6: Mem 16-bit 0x001bf9ac < Value 10 (0)
7: AndNext Mem 8-bit 0x000a2774 = Value 1 (0)
8: ResetNextIf Delta 8-bit 0x000a2774 = Value 0 (0)
9: PauseIf Delta 8-bit 0x000a22e8 > Mem 8-bit 0x000a22e8 (1)
10: Mem 8-bit 0x000a2774 = Value 1 (0)
```

In this achievement we want players to complete an objective without using a special item. The amount of items the player has is tracked in 0x000a22e8.
Once an item has been used condition 9 with the `PauseIf` will be true and prevent the achievement from unlocking.

In order to be able to reattempt the challenge both condition 7 and 8 must be true. Since ResetNextIf is used the PauseIf will then properly be reset.
Other hit counts would not be affected by this.
29 changes: 15 additions & 14 deletions docs/developer-docs/getting-started-as-an-achievement-developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ To begin, run the RALibRetro executable and login when prompted. If you have not

You should now have your main window running Sonic. There are 3 other dialogs we'll be using, which can be found under **RetroAchievements** in the menu. We'll be dealing with the **Memory Inspector** first:

![memoryinspector](/meminspdocs1.png)
![memoryinspector](/new-mem-insp.png)

The Memory Inspector can be used to find addresses in RAM for us to use. Essentially you are on a treasure hunt for memory locations - this dialog will help you examine and filter the game's RAM while the game is running.

Expand All @@ -48,19 +48,19 @@ The Memory Inspector can be used to find addresses in RAM for us to use. Essenti

**See also**: [Memory Inspector Overview](/developer-docs/memory-inspector) and [Memory Digging Tips](/developer-docs/tips-and-tricks#memory-digging-tips).

To start or restart a test, click **Reset** near the top left.
To start or restart a test, click **New Search** near the top left.

To keep things simple, we'll start by looking for the memory address which holds the number of rings we have collected. Our steps will be the following:

1. Load the ROM and start a new game.

2. Reset the memory dialog: click **Reset**.
2. Reset the memory dialog: click **New Search**.

3. Run the first filter: click **Filter Once**.

4. Return to the game and change the number of rings in memory. For example, collect a ring.

5. In the memory dialog, we now want to filter for values that are 'greater than previous values' (symbol `>`). We select this, then hit **Filter** again.
5. In the memory dialog, we now want to filter for values that are 'greater than previous values' (symbol `>`). We select this, then hit **Filter Once** again.

Each time you perform 4 then 5, the number of possibilities or 'candidates' will get smaller. We will continue doing this until the number of candidates reaches a very small number (as few as possible), it can take several attempts. Next we can click in the results window, and monitor the memory address in the memory viewer at the bottom. Continue using the game and you should see the values in memory change as you collect rings. If you don't, or if something doesn't look right, try another value. With the first Sonic the Hedgehog, the memory address for the number of rings should be `0xfe20`.

Expand All @@ -80,7 +80,7 @@ So the memory location for the number of rings is stored at `0xfe20` - note to a

Go to the **RetroAchievements** in the menu, choose **Achievement Sets** and you'll see this dialog:

![achievement_sets](/achlistdocs1.png)
![achievement_sets](/new-asset-list.png)

Here we group all the achievements we know about into three sets:

Expand All @@ -96,22 +96,23 @@ In the Achievements dialog, click **Local Achievements** (top left), then **Add

This is how it looks:

![achievement_editor](/acheditordocs1.png)
![achievement_editor](/new-ach-editor.png)

This is the final dialog where we bring all the data together. Most of the top fields are self explanatory, I.e. Title, Description, Points. The main one is the 'Requirements' part.

Let's say we want to add an achievement for collecting a certain number of rings. Let's say 15 rings for simplicity. After filling out the fields at the top and selecting a suitable icon (in this example we used the gold trophy badge (00136), but you can upload a new one), we need to fill out Requirements. We have one requirement, **that the number of rings is at least 15**. Next click is **Add New Requirement**.
Let's say we want to add an achievement for collecting a certain number of rings. Let's say 20 rings for simplicity. After filling out the fields at the top and selecting a suitable icon we need to fill out Requirements.
We have one requirement, **that the number of rings is at least 20**. Next click is **Add New Requirement**.

Clicking this button will add several default values to the Requirements list. These defaults just ensure that something relatively sensible is added to the list. For now, ignoring the field 'Special?'. When we clicked create, the default memory value will be whatever value you last left in the memory dialog.
Clicking this button will add several default values to the Requirements list. These defaults just ensure that something relatively sensible is added to the list. When we clicked create, the default memory value will be whatever value you last left in the memory dialog.

Next we will change these values to the following:

- **Size**: `16-bit` - we're using 16-bit because the value _can_ exceed 255 (which is 0xff in hex)
- **Memory**: `0xfe20`
- **Cmp**: `>=` - this is the comparison to make. We could have any comparison here, but it's sensible to have "greater than or equal to". This is important because if we get 10 rings then a super ring box to have 20, we would miss out having _exactly_ 15 rings.
- **Type**: `Value` - we're comparing this memory value to a fixed value: 15
- **Cmp**: `>=` - this is the comparison to make. We could have any comparison here, but it's sensible to have "greater than or equal to". This is important because if we get 15 rings then a super ring box to have 25, we would miss out having _exactly_ 20 rings.
- **Type**: `Value` - we're comparing this memory value to a fixed value: 20
- **Size**: `Empty` - this is only relevant when comparing memory to memory
- **Mem/Val**: `15` - the number of rings required: the value we are comparing the memory to
- **Mem/Val**: `20` - the number of rings required: the value we are comparing the memory to
- **Hit Count**: `0` - ignore this for now, its useful for when something needs to happen a certain number of times

With that set, we don't have any more conditions to add, so we return to the **Achievements Dialog**. To be safe, we should save our progress by hitting **Save Local**! This saves everything to file locally to ensure that we won't lose any progress.
Expand All @@ -122,13 +123,13 @@ With that set, we don't have any more conditions to add, so we return to the **A

We can now 'activate' this achievement locally by checking the box next to **Active** on the right-hand side in the Achievement Editor or by selecting the achievement in Achievement Sets and clicking **Activate Selected**. This will start monitoring these memory locations and will award the achievement once all the conditions are true. Now we can go ahead and test to see if this achievement works!

![testing_achievements](/achtestdocs1.png)
![testing_achievements](/new-pause-menu.png)

If we press `Esc` in-game, you will see the achievement show up as a demonstration of what it would look like in the in-game overlay!

![testing_achievements2](/achtestdocs2.png)
![testing_achievements2](/new-unlocked.png)

Although the achievement worked fine in our tests, an achievement with logic like that is not ready to be officially released. First of all, it has only one condition: "collected rings >= 15". The problem is that the memory address used to record the number of collected rings is also used when the game runs in "demo mode" (AI playing the game after you wait a while on the title screen). So, if Sonic collects 15 rings in demo mode this achievement will trigger, which is unwanted.
Although the achievement worked fine in our tests, an achievement with logic like that is not ready to be officially released. First of all, it has only one condition: "collected rings >= 20". The problem is that the memory address used to record the number of collected rings is also used when the game runs in "demo mode" (AI playing the game after you wait a while on the title screen). So, if Sonic collects 20 rings in demo mode this achievement will trigger, which is unwanted.

Check the tips in the next section below to know how to improve the logic of your achievement and then make it acceptable to be officially released.

Expand Down
47 changes: 41 additions & 6 deletions docs/developer-docs/leaderboards.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ description: Learn how to create and manage leaderboards for RetroAchievements,

This is how a game's Leaderboard List looks like on the website:

![leaderboard_list](/leaderboard-list.png)
![leaderboard_list](/new-lb-list.png)

In the center you can see every already made Leaderboard, and in the right column you can see the **Code Notes** for the game. The Code Notes are here to help with some conditions we'll see below.
Here you can see the different leaderboards that exist for a game title.
By selecting any leaderboard you can view its logic in detail.

And now you can also create and edit Leaderboards through the GUI just like achievements by selecting Leaderboards from the Assets List:
![leaderboard_edit_web](/new-lb-web-detail.png)

Here you are also able to view specific conditions or review what Code Note an address it related to.

Leaderboards can be created just like an achievement using the Assets List:

![Leaderboards GUI](/leaderboards-gui.png)

Expand Down Expand Up @@ -121,11 +126,41 @@ For more information on the value format, see [Value Definition](/developer-docs

**NOTE**: If you're using a system that doesn't run at 60Hz, you have to use "Time (Centiseconds)" and multiply by some value to do the conversion yourself (50Hz = `*2` [100/50], 60Hz = `*1.666666` [100/60], 75Hz =`*1.333333` [100/75]).

## Design Tips
## Instant Submission Leaderboards

Leaderboards can sometimes get pretty spammy and take up a lot of screen space when starting or depending on how many are active at once. It is generally good practice to try and eliminate this as much as possible when you can.
Comment thread
pilzkopf777 marked this conversation as resolved.
Outdated
You can do this by taking advantage of the fact that you can start and submit a leaderboard on the same frame by setting the start condition to what you would normally use as the submit and then setting the submit condition so something that is always true like 1=1.

For example in this leaderboard for a racing game the `Start` group is true for a single frame once a race has been completed in a specific mode:

### Start
```
1: AddAddress Mem 32-bit 0x003d03d8
Comment thread
pilzkopf777 marked this conversation as resolved.
2: Mem 32-bit 0x00000040 > Value 0 (0)
3: Mem 8-bit 0x0030f016 = Value 49 (0)
4: Mem 32-bit 0x00379f6c = Value 150 (0)
5: Mem 32-bit 0x0037a820 = Value 0 (0)
6: AddAddress Mem 32-bit 0x003d03d8
7: Mem 32-bit 0x00000034 = Value 1 (0)
8: AddAddress Mem 32-bit 0x003d03d8
9: Delta 32-bit 0x00000034 = Value 0 (0)
```

The `Cancel` group is always false. Since the leadeboard UI never shows up we do not need to cancel it either:

### Cancel
```
1: Value 0 = Value 1 (0)
```

Finally, `Submit` is always true. The moment the `Start` group is true the leaderbord will submit whatever is defined in the `Value` group:
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

Leaderboards can sometimes get pretty spammy and take up a lot of screen space when starting or depending on how many are active at once. It is generally good practice to try and eliminate this as much as possible when you can. You can do this by taking advantage of the fact that you can start and submit a leaderboard on the same frame by setting the start condition to what you would normally use as the submit and then setting the submit condition so something that is always true like 1=1.
### Submit
```
1: Value 1 = Value 1 (0)
```

If a game tracks a score or time that you are using to directly call for your value then consider starting and submitting the leaderboard on the same frame so that only one popup is called (for the submission) and the screen won't have extra clutter. There are still cases where it can be useful for players to have the values up on display if they are hidden in menus so use your best judgement.
If a game tracks a score or time that you are using to directly call for your value then consider starting and submitting the leaderboard on the same frame so that only one popup is called (for the submission) and the screen won't have extra clutter. There are still cases where it can be useful for players to have the values up on display if they are hidden in menus so use your best judgement.

In games where levels are short and retries are quick you really don't want to have it start/cancel/submit repeatedly after each start/retry when you can help it as the popups can start stacking together and clutter the screen well past the actual attempts are over. These are a prime candidate for starting and submitting on the same frame when possible.

Expand Down
6 changes: 3 additions & 3 deletions docs/developer-docs/memory-inspector.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Here is an overview of the **Memory Inspector** and its features. This tool can

## Memory Inspector Dialog

![memoryinspector-num](/memoryinspector-num.png)
![memoryinspector-num](/new-editor-overview.png)

### 1. New Search

Expand All @@ -23,7 +23,7 @@ The first drop down allows you to quick select All memory, System RAM, or Cartri

The second drop down specifies the size of the data you're interested in finding.

The `Reset` button clears out any previously captured results (part 3 of the dialog) and captures the current memory state.
The `New Search` button clears out any previously captured results (part 3 of the dialog) and captures the current memory state.

### 2. Filter

Expand Down Expand Up @@ -155,7 +155,7 @@ More facts about the different number notations:

The bottom part of the Memory Inspector is what we call the Memory Viewer:

![memviewer](/memviewer.png)
![memviewer](/new-mem-view.png)

You can see data in Memory Viewer using 8, 16 or 32-bit modes.

Expand Down
Loading
Loading