0adc772e1cb1b5af9ba2fec4eda30b2399c178ed
[ansitris] / src / game.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <time.h>
5 #include <inttypes.h>
6 #include "game.h"
7 #include "pieces.h"
8 #include "ansi.h"
9
10 enum {
11         G_DIAMOND       = 0x04,
12         G_CHECKER       = 0xb1,
13         G_LR_CORNER     = 0xd9,
14         G_UR_CORNER     = 0xbf,
15         G_UL_CORNER     = 0xda,
16         G_LL_CORNER     = 0xc0,
17         G_CROSS         = 0xc5,
18         G_HLINE         = 0xc4,
19         G_L_TEE         = 0xc3,
20         G_R_TEE         = 0xb4,
21         G_B_TEE         = 0xc1,
22         G_T_TEE         = 0xc2,
23         G_VLINE         = 0xb3,
24         G_CDOT          = 0xf8
25 };
26
27 enum { ERASE_PIECE, DRAW_PIECE };
28
29 enum { BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, YELLOW, WHITE };
30
31 /* dimensions of the whole screen */
32 #define SCR_ROWS        20
33 #define SCR_COLS        20
34
35 /* dimensions of the playfield */
36 #define PF_ROWS         18
37 #define PF_COLS         10
38 /* offset of the playfield from the left side of the screen */
39 #define PF_XOFFS        2
40 #define PF_YOFFS        0
41
42 #define CHAR(c, fg, bg) \
43         ((uint16_t)(c) | ((uint16_t)(fg) << 12) | ((uint16_t)(bg) << 8))
44
45 int scr[SCR_COLS * SCR_ROWS];
46
47 static int collision(int piece, const int *pos);
48 static void stick(int piece, const int *pos);
49 static void draw_piece(int piece, const int *pos, int rot, int mode);
50 static void drawbg(void);
51 static void wrtile(int tileid);
52
53
54 static int pos[2], next_pos[2];
55 static int cur_piece = -1;
56 static int cur_rot, prev_rot;
57
58 enum {
59         TILE_BLACK,
60         TILE_PF,
61         TILE_PFSEP,
62         TILE_IPIECE,
63         TILE_OPIECE,
64         TILE_JPIECE,
65         TILE_LPIECE,
66         TILE_SPIECE,
67         TILE_TPIECE,
68         TILE_ZPIECE
69 };
70 #define FIRST_PIECE_TILE        TILE_IPIECE
71
72 static uint16_t tiles[][2] = {
73         { CHAR(' ', BLACK, BLACK), CHAR(' ', BLACK, BLACK) },                   /* black tile */
74         { CHAR(' ', WHITE, WHITE), CHAR(' ', WHITE, WHITE) },                   /* playfield background */
75         { CHAR(G_CHECKER, WHITE, BLACK), CHAR(G_CHECKER, WHITE, BLACK) },       /* well separator */
76         { CHAR(' ', CYAN, CYAN), CHAR(' ', CYAN, CYAN) },                               /* straight */
77         { CHAR(' ', BLUE, BLUE), CHAR(' ', BLUE, BLUE) },                               /* box */
78         { CHAR(' ', GREEN, GREEN), CHAR(' ', GREEN, GREEN) },                   /* J */
79         { CHAR(' ', YELLOW, YELLOW), CHAR(' ', YELLOW, YELLOW) },               /* L */
80         { CHAR(' ', MAGENTA, MAGENTA), CHAR(' ', MAGENTA, MAGENTA) },   /* S */
81         { CHAR(' ', RED, BLACK), CHAR(' ', RED, BLACK) },               /* T */
82         { CHAR(' ', RED, RED), CHAR(' ', RED, RED) },                                   /* Z */
83 };
84
85
86 int init_game(void)
87 {
88         int i, j;
89         int *row = scr;
90
91         srand(time(0));
92
93         tick_interval = 1000;
94
95         ansi_clearscr();
96         ansi_cursor(0);
97
98         /* fill the screen buffer, and draw */
99         for(i=0; i<SCR_ROWS; i++) {
100                 for(j=0; j<SCR_COLS; j++) {
101                         if(i > PF_ROWS || j < PF_XOFFS - 1 || j > PF_XOFFS + PF_COLS) {
102                                 row[j] = TILE_BLACK;
103                         } else if((i == PF_ROWS && j >= PF_XOFFS && j < PF_XOFFS + PF_COLS) ||
104                                         j == PF_XOFFS - 1 || j == PF_XOFFS + PF_COLS) {
105                                 row[j] = TILE_PFSEP;
106                         } else {
107                                 row[j] = TILE_PF;
108                         }
109                 }
110                 row += SCR_COLS;
111         }
112
113         drawbg();
114         fflush(stdout);
115
116         return 0;
117 }
118
119 void cleanup_game(void)
120 {
121         ansi_reset();
122 }
123
124 long update(long msec)
125 {
126         static long prev_tick;
127         long dt;
128
129         dt = msec - prev_tick;
130
131         /* fall */
132         while(dt >= tick_interval) {
133                 if(cur_piece >= 0) {
134                         next_pos[0] = pos[0] + 1;
135                         if(collision(cur_piece, next_pos)) {
136                                 next_pos[0] = pos[0];
137                                 fprintf(stderr, "stick at row %d col %d\n", pos[0], pos[1]);
138                                 stick(cur_piece, next_pos);
139                                 cur_piece = -1;
140                         }
141                 } else {
142                         cur_piece = rand() % NUM_PIECES;
143                         fprintf(stderr, "spawn: %d\n", cur_piece);
144                         pos[0] = next_pos[0] = piece_spawnpos[cur_piece][0];
145                         pos[1] = next_pos[1] = PF_COLS / 2 + piece_spawnpos[cur_piece][1];
146                 }
147
148                 dt -= tick_interval;
149                 prev_tick = msec;
150         }
151
152         if(cur_piece >= 0 && (memcmp(pos, next_pos, sizeof pos) != 0 || cur_rot != prev_rot)) {
153                 draw_piece(cur_piece, pos, prev_rot, ERASE_PIECE);
154                 draw_piece(cur_piece, next_pos, cur_rot, DRAW_PIECE);
155                 memcpy(pos, next_pos, sizeof pos);
156                 prev_rot = cur_rot;
157         }
158         return tick_interval - dt;
159 }
160
161 void game_input(int c)
162 {
163         switch(c) {
164         case 27:
165                 quit = 1;
166                 break;
167
168         case 'a':
169                 next_pos[1] = pos[1] - 1;
170                 if(collision(cur_piece, next_pos)) {
171                         next_pos[1] = pos[1];
172                 }
173                 break;
174
175         case 'd':
176                 next_pos[1] = pos[1] + 1;
177                 if(collision(cur_piece, next_pos)) {
178                         next_pos[1] = pos[1];
179                 }
180                 break;
181
182         case 'w':
183         case ' ':
184                 prev_rot = cur_rot;
185                 cur_rot = (cur_rot + 1) & 3;
186                 if(collision(cur_piece, next_pos)) {
187                         cur_rot = prev_rot;
188                 }
189                 break;
190
191         case 's':
192                 next_pos[0] = pos[0] + 1;
193                 if(collision(cur_piece, next_pos)) {
194                         next_pos[0] = pos[0];
195                 }
196                 break;
197
198         default:
199                 break;
200         }
201 }
202
203 static int collision(int piece, const int *pos)
204 {
205         int i;
206         unsigned char *p = pieces[piece][cur_rot];
207
208         for(i=0; i<4; i++) {
209                 int x = PF_XOFFS + pos[1] + BLKX(*p);
210                 int y = PF_YOFFS + pos[0] + BLKY(*p);
211                 p++;
212
213                 if(scr[y * SCR_COLS + x] != TILE_PF) return 1;
214         }
215
216         return 0;
217 }
218
219 static void stick(int piece, const int *pos)
220 {
221         int i;
222         unsigned char *p = pieces[piece][cur_rot];
223
224         for(i=0; i<4; i++) {
225                 int x = PF_XOFFS + pos[1] + BLKX(*p);
226                 int y = PF_YOFFS + pos[0] + BLKY(*p);
227                 p++;
228
229                 scr[y * SCR_COLS + x] = piece + FIRST_PIECE_TILE;
230         }
231 }
232
233 static void draw_piece(int piece, const int *pos, int rot, int mode)
234 {
235         int i;
236         int tile = mode == ERASE_PIECE ? TILE_PF : FIRST_PIECE_TILE + piece;
237         unsigned char *p = pieces[piece][rot];
238
239         for(i=0; i<4; i++) {
240                 int x = PF_XOFFS + pos[1] + BLKX(*p);
241                 int y = PF_YOFFS + pos[0] + BLKY(*p);
242                 p++;
243
244                 ansi_setcursor(y, x * 2);
245                 wrtile(tile);
246         }
247         fflush(stdout);
248 }
249
250 static void drawbg(void)
251 {
252         int i, j;
253         int *sptr = scr;
254
255         for(i=0; i<SCR_ROWS; i++) {
256                 ansi_setcursor(i, 0);
257                 for(j=0; j<SCR_COLS; j++) {
258                         wrtile(*sptr++);
259                 }
260         }
261 }
262
263 static void wrtile(int tileid)
264 {
265         int i;
266
267         for(i=0; i<2; i++) {
268                 uint16_t c = tiles[tileid][i];
269                 unsigned char cc = c & 0xff;
270                 unsigned char ca = c >> 8;
271
272                 ansi_ibmchar(cc, ca);
273         }
274 }