/* * LEDhead for Wii * Copyright (C) 2017-2020 Nebiun * * 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. * * 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 3 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. If not, see . */ #include "Soccer2.h" #include "Games.h" // constants #define NUM_DEFENSEPLAYERS 5 #define TIME_STATSDISPLAY 5 #define TIME_BALLMOVE 5 #define GAMETIME 45.9 #define TIME_TICKTIMER 10 #define BALL_LOWKICK (3 + Platform_Random(2)) #define BALL_HIGHKICK (5 + Platform_Random(4)) #define IS_GOALAREA() ((nSector == 1) || (nSector == 9)) #define IS_ATTACKAREA() ((nSector == 1 && bHomeTeam) || (nSector == 9 && !bHomeTeam)) typedef struct { int state; } BLIP; typedef struct { Coord_t player; Coord_t receiver; Coord_t defense[NUM_DEFENSEPLAYERS]; } SCHEME_t; static BLIP Blips[SOCCER2_BLIP_COLUMNS][SOCCER2_BLIP_ROWS]; // debug static int trc_debug; // game variables static BOOL bGameOver; static BOOL bHomeTeam; static BOOL bInFrame = FALSE; static BOOL bPower; static BOOL bBounce = FALSE; static BOOL bTeamMate = FALSE; static BOOL bTeamMateSet = FALSE; static BOOL bKickKey = FALSE; static int nLevel = -1; static int nHScore; static int nVScore; static int fGameTime; static int nHalf; static int nTimerTickTimer; static int nSector; static int nBallDirectionX; static int nBallDirectionY; static int nTimerBallMove; static int nStatsIndex; static int nTimerStatsDisplay; static int nBallStep; static int nBallMax; typedef struct { int x; int y; int side; }BALL_OUT; static BALL_OUT ball_out; typedef struct { int nColumn; int nRow; int nBright; int nColumnOld; int nRowOld; int nMoveNow; }PLAYER; static PLAYER ball; static PLAYER player; static PLAYER receiver; static PLAYER goalkeeper; static PLAYER defense[NUM_DEFENSEPLAYERS]; // macros for dealing with the players #define SETPLAYERBRIGHTNESS(p,b) { \ (p)->nBright = b; \ } #define NOTECURRENTPLAYERPOSITION(p) { \ (p)->nColumnOld = (p)->nColumn; \ (p)->nRowOld = (p)->nRow; \ } #define SETPLAYER(p,x,y,b) { \ NOTECURRENTPLAYERPOSITION(p); \ if (x >= 0) { (p)->nColumn = (x) % SOCCER2_BLIP_COLUMNS; } \ if (y >= 0) { (p)->nRow = (y) % SOCCER2_BLIP_ROWS; } \ if (b >= 0) { (p)->nBright = b; }\ } #define UNSETPLAYER(p) { \ (p)->nBright = BLIP_OFF; \ } #define ISPLAYERMOVED(p) (((p)->nColumn != (p)->nColumnOld) || ((p)->nRow != (p)->nRowOld)) #define UNMOVEPLAYER(p) { \ (p)->nRow = (p)->nRowOld; \ (p)->nColumn = (p)->nColumnOld; \ } #define ISPLAYEROFFSCREEN(p) (!VALID_COLUMN((p)->nColumn) || !VALID_ROW((p)->nRow)) #define ISPLAYERENABLED(p) \ ((p)->nBright) #define ISBALL(x,y) generic_check(&ball, x, y) #define ISPLAYER(x,y) generic_check(&player, x, y) #define ISRECEIVER(x,y) generic_check(&receiver, x, y) #define ISGOALKEEPER(x,y) generic_check(&goalkeeper, x, y) #define SET_BALLOUT(p) { ball_out.x = (p)->nColumnOld; ball_out.y = (p)->nRowOld; ball_out.side = Platform_Random(2); } static BOOL generic_check(PLAYER *p, int x, int y) { if ((p->nColumn == x) && (p->nRow == y) && (p->nBright)){ return TRUE; } return FALSE; } static BOOL ISDEFENSE(int x, int y) { for (int i=0; inRow + 1)) { v = 1; } else if(row == (p->nRow - 1)) { v = -1; } else { v = 0; } c = (flag) ? -1 : 1; if(v != 0 && !ISOCCUPIED(p->nColumn,p->nRow+v)) { rtn = p->nColumn; } else if(VALID_COLUMN(p->nColumn+c) && !ISOCCUPIED(p->nColumn+c,p->nRow+v)) { rtn = p->nColumn+c; } else if(VALID_COLUMN(p->nColumn-c) && !ISOCCUPIED(p->nColumn-c,p->nRow+v)) { rtn = p->nColumn-c; } else { rtn = -1; } return rtn; } */ static int move_tocol(PLAYER *p, int col, int flag) { int v = 0; int r, rtn; if(col == (p->nColumn + 1)) { v = 1; } else if(col == (p->nColumn - 1)) { v = -1; } else { v = 0; } r = (flag) ? -1 : 1; if(v != 0 && !ISOCCUPIED(p->nColumn+v,p->nRow)) { rtn = p->nRow; } else if(VALID_ROW(p->nRow+r) && !ISOCCUPIED(p->nColumn+v,p->nRow+r)) { rtn = p->nRow+r; } else if(VALID_ROW(p->nRow-r) && !ISOCCUPIED(p->nColumn+v,p->nRow-r)) { rtn = p->nRow-r; } else { rtn = -1; } return rtn; } static BOOL move_around(PLAYER *p) { int x, y; int x1, y1; for(x=0; xnColumn) % SOCCER2_BLIP_COLUMNS; for(y=0; ynRow) % SOCCER2_BLIP_ROWS; if(!ISOCCUPIED(x1,y1)) { SETPLAYER(p, x1, y1, -1); return TRUE; } } } return FALSE; } static BOOL set_goalkeeper() { PLAYER *p; int x; if(nSector == 1 && bHomeTeam) x = 0; else if(nSector == 9 && !bHomeTeam) x = 6; else { UNSETPLAYER(&goalkeeper); return FALSE; } if( (p = GETPLAYERAT(x, 2)) != NULL ) { move_around(p); } SETPLAYER(&goalkeeper,x, 2, BLIP_DIM); return TRUE; } // finite state machine stuff #define FSM_RUNNING(x) ((x == FSM_INPLAY) || (x == FSM_SHOOTING) || (x == FSM_GOAL) || (x == FSM_PASSING)) #define next_fsm(x) { if(FSM_RUNNING(x)) {fsm = x; nextFsm = FSM_INVALID; } else { fsm = FSM_WAITSCOREPRESS; nextFsm = x; } } static void fsmKickoff(); static void fsmTurnover(); static void fsmThrowin(); static void fsmGoalKick(); static void fsmCornerKick(); static void fsmInPlay(); static void fsmPassing(); static void fsmShooting(); static void fsmGameOver(); static void fsmWaitScorePress(); static void fsmGoal(); enum FSM { FSM_INVALID = -1, FSM_KICKOFF = 0, FSM_TURNOVER, FSM_THROWIN, FSM_GOALKICK, FSM_CORNERKICK, FSM_INPLAY, FSM_PASSING, FSM_SHOOTING, FSM_GAMEOVER, FSM_WAITSCOREPRESS, FSM_GOAL }; static enum FSM fsm, nextFsm; typedef void (*FSMFCN)(); static FSMFCN fsmfcn[] = { fsmKickoff, fsmTurnover, fsmThrowin, fsmGoalKick, fsmCornerKick, fsmInPlay, fsmPassing, fsmShooting, fsmGameOver, fsmWaitScorePress, fsmGoal }; static int probability(int value) { int rtn; rtn = (Platform_Random(100) <= ((value/4 * (nLevel + 1)) - Platform_Random(4 - nLevel))) ? 1 : 0; return rtn; } static void lost_possession() { bHomeTeam = !bHomeTeam; PlatformSetInput(bHomeTeam); } static void InitGame() { bHomeTeam = FALSE; PlatformSetInput(bHomeTeam); nHScore = 0; nVScore = 0; fGameTime = GAMETIME; nHalf = 0; bGameOver = FALSE; nSector = 5; bTeamMate = FALSE; bTeamMateSet = FALSE; bKickKey = FALSE; next_fsm(FSM_INVALID); } static void DrawBlips() { static BOOL bBlink = FALSE; int x, y, nBright; for (int i=0; iplayer.x, ps->player.y, BLIP_BRIGHT); SETPLAYER(&receiver, ps->receiver.x, ps->receiver.y, BLIP_DIMBLINK); for(i=0; idefense[i].x, ps->defense[i].y, BLIP_DIM); } static void corner_scheme() { static SCHEME_t cl[2] = { { {6,0}, {6,1}, {{4,0}, {4,1}, {3,2}, {4,3}, {4,4}} }, { {0,4}, {0,3}, {{2,0}, {2,1}, {3,2}, {2,3}, {2,4}} } }; static SCHEME_t cr[2] = { { {6,4}, {6,3}, {{4,0}, {4,1}, {3,2}, {4,3}, {4,4}} }, { {0,0}, {0,1}, {{2,0}, {2,1}, {3,2}, {2,3}, {2,4}} } }; SCHEME_t *ps; int i; if(ball_out.x != 0 && ball_out.x != 6) return; if(ball_out.x == 0) { if(ball_out.y < 3) ps = &cr[1]; else if(ball_out.y > 3) ps = &cl[1]; else ps = ball_out.side ? &cr[1] : &cl[1]; } else { /* x == 6 */ if(ball_out.y < 3) ps = &cl[0]; else if(ball_out.y < 3) ps = &cr[0]; else ps = ball_out.side ? &cr[0] : &cl[0]; } UNSETPLAYER(&ball); SETPLAYER(&player, ps->player.x, ps->player.y, BLIP_BRIGHT); SETPLAYER(&receiver, ps->receiver.x, ps->receiver.y, BLIP_DIMBLINK); for(i=0; idefense[i].x, ps->defense[i].y, BLIP_DIM); set_goalkeeper(); } static void throwin_scheme() { static SCHEME_t p[] = { /* player receiver 0 1 2 3 4 */ { {3,4}, {3,3}, {{1,3}, {2,2}, {3,2}, {4,2}, {5,3}} }, { {2,4}, {2,3}, {{0,3}, {1,2}, {2,2}, {3,2}, {4,3}} }, { {1,4}, {1,3}, {{0,2}, {1,2}, {2,2}, {3,3}, {3,4}} }, { {0,4}, {0,3}, {{0,2}, {1,2}, {2,2}, {2,3}, {2,4}} }, { {3,0}, {3,1}, {{1,1}, {2,2}, {3,2}, {4,2}, {5,1}} }, { {2,0}, {2,1}, {{0,1}, {1,2}, {2,2}, {3,2}, {4,1}} }, { {1,0}, {1,1}, {{0,2}, {1,2}, {2,2}, {3,1}, {3,0}} }, { {0,0}, {0,1}, {{0,2}, {1,2}, {2,2}, {2,1}, {2,0}} }, { {6,4}, {6,3}, {{6,2}, {5,2}, {4,2}, {4,3}, {4,4}} }, { {5,4}, {5,3}, {{6,2}, {5,2}, {4,2}, {3,3}, {3,4}} }, { {4,4}, {4,3}, {{6,3}, {5,2}, {4,2}, {3,2}, {2,3}} }, { {6,0}, {6,1}, {{6,2}, {5,2}, {4,2}, {4,1}, {4,0}} }, { {5,0}, {5,1}, {{6,2}, {5,2}, {4,2}, {3,1}, {3,0}} }, { {4,0}, {4,1}, {{6,1}, {5,2}, {4,2}, {3,2}, {2,1}} }, }; int i; UNSETPLAYER(&ball); for(i=0; iplayer.x, ps->player.y, BLIP_BRIGHT); SETPLAYER(&receiver, ps->receiver.x, ps->receiver.y, BLIP_DIMBLINK); for(i=0; idefense[i].x, ps->defense[i].y, BLIP_DIM); } static void turnover_scheme() { int i; UNSETPLAYER(&ball); /* Swap blips specularly to middle line */ SETPLAYER(&player, SOCCER2_BLIP_COLUMNS - 1 - player.nColumn, player.nRow, BLIP_BRIGHT); SETPLAYER(&receiver, SOCCER2_BLIP_COLUMNS - 1 - receiver.nColumn, receiver.nRow, BLIP_DIMBLINK); for(i=0; inColumn != dx) { if(p->nColumn > dx && p->nColumn > 0) x--; else if(p->nColumn < dx && p->nColumn < SOCCER2_BLIP_COLUMNS-1) x++; } if(p->nRow != dy) { if(p->nRow > dy && p->nRow > 0) y--; else if(p->nRow < dy && p->nRow < SOCCER2_BLIP_ROWS-1) y++; } *nx = p->nColumn + x; *ny = p->nRow + y; return TRUE; } static BOOL IS_ALIGNED(const PLAYER *p1, const PLAYER *p2, int *dx, int *dy) { int x, y; x = p2->nColumn - p1->nColumn; y = p2->nRow - p1->nRow; if(dx != NULL) *dx = x; if(dy != NULL) *dy = y; if((x == 0) || (y == 0) || (x == y)) return TRUE; return FALSE; } static int moveCounterInit() { int rtn; switch(nLevel) { case LVL_ROOKIEFAST: rtn = 8 + Platform_Random(12); break; case LVL_PROSLOW: rtn = 4 + Platform_Random(8); break; case LVL_PROFAST: rtn = Platform_Random(4); break; case LVL_ROOKIESLOW: default: rtn = 12 + Platform_Random(16); break; } return rtn; } static enum FSM move_player(enum FSM curFsm) { enum FSM newFsm = FSM_INVALID; BOOL bChange; BOOL moved = FALSE; BOOL newsect = FALSE; int col, row; PLAYER *p; p = (bTeamMate || curFsm == FSM_PASSING) ? &receiver : &player; NOTECURRENTPLAYERPOSITION(p); col = p->nColumn; row = p->nRow; if (Soccer2_GetInputLEFTUP(&bChange)) { if (bChange) { col -= 1; row -= 1; moved = TRUE; } } else if (Soccer2_GetInputLEFTDOWN(&bChange)) { if (bChange) { col -= 1; row += 1; moved = TRUE; } } else if (Soccer2_GetInputRIGHTUP(&bChange)) { if (bChange) { col += 1; row -= 1; moved = TRUE; } } else if (Soccer2_GetInputRIGHTDOWN(&bChange)) { if (bChange) { col += 1; row += 1; moved = TRUE; } } else if (Soccer2_GetInputLEFT(&bChange)) { if (bChange) { col -= 1; moved = TRUE; } } else if (Soccer2_GetInputRIGHT(&bChange)) { if (bChange) { col += 1; moved = TRUE; } } else if (Soccer2_GetInputUP(&bChange)) { if (bChange) { row -= 1; moved = TRUE; } } else if (Soccer2_GetInputDOWN(&bChange)) { if (bChange) { row += 1; moved = TRUE; } } if(moved == FALSE) return newFsm; if(!VALID_COLUMN(col)) { moved = FALSE; /* Moves between sectors only if 'p' is the player and has the ball */ if (!ISPLAYERENABLED(&ball) && (p == &player)) { if(col < 0) { if(nSector == 1) { SET_BALLOUT(p); newFsm = (!bHomeTeam) ? FSM_CORNERKICK : FSM_GOALKICK; lost_possession(); } else if(nSector > 1) { nSector--; newsect = TRUE; col = SOCCER2_BLIP_COLUMNS - 1; moved = TRUE; } } else if(col >= SOCCER2_BLIP_COLUMNS) { if(nSector == 9) { SET_BALLOUT(p); newFsm = (!bHomeTeam) ? FSM_GOALKICK : FSM_CORNERKICK; lost_possession(); } else if(nSector < 9) { nSector++; newsect = TRUE; col = 0; moved = TRUE; } } } } if (!VALID_ROW(row)) { moved = FALSE; /* Invalid move */ } if(moved) { if(newsect) build_newsector(p, col, row); else if(!ISOCCUPIED(col, row) || ISPLAYER(col,row) || ISRECEIVER(col,row)) { SETPLAYER(p, col, row, -1); if(bTeamMate) { bTeamMateSet = TRUE; } } else if(ISBALL(col,row)) { UNSETPLAYER(&ball); Soccer2_PlaySound(SOCCER2_SOUND_BOUNCE, PLAYSOUNDFLAGS_PRIORITY); if(p == &receiver) { swap_player(&player, &receiver); } SETPLAYER(&player, col, row, BLIP_BRIGHT); SETPLAYERBRIGHTNESS(&receiver, BLIP_DIMBLINK); } newFsm = curFsm; } return newFsm; } static BOOL count_down() { // count down the clock if (Platform_IsNewSecond()){ // check for end of quarter if (fGameTime > 0.0){ fGameTime -= (int).1; if (fGameTime < 0.1){ // end of quarter ++nHalf; if (nHalf >= 2) { // game over bGameOver = TRUE; Soccer2_ClearScreen(); Soccer2_PlaySound(SOCCER2_SOUND_ENDGAME, PLAYSOUNDFLAGS_PRIORITY); next_fsm(FSM_GAMEOVER); return FALSE; } bHomeTeam = (nHalf % 2) ? FALSE : TRUE; PlatformSetInput(bHomeTeam); fGameTime = GAMETIME; Soccer2_ClearScreen(); Soccer2_PlaySound(SOCCER2_SOUND_ENDHALF, PLAYSOUNDFLAGS_PRIORITY); next_fsm(FSM_KICKOFF); return FALSE; } } } Soccer2_DrawSect(nSector, bHomeTeam); // make tick sounds if (fGameTime >= .1){ if (--nTimerTickTimer <= 0){ Soccer2_PlaySound(SOCCER2_SOUND_TICK, PLAYSOUNDFLAGS_ASYNC); nTimerTickTimer = TIME_TICKTIMER; } } return TRUE; } static BOOL move_receiver() { int x, y; int dx, dy; if(!ISPLAYERENABLED(&receiver)) { return FALSE; } if(bTeamMate || bTeamMateSet) { return FALSE; } if(IS_ALIGNED(&receiver, &player, &dx, &dy)) { return FALSE; } if(receiver.nMoveNow-- > 0) { return FALSE; } x = receiver.nColumn; y = receiver.nRow; if(!bHomeTeam) { if(dx < 0 && x < (SOCCER2_BLIP_COLUMNS - 1)) x++; else if(x < (SOCCER2_BLIP_COLUMNS - 1)) x++; } else { if(dx > 0 && x > 0) x--; else if(x > 0) x--; } if(ISOCCUPIED(x, y)) { int newy; newy = move_tocol(&receiver, x, Platform_Random(2)); if(newy >= 0) { y = newy; } else { /* Try move only on y axis */ x = receiver.nColumn; newy = move_tocol(&receiver, x, Platform_Random(2)); if(newy >= 0) { y = newy; } } } if(!ISOCCUPIED(x, y)) { SETPLAYER(&receiver, x, y, BLIP_DIMBLINK); receiver.nMoveNow = moveCounterInit(); return TRUE; } receiver.nMoveNow = 0; /* Try to move next time */ return FALSE; } static void initDefense() { int i; for(i=0; i < NUM_DEFENSEPLAYERS; i++) { defense[i].nMoveNow = moveCounterInit(); } } static BOOL passing() { int x, y; if (ISPLAYERENABLED(&ball)) return FALSE; Soccer2_PlaySound((bKickKey) ? SOCCER2_SOUND_LOWKICK : SOCCER2_SOUND_HIGHKICK, PLAYSOUNDFLAGS_PRIORITY); y = player.nRow - receiver.nRow; x = player.nColumn - receiver.nColumn; if(abs(x) == abs(y)) { if(y > 0) nBallDirectionY = -1; else if(y < 0) nBallDirectionY = 1; else nBallDirectionY = 0; if(x > 0) nBallDirectionX = -1; else if(x < 0) nBallDirectionX = 1; else nBallDirectionX = 0; } else if(abs(x) > abs(y)) { nBallDirectionY = 0; if(x > 0) nBallDirectionX = -1; else if(x < 0) nBallDirectionX = 1; } else { nBallDirectionX = 0; if(y > 0) nBallDirectionY = -1; else if(y < 0) nBallDirectionY = 1; } SETPLAYER(&ball, player.nColumn, player.nRow, BLIP_BRIGHT); UNSETPLAYER(&player); nBallStep = 0; nBallMax = (bKickKey) ? BALL_HIGHKICK : BALL_LOWKICK; nTimerBallMove = TIME_BALLMOVE; return TRUE; } static BOOL shooting() { if (ISPLAYERENABLED(&ball)) return FALSE; Soccer2_PlaySound((bKickKey) ? SOCCER2_SOUND_LOWKICK : SOCCER2_SOUND_HIGHKICK, PLAYSOUNDFLAGS_PRIORITY); nBallDirectionX = (!bHomeTeam) ? 1 : -1; if(IS_ATTACKAREA()) { if(player.nRow > 2) nBallDirectionY = -1; else if(player.nRow < 2) nBallDirectionY = 1; else nBallDirectionY = 0; } else { nBallDirectionY = 0; } SETPLAYER(&ball, player.nColumn, player.nRow, BLIP_BRIGHT); UNSETPLAYER(&receiver); SETPLAYERBRIGHTNESS(&player, BLIP_DIMBLINK); nBallStep = 0; nBallMax = (bKickKey) ? BALL_HIGHKICK : BALL_LOWKICK; nTimerBallMove = TIME_BALLMOVE; return TRUE; } static int bounce(enum FSM curFsm) { bBounce = TRUE; /* On sector 1 or 9, on shoot or passage it can be corner kick */ if( IS_ATTACKAREA() ) { if( (curFsm == FSM_SHOOTING) && probability(50) ) { nBallDirectionX = (bHomeTeam) ? -1 : 1; } else if((curFsm == FSM_PASSING) && probability(25)) { nBallDirectionX = (bHomeTeam) ? -1 : 1; } else { nBallDirectionX = (bHomeTeam) ? 1 : -1; } } else { if((curFsm == FSM_PASSING) && probability(15)) nBallDirectionX = (bHomeTeam) ? -1 : 1; else nBallDirectionX = (bHomeTeam) ? 1 : -1; } if(probability(20)) nBallDirectionY = 0; else if(probability(40)) nBallDirectionY = -1; else if(probability(40)) nBallDirectionY = 1; nBallStep = 0; nBallMax = (Platform_Random(2)) ? BALL_HIGHKICK : BALL_LOWKICK; Soccer2_PlaySound(SOCCER2_SOUND_BOUNCE, PLAYSOUNDFLAGS_PRIORITY); SETPLAYERBRIGHTNESS(&player, BLIP_DIMBLINK); SETPLAYERBRIGHTNESS(&receiver, BLIP_DIMBLINK); nTimerBallMove = TIME_BALLMOVE; return 0; } static enum FSM move_defense(enum FSM curFsm) { Coord_t gk = {0, 0}; Coord_t df[NUM_DEFENSEPLAYERS]; enum FSM newFsm = curFsm; int i, nx, ny; gk.x = goalkeeper.nColumn; gk.y = goalkeeper.nRow; for(i=0; i 0 && ball.nRow < 4)) { gk.y = ball.nRow; } } for(i=0; i gk.y) gk.y++; } else if(player.nRow > 0 && player.nRow < 4) { if((!bHomeTeam && player.nColumn == 5) || (bHomeTeam && player.nColumn == 1)) { gk.x = player.nColumn; gk.y = player.nRow; } else { gk.x = goalkeeper.nColumn; gk.y = player.nRow; } } else { if(player.nRow == 2) gk.y = 2; else gk.y = 2 + (player.nRow < 2) ? -1 : 1; } } } for(i=0; i= nBallMax)) { SETPLAYERBRIGHTNESS(&player, BLIP_DIMBLINK); SETPLAYERBRIGHTNESS(&receiver, BLIP_OFF); bBounce = FALSE; return FSM_INPLAY; } // move the ball NOTECURRENTPLAYERPOSITION(&ball); ball.nColumn += nBallDirectionX; ball.nRow += nBallDirectionY; nBallStep++; /* Check if is off-screen */ if (ISPLAYEROFFSCREEN(&ball)) { /* Choose throwin or goalkick */ if(!VALID_COLUMN(ball.nColumn)) { if(nSector > 1 && nBallDirectionX < 0) { if(nBallDirectionY == 0) { nSector--; build_newsector(&ball, SOCCER2_BLIP_COLUMNS - 1, ball.nRow); } else { nBallStep = nBallMax; ball.nColumn = 0; ball.nRow = ball.nRowOld; } } else if(nSector < 9 && nBallDirectionX > 0) { if(nBallDirectionY == 0) { nSector++; build_newsector(&ball, 0, ball.nRow); } else { nBallStep = nBallMax; ball.nColumn = 6; ball.nRow = ball.nRowOld; } } else { SET_BALLOUT(&ball); UNSETPLAYER(&ball); if(!bBounce) { lost_possession(); return FSM_GOALKICK; } bBounce = FALSE; return FSM_CORNERKICK; } } if(!VALID_ROW(ball.nRow)) { SET_BALLOUT(&ball); UNSETPLAYER(&ball); if(!bBounce) { lost_possession(); } else { bBounce = FALSE; } return FSM_THROWIN; } } /* Check if goal */ if(((fsm == FSM_SHOOTING) || (fsm == FSM_PASSING)) && ((nSector == 1 && ball.nColumn == 0 && ball.nRow == 2) || (nSector == 9 && ball.nColumn == 6 && ball.nRow == 2))) { bBounce = FALSE; if(bKickKey == TRUE) { if(ISGOALKEEPER(ball.nColumn,ball.nRow)) { if(probability(70)) { SET_BALLOUT(&ball); lost_possession(); return FSM_GOALKICK; } } else if(ISDEFENSE(ball.nColumn,ball.nRow)) { if(probability(20)) { bounce(curFsm); return FSM_INPLAY; } return FSM_GOAL; } else if(probability(30)) { SET_BALLOUT(&ball); bBounce = FALSE; lost_possession(); return FSM_GOALKICK; } return FSM_GOAL; } if (ISGOALKEEPER(ball.nColumn, ball.nRow)) { lost_possession(); return FSM_GOALKICK; } return FSM_GOAL; } /* non goal */ if( (bKickKey == FALSE) || (bKickKey == TRUE && (nBallStep == 1 || nBallStep == nBallMax)) ) { /* Check if bounced by defender or goalkeeper */ if (ISGOALKEEPER(ball.nColumn, ball.nRow)) { if(curFsm == FSM_PASSING || probability(50)) { SET_BALLOUT(&ball); bBounce = FALSE; lost_possession(); return FSM_GOALKICK; } bounce(curFsm); return FSM_INPLAY; } if (ISDEFENSE(ball.nColumn, ball.nRow)) { if(curFsm == FSM_PASSING) { bounce(curFsm); return FSM_INPLAY; } if(probability(50)) { lost_possession(); return FSM_TURNOVER; } bounce(curFsm); return FSM_INPLAY; } /* Check if receiver has gained possession */ if (ISRECEIVER(ball.nColumn, ball.nRow)) { UNSETPLAYER(&ball); bBounce = FALSE; Soccer2_PlaySound(SOCCER2_SOUND_BOUNCE, PLAYSOUNDFLAGS_PRIORITY); /* Swap player and receiver */ swap_player(&player, &receiver); SETPLAYERBRIGHTNESS(&player, BLIP_BRIGHT); SETPLAYERBRIGHTNESS(&receiver, BLIP_DIMBLINK); return FSM_INPLAY; } } /* Check if player has regained possession */ if (ISPLAYER(ball.nColumn, ball.nRow)) { UNSETPLAYER(&ball); bBounce = FALSE; Soccer2_PlaySound(SOCCER2_SOUND_BOUNCE, PLAYSOUNDFLAGS_PRIORITY); SETPLAYERBRIGHTNESS(&player, BLIP_BRIGHT); SETPLAYERBRIGHTNESS(&receiver, BLIP_DIMBLINK); return FSM_INPLAY; } } } else { bBounce = FALSE; } return curFsm; } int Soccer2_GetPower() { return (bPower ? TRUE : FALSE); } void Soccer2_PowerOn() { if (!bPower) { InitGame(); bPower = TRUE; } } void Soccer2_PowerOff() { bPower = FALSE; Soccer2_StopSound(); } void Soccer2_SetSkill(int i) { nLevel = i; } int Soccer2_GetSkill() { return nLevel; } void Soccer2_Run(int tu) { int x, y; // prevent reentrancy if (bInFrame){ return; } bInFrame = TRUE; // turn off all blips for (y = 0; y < SOCCER2_BLIP_ROWS; y++){ for (x = 0; x < SOCCER2_BLIP_COLUMNS; x++){ Blips[x][y].state = BLIP_OFF; } } if (!bPower){ Soccer2_ClearScreen(); bInFrame = FALSE; return; } Platform_StartDraw(); // run the game (fsmfcn[fsm])(); DrawBlips(); Platform_EndDraw(); bInFrame = FALSE; } // FINITE STATE MACHINE STUFF static void fsmWaitScorePress() { static BOOL pressed = FALSE; BOOL bChange; if(nextFsm == FSM_INVALID) { kickoff_scheme(); nextFsm = FSM_KICKOFF; } if(pressed == FALSE) { Soccer2_DrawSect(nSector, bHomeTeam); if (Soccer2_GetInputSCORE(&bChange)) { if (bChange) { pressed = TRUE; nStatsIndex = 0; nTimerStatsDisplay = TIME_STATSDISPLAY; if(nextFsm != FSM_KICKOFF) EraseBlips(); } } } else { if (--nTimerStatsDisplay <= 0) { ++nStatsIndex; nTimerStatsDisplay = TIME_STATSDISPLAY; } switch(nStatsIndex) { case 0: Soccer2_DrawStat(-1); break; case 1: Soccer2_DrawStat(nVScore); break; case 2: Soccer2_DrawStat(-1); break; case 3: Soccer2_DrawStat(nHScore); break; case 4: Soccer2_DrawStat(-1); break; case 5: Soccer2_DrawStat((int)fGameTime); break; case 6: Soccer2_DrawStat(-1); break; case 7: Soccer2_DrawSect(nSector, bHomeTeam); break; default: initDefense(); receiver.nMoveNow = moveCounterInit(); pressed = FALSE; bTeamMate = FALSE; bTeamMateSet = FALSE; bKickKey = FALSE; bBounce = FALSE; fsm = nextFsm; nextFsm = FSM_INVALID; switch(fsm) { case FSM_KICKOFF: kickoff_scheme(); break; case FSM_CORNERKICK: corner_scheme(); break; case FSM_GOALKICK: goalkick_scheme(); break; case FSM_THROWIN: throwin_scheme(); break; case FSM_TURNOVER: turnover_scheme(); default: break; } break; } } } static void fsmKickoff() { BOOL bChange; kickoff_scheme(); if(Soccer2_GetInputPASS(&bChange)) { if (bChange) { Platform_IsNewSecond(); if(passing() == TRUE) { next_fsm(FSM_PASSING); } } } Soccer2_DrawSect(nSector, bHomeTeam); } static void fsmTurnover() { enum FSM newFsm; newFsm = move_player(fsm); if(newFsm != FSM_INVALID) { Platform_IsNewSecond(); next_fsm(FSM_INPLAY); } } static void fsmThrowin() { BOOL bChange; if(Soccer2_GetInputPASS(&bChange)) { if (bChange) { Platform_IsNewSecond(); if(passing() == TRUE) { next_fsm(FSM_PASSING); } } } if(IS_LVLPRO(nLevel)) { if (Soccer2_GetInputTEAM(&bChange)) { if (bChange) { bTeamMate = !bTeamMate; if(bTeamMate == TRUE) Soccer2_PlaySound(SOCCER2_SOUND_BEEP, PLAYSOUNDFLAGS_PRIORITY); } } if (Soccer2_GetInputSCORE(&bChange)) { if (bChange) { bTeamMateSet = FALSE; } } /* Can move the receiver */ if(bTeamMate == TRUE) { move_player(FSM_THROWIN); } } Soccer2_DrawSect(nSector, bHomeTeam); } static void fsmGoalKick() { BOOL bChange; if(Soccer2_GetInputPASS(&bChange)) { if (bChange) { Platform_IsNewSecond(); if(passing() == TRUE) { next_fsm(FSM_PASSING); } } } Soccer2_DrawSect(nSector, bHomeTeam); } static void fsmCornerKick() { BOOL bChange; if(Soccer2_GetInputPASS(&bChange)) { if (bChange) { Platform_IsNewSecond(); if(passing() == TRUE) { next_fsm(FSM_PASSING); } } } if(IS_LVLPRO(nLevel)) { if (Soccer2_GetInputTEAM(&bChange)) { if (bChange) { bTeamMate = !bTeamMate; if(bTeamMate == TRUE) Soccer2_PlaySound(SOCCER2_SOUND_BEEP, PLAYSOUNDFLAGS_PRIORITY); } } if (Soccer2_GetInputSCORE(&bChange)) { if (bChange) { bTeamMateSet = FALSE; } } /* Can move the receiver */ if(bTeamMate == TRUE) { move_player(FSM_CORNERKICK); } } Soccer2_DrawSect(nSector, bHomeTeam); } static void fsmInPlay() { enum FSM newFsm; BOOL bChange; newFsm = move_player(fsm); if(newFsm == FSM_INVALID) { if (Soccer2_GetInputSHOOT(&bChange)) { if (bChange) { if(shooting() == TRUE) { next_fsm(FSM_SHOOTING); return; } } } if (Soccer2_GetInputPASS(&bChange)) { if (bChange) { if(passing() == TRUE) { next_fsm(FSM_PASSING); return; } } } if(IS_LVLPRO(nLevel)) { if (Soccer2_GetInputTEAM(&bChange)) { if (bChange) { bTeamMate = !bTeamMate; if(bTeamMate == TRUE) Soccer2_PlaySound(SOCCER2_SOUND_BEEP, PLAYSOUNDFLAGS_PRIORITY); } } if (Soccer2_GetInputSCORE(&bChange)) { if (bChange) { bTeamMateSet = FALSE; } } if (Soccer2_GetInputKICK(&bChange)) { if (bChange) { bKickKey = !bKickKey; Soccer2_PlaySound((bKickKey) ? SOCCER2_SOUND_LOWKICK : SOCCER2_SOUND_HIGHKICK, PLAYSOUNDFLAGS_PRIORITY); } } } } else if(newFsm != FSM_INPLAY) { if(!FSM_RUNNING(newFsm)) Soccer2_PlaySound(SOCCER2_SOUND_BALLOUT, PLAYSOUNDFLAGS_PRIORITY); next_fsm(newFsm); return; } // move the receiver move_receiver(); // move defenders newFsm = move_defense(fsm); if(!FSM_RUNNING(newFsm)) Soccer2_PlaySound(SOCCER2_SOUND_BALLOUT, PLAYSOUNDFLAGS_PRIORITY); next_fsm(newFsm); // count down the clock count_down(); } static void fsmPassing() { enum FSM newFsm; BOOL bChange; newFsm = move_player(fsm); if(newFsm == FSM_INVALID) { if (Soccer2_GetInputKICK(&bChange) && IS_LVLPRO(nLevel)) { if (bChange) { bKickKey = !bKickKey; Soccer2_PlaySound((bKickKey) ? SOCCER2_SOUND_LOWKICK : SOCCER2_SOUND_HIGHKICK, PLAYSOUNDFLAGS_PRIORITY); } } } else if(newFsm != FSM_PASSING) { if(!FSM_RUNNING(newFsm)) Soccer2_PlaySound(SOCCER2_SOUND_BALLOUT, PLAYSOUNDFLAGS_PRIORITY); next_fsm(newFsm); return; } // move defenders if(bKickKey) { newFsm = move_defense(fsm); if(!FSM_RUNNING(newFsm)) Soccer2_PlaySound(SOCCER2_SOUND_BALLOUT, PLAYSOUNDFLAGS_PRIORITY); next_fsm(newFsm); } // move the ball if(fsm == FSM_PASSING) { newFsm = move_ball(fsm); if(!FSM_RUNNING(newFsm)) Soccer2_PlaySound(SOCCER2_SOUND_BALLOUT, PLAYSOUNDFLAGS_PRIORITY); next_fsm(newFsm); } // count down the clock count_down(); } static void fsmShooting() { enum FSM newFsm; BOOL bChange; newFsm = move_player(fsm); if(newFsm == FSM_INVALID) { if (Soccer2_GetInputKICK(&bChange) && IS_LVLPRO(nLevel)) { if (bChange) { bKickKey = !bKickKey; Soccer2_PlaySound((bKickKey) ? SOCCER2_SOUND_LOWKICK : SOCCER2_SOUND_HIGHKICK, PLAYSOUNDFLAGS_PRIORITY); } } } else if(newFsm != FSM_SHOOTING) { if(!FSM_RUNNING(newFsm)) Soccer2_PlaySound(SOCCER2_SOUND_BALLOUT, PLAYSOUNDFLAGS_PRIORITY); next_fsm(newFsm); return; } // move defenders if(bKickKey) { newFsm = move_defense(fsm); if(!FSM_RUNNING(newFsm)) Soccer2_PlaySound(SOCCER2_SOUND_BALLOUT, PLAYSOUNDFLAGS_PRIORITY); next_fsm(newFsm); } // move the ball if(fsm == FSM_SHOOTING) { newFsm = move_ball(fsm); if(!FSM_RUNNING(newFsm)) Soccer2_PlaySound(SOCCER2_SOUND_BALLOUT, PLAYSOUNDFLAGS_PRIORITY); next_fsm(newFsm); } // count down the clock count_down(); } static void fsmGameOver() { next_fsm(FSM_GAMEOVER); } static void fsmGoal() { Soccer2_PlaySound(SOCCER2_SOUND_GOAL, PLAYSOUNDFLAGS_PRIORITY); if(!bHomeTeam) nVScore++; else nHScore++; nSector = 5; lost_possession(); next_fsm(FSM_KICKOFF); } #define LINE_STEP 20 void Soccer2_Debug(int f) { int w, h; int y = 0; Soccer2_GetSize(&w, &h); debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "level =%d", nLevel); y += LINE_STEP; debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "fsm =%d (%d)", fsm, FSM_RUNNING(fsm)); y += LINE_STEP; debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "nx fsm =%d (%d)", nextFsm, FSM_RUNNING(nextFsm)); y += LINE_STEP; debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "bnc=%d kk=%d", bBounce, bKickKey); y += LINE_STEP; debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "tm=%d tms=%d", bTeamMate, bTeamMateSet); y += LINE_STEP; debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "trc =%d", trc_debug); y += LINE_STEP; debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "ball =%d (%d,%d)(%d,%d)", ball.nBright, ball.nColumn, ball.nRow, ball.nColumnOld, ball.nRowOld); y += LINE_STEP; debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "play =%d (%d,%d)(%d,%d)", player.nBright, player.nColumn, player.nRow, player.nColumnOld, player.nRowOld); y += LINE_STEP; debugPrintf(realx(w)+10, realy(y), 0xFFFFFFFF, "recv =%d (%d,%d)(%d,%d)", receiver.nBright, receiver.nColumn, receiver.nRow, receiver.nColumnOld, receiver.nRowOld); y += LINE_STEP; }