/* 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 "Football.h" #include "Games.h" // constants #define NUM_DEFENSEPLAYERS 5 #define MAX_YARD 100 typedef int BLIP; static BLIP Blips[FOOTBALL_BLIP_COLUMNS][FOOTBALL_BLIP_ROWS]; // game variables static BOOL bHomeTeam; static BOOL bInFrame = FALSE; static BOOL bPower; static BOOL bPro2 = FALSE; static BOOL bGotFirstDown; static BOOL bDisplayScore; static BOOL bDisplayTime; static BOOL bDisplayYard; static BOOL bDisplayDown; static BOOL bDisplayBlips; static int nHScore; static int nVScore; static float fGameTime; static int nFirstDownYard; static int nDown; static int nQuarter; static int nCurrentYardline; typedef struct PLAYER { int nYard; int nColumn; int nLane; int nBright; int nColumnOld; int nLaneOld; int nYardOld; }PLAYER; static PLAYER ball; static PLAYER player[NUM_DEFENSEPLAYERS]; // macros for dealing with the players #define TRANSLATE_COLUMN(x) \ (!bHomeTeam ? x : (FOOTBALL_BLIP_COLUMNS - 1) - x) #define UNTRANSLATE_COLUMN(x) \ (bHomeTeam ? x : (FOOTBALL_BLIP_COLUMNS - 1) - x) #define TRANSLATE_YARD(x) \ (!bHomeTeam ? x : (MAX_YARD - x)) #define SETPLAYERBRIGHTNESS(p,b) { \ p.nBright = b; \ } #define NOTECURRENTPLAYERPOSITION(p) { \ p.nColumnOld = p.nColumn; \ p.nLaneOld = p.nLane; \ p.nYardOld = p.nYard; \ } #define SETPLAYER(p,a,x,y,b) { \ NOTECURRENTPLAYERPOSITION(p); \ p.nYard = a; \ p.nColumn = TRANSLATE_COLUMN(x); \ p.nLane = y; \ p.nBright = b; \ } #define UNSETPLAYER(p) { \ NOTECURRENTPLAYERPOSITION(p); \ p.nYard = -1; \ p.nColumn = -1; \ p.nLane = -1; \ p.nBright = BLIP_OFF; \ } #define MOVEPLAYERUP(p) { \ NOTECURRENTPLAYERPOSITION(p); \ p.nLane--; \ } #define MOVEPLAYERDOWN(p) { \ NOTECURRENTPLAYERPOSITION(p); \ p.nLane++; \ } #define MOVEPLAYERLEFT(p) { \ NOTECURRENTPLAYERPOSITION(p); \ p.nColumn--; \ if (p.nColumn < 0){ \ p.nColumn = FOOTBALL_BLIP_COLUMNS-1; \ } \ if (!bHomeTeam){ \ p.nYard--; \ } else { \ p.nYard++; \ } \ } #define MOVEPLAYERRIGHT(p) { \ NOTECURRENTPLAYERPOSITION(p); \ p.nColumn++; \ if (p.nColumn >= FOOTBALL_BLIP_COLUMNS){ \ p.nColumn = 0; \ } \ if (!bHomeTeam){ \ p.nYard++; \ } else { \ p.nYard--; \ } \ } #define MOVEPLAYERDOWNFIELD(p) { \ NOTECURRENTPLAYERPOSITION(p); \ p.nYard--; \ if (bHomeTeam){ \ p.nColumn++; \ if (p.nColumn >= FOOTBALL_BLIP_COLUMNS){ \ p.nColumn = 0; \ } \ } else { \ p.nColumn--; \ if (p.nColumn < 0){ \ p.nColumn = FOOTBALL_BLIP_COLUMNS-1; \ } \ } \ } #define MOVEPLAYERUPFIELD(p) { \ NOTECURRENTPLAYERPOSITION(p); \ p.nYard++; \ if (!bHomeTeam){ \ p.nColumn++; \ if (p.nColumn >= FOOTBALL_BLIP_COLUMNS){ \ p.nColumn = 0; \ } \ } else { \ p.nColumn--; \ if (p.nColumn < 0){ \ p.nColumn = FOOTBALL_BLIP_COLUMNS-1; \ } \ } \ } static BOOL ISBALL(int x, int y); static BOOL ISBALL(int x, int y) { if ((ball.nColumn == x) && (ball.nLane == y) && (ball.nBright)){ return TRUE; } return FALSE; } static BOOL ISDEFENSE(int x, int y); static BOOL ISDEFENSE(int x, int y) { for (int i=0; i FOOTBALL_BLIP_COLUMNS-1) || (p.nColumn < 0)) // finite state machine stuff static void fsmFormation(); static void fsmInPlay(); static void fsmPlayEnded(); static void fsmGameOver(); static enum FSM { FSM_FORMATION, FSM_INPLAY, FSM_PLAYENDED, FSM_GAMEOVER }fsm; typedef void (*FSMFCN)(); static FSMFCN fsmfcn[] = { fsmFormation, fsmInPlay, fsmPlayEnded, fsmGameOver }; // proto's static void InitGame(); static void DrawBlips(); static void EraseBlips(); BOOL Football_GetPower() { return (bPower ? TRUE : FALSE); } void Football_PowerOn() { InitGame(); bPower = TRUE; } void Football_PowerOff() { bPower = FALSE; Football_StopSound(); } void Football_SetSkill(int i){ if (i == 0){ bPro2 = FALSE; } else { bPro2 = TRUE; } } int Football_GetSkill(){ return bPro2 ? 1 : 0; } void InitGame() { bHomeTeam = TRUE; PlatformSetInput(bHomeTeam); nHScore = 0; nVScore = 0; fGameTime = 15.0; nDown = 0; nQuarter = 0; nCurrentYardline = 100 - 20; nFirstDownYard = nCurrentYardline - 10; bDisplayScore = FALSE; bDisplayTime = FALSE; bDisplayYard = FALSE; bDisplayDown = FALSE; bDisplayBlips = TRUE; fsm = FSM_FORMATION; } void Football_Run(int tu) { int x, y; // prevent reentrancy if (bInFrame){ return; } bInFrame = TRUE; // init the blips field for (y = 0; y < FOOTBALL_BLIP_ROWS; y++){ for (x = 0; x < FOOTBALL_BLIP_COLUMNS; x++){ Blips[x][y] = BLIP_OFF; } } if (!bPower){ Football_ClearScreen(); bInFrame = FALSE; return; } Platform_StartDraw(); // run the game (fsmfcn[fsm])(); // update the display if (bDisplayBlips){ DrawBlips(); } else { EraseBlips(); } if (bDisplayScore){ Football_DrawScores(nHScore, nVScore); } if (bDisplayYard){ Football_DrawYard(TRANSLATE_YARD(nCurrentYardline)); } else if (bDisplayTime){ Football_DrawTime(fGameTime); } if (bDisplayDown){ int nYardsToGo = nCurrentYardline - nFirstDownYard; if (nYardsToGo > ball.nYard){ // don't show yards to go if more than distance to goal Football_DrawDown(nDown, -1); } else { Football_DrawDown(nDown, nYardsToGo); } } if (!bDisplayDown && !bDisplayScore){ Football_DrawScores(-1, -1); } if (!bDisplayTime && !bDisplayYard){ Football_DrawTime(-1); } Platform_EndDraw(); bInFrame = FALSE; } void DrawBlips() { int x, y, nBright; static int nBlinkTimer = 0; static BOOL bBlink = FALSE; for (int i=0; i= 4) && (!bGotFirstDown)){ // give the ball to the other team bHomeTeam = !bHomeTeam; PlatformSetInput(bHomeTeam); nDown = 0; ball.nYard = MAX_YARD - ball.nYard; nFirstDownYard = ball.nYard - 10; Football_PlaySound(FOOTBALL_SOUND_ENDPOSSESSION, PLAYSOUNDFLAGS_PRIORITY); fsm = FSM_PLAYENDED; return; } else { Football_PlaySound(FOOTBALL_SOUND_ENDPLAY, PLAYSOUNDFLAGS_PRIORITY); fsm = FSM_PLAYENDED; return; } return; } // move the defense randomly towards the ball if (Platform_Random(100) < ((bPro2) ? 20 : 10)){ int i = Platform_Random(NUM_DEFENSEPLAYERS); if (player[i].nBright){ // pick horizontal or vertical movement toward the ball if (Platform_Random(2) == 0){ if (player[i].nColumn < ball.nColumn){ if (!ISDEFENSE(player[i].nColumn+1, player[i].nLane)){ NOTECURRENTPLAYERPOSITION(player[i]); player[i].nColumn++; } } else if (player[i].nColumn > ball.nColumn){ if (!ISDEFENSE(player[i].nColumn-1, player[i].nLane)){ NOTECURRENTPLAYERPOSITION(player[i]); player[i].nColumn--; } } } else { if (player[i].nLane < ball.nLane){ if (!ISDEFENSE(player[i].nColumn, player[i].nLane+1)){ NOTECURRENTPLAYERPOSITION(player[i]); player[i].nLane++; } } else if (player[i].nLane > ball.nLane){ if (!ISDEFENSE(player[i].nColumn, player[i].nLane-1)){ NOTECURRENTPLAYERPOSITION(player[i]); player[i].nLane--; } } } } } // check for collisions again if (ISDEFENSE(ball.nColumn, ball.nLane)){ // tackled! Football_ClearScreen(); int i = GETPLAYERAT(ball.nColumn, ball.nLane); if (i != -1){ UNMOVEPLAYER(player[i]); SETPLAYERBRIGHTNESS(player[i], BLIP_DIMBLINK); } if ((++nDown >= 4) && (!bGotFirstDown)){ // give the ball to the other team bHomeTeam = !bHomeTeam; PlatformSetInput(bHomeTeam); nDown = 0; ball.nYard = MAX_YARD - ball.nYard; nFirstDownYard = ball.nYard - 10; Football_PlaySound(FOOTBALL_SOUND_ENDPOSSESSION, PLAYSOUNDFLAGS_PRIORITY); fsm = FSM_PLAYENDED; return; } else { Football_PlaySound(FOOTBALL_SOUND_ENDPLAY, PLAYSOUNDFLAGS_PRIORITY); fsm = FSM_PLAYENDED; return; } return; } // count down the clock if (Platform_IsNewSecond()){ if (fGameTime > 0.0){ fGameTime -= (float).1; if (fGameTime > 0.0){ Football_PlaySound(FOOTBALL_SOUND_TICK, PLAYSOUNDFLAGS_ASYNC); } else { // end of quarter! fGameTime = 0.0; } } } } static void fsmPlayEnded() { bDisplayTime = FALSE; bDisplayScore = FALSE; bDisplayYard = FALSE; bDisplayDown = FALSE; bDisplayBlips = TRUE; BOOL bEndOfQuarter = (fGameTime < 0.1) ? TRUE : FALSE; // check for game over if (bEndOfQuarter && (nQuarter == 3)){ fGameTime = 0.0; bDisplayBlips = FALSE; fsm = FSM_GAMEOVER; return; } // display '00' if clock ran out if (bEndOfQuarter){ fGameTime = 0.0; bDisplayTime = TRUE; bDisplayBlips = FALSE; } if ((Football_GetInputSCORE(NULL) && !bEndOfQuarter) // end of quarter must press STATUS to set up next play || (Football_GetInputSTATUS(NULL))){ Football_StopSound(); // if clock ran down in previous play if (bEndOfQuarter){ ++nQuarter; if (nQuarter == 2){ // halftime - force kickoff // after halftime, visitor team gets possession bHomeTeam = FALSE; PlatformSetInput(bHomeTeam); ball.nYard = 100 - 20; nCurrentYardline = ball.nYard; nFirstDownYard = nCurrentYardline - 10; nDown = 0; fGameTime = 15.0; fsm = FSM_FORMATION; return; } else { fGameTime = 15.0; } } nCurrentYardline = ball.nYard; fsm = FSM_FORMATION; // fsmFormation(); return; } } static void fsmGameOver() { bDisplayTime = TRUE; bDisplayScore = TRUE; bDisplayYard = FALSE; bDisplayDown = FALSE; bDisplayBlips = FALSE; }