Okay, So how I made my OMGWTFBBQ stage selection screen ?

Mugi

Member
I see people really REALLY want to know, so I'll try and explain.

Before we start though, it has to be understood that this sort of code is written specifically to do a single task in an exact enviroment it was written into, as such, if you came here expecting to copy a snipped of code
that adds a stage selection screen to your game, you will be dissappointed.

before we get deeper into that, lets look at what we have here, and what we need to have, in order to pull this off.
It should also be mentioned, that there is many, many ways to achieve the same result, and I have been programming with 6502 assembly for roughly 2 weeks now, so my code is ugly, inefficient and while it works, it most definitely is not the best way, or even a good way to do this.

so, what we need to do in order to make a screen like my stage selection, or shatterhand's stage selection, or megamans stage selection screen (that's what everyone's here for, no? :p)

1) you have to gain the ability to erase your player object from this specific screen.
2) you have to be able to hide your HUD from this specific screen.
3) you have to be able to disable your inputs for the player character in this screen.
4) you have to gain control of the cursor through the controller

5) lastly, you have to gain the ability to modify your warp out location.

now, the way i pulled this off has it's limitations too.

You can only warp from stage to stage selection, or stage selection to stage (shatterhand and megaman does this.) If at any point you warp from stage to stage, and then stage selection, this code will break, because that's how i designed it.
if you need functionality like that, you're on your own to modify it for that.

now, lets start by setting up how to hide your player.

for this you need to set a variable (i call it stageSelectTrigger)
this variable has an initial value of 0

now, we need a way to trigger it, so we go to our warpTile.asm

in WarpTile.asm, after DeactivateCurrentObject, i added an extra line of code;

Code:
   DeactivateCurrentObject
    
; lets store our flag to see if we are in stage selection or not.    
    LDA stageSelectTrigger
    CMP #$00
    BEQ setStageSelectTrigger
    LDA #$00
    STA stageSelectTrigger
    JMP proceedWarping
    
setStageSelectTrigger:
    LDA #$01
    STA stageSelectTrigger    

proceedWarping:

what this does is simply checks the value of stageSelectTrigger, if it's 0 it sets it to 1, and if it's not 0, it sets it to 0.
Since like i mentioned, warping only happens between the select screen and a stage, and never between 2 stages, what this variable now does, is that it is always 1 when you are in stage selection screen.

now, i use this variable to hide my sprite hud by adding a check in predraw.asm

Code:
drawingSpriteHud:
    LDA stageSelectTrigger  ; Don't draw HUD in StageSelection.
    CMP #$00
    BEQ +
    RTS
+

but those of you who are not using a sprite hud, you can simply tick "hide HUD" from screen settings to hide it.

now, lets hide the player.
create an action state for your player that uses a blank tile as it's graphic. (i use actionstate 04 for this, so my code reflects that.)

since we now have a way to identify that we are in stage selection, we need to make the game spawn our player in it's "hidden" form into this screen.

for this we have to modify HandleScreenLoads.asm in routines/basic/system

around line 15 is the code that is commented as follows:

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;; SET SCREENS RELATIVE TO THE SCREEN TO BE LOADED

this code below the comment is what makes the player spawn, when a screen is loaded.

i added a piece of code here, that checks if stageSelectTrigger is 01, and if it is, it will spawn the player as normal, but instead of starting from action state 00, it will now spawn starting from action state 04.

Code:
	JMP whooploop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;; SET SCREENS RELATIVE TO THE SCREEN TO BE LOADED
checkStageSelectTrigger:
	LDA stageSelectTrigger
	CMP #$01
	BEQ createHiddenplayer
	JMP createplayer

createHiddenplayer:
	LDA playerToSpawn
	CreateObject newX, newY, playerToSpawn, #$04, currentNametable
	JMP proceedloadingscreen

whooploop:
	LDA newGameState
	STA gameSubState
	LDA loadObjectFlag
	BEQ +
	LDA #$00
	STA loadObjectFlag
	JMP checkStageSelectTrigger
createplayer:
	LDA playerToSpawn
	CreateObject newX, newY, playerToSpawn, #$00, currentNametable
proceedloadingscreen:
	TXA
	STA player1_object

+
	LDA newScreen
	STA currentScreen
	STA currentNametable
	STA nt_hold
	CLC
	ADC #$01
	STA rightNametable
	SEC
	SBC #$02
	STA leftNametable
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

now we have achieved hiding the player when we enter the stage selection screen.
next we have to ensure that the controller can not bring the player out of action state 04.
this is easy, you simply add the following to the beginning of all your input scripts, and extracontroller check script

Code:
 LDX player1_object
   GetCurrentActionType player1_object
    CMP #$04
    BNE +
    RTS
+

what this does is simply makes the game ignore every controller input when the player is in action state 04, thus preventing it from ever exiting the state, and staying permanently hidden.

okay so all that's done.

how does my oh-so-amazing unlimited multiwarp code works ?

here's how,

we make a new variable, called stageSelectCursor
and we make a powerup type item, with the following code in it.

Code:
    LDA #$01
    STA StageSelectCursor
    LDA #$07
    STA StageSelectCursorPos
    RTS

this sets the value of stageSelectCursor to 01 when the player picks it up. (this is used for "activating" controls on stage selection screen.)
now place the item somewhere on your stageselection screen, where the player picks it up automatically once it spawns to the screen. (remember, the player is just invisible, it didnt dissappear.)

now we have gained access to identify when we are allowed to control the screen (this step is not necessary per-se, but because my stage selection uses the scrolling entry, i had to make sure controls are disabled BEFORE the screen stops.)
The second value here; StageSelectCursorPos is not really necessary either, but i use it to ensure that whatever you input as your first button press in the stage selection, the cursor always goes to "stage1" regardless of what you press.


once we are at the screen and everything is loaded and good, and we're ready to start selecting our stage, here's how it works.

you create 3 new input scripts, movecursorleft.asm, movecursorright.asm and warptostage.asm
you assign them as you wish (my screen is controlled with left and right, and A button does the actual warping.)


be warned, these scripts are pretty convoluted as they are, so bear with me. I will only use one of the direction scripts as an example, the other one is essentially a copy of the other, with the values adjusted accordingly.

for this, we need another variable, stageSelectCursorPos

and what the code itself here does, is that it checks if we are in a proper status to control stage selection, if we are, it will set stageSelectCursorPos to 01. and changes our playerobject into a cursor on the screen.
(the blinky lights in my stage select screen.)
my code also sets another set of values, which i use to draw the animated stage load texts into the "monitors" on the wall.

so here's my moveSScursorRight.asm which is essentially the heart of my entire code.

Code:
   LDA StageSelectCursor
   CMP #$01
   BNE notInStageSelectionState_R
   
   LDA StageSelectCursorPos
   CMP #$07
   BEQ setTo01_R
   CMP #$01
   BEQ moveToStage02_R
   CMP #$02
   BEQ moveToStage03_R
   CMP #$03
   BEQ moveToStage04_R
   CMP #$04
   BEQ moveToStage05_R
   CMP #$05
   BEQ setTo01_R
   
notInStageSelectionState_R: 
   RTS
   
setTo01_R:
   LDA #$01
   STA StageSelectCursorPos
   LDA #$18
   STA temp
   LDA #$30
   STA temp1
   
   LDA #$28
   STA temp2
   LDA #$36
   STA temp3
   LDA #$15
   STA stageLabel
   JMP createCursor_R
   
moveToStage02_R: 
   INC StageSelectCursorPos
   LDA #$98
   STA temp
   LDA #$30
   STA temp1
   
   LDA #$A8
   STA temp2
   LDA #$36
   STA temp3
   LDA #$16
   STA stageLabel
   JMP createCursor_R
   
moveToStage03_R: 
   INC StageSelectCursorPos
   LDA #$58
   STA temp
   LDA #$50
   STA temp1
   
   LDA #$68
   STA temp2
   LDA #$56
   STA temp3
   LDA #$17
   STA stageLabel
   JMP createCursor_R
   
moveToStage04_R: 
   INC StageSelectCursorPos
   LDA #$18
   STA temp
   LDA #$70
   STA temp1
   
   LDA #$28
   STA temp2
   LDA #$76
   STA temp3
   LDA #$18
   STA stageLabel
   JMP createCursor_R

moveToStage05_R: 
   INC StageSelectCursorPos
   LDA #$98
   STA temp
   LDA #$70
   STA temp1
   
   LDA #$A8
   STA temp2
   LDA #$76
   STA temp3
   LDA #$19
   STA stageLabel

createCursor_R: 
   LDX player1_object
   DeactivateCurrentObject
   LDX #$03
   DeactivateCurrentObject
   CreateObject temp, temp1, #$06, #$00, currentNametable
   TXA
   STA player1_object
   CreateObject temp2, temp3, stageLabel, #$00, currentNametable

   RTS

essentially this code simply despawns and respawns the player in set coordinates depending on the value of stageSelectCursorPos variable.
so here we have it, that is how you make a cursor controllable stage select screen.

now, the part you're all here for. how does my unlimited multi-warp work ?

here's how.

warpToStage.asm (which is assigned to Press A button)

Code:
    LDA StageSelectCursor
    CMP #$01
    BEQ CheckCursorPosition
    RTS

CheckCursorPosition:
    LDA StageSelectCursorPos
    CMP #$01
    BEQ storeWarpLocation01
    CMP #$02
    BEQ storeWarpLocation02
    CMP #$03
    BEQ storeWarpLocation03
    CMP #$04
    BEQ storeWarpLocation04
    CMP #$05
    BEQ storeWarpLocation05
    RTS
    
storeWarpLocation01:
    LDA #$22
    STA temp2
    JMP doTheWarp
    
storeWarpLocation02:
    LDA #$24
    STA temp2
    JMP doTheWarp  
    
storeWarpLocation03:
    LDA #$26
    STA temp2
    JMP doTheWarp
    
storeWarpLocation04:
    LDA #$28
    STA temp2
    JMP doTheWarp
    
storeWarpLocation05:
    LDA #$2A
    STA temp2
    
doTheWarp:
    LDA #$00
    STA newGameState
    LDA warpMap
    STA currentMap
    CLC
    ADC #$01
    STA temp
    GoToScreen temp2, temp, #$02
    LDA #$00
    STA playerToSpawn
    LDX player1_object
    DeactivateCurrentObject
    
    ; let's reset stage selection screen flags so we dont get odd behavior.
    LDA #$00
    STA stageSelectTrigger
    STA StageSelectCursor
    STA StageSelectCursorPos
    
    LDA #$01
    STA loadObjectFlag
 
    LDA mapPosX
    STA newX
    LDA mapPosY
    STA newY

this code reads the variable stageSelectCursorPos, and depending on it's value, it will store another value into temp2.
it will then use this temp2 value to warp us.

GoToScreen temp2, temp, #$02

the value stored in temp2 is the number of the screen you wish to warp into.

it is also extremely important that you ALWAYS RESET THE VARIABLES HERE.

Code:
   ; let's reset stage selection screen flags so we dont get odd behavior.
    LDA #$00
    STA stageSelectTrigger
    STA StageSelectCursor
    STA StageSelectCursorPos

if you leave arbitrary numbers stored in these variables when you go to a stage, you will not have a functioning stage select screen when you return. always set all the variables back to 0.

so here you have it. maybe gimme a thumbs up once your "that blue guy with a gun arm who beats robot masters" game clone becomes a bestseller :p

enjoy stage selecting!

https://www.youtube.com/watch?v=VUnIKTP9nqA

- Mugi
 

Mugi

Member
to be honest, this only seems complicated, because it has so many steps you have to do in order to get everything to work.

the way i worked through this is that i broke it down to individual pieces that needed to be done.

what i want ?

a stage selection screen with cursor control and multi warps.
i draw the screen with no functionality.
..................................
i need to make the player dissappear
i created an action state, and dug out the code that spawns the player in each room, to make it hidden.
......................................
i need to disable inputs to make player stay in action state 04
write code to drop inputs in the input scrips.
.....................................
i need to hide hud
write a check to hide hud when in that screen
----------------------------------------
i need a cursor
write code that makes the player turn into a cursor object and have the cursor object spawn in predefined coordinates when left and right are pressed.
.....................................
i need multi-warp code
modify warp code to read the room number based on the cursor's position
................................


at each step of the flow chart, you can always run the game, and enter the room to see the functionality.
then you look at what you need to add into it to further proceed towards your end goal.

it took me several days of examining and testing to find out all the pieces of code that had to be modified and test implementations of them, but im fairly sure that if i had attempted to write it all up in one go,
it would never have gotten done.

by breaking it into pieces like that, it makes it infinitely easier to work with and see where you're going.
 

TolerantX

Active member
after you complete a stage and return how would you go about preventing the player from teleporting to the same stage again?
 

Mugi

Member
I dont. My game is designed so that you can revisit them.

If you want to prevent it though, the simple way would just be to set a variable that marks a stahe as cleared, and adding a check to that variable into the warp script to make cursor skip that stage once cleared.
 

DanielT1985

Member
This entire thing is amazing, but I just want to know if the "remove character sprite & disable controls" thing can be a tile that keeps the player in place and can't get out until an enemy action ends that teleports the player out of it in a different screen.
 

Mugi

Member
why not.
you can pretty much do whatever you want with this.
at the end of the day, it's just a primitive menu system.
 
Top Bottom