Add missile command sample game

This commit is contained in:
drmortalwombat 2022-01-31 21:17:14 +01:00
parent e1f605b28c
commit 995e39a57d
14 changed files with 640 additions and 143 deletions

View File

@ -34,9 +34,10 @@ char MixedColors[4][4][8] = {
void bmmc_put(const Bitmap * bm, int x, int y, char c)
{
char * dp = bm->data + bm->cwidth * (y & ~7) + ((x & ~7) | (y & 7));
char * dp = bm->data + bm->cwidth * (y & ~7) + ((x & ~7) | (y & 7));
char pat = cbytes[c & 3];
*dp = (*dp & andmask[x & 7]) | (cbytes[c & 3] & ormask[x & 7]);
*dp = ((*dp ^ pat) & andmask[x & 7]) ^ pat;
}
char bmmc_get(const Bitmap * bm, int x, int y)

View File

@ -11987,7 +11987,7 @@ void NativeCodeBasicBlock::BlockSizeReduction(void)
i += 3;
}
else if (i + 5 < mIns.Size() &&
mIns[i + 0].mType == ASMIT_LDA &&
mIns[i + 0].ChangesAccuAndFlag() &&
mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 &&
mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 &&
mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff &&
@ -13032,7 +13032,8 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass)
}
else
mIns.Insert(i + 0, NativeCodeInstruction(ASMIT_LDY, ASMIM_ZERO_PAGE, iins->mAddress));
mIns[i + 0].mLive = mIns[i - 1].mLive | LIVE_CPU_REG_Y | LIVE_MEM;
if (i > 0)
mIns[i + 0].mLive = mIns[i - 1].mLive | LIVE_CPU_REG_Y | LIVE_MEM;
}
progress = true;
@ -13859,7 +13860,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass)
#if 1
else if (
mIns[i + 0].mType == ASMIT_CLC &&
mIns[i + 0].mType == ASMIT_CLC &&
mIns[i + 1].mType == ASMIT_TYA &&
mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Y)))
{
@ -13899,7 +13900,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass)
#endif
else if (
mIns[i + 0].mType == ASMIT_ADC && mIns[i + 0].mMode == ASMIM_IMMEDIATE &&
mIns[i + 1].mType == ASMIT_CLC &&
mIns[i + 1].mType == ASMIT_CLC &&
mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C))
{
mIns[i + 0].mAddress += mIns[i + 2].mAddress;
@ -13909,7 +13910,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass)
}
else if (
mIns[i + 0].mType == ASMIT_SBC && mIns[i + 0].mMode == ASMIM_IMMEDIATE &&
mIns[i + 1].mType == ASMIT_SEC &&
mIns[i + 1].mType == ASMIT_SEC &&
mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C))
{
mIns[i + 0].mAddress += mIns[i + 2].mAddress;
@ -13920,7 +13921,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass)
else if (
mIns[i + 0].mType == ASMIT_STA && !(mIns[i + 0].mFlags & NCIF_VOLATILE) &&
mIns[i + 2].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) &&
!mIns[i + 1].ChangesAddress() && !mIns[i + 1].ChangesGlobalMemory() &&
!mIns[i + 1].ChangesAddress() && !mIns[i + 1].ChangesGlobalMemory() &&
!mIns[i + 1].ChangesYReg() && !mIns[i + 1].ChangesXReg())
{
mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED;
@ -13994,6 +13995,16 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass)
#endif
#endif
else if (
mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE &&
mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress &&
mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & (LIVE_MEM | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)))
{
mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A;
mIns[i + 2].mType = ASMIT_ROL; mIns[i + 2].mAddress = mIns[i + 1].mAddress; mIns[i + 2].mLive |= LIVE_MEM;
mIns[i + 1].mType = ASMIT_ASL; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C;
progress = true;
}
#if 1
else if (
mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE &&
@ -14230,6 +14241,36 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass)
progress = true;
}
#endif
#if 1
else if (
mIns[i + 0].mType == ASMIT_LDA &&
mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 1]) &&
mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE &&
mIns[i + 3].mType == ASMIT_ORA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0]))
{
mIns[i + 2].mType = ASMIT_ORA;
mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED;
progress = true;
}
#endif
#if 1
else if (
mIns[i + 0].mType == ASMIT_LDA &&
mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff &&
mIns[i + 2].mType == ASMIT_SEC &&
mIns[i + 3].mType == ASMIT_ADC)
{
mIns.Insert(i + 4, mIns[i + 0]);
mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED;
mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED;
mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_C;
mIns[i + 4].mType = ASMIT_SBC;
progress = true;
}
#endif
#if 1
else if (
mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE &&

View File

@ -73,7 +73,7 @@ int main(int argc, const char** argv)
#else
strcpy(strProductName, "oscar64");
strcpy(strProductVersion, "1.3.74");
strcpy(strProductVersion, "1.4.75");
#ifdef __APPLE__
uint32_t length = sizeof(basePath);

View File

@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,3,74,0
PRODUCTVERSION 1,3,74,0
FILEVERSION 1,4,75,0
PRODUCTVERSION 1,4,75,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -43,12 +43,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "oscar64"
VALUE "FileDescription", "oscar64 compiler"
VALUE "FileVersion", "1.3.74.0"
VALUE "FileVersion", "1.4.75.0"
VALUE "InternalName", "oscar64.exe"
VALUE "LegalCopyright", "Copyright (C) 2021"
VALUE "OriginalFilename", "oscar64.exe"
VALUE "ProductName", "oscar64"
VALUE "ProductVersion", "1.3.74.0"
VALUE "ProductVersion", "1.4.75.0"
END
END
BLOCK "VarFileInfo"

View File

@ -94,6 +94,12 @@
}
"Entry"
{
"MsmKey" = "8:_1ED10FB93DDA4801BF72003E21B2CE55"
"OwnerKey" = "8:_UNDEFINED"
"MsmSig" = "8:_UNDEFINED"
}
"Entry"
{
"MsmKey" = "8:_1F88FA4F35F043B3ABFCB552FCEA5CDD"
"OwnerKey" = "8:_UNDEFINED"
"MsmSig" = "8:_UNDEFINED"
@ -1087,6 +1093,26 @@
"IsDependency" = "11:FALSE"
"IsolateTo" = "8:"
}
"{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1ED10FB93DDA4801BF72003E21B2CE55"
{
"SourcePath" = "8:..\\samples\\games\\missile.c"
"TargetName" = "8:missile.c"
"Tag" = "8:"
"Folder" = "8:_BC04C0DDE264410096618981E4E890DA"
"Condition" = "8:"
"Transitive" = "11:FALSE"
"Vital" = "11:TRUE"
"ReadOnly" = "11:FALSE"
"Hidden" = "11:FALSE"
"System" = "11:FALSE"
"Permanent" = "11:FALSE"
"SharedLegacy" = "11:FALSE"
"PackageAs" = "3:1"
"Register" = "3:1"
"Exclude" = "11:FALSE"
"IsDependency" = "11:FALSE"
"IsolateTo" = "8:"
}
"{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1F88FA4F35F043B3ABFCB552FCEA5CDD"
{
"SourcePath" = "8:..\\include\\c64\\rasterirq.c"
@ -3492,15 +3518,15 @@
{
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:oscar64"
"ProductCode" = "8:{109FC925-4A6D-4868-A60D-42BF3DFE541B}"
"PackageCode" = "8:{C6215A39-7DD0-4667-93FB-46373135B9C8}"
"ProductCode" = "8:{1FF1624F-A9FD-402D-806B-6CE9925FB690}"
"PackageCode" = "8:{47F7AA54-A5D7-4A82-8276-0DCD0FE332D4}"
"UpgradeCode" = "8:{9AB61EFF-ACAC-4079-9950-8D96615CD4EF}"
"AspNetVersion" = "8:2.0.50727.0"
"RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:TRUE"
"DetectNewerInstalledVersion" = "11:TRUE"
"InstallAllUsers" = "11:FALSE"
"ProductVersion" = "8:1.3.74"
"ProductVersion" = "8:1.4.75"
"Manufacturer" = "8:oscar64"
"ARPHELPTELEPHONE" = "8:"
"ARPHELPLINK" = "8:"

View File

@ -2,4 +2,5 @@
../../bin/oscar64 snake.c
../../bin/oscar64 lander.c -n
../../bin/oscar64 maze3d.c -n
../../bin/oscar64 missile.c -O3 -n

View File

@ -223,15 +223,15 @@ void game_loop(void)
case GS_PLAYING:
{
// Check player input on every frame
joy_poll(1);
lander_move(&TheGame.lander, joyx[1], joyy[1]);
joy_poll(0);
lander_move(&TheGame.lander, joyx[0], joyy[0]);
LanderCollision col = lander_check(&TheGame.lander);
if (col == LCOL_GROUND)
game_state(GS_COLLIDE);
else if (col == LCOL_PAD)
game_state(GS_LANDED);
else
lander_show(&TheGame.lander, joyx[1], joyy[1]);
lander_show(&TheGame.lander, joyx[0], joyy[0]);
} break;
case GS_LANDED:
if (!--TheGame.count)

View File

@ -1,4 +1,4 @@
call ..\..\bin\oscar64 snake.c
call ..\..\bin\oscar64 -n lander.c
call ..\..\bin\oscar64 -n maze3d.c
call ..\..\bin\oscar64 -n missile.c
call ..\..\bin\oscar64 -n -O3 missile.c

View File

@ -419,19 +419,19 @@ int main(void)
for(;;)
{
// Read joystick input
joy_poll(1);
joy_poll(0);
// Forward or backward motion
if (joyy[1])
if (joyy[0])
{
// Target square
sbyte tx = px - dx * joyy[1];
sbyte ty = py - dy * joyy[1];
sbyte tx = px - dx * joyy[0];
sbyte ty = py - dy * joyy[0];
// Check i empty
if (maze[ty][tx] != '#')
{
if (joyy[1] < 0)
if (joyy[0] < 0)
{
// Forward animation
px = tx;
@ -457,7 +457,7 @@ int main(void)
// Check if new rotation
if (!rotate)
{
if (joyx[1] == 1)
if (joyx[0] == 1)
{
// Rotate right
sbyte t = dx; dx = -dy; dy = t;
@ -465,7 +465,7 @@ int main(void)
maze_draw(zxdist0);
screen_left();
}
else if (joyx[1] == -1)
else if (joyx[0] == -1)
{
// Rotate left
sbyte t = dx; dx = dy; dy = -t;
@ -474,7 +474,7 @@ int main(void)
screen_right();
}
}
else if (!joyx[1])
else if (!joyx[0])
{
// No rotation, may rotate again in next frame
rotate = false;

View File

@ -3,114 +3,295 @@
#include <c64/sprites.h>
#include <c64/joystick.h>
#include <c64/rasterirq.h>
#include <c64/cia.h>
#include <gfx/mcbitmap.h>
#include <string.h>
#include <stdlib.h>
// Make some room
#pragma region(main, 0x0a00, 0xc800, , , {code, data, bss, heap, stack} )
// Sprite assets
const char MissileSprites[] = {
#embed "../resources/missilesprites.bin"
};
// Charset assets
const char MissileChars[] = {
#embed "../resources/missilechars.bin"
};
// Graphics areas in bank 3
#define Color1 ((char *)0xc800)
#define Color2 ((char *)0xd800)
#define Hires ((char *)0xe000)
#define Sprites ((char *)0xd000)
#define Charset ((char *)0xd800)
// Joystick and crosshair control
int CrossX = 160, CrossY = 100;
bool CrossP = false;
char CrossDelay = 0;
// Display bitmap
Bitmap sbm;
const ClipRect scr = { 0, 0, 320, 200 };
// Structure for a explosion
struct Explosion
{
int x, y;
char r;
Explosion * next;
int x, y; // Center of circle
char r; // Radius of circle
Explosion * next; // Next explosion in list
};
// Structure for a missile (defensive and ICBM)
struct Missile
{
int sx, sy, tx, ty, x, y, dx, dy;
int d;
char cnt;
Missile * next;
int sx, sy; // start position
int tx, ty; // target position
int x, y; // current position
int dx, dy; // distance in x and y
int d; // error term for Bresenham
sbyte stepx; // direction in x (+1 or -1)
sbyte cnt; // speed counter
Missile * next; // next missile in list
};
// Storage space for explosion
Explosion explosions[16];
Explosion * efree, * eused;
// First free and first used explosion
Explosion * efree, * eused;
// Storage space for defending missiles
Missile missiles[8];
// First free and first used missile
Missile * mfree, * mused;
// Number of missiles available
char nmissiles;
// Storage space for ICMBs
Missile icbms[16];
// First free and first used ICBM
Missile * ifree, * iused;
// Speed and number of ICBMs still incomming
char icbmspeed, icbmcount;
bool cities[6];
// Cities not yet destroyed
bool cities[6];
char ncities;
// Init status bar at top
void status_init(void)
{
memset(Color1, ' ', 40);
memset(Color2, 1, 40);
for(char i=0; i<8; i++)
{
Color1[i + 2] = '0';
Color1[i + 12] = '0';
Color2[i + 12] = 7;
}
}
// Expand an 8x8 charactor to 16x16 on screen
void char_put(char cx, char cy, char c, char color)
{
// Get pointer to glyph data
const char * sp = MissileChars + 8 * c;
// Loop over all pixel
for(char y=0; y<8; y++)
{
char cl = sp[y];
for(char x=0; x<8; x++)
{
// Draw two pixel if bit is set
if (cl & 128)
{
bmmc_put(&sbm, cx + 2 * x, cy + 2 * y + 0, color);
bmmc_put(&sbm, cx + 2 * x, cy + 2 * y + 1, color);
}
// Next bit
cl <<= 1;
}
}
}
// Write a zero terminated string on screen
void char_write(char cx, char cy, const char * s, char color)
{
// Loop over all characters
while (*s)
{
char_put(cx, cy, *s, color);
s++;
cx += 16;
}
}
// Increment the score from a given digit on
void score_inc(char digit, unsigned val)
{
// Lowest digit to increment
char at = 9 - digit;
// Loop while there is still score to account for
while (val)
{
// Increment one character
char ch = Color1[at] + val % 10;
// Remove low digit from number
val /= 10;
// Check overflow
if (ch > '9')
{
ch -= 10;
val++;
}
Color1[at] = ch;
// Next higher character
at --;
}
}
// Reset score and update high score
void score_reset(void)
{
// Find first digit, where score and highscore differ
char i = 0;
while (i < 8 && Color1[i + 2] == Color1[i + 12])
i++;
// Check if new score is higher
if (i < 8 && Color1[i + 2] > Color1[i + 12])
{
// If so, copy to highscore
while (i < 8)
{
Color1[i + 12] = Color1[i + 2];
i++;
}
}
// Clear score
for(char i=0; i<8; i++)
Color1[i + 2] = '0';
}
// Update number of missiles
void status_missiles(char num)
{
char n = 0;
// Draw full pairs
while (2 * n + 1 < num)
{
Color1[25 + n] = 92;
n++;
}
// Draw remaining single one
if (num & 1)
{
Color1[25 + n] = 93;
n++;
}
// Empty remainder
while (n < 15)
{
Color1[25 + n] = 94;
n++;
}
}
// Initialize explosion list
void explosion_init(void)
{
// No explosion active
eused = nullptr;
// First free explosion element
efree = explosions;
// Build list
for(char i=0; i<15; i++)
explosions[i].next = explosions + i + 1;
// Terminate last element
explosions[15].next = nullptr;
}
// Start a new explosion
void explosion_start(int x, int y)
{
// Free slot in list of explosions?
if (efree)
{
// Move entry from free to used list
Explosion * e = efree;
efree = e->next;
e->next = eused;
eused = e;
// Initialize position and size
e->r = 0;
e->x = x;
e->y = y;
}
}
// Animate all explosions
void explosion_animate(void)
{
// Loop over active explosions with "e", use "ep" to point
// to previous explosion, so we can remove the current explosion
// from the list
Explosion * e = eused, * ep = nullptr;
while (e)
{
// Remember next entry in list
Explosion * en = e->next;
// Increment phase (radius)
e->r++;
// Advance every fourth frame
if (!(e->r & 3))
{
// Draw or erase outer perimeter depending on growing or
// shrinking explosion phase
if (e->r <= 64)
bmmc_circle(&sbm, &scr, e->x, e->y, e->r >> 2, 1);
else
bmmc_circle(&sbm, &scr, e->x, e->y, 33 - (e->r >> 2), 0);
}
// End of explosion live
if (e->r == 128)
{
// Remove explosion from used list
if (ep)
ep->next = e->next;
else
eused = e->next;
// Prepend it to free list
e->next = efree;
efree = e;
}
else
ep = e;
// Next explosion in list
e = en;
}
}
// Initialize defending missile list
void missile_init(void)
{
mused = nullptr;
@ -120,31 +301,47 @@ void missile_init(void)
missiles[7].next = nullptr;
}
// Add a new defending missile
void missile_start(int sx, int sy, int tx, int ty)
{
if (mfree)
// Check if entry in free list and missile in silo remaining
if (mfree && nmissiles > 0)
{
// Detach from free list
Missile * m = mfree;
mfree = m->next;
// Attach to active list
m->next = mused;
mused = m;
// Initialize start and target coordinates
m->sx = sx >> 1; m->x = sx >> 1;
m->sy = sy; m->y = sy;
m->tx = tx >> 1;
m->ty = ty;
// Initialize line drawing parameters
m->dy = m->sy - m->ty;
m->dx = m->tx - m->sx;
m->stepx = 1;
if (m->dx < 0)
{
m->dx = -m->dx;
m->stepx = -1;
}
m->d = m->dy - m->dx;
m->dx *= 2;
m->dy *= 2;
// Remove missile from silo
nmissiles--;
status_missiles(nmissiles);
}
}
// Animate all active missiles
void missile_animate(void)
{
Missile * m = mused, * mp = nullptr;
@ -152,6 +349,7 @@ void missile_animate(void)
{
Missile * mn = m->next;
// Advance missile position using one step of Bresenham
if (m->d >= 0)
{
m->y--;
@ -159,18 +357,18 @@ void missile_animate(void)
}
if (m->d < 0)
{
if (m->tx < m->sx)
m->x--;
else
m->x++;
m->x += m->stepx;
m->d += m->dy;
}
if (bmmc_get(&sbm, m->x, m->y) == 1 || m->y == m->ty)
// Check if target reached
if (m->y == m->ty)
{
// If so, clear line and start explosion
bmmcu_line(&sbm, m->sx * 2, m->sy, m->tx * 2, m->ty, 0);
explosion_start(m->x * 2, m->y);
// Remove from active list
if (mp)
mp->next = m->next;
else
@ -180,6 +378,7 @@ void missile_animate(void)
}
else
{
// Draw new pixel in missile trace
bmmc_put(&sbm, m->x * 2, m->y, 3);
mp = m;
}
@ -188,6 +387,7 @@ void missile_animate(void)
}
}
// Initialize incomming ICBM list
void icbm_init(void)
{
iused = nullptr;
@ -197,9 +397,10 @@ void icbm_init(void)
icbms[15].next = nullptr;
}
// Add a new ICBM to the attacking set
void icbm_start(int sx, int sy, int tx, int ty)
{
if (ifree)
if (icbmcount && ifree)
{
Missile * m = ifree;
ifree = m->next;
@ -210,16 +411,22 @@ void icbm_start(int sx, int sy, int tx, int ty)
m->sy = sy; m->y = sy;
m->tx = tx >> 1;
m->ty = ty;
m->cnt = 4;
m->cnt = 0;
m->dy = m->ty - m->sy;
m->dx = m->tx - m->sx;
m->stepx = 1;
if (m->dx < 0)
{
m->dx = -m->dx;
m->stepx = -1;
}
m->d = m->dy - m->dx;
m->dx *= 2;
m->dy *= 2;
icbmcount--;
}
}
@ -230,14 +437,16 @@ void icbm_animate(void)
{
Missile * mn = m->next;
m->cnt--;
if (!m->cnt)
// Check speed of ICBMs
m->cnt += icbmspeed;
while (m->cnt > 0)
{
bmmc_put(&sbm, m->x * 2, m->y, 2);
m->cnt -= 32;
m->cnt = 4;
// Draw pixel in trace
bmmc_put(&sbm, m->x * 2, m->y, 2);
// Advance using Bresenham
if (m->d >= 0)
{
m->y++;
@ -245,80 +454,126 @@ void icbm_animate(void)
}
if (m->d < 0)
{
if (m->tx < m->sx)
m->x--;
else
m->x++;
m->x += m->stepx;
m->d += m->dy;
}
// Check if colliding with cloud or target reached
if (bmmc_get(&sbm, m->x * 2, m->y) == 1 || m->y == m->ty)
{
// If so, clear trace and start explosion
bmmcu_line(&sbm, m->sx * 2, m->sy, m->tx * 2, m->ty, 0);
explosion_start(m->x * 2, m->y);
// Check if we hit the ground
if (m->y == m->ty)
{
// If so, find matching city
int x = m->x * 2;
char ix;
if (x > 160)
ix = (x - 202) / 32 + 3;
ix = ((x - 202) >> 5) + 3;
else
ix = (x - 58) / 32;
cities[ix] = false;
spr_show(ix + 1, false);
ix = (x - 58) >> 5;
// Destroy, destroy, annihilate, kill, kill
if (cities[ix])
{
ncities--;
cities[ix] = false;
spr_show(ix + 1, false);
}
}
// Add score for destroyed ICBM
score_inc(0, 25);
// Remove from list
if (mp)
mp->next = m->next;
else
iused = m->next;
m->next = ifree;
ifree = m;
}
else
{
bmmc_put(&sbm, m->x * 2, m->y, 1);
mp = m;
m = nullptr;
// End loop early
break;
}
}
else
// If ICBM still in flight
if (m)
{
// Remember for list management
mp = m;
// Draw white head
bmmc_put(&sbm, m->x * 2, m->y, 1);
}
m = mn;
}
}
void mountains_init(void)
// Initialize game screen
void screen_init(void)
{
// Clean up
bmmcu_rect_fill(&sbm, 0, 8, 320, 184, 0);
// Draw bottom
bmmcu_rect_fill(&sbm, 0, 192, 320, 8, 3);
bmmc_quad_fill(&sbm, &scr, 0, 192, 16, 184, 32, 184, 48, 192, MixedColors[3][3]);
bmmc_quad_fill(&sbm, &scr, 136, 192, 152, 184, 176, 184, 192, 192, MixedColors[3][3]);
bmmc_quad_fill(&sbm, &scr, 272, 192, 288, 184, 304, 184, 320, 192, MixedColors[3][3]);
// Show cities
for(char i=0; i<3; i++)
{
if (cities[i + 0])
spr_set(i + 1, true, 70 + 32 * i, 222, 65, 15, false, false, false);
if (cities[i + 3])
spr_set(i + 4, true, 214 + 32 * i, 222, 65, 15, false, false, false);
}
}
// Clear inner area of screen
void screen_clear(void)
{
bmmcu_rect_fill(&sbm, 0, 8, 320, 176, 0);
}
// Interrupt routine for joystick control, called by raster IRQ at bottom
// of screen
__interrupt void joy_interrupt()
{
joy_poll(1);
// Poll joystick
joy_poll(0);
CrossX += joyx[1]; CrossY += joyy[1];
// Move crosshair coordinates
CrossX += 2 * joyx[0]; CrossY += 2 * joyy[0];
// Stop at edges of screen
if (CrossX < 8)
CrossX = 8;
else if (CrossX > 312)
CrossX = 312;
if (CrossY < 16)
CrossY = 16;
if (CrossY < 20)
CrossY = 20;
else if (CrossY > 172)
CrossY = 172;
// Move crosshair sprite
spr_move(0, CrossX + 14, CrossY + 40);
if (joyb[1])
// Check button
if (joyb[0])
{
// Avoid quickfire and bouncing
if (CrossDelay == 0)
{
// Request fire from non interrupt code
CrossP = true;
CrossDelay = 4;
}
@ -327,64 +582,267 @@ __interrupt void joy_interrupt()
CrossDelay--;
}
enum GameState
{
GS_READY, // Getting ready
GS_LEVEL, // Show level message
GS_PLAYING, // Playing the game
GS_BONUS, // Landed on pad
GS_ARMAGEDDON, // Show end game animation
GS_END // Wait for restart
};
// State of the game
struct Game
{
GameState state;
byte count, level;
} TheGame; // Only one game, so global variable
// Character buffer for some texts
char cbuffer[10];
void game_state(GameState state)
{
TheGame.state = state;
switch(state)
{
case GS_READY:
// Start of new game
score_reset();
screen_init();
char_write(40, 100, s"READY PLAYER 1", 3);
// New cities
for(char i=0; i<6; i++)
cities[i] = true;
ncities = 6;
TheGame.count = 60;
// Starting at level 1
TheGame.level = 0;
break;
case GS_LEVEL:
// Advance to next level
TheGame.level++;
utoa(TheGame.level ,cbuffer, 10);
screen_clear();
char_write(96, 100, s"LEVEL", 3);
char_write(96 + 16 * 6, 100, cbuffer, 3);
TheGame.count = 30;
break;
case GS_PLAYING:
// Avoid old fire request
CrossP = false;
// Setup display
screen_init();
missile_init();
explosion_init();
icbm_init();
// A new set of 30 missiles
nmissiles = 30;
status_missiles(nmissiles);
// Game parameters based on level
if (TheGame.level < 112)
icbmspeed = 8 + TheGame.level;
else
icbmspeed = 120;
if (TheGame.level < 50)
icbmcount = 5 + TheGame.level / 2;
else
icbmcount = 30;
TheGame.count = 15;
break;
case GS_BONUS:
{
// Show bonus
unsigned bonus = ncities * 50 + nmissiles * 5;
utoa(bonus, cbuffer, 10);
char_write(96, 100, s"BONUS", 3);
char_write(96 + 16 * 6, 100, cbuffer, 3);
score_inc(0, bonus);
TheGame.count = 30;
} break;
case GS_ARMAGEDDON:
TheGame.count = 0;
break;
case GS_END:
char_write(104, 92, s"THE END", 0);
TheGame.count = 120;
break;
}
}
// Main game play code
void game_play(void)
{
// Check if fire request
if (CrossP)
{
// Find lauch site
int sx = 160;
if (CrossX < 120)
sx = 24;
else if (CrossX > 200)
sx = 296
// Fire missile
missile_start(sx, 184, CrossX, CrossY);
// Reset request
CrossP = false;
}
// Wait for next ICMB to enter the game
if (!--TheGame.count)
{
// Pick target city
char ci;
do {
ci = rand() & 7;
} while (ci >= 6 || !cities[ci]);
int cx;
if (ci < 3)
cx = 58 + 32 * ci;
else
cx = 202 + 32 * (ci - 3);
// Launch ICBM
icbm_start((rand() & 0xff) + 32, 0, cx, 184);
// Next lauch time
TheGame.count = 8 + (rand() & 63);
}
// Advance defending missiles by four pixels
for(char i=0; i<4; i++)
missile_animate();
// Advance ICBMs
icbm_animate();
// Show explosions
explosion_animate();
}
// Main game loop, entered every VSYNC, slower if too busy with explosions
void game_loop(void)
{
switch(TheGame.state)
{
case GS_READY:
if (!--TheGame.count)
game_state(GS_LEVEL);
break;
case GS_LEVEL:
if (!--TheGame.count)
game_state(GS_PLAYING);
break;
case GS_PLAYING:
game_play();
// Check for level and game end coditions
if (!icbmcount && !iused && !eused)
game_state(GS_BONUS);
else if (ncities == 0)
game_state(GS_ARMAGEDDON);
break;
case GS_BONUS:
if (!--TheGame.count)
game_state(GS_LEVEL);
break;
case GS_ARMAGEDDON:
// Draw end game animation
TheGame.count++;
bmmc_circle(&sbm, &scr, 160, 100, TheGame.count, 1);
explosion_animate();
if (TheGame.count == 90)
game_state(GS_END);
break;
case GS_END:
if (!--TheGame.count)
game_state(GS_READY);
break;
}
}
// Interrupts for status line and joystick routine
RIRQCode bottom, top;
int main(void)
{
// Activate trampoline
mmap_trampoline();
// Disable CIA interrupts, we do not want interference
// with our joystick interrupt
cia_init();
// Copy assets
mmap_set(MMAP_RAM);
memcpy(Sprites, MissileSprites, 1024);
memcpy(Charset, MissileChars, 2048);
mmap_set(MMAP_NO_ROM);
vic_setmode(VICM_HIRES_MC, Color1, Hires);
spr_init(Color1);
// initialize raster IRQ
rirq_init(true);
vic.color_back = VCOL_BLACK;
vic.color_border = VCOL_BLACK;
// Clean out screen space
memset(Color1, 0x18, 1000);
memset(Color2, 0x06, 1000);
memset(Hires, 0, 8000);
memset(Color2 + 40 * 23, 0x07, 80);
bm_init(&sbm, Hires, 40, 25);
missile_init();
explosion_init();
icbm_init();
mountains_init();
// initialize raster IRQ
rirq_init(true);
// Switch to hires mode
vic_setmode(VICM_HIRES_MC, Color1, Hires);
spr_init(Color1);
// Black background and border
vic.color_back = VCOL_BLACK;
vic.color_border = VCOL_BLACK;
// Init bitmap
bm_init(&sbm, Hires, 40, 25);
// Init status line
status_init();
// Init cross hair sprite
spr_set(0, true, CrossX + 14, CrossY + 40, 64, 1, false, false, false);
for(char i=0; i<3; i++)
{
spr_set(i + 1, true, 70 + 32 * i, 222, 65, 15, false, false, false);
spr_set(i + 4, true, 214 + 32 * i, 222, 65, 15, false, false, false);
cities[i + 0] = true;
cities[i + 3] = true;
}
// Build the switch to normal IRQ
// Build to multicolor highres at top of screen
rirq_build(&top, 3);
// Change color for ceiling
rirq_delay(&top, 10);
rirq_write(&top, 1, &vic.ctrl1, VIC_CTRL1_BMM | VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3);
rirq_write(&top, 2, &vic.memptr, 0x28);
// place this at the bottom
rirq_set(0, 57, &top);
// Build the switch to normal IRQ
// Switch to text mode for status line and poll joystick at bottom
rirq_build(&bottom, 3);
rirq_write(&bottom, 0, &vic.memptr, 0x27);
rirq_write(&bottom, 1, &vic.ctrl1, VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3);
// Change color for ceiling
rirq_call(&bottom, 2, joy_interrupt);
// place this at the bottom
rirq_set(1, 250, &bottom);
// sort the raster IRQs
@ -393,42 +851,12 @@ int main(void)
// start raster IRQ processing
rirq_start();
bool launched = false;
// start game state machine
game_state(GS_READY);
for(;;)
{
if (CrossP)
{
int sx = 160;
if (CrossX < 120)
sx = 24;
else if (CrossX > 200)
sx = 296
missile_start(sx, 184, CrossX, CrossY);
CrossP = false;
}
if (!(rand() & 63))
{
char ci;
do {
ci = rand() & 7;
} while (ci >= 6 || !cities[ci]);
int cx;
if (ci < 3)
cx = 58 + 32 * ci;
else
cx = 202 + 32 * (ci - 3);
icbm_start((rand() & 0xff) + 32, 0, cx, 184);
}
for(char i=0; i<4; i++)
missile_animate();
icbm_animate();
explosion_animate();
vic_waitFrame();
game_loop();
rirq_wait();
}
return 0;

View File

@ -233,8 +233,8 @@ void game_loop(void)
break;
case GS_PLAYING:
// Check player input on every frame
joy_poll(1);
snake_control(&TheGame.snake, joyx[1], joyy[1]);
joy_poll(0);
snake_control(&TheGame.snake, joyx[0], joyy[0]);
if (!--TheGame.count)
{

Binary file not shown.

View File

@ -388,8 +388,8 @@ int main(void)
for(;;)
{
// Check the joystick
joy_poll(1);
if (joyx[1] == 1)
joy_poll(0);
if (joyx[0] == 1)
{
// Move to the right
if (gridX < 24)
@ -399,7 +399,7 @@ int main(void)
scroll_left();
}
}
else if (joyx[1] == -1)
else if (joyx[0] == -1)
{
// Move to the left
if (gridX > 0)
@ -409,7 +409,7 @@ int main(void)
scroll_right();
}
}
else if (joyy[1] == 1)
else if (joyy[0] == 1)
{
// Move down
if (gridY < 39)
@ -419,7 +419,7 @@ int main(void)
scroll_up();
}
}
else if (joyy[1] == -1)
else if (joyy[0] == -1)
{
// Move up
if (gridY > 0)

View File

@ -33,11 +33,11 @@ int main(void)
for(;;)
{
// Poll joytick
joy_poll(1);
joy_poll(0);
// Change position according to joystick
spx += joyx[1];
spy += joyy[1];
spx += joyx[0];
spy += joyy[0];
// Move sprites on screen
spr_move(0, spx, spy);