cheating

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
cheating
by on (#28176)
i had a friend of mine beta testing my Pitfall! clone and his skills are terrible. i decided to put a cheat code in to help him out and this i what i came up with:

Code:
   ldx cheatIndex         ; if button_index >= cheat_length
   cpx #08
   bcs _cheat_Activate      ;     branch to set cheat == ACTIVE
   lda P1Joypad         ; if cur_pressed == cur_button
   cmp cheatCodeData, X
   beq _cheat_gotoNextButton   ;     branch to check next button on next loop

_cheat_checkPrevButton:   dex      ; else button didn't match
   cpx #$FF
   beq _cheat_resetIndex
   cmp cheatCodeData, X      ;     so check if cur_pressed == prev_button
   beq _cheat_HangOnPrevButton   ;         branch to wait for another frame

_cheat_resetIndex:   ldx #00      ;     else reset button index
   stx cheatIndex
   jmp _cheat_ExitCheck

_cheat_gotoNextButton:   inx      ; button matched so move on to next
   stx cheatIndex
   jmp _cheat_ExitCheck

_cheat_Activate:   lda #01      ; all 8 buttons matched so set cheat==true
   sta cheatActive

_cheat_HangOnPrevButton:
_cheat_ExitCheck:


i think it works nicely. has anyone else ever implemented a cheat system in their projects? i'd like to know how mine stacks up.

edit: should proabably describe it

so each frame of the title screen it checks the currently pressed buttons against a sequence of button presses.

1. if the sequence index(cheatIndex) is greater then the sequence size (8), the cheat is enabled.
2. else if the current pressed button matches the next button in sequence, the sequence index is incremented and then wait for another loop
3. if #2 fails, then the current pressed button is compared to the previous button in sequence. if it matches the, the code hangs for another loop.
4. else it resets the sequence index and we repeat.

if the whole process takes too long, then the cheat is no longer available

by on (#28200)
I really liked your idea, but I feel that the implementation could be better. Here's my shot at it:
Code:
   lda ButtonsJustPressed
   beq NoChange   ;If nothing was pressed, there is nothing to do

   ;Compare against the first cheat sequence
   ldx Cheat1Index   ;Index of the value to match
   cmp Cheat1, x   ;Compare with the current buttons
   bne Cheat1Reset   ;If they are different, go reset the sequence
   dec Cheat1Index ;Move closer to the end of the sequence
   bpl Cheat1End   ;Skip if we're not at the end of the sequence
   ;->ENABLE CHEAT HERE<-
Cheat1Reset:
   ldx #Cheat1StartIndex   ;Go back to the start of the sequence
   stx Cheat1Index
Cheat1End:

   ;Compare against the second cheat sequence
   ldx Cheat2Index
   cmp Cheat2, x
   bne Cheat2Reset
   dec Cheat2Index
   bpl Cheat2End
   ;->ENABLE CHEAT HERE<-
Chaet2Reset:
   ldx #Cheat2StartIndex
   stx Cheat2Index
Cheat2End:

NoChange:

First of all, the byte that holds the state of the buttons must only indicate buttons that were just pressed (the NewButtons XOR OldButtons AND NewButtons trick), and not the current snapshot of the controller. This saves you from having to compare against the previous index. Most games keep track of both types of keypresses, for things such as pausing (you only need to know if "start" was just pressed in order to pause, and you are not interested if was kept down for a few frames - in fact, that would unpause the game in the very next frame, and that'd be nasty).

Second, I'm scanning the cheat sequence backwards, because it's easier to detect if the index wrapped from 0 to 255, and that is the condition that indicates that the sequence was completed.

Also, in the code above, I'm supporting 2 different cheats, each with it's own button sequence, but more could be added without problems. I just commented the first one, because the second uses the exact same logic.

Note that I haven't tested this, this is just an idea, so there are probably bugs. One that I just thought of, which may not be a bug, is that it is possible to perform the whole button sequence while keeping pressed a button that does not belong to the sequence. To fix that you could probably load A with the current snapshot of the controller right after the "beq NoChange" command. That would make the code only run when something was pressed, but the whole controller must be as specified in the cheat sequence table.

by on (#28209)
your implementation is much more elegant. :)

tokumaru wrote:
First of all, the byte that holds the state of the buttons must only indicate buttons that were just pressed (the NewButtons XOR OldButtons AND NewButtons trick), and not the current snapshot of the controller. This saves you from having to compare against the previous index. Most games keep track of both types of keypresses, for things such as pausing (you only need to know if "start" was just pressed in order to pause, and you are not interested if was kept down for a few frames - in fact, that would unpause the game in the very next frame, and that'd be nasty).


the P1Joypad is a hangover from checking for START to skip the scrolling title screen (a la smb3's curtain intro) irregardless if it was newly pressed or held. though i did have that "start" problem earlier on in development with the menu system.

this game has some nasty code. most of it was from the early stages of development when i had little experience in a project this large. then i just got too lazy to fix what wasn't broke. i was able to salvage and clean up a lot of code for use in 2 other projects i started.

by on (#28213)
never-obsolete wrote:
your implementation is much more elegant. :)

I had never thought of implementing cheats the way you described. I liked the idea so much that I had to try! =)

Quote:
the P1Joypad is a hangover from checking for START to skip the scrolling title screen (a la smb3's curtain intro) irregardless if it was newly pressed or held. though i did have that "start" problem earlier on in development with the menu system.

Yeah, there are many places in a game, mostly ouside of the main game engine, where all you are interested in are the buttons that were just pressed, or else cursors would fly through the options because a person usually keeps the buttons pressed for longer than a frame. This is the trick I talked about:
Code:
   lda NewButtons
   eor OldButtons
   and NewButtons

This will result in a byte with 1's indicating buttons that were just pressed, and whatever was already pressed in the previous frame is ignored. It is very useful to keep track of both types of keypresses.

Quote:
this game has some nasty code. most of it was from the early stages of development when i had little experience in a project this large.

I know how it works! =) In fact, I always find myself rewriting code, and that must be one of the reasons I never finish anything!

Quote:
then i just got too lazy to fix what wasn't broke.

Probably a very wise decision!

by on (#28219)
Quote:
I had never thought of implementing cheats the way you described.


what ways have you tought of? this was the only way i could come up with for a Konomi code type cheat.

Quote:
Code:
 lda NewButtons
 eor OldButtons
 and NewButtons



i do something similar:

Code:
 lda JoypadOld
 eor #$FF
 and JoypadCurrent

by on (#28283)
never-obsolete wrote:
what ways have you tought of? this was the only way i could come up with for a Konomi code type cheat.

You don't want to know. =) The only time I ever implemented cheats like these was back when I made QBasic games, and wasn't able to program anything else. I used to keep the codes of the last N pressed keys in a string (N being the largest a cheat code could be), then I'd check the end of that string against strings containing the cheats. This worked fine in QBasic, but in assembly your idea has to be the best.