RATools icon indicating copy to clipboard operation
RATools copied to clipboard

(Can't process bitwise XOR with bit values, BUT ACTUALLY PROBABLY) Falsely tries to convert equalization of two values at end of AddSource chain into always_true() / always_false()

Open UnliquidSnake opened this issue 1 year ago • 3 comments

The game is Mega Man 2: https://retroachievements.org/game/5103

The achievement, as coded in the standard toolkit and working properly, is this:

0xH05c6b2>=1_0xH05c6b4=1_O:0xH055b5c=0_0xH055b5c=1_N:0xH0dac24=6_N:0xH05ce10=0_N:d0xH0dde04!=1_N:0xH0dde04=1_A:0xQ05cb10^1_A:0xQ05cb11^1_A:0xQ05cb12^1_0=0.1._A:d0xQ05cb10_A:d0xQ05cb11_A:d0xQ05cb12_0=2_A:0xQ05cb10^1_A:0xQ05cb11^1_A:0xQ05cb12^1_M:0=3_R:0xH2002e0<d0xH2002e0_R:0xH2002e0=0_R:0xH05ce10=21

The main thing here that can't be translated into RATools is the lines with the bitwise XORs. These bits go from 1 to 0 when a specific item for each bit is collected, and I have to keep count of them, including on the previous frame. The toolkit doesn't allow to use delta and invert at the same time, so the XOR's with 1 are used as a substitute.

The RATools implementation of it I'm trying to use looks like this:

function Logic_Ach_StageChallenge_Collection(StageID) => // Lines 1-2, settings check. WORKS Mem_GameDifficulty() >= Diff_Normal && Mem_GameSpeed() == Speed_Normal &&
// Lines 3-4, mode check. WORKS __ornext(Mem_CurrentMode() == Mode_Original || Mem_CurrentMode() == Mode_Navi) && // Lines 5-8, checkpoint for starting level. WORKS once(Proc_StageStarted(StageID) && // Lines 9-12, check that no items have been collected at start of level. BREAKS ((Mem_ItemFlag(0x00) ^ 1) + (Mem_ItemFlag(0x01) ^ 1) + (Mem_ItemFlag(0x02) ^ 1)) == 0) && // Lines 13-16, check that on previous frame, all items except 1 have been collected. BREAKS prev((Mem_ItemFlag(0x00) ^ 1) + (Mem_ItemFlag(0x01) ^ 1) + (Mem_ItemFlag(0x02) ^ 1)) == 2 && // Lines 17-20, check that all items have been collected on current frame. BREAKS (Mem_ItemFlag(0x00) ^ 1) + (Mem_ItemFlag(0x01) ^ 1) + (Mem_ItemFlag(0x02) ^ 1) == 3 && // Lines 21-23, reset if dead or reached the boss. WORKS never(Proc_PlayerDead()) && never(Proc_GameOver()) && never(Mem_ScreenPage() == BossScreens[StageID])
// Mem_ItemFlag() is a memory accessor for the bit value at the offset defined by the argument

If I try to make an achievement out of it in RATools, it converts the first 8 lines successfully, writes the 9th line with no comparison and nothing on the right side, and then stops. Same thing happens if I make a raw script out of it. I'm 99% sure that the XOR with bit value is what's breaking it. Please fix this accordingly. Feel free to ask any further questions if clarification is required. Thanks.

UnliquidSnake avatar Oct 09 '24 10:10 UnliquidSnake

I'm going to need more information to reproduce. I can define simple clauses representing the logic you claim is failing and they go through fine:

        [TestCase("(bit1(0x001234) ^ 1) + (bit1(0x001235) ^ 1) == 2", "A:0xN001234^1_A:0xN001235^1_0=2")]
        [TestCase("prev((bit1(0x001234) ^ 1) + (bit1(0x001235) ^ 1)) == 2", "A:d0xN001234^1_A:d0xN001235^1_0=2")]

I can't find any references to ^ in the achievements for the provided game.

And I'm not going to try to recreate the above logic without all the helper functions.

Jamiras avatar Oct 11 '24 14:10 Jamiras

These are new achievements I'm making for a revision, and... let me test this a bit more...

OH, SHIT. FUCK. Okay, I figured it out. So, ignore the RATools logic for now, that was a dumb-dumb on my part. I fixed it now.

I showed the raw code for an item collection achievement on Metal Man's stage. When you're making a new achievement yet and it is not in Local, it converts completely fine and works as intended. BUT, when it's already in Local and you're trying to work with it a bit more, it tries to convert the total item counts (in this case on lines 12, 16 & 20) into always_true() / always_false() statements, and that's what makes it break. While the raw lines out of context are written like "0=0", "0=3", and so on, they're at the end of an AddSource chain, and they need to be like this for it to count properly because I'm XOR'ing all of them, but the interpreter appears to ignore that. Please fix this.

To test, here is the RATools logic without all the helpers:

function TEST() => byte(0x05C6B2) == 0x01 && byte(0x05C6B4) == 0x01 && __ornext(byte(0x055B5C) == 0x00 || byte(0x055B5C) == 0x01) && once(byte(0x0DAC24) == 0x06 && byte(0x05CE10) == 0x00 && prev(byte(0x0DDE04) != 0x01) && byte(0x0DDE04) == 0x01 && (bit4(0x05CB10) ^ 1) + (bit4(0x05CB11) ^ 1) + (bit4(0x05CB12) ^ 1) == 0 ) && prev((bit4(0x05CB10) ^ 1) + (bit4(0x05CB11) ^ 1) + (bit4(0x05CB12) ^ 1)) == 0x02 && measured((bit4(0x05CB10) ^ 1) + (bit4(0x05CB11) ^ 1) + (bit4(0x05CB12) ^ 1) == 0x03) && never(byte(0x2002E0) < prev(byte(0x2002E0))) && never(byte(0x0DDCC8) == 0x01) && never(byte(0x05CE10) == 0x15)

UnliquidSnake avatar Oct 11 '24 15:10 UnliquidSnake

it tries to convert the total item counts (in this case on lines 12, 16 & 20) into always_true() / always_false() statements, and that's what makes it break. While the raw lines out of context are written like "0=0", "0=3", and so on, they're at the end of an AddSource chain, and they need to be like this for it to count properly because I'm XOR'ing all of them, but the interpreter appears to ignore that.

Are you on 1.15? I think that was fixed by #513.

When I paste your code into 1.15, I see this: image

It's clearly not showing the ^, but the 0=3 seems to be correct.

Jamiras avatar Nov 14 '24 23:11 Jamiras