ledHeadWii/source/game/SpaceAlert.c

525 lines
12 KiB
C
Raw Normal View History

2020-03-03 18:49:42 +01:00
/*
LEDhead
Copyright 2001, Peter Hirschberg
Author: Peter Hirschberg
The current version of this SOURCE CODE as well as the official
versions of the LEDHEAD APPLICATION are available from my website
at: http://www.peterhirschberg.com
Based on the handheld electronic games by Mattel Electronics.
All trademarks copyrighted by their respective owners. This
program is not affiliated or endorsed by Mattel Electronics.
License agreement:
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program (license.txt); if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Email : peter@peterhirschberg.com
Website : http://www.peterhirschberg.com
*/
#include "SpaceAlert.h"
#include "Games.h"
// constants
#define TIME_ATTACKWAVE 20
#define TIME_SHOWHIT 10
#define RAIDER_COUNT 20
//#define MISSILE_LIMIT (-10)
#define MISSILE_LIMIT (-7)
typedef int BLIP;
static BLIP Blips[SPACEALERT_BLIP_COLUMNS][SPACEALERT_BLIP_ROWS];
typedef struct tagRaider {
int nColumn;
int nRow;
int nSlow;
BOOL bAttacking;
BOOL bLeftColumn;
BOOL bMidColumn;
BOOL bRightColumn;
}RAIDER;
static RAIDER sRaiderA, sRaiderB;
// game variables
static int nColumnSelector;
static int nCurrentMissileRow;
static int nIndexAttackWave;
static int nIndexAttackFrame;
static int nTimerAttackWave;
static int nRaiderCount;
static int nPoints;
static BOOL bGameOver;
static BOOL bWin;
static BOOL bInFrame = FALSE;
static BOOL bPower;
static void InitGame();
static void InitAttack();
static void DoMissileUpdate();
static void DoRaidersUpdate();
static void DoHitTest();
static void UpdateBlips();
static void PaintGame();
BOOL SpaceAlert_GetPower()
{
return (bPower ? TRUE : FALSE);
}
void SpaceAlert_PowerOn()
{
InitGame();
bPower = TRUE;
}
void SpaceAlert_PowerOff()
{
bPower = FALSE;
SpaceAlert_StopSound();
SpaceAlert_StopRaiderSound();
}
void InitGame()
{
nColumnSelector = 1;
nCurrentMissileRow = SPACEALERT_BLIP_ROWS - 1;
nRaiderCount = RAIDER_COUNT;
nIndexAttackWave = 0;
nIndexAttackFrame = 0;
nTimerAttackWave = 1;
nPoints = 0;
bGameOver = FALSE;
bWin = TRUE;
sRaiderA.bAttacking = FALSE;
}
void SpaceAlert_Run(int tu)
{
int x, y;
// prevent reentrancy
if (bInFrame){ return; }
bInFrame = TRUE;
// init the blips field
for (y = 0; y < SPACEALERT_BLIP_ROWS; y++){
for (x = 0; x < SPACEALERT_BLIP_COLUMNS; x++){
Blips[x][y] = BLIP_OFF;
}
}
if (bPower){
if (bGameOver){
// handle game over
static BOOL bFlash = 0;
if (bWin){
// win
Blips[0][SPACEALERT_BLIP_ROWS-2] = BLIP_BRIGHT;
Blips[1][SPACEALERT_BLIP_ROWS-2] = BLIP_BRIGHT;
Blips[2][SPACEALERT_BLIP_ROWS-2] = BLIP_BRIGHT;
} else {
// lose
if (bFlash){
bFlash = FALSE;
Blips[1][SPACEALERT_BLIP_ROWS-1] = BLIP_BRIGHT;
} else {
bFlash = TRUE;
}
}
// allow stick and fire button to be moved
SpaceAlert_GetInputFIRE(NULL);
SpaceAlert_GetInputSTICK();
// draw the frame
PaintGame();
bInFrame = FALSE;
return;
}
} else {
// allow stick and fire button to be moved
SpaceAlert_GetInputFIRE(NULL);
SpaceAlert_GetInputSTICK();
SpaceAlert_ClearScreen();
bInFrame = FALSE;
return;
}
// get the current stick position
nColumnSelector = SpaceAlert_GetInputSTICK();
{
DoHitTest();
DoRaidersUpdate();
UpdateBlips();
PaintGame();
DoHitTest();
DoMissileUpdate();
}
bInFrame = FALSE;
}
void DoMissileUpdate()
{
// update the missiles
if (SpaceAlert_GetInputFIRE(NULL))
{
// fire
if (nCurrentMissileRow == (SPACEALERT_BLIP_ROWS - 1))
{
SpaceAlert_PlaySound(SPACEALERT_SOUND_FIRE, PLAYSOUNDFLAGS_ASYNC | PLAYSOUNDFLAGS_PRIORITY);
--nCurrentMissileRow;
return;
}
}
// move any active missiles
if (nCurrentMissileRow < (SPACEALERT_BLIP_ROWS - 1))
{
if (nCurrentMissileRow >= MISSILE_LIMIT){
// move missile up the screen
--nCurrentMissileRow;
} else {
// missile has hit its limit offscreen -- show new missile
nCurrentMissileRow = SPACEALERT_BLIP_ROWS - 1;
}
}
}
void DoRaidersUpdate()
{
// update the attack
if (nTimerAttackWave > 0){
--nTimerAttackWave;
if (nTimerAttackWave == 0){
// start a new attack wave
nIndexAttackWave = 0;
InitAttack();
}
} else {
nTimerAttackWave = 0;
}
// handle the raiders
for (int i=0; i<2; i++){
RAIDER *sRaiderP = i ? &sRaiderB : &sRaiderA;
RAIDER *sOtherRaiderP = i ? &sRaiderA : &sRaiderB;
if (sRaiderP->bAttacking){
// raider is attacking
if (sRaiderP->nSlow){
// handle the slow raiders
if (sRaiderP->nSlow == 1){
sRaiderP->nSlow = 2;
} else {
sRaiderP->nSlow = 1;
}
}
if (sRaiderP->nSlow <= 1){
// advance the raider
sRaiderP->nRow++;
if (sRaiderP->nRow >= SPACEALERT_BLIP_ROWS){
// miss
sRaiderP->bAttacking = FALSE;
--nRaiderCount;
if (sRaiderP->nColumn == 1){
// battlestart hit -- lose
// draw a blank screen during the tune
sRaiderA.bAttacking = FALSE;
sRaiderB.bAttacking = FALSE;
nCurrentMissileRow = -1;
SpaceAlert_ClearScreen();
SpaceAlert_StopRaiderSound();
SpaceAlert_PlaySound(SPACEALERT_SOUND_LOSE, PLAYSOUNDFLAGS_PRIORITY);
bGameOver = TRUE;
bWin = FALSE;
return;
}
else if (nRaiderCount <= 0){
// no more raiders -- win
// draw a blank screen during the tune
sRaiderA.bAttacking = FALSE;
sRaiderB.bAttacking = FALSE;
nCurrentMissileRow = -1;
SpaceAlert_ClearScreen();
SpaceAlert_StopRaiderSound();
SpaceAlert_PlaySound(SPACEALERT_SOUND_WIN, PLAYSOUNDFLAGS_PRIORITY);
bGameOver = TRUE;
bWin = TRUE;
return;
} else {
// set up next wave
nTimerAttackWave = TIME_ATTACKWAVE;
}
}
// change lanes randomly (don't change on last rows)
if (sRaiderP->nRow < SPACEALERT_BLIP_ROWS - 2){
if (Platform_Random(7) == 0){
// can only be in each column once per attack
switch(sRaiderP->nColumn){
case 0:
sRaiderP->bLeftColumn = TRUE;
if ((!sRaiderP->bMidColumn)
&& (sOtherRaiderP->nColumn != 1)
&& (sRaiderP->nRow != sOtherRaiderP->nRow))
{
sRaiderP->nColumn = 1;
}
break;
case 1:
sRaiderP->bMidColumn = TRUE;
if (Platform_Random(2))
{
if ((!sRaiderP->bLeftColumn)
&& (sOtherRaiderP->nColumn != 0)
&& (sRaiderP->nRow != sOtherRaiderP->nRow))
{
sRaiderP->nColumn = 0;
}
} else {
if ((!sRaiderP->bRightColumn)
&& (sOtherRaiderP->nColumn != 2)
&& (sRaiderP->nRow != sOtherRaiderP->nRow))
{
sRaiderP->nColumn = 2;
}
}
break;
case 2:
sRaiderP->bRightColumn = TRUE;
if ((!sRaiderP->bMidColumn)
&& (sOtherRaiderP->nColumn != 1)
&& (sRaiderP->nRow != sOtherRaiderP->nRow))
{
sRaiderP->nColumn = 1;
}
break;
}
}
}
}
}
}
// enable/disable raiders sound
if ((sRaiderA.bAttacking && (sRaiderA.nRow >= 0) && (sRaiderA.nRow < SPACEALERT_BLIP_ROWS))
|| (sRaiderB.bAttacking && (sRaiderB.nRow >= 0) && (sRaiderB.nRow < SPACEALERT_BLIP_ROWS))){
SpaceAlert_PlayRaiderSound();
} else {
SpaceAlert_StopRaiderSound();
}
}
void DoHitTest()
{
if (nCurrentMissileRow < 0)
{
// missile is off screen
return;
}
for (int i=0; i<2; i++){
RAIDER *sRaiderP = i ? &sRaiderB : &sRaiderA;
if (sRaiderP->bAttacking){
if (((nCurrentMissileRow) == sRaiderP->nRow)
&& (nColumnSelector == sRaiderP->nColumn)){
if ((nCurrentMissileRow == (SPACEALERT_BLIP_ROWS-1))
&& (nColumnSelector == 1)){
// don't count 0 point hits to center column if not fired
} else {
// hit a raider
sRaiderP->bAttacking = FALSE;
UpdateBlips();
// draw the blips field (minus the score)
{
SpaceAlert_ClearScreen();
Platform_StartDraw();
for (int y = 0; y < SPACEALERT_BLIP_ROWS; y++){
for (int x = 0; x < SPACEALERT_BLIP_COLUMNS; x++){
SpaceAlert_DrawBlip(Blips[x][y], x, y);
}
}
Platform_EndDraw();
}
SpaceAlert_PlaySound(SPACEALERT_SOUND_HIT, PLAYSOUNDFLAGS_PRIORITY);
// add the score
nPoints += (SPACEALERT_BLIP_ROWS-1) - nCurrentMissileRow;
// recharge the missile
nCurrentMissileRow = SPACEALERT_BLIP_ROWS - 1;
--nRaiderCount;
if (nRaiderCount <= 0){
// no more raiders -- win
// draw a blank screen during the tune
sRaiderA.bAttacking = FALSE;
sRaiderB.bAttacking = FALSE;
nCurrentMissileRow = -1;
SpaceAlert_ClearScreen();
SpaceAlert_StopRaiderSound();
SpaceAlert_PlaySound(SPACEALERT_SOUND_WIN, PLAYSOUNDFLAGS_PRIORITY);
bGameOver = TRUE;
bWin = TRUE;
return;
} else {
// set up next wave
nTimerAttackWave = TIME_ATTACKWAVE - ((Platform_Random(4) == 0) ? (TIME_ATTACKWAVE / 2) : 0);
}
}
}
}
}
}
void UpdateBlips()
{
// draw the raider blips
for (int i=0; i<2; i++){
RAIDER *sRaiderP = i ? &sRaiderB : &sRaiderA;
if (sRaiderP->bAttacking){
if ((sRaiderP->nColumn >= 0) && (sRaiderP->nColumn < SPACEALERT_BLIP_COLUMNS)
&& (sRaiderP->nRow >= 0) && (sRaiderP->nRow < SPACEALERT_BLIP_ROWS)){
Blips[sRaiderP->nColumn][sRaiderP->nRow] = BLIP_DIM;
}
}
}
// draw the player missile blip
if ((nCurrentMissileRow <= (SPACEALERT_BLIP_ROWS - 1))
&& (nCurrentMissileRow >= 0)){
Blips[nColumnSelector][nCurrentMissileRow] = BLIP_BRIGHT;
}
}
void PaintGame()
{
Platform_StartDraw();
// draw the blips field
for (int y = 0; y < SPACEALERT_BLIP_ROWS; y++){
for (int x = 0; x < SPACEALERT_BLIP_COLUMNS; x++){
SpaceAlert_DrawBlip(Blips[x][y], x, y);
}
}
// draw the score
if (bPower){
SpaceAlert_DrawScore(nPoints);
}
Platform_EndDraw();
}
void InitAttack()
{
sRaiderA.nRow = -2;
sRaiderA.nSlow = Platform_Random(3);
sRaiderA.nColumn = Platform_Random(3);
sRaiderA.bAttacking = TRUE;
sRaiderA.bLeftColumn = FALSE;
sRaiderA.bMidColumn = FALSE;
sRaiderA.bRightColumn = FALSE;
if ((Platform_Random(2) == 0) && (nRaiderCount > 1)){
// dual attack
if (Platform_Random(3) == 0){
// stagger the attack
sRaiderB.nRow = -(Platform_Random(5) + 2);
sRaiderB.nSlow = 1;
} else {
// attack in parallel
sRaiderB.nRow = -2;
sRaiderB.nSlow = Platform_Random(3);
}
// make sure the raiders don't start on the same column
do {
sRaiderB.nColumn = Platform_Random(3);
} while (sRaiderB.nColumn == sRaiderA.nColumn);
sRaiderB.bAttacking = TRUE;
sRaiderB.bLeftColumn = FALSE;
sRaiderB.bMidColumn = FALSE;
sRaiderB.bRightColumn = FALSE;
}
}