summaryrefslogtreecommitdiff
path: root/jeu-test/tetris_lan_src/game.c
diff options
context:
space:
mode:
Diffstat (limited to 'jeu-test/tetris_lan_src/game.c')
-rw-r--r--jeu-test/tetris_lan_src/game.c1781
1 files changed, 1781 insertions, 0 deletions
diff --git a/jeu-test/tetris_lan_src/game.c b/jeu-test/tetris_lan_src/game.c
new file mode 100644
index 0000000..6fe553b
--- /dev/null
+++ b/jeu-test/tetris_lan_src/game.c
@@ -0,0 +1,1781 @@
+
+#include "includes.h"
+
+// Lg jeu = 10. +1 mur et 2 vides de chaque côté.
+// Ht jeu = 22. +4 en haut pour apparition, +1 sol et 2 vides en dessous.
+#define TET_AREA_LG 16 //16
+#define TET_AREA_HT 29 //25
+
+#define TET_DRAW_LG (TET_AREA_LG-6)
+#define TET_DRAW_HT (TET_AREA_HT-7)
+
+#define TET_INTRO_CST ((TET_DRAW_HT << 2) | 0x03) // Constante pour intro/outro.
+#define TET_WAIT_CST 10
+
+#define TET_GFX_P1_OffsX 10 // Offset aire de jeu player 1.
+#define TET_GFX_P1_OffsY 10
+#define TET_GFX_P1_OffsNextX 130 // Offset Next pièce.
+#define TET_GFX_P1_OffsNextY 24
+
+#define TET_GFX_P2_OffsX 210 // Offset aire de jeu player 2.
+#define TET_GFX_P2_OffsY 10
+#define TET_GFX_P2_OffsNextX 138 // Offset Next pièce.
+#define TET_GFX_P2_OffsNextY 139
+
+#define FNT_SH_OffsX 2 // Offset de l'ombre des fontes.
+#define FNT_SH_OffsY 2
+#define FNT_SH_Color 224 // Index de la couleur noir (voir la palette du fichier gfx).
+
+#define TET_Score_Block 4
+#define TET_Score_1Line 10
+#define TET_Score_2Lines 25
+#define TET_Score_3Lines 50
+#define TET_Score_4Lines 100
+
+#define TET_LinesPerLevel 10
+#define TET_MaxLevel 10
+
+enum
+{
+ TET_State_Play = 0,
+ TET_State_GameOver,
+ TET_State_Pause,
+ TET_State_PauseLAN,
+ TET_State_FadeIn,
+ TET_State_Intro,
+ TET_State_FadeOut,
+ TET_State_CnxOpen, // Ouverture de la connexion TCP.
+ TET_State_Connect, // Attente de la connexion TCP.
+ TET_State_EndWait,
+};
+
+#define MAX_PIECES 15
+
+#define BLOCKS_MOD_NORM 7
+#define BLOCKS_MOD_EXT MAX_PIECES
+
+// Les pièces sur tous leurs 4 angles.
+u8 gpPieces[MAX_PIECES][4][16] = // 4 angles * tableau 4*4.
+{
+ // Pièce 1 : Barre.
+ {
+ { 0,0,0,0,
+ 1,1,1,1,
+ 0,0,0,0,
+ 0,0,0,0 },
+ { 0,1,0,0,
+ 0,1,0,0,
+ 0,1,0,0,
+ 0,1,0,0 },
+ { 0,0,0,0,
+ 1,1,1,1,
+ 0,0,0,0,
+ 0,0,0,0 },
+ { 0,1,0,0,
+ 0,1,0,0,
+ 0,1,0,0,
+ 0,1,0,0 }
+ },
+ // Pièce 2 : L à l'endroit.
+ {
+ { 0,2,0,0,
+ 0,2,0,0,
+ 0,2,2,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 2,2,2,0,
+ 2,0,0,0,
+ 0,0,0,0 },
+ { 0,2,2,0,
+ 0,0,2,0,
+ 0,0,2,0,
+ 0,0,0,0 },
+ { 0,0,2,0,
+ 2,2,2,0,
+ 0,0,0,0,
+ 0,0,0,0 }
+ },
+ // Pièce 3 : L à l'envers.
+ {
+ { 0,0,3,0,
+ 0,0,3,0,
+ 0,3,3,0,
+ 0,0,0,0 },
+ { 3,0,0,0,
+ 3,3,3,0,
+ 0,0,0,0,
+ 0,0,0,0 },
+ { 0,3,3,0,
+ 0,3,0,0,
+ 0,3,0,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 3,3,3,0,
+ 0,0,3,0,
+ 0,0,0,0 }
+ },
+ // Pièce 4 : T retourné.
+ {
+ { 0,0,0,0,
+ 0,4,0,0,
+ 4,4,4,0,
+ 0,0,0,0 },
+ { 0,4,0,0,
+ 0,4,4,0,
+ 0,4,0,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 4,4,4,0,
+ 0,4,0,0,
+ 0,0,0,0 },
+ { 0,4,0,0,
+ 4,4,0,0,
+ 0,4,0,0,
+ 0,0,0,0 }
+ },
+ // Pièce 5 : Carré.
+ {
+ { 0,0,0,0,
+ 0,5,5,0,
+ 0,5,5,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 0,5,5,0,
+ 0,5,5,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 0,5,5,0,
+ 0,5,5,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 0,5,5,0,
+ 0,5,5,0,
+ 0,0,0,0 }
+ },
+ // Pièce 6 : S.
+ {
+ { 0,6,0,0,
+ 0,6,6,0,
+ 0,0,6,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 0,6,6,0,
+ 6,6,0,0,
+ 0,0,0,0 },
+ { 0,6,0,0,
+ 0,6,6,0,
+ 0,0,6,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 0,6,6,0,
+ 6,6,0,0,
+ 0,0,0,0 }
+ },
+ // Pièce 7 : S à l'envers.
+ {
+ { 0,0,7,0,
+ 0,7,7,0,
+ 0,7,0,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 7,7,0,0,
+ 0,7,7,0,
+ 0,0,0,0 },
+ { 0,0,7,0,
+ 0,7,7,0,
+ 0,7,0,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 7,7,0,0,
+ 0,7,7,0,
+ 0,0,0,0 }
+ },
+
+ // Extended blocks.
+ // Pièce 8.
+ {
+ { 1,1,0,0,
+ 0,1,0,0,
+ 0,1,1,0,
+ 0,0,0,0 },
+ { 0,0,1,0,
+ 1,1,1,0,
+ 1,0,0,0,
+ 0,0,0,0 },
+ { 1,1,0,0,
+ 0,1,0,0,
+ 0,1,1,0,
+ 0,0,0,0 },
+ { 0,0,1,0,
+ 1,1,1,0,
+ 1,0,0,0,
+ 0,0,0,0 },
+ },
+ // Pièce 9.
+ {
+ { 2,0,0,0,
+ 2,0,0,0,
+ 2,2,2,0,
+ 0,0,0,0 },
+ { 2,2,2,0,
+ 2,0,0,0,
+ 2,0,0,0,
+ 0,0,0,0 },
+ { 2,2,2,0,
+ 0,0,2,0,
+ 0,0,2,0,
+ 0,0,0,0 },
+ { 0,0,2,0,
+ 0,0,2,0,
+ 2,2,2,0,
+ 0,0,0,0 },
+ },
+ // Pièce 10.
+ {
+ { 0,0,3,0,
+ 0,3,3,0,
+ 3,3,0,0,
+ 0,0,0,0 },
+ { 3,0,0,0,
+ 3,3,0,0,
+ 0,3,3,0,
+ 0,0,0,0 },
+ { 0,3,3,0,
+ 3,3,0,0,
+ 3,0,0,0,
+ 0,0,0,0 },
+ { 3,3,0,0,
+ 0,3,3,0,
+ 0,0,3,0,
+ 0,0,0,0 },
+ },
+ // Pièce 11.
+ {
+ { 0,4,0,0,
+ 4,4,4,0,
+ 0,4,0,0,
+ 0,0,0,0 },
+ { 0,4,0,0,
+ 4,4,4,0,
+ 0,4,0,0,
+ 0,0,0,0 },
+ { 0,4,0,0,
+ 4,4,4,0,
+ 0,4,0,0,
+ 0,0,0,0 },
+ { 0,4,0,0,
+ 4,4,4,0,
+ 0,4,0,0,
+ 0,0,0,0 },
+ },
+ // Pièce 12.
+ {
+ { 0,5,5,0,
+ 0,5,5,0,
+ 0,5,0,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 5,5,5,0,
+ 0,5,5,0,
+ 0,0,0,0 },
+ { 0,5,0,0,
+ 5,5,0,0,
+ 5,5,0,0,
+ 0,0,0,0 },
+ { 5,5,0,0,
+ 5,5,5,0,
+ 0,0,0,0,
+ 0,0,0,0 },
+ },
+ // Pièce 13.
+ {
+ { 0,6,6,0,
+ 6,6,0,0,
+ 0,6,0,0,
+ 0,0,0,0 },
+ { 0,6,0,0,
+ 6,6,6,0,
+ 0,0,6,0,
+ 0,0,0,0 },
+ { 0,6,0,0,
+ 0,6,6,0,
+ 6,6,0,0,
+ 0,0,0,0 },
+ { 6,0,0,0,
+ 6,6,6,0,
+ 0,6,0,0,
+ 0,0,0,0 },
+ },
+ // Pièce 14.
+ {
+ { 7,7,7,0,
+ 0,7,0,0,
+ 0,7,0,0,
+ 0,0,0,0 },
+ { 0,0,7,0,
+ 7,7,7,0,
+ 0,0,7,0,
+ 0,0,0,0 },
+ { 0,7,0,0,
+ 0,7,0,0,
+ 7,7,7,0,
+ 0,0,0,0 },
+ { 7,0,0,0,
+ 7,7,7,0,
+ 7,0,0,0,
+ 0,0,0,0 },
+ },
+ // Pièce 15.
+ {
+ { 0,0,0,0,
+ 1,0,1,0,
+ 1,1,1,0,
+ 0,0,0,0 },
+ { 0,1,1,0,
+ 0,1,0,0,
+ 0,1,1,0,
+ 0,0,0,0 },
+ { 0,0,0,0,
+ 1,1,1,0,
+ 1,0,1,0,
+ 0,0,0,0 },
+ { 1,1,0,0,
+ 0,1,0,0,
+ 1,1,0,0,
+ 0,0,0,0 },
+ },
+
+};
+
+
+// Messages.
+enum
+{
+ e_Msg_NextPiece = 0, // Next piece : Pièce.
+ e_Msg_PieceMove, // Déplacement d'une pièce : Pièce, x, y, angle.
+ e_Msg_PiecePose, // Une pièce pose > On l'incruste dans le tableau de jeu : Pièce, x, y, angle.
+ e_Msg_LinesSend, // Envoi de x lignes. Celui qui envoie des lignes envoie ce message.
+ e_Msg_LinesRecv, // X lignes reçues. Celui qui reçoit des lignes envoie ce message.
+
+ e_Msg_HolePos, // Position du trou, envoyé en début de partie par le master si nécessaire.
+ e_Msg_PauseOn, // Déclenchement de la pause.
+ e_Msg_PauseOff, // Arrêt de la pause.
+ e_Msg_Dead, // Mort du joueur.
+ e_Msg_StartingLevel, // Level en cours, envoyé en début de partie. Simplement pour avoir un affichage du n° de niveau cohérent, pas d'incidence sur le jeu.
+
+// prévoir un msg leave (sortie d'un des joueurs) ?
+
+};
+
+
+#define MSG_MAX 16
+struct SMsg
+{
+ u8 nType;
+ u8 pData[7];
+};
+
+// Structures à faire pointer sur pData en fonction du type de message.
+struct SMsg_Data0 // Pour move, pose.
+{
+ u8 nPiece;
+ u8 nX, nY, nAngle;
+};
+struct SMsg_Data1 // Générique pour plusieurs messages différents n'utilisant qu'un prm.
+{
+ union
+ {
+ u8 nLinesNb; // Nb de lignes.
+ u8 nHolePos; // Position du trou.
+ u8 nStartingLevel; // Niveau de départ.
+ };
+};
+
+#define MAX_PLAYERS 2
+struct STetris
+{
+ u8 pArea[TET_AREA_LG * TET_AREA_HT]; // Aire de jeu.
+ u8 pLines[TET_AREA_HT]; // Flag pour ligne complète.
+
+ u8 pRndLimit[MAX_PIECES]; // Pour limite des pièces identiques.
+
+ u32 nMaster; // Le slot est celui de la machine master ou celui de la machine esclave. Important pour le système de message.
+
+ u32 nCurPiece;
+ u32 nCurPieceNoDraw; // P2: Pour ne pas afficher la pièce en cours. On pose une pièce, et on n'aurait pas encore reçu la suivante.
+ u32 nNextPiece;
+ u32 nNextPieceNoDraw; // P2: Pour ne pas afficher la next pièce.
+
+ u32 nPosX, nPosY; // Pos (x,y) de la pièce (du bloc 4*4) dans l'aire de jeu.
+ u32 nSpeed, nSpdInc;
+ u32 nAngle; // [0;3].
+
+ u32 nBottomLines; // Nombre de lignes que nous envoie l'autre joueur, on les rajoute en bas dès qu'on pose la pièce en cours.
+
+ u32 nWait; // Si pas 0, nb de frames pendant lesquelles on fait clignoter les lignes pleines.
+ u32 nGameOver; // LAN : =1 si le joueur a perdu.
+
+ u32 nScore, nLines, nLevel;
+
+ struct SMsg pMsgStack[MSG_MAX]; // File d'attente, messages reçus.
+ u32 nMsgInStack; // Nb de messages dans la file d'attente.
+
+};
+struct STetris gTetris[MAX_PLAYERS];
+
+
+struct STetGen
+{
+ u32 nGameState, nLastState; // Phase en cours.
+ s32 nFadeVal; // Valeur du fade.
+ u32 nIntroVar; // Variable pour montée/descente de lignes pendant l'intro/outro.
+
+ u32 nVar0; // Variable misc 0.
+
+ u32 nHolePos; // Position du trou en LAN. 0 si pas de random (OPT_LAN_Hole).
+
+};
+struct STetGen gTetGen;
+
+
+void Sub_PutPieceInArea(struct STetris *pPlyr);
+u32 Sub_CheckLines(struct STetris *pPlyr);
+
+
+//-----------------------------------------------------------------------------
+// Chaque machine gère son aire de jeu normalement et gère de façon très simplifiée l'aire de l'ennemi :
+// - Quand on reçoit un "posé", on teste les lignes et on les supprime si nécessaire.
+// + on attend le message de rajout de lignes dans son aire.
+
+// Envoi d'un message à l'autre machine.
+void Msg_Send(struct STetris *pPlyr, u32 nMsgNo)
+{
+ struct SMsg sMsg;
+
+ if (gVar.nGameMode != GAME_MODE_LAN) return;
+
+ sMsg.nType = nMsgNo;
+ switch (nMsgNo)
+ {
+ case e_Msg_NextPiece : // Next piece : Pièce.
+ {
+ struct SMsg_Data0 *pData = (struct SMsg_Data0 *)sMsg.pData;
+ pData->nPiece = pPlyr->nNextPiece;
+ }
+ break;
+
+ case e_Msg_PieceMove : // Déplacement d'une pièce : Pièce, x, y, angle.
+ case e_Msg_PiecePose : // Une pièce pose > On l'incruste dans le tableau de jeu : Pièce, x, y, angle.
+ {
+ struct SMsg_Data0 *pData = (struct SMsg_Data0 *)sMsg.pData;
+ pData->nPiece = pPlyr->nCurPiece;
+ pData->nX = pPlyr->nPosX;
+ pData->nY = pPlyr->nPosY;
+ pData->nAngle = pPlyr->nAngle;
+ }
+ break;
+
+ case e_Msg_LinesSend : // Envoi de x lignes. Celui qui envoie des lignes envoie ce message.
+ case e_Msg_LinesRecv : // X lignes reçues. Celui qui reçoit des lignes envoie ce message.
+ {
+ struct SMsg_Data1 *pData = (struct SMsg_Data1 *)sMsg.pData;
+ pData->nLinesNb = gTetGen.nVar0;
+ }
+ break;
+
+ case e_Msg_PauseOn : // Déclenchement de la pause.
+ case e_Msg_PauseOff : // Arrêt de la pause.
+ case e_Msg_Dead : // Mort du joueur.
+ break;
+
+ case e_Msg_HolePos : // Position du trou.
+ {
+ struct SMsg_Data1 *pData = (struct SMsg_Data1 *)sMsg.pData;
+ pData->nHolePos = gTetGen.nHolePos;
+ }
+ break;
+
+ case e_Msg_StartingLevel : // Niveau de départ pour affichage. P1 > P2.
+ {
+ struct SMsg_Data1 *pData = (struct SMsg_Data1 *)sMsg.pData;
+ pData->nStartingLevel = pPlyr->nLevel;
+ }
+ break;
+
+ }
+
+ // Envoi du message.
+ TCP_Send((u8 *)&sMsg, sizeof(sMsg));
+
+}
+
+// Réception des messages.
+// On stocke les messages dans la file d'attente du joueur qu'il faut.
+// On sait qui reçoit quoi ! En gros, c'est le joueur 2 qui reçoit tout, sauf le "LinesSend". Donc je n'ai pas mis de destinataire.
+void Msg_Receive(void)
+{
+ struct SMsg *pMsg;
+ u8 pBuf[1024];
+ u32 nRcvSz;
+
+ if (gVar.nGameMode != GAME_MODE_LAN) return;
+
+ while (TCP_Receive((u8 *)pBuf, sizeof(pBuf), &nRcvSz)) // remplacer par un if ?
+ {
+ // On peut recevoir plusieurs messages bout à bout. Traitement.
+ pMsg = (struct SMsg *)pBuf;
+ while (nRcvSz >= sizeof(struct SMsg))
+ {
+ switch (pMsg->nType)
+ {
+ case e_Msg_NextPiece : // Next piece : Pièce.
+ case e_Msg_PieceMove : // Déplacement d'une pièce : Pièce, x, y, angle.
+ case e_Msg_PiecePose : // Une pièce pose > On l'incruste dans le tableau de jeu : Pièce, x, y, angle.
+ case e_Msg_LinesRecv : // X lignes reçues. Celui qui reçoit des lignes envoie ce message.
+ case e_Msg_Dead : // Mort du joueur.
+ case e_Msg_StartingLevel : // Niveau de départ pour affichage. P1 > P2.
+ if (gTetris[1].nMsgInStack >= MSG_MAX) { printf("P2: Stack overflow !\n"); break; }
+ memcpy(&gTetris[1].pMsgStack[gTetris[1].nMsgInStack], pMsg, sizeof(struct SMsg));
+ gTetris[1].nMsgInStack++;
+ break;
+
+ case e_Msg_HolePos : // Position du trou.
+ case e_Msg_PauseOn : // Déclenchement de la pause.
+ case e_Msg_PauseOff : // Arrêt de la pause.
+ case e_Msg_LinesSend : // Envoi de x lignes. Celui qui envoie des lignes envoie ce message.
+ if (gTetris[0].nMsgInStack >= MSG_MAX) { printf("P1: Stack overflow !\n"); break; }
+ memcpy(&gTetris[0].pMsgStack[gTetris[0].nMsgInStack], pMsg, sizeof(struct SMsg));
+ gTetris[0].nMsgInStack++;
+ break;
+
+ default :
+ printf("Unknown message type received (%d)! Ignored.\n", (int)pMsg->nType);
+ break;
+ }
+
+ pMsg++;
+ nRcvSz -= sizeof(struct SMsg);
+ }
+
+ if (nRcvSz) printf("Warning: Receive: %d remaining bytes.\n", (int)nRcvSz); // Note : Ca n'est encore jamais arrivé.
+
+ }
+
+}
+
+// Teste la file d'attente des messages.
+void Msg_Check(struct STetris *pPlyr)
+{
+ u32 i;
+ u32 nPoseLn = 0; // Pour compter les pièces qui posent et génèrent des lignes.
+
+ for (i = 0; i < pPlyr->nMsgInStack; i++)
+ {
+ switch (pPlyr->pMsgStack[i].nType)
+ {
+ case e_Msg_NextPiece : // Next piece : Pièce.
+ {
+ struct SMsg_Data0 *pData = (struct SMsg_Data0 *)pPlyr->pMsgStack[i].pData;
+ //
+ pPlyr->nNextPiece = pData->nPiece;
+ pPlyr->nNextPieceNoDraw = 0;
+ }
+ break;
+
+ case e_Msg_PiecePose : // Une pièce pose > On l'incruste dans le tableau de jeu : Pièce, x, y, angle.
+ if (nPoseLn)
+ {
+ // Cas foireux : Un pièce générant des lignes a déjà été posée, et on a une autre
+ // pièce qui doit poser (et normalement, ça devrait être après la disparition des
+ // lignes de la pièce d'avant !).
+ // > On recopie les messages en début de liste, et on quitte. Les messages restants seront traités APRES la disparition des lignes.
+ u32 j, nRemMsg;
+
+printf("Pose+ln!\n"); // Affichage de debug. Testé, mais normalement, ce cas ne devrait pas trop arriver.
+ nRemMsg = pPlyr->nMsgInStack - i;
+ for (j = 0; i < pPlyr->nMsgInStack; i++, j++) memcpy(&pPlyr->pMsgStack[j], &pPlyr->pMsgStack[i], sizeof(struct SMsg));
+ pPlyr->nMsgInStack = nRemMsg;
+ return;
+ }
+ // Pas de break !
+ case e_Msg_PieceMove : // Déplacement d'une pièce : Pièce, x, y, angle.
+ {
+ struct SMsg_Data0 *pData = (struct SMsg_Data0 *)pPlyr->pMsgStack[i].pData;
+ u8 nPrevAngle = pPlyr->nAngle;
+ //
+ pPlyr->nCurPiece = pData->nPiece;
+ pPlyr->nPosX = pData->nX;
+ pPlyr->nPosY = pData->nY;
+ pPlyr->nAngle = pData->nAngle;
+ pPlyr->nCurPieceNoDraw = 0;
+ // Sfx.
+ if (pPlyr->nAngle != nPrevAngle) Sfx_PlaySfx(e_Sfx_PieceRotate, e_SfxPrio_10);
+ }
+ // Une pièce pose, on regarde si des lignes disparaissent.
+ if (pPlyr->pMsgStack[i].nType == e_Msg_PiecePose)
+ {
+ // Incruste la pièce dans l'aire de jeu.
+ Sub_PutPieceInArea(pPlyr);
+ // La pièce a été incrustée, en attendant un msg de nouvelle pièce, on cache la pièce en cours.
+ pPlyr->nCurPieceNoDraw = 1;
+ // Test des lignes.
+ if (Sub_CheckLines(pPlyr)) nPoseLn++;
+ }
+ break;
+
+ case e_Msg_LinesSend : // Envoi de x lignes. Celui qui envoie des lignes envoie ce message.
+ {
+ struct SMsg_Data1 *pData = (struct SMsg_Data1 *)pPlyr->pMsgStack[i].pData;
+ // On reçoit un "Send" > on se prend des lignes...
+ pPlyr->nBottomLines += pData->nLinesNb;
+ if (pPlyr->nBottomLines > TET_DRAW_HT) pPlyr->nBottomLines = TET_DRAW_HT; // Limite max.
+ // ...et on envoie un "Receive" pour affichage sur l'autre machine.
+ gTetGen.nVar0 = pData->nLinesNb;
+ Msg_Send(pPlyr, e_Msg_LinesRecv);
+ }
+ break;
+
+ case e_Msg_LinesRecv : // X lignes reçues. Celui qui reçoit des lignes envoie ce message.
+ {
+ struct SMsg_Data1 *pData = (struct SMsg_Data1 *)pPlyr->pMsgStack[i].pData;
+ // On reçoit un "Receive" > On ajoute le nb de lignes à l'adversaire.
+ pPlyr->nBottomLines += pData->nLinesNb;
+ if (pPlyr->nBottomLines > TET_DRAW_HT) pPlyr->nBottomLines = TET_DRAW_HT; // Limite max.
+ }
+ break;
+
+ case e_Msg_Dead : // Mort du joueur adverse.
+printf("Death: Msg received.\n");
+ pPlyr->nGameOver = 1;
+ gTetGen.nGameState = TET_State_GameOver;
+ gTetGen.nIntroVar = TET_INTRO_CST;
+ break;
+
+ case e_Msg_HolePos : // Position du trou.
+ {
+ struct SMsg_Data1 *pData = (struct SMsg_Data1 *)pPlyr->pMsgStack[i].pData;
+ gTetGen.nHolePos = pData->nHolePos;
+ }
+ break;
+
+ case e_Msg_StartingLevel : // Niveau de départ pour affichage. P1 > P2.
+ {
+ struct SMsg_Data1 *pData = (struct SMsg_Data1 *)pPlyr->pMsgStack[i].pData;
+ pPlyr->nLevel = pData->nStartingLevel;
+ }
+ break;
+
+ case e_Msg_PauseOn : // L'autre joueur a déclenché la pause.
+ // Cas foireux ou les 2 joueurs font pause en même temps. Le Master garde la main.
+ if (gTetGen.nGameState == TET_State_Pause)
+ {
+ if (gVar.nTCPMaster) break; // Master, garde l'état TET_State_Pause.
+ gTetGen.nGameState = TET_State_PauseLAN; // Slave, passe de TET_State_Pause à TET_State_PauseLAN.
+ break;
+ }
+ // Cas normal.
+ gTetGen.nLastState = gTetGen.nGameState;
+ gTetGen.nGameState = TET_State_PauseLAN;
+ break;
+
+ case e_Msg_PauseOff : // L'autre joueur a arrêté la pause.
+ gTetGen.nGameState = gTetGen.nLastState;
+ break;
+ }
+
+ }
+ pPlyr->nMsgInStack = 0;
+
+}
+
+//-----------------------------------------------------------------------------
+
+
+// Nouvelle pièce.
+void NewBlock(struct STetris *pPlyr)
+{
+ static u8 nCnt; // Incrémentée à chaque passage. Si on tire trop de blocs étendus, c'est injouable.
+
+/*
+// Version 1 :
+ // Tirage de la nouvelle pièce.
+ gTetris.nCurPiece = gTetris.nNextPiece;
+ //gTetris.nNextPiece = rand() % (gVar.nOptFlags & OPT_BlocksSet ? BLOCKS_MOD_EXT : BLOCKS_MOD_NORM);
+ gTetris.nNextPiece = rand() % ((gVar.nOptFlags & OPT_BlocksSet) && (++nCnt & 1) ? BLOCKS_MOD_EXT : BLOCKS_MOD_NORM);
+*/
+
+
+// Version 2 :
+ // Quand toutes les pièces "normales" on été tirées au moins 1 fois, on décrémente les cases.
+ // Entre temps, chaque pièce ne sort au maximum que 2 fois.
+ u32 i;
+
+ // Tirage de la nouvelle pièce.
+ pPlyr->nCurPiece = pPlyr->nNextPiece;
+ pPlyr->nNextPiece = rand() % ((gVar.nOptFlags & OPT_BlocksSet) && (++nCnt & 1) ? BLOCKS_MOD_EXT : BLOCKS_MOD_NORM);
+ while (pPlyr->pRndLimit[pPlyr->nNextPiece] > 1)
+ {
+ pPlyr->nNextPiece++;
+ if (pPlyr->nNextPiece >= BLOCKS_MOD_NORM) pPlyr->nNextPiece = 0;
+ }
+ pPlyr->pRndLimit[pPlyr->nNextPiece]++;
+ for (i = 0; i < BLOCKS_MOD_NORM; i++) if (pPlyr->pRndLimit[i] == 0) break;
+ if (i == BLOCKS_MOD_NORM)
+ {
+ for (i = 0; i < BLOCKS_MOD_NORM; i++) pPlyr->pRndLimit[i]--; // On décrémente les pièces normales...
+ for (; i < MAX_PIECES; i++) pPlyr->pRndLimit[i] = 0; // ... et on raz des pièces étendues.
+ }
+//< v2
+
+
+ // Reset position.
+ pPlyr->nPosX = (TET_AREA_LG / 2) - 2; // Le bloc fait 4x4.
+ pPlyr->nPosY = 0;
+ pPlyr->nAngle = 0;
+ // RAZ virgule vitesse.
+ pPlyr->nSpeed = 0;
+}
+
+// Initialise la vitesse en fonction du niveau.
+void InitSpeed(struct STetris *pPlyr)
+{
+ // Level de 1 à 10.
+ pPlyr->nSpdInc = (0x30 * pPlyr->nLevel) / TET_MaxLevel;
+
+}
+
+// Init générale.
+void TetrisInit(void)
+{
+ u32 i, p;
+
+ for (p = 0; p < MAX_PLAYERS; p++)
+ {
+ // Clean toute la zone.
+ for (i = 0; i < TET_AREA_LG * TET_AREA_HT; i++)
+ {
+ gTetris[p].pArea[i] = 0;
+ }
+ // Place les murs (limites droite et gauche).
+ for (i = 0; i < TET_AREA_HT; i++)
+ {
+ gTetris[p].pArea[2 + (i * TET_AREA_LG)] = 1;
+ gTetris[p].pArea[3 + TET_DRAW_LG + (i * TET_AREA_LG)] = 1;
+ gTetris[p].pLines[i] = 0; // + clear flag lignes complètes.
+ }
+ // Place le sol.
+ for (i = 0; i < TET_AREA_LG; i++)
+ {
+ gTetris[p].pArea[i + ((TET_AREA_HT - 3) * TET_AREA_LG)] = 1;
+ }
+
+ // Placement du handicap de départ (si nécessaire, et pas en LAN).
+ if (gVar.nGameMode != GAME_MODE_LAN)
+ for (i = 0; i < gExg.nHandicap * 2; i++)
+ {
+ u32 j, k;
+
+ for (j = 0; j < 3; j++)
+ {
+ k = rand() % TET_DRAW_LG;
+ gTetris[p].pArea[k + 3 + ((TET_AREA_HT - 3 - (i+1)) * TET_AREA_LG)] = 1;
+ }
+ }
+
+ // Clean table des pièces identiques.
+ for (i = 0; i < MAX_PIECES; i++) gTetris[p].pRndLimit[i] = 0;
+
+ // Init de la nouvelle pièce. 2 fois pour init Next et Current.
+ srand(time(NULL)); // Init hasard.
+ NewBlock(&gTetris[p]);
+ NewBlock(&gTetris[p]);
+
+ // Misc.
+ gTetris[p].nCurPieceNoDraw = 0;
+ gTetris[p].nNextPieceNoDraw = 0;
+ gTetris[p].nBottomLines = 0;
+ //
+ gTetris[p].nWait = 0;
+ gTetris[p].nGameOver = 0;
+ gTetris[p].nNextPiece = gTetris[p].nCurPiece;
+ //
+ gTetris[p].nScore = 0;
+ gTetris[p].nLines = 0;
+ gTetris[p].nLevel = gExg.nStartingLevel;
+ InitSpeed(&gTetris[p]); // Initialise la vitesse.
+
+ gTetris[p].nMsgInStack = 0; // Nb de messages dans la file d'attente.
+
+ }
+
+ // Id : Slot du master ou du slave.
+ gTetris[0].nMaster = gVar.nTCPMaster;
+ gTetris[1].nMaster = gVar.nTCPMaster ^ 1;
+printf("M/S : slot0:%d / slot1:%d\n", (int)gTetris[0].nMaster, (int)gTetris[1].nMaster);
+
+ // P2 : On attend les messages next piece et piece move pour l'affichage.
+ gTetris[1].nCurPieceNoDraw = 1;
+ gTetris[1].nNextPieceNoDraw = 1;
+ gTetris[1].nLevel = 1; // Par défaut, le P2 est au niveau 1.
+
+ // Misc.
+ gTetGen.nFadeVal = 0;
+ gTetGen.nIntroVar = TET_INTRO_CST;
+ gTetGen.nGameState = TET_State_FadeIn;
+ gTetGen.nHolePos = 0; // Par défaut, trou à gauche.
+
+}
+
+// Check !
+// On regarde s'il y a collision en (x,y) pour une pièce.
+u32 Check(struct STetris *pPlyr, u32 nPosX, u32 nPosY, u32 nAngle)
+{
+ u32 x, y;
+
+ for (y = 0; y < 4; y++)
+ {
+ for (x = 0; x < 4; x++)
+ {
+ if (gpPieces[pPlyr->nCurPiece][nAngle][(y * 4) + x] &&
+ pPlyr->pArea[((nPosY + y) * TET_AREA_LG) + (nPosX + x)])
+ {
+ return (1); // Collision.
+ }
+ }
+ }
+
+ // Il n'y a pas eu de collisions.
+ return (0);
+}
+
+// Dessine un bloc à l'écran.
+// !!! L'écran doit être locké !!!
+void DrawBlockGfx(u32 nScrPosX, u32 nScrPosY, u32 nColor)
+{
+ u32 j;
+ u8 *pDst, *pSrc;
+
+ // Init Src et Dst.
+ pDst = (u8 *)gVar.pScreen->pixels;
+ pSrc = (u8 *)gVar.pBackground->pixels;
+ pSrc += (SCR_Height * SCR_Width) + (nColor * 10);
+ // Dessin du bloc 10x10.
+ for (j = 0; j < 10; j++)
+ {
+ *(u32 *)(pDst + ((j + nScrPosY) * SCR_Width) + nScrPosX) = *(u32 *)(pSrc + (j * SCR_Width));
+ *(u32 *)(pDst + ((j + nScrPosY) * SCR_Width) + nScrPosX + 4) = *(u32 *)(pSrc + (j * SCR_Width) + 4);
+ *(u16 *)(pDst + ((j + nScrPosY) * SCR_Width) + nScrPosX + 8) = *(u16 *)(pSrc + (j * SCR_Width) + 8);
+ }
+
+}
+
+// Dessin de l'aire de jeu, sous-routine d'affichage.
+void Sub_Draw(struct STetris *pPlyr, u32 nOffsX, u32 nOffsY)
+{
+ u32 x, y;
+ u32 nVal;
+
+ // Parcours de l'aire de jeu.
+ for (y = 0; y < TET_DRAW_HT; y++)
+ {
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ nVal = pPlyr->pArea[((y + 4) * TET_AREA_LG) + (x + 3)];
+ if (nVal)
+ {
+ DrawBlockGfx((x * 10) + nOffsX, (y * 10) + nOffsY, nVal - 1);
+ }
+ }
+ }
+
+ if (pPlyr->nCurPieceNoDraw) return; // Stop ici s'il ne faut pas afficher la pièce en cours.
+
+ // Ghost ?
+ if ((gVar.nOptFlags & OPT_Ghost) && pPlyr->nWait == 0)
+ {
+ u32 nGhostY;
+
+ // Recherche de la position du ghost.
+ nGhostY = pPlyr->nPosY;
+ while (Check(pPlyr, pPlyr->nPosX, nGhostY + 1, pPlyr->nAngle) == 0) nGhostY++;
+ // Dessin du ghost.
+ if (nGhostY != pPlyr->nPosY)
+ for (y = 0; y < 4; y++)
+ {
+ if ((s32)(y + nGhostY - 4) >= 0)
+ for (x = 0; x < 4; x++)
+ {
+ nVal = gpPieces[pPlyr->nCurPiece][pPlyr->nAngle][(y * 4) + x];
+ if (nVal)
+ {
+ DrawBlockGfx(((x + pPlyr->nPosX - 3) * 10) + nOffsX, ((y + nGhostY - 4) * 10) + nOffsY, 8);
+ }
+ }
+ }
+ }
+
+ // Dessin de la pièce en cours.
+ for (y = 0; y < 4; y++)
+ {
+ if ((s32)(y + pPlyr->nPosY - 4) >= 0)
+ for (x = 0; x < 4; x++)
+ {
+ nVal = gpPieces[pPlyr->nCurPiece][pPlyr->nAngle][(y * 4) + x];
+ if (nVal)
+ {
+ DrawBlockGfx(((x + pPlyr->nPosX - 3) * 10) + nOffsX, ((y + pPlyr->nPosY - 4) * 10) + nOffsY, nVal - 1);
+ }
+ }
+ }
+
+}
+
+// Dessin de l'aire de jeu.
+void Draw(void)
+{
+ SDL_LockSurface(gVar.pScreen);
+
+ Sub_Draw(&gTetris[0], TET_GFX_P1_OffsX, TET_GFX_P1_OffsY);
+ if (gVar.nGameMode == GAME_MODE_LAN) Sub_Draw(&gTetris[1], TET_GFX_P2_OffsX, TET_GFX_P2_OffsY);
+
+ SDL_UnlockSurface(gVar.pScreen);
+
+}
+
+// Dessin de la next piece, sous-routine d'affichage.
+void Sub_DrawNext(struct STetris *pPlyr, u32 nOffsX, u32 nOffsY)
+{
+ s32 x, y, offsx, offsy;
+ u32 nVal;
+ s8 pOffs[MAX_PIECES * 2] =
+ { 5,10, 5,10, 5,10, 10,5, 4,5, 5,10, 5,10, // Normaux.
+ 10,10, 10,10, 10,10, 10,10, 5,10, 10,10, 10,10, 10,5 }; // Extended.
+
+ if (pPlyr->nNextPieceNoDraw) return; // Stop ici s'il ne faut pas afficher la next pièce.
+
+ // Décalages suivant les pièces.
+ offsx = nOffsX + pOffs[pPlyr->nNextPiece * 2];
+ offsy = nOffsY + pOffs[(pPlyr->nNextPiece * 2) + 1];
+
+ // Dessin de la pièce suivante.
+ for (y = 0; y < 4; y++)
+ {
+ for (x = 0; x < 4; x++)
+ {
+ nVal = gpPieces[pPlyr->nNextPiece][0][(y * 4) + x];
+ if (nVal)
+ {
+ DrawBlockGfx((x * 10) + offsx, (y * 10) + offsy, nVal - 1);
+ }
+ }
+ }
+
+}
+
+// Dessine la Next Piece.
+void DrawNext(void)
+{
+ SDL_LockSurface(gVar.pScreen);
+
+ Sub_DrawNext(&gTetris[0], TET_GFX_P1_OffsNextX, TET_GFX_P1_OffsNextY);
+ if (gVar.nGameMode == GAME_MODE_LAN) Sub_DrawNext(&gTetris[1], TET_GFX_P2_OffsNextX, TET_GFX_P2_OffsNextY);
+
+ SDL_UnlockSurface(gVar.pScreen);
+
+}
+
+// Intro, petite présentation au départ.
+// L'aire de jeu est pleine et se vide.
+void DrawIntro(void)
+{
+ s32 y, x;
+
+ if (gTetGen.nGameState != TET_State_Intro &&
+ gTetGen.nGameState != TET_State_FadeIn &&
+ gTetGen.nGameState != TET_State_CnxOpen &&
+ gTetGen.nGameState != TET_State_Connect) return;
+
+ SDL_LockSurface(gVar.pScreen);
+
+ // Dessin des lignes.
+ x = 0;
+ for (y = gTetGen.nIntroVar >> 2; y > 0; y--)
+ {
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ DrawBlockGfx((x * 10) + TET_GFX_P1_OffsX, ((TET_DRAW_HT - y) * 10) + TET_GFX_P1_OffsY, 7);
+ DrawBlockGfx((x * 10) + TET_GFX_P2_OffsX, ((TET_DRAW_HT - y) * 10) + TET_GFX_P2_OffsY, 7);
+ }
+ }
+
+ SDL_UnlockSurface(gVar.pScreen);
+
+}
+
+// Affichage du win/lose/draw (LAN).
+void DrawWinLose(void)
+{
+ if (gVar.nGameMode != GAME_MODE_LAN) return;
+
+ char *pGame = "GAME", *pOver = "OVER";
+ char *pWin = "WINNER", *pLose = "LOSER", *pDraw = "DRAW", *pWins = "WINS";
+ char *pP1, *pP2;
+
+ u8 pClr[4] = { 252, 253, 254, 253 }; // Blanc / gris clair / gris foncé / gris clair.
+ static u8 nClr = 0;
+ nClr++;
+ nClr &= 15;
+
+ // 1er temps : Game Over (En plus, ça laisse le temps aux derniers messages le temps d'arriver).
+ if (gTetGen.nIntroVar)// > TET_INTRO_CST / 2)
+ {
+ // Game Over sur aire P1.
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) - 18)) + (TET_GFX_P1_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pGame) * 16) / 2)), pGame, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + ((TET_DRAW_HT * 10) / 2) - 18)) + (TET_GFX_P1_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pGame) * 16) / 2)), pGame, pClr[nClr >> 2]);
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) + 2)) + (TET_GFX_P1_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pOver) * 16) / 2)), pOver, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + ((TET_DRAW_HT * 10) / 2) + 2)) + (TET_GFX_P1_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pOver) * 16) / 2)), pOver, pClr[nClr >> 2]);
+ // Game Over sur aire P2.
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) - 18)) + (TET_GFX_P2_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pGame) * 16) / 2)), pGame, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + ((TET_DRAW_HT * 10) / 2) - 18)) + (TET_GFX_P2_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pGame) * 16) / 2)), pGame, pClr[nClr >> 2]);
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) + 2)) + (TET_GFX_P2_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pOver) * 16) / 2)), pOver, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + ((TET_DRAW_HT * 10) / 2) + 2)) + (TET_GFX_P2_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pOver) * 16) / 2)), pOver, pClr[nClr >> 2]);
+ }
+ else
+ // Second temps : Winner / Loser.
+ {
+ // Sélection des phrases pour P1 et P2.
+ if (gTetris[0].nGameOver && gTetris[1].nGameOver)
+ {
+ // Draw game.
+ pP1 = pP2 = pDraw;
+ }
+ else
+ {
+ if (gTetris[0].nGameOver)
+ {
+ pP1 = pLose;
+ pP2 = pWin;
+ }
+ else
+ {
+ pP1 = pWin;
+ pP2 = pLose;
+ }
+ }
+
+ // Affichage P1.
+ char pStrWins[] = " ";
+ char *pStrPtr;
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) - 40)) + (TET_GFX_P1_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pP1) * 16) / 2)), pP1, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + ((TET_DRAW_HT * 10) / 2) - 40)) + (TET_GFX_P1_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pP1) * 16) / 2)), pP1, pClr[nClr >> 2]);
+ //
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) + 0)) + (TET_GFX_P1_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pWins) * 16) / 2)), pWins, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + ((TET_DRAW_HT * 10) / 2) + 0)) + (TET_GFX_P1_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pWins) * 16) / 2)), pWins, pClr[nClr >> 2]);
+ //
+ MyItoa(gVar.pWins[0], pStrWins);
+ pStrPtr = pStrWins;
+ while (*pStrPtr == ' ') pStrPtr++;
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) + 20)) + (TET_GFX_P1_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pStrPtr) * 16) / 2)), pStrPtr, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + ((TET_DRAW_HT * 10) / 2) + 20)) + (TET_GFX_P1_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pStrPtr) * 16) / 2)), pStrPtr, pClr[nClr >> 2]);
+
+ // Affichage P2.
+ memset(pStrWins, ' ', strlen(pStrWins));
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) - 40)) + (TET_GFX_P2_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pP2) * 16) / 2)), pP2, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + ((TET_DRAW_HT * 10) / 2) - 40)) + (TET_GFX_P2_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pP2) * 16) / 2)), pP2, pClr[nClr >> 2]);
+ //
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) + 0)) + (TET_GFX_P2_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pWins) * 16) / 2)), pWins, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + ((TET_DRAW_HT * 10) / 2) + 0)) + (TET_GFX_P2_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pWins) * 16) / 2)), pWins, pClr[nClr >> 2]);
+ //
+ MyItoa(gVar.pWins[1], pStrWins);
+ pStrPtr = pStrWins;
+ while (*pStrPtr == ' ') pStrPtr++;
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) + 20)) + (TET_GFX_P2_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pStrPtr) * 16) / 2)), pStrPtr, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + ((TET_DRAW_HT * 10) / 2) + 20)) + (TET_GFX_P2_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pStrPtr) * 16) / 2)), pStrPtr, pClr[nClr >> 2]);
+
+ }
+
+}
+
+// Affichage du texte de synchro.
+void DrawSync(void)
+{
+ if (gTetGen.nGameState != TET_State_Connect) return;
+ if (gVar.nGameMode != GAME_MODE_LAN) return;
+
+ u8 pClr[4] = { 252, 253, 254, 253 }; // Blanc / gris clair / gris foncé / gris clair.
+ static u8 nClr = 0;
+ nClr++;
+ nClr &= 15;
+
+ // Affichage.
+ char *pSync = "SYNCHRO";
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) - 8)) + (TET_GFX_P1_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pSync) * 16) / 2)), pSync, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + ((TET_DRAW_HT * 10) / 2) - 8)) + (TET_GFX_P1_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pSync) * 16) / 2)), pSync, pClr[nClr >> 2]);
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) - 8)) + (TET_GFX_P2_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pSync) * 16) / 2)), pSync, FNT_SH_Color);
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + ((TET_DRAW_HT * 10) / 2) - 8)) + (TET_GFX_P2_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pSync) * 16) / 2)), pSync, pClr[nClr >> 2]);
+
+}
+
+// Pause, s'il y a qque chose à dessiner, c'est ici.
+void DrawPause(void)
+{
+ if (gTetGen.nGameState != TET_State_Pause &&
+ gTetGen.nGameState != TET_State_PauseLAN) return;
+
+ // Affichage.
+ char *pSync = "PAUSE";
+ FontPrintClr((SCR_Width * (TET_GFX_P1_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) - 8)) + (TET_GFX_P1_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pSync) * 16) / 2)), pSync, FNT_SH_Color);
+ FontPrint((SCR_Width * (TET_GFX_P1_OffsY + ((TET_DRAW_HT * 10) / 2) - 8)) + (TET_GFX_P1_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pSync) * 16) / 2)), pSync);
+ if (gVar.nGameMode == GAME_MODE_LAN)
+ {
+ FontPrintClr((SCR_Width * (TET_GFX_P2_OffsY + FNT_SH_OffsY + ((TET_DRAW_HT * 10) / 2) - 8)) + (TET_GFX_P2_OffsX + FNT_SH_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pSync) * 16) / 2)), pSync, FNT_SH_Color);
+ FontPrint((SCR_Width * (TET_GFX_P2_OffsY + ((TET_DRAW_HT * 10) / 2) - 8)) + (TET_GFX_P2_OffsX + ((TET_DRAW_LG * 10) / 2) - ((strlen(pSync) * 16) / 2)), pSync);
+ }
+
+}
+
+// Affichage du score, du niveau, du nombre de lignes.
+void DrawScores(void)
+{
+ u32 i;
+
+ // Score, Lines, Level.
+ char pStr[] = " ";
+ // P1.
+ MyItoa(gTetris[0].nLevel, pStr);
+ Font8Print((SCR_Width * 81) + 147, pStr);
+ MyItoa(gTetris[0].nLines, pStr);
+ Font8Print((SCR_Width * 94) + 147, pStr);
+ MyItoa(gTetris[0].nScore, pStr);
+ Font8Print((SCR_Width * 107) + 147, pStr);
+
+ if (gVar.nGameMode == GAME_MODE_1Player) return; // Stop en 1 player.
+
+ // P2.
+ memset(pStr, ' ', strlen(pStr));
+ MyItoa(gTetris[1].nLevel, pStr);
+ Font8Print((SCR_Width * 196) + 155, pStr);
+ MyItoa(gTetris[1].nLines, pStr);
+ Font8Print((SCR_Width * 209) + 155, pStr);
+ MyItoa(gTetris[1].nScore, pStr);
+ Font8Print((SCR_Width * 222) + 155, pStr);
+
+ // Affichage de la position du trou sur les aires de jeu.
+ Font8Print((SCR_Width * (SCR_Height - 8)) + TET_GFX_P1_OffsX + (gTetGen.nHolePos * 10), "^");
+ Font8Print((SCR_Width * (SCR_Height - 8)) + TET_GFX_P2_OffsX + (gTetGen.nHolePos * 10), "^");
+
+ // Affichage du nombre de lignes à se prendre.
+ for (i = 0; i < gTetris[0].nBottomLines; i++)
+ Font8Print((SCR_Width * (TET_GFX_P1_OffsY + (TET_DRAW_HT * 10) - 8 - (i * 4))) + TET_GFX_P1_OffsX - 8, "_");
+ for (i = 0; i < gTetris[1].nBottomLines; i++)
+ Font8Print((SCR_Width * (TET_GFX_P2_OffsY + (TET_DRAW_HT * 10) - 8 - (i * 4))) + TET_GFX_P2_OffsX + (TET_DRAW_LG * 10) + 1, "_");
+
+}
+
+// Game Over.
+// L'aire de jeu se remplit de lignes.
+void DrawGameOver(void)
+{
+ s32 y, x;
+
+ if (gTetGen.nGameState != TET_State_GameOver &&
+ gTetGen.nGameState != TET_State_EndWait &&
+ gTetGen.nGameState != TET_State_FadeOut) return;
+
+ SDL_LockSurface(gVar.pScreen);
+
+ // Dessin des lignes.
+ x = 0;
+ for (y = TET_DRAW_HT - (gTetGen.nIntroVar >> 2); y > 0; y--)
+ {
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ DrawBlockGfx((x * 10) + TET_GFX_P1_OffsX, ((TET_DRAW_HT - y) * 10) + TET_GFX_P1_OffsY, 7);
+ DrawBlockGfx((x * 10) + TET_GFX_P2_OffsX, ((TET_DRAW_HT - y) * 10) + TET_GFX_P2_OffsY, 7);
+ }
+ }
+
+ SDL_UnlockSurface(gVar.pScreen);
+
+ DrawWinLose(); // Affichage du win/lose (LAN).
+
+}
+
+// Fade.
+void Fade(s32 nFadeVal, SDL_Surface *pSurf)
+{
+ if (nFadeVal < 0) return;
+
+ SDL_Color pPal[256];
+ SDL_Color *pSrc = pSurf->format->palette->colors;
+ u32 i, nNbColors;
+
+ nNbColors = pSurf->format->palette->ncolors;
+ if (nFadeVal > 255) nFadeVal = 255;
+ for (i = 0; i < nNbColors; i++)
+ {
+ pPal[i].r = (pSrc->r * nFadeVal) / 255;
+ pPal[i].g = (pSrc->g * nFadeVal) / 255;
+ pPal[i].b = (pSrc->b * nFadeVal) / 255;
+ pSrc++;
+ }
+ SDL_SetPalette(gVar.pScreen, SDL_PHYSPAL, pPal, 0, nNbColors);
+
+}
+
+// Gestion état Wait : En attente avant de faire disparaitre les lignes complètes.
+void TetrisWait(struct STetris *pPlyr)
+{
+ u32 x, y;
+ u32 y2;
+
+ pPlyr->nWait--;
+ if (pPlyr->nWait == 0)
+ {
+ // Timer à 0, on dégage les lignes.
+
+ // Suppression des lignes.
+ for (y = TET_DRAW_HT + 4 - 1; y >= 1; y--)
+ {
+ if (pPlyr->pLines[y])
+ {
+ // Recopie.
+ for (y2 = y; y2 >= 1; y2--)
+ {
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ pPlyr->pArea[(y2 * TET_AREA_LG) + (x + 3)] =
+ pPlyr->pArea[((y2 - 1) * TET_AREA_LG) + (x + 3)];
+ }
+ pPlyr->pLines[y2] = pPlyr->pLines[y2 - 1];
+ }
+ // Première ligne à 0.
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ pPlyr->pArea[x + 3] = 0;
+ }
+ pPlyr->pLines[0] = 0;
+ y++;
+ }
+ }
+
+ // RAZ flags lignes.
+ for (y = 0; y < TET_AREA_HT; y++)
+ {
+ pPlyr->pLines[y] = 0;
+ }
+
+ }
+ else
+ {
+ // En attente.
+
+ // On fait clignoter les lignes complètes.
+ for (y = 0; y < TET_DRAW_HT; y++)
+ {
+ if (pPlyr->pLines[y + 4])
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ pPlyr->pArea[((y + 4) * TET_AREA_LG) + (x + 3)] = pPlyr->nWait & 7;
+ }
+ }
+
+ }
+
+}
+
+
+// Ajoute des lignes à la table joueur par le bas (au moment ou une pièce pose, juste avant de jouer la nouvelle pièce).
+void LinesAdd(struct STetris *pPlyr)
+{
+ u32 x, y;
+ u32 nOffs;
+
+ if (pPlyr->nBottomLines == 0) return;
+
+// peut-être limiter à 3 lignes par 3 lignes ? > à tester.
+
+ // Décalage.
+ nOffs = pPlyr->nBottomLines;
+ for (y = 0; y <= TET_DRAW_HT + 4 - 1 - nOffs; y++)
+ {
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ pPlyr->pArea[(y * TET_AREA_LG) + (x + 3)] =
+ pPlyr->pArea[((y + nOffs) * TET_AREA_LG) + (x + 3)];
+ }
+ }
+
+ // Remplissage des lignes du bas.
+ for (; y <= TET_DRAW_HT + 4 - 1; y++)
+ {
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ pPlyr->pArea[(y * TET_AREA_LG) + (x + 3)] = 8;
+ }
+ // Trou.
+ pPlyr->pArea[(y * TET_AREA_LG) + (gTetGen.nHolePos + 3)] = 0;
+ }
+
+ // RAZ lignes.
+ pPlyr->nBottomLines = 0;
+
+}
+
+
+// Sous-routine, incruste la pièce en cours dans l'aire de jeu.
+void Sub_PutPieceInArea(struct STetris *pPlyr)
+{
+ u32 x, y;
+
+ // Incruste la pièce dans l'aire de jeu.
+ for (y = 0; y < 4; y++)
+ {
+ for (x = 0; x < 4; x++)
+ {
+ if (gpPieces[pPlyr->nCurPiece][pPlyr->nAngle][(y * 4) + x])
+ {
+ pPlyr->pArea[((pPlyr->nPosY + y) * TET_AREA_LG) + (pPlyr->nPosX + x)] =
+ gpPieces[pPlyr->nCurPiece][pPlyr->nAngle][(y * 4) + x];
+ }
+ }
+ }
+ // Score de la pièce.
+ pPlyr->nScore += TET_Score_Block;
+
+ // Rajout des lignes de malus si nécessaire.
+ LinesAdd(pPlyr);
+
+}
+
+// Sous-routine, test des lignes.
+// Out : Le nombre de lignes pour envoi de merde à l'autre joueur.
+u32 Sub_CheckLines(struct STetris *pPlyr)
+{
+ u32 x, y;
+ u32 nLn, nLn2;
+ u32 nVal;
+
+ // Test des lignes.
+ nLn2 = 0;
+ for (y = 0; y < TET_DRAW_HT + 4; y++)
+ {
+ nLn = 0;
+ for (x = 0; x < TET_DRAW_LG; x++)
+ {
+ nVal = pPlyr->pArea[(y * TET_AREA_LG) + (x + 3)];
+ if (nVal)
+ {
+ nLn++;
+ }
+ }
+ // Ligne complète ?
+ if (nLn == TET_DRAW_LG)
+ {
+ pPlyr->pLines[y] = 1; // On flague.
+ nLn2++;
+ }
+ }
+ // Y-a-t'il eu des lignes ?
+ if (nLn2)
+ {
+ pPlyr->nWait = TET_WAIT_CST; // Attente de x frames.
+
+ // Score pour lignes.
+ u32 pScLn[4] = { TET_Score_1Line, TET_Score_2Lines, TET_Score_3Lines, TET_Score_4Lines };
+ pPlyr->nScore += pScLn[nLn2 - 1];
+ // Nb de lignes ++.
+ u32 nOld = pPlyr->nLines;
+ pPlyr->nLines += nLn2;
+ // Level passé ?
+ if (pPlyr->nLevel < TET_MaxLevel &&
+ pPlyr->nLines / TET_LinesPerLevel != nOld / TET_LinesPerLevel) // Avec les /, permet de changer de niveau même en ne començant pas au niveau 1.
+ {
+ // Level up !
+ pPlyr->nLevel++;
+ InitSpeed(pPlyr);
+ // Sfx.
+ Sfx_PlaySfx(e_Sfx_LevelUp, e_SfxPrio_20);
+ }
+
+ // Sfx ligne complète.
+ Sfx_PlaySfx(e_Sfx_Explosion2, e_SfxPrio_10);
+
+ }
+ else
+ {
+ // Sfx pièce posée.
+ Sfx_PlaySfx(e_Sfx_PieceSticks, e_SfxPrio_10);
+ }
+
+ return (nLn2);
+}
+
+
+#define KB_REPEAT_1ST 12
+// Game : La gestion du jeu normal.
+void TetrisGame(struct STetris *pPlyr)
+{
+ u32 nLn2;
+
+ u32 nLastPosX, nLastPosY, nLastAngle; // Pour envoi de message.
+ nLastPosX = pPlyr->nPosX;
+ nLastPosY = pPlyr->nPosY;
+ nLastAngle = pPlyr->nAngle;
+
+ static u32 nRepeatR, nRepeatL; // !!! ATTENTION !!! Avec les variables statiques, ce n'est pas prévu pour plusieurs joueurs en local ! Si on veut faire un multiplayer en local, il faut sortir ces variables.
+
+ // Gestion de la répétition des touches.
+ if (gVar.pKeys[SDLK_RIGHT] == 0) nRepeatR = 0;
+ nRepeatR++;
+ if (nRepeatR > KB_REPEAT_1ST && (nRepeatR & 0x03) == 0) gVar.pKeysTrig[TRIG_RIGHT] = 1;
+ if (gVar.pKeys[SDLK_LEFT] == 0) nRepeatL = 0;
+ nRepeatL++;
+ if (nRepeatL > KB_REPEAT_1ST && (nRepeatL & 0x03) == 0) gVar.pKeysTrig[TRIG_LEFT] = 1;
+
+ // Déplacement ?
+ if (gVar.pKeysTrig[TRIG_RIGHT])
+ {
+ if (Check(pPlyr, pPlyr->nPosX + 1, pPlyr->nPosY, pPlyr->nAngle) == 0)
+ {
+ pPlyr->nPosX += 1;
+ }
+ }
+ if (gVar.pKeysTrig[TRIG_LEFT])
+ {
+ if (Check(pPlyr, pPlyr->nPosX - 1, pPlyr->nPosY, pPlyr->nAngle) == 0)
+ {
+ pPlyr->nPosX -= 1;
+ }
+ }
+ // Rotation ?
+ if (gVar.pKeysTrig[TRIG_UP])
+ {
+ s32 nRot = (gVar.nOptFlags & OPT_Rotation ? -1 : 1); // Sens de rotation.
+ u8 nPrevAngle = pPlyr->nAngle;
+
+ if (Check(pPlyr, pPlyr->nPosX, pPlyr->nPosY, (pPlyr->nAngle + nRot) & 3) == 0)
+ {
+ pPlyr->nAngle += nRot;
+ pPlyr->nAngle &= 3;
+ }
+
+ else
+ {
+ // Aide : Si on ne peut pas tourner, on regarde une case à droite et à gauche.
+ // Si ça passe, on y va.
+ if (Check(pPlyr, pPlyr->nPosX - 1, pPlyr->nPosY, (pPlyr->nAngle + nRot) & 3) == 0)
+ {
+ pPlyr->nPosX -= 1;
+ pPlyr->nAngle += nRot;
+ pPlyr->nAngle &= 3;
+ }
+ else
+ if (Check(pPlyr, pPlyr->nPosX + 1, pPlyr->nPosY, (pPlyr->nAngle + nRot) & 3) == 0)
+ {
+ pPlyr->nPosX += 1;
+ pPlyr->nAngle += nRot;
+ pPlyr->nAngle &= 3;
+ }
+
+ }
+
+ // Sfx.
+ if (pPlyr->nAngle != nPrevAngle) Sfx_PlaySfx(e_Sfx_PieceRotate, e_SfxPrio_10);
+
+ }
+
+ // Vitesse.
+ if (gVar.pKeysTrig[TRIG_Space]) // Espace : On pose la pièce directement.
+ {
+ // On teste toutes les lignes jusqu'à ce qu'on ne puisse plus descendre.
+ while (Check(pPlyr, pPlyr->nPosX, pPlyr->nPosY + 1, pPlyr->nAngle) == 0) pPlyr->nPosY++;
+ pPlyr->nSpeed |= 0x100; // On va forcer la dernière descente de ligne.
+ }
+ if (gVar.pKeys[SDLK_DOWN])
+ {
+ // Accélération.
+ pPlyr->nSpeed += 0x80; // Revoir. Pas bien. Quand une pièce pose, il faut un trigger pour ne pas descendre la suivante.
+ }
+ else
+ {
+ // Vitesse normale.
+ pPlyr->nSpeed += pPlyr->nSpdInc;
+ }
+ // Faut-il descendre d'une ligne ?
+ if (pPlyr->nSpeed & ~0xFF)
+ {
+ pPlyr->nSpeed &= 0xFF;
+
+ // Peut-on descendre d'une ligne ?
+ if (Check(pPlyr, pPlyr->nPosX, pPlyr->nPosY + 1, pPlyr->nAngle) == 0)
+ {
+ // Oui.
+ pPlyr->nPosY += 1;
+ // Msg.
+ Msg_Send(pPlyr, e_Msg_PieceMove);
+ }
+ else
+ {
+ // Msg.
+ Msg_Send(pPlyr, e_Msg_PiecePose);
+ // Non, on incruste la pièce dans l'aire de jeu.
+ Sub_PutPieceInArea(pPlyr);
+ // Test de lignes.
+ if ((nLn2 = Sub_CheckLines(pPlyr)) >= 2)
+ {
+ // Msg : Envoi de lignes.
+ gTetGen.nVar0 = nLn2 - 1;
+ Msg_Send(pPlyr, e_Msg_LinesSend);
+ }
+
+ // Nouvelle pièce.
+ NewBlock(pPlyr);
+ Msg_Send(pPlyr, e_Msg_NextPiece); // Id de la next piece.
+ Msg_Send(pPlyr, e_Msg_PieceMove); // + déplacement immédiat de la nouvelle pièce pour affichage !
+ // Pas de pb pour la nouvelle pièce ?
+ if (Check(pPlyr, pPlyr->nPosX, pPlyr->nPosY, pPlyr->nAngle) != 0)
+ {
+ // Si. Game Over.
+ Msg_Send(pPlyr, e_Msg_Dead); // Mort du joueur.
+ //
+ pPlyr->nGameOver = 1;
+ gTetGen.nGameState = TET_State_GameOver;
+ gTetGen.nIntroVar = TET_INTRO_CST;
+ //
+ pPlyr->nNextPiece = pPlyr->nCurPiece; // Pour éviter les deux Next pièces qui s'enchainent quand perdu.
+ Msg_Send(pPlyr, e_Msg_NextPiece); // Id de la next piece.
+
+ }
+
+ gVar.pKeys[SDLK_DOWN] = 0; // RAZ flèche bas. Oblige à relacher et réappuyer.
+
+ }
+ }
+ else
+ {
+ // Pas de posé, pas de descente de ligne, msg de déplacement si nécessaire.
+ if (nLastPosX != pPlyr->nPosX || nLastPosY != pPlyr->nPosY || nLastAngle != pPlyr->nAngle)
+ {
+ Msg_Send(pPlyr, e_Msg_PieceMove);
+ }
+ }
+
+}
+
+/*
+// Traitement de l'aire de jeu du joueur 2 en mode LAN.
+void TetrisGameLAN(struct STetris *pPlyr)
+{
+
+}
+*/
+
+// Tetris - Main.
+u32 TetrisMain(void)
+{
+ u32 nRetVal = GAME_Null; // Valeur de sortie.
+
+ // Selon l'état.
+ switch (gTetGen.nGameState)
+ {
+ case TET_State_CnxOpen : // LAN : Ouverture de la connexion TCP.
+ TCP_CnxOpen();
+ gTetGen.nGameState = TET_State_Connect;
+ break;
+
+ case TET_State_Connect : // LAN : Attente de la connexion TCP.
+ if (TCP_CnxWait())
+ {
+ gTetGen.nGameState = TET_State_Intro;
+ // Envoi de la pièce en cours et de la next piece.
+ Msg_Send(&gTetris[0], e_Msg_NextPiece);
+ Msg_Send(&gTetris[0], e_Msg_PieceMove);
+ // Envoi de son niveau de départ.
+ Msg_Send(&gTetris[0], e_Msg_StartingLevel);
+ // Master : Position du trou.
+ if (gVar.nTCPMaster && (gVar.nOptFlags & OPT_LAN_Hole))
+ {
+ gTetGen.nHolePos = rand() % TET_DRAW_LG;
+ Msg_Send(&gTetris[0], e_Msg_HolePos);
+ }
+ }
+ break;
+
+ case TET_State_Play :
+ // Réception des messages en mode LAN.
+ Msg_Receive();
+
+ // En attente quand des lignes sont faites ?
+ if (gTetris[0].nWait)
+ {
+ // En attente.
+ TetrisWait(&gTetris[0]);
+ }
+ else
+ {
+ // Boucle normale.
+ Msg_Check(&gTetris[0]); // Traitement des messages reçus.
+ TetrisGame(&gTetris[0]);
+ }
+
+ // Si LAN, 2ème joueur.
+ if (gVar.nGameMode == GAME_MODE_LAN)
+ {
+ // En attente quand des lignes sont faites ?
+ if (gTetris[1].nWait)
+ {
+ // En attente.
+ TetrisWait(&gTetris[1]);
+ }
+ else
+ {
+ // Boucle normale.
+ Msg_Check(&gTetris[1]); // Traitement des messages reçus.
+ //TetrisGameLAN(&gTetris[1]); // Finalement, rien de spécial à faire ici.
+ }
+ }
+
+ // On veut une pause ? / Le && State_Play, c'est parce qu'on peut déjà être passé en State_Pause par les messages !
+ if (gVar.pKeysTrig[TRIG_KeyP] && gTetGen.nGameState == TET_State_Play)
+ {
+ gTetGen.nLastState = gTetGen.nGameState;
+ gTetGen.nGameState = TET_State_Pause;
+ // LAN.
+ Msg_Send(&gTetris[0], e_Msg_PauseOn);
+ }
+ break;
+
+ case TET_State_GameOver :
+ // Pendant le début de la montée des lignes, on continue à recevoir les messages pour dernières mises à jour (pièce en cours, autre message de mort...).
+ if (gTetGen.nIntroVar > TET_INTRO_CST - 32)
+ {
+ Msg_Receive();
+ // Traitement des messages reçus.
+ Msg_Check(&gTetris[0]);
+ Msg_Check(&gTetris[1]);
+ }
+ // Transmissions terminées. Incrémentation du nombre de victoires général.
+ else if (gTetGen.nIntroVar == TET_INTRO_CST - 32)
+ {
+ if (gVar.nGameMode == GAME_MODE_LAN)
+ if (!(gTetris[0].nGameOver && gTetris[1].nGameOver)) // Pas si match nul.
+ {
+ gVar.pWins[(gTetris[0].nGameOver ? 1 : 0)]++;
+ }
+ }
+
+ // Les lignes.
+ if (gTetGen.nIntroVar)
+ {
+ gTetGen.nIntroVar--;
+ // Si terminé, fade out.
+ if (gTetGen.nIntroVar == 0) gTetGen.nFadeVal = 256;
+ }
+ else
+ {
+ if (gVar.nGameMode == GAME_MODE_LAN)
+ {
+ // LAN, on va passer dans une phase d'attente.
+ gTetGen.nGameState = TET_State_EndWait;
+ TCP_CnxClose();
+ }
+ else
+ {
+ // 1 player : On quitte.
+ gTetGen.nGameState = TET_State_FadeOut;
+ }
+ }
+ break;
+
+ case TET_State_EndWait : // Mode 2 joueurs, Win/Lose. Attente d'une touche.
+ if (gVar.pKeysTrig[TRIG_Space]) // Espace.
+ {
+ gTetGen.nGameState = TET_State_FadeOut;
+ }
+ break;
+
+ case TET_State_FadeOut :
+ // Fade out.
+ {
+ if (gTetGen.nFadeVal >= 0)
+ {
+ gTetGen.nFadeVal -= 4; // Attention, ça tombe bien...
+ }
+ else
+ {
+ // On quitte ici, quand FadeVal tombe sous 0.
+ gExg.nScore = gTetris[0].nScore; // Pour les High scores.
+ nRetVal = GAME_GameOver;
+ //TCP_CnxClose();
+ }
+ }
+ break;
+
+ case TET_State_FadeIn :
+ // Le fade.
+ if (gTetGen.nFadeVal >= 0 && gTetGen.nFadeVal < 256)
+ {
+ gTetGen.nFadeVal += 4;
+ }
+ else
+ {
+ gTetGen.nFadeVal = -1; // Arrêt du fade.
+ gTetGen.nGameState = (gVar.nGameMode == GAME_MODE_LAN ? TET_State_CnxOpen : TET_State_Intro);
+ }
+ break;
+
+ case TET_State_Intro :
+ // Réception des messages en mode LAN.
+ Msg_Receive();
+ // Traitement des messages reçus.
+ Msg_Check(&gTetris[0]);
+ Msg_Check(&gTetris[1]);
+
+ // Les lignes qui descendent.
+ {
+ // Terminé ?
+ if (--gTetGen.nIntroVar == 0)
+ {
+ gTetGen.nGameState = TET_State_Play;
+ //
+ NewBlock(&gTetris[0]); // => On récupère la Next pièce => Affichage correct !
+ Msg_Send(&gTetris[0], e_Msg_NextPiece); // revoir...
+ Msg_Send(&gTetris[0], e_Msg_PieceMove); // revoir...
+ }
+ }
+ break;
+
+ case TET_State_Pause :
+ // On reprend ?
+ if (gVar.pKeysTrig[TRIG_KeyP])
+ {
+ gTetGen.nGameState = gTetGen.nLastState;
+ // LAN.
+ Msg_Send(&gTetris[0], e_Msg_PauseOff);
+ break;
+ }
+ // ... et on enchaîne pour recevoir les messages, toujours au cas où les 2 joueurs appuient en même temps.
+ case TET_State_PauseLAN : // En LAN, celui qui n'a pas déclenché la pause se retrouve dans cet état. L'autre est en pause normale.
+ // Réception des messages en mode LAN.
+ Msg_Receive();
+ // Traitement des messages reçus.
+ Msg_Check(&gTetris[0]);
+ break;
+
+ }
+
+ // Dessin du jeu.
+ Draw();
+ DrawNext();
+ DrawScores();
+ DrawPause();
+ DrawIntro();
+ DrawSync(); // Texte de synchro.
+ DrawGameOver();
+ Fade(gTetGen.nFadeVal, gVar.pBackground);
+
+ return (nRetVal);
+}
+
+