/* 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 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 }; static 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; } static void DrawBlips() { int x, y, nBright; static int nBlinkTimer = 0; static BOOL bBlink = FALSE; for (int i=0; i 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; } // FINITE STATE MACHINE STUFF static void fsmFormation() { bDisplayTime = FALSE; bDisplayScore = FALSE; bDisplayYard = FALSE; bDisplayDown = FALSE; bDisplayBlips = FALSE; if (Football_GetInputSCORE(NULL)){ bDisplayScore = TRUE; bDisplayTime = TRUE; } else if (Football_GetInputSTATUS(NULL)){ bDisplayYard = TRUE; bDisplayDown = TRUE; } else { bDisplayBlips = TRUE; } if (bGotFirstDown){ nFirstDownYard = nCurrentYardline - 10; nDown = 0; bGotFirstDown = FALSE; } // set up the players in formation SETPLAYER(ball, nCurrentYardline+0, 8, 1, BLIP_BRIGHT); SETPLAYER(player[0], 0, 5, 0, BLIP_DIM); SETPLAYER(player[1], 0, 5, 1, BLIP_DIM); SETPLAYER(player[2], 0, 5, 2, BLIP_DIM); SETPLAYER(player[3], 0, 0, 1, BLIP_DIM); SETPLAYER(player[4], 0, 3, 1, BLIP_DIM); // wait for kickoff or movement, then start play if (Football_GetInputKICK(NULL) && (nDown == 3)){ // if 4th down player can attempt fieldgoal/punt // calculate where ball will land int nDistance = Platform_Random(64) + 1; if ((ball.nYard - nDistance) < 0){ // make it a field goal if (bHomeTeam){ nHScore += 3; } else { nVScore += 3; } Football_ClearScreen(); Football_PlaySound(FOOTBALL_SOUND_SCORE, PLAYSOUNDFLAGS_PRIORITY); // give the ball to the other team bHomeTeam = !bHomeTeam; PlatformSetInput(bHomeTeam); nDown = 0; ball.nYard = 20; nCurrentYardline = ball.nYard; nFirstDownYard = nCurrentYardline - 10; fsm = FSM_FORMATION; return; } else { // make it a punt // give the ball to the other team bHomeTeam = !bHomeTeam; PlatformSetInput(bHomeTeam); nDown = 0; ball.nYard = MAX_YARD - (ball.nYard - nDistance); nCurrentYardline = ball.nYard; nFirstDownYard = ball.nYard - 10; Football_ClearScreen(); Football_PlaySound(FOOTBALL_SOUND_ENDPOSSESSION, PLAYSOUNDFLAGS_PRIORITY); fsm = FSM_FORMATION; return; } } else if (Football_TestForMovement()) { Platform_IsNewSecond(); fsm = FSM_INPLAY; fsmInPlay(); return; } } static void fsmInPlay() { bDisplayTime = FALSE; bDisplayScore = FALSE; bDisplayYard = FALSE; bDisplayDown = FALSE; bDisplayBlips = TRUE; BOOL bChange; if (Football_GetInputRUN(&bChange)) { if (bChange) { MOVEPLAYERDOWNFIELD(ball); } } else if (Football_GetInputUP(&bChange)) { if (bChange) { if (ball.nLane){ MOVEPLAYERUP(ball); } } } else if (Football_GetInputDOWN(&bChange)) { if (bChange) { if (ball.nLane < 2){ MOVEPLAYERDOWN(ball); } } } int nYardsToGo = ball.nYard - nFirstDownYard; // 1st down if (nYardsToGo <= 0){ if (!bGotFirstDown){ bGotFirstDown = TRUE; } } else { bGotFirstDown = FALSE; } // check for touchdown if (ball.nYard < 0){ // touchdown!! if (bHomeTeam){ nHScore += 7; } else { nVScore += 7; } Football_ClearScreen(); Football_PlaySound(FOOTBALL_SOUND_SCORE, PLAYSOUNDFLAGS_PRIORITY); Platform_IsNewSecond(); bHomeTeam = !bHomeTeam; PlatformSetInput(bHomeTeam); ball.nYard = 100 - 20; nCurrentYardline = ball.nYard; nFirstDownYard = nCurrentYardline - 10; nDown = 0; fsm = FSM_PLAYENDED; return; } // check for collisions if (ISDEFENSE(ball.nColumn, ball.nLane)){ // tackled! Football_ClearScreen(); int i = GETPLAYERAT(ball.nColumn, ball.nLane); if (i != -1){ SETPLAYERBRIGHTNESS(player[i], BLIP_DIMBLINK); } UNMOVEPLAYER(ball); 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; } // 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; }