mirror of
https://github.com/catseye/SixtyPical.git
synced 2024-11-25 23:49:17 +00:00
423 lines
9.3 KiB
Plaintext
423 lines
9.3 KiB
Plaintext
// ****************************
|
|
// * Demo Game for SixtyPical *
|
|
// ****************************
|
|
|
|
include "joy2delta.60p"
|
|
|
|
// ----------------------------------------------------------------
|
|
// Type Definitions
|
|
// ----------------------------------------------------------------
|
|
|
|
//
|
|
// Type of routines (and vectors to those routines) which are called on each frame
|
|
// to implement a certain state of the game (title screen, in play, game over, etc.)
|
|
//
|
|
// This type is also used as the type for the interrupt vector, even though
|
|
// the interrupt routine saves and restores everything before being called and
|
|
// thus clearly does not actually trash all the registers. It is declared this
|
|
// way so that the game state routines, which do trash these registers, can be
|
|
// assigned to it.
|
|
//
|
|
// This type is also used as the type for the location the old interrupt vector
|
|
// is backed up to, because all the game state routines `goto` the old handler
|
|
// and the end of their own routines, so the type needs to be compatible.
|
|
// (In a good sense, it is a continuation.)
|
|
//
|
|
|
|
typedef routine
|
|
inputs joy2, press_fire_msg, dispatch_game_state,
|
|
actor_pos, actor_delta, actor_logic,
|
|
player_died,
|
|
screen, colormap
|
|
outputs dispatch_game_state,
|
|
actor_pos, actor_delta, actor_logic,
|
|
player_died,
|
|
screen, colormap
|
|
trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic
|
|
game_state_routine
|
|
|
|
//
|
|
// Routines that are called to get the new state of each actor (player, enemy, etc.)
|
|
//
|
|
// Routines that conform to this type also follow this convention:
|
|
//
|
|
// Set player_died to 1 if the player perished. Unchanged otherwise.
|
|
//
|
|
|
|
typedef routine
|
|
inputs pos, delta, joy2, screen, player_died
|
|
outputs pos, delta, new_pos, screen, player_died
|
|
trashes a, x, y, z, n, v, c, ptr
|
|
logic_routine
|
|
|
|
// ----------------------------------------------------------------
|
|
// System Locations
|
|
// ----------------------------------------------------------------
|
|
|
|
byte vic_border @ 53280
|
|
byte vic_bg @ 53281
|
|
byte table[2048] screen @ 1024
|
|
byte table[2048] colormap @ 55296
|
|
|
|
// ----------------------------------------------------------------
|
|
// Global Variables
|
|
// ----------------------------------------------------------------
|
|
|
|
pointer ptr @ 254
|
|
|
|
word table[256] actor_pos
|
|
word pos
|
|
word new_pos
|
|
|
|
word table[256] actor_delta
|
|
|
|
byte player_died
|
|
|
|
vector logic_routine table[256] actor_logic
|
|
vector logic_routine dispatch_logic
|
|
|
|
byte table[18] press_fire_msg: "PRESS`FIRE`TO`PLAY"
|
|
|
|
//
|
|
// Points to the routine that implements the current game state.
|
|
//
|
|
|
|
vector game_state_routine
|
|
dispatch_game_state
|
|
|
|
//
|
|
// Interrupt vector. Has same type as game states (see above.)
|
|
//
|
|
|
|
vector game_state_routine
|
|
cinv @ 788
|
|
|
|
//
|
|
// Location to which the old interrupt vector is saved before replacement.
|
|
//
|
|
|
|
vector game_state_routine
|
|
save_cinv
|
|
|
|
// ----------------------------------------------------------------
|
|
// Utility Routines
|
|
// ----------------------------------------------------------------
|
|
|
|
// You can repeatedly (i.e. as part of actor logic or an IRQ handler)
|
|
// call this routine.
|
|
// Upon return, if carry is set, the button was pressed then released.
|
|
|
|
define check_button routine
|
|
inputs joy2
|
|
outputs c
|
|
trashes a, z, n
|
|
static byte button_down : 0
|
|
{
|
|
ld a, button_down
|
|
if z {
|
|
ld a, joy2
|
|
and a, $10
|
|
if z {
|
|
ld a, 1
|
|
st a, button_down
|
|
}
|
|
st off, c
|
|
} else {
|
|
ld a, joy2
|
|
and a, $10
|
|
if not z {
|
|
ld a, 0
|
|
st a, button_down
|
|
st on, c
|
|
} else {
|
|
st off, c
|
|
}
|
|
}
|
|
}
|
|
|
|
define clear_screen routine
|
|
outputs screen, colormap
|
|
trashes a, y, c, n, z
|
|
{
|
|
ld y, 0
|
|
repeat {
|
|
ld a, 1
|
|
st a, colormap + y
|
|
st a, colormap + 250 + y
|
|
st a, colormap + 500 + y
|
|
st a, colormap + 750 + y
|
|
|
|
ld a, 32
|
|
st a, screen + y
|
|
st a, screen + 250 + y
|
|
st a, screen + 500 + y
|
|
st a, screen + 750 + y
|
|
|
|
inc y
|
|
cmp y, 250
|
|
} until z
|
|
}
|
|
|
|
define calculate_new_position routine
|
|
inputs pos, delta
|
|
outputs new_pos
|
|
trashes a, c, n, z, v
|
|
{
|
|
copy pos, new_pos
|
|
st off, c
|
|
add new_pos, delta
|
|
}
|
|
|
|
define check_new_position_in_bounds routine
|
|
inputs new_pos
|
|
outputs c
|
|
trashes a, z, n, v
|
|
static word compare_target : 0
|
|
{
|
|
copy 1000, compare_target
|
|
st on, c
|
|
sub compare_target, new_pos
|
|
|
|
if not c {
|
|
copy word 0, compare_target
|
|
st on, c
|
|
sub compare_target, new_pos
|
|
if not c {
|
|
st off, c
|
|
} else {
|
|
st on, c
|
|
}
|
|
} else {
|
|
st on, c
|
|
}
|
|
}
|
|
|
|
define init_game routine
|
|
inputs actor_pos, actor_delta, actor_logic
|
|
outputs actor_pos, actor_delta, actor_logic, player_died
|
|
trashes pos, a, y, z, n, c, v
|
|
{
|
|
ld y, 0
|
|
copy word 0, pos
|
|
repeat {
|
|
copy pos, actor_pos + y
|
|
copy word 40, actor_delta + y
|
|
copy enemy_logic, actor_logic + y
|
|
|
|
st off, c
|
|
add pos, word 7
|
|
|
|
inc y
|
|
cmp y, 16
|
|
} until z
|
|
|
|
ld y, 0
|
|
copy word 40, actor_pos + y
|
|
copy word 0, actor_delta + y
|
|
copy player_logic, actor_logic + y
|
|
|
|
st y, player_died
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Actor Logics
|
|
// ----------------------------------------------------------------
|
|
|
|
define player_logic logic_routine
|
|
{
|
|
call read_stick
|
|
|
|
call calculate_new_position
|
|
call check_new_position_in_bounds
|
|
|
|
if c {
|
|
point ptr into screen {
|
|
reset ptr 0
|
|
st off, c
|
|
add ptr, new_pos
|
|
ld y, 0
|
|
|
|
// check collision.
|
|
ld a, [ptr] + y
|
|
|
|
// if "collision" is with your own self, treat it as if it's blank space!
|
|
cmp a, 81
|
|
if z {
|
|
ld a, 32
|
|
}
|
|
cmp a, 32
|
|
if z {
|
|
reset ptr 0
|
|
st off, c
|
|
add ptr, pos
|
|
copy 32, [ptr] + y
|
|
|
|
copy new_pos, pos
|
|
|
|
reset ptr 0
|
|
st off, c
|
|
add ptr, pos
|
|
copy 81, [ptr] + y
|
|
}
|
|
}
|
|
} else {
|
|
ld a, 1
|
|
st a, player_died
|
|
}
|
|
}
|
|
|
|
define enemy_logic logic_routine
|
|
static word compare_target : 0
|
|
{
|
|
call calculate_new_position
|
|
call check_new_position_in_bounds
|
|
|
|
if c {
|
|
point ptr into screen {
|
|
reset ptr 0
|
|
st off, c
|
|
add ptr, new_pos
|
|
ld y, 0
|
|
|
|
// check collision.
|
|
ld a, [ptr] + y
|
|
|
|
// if "collision" is with your own self, treat it as if it's blank space!
|
|
cmp a, 82
|
|
if z {
|
|
ld a, 32
|
|
}
|
|
cmp a, 32
|
|
if z {
|
|
reset ptr 0
|
|
st off, c
|
|
add ptr, pos
|
|
copy 32, [ptr] + y
|
|
|
|
copy new_pos, pos
|
|
|
|
reset ptr 0
|
|
st off, c
|
|
add ptr, pos
|
|
copy 82, [ptr] + y
|
|
}
|
|
}
|
|
} else {
|
|
copy delta, compare_target
|
|
st on, c
|
|
sub compare_target, word 40
|
|
if not z {
|
|
copy word 40, delta
|
|
} else {
|
|
copy $ffd8, delta
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Game States
|
|
// ----------------------------------------------------------------
|
|
|
|
define game_state_title_screen game_state_routine
|
|
{
|
|
ld y, 0
|
|
for y up to 17 {
|
|
ld a, press_fire_msg + y
|
|
|
|
st on, c
|
|
sub a, 64 // yuck. oh well
|
|
|
|
st a, screen + y
|
|
}
|
|
|
|
st off, c
|
|
call check_button
|
|
|
|
if c {
|
|
call clear_screen
|
|
call init_game
|
|
copy game_state_play, dispatch_game_state
|
|
}
|
|
|
|
goto save_cinv
|
|
}
|
|
|
|
define game_state_play game_state_routine
|
|
{
|
|
ld x, 0
|
|
st x, player_died
|
|
for x up to 15 {
|
|
copy actor_pos + x, pos
|
|
copy actor_delta + x, delta
|
|
|
|
//
|
|
// Save our loop counter on the stack temporarily. This means that routines
|
|
// like `dispatch_logic` and `clear_screen` are allowed to do whatever they
|
|
// want with the `x` register; we will restore it at the end of this block.
|
|
//
|
|
save x {
|
|
copy actor_logic + x, dispatch_logic
|
|
call dispatch_logic
|
|
}
|
|
|
|
copy pos, actor_pos + x
|
|
copy delta, actor_delta + x
|
|
}
|
|
|
|
ld a, player_died
|
|
if not z {
|
|
// Player died! Want no dead!
|
|
call clear_screen
|
|
copy game_state_game_over, dispatch_game_state
|
|
}
|
|
|
|
goto save_cinv
|
|
}
|
|
|
|
define game_state_game_over game_state_routine
|
|
{
|
|
st off, c
|
|
call check_button
|
|
|
|
if c {
|
|
call clear_screen
|
|
call init_game
|
|
copy game_state_title_screen, dispatch_game_state
|
|
}
|
|
|
|
goto save_cinv
|
|
}
|
|
|
|
// *************************
|
|
// * Main Game Loop Driver *
|
|
// *************************
|
|
|
|
define our_cinv preserved game_state_routine
|
|
{
|
|
goto dispatch_game_state
|
|
}
|
|
|
|
define main routine
|
|
inputs cinv
|
|
outputs cinv, save_cinv, pos, dispatch_game_state, screen, colormap
|
|
trashes a, y, n, c, z, vic_border, vic_bg
|
|
{
|
|
ld a, 5
|
|
st a, vic_border
|
|
ld a, 0
|
|
st a, vic_bg
|
|
ld y, 0
|
|
|
|
call clear_screen
|
|
|
|
copy game_state_title_screen, dispatch_game_state
|
|
|
|
copy word 0, pos
|
|
with interrupts off {
|
|
copy cinv, save_cinv
|
|
copy our_cinv, cinv
|
|
}
|
|
|
|
repeat { } forever
|
|
}
|