ledHeadWii/source/game/AutoRace.c

453 lines
9.5 KiB
C
Raw Permalink 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 "AutoRace.h"
#include "Games.h"
// constants
#define TIMER_CARADVANCE 10
typedef int BLIP;
static BLIP Blips[AUTORACE_BLIP_COLUMNS][AUTORACE_BLIP_ROWS];
typedef struct ONCOMINGCAR{
int nRow;
int nColumn;
BOOL bActive;
}ONCOMINGCAR;
static ONCOMINGCAR sOncomingCarA, sOncomingCarB;
// game variables
static int nLaneSelector;
static int nGear;
static int nCarRow;
static int nGameTimer;
static int nLaps;
static BOOL bGameEnd; // 1 = loose, 2 = win
static BOOL bInFrame = FALSE;
static BOOL bPower;
static int nCarAdvanceTimer;
static int nGearCounter;
static void fsmPlayStartWait();
static void fsmInPlay();
static void fsmCrash();
static void fsmEndLap();
static void fsmEndGame();
static enum FSM {
FSM_PLAYSTARTWAIT=0,
FSM_INPLAY,
FSM_CRASH,
FSM_ENDLAP,
FSM_ENDGAME
}fsm;
typedef void (*FSMFCN)();
static FSMFCN fsmfcn[] = {
fsmPlayStartWait,
fsmInPlay,
fsmCrash,
fsmEndLap,
fsmEndGame
};
static void InitGame();
static void UpdateOurCar();
static void UpdateOncomingCars();
static void DoHitTest();
static void UpdateBlips();
static void StartSecondCar();
BOOL AutoRace_GetPower()
{
return (bPower ? TRUE : FALSE);
}
void AutoRace_PowerOn()
{
InitGame();
bPower = TRUE;
}
void AutoRace_PowerOff()
{
bPower = FALSE;
AutoRace_StopSound();
AutoRace_StopEngineSound();
}
int AutoRace_GetSkill()
{
return nGear;
}
void AutoRace_SetSkill(int i)
{
if (i > 3) {
i = 3;
}
else if (i < 0) {
i = 0;
}
nGear = i;
}
void InitGame()
{
// set our car's position
nLaneSelector = 1;
nCarRow = AUTORACE_BLIP_ROWS - 1;
// init the other cars
sOncomingCarA.bActive = FALSE;
sOncomingCarB.bActive = FALSE;
nGameTimer = 0;
bGameEnd = 0;
nLaps = 0;
nCarAdvanceTimer = TIMER_CARADVANCE;
nGearCounter = 999;
fsm = FSM_PLAYSTARTWAIT;
}
void AutoRace_Run(int tu)
{
int x, y;
// prevent reentrancy
if (bInFrame){ return; }
bInFrame = TRUE;
// init the blips field
for (y = 0; y < AUTORACE_BLIP_ROWS; y++){
for (x = 0; x < AUTORACE_BLIP_COLUMNS; x++){
Blips[x][y] = BLIP_OFF;
}
}
if (!bPower){
fsm = FSM_PLAYSTARTWAIT;
}
Platform_StartDraw();
(fsmfcn[fsm])();
UpdateBlips();
// draw the blips field
for (int y = 0; y < AUTORACE_BLIP_ROWS; y++){
for (int x = 0; x < AUTORACE_BLIP_COLUMNS; x++){
AutoRace_DrawBlip(Blips[x][y], x, y);
}
}
// draw the score
AutoRace_DrawTime(nGameTimer);
Platform_EndDraw();
bInFrame = FALSE;
}
void fsmInPlay()
{
BOOL bMoveCars = FALSE;
if (Platform_IsNewSecond()){
// update the game timer
++nGameTimer;
if (nGameTimer == 99){
// time's up -- game over!
bGameEnd = 1;
fsm = FSM_ENDGAME;
return;
}
}
// get the current stick position
nLaneSelector = AutoRace_GetInputSTICK();
// check for gear changes
nGear = AutoRace_GetInputGEAR(NULL);
// move the cars at the correct speed per gear selection
AutoRace_PlayEngineSound();
switch(nGear){
case 0:
if (nGearCounter > 3){
nGearCounter = 0;
bMoveCars = TRUE;
}
break;
case 1:
if (nGearCounter > 2){
nGearCounter = 0;
bMoveCars = TRUE;
}
break;
case 2:
if (nGearCounter > 1){
nGearCounter = 0;
bMoveCars = TRUE;
}
break;
case 3:
// move every frame
bMoveCars = TRUE;
nGearCounter = 0;
break;
}
++nGearCounter;
if (bMoveCars){
UpdateOncomingCars();
DoHitTest();
UpdateOurCar();
DoHitTest();
}
}
void UpdateOurCar()
{
if (nCarAdvanceTimer){
--nCarAdvanceTimer;
}
if (nCarAdvanceTimer == 0){
// move our car up
--nCarRow;
if (nCarRow <= 0){
// lap completed!
fsm = FSM_ENDLAP;
}
nCarAdvanceTimer = TIMER_CARADVANCE;
}
}
void UpdateOncomingCars()
{
// move the oncoming cars down
for (int i=0; i<2; i++){
ONCOMINGCAR *pOncomingCar = i ? &sOncomingCarB : &sOncomingCarA;
if (pOncomingCar->bActive){
pOncomingCar->nRow++;
if (pOncomingCar->nRow >= AUTORACE_BLIP_ROWS){
// car has left the screen
pOncomingCar->bActive = FALSE;
}
}
}
// count the number of oncoming cars
// should be at least 1 car at all times
// maximum 2 oncoming cars at any given time
if (!sOncomingCarA.bActive && !sOncomingCarB.bActive){
// no cars are approaching, start at least one
sOncomingCarA.bActive = TRUE;
sOncomingCarA.nRow = -2;
sOncomingCarA.nColumn = Platform_Random(3);
// and perhaps start a second car
if (Platform_Random(5) == 0){
StartSecondCar();
}
}
if (!sOncomingCarA.bActive || !sOncomingCarB.bActive){
// only 1 car is active -- randomly try to start a second car
if (Platform_Random(10) == 0){
StartSecondCar();
}
}
}
void StartSecondCar()
{
if (sOncomingCarA.bActive && sOncomingCarB.bActive){
// both cars are already active
return;
}
ONCOMINGCAR *pNewOncomingCar, *pOtherOncomingCar;
if (sOncomingCarA.bActive){
pNewOncomingCar = &sOncomingCarB;
pOtherOncomingCar = &sOncomingCarA;
} else {
pNewOncomingCar = &sOncomingCarA;
pOtherOncomingCar = &sOncomingCarB;
}
pNewOncomingCar->bActive = TRUE;
pNewOncomingCar->nRow = -2;
pNewOncomingCar->nColumn = Platform_Random(3);
if ((pNewOncomingCar->nColumn == pOtherOncomingCar->nColumn)
&& (pNewOncomingCar->nRow == pOtherOncomingCar->nRow)
&& pOtherOncomingCar->bActive){
// car is on top of other car -- reposition the car behind the other car
pNewOncomingCar->nRow = pNewOncomingCar->nRow-1;
}
}
void DoHitTest()
{
for (int i=0; i<2; i++){
ONCOMINGCAR *pOncomingCar = i ? &sOncomingCarB : &sOncomingCarA;
if (pOncomingCar->bActive){
if ((pOncomingCar->nRow == nCarRow)
&& (pOncomingCar->nColumn == nLaneSelector)){
// hit an oncoming car!
fsm = FSM_CRASH;
if (nCarRow < (AUTORACE_BLIP_ROWS - 1)){
++nCarRow;
}
else {
// if oncoming car is in the last row and hits us
// delete it to keep it from hitting us every frame
// (since our car can't be moved out of the way)
pOncomingCar->bActive = FALSE;
}
}
}
}
}
void UpdateBlips()
{
// draw the oncoming car blips
for (int i=0; i<2; i++){
ONCOMINGCAR *pOncomingCar = i ? &sOncomingCarB : &sOncomingCarA;
if (pOncomingCar->bActive){
if ((pOncomingCar->nColumn >= 0) && (pOncomingCar->nColumn < AUTORACE_BLIP_COLUMNS)
&& (pOncomingCar->nRow >= 0) && (pOncomingCar->nRow < AUTORACE_BLIP_ROWS)){
Blips[pOncomingCar->nColumn][pOncomingCar->nRow] = (fsm == FSM_CRASH) ? BLIP_BRIGHT : BLIP_DIM;
}
else if ((pOncomingCar->nColumn >= 0) && (pOncomingCar->nColumn < AUTORACE_BLIP_COLUMNS)
&& (pOncomingCar->nRow == -1)){
// oncoming car blips display on-screen for an extra frame when they first appear
// (to give you a better chance of avoiding them when our car is near the top)
// put them on-screen even if they are just off-screen
Blips[pOncomingCar->nColumn][0] = (fsm == FSM_CRASH) ? BLIP_BRIGHT : BLIP_DIM;
}
}
}
// draw the player's car blip
if ((nCarRow <= (AUTORACE_BLIP_ROWS - 1))
&& (nCarRow >= 0)){
Blips[nLaneSelector][nCarRow] = BLIP_BRIGHT;
}
}
void fsmPlayStartWait()
{
nLaneSelector = AutoRace_GetInputSTICK();
// check for gear changes
nGear = AutoRace_GetInputGEAR(NULL);
if (bPower){
Blips[nLaneSelector][nCarRow] = BLIP_BRIGHT;
}
else {
AutoRace_ClearScreen();
}
// wait for 1st gear before starting
if (nGear == 0){
Platform_IsNewSecond();
fsm = FSM_INPLAY;
}
}
void fsmCrash()
{
AutoRace_PlaySound(AUTORACE_SOUND_HIT, PLAYSOUNDFLAGS_PRIORITY);
fsm = FSM_INPLAY;
}
void fsmEndLap()
{
// lap completed!
AutoRace_StopEngineSound();
Platform_Pause(300);
nCarRow = AUTORACE_BLIP_ROWS - 1;
++nLaps;
if (nLaps >= 4){
// finished race -- game over!
bGameEnd = 2;
fsm = FSM_ENDGAME;
}
else {
AutoRace_PlayEngineSound();
fsm = FSM_INPLAY;
}
}
void fsmEndGame()
{
AutoRace_ClearScreen();
AutoRace_StopEngineSound();
switch(bGameEnd) {
case 1:
AutoRace_PlaySound(AUTORACE_SOUND_TIME, PLAYSOUNDFLAGS_PRIORITY);
break;
case 2:
AutoRace_PlaySound(AUTORACE_SOUND_WIN, PLAYSOUNDFLAGS_PRIORITY);
break;
default:
break;
}
bGameEnd = 0;
}