Skip to content

Remember

VERSION NOTICE

This feature will be available at the RA_Integration version 1.4 milestone for developers, with support to be added for rcheevos version 11.4 milestone.

The Remember condition is chosen in the Flag column of the Asset Editor. Since many of the use cases tend to be fairly complex, it is recommended you be familiar with the rest of the tool kit before studying some of the more complicated examples below.

When a condition has the Remember flag, the value of the condition is stored in the recall accumulator. This value may be used in later logic by using the Recall operand found in the Type column of either side.

There is one remembered value per group (Core, Alt1, Alt2, etc). This value is initialized at the beginning of an evaluation as a unsigned integer with value 0. If you Remember a new value, it will overwrite the previously remembered value. If you remember a Float value, the remembered value will be a floating point value instead of an unsigned integer. Using Recall before a value is remembered using the Remember condition will generate a warning in the asset editor.

The Remember condition is a combining condition and does not break any chain it is in. Conditions that follow it are considered part of the chain.

Basic Usage

Simple Example

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1SubSourceDelta16-bit0x1234
2RememberMem16-bit0x1234

In this example, the Delta value of 0x1234, the value from the previous frame, is subtracted from the Mem value of 0x1234 and remembered.

INFO

Remember sets the value of the recall accumulator.

Simple Example - With Operators

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem16-bit0x1234-Delta16-Bit0x1234

The Remember condition can optionally take two operands and process a value based on an operator such as multiplication, division, addition, subtraction, and others. In this example, we used the subtraction operator to store the equivalent value as in the first example, but we do it in a single line.

Using the Stored Value

To use the stored value, specify the Recall operator as the Type of operand:

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
2Recall=Value0x05

In this example it uses the stored value and compares it to a constant value of 5. However, using the stored value once does not show off the main usefulness of Remember.

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem16-bit0x1234-Delta16-Bit0x1224
2And NextRecall=Value0x05
3Add HitsMem8-Bit0x1000=Value0x031 (0)
4And NextRecall=Value0x14
5Add HitsMem8-Bit0x1000=Value0x031 (0)
6And NextRecall=Value0x64
7Add HitsMem8-Bit0x1000=Value0x031 (0)
8MeasuredValue0x00=Value0x003 (0)

In this example, we are using the stored value in multiple chains within an Add Hits chain without having to recalculate the value each time. Here we are looking for specific increases of 0x1234 to occur while the value of 0x1000 is equal to 3. Perhaps 0x1234 is money, 0x1000 is the current level, and you want to make sure the player collects a gem worth 5 units of money, one worth 20, and one worth 100 in level 3. Recall is used here to check all these values of the difference between Mem and Delta without having to recalculate it each time. See the Add Hits document for more information about how that condition works.

INFO

Recall retrieves the value stored in the recall accumulator.

Interaction with Pause If

Because Pause If logic is processed before other logic, Pause If logic can only recall values that have been remembered by pause logic. Values remembered during Pause If logic continue to be remembered during the remaining logic processing.

See the Pause If document for more information about how that condition works.

Example of Use with Pause If

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem16-bit0x1234-Delta16-Bit0x1234
2Pause IfRecall>=Value0x800000001 (0)
3And NextRecall=Value0x05
4Add HitsMem8-Bit0x1000=Value0x031 (0)
5And NextRecall=Value0x14
6Add HitsMem8-Bit0x1000=Value0x031 (0)
7And NextRecall=Value0x64
8Add HitsMem8-Bit0x1000=Value0x031 (0)
9MeasuredValue0x00=Value0x003 (0)

This example extends the previous Using the Stored Value example by adding a Pause If lock if the player loses money*. Because Remember is a combining condition, it links with the following Pause If to form a chain and is processed as part of the pause processing, allowing the value to be used by the Pause If. The remembered value is available to the remaining logic and can therefore be used by the Add Hits chain.

Notes:

  • * Due to the way unsigned math works, the subtraction will underflow back to 0xfffffff at negative one, so any value greater or equal to 0x80000000 is treated as negative).
  • Because pause processing happens first, you could put conditions 1-2 at the end of the logic and it will still work! The value remembered by the pause logic will still be available to the Add Hits chain. However because this may be a confusing effect, we recommend putting your pause logic at the beginning when using it with Remember and Recall.

TIP

To avoid unexpected behavior with Remember and Recall, place pause logic that uses these features at the top of the group.

Example of Incorrect Usage with Pause If

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem16-bit0x1234-Delta16-Bit0x1234
2And NextRecall=Value0x05
3Add HitsMem8-Bit0x1000=Value0x031 (0)
4And NextRecall=Value0x14
5Add HitsMem8-Bit0x1000=Value0x031 (0)
6And NextRecall=Value0x64
7Add HitsMem8-Bit0x1000=Value0x031 (0)
8MeasuredValue0x00=Value0x003 (0)
9Pause IfRecall>=Value0x80000001 (0)

In this example, the Remember condition is not chained with the Pause If. Because of this, the Pause If condition will read a value of 0 from the Recall operand and never be paused, as a consequence of the pause logic being processed first. Fear not though, as the validation is smart enough to recognize this and will generate a warning on condition 9 that Recall is being used in a Pause If without a value being remembered in a Pause If.

DANGER

When using the Recall type operator in Pause If logic, be sure that the Remember condition that sets the value you wish to use is in the same Pause If chain or an earlier Pause If chain. Otherwise the recalled value with be 0!

Using Recall within a Remember Condition

You can perform math on a remembered value and remember the result:

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem16-bit0x1002+Mem16-Bit0x1004
2RememberRecall*Value0x03
3RememberRecall+Mem16-Bit0x1000
4RememberRecall%Value0x05
5Recall=Value0x00

In this example, let's say you need to calculate the value of 0x1000 + (0x1002 + 0x1004) * 3 and check to see if that is a multiple of 5. You can use Recall in a Remember condition to operate on the last remembered value and store the new result.

Notes:

  • If the value of the modulus (%) operation is 0, that means there is no remainder from the division by 5, meaning the value is a multiple of 5.

Using Remember for Calculated Pointer Offsets

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem8-bit0x0789*Value0x60
2AddAddressMem32-bit0x1234+Recall
3Mem16-bit0x08>Value0x00

In this example, let's say you have an address that points to a table of data, but in order to know where in the table you want to go, you have to calculate the offset from another source. 0x1234 will be used as the pointer to the table of data. 0x0789 will be the index in the table. The size of data for table entries is 0x60 bytes. And finally, the data we care about is 0x08 bytes into the data's structure. Perhaps this is "Player Health" and the index we are calculating is the index in the table where the "Player Data" exists and we need to check if this value is greater than 0.

See the Add Address document for more information about how that condition works.

Breakdown of Conditions:

  1. Scales the index by the size of the data structure. Now it represents how far from the beginning of the table the data is located.
  2. Uses the address for the start of the table and adds to it the stored value. Now this Add Address points to the start of the "Player Data" in the table.
  3. Offsets from the Address by 0x08, where it reads the current memory value for the Player's Health with 16-bit size, then uses a comparison to check if it is greater than zero.

Deeper Chaining of Remember Conditions

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem8-bit0x0789*Value0x60
2AddAddressMem32-bit0x1234+Recall
3And NextMem16-bit0x08<Delta0x080 (0)
4AddAddressMem32-bit0x1234+Recall
5And NextMem8-bit0x24!=Value0xff0 (0)
6AddAddressMem32-bit0x1234+Recall
7RememberMem16-bit0x24*Value0x60
8AddAddressMem32-bit0x1234+Recall
9Reset IfMem8-bit0x00=Value0xa70 (0)

This example shows how using Remember continues the chain of And Next conditions and how you can remember new values during the chain without affecting earlier parts of the chain or breaking the chain.

In this example, we build on the previous Add Address example by chaining it with other conditions. Pretend this is a challenge that fails if the player loses health while holding a certain object. Suppose the player data stores the index of the object at offset 0x24 (the value of which is 0xff if no object is held). Suppose that index is an index into the same table that holds the player data. This example shows the reset chain that accomplishes this. Suppose that offset 0x00 of object data holds the 8-bit ID of the object.

Breakdown of Conditions:

  1. Scales the index by the size of the data structure. Now it represents how far from the beginning of the table the data is located.
  2. Uses the address for the start of the table and adds to it the stored value. Now this Add Address points to the start of the "Player Data" in the table.
  3. Checks if the player lost health, using the offset for health established in the previous example.
  4. Re-uses the address for the start of the player data
  5. Checks the offset of the held object and makes sure it's not 0xff.
  6. Re-uses the address for the start of the player data
  7. Stores the index of the held object times the size of the object data and now the stored value is the location in the table where the object's data is located.
  8. Uses the object table pointer + location in table of object
  9. Reads the ID of the object and resets if holding the specific object while the other two core conditions in the chain are true (Player lost health and an object is held.

Other Considerations

Interaction With Floats

A remembered value will match the type of data being remembered. If operations are being performed, the first operand usually determines the type, except in the case of division (where the second operand determines the type). If you need to force an integer value to be stored as a float, you can do either of the following:

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberFloat0.0+Mem16-Bit0x1234
IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem16-Bit0x1234/Float1.0

Integer Overflow

When the stored value is an integer, all logic being performed is limited to 32-bits. If the total value exceeds 0xffffffff, there will be an overflow and only the 32 least significant bits will be used in the comparison.

IDFlagTypeSizeMemoryCmpTypeSizeMem/ValHits
1RememberMem32-bit0x8010+Mem32-Bit0x8020
2Recall>Value0x300000000 (0)

If 0x8010 is 0xc0000000 and 0x8020 is 0x54321abc, then the total will be 0x114321abc, which is more than 32-bits and will be truncated to 0x14321abc, which is not more then 0x30000000, so the logic will evaluate false.

Released under the GPL-3 License. There are no copyright-protected ROMs available for download on RetroAchievements.