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? )
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;
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
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:
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.
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
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.
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.
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)
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.
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
enjoy stage selecting!
https://www.youtube.com/watch?v=VUnIKTP9nqA
- Mugi
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? )
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
enjoy stage selecting!
https://www.youtube.com/watch?v=VUnIKTP9nqA
- Mugi