12 static int init(void);
13 static void cleanup(void);
14 static void start(void);
15 static void stop(void);
16 static void update(float dt);
17 static void draw(void);
18 static void draw_block(int block, const int *pos, int rot);
19 static void drawpf(void);
20 static void reshape(int x, int y);
21 static void keyboard(int key, int pressed);
22 static void mouse(int bn, int pressed, int x, int y);
23 static void motion(int x, int y);
24 static void wheel(int dir);
26 static void update_cur_block(void);
27 static void addscore(int nlines);
28 static int spawn(void);
29 static int collision(int block, const int *pos);
30 static void stick(int block, const int *pos);
31 static void erase_completed(void);
33 struct game_screen game_screen = {
50 static struct cmesh *blkmesh, *wellmesh;
51 static unsigned int tex_well;
53 static float cam_theta, cam_phi, cam_dist = 30;
54 static int bnstate[16];
55 static int prev_mx, prev_my;
57 static long tick_interval;
59 /* dimensions of the playfield */
65 #define PF_VIS_SHIFT 9
67 static unsigned int pfield[PF_ROWS * PF_COLS];
69 static int pos[2], next_pos[2];
70 static int cur_block, next_block, prev_block;
71 static int cur_rot, prev_rot;
72 static int complines[4];
73 static int num_complines;
76 static int score, level, lines;
77 static int just_spawned;
80 static const long level_speed[NUM_LEVELS] = {
81 887, 820, 753, 686, 619, 552, 469, 368, 285, 184,
82 167, 151, 134, 117, 107, 98, 88, 79, 69, 60, 50
88 if(!(blkmesh = cmesh_alloc()) || cmesh_load(blkmesh, "data/noisecube.obj") == -1) {
89 error_log("failed to load block mesh\n");
93 if(!(wellmesh = cmesh_alloc()) || cmesh_load(wellmesh, "data/well.obj") == -1) {
94 error_log("failed to load well mesh\n");
98 if(!(tex_well = img_gltexture_load("data/grid.png"))) {
99 error_log("failed to load well texture\n");
106 static void cleanup(void)
111 static void start(void)
118 score = level = lines = 0;
119 tick_interval = level_speed[0];
122 next_block = rand() % NUM_BLOCKS;
124 memset(pfield, 0, PF_COLS * PF_ROWS * sizeof *pfield);
127 static void stop(void)
131 static void update(float dtsec)
133 static long prev_tick;
137 prev_tick = time_msec;
140 dt = time_msec - prev_tick;
144 int i, row = PF_ROWS - gameover;
147 if(dt < GAMEOVER_FILL_RATE) {
152 ptr = pfield + row * PF_COLS;
153 for(i=0; i<PF_COLS; i++) {
154 *ptr++ = TILE_GAMEOVER;
165 /* lines where completed, we're in blinking mode */
166 int i, j, blink = dt >> 8;
174 for(i=0; i<num_complines; i++) {
175 unsigned int *ptr = pfield + complines[i] * PF_COLS;
176 for(j=0; j<PF_COLS; j++) {
177 *ptr = (*ptr & ~PF_VIS) | ((blink & 1) << PF_VIS_SHIFT);
184 while(dt >= tick_interval) {
187 next_pos[0] = pos[0] + 1;
188 if(collision(cur_block, next_pos)) {
189 next_pos[0] = pos[0];
190 stick(cur_block, next_pos);
202 prev_tick = time_msec;
208 static void draw(void)
210 glTranslatef(0, 0, -cam_dist);
211 glRotatef(cam_phi, 1, 0, 0);
212 glRotatef(cam_theta, 0, 1, 0);
214 glPushAttrib(GL_ENABLE_BIT);
215 glBindTexture(GL_TEXTURE_2D, tex_well);
216 glEnable(GL_TEXTURE_2D);
217 glDisable(GL_LIGHTING);
219 cmesh_draw(wellmesh);
222 /* center playfield */
224 glTranslatef(-PF_COLS / 2 + 0.5, PF_ROWS / 2 - 0.5, 0);
228 draw_block(cur_block, pos, cur_rot);
234 static void draw_block(int block, const int *pos, int rot)
237 unsigned char *p = blocks[block][rot];
239 /*pal = FIRST_BLOCK_PAL + block;*/
242 int x = pos[1] + BLKX(*p);
243 int y = pos[0] + BLKY(*p);
249 glTranslatef(x, -y, 0);
255 static void drawpf(void)
258 unsigned int *sptr = pfield;
260 for(i=0; i<PF_ROWS; i++) {
261 for(j=0; j<PF_COLS; j++) {
262 unsigned int val = *sptr++;
265 glTranslatef(j, -i, 0);
274 static void reshape(int x, int y)
278 static void keyboard(int key, int pressed)
287 next_pos[1] = pos[1] - 1;
288 if(collision(cur_block, next_pos)) {
289 next_pos[1] = pos[1];
298 next_pos[1] = pos[1] + 1;
299 if(collision(cur_block, next_pos)) {
300 next_pos[1] = pos[1];
310 cur_rot = (cur_rot + 1) & 3;
311 if(collision(cur_block, next_pos)) {
320 /* ignore drops until the first update after a spawn */
321 if(cur_block >= 0 && !just_spawned && !pause) {
322 next_pos[0] = pos[0] + 1;
323 if(collision(cur_block, next_pos)) {
324 next_pos[0] = pos[0];
326 stick(cur_block, next_pos); /* stick immediately */
333 if(!pause && cur_block >= 0) {
334 next_pos[0] = pos[0] + 1;
335 while(!collision(cur_block, next_pos)) {
340 stick(cur_block, next_pos); /* stick immediately */
347 if(score && is_highscore(score)) {
348 name = name_screen(score);
350 save_score(name, score, lines, level);
352 /* TODO: pop screen */
360 if(score && is_highscore(score)) {
361 name = name_screen(score);
363 save_score(name, score, lines, level);
365 /* TODO: pop screen */
373 static void mouse(int bn, int pressed, int x, int y)
375 bnstate[bn] = pressed;
380 static void motion(int x, int y)
382 float dx = x - prev_mx;
383 float dy = y - prev_my;
388 cam_theta += dx * 0.5;
391 if(cam_phi < -90) cam_phi = -90;
392 if(cam_phi > 90) cam_phi = 90;
395 cam_dist += dy * 0.1;
396 if(cam_dist < 0) cam_dist = 0;
400 static void wheel(int dir)
404 static void update_cur_block(void)
406 if(cur_block < 0) return;
408 memcpy(pos, next_pos, sizeof pos);
412 static void addscore(int nlines)
414 static const int stab[] = {40, 100, 300, 1200}; /* bonus per line completed */
418 score += stab[nlines - 1] * (level + 1);
422 if(level > NUM_LEVELS - 1) level = NUM_LEVELS - 1;
424 tick_interval = level_speed[level];
427 static int spawn(void)
432 r = rand() % NUM_BLOCKS;
433 } while(tries-- > 0 && (r | prev_block | next_block) == prev_block);
435 cur_block = next_block;
438 prev_rot = cur_rot = 0;
439 pos[0] = block_spawnpos[cur_block][0];
440 next_pos[0] = pos[0] + 1;
441 pos[1] = next_pos[1] = PF_COLS / 2 + block_spawnpos[cur_block][1];
443 if(collision(cur_block, next_pos)) {
451 static int collision(int block, const int *pos)
454 unsigned char *p = blocks[block][cur_rot];
457 int x = pos[1] + BLKX(*p);
458 int y = pos[0] + BLKY(*p);
463 if(x < 0 || x >= PF_COLS || y >= PF_ROWS) return 1;
464 if(pfield[y * PF_COLS + x] & PF_FULL) return 1;
470 static void stick(int block, const int *pos)
473 unsigned int *pfline;
474 unsigned char *p = blocks[block][cur_rot];
477 prev_block = cur_block; /* used by the spawn routine */
481 int x = pos[1] + BLKX(*p);
482 int y = pos[0] + BLKY(*p);
485 pfline = pfield + y * PF_COLS;
486 pfline[x] = PF_FULL | PF_VIS | block;
489 for(j=0; j<PF_COLS; j++) {
490 if(!(pfline[j] & PF_FULL)) {
496 complines[num_complines++] = y;
503 addscore(num_complines);
507 static void erase_completed(void)
509 int i, j, srow, drow;
510 unsigned int *pfstart = pfield;
513 /* sort completed lines from highest to lowest row number */
514 for(i=0; i<num_complines-1; i++) {
515 for(j=i+1; j<num_complines; j++) {
516 if(complines[j] > complines[i]) {
517 int tmp = complines[j];
518 complines[j] = complines[i];
524 srow = drow = PF_ROWS - 1;
525 dptr = pfstart + drow * PF_COLS;
527 for(i=0; i<PF_ROWS; i++) {
528 for(j=0; j<num_complines; j++) {
529 if(complines[j] == srow) {
535 for(j=0; j<PF_COLS; j++) {
539 } else if(srow != drow) {
540 unsigned int *sptr = pfstart + srow * PF_COLS;
541 memcpy(dptr, sptr, PF_COLS * sizeof *dptr);