/*
* 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;
}