summaryrefslogtreecommitdiff
path: root/jeu-test
diff options
context:
space:
mode:
authorDamien Appert <dappert>2010-09-12 14:13:14 +0000
committerDamien Appert <dappert>2010-09-12 14:13:14 +0000
commit19ecfb8c29a6f105c25e887531072332e29c000c (patch)
tree87a8e6c300ce1eac627ac5724565dd3125a3c26e /jeu-test
parent6c63104925067616f97d009eccf1ed56cab37d96 (diff)
download2010-netlemmings-19ecfb8c29a6f105c25e887531072332e29c000c.tar.gz
2010-netlemmings-19ecfb8c29a6f105c25e887531072332e29c000c.tar.bz2
2010-netlemmings-19ecfb8c29a6f105c25e887531072332e29c000c.zip
jeu tétris jouter pour tester SDL en Lan
git-svn-id: file:///var/svn/2010-netlemmings/trunk@15 077b3477-7977-48bd-8428-443f22f7bfda
Diffstat (limited to 'jeu-test')
-rw-r--r--jeu-test/tetris_lan_src/Makefile14
-rw-r--r--jeu-test/tetris_lan_src/_LISEZMOI.txt94
-rw-r--r--jeu-test/tetris_lan_src/ctypes.h10
-rw-r--r--jeu-test/tetris_lan_src/font.c148
-rw-r--r--jeu-test/tetris_lan_src/font.h7
-rw-r--r--jeu-test/tetris_lan_src/frame.c28
-rw-r--r--jeu-test/tetris_lan_src/frame.h6
-rw-r--r--jeu-test/tetris_lan_src/game.c1781
-rw-r--r--jeu-test/tetris_lan_src/game.h9
-rw-r--r--jeu-test/tetris_lan_src/gfx/bkg.bmpbin0 -> 81720 bytes
-rw-r--r--jeu-test/tetris_lan_src/gfx/bkg_menu.bmpbin0 -> 77878 bytes
-rw-r--r--jeu-test/tetris_lan_src/gfx/fnt.bmpbin0 -> 17460 bytes
-rw-r--r--jeu-test/tetris_lan_src/gfx/fnt8.bmpbin0 -> 5176 bytes
-rw-r--r--jeu-test/tetris_lan_src/high.scrbin0 -> 132 bytes
-rw-r--r--jeu-test/tetris_lan_src/includes.h108
-rw-r--r--jeu-test/tetris_lan_src/main.c320
-rw-r--r--jeu-test/tetris_lan_src/menu.c711
-rw-r--r--jeu-test/tetris_lan_src/menu.h22
-rw-r--r--jeu-test/tetris_lan_src/sfx.c354
-rw-r--r--jeu-test/tetris_lan_src/sfx.h33
-rw-r--r--jeu-test/tetris_lan_src/sfx/_explosion2.wavbin0 -> 21426 bytes
-rw-r--r--jeu-test/tetris_lan_src/sfx/_level_up.wavbin0 -> 89804 bytes
-rw-r--r--jeu-test/tetris_lan_src/sfx/_menu_click.wavbin0 -> 6836 bytes
-rw-r--r--jeu-test/tetris_lan_src/sfx/_menu_validate.wavbin0 -> 9494 bytes
-rw-r--r--jeu-test/tetris_lan_src/sfx/_piece_rotate.wavbin0 -> 6836 bytes
-rw-r--r--jeu-test/tetris_lan_src/sfx/_piece_rotate_.wavbin0 -> 8894 bytes
-rw-r--r--jeu-test/tetris_lan_src/sfx/_piece_sticks.wavbin0 -> 3148 bytes
-rw-r--r--jeu-test/tetris_lan_src/tcpip.c541
-rw-r--r--jeu-test/tetris_lan_src/tcpip.h13
-rwxr-xr-xjeu-test/tetris_lan_src/tetrisbin0 -> 193510 bytes
-rw-r--r--jeu-test/tetris_lan_src/tetris.optbin0 -> 8 bytes
31 files changed, 4199 insertions, 0 deletions
diff --git a/jeu-test/tetris_lan_src/Makefile b/jeu-test/tetris_lan_src/Makefile
new file mode 100644
index 0000000..3566d5b
--- /dev/null
+++ b/jeu-test/tetris_lan_src/Makefile
@@ -0,0 +1,14 @@
+# Makefile
+#PATH = /usr/include/SDL
+TARGET = tetris
+OBJECTS = main.o game.o frame.o font.o menu.o sfx.o tcpip.o
+
+CFLAGS = -O3 -Wall -g -I/usr/include/SDL -L/usr/lib
+LIBS = -lSDL #-lSDL_image
+CC = gcc
+
+all: $(TARGET)
+
+$(TARGET): $(OBJECTS)
+ $(CC) $(CFLAGS) -o $@ $^ $(LIBS)
+
diff --git a/jeu-test/tetris_lan_src/_LISEZMOI.txt b/jeu-test/tetris_lan_src/_LISEZMOI.txt
new file mode 100644
index 0000000..82d8bf0
--- /dev/null
+++ b/jeu-test/tetris_lan_src/_LISEZMOI.txt
@@ -0,0 +1,94 @@
+
+ Tetris - 2 players LAN.
+ Done by Clément CORDE.
+ Contact : c1702@yahoo.com
+
+
+ Bonjour,
+
+ Je vous laisse récupérer et regarder le lisezmoi.txt du Tetris simple, je me contente de rajouter en plus ici ce que j'ai fait pour la partie "réseau".
+
+ Ceci est mon premier essai d'un jeu en réseau. Ca semble marcher pas trop mal, mais je suis toujours preneur de conseils.
+
+
+> Réseau :
+
+ J'utilise le système des sockets et j'ai pris l'option d'utiliser le protocole TCP/IP.
+
+ C'est un tout petit peu plus lourd que de l'UDP, mais ça simplifiait grandement les choses : En effet, TCP gère lui même les retransmissions et le fait que les paquets arrivent bien dans l'ordre. Avec UDP, il aurait fallu faire ça "à la main".
+
+ J'utilise aussi l'option "TCP_NODELAY". Ce flag coupe le "Nagle algorithm", petit truc utilisé à peu près partout qui fait que les paquets à envoyer, s'ils sont petits, sont bufferisés et envoyés plus tard en une fois, dans le but de ne pas utiliser trop de bande passante (en gros, pour éviter d'envoyer des paquets TCP dans lesquels les headers, adresses, checksums et autres prennent 4 fois plus de place que le message lui même). C'est un truc très utile, sauf dans notre cas, où justement, on veut être averti dès qu'une pièce bouge. Sans ce flag, on reçoit plusieurs messages en une fois, et le résultat est que ça à l'air saccadé, sans pour autant que le jeu ne rame !
+
+ Je me suis basé sur plusieurs tutoriaux pour voir comment fonctionnaient les communications client/serveur. Je citerai les pages de "quantic-storm" sont très bien, car elles donnent les fonctions à utiliser sous Windows et sous Linux, et les différences entre elles. Ensuite, il y a plein de doc sur MSDN, et il y a les man pages.
+
+ Pour jouer à deux, un joueur doit choisir le mode "serveur", l'autre le mode "client".
+
+ Le serveur ouvre une socket d'écoute (voir la variable (structure) gSockListen), et attend qu'un client se connecte. Quand il reçoit une connexion, il ouvre une nouvelle socket pour dialoguer avec ce client (voir la variable gSockCom).
+
+ Le Client, lui, ouvre une socket (gSockCom, mais on exécute le prg depuis une autre machine) avec laquelle il contacte et communique avec le serveur.
+
+ Et bien sûr (s'il était besoin de préciser), on utilise les sockets en mode non bloquant !
+
+ Le truc, c'est qu'il faut appeler les fonctions dans le bon ordre. En résumé :
+- Server: socket / bind / listen / accept / send and-or recv / close
+- Client: socket / connect / send and-or recv / close
+
+
+> Système de jeu et de messages :
+
+ Tout d'abord, il faut préciser que ce système est spécifiquement adapté à un mode 2 joueurs. Il y aurait des choses à revoir pour plus de joueurs simultanés.
+
+ Chaque joueur gère son aire de jeu (celle de gauche) et partielement celle de l'autre joueur. Quand une pièce bouge (déplacement ou rotation) chez nous, on envoie un msg à l'autre machine (e_Msg_PieceMove). On reçoit donc pour le deuxième joueur la position de la pièce en cours, son n°, son angle. On ne gère donc pas la descente ou l'accélération pour le 2ème joueur. Par contre, quand une pièce "pose" (message e_Msg_PiecePose), on teste la disparition des lignes. Le score/niveau/nombre de lignes du joueur 2 est géré localement aussi.
+
+ Le petit truc un peu spécial, ce sont les messages d'envoi de lignes à l'autre joueur. J'ai préféré gérer tout ça avec des messages pour être sûr de ne pas avoir de désynchronisation. Je m'explique : Quand je fais 4 lignes, j'en envoie 3 à l'autre (e_Msg_LinesSend). Quand l'autre les reçoit, il prend ça en compte, et nous renvoie un message de reception (e_Msg_LinesRecv), et à ce moment là, on prend en les lignes pour le joueur 2. Comme ça, on est sûrs de prendre en compte l'addition de lignes au bon moment. Bon, maintenant, il y a peut-être plus simple, mais ça fonctionne apparement pas mal.
+
+ Ah oui, on ne rajoute les lignes malus qu'au moment ou une pièce pose, ce qui évite les problèmes du style la pièce en cours qui est à une ligne de poser, le tableau monte de 3 lignes, et la pièce en cours se retrouve dans les blocs... C'est gérable, mais bon, le malus est déjà suffisament chiant pour qu'en plus, ce ne soit pas nécessaire de gêner le joueur sur sa pièce en cours.
+
+ J'ai aussi réfléchi à la façon de positionner le trou sur les lignes de malus. Soit le Master à mis l'option "LAN RND HOLE" à ON, et la position du trou est décidée en début de partie. Le trou est à la même position pour les deux joueurs. Soit l'option est à OFF, et le trou sera à gauche. La position du trou est indiquée par un '^' sous l'aire de jeu.
+
+ Liste des messages et de leurs paramètres :
+
+- 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.
+
+ Du coup, on voit bien si un message est a destination du joueur 2 (la plupart) ou du joueur 1. C'est pour ça que la réception des messages est très simple : Je distribue un message qui arrive en fonction de son type, sans gérer de destinataire.
+
+
+ > Séparation jeu / réseau :
+
+ J'ai fait en sorte que quand on doit envoyer et recevoir des messages, on n'ait pas a se soucier des sockets. Tout ça est isolé dans le fichier "tcpip.c", et je fournis au jeu des fonctions TCP_Send et TCP_Receive.
+
+
+ > Compilation :
+
+ Toujours Code::Blocks sous Windows XP (SP3), avec gcc et SDL. Sur ma Debian 5, c'est aussi gcc, bien sûr !
+
+ Je n'ai pas inclus le fichier projet pour la raison suivante : Comme il y a peu de chances que vos répertoires soient les mêmes que les miens, le projet ne se lirait de toutes façons probablement pas.
+
+ Créez un nouveau projet SDL, copiez tous les fichiers sources ainsi que les répertoires "gfx" et "sfx" dans le répertoire du projet, incluez tous les fichiers sources au projet, et ça devrait fonctionner.
+
+ Options de compilation :
+ -Wall : Voir tous les warnings.
+ -O3 : Optimisation du code pour la vitesse.
+ -DNDEBUG (= #define NDEBUG) : En mode release, pour que les asserts ne soient plus compilés.
+
+ De plus, pour la version windows, il est nécessaire de rajouter la librairie "libws2_32" dans les "link libraries" du "linker settings".
+
+
+> License :
+
+ Je n'ai pas la prétention que ce code soit bon, puissant, ou quoi que ce soit. En revanche, il fonctionne. Je distribue les sources pour que les gens puissent s'en inspirer, peut-être y trouver des astuces, voire réutiliser une partie du code. Dans ce dernier cas, il est bien entendu que celà doit rester dans un cadre non commercial ! Dans le cas contraire, vous êtes priés de me contacter avant.
+
+ Et pour ceux à qui ce code aura été utile, un p'tit greeting quelque part, c'est toujours sympa ! (^_^)
+
+
+--End of file--
diff --git a/jeu-test/tetris_lan_src/ctypes.h b/jeu-test/tetris_lan_src/ctypes.h
new file mode 100644
index 0000000..70fdf01
--- /dev/null
+++ b/jeu-test/tetris_lan_src/ctypes.h
@@ -0,0 +1,10 @@
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned long u32;
+
+typedef signed char s8;
+typedef signed short s16;
+typedef signed long s32;
+
+
diff --git a/jeu-test/tetris_lan_src/font.c b/jeu-test/tetris_lan_src/font.c
new file mode 100644
index 0000000..24ad3b7
--- /dev/null
+++ b/jeu-test/tetris_lan_src/font.c
@@ -0,0 +1,148 @@
+
+#include "includes.h"
+
+
+// itoa.
+void MyItoa(u32 nNb, char *pDst)
+{
+ char *pPtr;
+ u32 nTmp;
+
+ pPtr = pDst + strlen(pDst) - 1;
+ nTmp = nNb;
+ do
+ {
+ *pPtr-- = (char)((nTmp % 10) + '0');
+ } while (pPtr >= pDst && (nTmp /= 10) > 0);
+
+}
+
+
+// Affichage avec une fonte 16x16.
+// !!! Attention, ce n'est pas prévu pour afficher en dehors de l'écran !!!
+void FontPrint(u32 nOffset, char *pStr)
+{
+ u32 nOffTmp;
+ u32 nOffFnt;
+ u32 y;
+ char cChr;
+ u8 *pScr, *pFnt;
+
+ pScr = (u8 *)gVar.pScreen->pixels;
+ pFnt = (u8 *)gVar.pFont1616->pixels;
+
+ SDL_LockSurface(gVar.pScreen);
+
+ nOffTmp = nOffset;
+ while (*pStr)
+ {
+ cChr = *pStr++;
+ if (cChr != ' ')
+ {
+ nOffFnt = (((u32)cChr) - ' ') << 4;
+ for (y = 0; y < 16; y++)
+ {
+ // On fait un OR + la fonte est en couleur 255 : Transparence.
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width)) |= *(u32 *)(pFnt + nOffFnt + (y * 1024));
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 4) |= *(u32 *)(pFnt + nOffFnt + (y * 1024) + 4);
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 8) |= *(u32 *)(pFnt + nOffFnt + (y * 1024) + 8);
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 12) |= *(u32 *)(pFnt + nOffFnt + (y * 1024) + 12);
+ }
+ }
+ nOffTmp += 16;
+ }
+
+ SDL_UnlockSurface(gVar.pScreen);
+
+}
+
+// Affichage avec une fonte 16x16.
+// !!! Attention, ce n'est pas prévu pour afficher en dehors de l'écran !!!
+// Pareil qu'au dessus, mais fait un trou pour voir la couleur 0 au lieu du OR pour afficher la 255.
+void FontPrintClr(u32 nOffset, char *pStr, u32 nClr)
+{
+ u32 nOffTmp;
+ u32 nOffFnt;
+ u32 y, nMask;
+ char cChr;
+ u8 *pScr, *pFnt;
+
+ pScr = (u8 *)gVar.pScreen->pixels;
+ pFnt = (u8 *)gVar.pFont1616->pixels;
+
+ // Recopie l'index de la couleur sur les 4 bytes du u32.
+ nClr &= 0xFF;
+ nClr |= (nClr << 8) | (nClr << 16) | (nClr << 24);
+
+ SDL_LockSurface(gVar.pScreen);
+
+ nOffTmp = nOffset;
+ while (*pStr)
+ {
+ cChr = *pStr++;
+ if (cChr != ' ')
+ {
+ nOffFnt = (((u32)cChr) - ' ') << 4;
+ for (y = 0; y < 16; y++)
+ {
+ // La fonte est en couleur 255.
+ nMask = *(u32 *)(pFnt + nOffFnt + (y * 1024));
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width)) &= ~nMask;
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width)) |= (nClr & nMask);
+
+ nMask = *(u32 *)(pFnt + nOffFnt + (y * 1024) + 4);
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 4) &= ~nMask;
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 4) |= (nClr & nMask);
+
+ nMask = *(u32 *)(pFnt + nOffFnt + (y * 1024) + 8);
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 8) &= ~nMask;
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 8) |= (nClr & nMask);
+
+ nMask = *(u32 *)(pFnt + nOffFnt + (y * 1024) + 12);
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 12) &= ~nMask;
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 12) |= (nClr & nMask);
+ }
+ }
+ nOffTmp += 16;
+ }
+
+ SDL_UnlockSurface(gVar.pScreen);
+
+}
+
+// Affichage avec une fonte 8x8.
+// !!! Attention, ce n'est pas prévu pour afficher en dehors de l'écran !!!
+void Font8Print(u32 nOffset, char *pStr)
+{
+ u32 nOffTmp;
+ u32 nOffFnt;
+ u32 y;
+ char cChr;
+ u8 *pScr, *pFnt;
+
+ pScr = (u8 *)gVar.pScreen->pixels;
+ pFnt = (u8 *)gVar.pFont88->pixels;
+
+ SDL_LockSurface(gVar.pScreen);
+
+ nOffTmp = nOffset;
+ while (*pStr)
+ {
+ cChr = *pStr++;
+ if (cChr != ' ')
+ {
+ nOffFnt = (((u32)cChr) - ' ') << 3;
+ for (y = 0; y < 8; y++)
+ {
+ // On fait un OR + la fonte est en couleur 255 : Transparence.
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width)) |= *(u32 *)(pFnt + nOffFnt + (y * 512));
+ *(u32 *)(pScr + nOffTmp + (y * SCR_Width) + 4) |= *(u32 *)(pFnt + nOffFnt + (y * 512) + 4);
+ }
+ }
+ nOffTmp += 8;
+ }
+
+ SDL_UnlockSurface(gVar.pScreen);
+
+}
+
diff --git a/jeu-test/tetris_lan_src/font.h b/jeu-test/tetris_lan_src/font.h
new file mode 100644
index 0000000..d8781c8
--- /dev/null
+++ b/jeu-test/tetris_lan_src/font.h
@@ -0,0 +1,7 @@
+
+// Prototypes.
+void FontPrint(u32 nOffset, char *pStr);
+void FontPrintClr(u32 nOffset, char *pStr, u32 nClr);
+void Font8Print(u32 nOffset, char *pStr);
+void MyItoa(u32 nNb, char *pDst);
+
diff --git a/jeu-test/tetris_lan_src/frame.c b/jeu-test/tetris_lan_src/frame.c
new file mode 100644
index 0000000..5236c03
--- /dev/null
+++ b/jeu-test/tetris_lan_src/frame.c
@@ -0,0 +1,28 @@
+
+#include "includes.h"
+
+#define FPS_Default 1000 / 70
+u32 nTimer1, nTimer2;
+
+// Init timers.
+void FrameInit(void)
+{
+ nTimer1 = SDL_GetTicks();
+}
+
+// Attente de la frame.
+void FrameWait(void)
+{
+ // S'assurer que l'on ne va pas trop vite...
+ while (1)
+ {
+ nTimer2 = SDL_GetTicks() - nTimer1;
+ if (nTimer2 >= FPS_Default) break;
+ SDL_Delay(3);
+ }
+ nTimer1 = SDL_GetTicks();
+}
+
+
+
+
diff --git a/jeu-test/tetris_lan_src/frame.h b/jeu-test/tetris_lan_src/frame.h
new file mode 100644
index 0000000..7ac9da1
--- /dev/null
+++ b/jeu-test/tetris_lan_src/frame.h
@@ -0,0 +1,6 @@
+
+// Prototypes.
+void FrameInit(void);
+void FrameWait(void);
+
+
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);
+}
+
+
diff --git a/jeu-test/tetris_lan_src/game.h b/jeu-test/tetris_lan_src/game.h
new file mode 100644
index 0000000..7bf672d
--- /dev/null
+++ b/jeu-test/tetris_lan_src/game.h
@@ -0,0 +1,9 @@
+
+// Prototypes.
+void TetrisInit(void);
+u32 TetrisMain(void);
+
+void Fade(s32 nFadeVal, SDL_Surface *pSurf);
+
+
+
diff --git a/jeu-test/tetris_lan_src/gfx/bkg.bmp b/jeu-test/tetris_lan_src/gfx/bkg.bmp
new file mode 100644
index 0000000..89d1f77
--- /dev/null
+++ b/jeu-test/tetris_lan_src/gfx/bkg.bmp
Binary files differ
diff --git a/jeu-test/tetris_lan_src/gfx/bkg_menu.bmp b/jeu-test/tetris_lan_src/gfx/bkg_menu.bmp
new file mode 100644
index 0000000..fb270ab
--- /dev/null
+++ b/jeu-test/tetris_lan_src/gfx/bkg_menu.bmp
Binary files differ
diff --git a/jeu-test/tetris_lan_src/gfx/fnt.bmp b/jeu-test/tetris_lan_src/gfx/fnt.bmp
new file mode 100644
index 0000000..fd02fd5
--- /dev/null
+++ b/jeu-test/tetris_lan_src/gfx/fnt.bmp
Binary files differ
diff --git a/jeu-test/tetris_lan_src/gfx/fnt8.bmp b/jeu-test/tetris_lan_src/gfx/fnt8.bmp
new file mode 100644
index 0000000..513ad4c
--- /dev/null
+++ b/jeu-test/tetris_lan_src/gfx/fnt8.bmp
Binary files differ
diff --git a/jeu-test/tetris_lan_src/high.scr b/jeu-test/tetris_lan_src/high.scr
new file mode 100644
index 0000000..9817614
--- /dev/null
+++ b/jeu-test/tetris_lan_src/high.scr
Binary files differ
diff --git a/jeu-test/tetris_lan_src/includes.h b/jeu-test/tetris_lan_src/includes.h
new file mode 100644
index 0000000..9f3c236
--- /dev/null
+++ b/jeu-test/tetris_lan_src/includes.h
@@ -0,0 +1,108 @@
+
+// Includes.
+#include <stdlib.h>
+#include <unistd.h>
+#include "SDL.h"
+//#include "SDL_image.h"
+#include <time.h>
+
+#include <math.h>
+#define PI 3.1415927
+
+#include "ctypes.h"
+
+#include "menu.h"
+#include "game.h"
+#include "frame.h"
+#include "font.h"
+#include "sfx.h"
+#include "tcpip.h"
+
+// Define.
+#define SCR_Width 320
+#define SCR_Height 240
+
+enum
+{
+ TRIG_UP = 0,
+ TRIG_DOWN, // Trig down et return => pour le menu.
+ TRIG_RIGHT,
+ TRIG_LEFT,
+ TRIG_Return,
+ TRIG_Space,
+ TRIG_KeyP,
+ TRIG_MaxKeys
+};
+
+enum
+{
+ MENU_Null = 0, // Codes de sortie du menu.
+ MENU_Main,
+ MENU_Quit,
+ MENU_Game_1Player,
+ MENU_Game_LAN,
+ MENU_Options,
+ MENU_HallOfFame,
+ MENU_MaxItem
+};
+
+enum
+{
+ GAME_Null = 0, // Codes de sortie du jeu.
+ GAME_GameOver,
+ GAME_Quit,
+ GAME_MaxItem
+};
+
+enum
+{
+ GAME_MODE_1Player = 0,
+ GAME_MODE_LAN,
+};
+
+enum
+{
+ OPT_Ghost = 1,
+ OPT_Rotation = 1 << 1,
+ OPT_BlocksSet = 1 << 2,
+ OPT_Sound = 1 << 3,
+ OPT_LAN_Hole = 1 << 4,
+};
+
+// Types de variables.
+struct SGene
+{
+ SDL_Surface *pScreen; // Ptr sur le buffer écran.
+ u8 *pKeys; // Buffer clavier (ira pointer une table fournie par SDL).
+ u8 pKeysTrig[TRIG_MaxKeys]; // Trigger.
+
+ SDL_Surface *pBackground; // Ptr sur l'image de décor.
+ SDL_Surface *pBkgMenu; // L'image de fond du menu.
+ SDL_Surface *pFont1616; // Ptr sur la fonte 16x16.
+ SDL_Surface *pFont88; // Ptr sur la fonte 8x8.
+
+ SDL_Surface *pCurBkg; // Ptr sur l'image de fond courante.
+
+ u32 nOptFlags; // Options.
+ u32 nGameMode; // Mode 1 joueur / LAN.
+
+ u32 nTCPMaster; // 1 = Master / 0 = slave.
+ char *pIPAddress;
+ u32 nPort;
+
+ u32 pWins[2]; // Nombre de victoires en LAN.
+
+};
+
+struct SExchange // Echanges entre les modules.
+{
+ u32 nStartingLevel; // Niveau de départ.
+ u32 nHandicap; // Handicap de départ.
+ u32 nScore; // Score en sortie.
+};
+
+// Variables générales.
+extern struct SGene gVar;
+extern struct SExchange gExg;
+
+
diff --git a/jeu-test/tetris_lan_src/main.c b/jeu-test/tetris_lan_src/main.c
new file mode 100644
index 0000000..b410889
--- /dev/null
+++ b/jeu-test/tetris_lan_src/main.c
@@ -0,0 +1,320 @@
+#ifdef __cplusplus
+ #include <cstdlib>
+#else
+ #include <stdlib.h>
+#endif
+#ifdef __APPLE__
+#include <SDL/SDL.h>
+#else
+#include <SDL.h>
+#endif
+
+//-----------------------
+//
+// A Little Tetris Game
+// Done by Clément CORDE
+// c1702@yahoo.com
+//
+//-----------------------
+
+#include "includes.h"
+
+
+// Variables générales.
+struct SGene gVar;
+struct SExchange gExg;
+
+
+// Gestionnaire d'évènements.
+int EventHandler(void)
+{
+ static u8 nFullScreen = 0;
+ SDL_Event event;
+
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_KEYDOWN:
+ gVar.pKeys = SDL_GetKeyState(NULL);
+
+ // Triggers.
+ if (event.key.keysym.sym == SDLK_UP) gVar.pKeysTrig[TRIG_UP] = 1;
+ if (event.key.keysym.sym == SDLK_DOWN) gVar.pKeysTrig[TRIG_DOWN] = 1;
+ if (event.key.keysym.sym == SDLK_RIGHT) gVar.pKeysTrig[TRIG_RIGHT] = 1;
+ if (event.key.keysym.sym == SDLK_LEFT) gVar.pKeysTrig[TRIG_LEFT] = 1;
+ if (event.key.keysym.sym == SDLK_p) gVar.pKeysTrig[TRIG_KeyP] = 1;
+ if (event.key.keysym.sym == SDLK_RETURN) gVar.pKeysTrig[TRIG_Return] = 1;
+ if (event.key.keysym.sym == SDLK_SPACE) gVar.pKeysTrig[TRIG_Space] = 1;
+
+ // Ingame toggles.
+ if (event.key.keysym.sym == SDLK_r) gVar.nOptFlags ^= OPT_Rotation;
+ if (event.key.keysym.sym == SDLK_g) gVar.nOptFlags ^= OPT_Ghost;
+
+ if (gVar.pKeys[SDLK_ESCAPE]) // Arrêt d'urgence !
+ {
+ TCP_CnxClose();
+ exit(0);//return (1);
+ }
+
+ // Toggle fullscreen/windowed.
+ if (gVar.pKeys[SDLK_F10])
+ {
+ SDL_Surface *pTmp = gVar.pScreen;
+
+ gVar.pScreen = SDL_SetVideoMode(SCR_Width, SCR_Height, 8, SDL_HWSURFACE | SDL_DOUBLEBUF | ((nFullScreen ^ 1) ? SDL_FULLSCREEN : 0));
+ if (gVar.pScreen == NULL)
+ {
+ // Raté.
+ fprintf(stderr, "Couldn't set video mode: %sn",SDL_GetError());
+ gVar.pScreen = pTmp; // Récupère l'ancien.
+ }
+ else
+ {
+ // Ok.
+ SDL_SetPalette(gVar.pScreen, SDL_LOGPAL | SDL_PHYSPAL, gVar.pCurBkg->format->palette->colors, 0, gVar.pCurBkg->format->palette->ncolors);
+ SDL_FreeSurface(pTmp); // Libère l'ancien.
+ nFullScreen ^= 1;
+ }
+ }
+
+ break;
+
+ case SDL_KEYUP:
+ gVar.pKeys = SDL_GetKeyState(NULL);
+ break;
+
+ case SDL_QUIT: // Fermeture de la fenêtre.
+ TCP_CnxClose();
+ exit(0);
+ break;
+ }
+ }
+ return (0);
+}
+
+// Load image.
+void LoadPic(SDL_Surface **pDst, char *pFilename)
+{
+ *pDst = SDL_LoadBMP(pFilename);
+ if (*pDst == NULL) {
+ fprintf(stderr, "Couldn't load picture '%s': %s\n", pFilename, SDL_GetError());
+ exit(1);
+ }
+}
+
+// Le Menu (générique).
+u32 Menu(void (*pFctInit)(void), u32 (*pFctMain)(void))
+{
+ u32 i;
+ u32 nMenuVal = MENU_Null;
+
+ // Sets up palette.
+ //SDL_SetColors(gVar.pScreen, gVar.pBkgMenu->format->palette->colors, 0, gVar.pBkgMenu->format->palette->ncolors);
+ SDL_SetPalette(gVar.pScreen, SDL_LOGPAL, gVar.pBkgMenu->format->palette->colors, 0, gVar.pBkgMenu->format->palette->ncolors);
+ gVar.pCurBkg = gVar.pBkgMenu;
+
+ // Main loop.
+ (*pFctInit)();
+ gVar.pKeys = SDL_GetKeyState(NULL); // Lecture dans le vide, pour init du ptr.
+ FrameInit();
+ while (nMenuVal == MENU_Null)
+ {
+ // Nettoyage du trigger.
+ for (i = 0; i < TRIG_MaxKeys; i++) gVar.pKeysTrig[i] = 0;
+
+ // Gestion des évenements.
+ EventHandler();
+
+ // Recopie le décor.
+ if (SDL_BlitSurface(gVar.pBkgMenu, NULL, gVar.pScreen, NULL) < 0)
+ {
+ fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ // Menu Main.
+ nMenuVal = (*pFctMain)();
+
+ // Wait for frame.
+ FrameWait();
+ SDL_Flip(gVar.pScreen); // => Refresh écran.
+
+ }
+
+ return (nMenuVal);
+}
+
+// Le jeu.
+void Game(void)
+{
+ u32 i;
+ u32 nTetVal = GAME_Null;
+
+ // Sets up palette.
+ //SDL_SetColors(gVar.pScreen, gVar.pBackground->format->palette->colors, 0, gVar.pBackground->format->palette->ncolors);
+ SDL_SetPalette(gVar.pScreen, SDL_LOGPAL, gVar.pBackground->format->palette->colors, 0, gVar.pBackground->format->palette->ncolors);
+ gVar.pCurBkg = gVar.pBackground;
+
+ // Main loop.
+ TetrisInit();
+ gVar.pKeys = SDL_GetKeyState(NULL); // Lecture dans le vide, pour init du ptr.
+ FrameInit();
+ while (nTetVal == GAME_Null)
+ {
+ // Nettoyage du trigger.
+ for (i = 0; i < TRIG_MaxKeys; i++) gVar.pKeysTrig[i] = 0;
+
+ // Gestion des évenements.
+ EventHandler();
+
+ // Recopie le décor. (possible optim => seulement l'aire de jeu).
+ if (SDL_BlitSurface(gVar.pBackground, NULL, gVar.pScreen, NULL) < 0)
+ {
+ fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ // Game.
+ nTetVal = TetrisMain();
+
+ // Wait for frame.
+ FrameWait();
+ SDL_Flip(gVar.pScreen); // => Refresh écran.
+
+ }
+
+ // High score ? (Slt en mode 1 joueur).
+ if (gVar.nGameMode == GAME_MODE_1Player && nTetVal == GAME_GameOver)
+ {
+ if (Scr_CheckHighSc(gExg.nScore) >= 0)
+ {
+ // Saisie du nom.
+ Menu(MenuGetNameInit, MenuGetName);
+ // Affichage de la table des high scores.
+ Menu(MenuMainInit, MenuHighScores); // !!! Même init que main !!!
+ }
+ }
+
+}
+
+// Point d'entrée.
+int main(int argc, char *argv[])
+{
+ u32 nLoop = 1;
+ u32 nMenuVal;
+
+
+//> revoir comment faire
+ // Récupération des paramètres de la ligne de commande :
+ // 1 = Adresse IP.
+ // 2 = Port.
+ if (argc != 3)
+ {
+ printf("Usage: Tetris <IP address> <Port>\n");
+ exit (1);
+ }
+ gVar.pIPAddress = argv[1];
+ gVar.nPort = atoi(argv[2]);
+ printf("Connect to %s, port %d\n", gVar.pIPAddress, (int)gVar.nPort);
+//<
+
+
+ // SDL Init.
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
+ exit(1);
+ }
+ // atexit : Quand on quittera (exit, return...), SDL_Quit() sera appelée.
+ atexit(SDL_Quit);
+
+ // Video mode init.
+ gVar.pScreen = SDL_SetVideoMode(SCR_Width, SCR_Height, 8, SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF);
+ if (gVar.pScreen == NULL)
+ {
+ fprintf(stderr, "Couldn't set video mode: %sn",SDL_GetError());
+ exit(1);
+ }
+ SDL_WM_SetCaption("Tetris", NULL); // Nom de la fenêtre.
+
+ // Load backgound picture.
+ LoadPic(&gVar.pBackground, "gfx/bkg.bmp");
+ // Load menu backgound picture.
+ LoadPic(&gVar.pBkgMenu, "gfx/bkg_menu.bmp");
+ // Load fonts.
+ LoadPic(&gVar.pFont1616, "gfx/fnt.bmp");
+ LoadPic(&gVar.pFont88, "gfx/fnt8.bmp");
+
+ // Init sound.
+ Sfx_SoundInit();
+ Sfx_LoadWavFiles();
+ Sfx_SoundOn(); // Starts playback.
+
+ TCP_EngineInit();
+
+ Scr_Load(); // Lecture du fichier des high scores.
+ Opt_Load(); // Lecture des options.
+
+ MenuInitMisc();
+ gVar.pWins[0] = gVar.pWins[1] = 0; // RAZ du nombre de victoires en LAN.
+
+ SDL_ShowCursor(SDL_DISABLE); // Cache le pointeur de la souris.
+
+ // Boucle infinie.
+ gExg.nStartingLevel = 1;
+ gExg.nHandicap = 0;
+ nMenuVal = MENU_Main;
+ while (nLoop)
+ {
+ switch (nMenuVal)
+ {
+ case MENU_Main : // Main menu.
+ nMenuVal = Menu(MenuMainInit2, MenuMain);
+ break;
+
+ case MENU_Game_1Player : // Jeu : 1 joueur.
+ gVar.nGameMode = GAME_MODE_1Player;
+ Game();
+ nMenuVal = MENU_Main;
+ break;
+
+ case MENU_Game_LAN : // Jeu : LAN.
+ gVar.nGameMode = GAME_MODE_LAN;
+ Game();
+ nMenuVal = MENU_Main;
+ break;
+
+ case MENU_HallOfFame : // High scores.
+ Menu(MenuMainInit, MenuHighScores); // !!! Même init que main !!!
+ nMenuVal = MENU_Main;
+ break;
+
+ case MENU_Options : // Options.
+ Menu(MenuMainInit, MenuOptions); // !!! Même init que main !!!
+ nMenuVal = MENU_Main;
+ break;
+
+ case MENU_Quit : // Sortie.
+ nLoop = 0;
+ break;
+ }
+
+ }
+
+ SDL_ShowCursor(SDL_ENABLE); // Réautorise l'affichage du curseur de la souris.
+
+ Sfx_SoundOff(); // Stops playback.
+ Sfx_FreeWavFiles(); // Libère les ressources des fx.
+
+ // Free the allocated BMP surfaces.
+ SDL_FreeSurface(gVar.pBackground);
+ SDL_FreeSurface(gVar.pBkgMenu);
+ SDL_FreeSurface(gVar.pFont1616);
+ SDL_FreeSurface(gVar.pFont88);
+
+ TCP_EngineClose();
+
+ return (0);
+}
+
+
diff --git a/jeu-test/tetris_lan_src/menu.c b/jeu-test/tetris_lan_src/menu.c
new file mode 100644
index 0000000..5e6b276
--- /dev/null
+++ b/jeu-test/tetris_lan_src/menu.c
@@ -0,0 +1,711 @@
+
+#include "includes.h"
+
+#define NBELEM(tab) (sizeof(tab) / sizeof(tab[0]))
+
+#define CURS_Acc 0x200
+
+enum
+{
+ MENU_State_FadeIn = 0,
+ MENU_State_Input,
+ MENU_State_FadeOut,
+};
+
+#define OPT_Filename "tetris.opt"
+
+#define MENU_SFX_VALIDATION e_Sfx_MenuValidate
+#define MENU_SFX_CLIC e_Sfx_MenuClic
+
+#define HISC_Nb 8
+#define HISC_NameLg (10+1)
+#define HISC_Filename "high.scr"
+struct SScore
+{
+ char pName[HISC_NameLg];
+ u32 nScore;
+};
+struct SScore gpHighScores[HISC_Nb];
+
+struct SMenu
+{
+ u32 nChoix;
+ u32 nState; // Etat (fade in...).
+
+ s32 nFadeVal;
+
+ u8 nSin; // Pour bouffoner le titre.
+
+ s16 nCursPos; // Pour faire bouger le curseur.
+ s16 nCursAccel;
+
+ // Variables pour saisie du nom.
+ u32 nScIdx; // Pos dans la chaîne. / Pas remis à 0 !
+ char pScName[HISC_NameLg]; // Nom saisi. / Pas remis à 0 !
+
+ u8 pScKeyDown; // Pour pseudo trigger.
+ u32 nScRank; // Rang de classement.
+
+};
+struct SMenu gMenu;
+
+struct SMenuItm
+{
+ u32 nRval; // Valeur renvoyée. MENU_Null => Pas une ligne à valider.
+ u32 *pVal; // Si valeur à afficher dans une ligne, un ptr dessus.
+ u32 nValMin, nValMax; // Bornes de la valeur ci dessus.
+ char pStr[16]; // Texte affiché. On remplace le @ par la valeur.
+};
+
+
+
+// Curseur - Init.
+void CurseurInit(void)
+{
+ gMenu.nCursPos = 0;
+ gMenu.nCursAccel = CURS_Acc;
+}
+
+// Curseur - Déplacement.
+void CurseurMove(void)
+{
+ gMenu.nCursAccel -= 0x20; // Gravité.
+ gMenu.nCursPos += gMenu.nCursAccel;
+ if (gMenu.nCursPos < 0) CurseurInit();
+}
+
+// Menu principal - Init.
+// On pourrait passer un prm au cas où il y a ait plusieurs menus du même genre.
+void MenuMainInit(void)
+{
+ gMenu.nChoix = 0;
+
+ gMenu.nState = MENU_State_FadeIn;
+ gMenu.nFadeVal = 0;
+
+ gMenu.nSin = 0;
+ CurseurInit();
+
+}
+// Idem, avec choix qui se replace sur 1 joueur ou LAN.
+void MenuMainInit2(void)
+{
+ MenuMainInit();
+ gMenu.nChoix = gVar.nGameMode;
+}
+
+// Menu - Init misc.
+void MenuInitMisc(void)
+{
+ MenuMainInit();
+
+ char pEmptyName[HISC_NameLg] = "..........";
+ gMenu.nScIdx = 0;
+ strcpy(gMenu.pScName, pEmptyName);
+
+}
+
+// Menu principal.
+#define MENUMAIN_INTERLIGNE 25
+u32 MenuMain(void)
+{
+ // >>> Possibilité de sortir ça pour faire un autre menu du même type.
+ struct SMenuItm pItems[] =
+ {
+ { MENU_Game_1Player, NULL, 0, 0, "1 PLAYER MODE" },
+ { MENU_Game_LAN, &gVar.nTCPMaster, 0, 1, "LAN PLAY : " },
+// { MENU_Null, &gExg.nStartingLevel, 1, 5, "LEVEL : @" },
+ { MENU_Null, &gExg.nStartingLevel, 1, 9, "LEVEL : @" },
+ { MENU_Null, &gExg.nHandicap, 0, 5, "HANDICAP : @" },
+ { MENU_HallOfFame, NULL, 0, 0, "HALL OF FAME" },
+ { MENU_Options, NULL, 0, 0, "OPTIONS" },
+ { MENU_Quit, NULL, 0, 0, "QUIT" },
+ };
+ u32 nNbLines = NBELEM(pItems);
+ // <<<
+
+ u32 nRet = MENU_Null;
+ u32 i;
+ u32 nPosX, nPosY;
+
+
+ // Selon l'état.
+ switch (gMenu.nState)
+ {
+ case MENU_State_FadeIn :
+ gMenu.nFadeVal += 4;
+ if (gMenu.nFadeVal > 256)
+ {
+ gMenu.nState = MENU_State_Input;
+ gMenu.nFadeVal = -1;
+ }
+ break;
+
+ case MENU_State_FadeOut :
+ gMenu.nFadeVal -= 4;
+ if (gMenu.nFadeVal < 0)
+ {
+ nRet = pItems[gMenu.nChoix].nRval; // Valeur choisie par l'utilisateur.
+ }
+ break;
+
+ case MENU_State_Input :
+ // Gestion du clavier.
+ if (gVar.pKeysTrig[TRIG_UP])
+ {
+ if (--gMenu.nChoix >= nNbLines) gMenu.nChoix = nNbLines - 1;
+ Sfx_PlaySfx(MENU_SFX_CLIC, e_SfxPrio_10);
+ }
+ if (gVar.pKeysTrig[TRIG_DOWN])
+ {
+ if (++gMenu.nChoix >= nNbLines) gMenu.nChoix = 0;
+ Sfx_PlaySfx(MENU_SFX_CLIC, e_SfxPrio_10);
+ }
+
+ // Une valeur numérique à changer ?
+ if (pItems[gMenu.nChoix].pVal != NULL)
+ {
+ if (gVar.pKeysTrig[TRIG_RIGHT])
+ {
+ if (++*pItems[gMenu.nChoix].pVal > pItems[gMenu.nChoix].nValMax)
+ *pItems[gMenu.nChoix].pVal = pItems[gMenu.nChoix].nValMax;
+ else
+ Sfx_PlaySfx(MENU_SFX_CLIC, e_SfxPrio_10);
+ }
+ if (gVar.pKeysTrig[TRIG_LEFT])
+ {
+ if (--*(s32 *)pItems[gMenu.nChoix].pVal < (s32)pItems[gMenu.nChoix].nValMin)
+ *pItems[gMenu.nChoix].pVal = pItems[gMenu.nChoix].nValMin;
+ else
+ Sfx_PlaySfx(MENU_SFX_CLIC, e_SfxPrio_10);
+ }
+ }
+
+ // Validation.
+ if ((gVar.pKeysTrig[TRIG_Return] || gVar.pKeysTrig[TRIG_Space]) && pItems[gMenu.nChoix].nRval != MENU_Null)
+ {
+ gMenu.nState = MENU_State_FadeOut;
+ gMenu.nFadeVal = 256;
+ Sfx_PlaySfx(MENU_SFX_VALIDATION, e_SfxPrio_10);
+ }
+ break;
+ }
+
+ // Affichage.
+ CurseurMove();
+
+ // Affichage des lignes.
+ nPosX = 40;
+ nPosY = (SCR_Height - ((nNbLines * MENUMAIN_INTERLIGNE) - (MENUMAIN_INTERLIGNE / 2))) / 2;
+ for (i = 0; i < nNbLines; i++)
+ {
+ // Une valeur numérique ?
+ if (pItems[i].pVal != NULL)
+ {
+ char *pPtr = strchr(pItems[i].pStr, '@');
+ if (pPtr != NULL) *pPtr = (char)(0x30 + *pItems[i].pVal);
+ }
+
+ // Print.
+ FontPrint((SCR_Width * (nPosY + (i * MENUMAIN_INTERLIGNE))) + nPosX, pItems[i].pStr);
+ // Et le patch dégueulasse pour la ligne Serveur/Client...
+ if (pItems[i].nRval == MENU_Game_LAN)
+ {
+ FontPrint((SCR_Width * (nPosY + (i * MENUMAIN_INTERLIGNE))) + nPosX + (strlen(pItems[i].pStr) * 16), *pItems[i].pVal ? "SERVER" : "CLIENT");
+ }
+
+ // Curseur.
+ if (i == gMenu.nChoix)
+ {
+ FontPrint((SCR_Width * (nPosY + (i * MENUMAIN_INTERLIGNE))) + (nPosX - 24) - (gMenu.nCursPos >> 8), ">");
+ }
+ }
+
+ // Le titre.
+ nPosX = (s32) (cos(gMenu.nSin * 2 * PI / 256) * ((SCR_Width - (6*16)) / 2)); // Faire préca table si trop lent.
+ FontPrint((SCR_Width * 8) + ((SCR_Width - (6*16)) / 2) + nPosX, "TETRIS");
+ FontPrint((SCR_Width * (SCR_Height - 16 - 8)) + ((SCR_Width - (6*16)) / 2) - nPosX, "TETRIS");
+ gMenu.nSin++;
+
+ Fade(gMenu.nFadeVal, gVar.pBkgMenu);
+
+ return (nRet); // Renvoie le choix.
+}
+
+
+// Menu item options
+struct SOptMenuItm
+{
+ u32 nRval; // Valeur renvoyée. MENU_Null => Pas une ligne à valider.
+ u32 nBit; // Bit à XORer.
+ char pStr[16]; // Texte affiché.
+ u8 nSkipLn; // Sauter ligne ou pas.
+ char pStrBit0[10]; // Texte affiché quand bit = 0.
+ char pStrBit1[10]; // Texte affiché quand bit = 1.
+};
+
+// Menu des options.
+#define MENUOPT_INTERLIGNE 26//28
+#define MENUOPT_INTERLIGNE2 20
+u32 MenuOptions(void)
+{
+ struct SOptMenuItm pItems[] =
+ {
+ { MENU_Null, OPT_Ghost, "GHOST:", 0, "OFF", "ON" },
+ { MENU_Null, OPT_Rotation, "ROTATION:", 1, "CLOCKWISE", "REVERSE" },
+ { MENU_Null, OPT_BlocksSet, "BLOCKS SET:", 1, "NORMAL", "EXTENDED" },
+ { MENU_Null, OPT_LAN_Hole, "LAN RND HOLE:", 0, "OFF", "ON" },
+ { MENU_Null, OPT_Sound, "SOUND:", 0, "OFF", "ON" },
+ { MENU_Main, 0, "MAIN MENU", 0, "", "" },
+ };
+ u32 nNbLines = NBELEM(pItems);
+
+ u32 nRet = MENU_Null;
+ u32 i;
+ u32 nPosX, nPosY;
+
+
+ // Selon l'état.
+ switch (gMenu.nState)
+ {
+ case MENU_State_FadeIn :
+ gMenu.nFadeVal += 4;
+ if (gMenu.nFadeVal > 256)
+ {
+ gMenu.nState = MENU_State_Input;
+ gMenu.nFadeVal = -1;
+ }
+ break;
+
+ case MENU_State_FadeOut :
+ gMenu.nFadeVal -= 4;
+ if (gMenu.nFadeVal < 0)
+ {
+ Opt_Save();
+ nRet = pItems[gMenu.nChoix].nRval; // Valeur choisie par l'utilisateur.
+ }
+ break;
+
+ case MENU_State_Input :
+ // Gestion du clavier.
+ if (gVar.pKeysTrig[TRIG_UP])
+ {
+ if (--gMenu.nChoix >= nNbLines) gMenu.nChoix = nNbLines - 1;
+ Sfx_PlaySfx(MENU_SFX_CLIC, e_SfxPrio_10);
+ }
+ if (gVar.pKeysTrig[TRIG_DOWN])
+ {
+ if (++gMenu.nChoix >= nNbLines) gMenu.nChoix = 0;
+ Sfx_PlaySfx(MENU_SFX_CLIC, e_SfxPrio_10);
+ }
+
+ // Une valeur à changer ?
+ if (pItems[gMenu.nChoix].nBit != 0)
+ {
+ if (gVar.pKeysTrig[TRIG_Return] || gVar.pKeysTrig[TRIG_Space] ||
+ gVar.pKeysTrig[TRIG_LEFT] || gVar.pKeysTrig[TRIG_RIGHT])
+ {
+ gVar.nOptFlags ^= pItems[gMenu.nChoix].nBit;
+ Sfx_PlaySfx(MENU_SFX_CLIC, e_SfxPrio_10);
+ }
+ }
+
+ // Validation.
+ if ((gVar.pKeysTrig[TRIG_Return] || gVar.pKeysTrig[TRIG_Space]) && pItems[gMenu.nChoix].nRval != MENU_Null)
+ {
+ gMenu.nState = MENU_State_FadeOut;
+ gMenu.nFadeVal = 256;
+ Sfx_PlaySfx(MENU_SFX_VALIDATION, e_SfxPrio_10);
+ }
+ break;
+ }
+
+ // Affichage.
+ CurseurMove();
+
+ // Titre.
+ char pTitle[] = "OPTIONS";
+ FontPrint((SCR_Width * 8) + ((SCR_Width - (strlen(pTitle) * 16)) / 2), pTitle);
+
+ // Affichage des lignes.
+ nPosX = 40;
+ nPosY = 42;
+ for (i = 0; i < nNbLines; i++)
+ {
+ // Curseur.
+ if (i == gMenu.nChoix)
+ {
+ FontPrint((SCR_Width * nPosY) + (nPosX - 24) - (gMenu.nCursPos >> 8), ">");
+ }
+ // Print.
+ FontPrint((SCR_Width * nPosY) + nPosX, pItems[i].pStr);
+ // Valeur du bit.
+ if (pItems[i].nBit)
+ {
+ char *pStr = (gVar.nOptFlags & pItems[i].nBit ? pItems[i].pStrBit1 : pItems[i].pStrBit0);
+ if (pItems[i].nSkipLn) nPosY += MENUOPT_INTERLIGNE2;
+ FontPrint((SCR_Width * nPosY) + SCR_Width - (strlen(pStr) * 16) - 16, pStr);
+ }
+ nPosY += MENUOPT_INTERLIGNE;
+ }
+
+ Fade(gMenu.nFadeVal, gVar.pBkgMenu);
+
+ return (nRet); // Renvoie le choix.
+}
+
+
+// Menu des High-scores.
+u32 MenuHighScores(void)
+{
+ u32 nRet = MENU_Null;
+ u32 i;
+ u32 nPosX, nPosY;
+
+
+ // Selon l'état.
+ switch (gMenu.nState)
+ {
+ case MENU_State_FadeIn :
+ gMenu.nFadeVal += 4;
+ if (gMenu.nFadeVal > 256)
+ {
+ gMenu.nState = MENU_State_Input;
+ gMenu.nFadeVal = -1;
+ }
+ break;
+
+ case MENU_State_FadeOut :
+ gMenu.nFadeVal -= 4;
+ if (gMenu.nFadeVal < 0)
+ {
+ nRet = MENU_Main; // Sortie.
+ }
+ break;
+
+ case MENU_State_Input :
+ // Gestion du clavier.
+ if (gVar.pKeysTrig[TRIG_Return] || gVar.pKeysTrig[TRIG_Space])
+ {
+ gMenu.nState = MENU_State_FadeOut;
+ gMenu.nFadeVal = 256;
+ Sfx_PlaySfx(MENU_SFX_VALIDATION, e_SfxPrio_10);
+ }
+ break;
+ }
+
+ // Affichage.
+ //CurseurMove();
+
+ // Titre.
+ char pTitle[] = "HALL OF FAME";
+ FontPrint((SCR_Width * 8) + ((SCR_Width - (strlen(pTitle) * 16)) / 2), pTitle);
+
+ // Affichage des lignes.
+ nPosX = 8;
+ nPosY = 40;
+ for (i = 0; i < HISC_Nb; i++)
+ {
+ char pStr[] = " ";
+
+ // Nom.
+ FontPrint((SCR_Width * (nPosY + (i * 24))) + nPosX, gpHighScores[i].pName);
+ // Score.
+ MyItoa(gpHighScores[i].nScore, pStr);
+ FontPrint((SCR_Width * (nPosY + (i * 24))) + SCR_Width - (7 * 16) - 8, pStr);
+
+// /// Curseur. / Voir plus tard, pour marquer le score qui vient d'être fait ?
+// if (i == gMenu.nChoix)
+// {
+// FontPrint((SCR_Width * (nPosY + (i * 32))) + (nPosX - 24) - (gMenu.nCursPos >> 8), ">");
+// }
+ }
+
+ Fade(gMenu.nFadeVal, gVar.pBkgMenu);
+
+ return (nRet);
+}
+
+
+// Scores - Check si un score entre au Hall of Fame.
+// Out : -1, pas dedans / >= 0, rang.
+s32 Scr_CheckHighSc(u32 nScorePrm)
+{
+ s32 i, nRank;
+
+ nRank = -1;
+ for (i = HISC_Nb - 1; i >= 0; i--)
+ {
+ if (nScorePrm >= gpHighScores[i].nScore)
+ {
+ nRank = i;
+ }
+ }
+
+ return (nRank);
+}
+
+// Insère un nom dans la table.
+void Scr_PutNameInTable(char *pName, u32 nScore)
+{
+ s32 nRank = Scr_CheckHighSc(nScore);
+ s32 i;
+
+ if (nRank < 0) return; // Ne devrait pas arriver.
+
+ // Décalage de la table.
+ for (i = HISC_Nb - 2; i >= nRank; i--)
+ {
+ strcpy(gpHighScores[i + 1].pName, gpHighScores[i].pName);
+ gpHighScores[i + 1].nScore = gpHighScores[i].nScore;
+ }
+ // Le score à insérer.
+ strcpy(gpHighScores[nRank].pName, pName);
+ gpHighScores[nRank].nScore = nScore;
+
+}
+
+
+// RAZ de la table des high scores.
+void Scr_RazTable(void)
+{
+ char pDefault[HISC_NameLg] = "..........";
+ u32 i;
+
+ for (i = 0; i < HISC_Nb; i++)
+ {
+ strcpy(gpHighScores[i].pName, pDefault);
+ gpHighScores[i].nScore = 0;
+ }
+
+}
+
+// Calcule le checksum de la table des scores.
+u32 Scr_CalcChecksum(void)
+{
+ u32 i, j;
+ u32 nChk = 0;
+
+ for (i = 0; i < HISC_Nb; i++)
+ {
+ nChk += gpHighScores[i].nScore;
+ for (j = 0; j < HISC_NameLg; j++) nChk += ((u32)gpHighScores[i].pName[j]) << (8 * (j & 3));
+ }
+ return (nChk);
+}
+
+// Lecture du fichier des high scores.
+void Scr_Load(void)
+{
+ FILE *pFile;
+ u32 nChk;
+
+ if ((pFile = fopen(HISC_Filename, "rb")) != NULL)
+ {
+ // Le fichier existe, lecture.
+ fread(gpHighScores, sizeof(struct SScore), HISC_Nb, pFile);
+ fread(&nChk, sizeof(u32), 1, pFile);
+ fclose(pFile);
+ // Checksum ok ?
+ if (nChk != Scr_CalcChecksum())
+ {
+ // Wrong checksum, RAZ table.
+ printf("Scr_Load: Wrong checksum! Resetting table.\n");
+ Scr_RazTable();
+ }
+ }
+ else
+ {
+ // Le fichier n'existe pas, RAZ table.
+ Scr_RazTable();
+ }
+
+}
+
+// Sauvegarde du fichier des high scores.
+void Scr_Save(void)
+{
+ FILE *pFile;
+ u32 nChk;
+
+ if ((pFile = fopen(HISC_Filename, "wb")) == NULL)
+ {
+ printf("Unable to save highscores table.\n");
+ return;
+ }
+ // Sauvegarde des enregistrements.
+ fwrite(gpHighScores, sizeof(struct SScore), HISC_Nb, pFile);
+ // Checksum.
+ nChk = Scr_CalcChecksum();
+ fwrite(&nChk, sizeof(u32), 1, pFile);
+ fclose(pFile);
+
+}
+
+
+// Lecture du fichier des options.
+void Opt_Load(void)
+{
+ FILE *pFile;
+
+ if ((pFile = fopen(OPT_Filename, "rb")) != NULL)
+ {
+ // Le fichier existe, lecture.
+ fread(&gVar.nOptFlags, sizeof(gVar.nOptFlags), 1, pFile);
+ fclose(pFile);
+ }
+ else
+ {
+ // Le fichier n'existe pas, raz flags.
+ gVar.nOptFlags = OPT_Rotation | OPT_Sound;
+ }
+
+}
+
+// Sauvegarde du fichier des options.
+void Opt_Save(void)
+{
+ FILE *pFile;
+
+ if ((pFile = fopen(OPT_Filename, "wb")) == NULL)
+ {
+ printf("Unable to save options.\n");
+ return;
+ }
+ // Sauvegarde des flags.
+ fwrite(&gVar.nOptFlags, sizeof(gVar.nOptFlags), 1, pFile);
+ fclose(pFile);
+
+}
+
+// Init.
+void MenuGetNameInit(void)
+{
+ MenuMainInit();
+ gMenu.pScKeyDown = 0; // Pseudo trigger pour saisie du nom.
+ gMenu.nScRank = Scr_CheckHighSc(gExg.nScore); // Rang.
+
+}
+
+// Saisie du nom quand high-score.
+u32 MenuGetName(void)
+{
+ u32 nRet = MENU_Null;
+ u32 i;
+ static u8 nCligno = 0; // POur clignotement du curseur.
+
+ // Selon l'état.
+ switch (gMenu.nState)
+ {
+ case MENU_State_FadeIn :
+ gMenu.nFadeVal += 4;
+ if (gMenu.nFadeVal > 256)
+ {
+ gMenu.nState = MENU_State_Input;
+ gMenu.nFadeVal = -1;
+ }
+ break;
+
+ case MENU_State_FadeOut :
+ gMenu.nFadeVal -= 4;
+ if (gMenu.nFadeVal < 0)
+ {
+ // Si pas de nom, mettre John Doe.
+ char pDefName[] = "JOHN DOE..";
+ if (gMenu.nScIdx == 0)
+ {
+ strcpy(gMenu.pScName, pDefName);
+ gMenu.nScIdx = strlen(pDefName);
+ }
+ // Rajoute le nom à la table.
+ Scr_PutNameInTable(gMenu.pScName, gExg.nScore);
+ Scr_Save(); // Sauvegarde du fichier.
+
+ nRet = MENU_Main; // Sortie.
+ }
+ break;
+
+ case MENU_State_Input :
+ // Gestion du clavier.
+ if (gVar.pKeysTrig[TRIG_Return])
+ {
+ gMenu.nState = MENU_State_FadeOut;
+ gMenu.nFadeVal = 256;
+ Sfx_PlaySfx(MENU_SFX_VALIDATION, e_SfxPrio_10);
+ break;
+ }
+
+ // On regarde quelle touche est enfoncée.
+ u32 nChr = 0;
+
+ if (gVar.pKeys[SDLK_SPACE]) nChr = ' ';
+ for (i = SDLK_a; i <= SDLK_z; i++)
+ {
+ if (gVar.pKeys[i])
+ {
+ nChr = i - SDLK_a + 'A';
+ break;
+ }
+ }
+ for (i = SDLK_0; i <= SDLK_9; i++)
+ {
+ if (gVar.pKeys[i])
+ {
+ nChr = i - SDLK_0 + '0';
+ break;
+ }
+ }
+ if (gVar.pKeys[SDLK_BACKSPACE])
+ {
+ nChr = SDLK_BACKSPACE;
+ }
+
+ // Pseudo trigger.
+ if (gMenu.pScKeyDown == 0 && nChr)
+ {
+ if (nChr == SDLK_BACKSPACE)
+ {
+ if (gMenu.nScIdx) gMenu.pScName[--gMenu.nScIdx] = '.';
+ }
+ else if (gMenu.nScIdx < strlen(gMenu.pScName))
+ {
+ gMenu.pScName[gMenu.nScIdx++] = nChr;
+ }
+ gMenu.pScKeyDown = 1;
+ Sfx_PlaySfx(MENU_SFX_CLIC, e_SfxPrio_10);
+ }
+ else if (gMenu.pScKeyDown == 1 && nChr == 0)
+ {
+ gMenu.pScKeyDown = 0; // Release.
+ }
+ break;
+ }
+
+ // Affichage.
+
+ // Lignes.
+ char pWin[][20] =
+ {
+ "CONGRATULATIONS!",
+ "YOU RANKED #@",
+ "ENTER YOUR NAME:"
+ };
+ // On rajoute le rank dans sa ligne.
+ char *pPtr = strchr(pWin[1], '@');
+ if (pPtr != NULL) *pPtr = (char)(0x31 + gMenu.nScRank);
+
+ FontPrint((SCR_Width * (8+16+16)) + ((SCR_Width - (strlen(pWin[0]) * 16)) / 2), pWin[0]);
+ FontPrint((SCR_Width * (40+40+16)) + ((SCR_Width - (strlen(pWin[1]) * 16)) / 2), pWin[1]);
+ FontPrint((SCR_Width * (72+64+16)) + ((SCR_Width - (strlen(pWin[2]) * 16)) / 2), pWin[2]);
+ FontPrint((SCR_Width * (104+64+16)) + ((SCR_Width - (strlen(gMenu.pScName) * 16)) / 2), gMenu.pScName);
+ // Curseur.
+ if (nCligno & 16) FontPrint((SCR_Width * (104+64+28)) + ((SCR_Width - (strlen(gMenu.pScName) * 16)) / 2) + (gMenu.nScIdx * 16), "-");
+ nCligno++;
+
+ Fade(gMenu.nFadeVal, gVar.pBkgMenu);
+
+ return (nRet);
+}
+
+
+
diff --git a/jeu-test/tetris_lan_src/menu.h b/jeu-test/tetris_lan_src/menu.h
new file mode 100644
index 0000000..2e20a32
--- /dev/null
+++ b/jeu-test/tetris_lan_src/menu.h
@@ -0,0 +1,22 @@
+
+// Prototypes.
+void MenuInitMisc(void);
+
+void MenuMainInit(void);
+void MenuMainInit2(void);
+u32 MenuMain(void);
+
+u32 MenuHighScores(void);
+void MenuGetNameInit(void);
+u32 MenuGetName(void);
+
+u32 MenuOptions(void);
+
+s32 Scr_CheckHighSc(u32 nScorePrm);
+void Scr_Load(void);
+void Scr_Save(void);
+
+void Opt_Load(void);
+void Opt_Save(void);
+
+
diff --git a/jeu-test/tetris_lan_src/sfx.c b/jeu-test/tetris_lan_src/sfx.c
new file mode 100644
index 0000000..990e3bb
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx.c
@@ -0,0 +1,354 @@
+
+#include "includes.h"
+
+
+struct SSfxGene
+{
+ u8 nInit; // Son initialisé (1) ou pas (0).
+ SDL_AudioSpec sAudioSpec;
+ SDL_AudioCVT pCvt[e_Sfx_LAST];
+
+};
+struct SSfxGene gSfx;
+
+
+#define SFX_MAX_SOUNDS 2
+struct SSample
+{
+ u8 *pData;
+ u32 nDPos;
+ u32 nDLen;
+ u8 nPrio; // Priorité du son en cours.
+} gpSounds[SFX_MAX_SOUNDS];
+
+// Mixer, appelé par SDL.
+void Sfx_MixAudio(void *unused, u8 *stream, int len)
+{
+ u32 i;
+ u32 amount;
+
+ for (i = 0; i < SFX_MAX_SOUNDS; i++)
+ {
+ amount = (gpSounds[i].nDLen - gpSounds[i].nDPos);
+ if (amount > (u32)len)
+ {
+ amount = len;
+ }
+ SDL_MixAudio(stream, &gpSounds[i].pData[gpSounds[i].nDPos], amount, SDL_MIX_MAXVOLUME);
+ gpSounds[i].nDPos += amount;
+ }
+}
+
+// Nettoyage des canaux.
+void Sfx_ClearChannels(void)
+{
+ u32 i;
+
+ for (i = 0; i < SFX_MAX_SOUNDS; i++)
+ {
+ gpSounds[i].nDPos = 0;
+ gpSounds[i].nDLen = 0;
+ }
+
+}
+
+
+// Sound, initialisation. A appeler 1 fois.
+void Sfx_SoundInit(void)
+{
+ gSfx.nInit = 0;
+
+ // Set 16-bit stereo audio at 22Khz.
+ gSfx.sAudioSpec.freq = 22050;
+ gSfx.sAudioSpec.format = AUDIO_S16;
+ gSfx.sAudioSpec.channels = 2;
+ gSfx.sAudioSpec.samples = 512; // A good value for games.
+ gSfx.sAudioSpec.callback = Sfx_MixAudio;
+ gSfx.sAudioSpec.userdata = NULL;
+
+ // Open the audio device and start playing sound!
+ if (SDL_OpenAudio(&gSfx.sAudioSpec, NULL) < 0)
+ {
+ //fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
+ //exit(1);
+ printf("Unable to open audio: %s\n", SDL_GetError());
+ printf("Sound disabled.\n");
+ return;
+ }
+
+ gSfx.nInit = 1; // Ok.
+
+ Sfx_ClearChannels(); // Nettoyage des structures.
+
+}
+
+// Sound on.
+void Sfx_SoundOn(void)
+{
+ if (!gSfx.nInit) return;
+ SDL_PauseAudio(0);
+
+}
+
+// Sound off.
+void Sfx_SoundOff(void)
+{
+ if (!gSfx.nInit) return;
+ SDL_CloseAudio();
+
+}
+
+
+// Chargement de tous les fichiers WAV.
+void Sfx_LoadWavFiles(void)
+{
+ u32 i;
+
+ SDL_AudioSpec sWave;
+ u8 *pData;
+ Uint32 nDLen;
+
+ char *pSfxFilenames[e_Sfx_LAST] = {
+ "sfx/_menu_click.wav", "sfx/_menu_validate.wav", "sfx/_explosion2.wav",
+ "sfx/_level_up.wav", "sfx/_piece_sticks.wav", "sfx/_piece_rotate.wav",
+ };
+
+ if (!gSfx.nInit) return;
+
+ for (i = 0; i < e_Sfx_LAST; i++)
+ {
+ // Load the sound file and convert it to 16-bit stereo at 22kHz
+ if (SDL_LoadWAV(pSfxFilenames[i], &sWave, &pData, &nDLen) == NULL)
+ {
+ fprintf(stderr, "Couldn't load %s: %s\n", pSfxFilenames[i], SDL_GetError());
+ return;
+ }
+ SDL_BuildAudioCVT(&gSfx.pCvt[i], sWave.format, sWave.channels, sWave.freq,
+ gSfx.sAudioSpec.format, gSfx.sAudioSpec.channels, gSfx.sAudioSpec.freq);
+
+ gSfx.pCvt[i].buf = (u8*)malloc(nDLen * gSfx.pCvt[i].len_mult);
+ memcpy(gSfx.pCvt[i].buf, pData, nDLen);
+ gSfx.pCvt[i].len = nDLen;
+ SDL_ConvertAudio(&gSfx.pCvt[i]);
+ SDL_FreeWAV(pData);
+
+ }
+
+}
+
+// Libère les ressources occupées par les fichiers WAV.
+void Sfx_FreeWavFiles(void)
+{
+ u32 i;
+
+ if (!gSfx.nInit) return;
+
+ for (i = 0; i < e_Sfx_LAST; i++)
+ {
+ free(gSfx.pCvt[i].buf);
+ }
+
+}
+
+
+// Joue un son.
+// Le minimum :
+// On commence par chercher un canal vide.
+// Si il n'y en a pas, on note celui qui à la priorité la plus faible.
+// Si plusieurs ont la même priorité, on note celui qui est le plus proche de la fin.
+// Enfin, si la prio du son à jouer est ok, on le joue dans le canal noté.
+void Sfx_PlaySfx(u32 nSfxNo, u32 nSfxPrio)
+{
+ u32 index;
+
+ u8 nPrioMinVal = 255;
+ u32 nPrioMinPos = 0;
+ u32 nPrioMinDiff = (u32)-1;
+
+ if (!(gVar.nOptFlags & OPT_Sound)) return; // Pas si sound off.
+
+ if (nSfxNo >= e_Sfx_LAST) return; // Sécurité.
+
+ // Look for an empty (or finished) sound slot.
+ for (index = 0; index < SFX_MAX_SOUNDS; index++)
+ {
+ if (gpSounds[index].nDPos == gpSounds[index].nDLen)
+ {
+ break;
+ }
+ //
+ if (gpSounds[index].nPrio < nPrioMinVal)
+ {
+ nPrioMinVal = gpSounds[index].nPrio;
+ nPrioMinPos = index;
+ nPrioMinDiff = gpSounds[index].nDLen - gpSounds[index].nDPos;
+ }
+ else if (gpSounds[index].nPrio == nPrioMinVal)
+ {
+ if (gpSounds[index].nDLen - gpSounds[index].nDPos < nPrioMinDiff)
+ {
+ //nPrioMinVal = sounds[index].nPrio;
+ nPrioMinPos = index;
+ nPrioMinDiff = gpSounds[index].nDLen - gpSounds[index].nDPos;
+ }
+ }
+
+ }
+
+ // On a trouvé un emplacement libre ?
+ if (index == SFX_MAX_SOUNDS)
+ {
+ // Non, la prio demandée est > ou == à la prio mini en cours ?
+ if (nSfxPrio < nPrioMinVal) return;
+ index = nPrioMinPos;
+ }
+
+ // Put the sound data in the slot (it starts playing immediately).
+ SDL_LockAudio();
+ gpSounds[index].pData = gSfx.pCvt[nSfxNo].buf;
+ gpSounds[index].nDLen = gSfx.pCvt[nSfxNo].len_cvt;
+ gpSounds[index].nDPos = 0;
+ gpSounds[index].nPrio = (u8)nSfxPrio;
+ SDL_UnlockAudio();
+
+}
+
+
+/*
+void _PlaySound(char *file)
+{
+ int index;
+ SDL_AudioSpec wave;
+ Uint8 *data;
+ Uint32 dlen;
+ SDL_AudioCVT cvt;
+
+ // Look for an empty (or finished) sound slot
+ for ( index=0; index<SFX_MAX_SOUNDS; ++index ) {
+ if ( sounds[index].dpos == sounds[index].dlen ) {
+ break;
+ }
+ }
+ if ( index == SFX_MAX_SOUNDS )
+ return;
+
+ // Load the sound file and convert it to 16-bit stereo at 22kHz
+ if ( SDL_LoadWAV(file, &wave, &data, &dlen) == NULL ) {
+ fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError());
+ return;
+ }
+ SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq,
+ AUDIO_S16, 2, 22050);
+ cvt.buf = (u8*)malloc(dlen*cvt.len_mult);
+ memcpy(cvt.buf, data, dlen);
+ cvt.len = dlen;
+ SDL_ConvertAudio(&cvt);
+ SDL_FreeWAV(data);
+
+ // Put the sound data in the slot (it starts playing immediately)
+ if ( sounds[index].data ) {
+ free(sounds[index].data);
+ }
+ SDL_LockAudio();
+ sounds[index].data = cvt.buf;
+ sounds[index].dlen = cvt.len_cvt;
+ sounds[index].dpos = 0;
+ SDL_UnlockAudio();
+}
+*/
+
+
+/*
+#include "SDL.h"
+#include "SDL_audio.h"
+{
+ extern void mixaudio(void *unused, Uint8 *stream, int len);
+ SDL_AudioSpec fmt;
+
+ // Set 16-bit stereo audio at 22Khz
+ fmt.freq = 22050;
+ fmt.format = AUDIO_S16;
+ fmt.channels = 2;
+ fmt.samples = 512; // A good value for games
+ fmt.callback = mixaudio;
+ fmt.userdata = NULL;
+
+ // Open the audio device and start playing sound!
+ if ( SDL_OpenAudio(&fmt, NULL) < 0 ) {
+ fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
+ exit(1);
+ }
+ SDL_PauseAudio(0);
+
+ ...
+
+ SDL_CloseAudio();
+}
+
+
+#define NUM_SOUNDS 2
+struct sample {
+ Uint8 *data;
+ Uint32 dpos;
+ Uint32 dlen;
+} sounds[NUM_SOUNDS];
+
+void mixaudio(void *unused, Uint8 *stream, int len)
+{
+ int i;
+ Uint32 amount;
+
+ for ( i=0; i<NUM_SOUNDS; ++i ) {
+ amount = (sounds[i].dlen-sounds[i].dpos);
+ if ( amount > len ) {
+ amount = len;
+ }
+ SDL_MixAudio(stream, &sounds[i].data[sounds[i].dpos], amount, SDL_MIX_MAXVOLUME);
+ sounds[i].dpos += amount;
+ }
+}
+
+void PlaySound(char *file)
+{
+ int index;
+ SDL_AudioSpec wave;
+ Uint8 *data;
+ Uint32 dlen;
+ SDL_AudioCVT cvt;
+
+ // Look for an empty (or finished) sound slot
+ for ( index=0; index<NUM_SOUNDS; ++index ) {
+ if ( sounds[index].dpos == sounds[index].dlen ) {
+ break;
+ }
+ }
+ if ( index == NUM_SOUNDS )
+ return;
+
+ // Load the sound file and convert it to 16-bit stereo at 22kHz
+ if ( SDL_LoadWAV(file, &wave, &data, &dlen) == NULL ) {
+ fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError());
+ return;
+ }
+ SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq,
+ AUDIO_S16, 2, 22050);
+ cvt.buf = malloc(dlen*cvt.len_mult);
+ memcpy(cvt.buf, data, dlen);
+ cvt.len = dlen;
+ SDL_ConvertAudio(&cvt);
+ SDL_FreeWAV(data);
+
+ // Put the sound data in the slot (it starts playing immediately)
+ if ( sounds[index].data ) {
+ free(sounds[index].data);
+ }
+ SDL_LockAudio();
+ sounds[index].data = cvt.buf;
+ sounds[index].dlen = cvt.len_cvt;
+ sounds[index].dpos = 0;
+ SDL_UnlockAudio();
+}
+
+*/
+
+
diff --git a/jeu-test/tetris_lan_src/sfx.h b/jeu-test/tetris_lan_src/sfx.h
new file mode 100644
index 0000000..7c7fc99
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx.h
@@ -0,0 +1,33 @@
+
+// Prototypes.
+void Sfx_SoundInit(void);
+void Sfx_SoundOn(void);
+void Sfx_SoundOff(void);
+void Sfx_LoadWavFiles(void);
+void Sfx_FreeWavFiles(void);
+void Sfx_PlaySfx(u32 nSfxNo, u32 nSfxPrio);
+
+
+// Enums.
+enum
+{
+ e_Sfx_MenuClic,
+ e_Sfx_MenuValidate,
+ e_Sfx_Explosion2,
+ e_Sfx_LevelUp,
+ e_Sfx_PieceSticks,
+ e_Sfx_PieceRotate,
+
+ e_Sfx_LAST
+};
+
+
+enum
+{
+ e_SfxPrio_0 = 0,
+ e_SfxPrio_10 = 10,
+ e_SfxPrio_20 = 20,
+ e_SfxPrio_30 = 30,
+ e_SfxPrio_40 = 40,
+ e_SfxPrio_Max = 254,
+};
diff --git a/jeu-test/tetris_lan_src/sfx/_explosion2.wav b/jeu-test/tetris_lan_src/sfx/_explosion2.wav
new file mode 100644
index 0000000..2976fbc
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx/_explosion2.wav
Binary files differ
diff --git a/jeu-test/tetris_lan_src/sfx/_level_up.wav b/jeu-test/tetris_lan_src/sfx/_level_up.wav
new file mode 100644
index 0000000..4ea04c8
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx/_level_up.wav
Binary files differ
diff --git a/jeu-test/tetris_lan_src/sfx/_menu_click.wav b/jeu-test/tetris_lan_src/sfx/_menu_click.wav
new file mode 100644
index 0000000..3250a90
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx/_menu_click.wav
Binary files differ
diff --git a/jeu-test/tetris_lan_src/sfx/_menu_validate.wav b/jeu-test/tetris_lan_src/sfx/_menu_validate.wav
new file mode 100644
index 0000000..25c5e33
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx/_menu_validate.wav
Binary files differ
diff --git a/jeu-test/tetris_lan_src/sfx/_piece_rotate.wav b/jeu-test/tetris_lan_src/sfx/_piece_rotate.wav
new file mode 100644
index 0000000..3250a90
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx/_piece_rotate.wav
Binary files differ
diff --git a/jeu-test/tetris_lan_src/sfx/_piece_rotate_.wav b/jeu-test/tetris_lan_src/sfx/_piece_rotate_.wav
new file mode 100644
index 0000000..68b5cd6
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx/_piece_rotate_.wav
Binary files differ
diff --git a/jeu-test/tetris_lan_src/sfx/_piece_sticks.wav b/jeu-test/tetris_lan_src/sfx/_piece_sticks.wav
new file mode 100644
index 0000000..dccbe70
--- /dev/null
+++ b/jeu-test/tetris_lan_src/sfx/_piece_sticks.wav
Binary files differ
diff --git a/jeu-test/tetris_lan_src/tcpip.c b/jeu-test/tetris_lan_src/tcpip.c
new file mode 100644
index 0000000..5afcbdf
--- /dev/null
+++ b/jeu-test/tetris_lan_src/tcpip.c
@@ -0,0 +1,541 @@
+
+#include "includes.h"
+
+#ifdef WIN32
+#define socklen_t int
+
+#include "Winsock2.h"
+#endif
+
+#ifdef __LINUX__
+typedef enum { FALSE=0, TRUE } BOOL;
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/tcp.h> // Pour TCP_NODELAY et SOL_TCP.
+
+#include <errno.h>
+#endif
+
+struct SSock
+{
+ BOOL nCnx; // Socket connectée ou pas.
+ int nSocket; // No de socket.
+ struct in_addr nIPAddr; // IP du client connecté.
+};
+struct SSock gSockListen; // Socket d'écoute du Master.
+struct SSock gSockCom; // Mst: Socket de com avec le client / Clt: Socket de com avec le master.
+
+struct STCPIP
+{
+#ifdef WIN32
+ WSADATA WSAData;
+#endif
+
+};
+struct STCPIP gTCPIP;
+
+//=============================================================================
+//=============================================================================
+
+// Init.
+void TCP_EngineInit(void)
+{
+#ifdef WIN32
+ int nRet;
+ nRet = WSAStartup(MAKEWORD(2,2), &gTCPIP.WSAData);
+ if (nRet != 0)
+ {
+ printf("Unable to initialize Winsock: %d %d", nRet, WSAGetLastError());
+ exit (1);
+ }
+#endif
+
+ gVar.nTCPMaster = 0; // 1 = Master / 0 = Slave.
+
+}
+
+// Fermeture.
+void TCP_EngineClose(void)
+{
+#ifdef WIN32
+ WSACleanup();
+#endif
+}
+
+#ifdef WIN32
+// Passage socket en mode bloquant / non bloquant.
+// Out : 0 = Ok.
+int Sock_SetBlockMode(int nSocket, BOOL bBlock)
+{
+ int errsv;
+ int nResult;
+ u32 arg = (bBlock == TRUE ? 0 : 1);
+
+ if ((nResult = ioctlsocket(nSocket, FIONBIO, &arg)) == SOCKET_ERROR)
+ {
+ errsv = WSAGetLastError();
+ printf("ioctlsocket: Error (%d).\n", errsv);
+ exit (1);
+ }
+
+ return (nResult);
+}
+#endif
+
+#ifdef __LINUX__
+// Passage socket en mode bloquant / non bloquant.
+int Sock_SetBlockMode(int nSocket, BOOL nBlock)
+{
+ int flags;
+ int r;
+ int errsv;
+
+ if ((flags = fcntl(nSocket, F_GETFL)) == -1)
+ {
+ errsv = errno;
+ printf("fnctl1: %d - %s\n", errsv, strerror(errsv));
+ exit (1);
+ }
+
+ if (nBlock == TRUE)
+ {
+ r = fcntl(nSocket, F_SETFL, flags & ~O_NONBLOCK);
+ }
+ else
+ {
+ r = fcntl(nSocket, F_SETFL, flags | O_NONBLOCK);
+ }
+ if (r == -1)
+ {
+ errsv = errno;
+ printf("fnctl2: %d - %s\n", errsv, strerror(errsv));
+ exit (1);
+ }
+
+ return (r);
+}
+#endif
+
+// Place le flag TCP_NODELAY pour couper le "Nagle algorithm".
+// Out : 0 = Ok / 1 = Erreur.
+int Sock_SetTcpNoDelay(int nSocket, BOOL bVal)
+{
+ int errsv;
+#ifdef WIN32
+ BOOL bOptVal = bVal;
+ int bOptLen = sizeof(BOOL);
+
+ if (setsockopt(nSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&bOptVal, bOptLen) == SOCKET_ERROR)
+ {
+ errsv = WSAGetLastError();
+ printf("setsockopt: Error (%d).\n", errsv);
+ return (1);
+ }
+#endif
+#ifdef __LINUX__
+ int nOnOff = (bVal == TRUE ? 1 : 0);
+
+ if (setsockopt(nSocket, SOL_TCP, TCP_NODELAY, &nOnOff, sizeof(nOnOff)) == -1)
+ {
+ errsv = errno;
+ printf("setsockopt: %d - %s\n", errsv, strerror(errsv));
+ return (1);
+ }
+#endif
+
+ printf("TCP_NODELAY: Ok.\n");
+
+ return (0);
+}
+
+// Ouverture de socket.
+int SocketOpen(void)
+{
+ int nSocket;
+#ifdef WIN32
+ //if ((nSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ if ((nSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
+ {
+ printf("SocketOpen(): Unable to open socket.\n");
+ exit (1);
+ }
+
+ // Flag SO_REUSEADDR : Normalement, ne sert que du côté serveur. Au cas où il y ait un crash et que la socket n'ait pas été correctement fermée.
+ BOOL bOptVal = TRUE;
+ int bOptLen = sizeof(BOOL);
+ if (setsockopt(nSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&bOptVal, bOptLen) == SOCKET_ERROR)
+ {
+ int errsv = WSAGetLastError();
+ printf("setsockopt: Error (%d).\n", errsv);
+ }
+ else
+ {
+ printf("SO_REUSEADDR: Ok.\n");
+ }
+#endif
+#ifdef __LINUX__
+ //if ((nSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ if ((nSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
+ {
+ int errsv;
+ errsv = errno;
+ printf("SocketOpen(): %d - %s\n", errsv, strerror(errsv));
+ exit (1);
+ }
+
+ // Flag SO_REUSEADDR : Normalement, ne sert que du côté serveur. Au cas où il y ait un crash et que la socket n'ait pas été correctement fermée.
+ int on = 1;
+ if (setsockopt(nSocket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
+ {
+ int errsv = errno;
+ printf("setsockopt: %d - %s\n", errsv, strerror(errsv));
+ }
+ else
+ {
+ printf("SO_REUSEADDR: Ok.\n");
+ }
+#endif
+ return (nSocket);
+}
+
+// Fermeture de socket.
+void SocketClose(int nSocket)
+{
+ int errsv;
+
+#ifdef WIN32
+ if (closesocket(nSocket) == SOCKET_ERROR)
+ {
+ errsv = WSAGetLastError();
+ printf("SocketClose: Error while closing socket (%d).\n", errsv);
+ exit (1);
+ }
+#endif
+#ifdef __LINUX__
+ if (close(nSocket) == -1)
+ {
+ errsv = errno;
+ printf("SocketClose: %d - %s\n", errsv, strerror(errsv));
+ exit (1);
+ }
+#endif
+}
+
+//=============================================================================
+//=============================================================================
+
+// Server:
+// socket / bind / listen / accept / send and-or recv / close
+
+// Start.
+void TCPServerStart(void)
+{
+ int errsv;
+
+ // Ouverture d'une socket pour l'écoute.
+ gSockListen.nSocket = SocketOpen();
+ // Passage de la socket en mode non bloquant...
+ Sock_SetBlockMode(gSockListen.nSocket, FALSE);
+ Sock_SetTcpNoDelay(gSockListen.nSocket, TRUE); // ...et en TCP_NODELAY.
+ // bind.
+ struct sockaddr_in sSrv;
+ memset(&sSrv, 0, sizeof(sSrv));
+ sSrv.sin_family = AF_INET;
+ sSrv.sin_addr.s_addr = htonl(INADDR_ANY); // Ecoute sur toutes les adresses IP LOCALES.
+ sSrv.sin_port = htons(gVar.nPort);
+ if (bind(gSockListen.nSocket, (struct sockaddr *)&sSrv, sizeof(sSrv)) != 0)
+ {
+#ifdef WIN32
+ errsv = WSAGetLastError();
+ printf("bind: Failed (%d).\n", errsv);
+#endif
+#ifdef __LINUX__
+ errsv = errno;
+ printf("bind: tcpip l.258 %d - %s\n", errsv, strerror(errsv));
+#endif
+ exit (1);
+ }
+ // listen.
+ if (listen(gSockListen.nSocket, 1) != 0)
+ {
+#ifdef WIN32
+ errsv = WSAGetLastError();
+ printf("listen: Failed (%d).\n", errsv);
+#endif
+#ifdef __LINUX__
+ errsv = errno;
+ printf("listen: %d - %s\n", errsv, strerror(errsv));
+#endif
+ exit (1);
+ }
+ gSockListen.nCnx = TRUE;
+
+ printf("Server started.\n\n");
+
+ // Reset du slot de connexion client.
+ gSockCom.nCnx = FALSE;
+
+}
+
+// A placer dans une boucle, attend la connexion des clients.
+u32 TCPServer_WaitForClient(void)
+{
+ struct sockaddr_in sFrom;
+ socklen_t nFromLen = sizeof(sFrom);
+ int errsv;
+
+ // accept.
+ int nAccept;
+ memset(&sFrom, 0, sizeof(sFrom));
+#ifdef WIN32
+ if ((nAccept = accept(gSockListen.nSocket, (struct sockaddr *)&sFrom, &nFromLen)) == -1)
+ {
+ errsv = WSAGetLastError();
+ if (errsv != WSAEWOULDBLOCK)
+ {
+ printf("accept: Error (%d).\n", errsv);
+ exit (1);
+ }
+ }
+#endif
+#ifdef __LINUX__
+ if ((nAccept = accept(gSockListen.nSocket, (struct sockaddr *)&sFrom, &nFromLen)) == -1)
+ {
+ errsv = errno;
+ if (errsv != EWOULDBLOCK)
+ {
+ printf("accept: %d - %s\n", errsv, strerror(errsv));
+ exit (1);
+ }
+ }
+#endif
+ else
+ {
+ // Une connexion a eu lieu.
+ printf("Incoming connexion from IP: %s\n", inet_ntoa(sFrom.sin_addr));
+ //
+ gSockCom.nSocket = nAccept;
+ gSockCom.nIPAddr = sFrom.sin_addr;
+ Sock_SetBlockMode(gSockCom.nSocket, FALSE); // On passe la socket client en mode non bloquant...
+ Sock_SetTcpNoDelay(gSockCom.nSocket, TRUE); // ...et en TCP_NODELAY.
+ gSockCom.nCnx = TRUE;
+ printf("Client connected.\n");
+ return (1);
+ }
+
+ return (0);
+
+}
+
+// Stop.
+void TCPServerStop(void)
+{
+ // Fermeture des sockets :
+ // Socket d'écoute.
+ if (gSockListen.nCnx == TRUE) SocketClose(gSockListen.nSocket);
+ // Socket Client.
+ if (gSockCom.nCnx == TRUE) SocketClose(gSockCom.nSocket);
+
+}
+
+//=============================================================================
+//=============================================================================
+
+// Client:
+// socket / connect / send and-or recv / close
+
+// Start.
+void TCPClientStart(void)
+{
+ // Ouverture de socket.
+ gSockCom.nSocket = SocketOpen();
+ // On passe la socket en mode non bloquant...
+ Sock_SetBlockMode(gSockCom.nSocket, FALSE);
+ Sock_SetTcpNoDelay(gSockCom.nSocket, TRUE); // ...et en TCP_NODELAY.
+ //
+ gSockCom.nCnx = TRUE;
+
+}
+
+// A placer dans une boucle, se connecte au serveur et attend la connexion.
+u32 TCPClient_ConnectToServer(void)
+{
+ // connect.
+ int errsv;
+ struct sockaddr_in sDst;
+ memset(&sDst, 0, sizeof(sDst));
+ sDst.sin_family = AF_INET;
+ sDst.sin_addr.s_addr = inet_addr(gVar.pIPAddress); //inet_addr(gPrm.pIPAddr);
+ sDst.sin_port = htons(gVar.nPort);
+
+#ifdef WIN32
+ if (connect(gSockCom.nSocket, (struct sockaddr *)&sDst, sizeof(sDst)) != 0)
+ {
+ errsv = WSAGetLastError();
+//printf("connect: %d\n", errsv);
+//WSAEWOULDBLOCK : 10035
+//WSAEALREADY : 10037
+//WSAEISCONN : 10056
+//WSAENOTCONN : 10057
+ if (errsv == WSAEISCONN)
+ {
+printf("connect: Success!\n\n");
+ return (1);
+ }
+ }
+#endif
+#ifdef __LINUX__
+ if (connect(gSockCom.nSocket, (struct sockaddr *)&sDst, sizeof(sDst)) != 0)
+ {
+ errsv = errno;
+//printf("connect: %d - %s\n", errsv, strerror(errsv));
+ if (errsv == EISCONN)
+ {
+printf("connect: Success!\n\n");
+ return (1);
+ }
+ }
+#endif
+
+ return (0);
+}
+
+// Stop.
+void TCPClientStop(void)
+{
+ // Fermeture de la socket.
+ if (gSockCom.nCnx == TRUE) SocketClose(gSockCom.nSocket);
+
+}
+
+//=============================================================================
+//=============================================================================
+
+// Réception d'un message.
+// Le ou les messages sont copiés dans pRcvBuf (taille du buffer nRcvBufSzMax) et le nombre de bytes reçus renvoyé via pRcvBufSz.
+u32 TCPReceive(int nSocket, u8 *pRcvBuf, u32 nRcvBufSzMax, u32 *pRcvBufSz)
+{
+ int nRecv;
+ int errsv;
+
+ // recv.
+ memset(pRcvBuf, 0, nRcvBufSzMax);
+#ifdef WIN32
+ if ((nRecv = recv(nSocket, pRcvBuf, nRcvBufSzMax, 0)) == SOCKET_ERROR)
+ {
+ errsv = WSAGetLastError();
+ if (errsv != WSAEWOULDBLOCK)
+ {
+ printf("recv: Error (%d).\n", errsv);
+ //exit (1);
+ }
+ }
+#endif
+#ifdef __LINUX__
+ if ((nRecv = recv(nSocket, pRcvBuf, nRcvBufSzMax, 0)) == -1)
+ {
+ errsv = errno;
+ if (errsv != EAGAIN)
+ {
+ printf("recv: %d - %s\n", errsv, strerror(errsv));
+ //exit (1);
+ }
+ }
+#endif
+ else
+ {
+ // Message reçu.
+ *pRcvBufSz = nRecv;
+
+printf("recv: %d bytes\n", nRecv);
+
+if (nRecv == 0) exit(1); // à revoir... Pour éviter la fin.
+
+ return (1);
+ }
+
+ return (0);
+}
+
+// Appelable depuis l'extérieur (sans s'occuper des sockets).
+// Renvoie : 0 = Rien n'a été reçu / 1 = Un(des) message(s) reçu(s) + taille en bytes dans pSz.
+u32 TCP_Receive(u8 *pBuf, u32 nSz, u32 *pSz)
+{
+ return (TCPReceive(gSockCom.nSocket, pBuf, nSz, pSz));
+}
+
+
+// Envoi d'un message.
+void TCPSend(int nSocket, u8 *pBuf, u32 nSz)
+{
+ int errsv;
+ int nSend;
+
+#ifdef WIN32
+ if ((nSend = send(nSocket, (char *)pBuf, nSz, 0)) == SOCKET_ERROR)
+ {
+ errsv = WSAGetLastError();
+ printf("send: Error (%d).\n", errsv);
+ //exit (1);
+ }
+#endif
+#ifdef __LINUX__
+ if ((nSend = send(nSocket, (char *)pBuf, nSz, 0)) == -1)
+ {
+ errsv = errno;
+ printf("send: %d - %s\n", errsv, strerror(errsv));
+ //exit (1);
+ }
+#endif
+
+}
+
+// Envoi d'un message.
+// Appelable depuis l'extérieur (sans s'occuper des sockets).
+void TCP_Send(u8 *pBuf, u32 nSz)
+{
+ TCPSend(gSockCom.nSocket, pBuf, nSz);
+}
+
+//=============================================================================
+//=============================================================================
+
+// Ouverture de la cnx.
+void TCP_CnxOpen(void)
+{
+ if (gVar.nTCPMaster)
+ {
+ TCPServerStart();
+ }
+ else
+ {
+ TCPClientStart();
+ }
+}
+
+// Attente de la cnx. A placer dans une boucle.
+u32 TCP_CnxWait(void)
+{
+ return (gVar.nTCPMaster ? TCPServer_WaitForClient() : TCPClient_ConnectToServer());
+}
+
+// Fermeture de la cnx.
+void TCP_CnxClose(void)
+{
+ if (gVar.nTCPMaster)
+ {
+ TCPServerStop();
+ }
+ else
+ {
+ TCPClientStop();
+ }
+printf("TCP_CnxClose() : Ok.\n");
+}
+
+
+
diff --git a/jeu-test/tetris_lan_src/tcpip.h b/jeu-test/tetris_lan_src/tcpip.h
new file mode 100644
index 0000000..46769ec
--- /dev/null
+++ b/jeu-test/tetris_lan_src/tcpip.h
@@ -0,0 +1,13 @@
+
+
+
+void TCP_EngineInit(void);
+void TCP_EngineClose(void);
+
+void TCP_CnxOpen(void);
+u32 TCP_CnxWait(void);
+void TCP_CnxClose(void);
+
+u32 TCP_Receive(u8 *pBuf, u32 nSz, u32 *pSz);
+void TCP_Send(u8 *pBuf, u32 nSz);
+
diff --git a/jeu-test/tetris_lan_src/tetris b/jeu-test/tetris_lan_src/tetris
new file mode 100755
index 0000000..1b84aa1
--- /dev/null
+++ b/jeu-test/tetris_lan_src/tetris
Binary files differ
diff --git a/jeu-test/tetris_lan_src/tetris.opt b/jeu-test/tetris_lan_src/tetris.opt
new file mode 100644
index 0000000..f0cd083
--- /dev/null
+++ b/jeu-test/tetris_lan_src/tetris.opt
Binary files differ