ok now it works nicely in VR
[vrtris] / src / gamescr.c
1 #include <stdlib.h>
2 #include <time.h>
3 #include <assert.h>
4 #include <imago2.h>
5 #ifdef BUILD_VR
6 #include <goatvr.h>
7 #endif
8 #include <drawtext.h>
9 #include "opengl.h"
10 #include "game.h"
11 #include "screen.h"
12 #include "cmesh.h"
13 #include "blocks.h"
14 #include "logger.h"
15 #include "gameinp.h"
16 #include "color.h"
17
18 #define FONTSZ  75
19
20 int init_starfield(void);
21 void draw_starfield(void);
22
23 static int init(void);
24 static void cleanup(void);
25 static void start(void);
26 static void stop(void);
27 static void update(float dt);
28 static void draw(void);
29 static void draw_block(int block, const int *pos, int rot, float sat, float alpha);
30 static void drawpf(void);
31 static void reshape(int x, int y);
32 static void keyboard(int key, int pressed);
33 static void mouse(int bn, int pressed, int x, int y);
34 static void motion(int x, int y);
35 static void wheel(int dir);
36
37 static void update_cur_block(void);
38 static void addscore(int nlines);
39 static int spawn(void);
40 static int collision(int block, const int *pos);
41 static void stick(int block, const int *pos);
42 static void erase_completed(void);
43
44 struct game_screen game_screen = {
45         "game",
46         1,      /* opaque */
47         0,      /* next */
48         init,
49         cleanup,
50         start,
51         stop,
52         update,
53         draw,
54         reshape,
55         keyboard,
56         mouse,
57         motion,
58         wheel
59 };
60
61 static struct cmesh *blkmesh, *wellmesh;
62 static unsigned int tex_well;
63
64 static struct dtx_font *scorefont;
65
66 static float cam_theta, cam_phi, cam_dist;
67 static float cam_height;
68 static unsigned int bnstate;
69 static int prev_mx, prev_my;
70
71 static long tick_interval;
72
73 /* dimensions of the playfield */
74 #define PF_ROWS         18
75 #define PF_COLS         10
76
77 #define PF_FULL         0x100
78 #define PF_VIS          0x200
79 #define PF_VIS_SHIFT    9
80
81 static unsigned int pfield[PF_ROWS * PF_COLS];
82
83 static int pos[2], next_pos[2];
84 static int cur_block, next_block, prev_block;
85 static int cur_rot, prev_rot;
86 static int complines[4];
87 static int num_complines;
88 static int gameover;
89 static int pause;
90 static int score, level, lines;
91 static int just_spawned;
92
93 #ifdef BUILD_VR
94 static int vrbn_a = 0, vrbn_x = 4;
95 static float vrscale = 40.0f;
96 #endif
97
98 #define NUM_LEVELS      21
99 static const long level_speed[NUM_LEVELS] = {
100         887, 820, 753, 686, 619, 552, 469, 368, 285, 184,
101         167, 151, 134, 117, 107, 98, 88, 79, 69, 60, 50
102 };
103
104 static const float blkcolor[][4] = {
105         {1.0, 0.65, 0.0, 1},
106         {0.16, 1.0, 0.4, 1},
107         {0.65, 0.65, 1.0, 1},
108         {1.0, 0.9, 0.1, 1},
109         {0.0, 1.0, 1.0, 1},
110         {1.0, 0.5, 1.0, 1},
111         {1.0, 0.35, 0.2, 1},
112         {0.5, 0.5, 0.5, 1}
113 };
114
115 #define GAMEOVER_FILL_RATE      50
116
117
118 static int init(void)
119 {
120         if(!(scorefont = dtx_open_font("data/score.font", 0))) {
121                 error_log("failed to open score font\n");
122                 return -1;
123         }
124         dtx_prepare_range(scorefont, FONTSZ, 32, 127);
125         dtx_save_glyphmap("foo.ppm", dtx_get_glyphmap(scorefont, 0));
126
127         if(init_starfield() == -1) {
128                 return -1;
129         }
130
131         if(!(blkmesh = cmesh_alloc()) || cmesh_load(blkmesh, "data/noisecube.obj") == -1) {
132                 error_log("failed to load block mesh\n");
133                 return -1;
134         }
135
136         if(!(wellmesh = cmesh_alloc()) || cmesh_load(wellmesh, "data/well.obj") == -1) {
137                 error_log("failed to load well mesh\n");
138                 return -1;
139         }
140
141         if(!(tex_well = img_gltexture_load("data/grid.png"))) {
142                 error_log("failed to load well texture\n");
143                 return -1;
144         }
145
146         return 0;
147 }
148
149 static void cleanup(void)
150 {
151         cmesh_free(blkmesh);
152         cmesh_free(wellmesh);
153         glDeleteTextures(1, &tex_well);
154         dtx_close_font(scorefont);
155 }
156
157 static void start(void)
158 {
159         srand(time(0));
160
161         glClearColor(0.12, 0.12, 0.18, 1);
162
163         pause = 0;
164         gameover = 0;
165         num_complines = 0;
166         score = level = lines = 0;
167         tick_interval = level_speed[0];
168         cur_block = -1;
169         prev_block = 0;
170         next_block = rand() % NUM_BLOCKS;
171
172         memset(pfield, 0, PF_COLS * PF_ROWS * sizeof *pfield);
173
174         ginp_repeat(500, 75, GINP_LEFT | GINP_RIGHT | GINP_DOWN);
175
176         dtx_use_font(scorefont, FONTSZ);
177
178         cam_theta = 0;
179         cam_phi = 0;
180         cam_dist = 30;
181         cam_height = 0;
182
183 #ifdef BUILD_VR
184         if(goatvr_invr()) {
185                 int bn = goatvr_lookup_button("A");
186                 if(bn >= 0) vrbn_a = bn;
187
188                 bn = goatvr_lookup_button("X");
189                 if(bn >= 0) vrbn_x = bn;
190
191                 /* switch to VR-optimized camera parameters */
192                 cam_theta = 0;
193                 cam_phi = -2.5;
194                 cam_dist = 20;
195                 cam_height = 3.5;
196                 vrscale = 40.0f;
197
198                 goatvr_set_units_scale(vrscale);
199         }
200 #endif
201 }
202
203 static void stop(void)
204 {
205         goatvr_set_units_scale(1.0f);
206 }
207
208 #define JTHRES  0.6
209
210 #define CHECK_BUTTON(idx, gbn) \
211         if(joy_bnstate & (1 << idx)) { \
212                 ginp_bnstate |= gbn; \
213         }
214
215 static void update_input(float dtsec)
216 {
217 #ifdef BUILD_VR
218         int num_vr_sticks;
219
220         if(goatvr_invr() && (num_vr_sticks = goatvr_num_sticks()) > 0) {
221                 float p[2];
222
223                 goatvr_stick_pos(0, p);
224                 p[1] *= 0.65;   /* drops harder to trigger accidentally */
225
226                 if(fabs(p[0]) > fabs(joy_axis[GPAD_LSTICK_X])) {
227                         joy_axis[GPAD_LSTICK_X] = p[0];
228                 }
229                 if(fabs(p[1]) > fabs(joy_axis[GPAD_LSTICK_Y])) {
230                         joy_axis[GPAD_LSTICK_Y] = -p[1];
231                 }
232
233                 if(goatvr_button_state(vrbn_a)) {
234                         joy_bnstate |= 1 << GPAD_A;
235                 }
236                 if(goatvr_button_state(vrbn_x)) {
237                         joy_bnstate |= 1 << GPAD_START;
238                 }
239                 if(goatvr_action(0, GOATVR_ACTION_TRIGGER) || goatvr_action(1, GOATVR_ACTION_TRIGGER)) {
240                         joy_bnstate |= 1 << GPAD_UP;
241                 }
242         }
243 #endif  /* BUILD_VR */
244
245         ginp_bnstate = 0;
246
247         /* joystick axis */
248         if(joy_axis[GPAD_LSTICK_X] >= JTHRES) {
249                 ginp_bnstate |= GINP_RIGHT;
250         } else if(joy_axis[GPAD_LSTICK_X] <= -JTHRES) {
251                 ginp_bnstate |= GINP_LEFT;
252         }
253
254         if(joy_axis[GPAD_LSTICK_Y] >= JTHRES) {
255                 ginp_bnstate |= GINP_DOWN;
256         } else if(joy_axis[GPAD_LSTICK_Y] <= -JTHRES) {
257                 ginp_bnstate |= GINP_UP;
258         }
259
260         CHECK_BUTTON(GPAD_LEFT, GINP_LEFT);
261         CHECK_BUTTON(GPAD_RIGHT, GINP_RIGHT);
262         CHECK_BUTTON(GPAD_UP, GINP_UP);
263         CHECK_BUTTON(GPAD_DOWN, GINP_DOWN);
264         CHECK_BUTTON(GPAD_A, GINP_ROTATE);
265         CHECK_BUTTON(GPAD_START, GINP_PAUSE);
266
267         update_ginp();
268
269         if(GINP_PRESS(GINP_LEFT)) {
270                 game_keyboard('a', 1);
271         }
272         if(GINP_PRESS(GINP_RIGHT)) {
273                 game_keyboard('d', 1);
274         }
275         if(GINP_PRESS(GINP_DOWN)) {
276                 game_keyboard('s', 1);
277         }
278         if(GINP_PRESS(GINP_UP)) {
279                 game_keyboard('\t', 1);
280         }
281         if(GINP_PRESS(GINP_ROTATE)) {
282                 game_keyboard('w', 1);
283         }
284         if(GINP_PRESS(GINP_PAUSE)) {
285                 game_keyboard('p', 1);
286         }
287
288 #ifdef BUILD_VR
289         memset(joy_axis, 0, sizeof joy_axis);
290         joy_bnstate = 0;
291 #endif
292 }
293
294 static void update(float dtsec)
295 {
296         static long prev_tick;
297         long dt;
298
299         update_input(dtsec);
300
301         if(pause) {
302                 prev_tick = time_msec;
303                 return;
304         }
305         dt = time_msec - prev_tick;
306
307         if(gameover) {
308                 int i, row = PF_ROWS - gameover;
309                 unsigned int *ptr;
310
311                 if(dt < GAMEOVER_FILL_RATE) {
312                         return;
313                 }
314
315                 if(row >= 0) {
316                         ptr = pfield + row * PF_COLS;
317                         for(i=0; i<PF_COLS; i++) {
318                                 *ptr++ = PF_VIS | PF_FULL | 7;
319                         }
320
321                         gameover++;
322                         prev_tick = time_msec;
323                 }
324                 return;
325         }
326
327         if(num_complines) {
328                 /* lines where completed, we're in blinking mode */
329                 int i, j, blink = dt >> 8;
330
331                 if(blink > 6) {
332                         erase_completed();
333                         num_complines = 0;
334                         return;
335                 }
336
337                 for(i=0; i<num_complines; i++) {
338                         unsigned int *ptr = pfield + complines[i] * PF_COLS;
339                         for(j=0; j<PF_COLS; j++) {
340                                 *ptr = (*ptr & ~PF_VIS) | ((blink & 1) << PF_VIS_SHIFT);
341                                 ptr++;
342                         }
343                 }
344                 return;
345         }
346
347         /* fall */
348         while(dt >= tick_interval) {
349                 if(cur_block >= 0) {
350                         just_spawned = 0;
351                         next_pos[0] = pos[0] + 1;
352                         if(collision(cur_block, next_pos)) {
353                                 next_pos[0] = pos[0];
354                                 stick(cur_block, next_pos);
355                                 return;
356                         }
357                 } else {
358                         /* respawn */
359                         if(spawn() == -1) {
360                                 gameover = 1;
361                                 return;
362                         }
363                 }
364
365                 dt -= tick_interval;
366                 prev_tick = time_msec;
367         }
368
369         update_cur_block();
370 }
371
372 static void draw(void)
373 {
374         static const int nextblk_pos[] = {0, 0};
375         static const float lpos[] = {-1, 1, 6, 1};
376         float t;
377
378         glTranslatef(0, 0, -cam_dist);
379         glRotatef(cam_phi, 1, 0, 0);
380         glRotatef(cam_theta, 0, 1, 0);
381         glTranslatef(0, -cam_height, 0);
382
383         glLightfv(GL_LIGHT0, GL_POSITION, lpos);
384
385         draw_starfield();
386
387         glPushAttrib(GL_ENABLE_BIT);
388         glEnable(GL_COLOR_MATERIAL);
389         glBindTexture(GL_TEXTURE_2D, tex_well);
390         glEnable(GL_TEXTURE_2D);
391         //glDisable(GL_LIGHTING);
392         glColor3f(1, 1, 1);
393         cmesh_draw(wellmesh);
394         glPopAttrib();
395
396         /* center playfield */
397         glPushMatrix();
398         glTranslatef(-PF_COLS / 2 + 0.5, PF_ROWS / 2 - 0.5, 0);
399
400         drawpf();
401         if(cur_block >= 0) {
402                 draw_block(cur_block, pos, cur_rot, 1.0f, 1.0f);
403         }
404         glPopMatrix();
405
406         glPushAttrib(GL_ENABLE_BIT);
407         glEnable(GL_BLEND);
408         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
409
410         glPushMatrix();
411         t = (float)time_msec / 1000.0f;
412         glTranslatef(-PF_COLS / 2 + 0.5 + PF_COLS + 3, PF_ROWS / 2 - 0.5, 0);
413         glTranslatef(1.5, -1, 0);
414         glRotatef(cos(t) * 8.0f, 1, 0, 0);
415         glRotatef(sin(t * 1.2f) * 10.0f, 0, 1, 0);
416         glTranslatef(-1.5, 1, 0);
417         draw_block(next_block, nextblk_pos, 0, 0.25f, 0.75f);
418         glPopMatrix();
419         glPopAttrib();
420
421         glPushAttrib(GL_ENABLE_BIT);
422         glDisable(GL_LIGHTING);
423
424         glPushMatrix();
425         glTranslatef(-11, 6, 0);
426         glScalef(0.05, 0.05, 0.05);
427
428         glColor3f(1, 1, 1);
429         dtx_string("Score");
430         glTranslatef(0, -dtx_line_height() * 1.5, 0);
431         glPushMatrix();
432         glScalef(1.5, 1.5, 1.5);
433         dtx_printf("%d", score);
434         glPopMatrix();
435
436         glTranslatef(0, -dtx_line_height() * 2, 0);
437         dtx_string("Level");
438         glTranslatef(0, -dtx_line_height() * 1.5, 0);
439         glPushMatrix();
440         glScalef(1.5, 1.5, 1.5);
441         dtx_printf("%d", level);
442         glPopMatrix();
443
444         glTranslatef(0, -dtx_line_height() * 2, 0);
445         dtx_string("Lines");
446         glTranslatef(0, -dtx_line_height() * 1.5, 0);
447         glPushMatrix();
448         glScalef(1.5, 1.5, 1.5);
449         dtx_printf("%d", lines);
450         glPopMatrix();
451
452         glPopMatrix();
453         glPopAttrib();
454 }
455
456 static const float blkspec[] = {0.85, 0.85, 0.85, 1};
457
458 static void draw_block(int block, const int *pos, int rot, float sat, float alpha)
459 {
460         int i;
461         unsigned char *p = blocks[block][rot];
462         float col[4], hsv[3];
463
464         rgb_to_hsv(blkcolor[block][0], blkcolor[block][1], blkcolor[block][2],
465                         hsv, hsv + 1, hsv + 2);
466         hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], col, col + 1, col + 2);
467         col[3] = alpha;
468
469         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
470         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, blkspec);
471         glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0f);
472
473         for(i=0; i<4; i++) {
474                 int x = pos[1] + BLKX(*p);
475                 int y = pos[0] + BLKY(*p);
476                 p++;
477
478                 if(y < 0) continue;
479
480                 glPushMatrix();
481                 glTranslatef(x, -y, 0);
482                 cmesh_draw(blkmesh);
483                 glPopMatrix();
484         }
485 }
486
487 static void drawpf(void)
488 {
489         int i, j;
490         unsigned int *sptr = pfield;
491
492         for(i=0; i<PF_ROWS; i++) {
493                 for(j=0; j<PF_COLS; j++) {
494                         unsigned int val = *sptr++;
495
496                         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, blkcolor[val & 7]);
497                         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, blkspec);
498                         glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0f);
499
500                         if((val & (PF_FULL | PF_VIS)) == (PF_FULL | PF_VIS)) {
501                                 glPushMatrix();
502                                 glTranslatef(j, -i, 0);
503                                 cmesh_draw(blkmesh);
504                                 glPopMatrix();
505                         }
506                 }
507         }
508 }
509
510
511 static void reshape(int x, int y)
512 {
513 }
514
515 static void keyboard(int key, int pressed)
516 {
517         /*char *name = 0;*/
518
519         if(!pressed) return;
520
521         switch(key) {
522         case 'a':
523         case KEY_LEFT:
524                 if(!pause) {
525                         next_pos[1] = pos[1] - 1;
526                         if(collision(cur_block, next_pos)) {
527                                 next_pos[1] = pos[1];
528                         } else {
529                                 /*snd_shift();*/
530                         }
531                 }
532                 break;
533
534         case 'd':
535         case KEY_RIGHT:
536                 if(!pause) {
537                         next_pos[1] = pos[1] + 1;
538                         if(collision(cur_block, next_pos)) {
539                                 next_pos[1] = pos[1];
540                         } else {
541                                 /*snd_shift();*/
542                         }
543                 }
544                 break;
545
546         case 'w':
547         case KEY_UP:
548         case ' ':
549                 if(!pause) {
550                         prev_rot = cur_rot;
551                         cur_rot = (cur_rot + 1) & 3;
552                         if(collision(cur_block, next_pos)) {
553                                 cur_rot = prev_rot;
554                         } else {
555                                 /*snd_rot();*/
556                         }
557                 }
558                 break;
559
560         case 's':
561         case KEY_DOWN:
562                 /* ignore drops until the first update after a spawn */
563                 if(cur_block >= 0 && !just_spawned && !pause) {
564                         next_pos[0] = pos[0] + 1;
565                         if(collision(cur_block, next_pos)) {
566                                 next_pos[0] = pos[0];
567                                 update_cur_block();
568                                 stick(cur_block, next_pos);     /* stick immediately */
569                         }
570                 }
571                 break;
572
573         case '\n':
574         case '\t':
575         case '0':
576                 if(!pause && cur_block >= 0) {
577                         next_pos[0] = pos[0] + 1;
578                         while(!collision(cur_block, next_pos)) {
579                                 next_pos[0]++;
580                         }
581                         next_pos[0]--;
582                         update_cur_block();
583                         stick(cur_block, next_pos);     /* stick immediately */
584                 }
585                 break;
586
587         case 'p':
588                 if(gameover) {
589                         /*
590                         if(score && is_highscore(score)) {
591                                 name = name_screen(score);
592                         }
593                         save_score(name, score, lines, level);
594                         */
595                         /* TODO: pop screen */
596                 } else {
597                         pause ^= 1;
598                 }
599                 break;
600
601         case '\b':
602                 /*
603                 if(score && is_highscore(score)) {
604                         name = name_screen(score);
605                 }
606                 save_score(name, score, lines, level);
607                 */
608                 /* TODO: pop screen */
609                 break;
610
611         default:
612                 break;
613         }
614 }
615
616 static void mouse(int bn, int pressed, int x, int y)
617 {
618         if(pressed) {
619                 bnstate |= 1 << bn;
620         } else {
621                 bnstate &= ~(1 << bn);
622         }
623         prev_mx = x;
624         prev_my = y;
625 }
626
627 static void motion(int x, int y)
628 {
629         float dx = x - prev_mx;
630         float dy = y - prev_my;
631         prev_mx = x;
632         prev_my = y;
633
634         if(bnstate & 1) {
635                 cam_theta += dx * 0.5;
636                 cam_phi += dy * 0.5;
637
638                 if(cam_phi < -90) cam_phi = -90;
639                 if(cam_phi > 90) cam_phi = 90;
640         }
641         if(bnstate & 2) {
642                 cam_height += dy * 0.1;
643         }
644         if(bnstate & 4) {
645                 cam_dist += dy * 0.1;
646                 if(cam_dist < 0) cam_dist = 0;
647         }
648
649 #ifdef DBG_PRINT_VIEW
650         if(bnstate) {
651                 debug_log("Camera params\n");
652                 debug_log("  theta: %g  phi: %g  dist: %g  height: %g\n", cam_theta,
653                                 cam_phi, cam_dist, cam_height);
654         }
655 #endif
656 }
657
658 static void wheel(int dir)
659 {
660         /* debug code, used to figure out the best scales */
661         /*
662         vrscale += dir * 0.01;
663         if(vrscale < 0.01) vrscale = 0.01;
664         goatvr_set_units_scale(vrscale);
665         debug_log("VR scale: %g\n", vrscale);
666         */
667 }
668
669 static void update_cur_block(void)
670 {
671         if(cur_block < 0) return;
672
673         memcpy(pos, next_pos, sizeof pos);
674         prev_rot = cur_rot;
675 }
676
677 static void addscore(int nlines)
678 {
679         static const int stab[] = {40, 100, 300, 1200}; /* bonus per line completed */
680
681         assert(nlines < 5);
682
683         score += stab[nlines - 1] * (level + 1);
684         lines += nlines;
685
686         level = lines / 10;
687         if(level > NUM_LEVELS - 1) level = NUM_LEVELS - 1;
688
689         tick_interval = level_speed[level];
690 }
691
692 static int spawn(void)
693 {
694         int r, tries = 2;
695
696         do {
697                 r = rand() % NUM_BLOCKS;
698         } while(tries-- > 0 && (r | prev_block | next_block) == prev_block);
699
700         cur_block = next_block;
701         next_block = r;
702
703         prev_rot = cur_rot = 0;
704         pos[0] = block_spawnpos[cur_block][0];
705         next_pos[0] = pos[0] + 1;
706         pos[1] = next_pos[1] = PF_COLS / 2 + block_spawnpos[cur_block][1];
707
708         if(collision(cur_block, next_pos)) {
709                 return -1;
710         }
711
712         just_spawned = 1;
713         return 0;
714 }
715
716 static int collision(int block, const int *pos)
717 {
718         int i;
719         unsigned char *p = blocks[block][cur_rot];
720
721         for(i=0; i<4; i++) {
722                 int x = pos[1] + BLKX(*p);
723                 int y = pos[0] + BLKY(*p);
724                 p++;
725
726                 if(y < 0) continue;
727
728                 if(x < 0 || x >= PF_COLS || y >= PF_ROWS) return 1;
729                 if(pfield[y * PF_COLS + x] & PF_FULL) return 1;
730         }
731
732         return 0;
733 }
734
735 static void stick(int block, const int *pos)
736 {
737         int i, j, nblank;
738         unsigned int *pfline;
739         unsigned char *p = blocks[block][cur_rot];
740
741         num_complines = 0;
742         prev_block = cur_block; /* used by the spawn routine */
743         cur_block = -1;
744
745         for(i=0; i<4; i++) {
746                 int x = pos[1] + BLKX(*p);
747                 int y = pos[0] + BLKY(*p);
748                 p++;
749
750                 pfline = pfield + y * PF_COLS;
751                 pfline[x] = PF_FULL | PF_VIS | block;
752
753                 nblank = 0;
754                 for(j=0; j<PF_COLS; j++) {
755                         if(!(pfline[j] & PF_FULL)) {
756                                 nblank++;
757                         }
758                 }
759
760                 if(nblank == 0) {
761                         complines[num_complines++] = y;
762                 }
763         }
764
765         /*snd_stick();*/
766
767         if(num_complines) {
768                 addscore(num_complines);
769         }
770 }
771
772 static void erase_completed(void)
773 {
774         int i, j, srow, drow;
775         unsigned int *pfstart = pfield;
776         unsigned int *dptr;
777
778         /* sort completed lines from highest to lowest row number */
779         for(i=0; i<num_complines-1; i++) {
780                 for(j=i+1; j<num_complines; j++) {
781                         if(complines[j] > complines[i]) {
782                                 int tmp = complines[j];
783                                 complines[j] = complines[i];
784                                 complines[i] = tmp;
785                         }
786                 }
787         }
788
789         srow = drow = PF_ROWS - 1;
790         dptr = pfstart + drow * PF_COLS;
791
792         for(i=0; i<PF_ROWS; i++) {
793                 for(j=0; j<num_complines; j++) {
794                         if(complines[j] == srow) {
795                                 srow--;
796                         }
797                 }
798
799                 if(srow < 0) {
800                         for(j=0; j<PF_COLS; j++) {
801                                 dptr[j] &= ~PF_FULL;
802                         }
803
804                 } else if(srow != drow) {
805                         unsigned int *sptr = pfstart + srow * PF_COLS;
806                         memcpy(dptr, sptr, PF_COLS * sizeof *dptr);
807                 }
808
809                 srow--;
810                 drow--;
811                 dptr -= PF_COLS;
812         }
813 }
814