SixtyPical/eg/c64/demo-game/demo-game.60p

395 lines
8.9 KiB
Plaintext

// ****************************
// * Demo Game for SixtyPical *
// ****************************
// Copyright (c) 2014-2024, Chris Pressey, Cat's Eye Technologies.
// This file is distributed under a 2-clause BSD license. See LICENSES/ dir.
// SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-SixtyPical
include "joystick.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
// ----------------------------------------------------------------
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
}