2020-07-29 13:24:00 -05:00
|
|
|
/// SUPER BREAD PAN
|
2020-07-20 04:01:38 -05:00
|
|
|
// SUPERPANSKATOL'
|
|
|
|
// Based on Garydos'es Pong example
|
|
|
|
|
|
|
|
import nes_joy
|
|
|
|
|
|
|
|
segment(chrrom) const array graphics @ $0000 = file("tileset.chr")
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// STRUCTS
|
|
|
|
// ============================================================================
|
|
|
|
// sprite layout in oam
|
|
|
|
struct Sprite {
|
|
|
|
byte y,
|
|
|
|
byte tile,
|
|
|
|
byte attrs,
|
|
|
|
byte x
|
|
|
|
}
|
|
|
|
|
|
|
|
// abstraction for 16x16 animated monstrosities
|
|
|
|
struct Entity {
|
|
|
|
Sprite top0, // left side, for the record
|
|
|
|
Sprite bottom0,
|
|
|
|
Sprite top1, // right side, please kill me
|
|
|
|
Sprite bottom1,
|
|
|
|
|
2020-07-21 03:28:20 -05:00
|
|
|
// all this can be crammed into a byte (if merge dircount w frame) `o`
|
|
|
|
// note to self: do that
|
2020-07-20 04:01:38 -05:00
|
|
|
byte movement, // 0-3; idle, walk, jump/fall
|
2020-07-21 03:28:20 -05:00
|
|
|
byte jump, // 0/1
|
|
|
|
byte direction, // 0/1
|
|
|
|
byte dircount, // counter for time-limited movement; i.e., jumping
|
2020-07-29 13:24:00 -05:00
|
|
|
byte frame, // internal count used for animation; max 5
|
|
|
|
byte walkspeed
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
|
|
|
|
2020-10-09 19:14:51 -05:00
|
|
|
|
2020-07-20 04:01:38 -05:00
|
|
|
// ============================================================================
|
|
|
|
// VARIABLES
|
|
|
|
// ============================================================================
|
|
|
|
byte i
|
|
|
|
byte score1
|
|
|
|
array oam_buffer [256] @$200 // sprite buffer
|
|
|
|
word framecounter
|
|
|
|
Entity sam @$204 // player character
|
2020-07-29 13:24:00 -05:00
|
|
|
Entity baby @$250 // baby character
|
2020-07-20 04:01:38 -05:00
|
|
|
volatile Gamestate gamestate // the current Gamestate
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// CONSTANTS
|
|
|
|
// ============================================================================
|
|
|
|
const array pallete = [
|
2020-10-08 22:58:25 -05:00
|
|
|
$22,$29,$1A,$0F,
|
|
|
|
// construction-blocks
|
|
|
|
$00,$06,$07,$16,
|
|
|
|
// construction-bricks
|
|
|
|
$16,$06,$07,$00,
|
|
|
|
$22,$27,$17,$0F,
|
2020-07-29 13:24:00 -05:00
|
|
|
$22,$1C,$15,$14,
|
|
|
|
$0F,$18,$28,$0F, // sam
|
|
|
|
$0F,$28,$29,$0F, // enemies
|
|
|
|
$03,$07,$05,$03 // angry owo
|
2020-07-20 04:01:38 -05:00
|
|
|
]
|
|
|
|
|
2020-10-08 22:58:25 -05:00
|
|
|
// palette selections for the 16x16 ppu tiles
|
|
|
|
// %DownRight DownLeft TopRight TopLeft
|
2020-07-20 04:01:38 -05:00
|
|
|
const array attribute = [
|
2020-10-08 22:58:25 -05:00
|
|
|
%01010101, %00000101, %00000101, %00000101, %00000101, %00000101, %00000101, %00000101,
|
2020-07-20 04:01:38 -05:00
|
|
|
%00000101, %00000101, %00000101, %00000101, %00000101, %00000101, %00000101, %00000101,
|
|
|
|
%00000101, %00000101, %00000101, %00000101, %00000101, %00000101, %00000101, %00000101
|
|
|
|
]
|
|
|
|
|
|
|
|
enum Gamestate {
|
|
|
|
STATETITLE,
|
|
|
|
STATEPLAYING,
|
|
|
|
STATEGAMEOVER
|
|
|
|
}
|
|
|
|
|
|
|
|
const array scorebackground = "P1 Score- " ascii
|
|
|
|
const array gameover_msg = "G A M E O V E R" ascii
|
|
|
|
const array title_msg = "Press Start" ascii
|
|
|
|
|
|
|
|
// -------------------------------------
|
|
|
|
// LOCATIONS
|
|
|
|
// -------------------------------------
|
|
|
|
// vram locations declared as constants for readability/convenience
|
|
|
|
// *note that these do not correlate to CPU ram locations*
|
|
|
|
const word ppu_pallete_ram = $3F00
|
|
|
|
const word ppu_nametable_ram = $2000
|
|
|
|
const word ppu_nametable_0_attr_ram = $23C0
|
|
|
|
|
|
|
|
// -------------------------------------
|
|
|
|
// LEVEL
|
|
|
|
// -------------------------------------
|
|
|
|
const byte RIGHTWALL = $F4
|
|
|
|
const byte TOPWALL = $18
|
|
|
|
const byte BOTTOMWALL = $B0
|
|
|
|
const byte LEFTWALL = $04
|
|
|
|
|
|
|
|
// -------------------------------------
|
2020-07-29 13:24:00 -05:00
|
|
|
// ATTRIBUTES
|
2020-07-20 04:01:38 -05:00
|
|
|
// -------------------------------------
|
|
|
|
// i don't actually remember if the super crate box guy is named sam or not
|
|
|
|
// or if he even has a name?
|
|
|
|
const byte SAM_ATTR = %00000001
|
|
|
|
const byte SAM_ATTR_HFLIP = %01000001
|
|
|
|
const byte SAM_ATTR_VFLIP = %10000001
|
|
|
|
const byte SAM_ATTR_HVFLIP = %11000001
|
|
|
|
|
2020-07-29 13:24:00 -05:00
|
|
|
const byte BAD_ATTR = %00000010
|
|
|
|
const byte BAD_ATTR_HFLIP = %01000010
|
|
|
|
const byte BAD_ATTR_VFLIP = %10000010
|
|
|
|
const byte BAD_ATTR_HVFLIP = %11000010
|
|
|
|
|
|
|
|
const byte MAD_ATTR = %00000011
|
|
|
|
const byte MAD_ATTR_HFLIP = %01000011
|
|
|
|
const byte MAD_ATTR_VFLIP = %10000011
|
|
|
|
const byte MAD_ATTR_HVFLIP = %11000011
|
|
|
|
|
|
|
|
// -------------------------------------
|
|
|
|
// PHYSICS?
|
|
|
|
// -------------------------------------
|
|
|
|
const byte SAM_WALKSPEED = 2
|
|
|
|
const byte BABY_WALKSPEED = SAM_WALKSPEED / 2
|
|
|
|
const byte FALLSPEED = 4
|
|
|
|
|
2020-07-20 04:01:38 -05:00
|
|
|
// -------------------------------------
|
|
|
|
// ANIMATIONS
|
|
|
|
// -------------------------------------
|
|
|
|
// all Entity (16x16)'s animations are assumed by update_entity() to consist of
|
|
|
|
// 5 frames. An animation array just contains 20 locations in CHR, four per
|
|
|
|
// frame, in this totally logical order:
|
|
|
|
// top-left, bottom-left, top-right, bottom-right
|
2020-07-29 13:24:00 -05:00
|
|
|
const array SAM_IDLE = [ $01, $11, $02, $12,
|
|
|
|
$03, $13, $04, $14,
|
|
|
|
$05, $15, $06, $16,
|
|
|
|
$07, $17, $08, $18,
|
|
|
|
$09, $19, $0A, $1A ]
|
|
|
|
|
|
|
|
const array SAM_WALK = [ $21, $31, $22, $32,
|
|
|
|
$23, $33, $24, $34,
|
|
|
|
$25, $35, $26, $36,
|
|
|
|
$27, $37, $28, $38,
|
|
|
|
$29, $39, $2A, $3A ]
|
|
|
|
|
|
|
|
const array BABY_WALK = [ $00, $C0, $00, $00,
|
|
|
|
$00, $C1, $00, $C2,
|
|
|
|
$00, $C3, $00, $00,
|
|
|
|
$00, $C4, $00, $00,
|
|
|
|
$00, $C5, $00, $00 ]
|
2020-07-20 04:01:38 -05:00
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// CORE
|
|
|
|
// ============================================================================
|
|
|
|
// quite important uwu
|
|
|
|
|
|
|
|
void main() {
|
2020-07-21 03:28:20 -05:00
|
|
|
sam.dircount = 0
|
2020-07-20 04:01:38 -05:00
|
|
|
gamestate = STATETITLE
|
|
|
|
title_init()
|
|
|
|
while(true){} // all work is done in nmi
|
|
|
|
// thnx nmi <3
|
|
|
|
}
|
|
|
|
|
|
|
|
// run at each non-maskable interrupt
|
|
|
|
// generated during each vertical blanking interval. cool
|
|
|
|
void nmi() {
|
|
|
|
// push all sprite info to the ppu (picture processing unit)
|
|
|
|
// through dma (direct memory access) transfer
|
|
|
|
ppu_oam_dma_write( oam_buffer.addr.hi )
|
|
|
|
main_game_logic()
|
|
|
|
}
|
|
|
|
|
|
|
|
// run at each interrupt request
|
|
|
|
// do I know when these are called on the nes? absolutely not
|
|
|
|
// at reset? maybe?
|
|
|
|
void irq() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// pretty self-explanatory
|
|
|
|
inline void main_game_logic() {
|
|
|
|
// use a return dispatch here
|
|
|
|
// to use different logic for each screen/gamestate
|
|
|
|
return [gamestate] {
|
|
|
|
STATETITLE @ title_logic
|
|
|
|
STATEPLAYING @ ingame_logic
|
|
|
|
STATEGAMEOVER @ gameover_logic
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// uwu owo uwu owo uwu owo
|
|
|
|
inline void init_graphics() {
|
|
|
|
init_sprites()
|
|
|
|
load_palletes()
|
|
|
|
}
|
|
|
|
|
|
|
|
// loads palletes onto the ppu
|
|
|
|
macro void load_palletes() {
|
|
|
|
byte i
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr( ppu_pallete_ram )
|
|
|
|
|
|
|
|
for i,0,until,$20 {
|
|
|
|
ppu_write_data( pallete[i] )
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleans up oam (object attribute memory)
|
|
|
|
// each sprite gets 4 bytes in oam:
|
|
|
|
// 1: ypos, 2: tile index, 3: attr table, 4: xpos
|
|
|
|
void init_sprites() {
|
|
|
|
byte i
|
|
|
|
for i,0,to,255 {
|
|
|
|
if (i & %00000011) == 0 {
|
|
|
|
//each sprite takes up 4 bytes, and we want to edit
|
|
|
|
//the y position of each sprite (0th byte)
|
|
|
|
//so we use the %00000011 mask to write every 4th byte
|
|
|
|
//(every 0th sprite byte)
|
|
|
|
oam_buffer[i] = $ef // move the sprite off screen
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
oam_buffer[i] = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// TITLE-SCREEN
|
|
|
|
// ============================================================================
|
|
|
|
void title_init() {
|
|
|
|
byte i
|
|
|
|
//for now, turn off the screen and nmi
|
|
|
|
ppu_ctrl = 0
|
|
|
|
ppu_mask = 0
|
|
|
|
|
|
|
|
//initialize the sprites and palletes
|
|
|
|
init_graphics()
|
|
|
|
|
|
|
|
//write a full screen of background data
|
|
|
|
|
|
|
|
load_sky_background()
|
|
|
|
|
|
|
|
//write the title screen message
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_ram+$018B) // point the PPU to the message's start
|
|
|
|
for i,0,until,$0B {
|
|
|
|
ppu_write_data(title_msg[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
//write the border
|
|
|
|
//top border
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_ram+$0020)
|
|
|
|
for i,0,until,$20 {
|
|
|
|
ppu_write_data($01)
|
|
|
|
}
|
|
|
|
//bottom border
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_ram+$0380)
|
|
|
|
for i,0,until,$20 {
|
|
|
|
ppu_write_data($01)
|
|
|
|
}
|
|
|
|
|
|
|
|
//set ppu address increment to 32 so we can draw the left and right borders
|
|
|
|
//(allows us to draw to the nametable in vertical strips rather than horizontal)
|
|
|
|
ppu_ctrl = %00000100
|
|
|
|
|
|
|
|
//left border
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_ram)
|
|
|
|
for i,0,until,$20 {
|
|
|
|
ppu_write_data($01)
|
|
|
|
}
|
|
|
|
//right border
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_ram+$1F)
|
|
|
|
for i,0,until,$20 {
|
|
|
|
ppu_write_data($01)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
framecounter = 0
|
|
|
|
ppu_set_scroll(0,0)
|
|
|
|
ppu_wait_vblank() //wait for next vblank before re-enabling NMI
|
|
|
|
//so that we don't get messed up scroll registers
|
|
|
|
//re-enable the screen and nmi
|
|
|
|
ppu_ctrl = %10010000 // enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
|
|
|
|
ppu_mask = %00011110 // enable sprites, enable background, no clipping on left side
|
|
|
|
}
|
|
|
|
|
|
|
|
void title_logic() {
|
|
|
|
read_joy1()
|
|
|
|
if input_start != 0 {
|
|
|
|
gamestate = STATEPLAYING
|
|
|
|
ingame_init()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
framecounter += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// IN-GAME
|
|
|
|
// ============================================================================
|
|
|
|
void ingame_init() {
|
|
|
|
//for now, turn off the screen and nmi
|
|
|
|
ppu_ctrl = 0
|
|
|
|
ppu_mask = 0
|
|
|
|
|
|
|
|
//write a full screen of data
|
|
|
|
load_sky_background()
|
|
|
|
draw_score_text_background()
|
2020-10-09 19:14:51 -05:00
|
|
|
init_map()
|
|
|
|
|
2020-07-20 04:01:38 -05:00
|
|
|
load_ingame_attr_table()
|
|
|
|
|
|
|
|
ppu_set_scroll(0,0)
|
|
|
|
ppu_wait_vblank() //wait for next vblank before re-enabling NMI
|
|
|
|
//so that we don't get messed up scroll registers
|
|
|
|
//re-enable the screen and nmi
|
|
|
|
ppu_ctrl = %10010000 // enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
|
|
|
|
ppu_mask = %00011110 // enable sprites, enable background, no clipping on left side
|
2020-07-21 03:28:20 -05:00
|
|
|
|
|
|
|
sam.top0.y = 40
|
|
|
|
sam.top0.x = 50
|
2020-07-29 13:24:00 -05:00
|
|
|
sam.walkspeed = SAM_WALKSPEED
|
|
|
|
baby.top0.y = 40
|
|
|
|
baby.top0.x = 100
|
|
|
|
baby.walkspeed = BABY_WALKSPEED
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void ingame_logic() {
|
|
|
|
draw_score()
|
|
|
|
//update scroll last because writes to vram also
|
|
|
|
//overwrite the scroll register
|
|
|
|
ppu_set_scroll(0,0) // tell the ppu there is no background scrolling
|
|
|
|
|
|
|
|
ingame_input()
|
|
|
|
update_ingame_sprites()
|
|
|
|
if score1 >= 15 {
|
|
|
|
//Someone's reached 15 points, the game is over,
|
|
|
|
//so set the state to game over and reset the
|
|
|
|
//framecounter
|
|
|
|
gamestate = STATEGAMEOVER
|
|
|
|
|
|
|
|
//move all the sprites off screen
|
|
|
|
//in preperation for the gameover screen
|
|
|
|
sam.top0.x = $ef
|
|
|
|
|
|
|
|
gameover_init()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ingame_input ( ) {
|
|
|
|
// Player 1 controls
|
|
|
|
read_joy1()
|
|
|
|
sam.movement = 0
|
|
|
|
|
|
|
|
// left
|
2020-07-21 03:28:20 -05:00
|
|
|
if ( input_dx < 0 ) {
|
2020-07-20 04:01:38 -05:00
|
|
|
sam.direction = 0
|
2020-07-21 03:28:20 -05:00
|
|
|
sam.movement = 1
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
|
|
|
// right
|
2020-07-21 03:28:20 -05:00
|
|
|
else if ( input_dx > 0 ) {
|
2020-07-20 04:01:38 -05:00
|
|
|
sam.direction = 1
|
2020-07-21 03:28:20 -05:00
|
|
|
sam.movement = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// a button
|
|
|
|
if ( input_btn == 1 && sam.jump == 0 ) {
|
|
|
|
sam.jump = 1
|
|
|
|
sam.dircount = 0
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void update_ingame_sprites ( )
|
|
|
|
{
|
|
|
|
// player. sam is the player.
|
2020-07-21 03:28:20 -05:00
|
|
|
update_entity( pointer.Entity( sam.addr ), SAM_IDLE, SAM_WALK )
|
2020-07-29 13:24:00 -05:00
|
|
|
update_entity( pointer.Entity( baby.addr ), BABY_WALK, BABY_WALK )
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
macro void load_ingame_attr_table() {
|
|
|
|
byte i
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_0_attr_ram) // point the PPU to nametable 0's attribute table
|
2020-10-08 22:58:25 -05:00
|
|
|
for i,0,until,$37 {
|
|
|
|
ppu_write_data(attribute[0])
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// GAME-OVER
|
|
|
|
// ============================================================================
|
|
|
|
void gameover_init() {
|
|
|
|
//for now, turn off nmi and sprites
|
|
|
|
ppu_ctrl = 0
|
|
|
|
ppu_mask = 0
|
|
|
|
|
|
|
|
draw_score() //draw the final score
|
|
|
|
draw_gameover() //draw the game over message
|
|
|
|
|
|
|
|
framecounter = 0
|
|
|
|
ppu_set_scroll(0,0) // tell the ppu there is no background scrolling
|
|
|
|
ppu_wait_vblank() //wait for next vblank before re-enabling NMI
|
|
|
|
//so that we don't get messed up scroll registers
|
|
|
|
//re-enable the screen and nmi
|
|
|
|
ppu_ctrl = %10010000 // enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
|
|
|
|
ppu_mask = %00011110 // enable sprites, enable background, no clipping on left side
|
|
|
|
}
|
|
|
|
|
|
|
|
void gameover_logic() {
|
|
|
|
if framecounter >= 240{
|
|
|
|
//3 seconds have passed,
|
|
|
|
//reset the game
|
|
|
|
simulate_reset()
|
|
|
|
}
|
|
|
|
framecounter += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_gameover() {
|
|
|
|
byte i
|
|
|
|
|
|
|
|
//draw the static game over message
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_ram+$0107) // point the PPU to the message's start
|
|
|
|
for i,0,until,$12 {
|
|
|
|
ppu_write_data(gameover_msg[i])
|
|
|
|
}
|
|
|
|
//draw the win message
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_ram+$01AC) // point the PPU to the message's start
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// MISC GRAPHICS
|
|
|
|
// ============================================================================
|
|
|
|
// -------------------------------------
|
|
|
|
// BACKGROUND
|
|
|
|
// -------------------------------------
|
|
|
|
inline void load_sky_background() {
|
|
|
|
word xx
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
ppu_set_addr(ppu_nametable_ram) // point the PPU to palette ram
|
|
|
|
for xx,0,until,$0300 {
|
|
|
|
ppu_write_data($00) // $00 = sky
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-09 19:14:51 -05:00
|
|
|
|
|
|
|
const array(byte) bg_pallete = [ $2C,
|
|
|
|
$29,$1A,$0F, $00,
|
|
|
|
// construction-blocks
|
|
|
|
$06,$07,$16, $16,
|
|
|
|
// construction-bricks
|
|
|
|
$06,$07,$00, $22,
|
|
|
|
$27,$17,$0F ]
|
|
|
|
|
|
|
|
const array(word) locations = [ $280 ]
|
|
|
|
const array(byte) location_tiles = [ $80 ]
|
|
|
|
|
|
|
|
const array(word) ranges = [ $2C0,$380 ]
|
|
|
|
const array(byte) range_tiles = [ $83 ]
|
|
|
|
|
|
|
|
void init_map () {
|
2020-07-20 04:01:38 -05:00
|
|
|
byte i
|
2020-10-09 19:14:51 -05:00
|
|
|
word j
|
|
|
|
word last_range
|
|
|
|
last_range = 0
|
2020-07-20 04:01:38 -05:00
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
2020-10-09 19:14:51 -05:00
|
|
|
|
|
|
|
ppu_set_addr( ppu_pallete_ram )
|
|
|
|
for i,0,until,$F {
|
|
|
|
ppu_write_data( bg_pallete[i] )
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
2020-10-09 19:14:51 -05:00
|
|
|
for i,0,until,$1 {
|
|
|
|
ppu_set_addr(ppu_nametable_ram + locations[i]) // point the PPU to the top boundary's start
|
|
|
|
ppu_write_data(location_tiles[i]) //write the top boundary tile
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
2020-10-09 19:14:51 -05:00
|
|
|
|
|
|
|
for i,0,until,$2 {
|
|
|
|
if last_range != $0 {
|
|
|
|
ppu_set_addr(ppu_nametable_ram + ranges[i-1]) // point the PPU to the top boundary's start
|
|
|
|
for j,ranges[i-1],until,ranges[1] {
|
|
|
|
ppu_write_data(range_tiles[0]) //write the top boundary tile
|
|
|
|
}
|
|
|
|
last_range = 0
|
|
|
|
} else {
|
|
|
|
last_range = 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro void draw_score_text_background() {
|
|
|
|
byte i
|
2020-07-20 04:01:38 -05:00
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
2020-10-09 19:14:51 -05:00
|
|
|
ppu_set_addr(ppu_nametable_ram+$20) // point the PPU to score text's start
|
|
|
|
for i,0,until,$1C {
|
|
|
|
ppu_write_data(scorebackground[i])
|
2020-07-20 04:01:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-09 19:14:51 -05:00
|
|
|
|
2020-07-20 04:01:38 -05:00
|
|
|
// -------------------------------------
|
|
|
|
// SPRITES
|
|
|
|
// -------------------------------------
|
2020-07-21 03:28:20 -05:00
|
|
|
void update_entity ( pointer.Entity ent, pointer idle, pointer walk ) {
|
|
|
|
entity_physics( ent )
|
2020-07-29 13:24:00 -05:00
|
|
|
if ( ent[0].bottom0.tile > $39 ) {
|
|
|
|
entity_destiny ( ent )
|
|
|
|
}
|
2020-07-21 03:28:20 -05:00
|
|
|
entity_sprite( ent, ent[0].top0.x, ent[0].top0.y, idle, walk )
|
|
|
|
}
|
|
|
|
|
2020-07-20 04:01:38 -05:00
|
|
|
// sets the current frame of ent's animation and determines which animation to use
|
2020-07-21 03:28:20 -05:00
|
|
|
void entity_sprite ( pointer.Entity ent, byte xx, byte yy, pointer idle, pointer walk ) {
|
2020-07-20 04:01:38 -05:00
|
|
|
if ( ent[0].direction == 0 ) {
|
2020-07-29 13:24:00 -05:00
|
|
|
if ( ent[0].bottom0.tile < $3A ) { // if player
|
|
|
|
ent[0].top0.attrs = SAM_ATTR_HFLIP
|
|
|
|
ent[0].bottom0.attrs = SAM_ATTR_HFLIP
|
|
|
|
ent[0].top1.attrs = SAM_ATTR_HFLIP
|
|
|
|
ent[0].bottom1.attrs = SAM_ATTR_HFLIP
|
|
|
|
} else {
|
|
|
|
ent[0].top0.attrs = BAD_ATTR_HFLIP
|
|
|
|
ent[0].bottom0.attrs = BAD_ATTR_HFLIP
|
|
|
|
ent[0].top1.attrs = BAD_ATTR_HFLIP
|
|
|
|
ent[0].bottom1.attrs = BAD_ATTR_HFLIP
|
|
|
|
}
|
2020-07-20 04:01:38 -05:00
|
|
|
ent[0].top1.x = xx - 8
|
|
|
|
ent[0].bottom1.x = xx - 8
|
|
|
|
}
|
|
|
|
else {
|
2020-07-29 13:24:00 -05:00
|
|
|
if ( ent[0].bottom0.tile < $3A ) { // if player
|
|
|
|
ent[0].top0.attrs = SAM_ATTR
|
|
|
|
ent[0].bottom0.attrs = SAM_ATTR
|
|
|
|
ent[0].top1.attrs = SAM_ATTR
|
|
|
|
ent[0].bottom1.attrs = SAM_ATTR
|
|
|
|
} else {
|
|
|
|
ent[0].top0.attrs = BAD_ATTR
|
|
|
|
ent[0].bottom0.attrs = BAD_ATTR
|
|
|
|
ent[0].top1.attrs = BAD_ATTR
|
|
|
|
ent[0].bottom1.attrs = BAD_ATTR
|
|
|
|
}
|
2020-07-20 04:01:38 -05:00
|
|
|
ent[0].top1.x = xx + 8
|
|
|
|
ent[0].bottom1.x = xx + 8
|
|
|
|
}
|
|
|
|
|
|
|
|
ent[0].top0.x = xx
|
|
|
|
ent[0].bottom0.x = xx
|
|
|
|
|
|
|
|
ent[0].top0.y = yy
|
|
|
|
ent[0].bottom0.y = yy + 8
|
|
|
|
ent[0].top1.y = yy
|
|
|
|
ent[0].bottom1.y = yy + 8
|
|
|
|
|
|
|
|
byte ani
|
|
|
|
ani = 0
|
|
|
|
if ( ent[0].frame < 4 ) {
|
|
|
|
ani = 0
|
|
|
|
} else if ( ent[0].frame < 8 ) {
|
|
|
|
ani = 4
|
|
|
|
} else if (ent[0].frame < 12 ) {
|
|
|
|
ani = 8
|
|
|
|
} else if (ent[0].frame < 16 ) {
|
|
|
|
ani = 12
|
|
|
|
} else if (ent[0].frame < 20 ) {
|
|
|
|
ani = 16
|
|
|
|
} else {
|
|
|
|
ent[0].frame = 0
|
|
|
|
ani = 0
|
|
|
|
}
|
|
|
|
|
2020-07-21 03:28:20 -05:00
|
|
|
if ( ent[0].jump == 1 ) {
|
|
|
|
ent[0].top0.tile = idle[0]
|
|
|
|
ent[0].bottom0.tile = idle[1]
|
|
|
|
ent[0].top1.tile = idle[2]
|
|
|
|
ent[0].bottom1.tile = idle[3]
|
|
|
|
} else if ( ent[0].movement == 1 ) {
|
2020-07-20 04:01:38 -05:00
|
|
|
ent[0].top0.tile = walk[ani + 0]
|
|
|
|
ent[0].bottom0.tile = walk[ani + 1]
|
|
|
|
ent[0].top1.tile = walk[ani + 2]
|
|
|
|
ent[0].bottom1.tile = walk[ani + 3]
|
|
|
|
} else {
|
|
|
|
ent[0].top0.tile = idle[ani + 0]
|
|
|
|
ent[0].bottom0.tile = idle[ani + 1]
|
|
|
|
ent[0].top1.tile = idle[ani + 2]
|
|
|
|
ent[0].bottom1.tile = idle[ani + 3]
|
|
|
|
}
|
|
|
|
|
|
|
|
ent[0].frame += 1
|
|
|
|
}
|
|
|
|
|
2020-07-21 03:28:20 -05:00
|
|
|
// -------------------------------------
|
|
|
|
// "PHYSICS"
|
|
|
|
// -------------------------------------
|
|
|
|
void entity_physics ( pointer.Entity ent ) {
|
2020-07-29 13:24:00 -05:00
|
|
|
// if player
|
|
|
|
if ( ent[0].bottom0.tile < $3A ) {
|
|
|
|
entity_jump_physics( ent )
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ent[0].bottom0.y < BOTTOMWALL - 10 && ent[0].jump == 0 ) {
|
|
|
|
ent[0].top0.y += FALLSPEED
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ent[0].movement == 1 ) {
|
|
|
|
if ( ent[0].direction == 0 ) {
|
|
|
|
ent[0].top0.x -= ent[0].walkspeed
|
|
|
|
} else if ( ent[0].direction == 1) {
|
|
|
|
ent[0].top0.x += ent[0].walkspeed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void entity_jump_physics ( pointer.Entity ent ) {
|
2020-07-21 03:28:20 -05:00
|
|
|
if ( ent[0].bottom0.y < BOTTOMWALL - 10 && ent[0].jump == 1 && ent[0].dircount == 0 ) {
|
|
|
|
// if not on ground, don't start jumping
|
|
|
|
ent[0].jump = 0
|
|
|
|
} else if ( ent[0].jump == 1 )
|
|
|
|
{
|
|
|
|
// a meager attempt at replicating scb's jump arc
|
|
|
|
// a few deficiencies:
|
|
|
|
// * a bit too short
|
|
|
|
// * "float" is a bit too short
|
|
|
|
// it might work out gameplay-wise, though, so idk we'll see
|
|
|
|
if ( ent[0].dircount < 3) {
|
|
|
|
ent[0].top0.y -= 6
|
|
|
|
} else if ( ent[0].dircount == 3 ) {
|
|
|
|
ent[0].top0.y -= 2
|
|
|
|
} else if ( ent[0].dircount == 4 ) {
|
|
|
|
ent[0].top0.y -= 6
|
|
|
|
} else if ( ent[0].dircount < 9 ) {
|
|
|
|
ent[0].top0.y -= 3
|
|
|
|
} else if ( 9 <= ent[0].dircount <= 10 ) {
|
|
|
|
ent[0].top0.y -= 1
|
|
|
|
} else if ( ent[0].dircount == 13 ) {
|
|
|
|
ent[0].top0.y += 2
|
|
|
|
} else if ( 13 < ent[0].dircount < 20 ) {
|
|
|
|
ent[0].top0.y += 2
|
|
|
|
} else if ( 20 < ent[0].dircount ) {
|
|
|
|
ent[0].dircount = -1
|
|
|
|
ent[0].jump = 0
|
|
|
|
}
|
|
|
|
ent[0].dircount += 1
|
|
|
|
}
|
2020-07-29 13:24:00 -05:00
|
|
|
}
|
2020-07-21 03:28:20 -05:00
|
|
|
|
2020-07-29 13:24:00 -05:00
|
|
|
// enemy pathfinding, blah blah.
|
|
|
|
// determines an npc's destiny
|
|
|
|
void entity_destiny ( pointer.Entity ent ) {
|
|
|
|
ent[0].movement = 1
|
2020-07-21 03:28:20 -05:00
|
|
|
}
|
|
|
|
|
2020-07-20 04:01:38 -05:00
|
|
|
// -------------------------------------
|
|
|
|
// ETC
|
|
|
|
// -------------------------------------
|
|
|
|
inline void draw_score() {
|
|
|
|
byte digit01
|
|
|
|
byte digit10
|
|
|
|
read_ppu_status() // read PPU status to reset the high/low latch
|
|
|
|
|
|
|
|
//display player1's score
|
|
|
|
digit01 = score1 %% 10 //get the ones digit
|
|
|
|
digit10 = score1 / 10 //get the tens digit
|
|
|
|
digit10 %%= 10
|
|
|
|
|
|
|
|
ppu_set_addr(ppu_nametable_ram+$29) // point the PPU to player1's score number
|
|
|
|
if digit10 > 0 {
|
|
|
|
ppu_write_data(digit10 + '0')
|
|
|
|
}
|
|
|
|
ppu_write_data(digit01 + '0')
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// UTIL
|
|
|
|
// ============================================================================
|
|
|
|
inline asm void ppu_wait_vblank() {
|
|
|
|
vblankwait:
|
|
|
|
BIT $2002
|
|
|
|
! BPL vblankwait
|
|
|
|
? RTS
|
|
|
|
}
|