10 static int init(void);
11 static void cleanup(void);
12 static void start(void);
13 static void stop(void);
14 static void update(float dt);
15 static void draw(void);
16 static void draw_block(int block, const int *pos, int rot);
17 static void drawpf(void);
18 static void reshape(int x, int y);
19 static void keyboard(int key, int pressed);
20 static void mouse(int bn, int pressed, int x, int y);
21 static void motion(int x, int y);
22 static void wheel(int dir);
24 static void addscore(int nlines);
25 static int spawn(void);
26 static int collision(int block, const int *pos);
27 static void stick(int block, const int *pos);
28 static void erase_completed(void);
30 struct game_screen game_screen = {
47 static struct cmesh *blkmesh;
48 static float cam_theta, cam_phi, cam_dist = 30;
49 static int bnstate[16];
50 static int prev_mx, prev_my;
52 static long tick_interval;
54 /* dimensions of the playfield */
60 #define PF_VIS_SHIFT 9
62 static unsigned int pfield[PF_ROWS * PF_COLS];
64 static int pos[2], next_pos[2];
65 static int cur_block, next_block, prev_block;
66 static int cur_rot, prev_rot;
67 static int complines[4];
68 static int num_complines;
71 static int score, level, lines;
72 static int just_spawned;
75 static const long level_speed[NUM_LEVELS] = {
76 887, 820, 753, 686, 619, 552, 469, 368, 285, 184,
77 167, 151, 134, 117, 107, 98, 88, 79, 69, 60, 50
83 if(!(blkmesh = cmesh_alloc())) {
86 if(cmesh_load(blkmesh, "data/noisecube.obj") == -1) {
87 fprintf(stderr, "failed to load block model\n");
93 static void cleanup(void)
98 static void start(void)
105 score = level = lines = 0;
106 tick_interval = level_speed[0];
109 next_block = rand() % NUM_BLOCKS;
111 memset(pfield, 0, PF_COLS * PF_ROWS * sizeof *pfield);
114 static void stop(void)
118 static void update(float dtsec)
120 static long prev_tick;
124 prev_tick = time_msec;
127 dt = time_msec - prev_tick;
131 int i, row = PF_ROWS - gameover;
134 if(dt < GAMEOVER_FILL_RATE) {
139 ptr = pfield + row * PF_COLS;
140 for(i=0; i<PF_COLS; i++) {
141 *ptr++ = TILE_GAMEOVER;
152 /* lines where completed, we're in blinking mode */
153 int i, j, blink = dt >> 8;
161 for(i=0; i<num_complines; i++) {
162 unsigned int *ptr = pfield + complines[i] * PF_COLS;
163 for(j=0; j<PF_COLS; j++) {
164 *ptr = (*ptr & ~PF_VIS) | ((blink & 1) << PF_VIS_SHIFT);
171 while(dt >= tick_interval) {
174 next_pos[0] = pos[0] + 1;
175 if(collision(cur_block, next_pos)) {
176 next_pos[0] = pos[0];
177 stick(cur_block, next_pos);
189 prev_tick = time_msec;
193 static void draw(void)
195 glTranslatef(0, 0, -cam_dist);
196 glRotatef(cam_phi, 1, 0, 0);
197 glRotatef(cam_theta, 0, 1, 0);
199 /* center playfield */
201 glTranslatef(-PF_COLS / 2, PF_ROWS / 2, 0);
205 draw_block(cur_block, pos, cur_rot);
211 static void draw_block(int block, const int *pos, int rot)
214 unsigned char *p = blocks[block][rot];
216 /*pal = FIRST_BLOCK_PAL + block;*/
219 int x = pos[1] + BLKX(*p);
220 int y = pos[0] + BLKY(*p);
226 glTranslatef(x, y, 0);
232 static void drawpf(void)
235 unsigned int *sptr = pfield;
237 for(i=0; i<PF_ROWS; i++) {
238 for(j=0; j<PF_COLS; j++) {
239 unsigned int val = *sptr++;
242 glTranslatef(j, i, 0);
251 static void reshape(int x, int y)
255 static void keyboard(int key, int pressed)
264 next_pos[1] = pos[1] - 1;
265 if(collision(cur_block, next_pos)) {
266 next_pos[1] = pos[1];
275 next_pos[1] = pos[1] + 1;
276 if(collision(cur_block, next_pos)) {
277 next_pos[1] = pos[1];
287 cur_rot = (cur_rot + 1) & 3;
288 if(collision(cur_block, next_pos)) {
297 /* ignore drops until the first update after a spawn */
298 if(cur_block >= 0 && !just_spawned && !pause) {
299 next_pos[0] = pos[0] + 1;
300 if(collision(cur_block, next_pos)) {
301 next_pos[0] = pos[0];
302 stick(cur_block, next_pos); /* stick immediately */
309 if(!pause && cur_block >= 0) {
310 next_pos[0] = pos[0] + 1;
311 while(!collision(cur_block, next_pos)) {
315 stick(cur_block, next_pos); /* stick immediately */
322 if(score && is_highscore(score)) {
323 name = name_screen(score);
325 save_score(name, score, lines, level);
327 /* TODO: pop screen */
335 if(score && is_highscore(score)) {
336 name = name_screen(score);
338 save_score(name, score, lines, level);
340 /* TODO: pop screen */
348 static void mouse(int bn, int pressed, int x, int y)
350 bnstate[bn] = pressed;
355 static void motion(int x, int y)
357 float dx = x - prev_mx;
358 float dy = y - prev_my;
363 cam_theta += dx * 0.5;
366 if(cam_phi < -90) cam_phi = -90;
367 if(cam_phi > 90) cam_phi = 90;
370 cam_dist += dy * 0.1;
371 if(cam_dist < 0) cam_dist = 0;
375 static void wheel(int dir)
379 static void addscore(int nlines)
381 static const int stab[] = {40, 100, 300, 1200}; /* bonus per line completed */
385 score += stab[nlines - 1] * (level + 1);
389 if(level > NUM_LEVELS - 1) level = NUM_LEVELS - 1;
391 tick_interval = level_speed[level];
394 static int spawn(void)
399 r = rand() % NUM_BLOCKS;
400 } while(tries-- > 0 && (r | prev_block | next_block) == prev_block);
402 cur_block = next_block;
405 prev_rot = cur_rot = 0;
406 pos[0] = block_spawnpos[cur_block][0];
407 next_pos[0] = pos[0] + 1;
408 pos[1] = next_pos[1] = PF_COLS / 2 + block_spawnpos[cur_block][1];
410 if(collision(cur_block, next_pos)) {
418 static int collision(int block, const int *pos)
421 unsigned char *p = blocks[block][cur_rot];
424 int x = pos[1] + BLKX(*p);
425 int y = pos[0] + BLKY(*p);
430 if(pfield[y * PF_COLS + x] & PF_FULL) return 1;
436 static void stick(int block, const int *pos)
439 unsigned int *pfline;
440 unsigned char *p = blocks[block][cur_rot];
443 prev_block = cur_block; /* used by the spawn routine */
447 int x = pos[1] + BLKX(*p);
448 int y = pos[0] + BLKY(*p);
451 pfline = pfield + y * PF_COLS;
452 pfline[x] = PF_FULL | PF_VIS | block;
455 for(j=0; j<PF_COLS; j++) {
456 if(!(pfline[j] & PF_FULL)) {
462 complines[num_complines++] = y;
469 addscore(num_complines);
473 static void erase_completed(void)
475 int i, j, srow, drow;
476 unsigned int *pfstart = pfield;
479 /* sort completed lines from highest to lowest row number */
480 for(i=0; i<num_complines-1; i++) {
481 for(j=i+1; j<num_complines; j++) {
482 if(complines[j] > complines[i]) {
483 int tmp = complines[j];
484 complines[j] = complines[i];
490 srow = drow = PF_ROWS - 1;
491 dptr = pfstart + drow * PF_COLS;
493 for(i=0; i<PF_ROWS; i++) {
494 for(j=0; j<num_complines; j++) {
495 if(complines[j] == srow) {
501 for(j=0; j<PF_COLS; j++) {
505 } else if(srow != drow) {
506 unsigned int *sptr = pfstart + srow * PF_COLS;
507 memcpy(dptr, sptr, PF_COLS * sizeof *dptr);