[4.5.6] Rad Rabbit's Bad Hare Day - (WIP)

kevin81

Active member
A four character code with 16 possible values per character adds up to over 65k possible combinations. I don't know how many valid codes there will be, but let's say there are 50 levels, and someone enters a different code every second, it'll take 15 minutes before the probability of a right code is bigger than a wrong code. In other words, I think 16 options per character is okay.
 

CluckFox

Active member
Your password is literally a 16-bit integer, so there are 2^16, or 65536 possible combinations. Making sure valid passwords aren't clustered together can be done with a hash function of some sort. I know at least one algorithm by heart that will work nicely in 16 bits.
 

Jonny

Well-known member
Thank you @CluckFox and @kevin81
There is only going to be about 15 stages. I could always see what graphics space I end up using of the tileset and add more if I don't use it. It would probably be less odd to have 0 to 9 and the full alphabet.

For the algorithm, would that be something that would be in the code or only to come up with some decent codes? Tbh I was just going to make them up randomly. If there's a mathematical way to it better I'd be interested in learning about that.

I suppose they're not nuke codes or anything so won't matter a great deal if someone manages to cheat accidentally on purpose.
 

CluckFox

Active member
For the algorithm, would that be something that would be in the code or only to come up with some decent codes? Tbh I was just going to make them up randomly. If there's a mathematical way to it better I'd be interested in learning about that.
It would be to generate decent codes for a lookup table. When the password is entered you would loop through each level's "decent" code to see if it matches. Incidentally I was working on this as a way to store cheat codes ala Konami Code in 2 bytes no matter how many steps the code has.


I've adapted this to 6502 and I'll share the code, I just never had a context to share it in until now.
 

Jonny

Well-known member
It would be to generate decent codes for a lookup table. When the password is entered you would loop through each level's "decent" code to see if it matches. Incidentally I was working on this as a way to store cheat codes ala Konami Code in 2 bytes no matter how many steps the code has.


I've adapted this to 6502 and I'll share the code, I just never had a context to share it in until now.

Lookup tables was the next thing I wanted to learn about.
Thank you for the link. If I can understand it, I'll give it a go.
 

Jonny

Well-known member
Have you made any progress with lookup tables? I can put my idea to actual code if you'd like.
Would like very much. That would be amazing. I just hope I can understand it because I only really know basic ASM stuff.
 

Jonny

Well-known member
@CluckFox
This is where I'm upto so far regards a working passcode system. All this is cobbled together from scripts other people have written, mainly mugi and dale. I didn't want to just post code and not mention that.
This is what I'm doing as an input (press B) to select and store the 4 variables (pass1, pass2, pass3, pass4)
Code:
    LDA codeSelectCur  
    CMP #$01            
    BNE noneSelected
    LDA newGameCur      
    CMP #$01          
    BNE noneSelected

    LDA codeCursorPos
    CMP #$01
    BEQ setPass1
    CMP #$02
    BEQ setPass2
    CMP #$03
    BEQ setPass3
    CMP #$04
    BEQ setPass4

noneSelected:
    RTS

setPass1:
    INC pass1
    BNE dontZero1
    LDA #$F1
    STA pass1
    dontZero1:
    JMP noneSelected

setPass2:
    INC pass2
    BNE dontZero2
    LDA #$F1
    STA pass2
    dontZero2:
    JMP noneSelected

setPass3:
    INC pass3
    BNE dontZero3
    LDA #$F1
    STA pass3
    dontZero3:
    JMP noneSelected

setPass4:
    INC pass4
    BNE dontZero4
    LDA #$F1
    STA pass4
    dontZero4:
    JMP noneSelected
Simple enough.

And this is where I was thinking of doing the checking agaist a table of correct codes. It's another input script which either starts a new game if cursor is in that place or does something else if start is pressed when cursor is on 'passcode'...
Code:
;;; CHECK NEWGAME OR PASSCODE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    LDA newGameCur
    CMP #$01              ;; 0-NEW GAME / 1-PASS CODE     ;;
    BNE isNewGame
    JMP isPassCheck
 
isNewGame:

    ChangeActionStep #$00,#$00
    WarpToScreen warpToMap, warpToScreen, #$01
 
    RTS

isPassCheck:

    ;;; IS PASSCODE VALID CHECKS ;;;
    ;;; THEN WARP OR ERROR SOUND ;;;
 
    RTS

I'd presume that the code to check againt a passcodes table would need to be a seperate subroutine and not entirely in this input script.
In theory, the routine would check if the code matched one in the table, then return the warp screen value which corresponds to that code. Thats how I was imagining it to work anyway.
 

CluckFox

Active member
Many thanks to @dale_coop and @Mugi for their contributions. :)

The lookup table & subroutine to read it should probably go in whatever non-static bank is loaded during input script execution. There are lots of ways of implementing such a lookup table. I'll write a short example, and once I can get it to work myself I'll post it here.
 

Jonny

Well-known member
Many thanks to @dale_coop and @Mugi for their contributions. :)

The lookup table & subroutine to read it should probably go in whatever non-static bank is loaded during input script execution. There are lots of ways of implementing such a lookup table. I'll write a short example, and once I can get it to work myself I'll post it here.
That's fantastic. No rush at all, I've got plenty more stuff to work though.

Until your kind offer, this was going to be my starting point.... http://www.nesmakers.com/index.php?...our-game-v4-0-11-intermediate-difficulty.909/

Not sure if that's helpful, I'd presume you know most of whats in that post already.
 

CluckFox

Active member
This is my idea for the table itself. Subroutine to follow.

For simplicity, it stores each passcode digit as a byte: first four bytes are the code for the first level, second four bytes are the second level, etc. Adjust as needed.

PASSCODE_SIZE is a convenience constant for the lookup subroutine, no need to adjust this.

MAX_LEVEL should be adjusted to the (number of rows - 1) in your particular lookup table.

Code:
PasscodeLookupTable:
;; level first
.db $0f, $0a, $0c, $0e
;; level second
.db $01, $03, $03, $07
;; level third
.db $01, $00, $06, $06

MAX_LEVEL=$02
PASSCODE_SIZE=$04
 

CluckFox

Active member
Here is my test script. It hard-codes a value (argX_hold are used here instead of pass1, pass2, etc) and then checks it against the table. If it matches it plays a sound, otherwise it resets. The doPasscodeLookup routine will set the Accumulator to the level that matched, starting with zero; and the carry flag will be clear. If no match is found, the Accumulator will be set to #MAX_LEVEL and the carry flag will be set.

Code:
;;; By CluckFox, for NESMaker forums
;;+ Calling Context - input script
;;; Invoked when the select button is pressed to
;;; test arbitrary code
;;- Calling Context

SwitchBank #$18
TXA
PHA
TYA
PHA
LDA tempA
PHA
LDA tempB
PHA

LDA #$01
STA arg0_hold
LDA #$03
STA arg1_hold
LDA #$03
STA arg2_hold
LDA #$07
STA arg3_hold
JSR doPasscodeLookup
BCC +success
JSR RESET

+success
PlaySound #sfx_woot
PLA
STA tempB
PLA
STA tempA
PLA
TAY
PLA
TAX

ReturnBank
RTS

I put the following code at the end of Bank $18. It doesn't have to be there, it's just where I picked for testing purposes.

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PasscodeLookupTable:
;; level 1 - FACE
.db $0f, $0a, $0c, $0e
;; level 2 - 1337
.db $01, $03, $03, $07
;; level 3 - 1066
.db $01, $00, $06, $06

MAX_LEVEL=$02
PASSCODE_SIZE=$04

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
doPasscodeLookup:
LDX #$00

-levelLoop
TXA             ;; X indexes PasscodeLookupTable
LSR
LSR
CLC
STA tempA       ;; X/4 is the level whose passcode is being currently
                ;; evaluated, since each row in the table is 4 bytes
JSR doComparePasscode
                ;; doComparePasscode always leaves X as a value divisible
                ;; by 4
BCS +tryNextLevel
CLC
LDA tempA
RTS             ;; if the passcode matched, load the current row number
                ;; into A and return with carry cleared

+tryNextLevel
LDA tempA       
CMP #MAX_LEVEL
BCC +keepLooking
JMP +fail       ;; if doComparePasscode failed on the last row, end the loop
                ;; otherwise keep going
+keepLooking
JMP -levelLoop

+fail
SEC             ;; if no passcodes matched, return with carry set
RTS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
doComparePasscode:
STX tempB
LDA #$04
CLC
ADC tempB
STA tempB                   ;; tempB will be the next multiple of four to load into
                            ;; X if there is no match. This way the calling loop will
                            ;; only ever "see" values of X that correspond
                            ;; to the start of a row
LDA PasscodeLookupTable,x
CMP pass1
BEQ +pass
JMP +fail

+pass
INX                         ;; pass1 matched, increment X to the next digit
                            ;; in the table
LDA PasscodeLookupTable,x
CMP pass2
BEQ +pass
JMP +fail

+pass
INX                         ;; pass2 matched, increment X to the next digit
LDA PasscodeLookupTable,x
CMP pass3
BEQ +pass
JMP +fail

+pass
INX                         ;; pass3 matched, increment X to the next digit
LDA PasscodeLookupTable,x
CMP pass4
BEQ +success                ;; pass4 matched, a complete password match

+fail
LDX tempB
SEC
RTS                         ;; if no match, return with carry set

+success
LDX tempB
CLC
RTS                         ;; if successful match, return with carry clear
 

Jonny

Well-known member
This is amazing! Thanks a million. I appreciate the commenting too, that helps a lot.
Looking forward to trying it all out. I'll not be able to do any gamedev stuff for a couple of days now as I'll be entertaining my 2 year old, but I'll let you know how I get on as soon as a do.

@CluckFox
Turns out I got some time to try this out after all.

Your code works prefectly. I understand whats going on in Bank 18 (most of it) but not too sure about the input script and the argX_hold stuff. Does tempA end up being the level number I would use for Warping code? Not being very good at code, I'll need to study this for a while to figure it out. There's no way I could have done this with my current knowledge of ASM so I really really appreciate your help a lot. If I can do you a tileset or anything graphics wise, let me know.

I ran some tests, using tempA to create an object on screen when a correct code is entered. It seems to return the level number but I'm not totally sure I'm understanding it all correctly.

codething.gif
 
Last edited:

CluckFox

Active member
I ran some tests, using tempA to create an object on screen when a correct code is entered. It seems to return the level number but I'm not totally sure I'm understanding it all correctly.
Sorry for not getting back to you for so long! I didn't notice your reply until dale_coop nudged me. :)

The arg0_hold .. arg3_hold variables are for passing the four passcode digits to the doPasscodeLookup subroutine.

After the subroutine, if the passcode was a match, the carry will be clear (thus BCC +success in the test script), and the level that was matched is set in the X register, so you would access it like this.

Code:
...
...
JSR doPasscodeLookup
BCC +success
JMP RESET

+success
TXA
STA someVariable
...
...

After you have the level number in a variable you'll need to use that variable to figue which screen ID to set warpToScreen to.
 

Bucket Mouse

Active member
@Jonny can you please tell me how you made those graphics, and how they work with an actual warp? Because I just can't seem to make mine work.

My password screen uses two Game Objects to point to whatever part of the password the user can currently change. The position of these objects changes depending on what the player has selected. And right before the game warps, the script is programmed to destroy both these objects (otherwise they stay there on the next screen):

Code:
LDX #$00
DestroyObject
LDX #$01
DestroyObject
WarpToScreen warpToMap, tempB, #$01 ; using a variable to reduce redundant macro code

Problem is, the game warps and THEN it executes the Destroy function, EVEN THOUGH the Warp is clearly BELOW it. So it eliminates the Player Sprite and makes it impossible to continue.

Then I got the brilliant idea to have the script instead change the action step of the objects to a step with "Destroy Me" selected.

Code:
TAX
LDA stageTable,x ; use lookup tables, they're so much quicker
STA tempB
ChangeActionStep #$00, #$06
ChangeActionStep #$01, #$06
WarpToScreen warpToMap, tempB, #$01 ; using a variable to reduce redundant macro code

That works a little better, but strangely, it only eliminates ONE of the objects on the next screen. It also waits until the new screen is loaded for one of the objects to disappear (the other one hangs around like I didn't just tell it to leave).

The main problem is that it's warping before stuff that it shouldn't. How do I get these objects to go away properly? Did you do anything differently, and if so, how does it work?
 

Jonny

Well-known member
@Jonny can you please tell me how you made those graphics, and how they work with an actual warp? Because I just can't seem to make mine work.
Of course mate. Well, I can try...

Regarding the warp, I've only implemented the 'NEW GAME' warp. I haven't put in all the password warping work CluckFox did yet.
For that warp, I'm loading the player1_object into X (which at that time is the cursor), destroying it, then creating a new object before the warp.
Code:
LDX player1_object
    DestroyObject
    CreateObject temp, temp1, #$00, #$00
    TXA
    STA player1_object

    WarpToScreen warpToMap, warpToScreen, #$01
   
    RTS

For the 'NEW GAME' / 'PASSCODE' graphics the code is quite crude, maybe cringe-worthy code but it works for what I want. I'm just going to post my doSpritePreDraw.asm and maybe you can pick it apart and improve it or make it work with your project...
Code:
;;; START SCREEN MENU ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    LDA gameState         ;; CHECK ON A PASSCODE SCREEN   ;;
    CMP #$02              ;; PASSCODE SCREEN IS $02       ;;
    BEQ BoSelector
    JMP StartScreenSelector

BoSelector:
    LDA #SELECTION_POS_Y  ;; Y POSITION WHEN 0 SELECTED   ;;
    STA temp1
    LDA #SELECTION_POS_X  ;; X POSITION OF ALL SELECTIONS ;;
    STA temp2              

    LDX #$00
   
startScreenSelectorLoop:
   
    CPX newGameCur
    BCC dothis
    JMP donecur
   
dothis:                  
    LDA temp1             ;; LOAD Y POSITION              ;;  
    CLC
    ADC #SELECTION_STEP_Y ;; ADD Y STEP TO POSITION       ;;
    STA temp1

;;; NEW GAME ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   
    LDA temp2
    ADC #$1D
    STA temp2
    LDA temp1
    SBC #$0B
    STA temp1
    DrawSprite temp2, temp1, #$E0, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$E1, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$E2, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$E3, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$E4, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$E5, #%00000011, #$00
   
    LDA temp2
    SBC #$44
    STA temp2
    LDA temp1
    ADC #$0B
    STA temp1
   
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   
    INX
    JMP startScreenSelectorLoop
   
donecur:

;;; DRAW FOUR CORNERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    DrawSprite temp2, temp1, #$42, #%00000010, #$00

    LDA temp2
    ADC #$62
    STA temp2
    DrawSprite temp2, temp1, #$42, #%01000010, #$00
   
    LDA temp1
    ADC #$09
    STA temp1
    DrawSprite temp2, temp1, #$42, #%11000010, #$00
   
    LDA temp2
    SBC #$61
    STA temp2
    DrawSprite temp2, temp1, #$42, #%10000010, #$00
   
;;; PASSCODE MASK ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  
   
    LDA newGameCur
    CMP #$00
    BEQ domasking
   
    JMP StartScreenSelector:
   
domasking:      
   
    LDA temp1
    ADC #$0B
    STA temp1
    LDA temp2
    ADC #$15
    STA temp2
    DrawSprite temp2, temp1, #$E6, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$E7, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$E8, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$E9, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$EA, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$EB, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$EC, #%00000011, #$00
   
    LDA temp2
    ADC #$08
    STA temp2
    DrawSprite temp2, temp1, #$ED, #%00000011, #$00

StartScreenSelector:
It's basically just loads of DrawSprite macros for the corners and masking over the background tiles.

Monster Tiles...
Monster_1_06.png
Background Tiles (SS)...
BckSSChr28.png

I hope this is the info you need. If not, let me know.
 

Jonny

Well-known member
I've been doing some work on the tilesets I'll be using for the outside stages. These will be short 'travel' stages between the bigger levels.
They're finally starting to take shape. I've tried a few different ideas but ended up going back to some tiles I made when I first started with NesMaker.
View: https://youtu.be/lKXgcGoomGM

I don't know why but I don't really like doing outside graphics. It's kinda essental to the game and its story though. This is still WIP but I'd be interested to know if you like it or not or prefer my first attempt months ago (first post on this thread).
 
Last edited:
Top Bottom