rnd-20060207-2-src
[rocksndiamonds.git] / src / game_em / graphics.c
1 /* 2000-08-13T14:36:17Z
2  *
3  * graphics manipulation crap
4  */
5
6 #include "global.h"
7 #include "display.h"
8 #include "level.h"
9
10
11 unsigned int frame;             /* current screen frame */
12 #if 0
13 unsigned int screen_x;          /* current scroll position */
14 unsigned int screen_y;
15 #else
16 int screen_x;                   /* current scroll position */
17 int screen_y;
18 #endif
19
20 /* tiles currently on screen */
21 static unsigned int screentiles[MAX_BUF_YSIZE][MAX_BUF_XSIZE];
22 static unsigned int crumbled_state[MAX_BUF_YSIZE][MAX_BUF_XSIZE];
23
24 static boolean redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
25
26
27 /* copy the entire screen to the window at the scroll position
28  *
29  * perhaps use mit-shm to speed this up
30  */
31
32 void BlitScreenToBitmap_EM(Bitmap *target_bitmap)
33 {
34   unsigned int x = screen_x % (MAX_BUF_XSIZE * TILEX);
35   unsigned int y = screen_y % (MAX_BUF_YSIZE * TILEY);
36
37   if (x < 2 * TILEX && y < 2 * TILEY)
38   {
39     BlitBitmap(screenBitmap, target_bitmap, x, y,
40                SCR_FIELDX * TILEX, SCR_FIELDY * TILEY, SX, SY);
41   }
42   else if (x < 2 * TILEX && y >= 2 * TILEY)
43   {
44     BlitBitmap(screenBitmap, target_bitmap, x, y,
45                SCR_FIELDX * TILEX, MAX_BUF_YSIZE * TILEY - y,
46                SX, SY);
47     BlitBitmap(screenBitmap, target_bitmap, x, 0,
48                SCR_FIELDX * TILEX, y - 2 * TILEY,
49                SX, SY + MAX_BUF_YSIZE * TILEY - y);
50   }
51   else if (x >= 2 * TILEX && y < 2 * TILEY)
52   {
53     BlitBitmap(screenBitmap, target_bitmap, x, y,
54                MAX_BUF_XSIZE * TILEX - x, SCR_FIELDY * TILEY,
55                SX, SY);
56     BlitBitmap(screenBitmap, target_bitmap, 0, y,
57                x - 2 * TILEX, SCR_FIELDY * TILEY,
58                SX + MAX_BUF_XSIZE * TILEX - x, SY);
59   }
60   else
61   {
62     BlitBitmap(screenBitmap, target_bitmap, x, y,
63                MAX_BUF_XSIZE * TILEX - x, MAX_BUF_YSIZE * TILEY - y,
64                SX, SY);
65     BlitBitmap(screenBitmap, target_bitmap, 0, y,
66                x - 2 * TILEX, MAX_BUF_YSIZE * TILEY - y,
67                SX + MAX_BUF_XSIZE * TILEX - x, SY);
68     BlitBitmap(screenBitmap, target_bitmap, x, 0,
69                MAX_BUF_XSIZE * TILEX - x, y - 2 * TILEY,
70                SX, SY + MAX_BUF_YSIZE * TILEY - y);
71     BlitBitmap(screenBitmap, target_bitmap, 0, 0,
72                x - 2 * TILEX, y - 2 * TILEY,
73                SX + MAX_BUF_XSIZE * TILEX - x, SY + MAX_BUF_YSIZE * TILEY - y);
74   }
75 }
76
77 void blitscreen(void)
78 {
79 #if 1
80
81   static boolean scrolling_last = FALSE;
82   unsigned int left = screen_x / TILEX;
83   unsigned int top  = screen_y / TILEY;
84   boolean scrolling = (screen_x % TILEX != 0 || screen_y % TILEY != 0);
85   int x, y;
86
87   SyncDisplay();
88
89   if (redraw_tiles > REDRAWTILES_THRESHOLD || scrolling || scrolling_last)
90   {
91     /* blit all (up to four) parts of the scroll buffer to the backbuffer */
92     BlitScreenToBitmap_EM(backbuffer);
93
94     /* blit the completely updated backbuffer to the window (in one blit) */
95     BlitBitmap(backbuffer, window, SX, SY, SXSIZE, SYSIZE, SX, SY);
96   }
97   else
98   {
99     for (x = 0; x < SCR_FIELDX; x++)
100     {
101       for (y = 0; y < SCR_FIELDY; y++)
102       {
103         int xx = (left + x) % MAX_BUF_XSIZE;
104         int yy = (top  + y) % MAX_BUF_YSIZE;
105
106         if (redraw[xx][yy])
107           BlitBitmap(screenBitmap, window,
108                      xx * TILEX, yy * TILEY, TILEX, TILEY,
109                      SX + x * TILEX, SY + y * TILEY);
110       }
111     }
112   }
113
114   for (x = 0; x < MAX_BUF_XSIZE; x++)
115     for (y = 0; y < MAX_BUF_YSIZE; y++)
116       redraw[x][y] = FALSE;
117   redraw_tiles = 0;
118
119   scrolling_last = scrolling;
120
121 #else
122
123   /* blit all (up to four) parts of the scroll buffer to the window */
124   BlitScreenToBitmap_EM(window);
125
126 #endif
127 }
128
129 static void DrawLevelField_EM(int x, int y, int sx, int sy,
130                               boolean draw_masked)
131 {
132   int tile = Draw[y][x];
133   struct GraphicInfo_EM *g = &graphic_info_em_object[tile][frame];
134   int src_x = g->src_x + g->src_offset_x;
135   int src_y = g->src_y + g->src_offset_y;
136   int dst_x = sx * TILEX + g->dst_offset_x;
137   int dst_y = sy * TILEY + g->dst_offset_y;
138   int width = g->width;
139   int height = g->height;
140
141   if (draw_masked)
142   {
143     if (width > 0 && height > 0)
144     {
145       SetClipOrigin(g->bitmap, g->bitmap->stored_clip_gc,
146                     dst_x - src_x, dst_y - src_y);
147       BlitBitmapMasked(g->bitmap, screenBitmap,
148                        src_x, src_y, width, height, dst_x, dst_y);
149     }
150   }
151   else
152   {
153     if ((width != TILEX || height != TILEY) && !g->preserve_background)
154       ClearRectangle(screenBitmap, sx * TILEX, sy * TILEY, TILEX, TILEY);
155
156     if (width > 0 && height > 0)
157       BlitBitmap(g->bitmap, screenBitmap,
158                  src_x, src_y, width, height, dst_x, dst_y);
159   }
160 }
161
162 static void DrawLevelFieldCrumbled_EM(int x, int y, int sx, int sy,
163                                       int crm, boolean draw_masked)
164 {
165   int tile = Draw[y][x];
166   struct GraphicInfo_EM *g = &graphic_info_em_object[tile][frame];
167   unsigned int i;
168
169   if (crm == 0)         /* no crumbled edges for this tile */
170     return;
171
172   for (i = 0; i < 4; i++)
173   {
174     if (crm & (1 << i))
175     {
176       int width, height, cx, cy;
177
178       if (i == 1 || i == 2)
179       {
180         width = g->crumbled_border_size;
181         height = TILEY;
182         cx = (i == 2 ? TILEX - g->crumbled_border_size : 0);
183         cy = 0;
184       }
185       else
186       {
187         width = TILEX;
188         height = g->crumbled_border_size;
189         cx = 0;
190         cy = (i == 3 ? TILEY - g->crumbled_border_size : 0);
191       }
192
193       if (width > 0 && height > 0)
194       {
195         int src_x = g->crumbled_src_x + cx;
196         int src_y = g->crumbled_src_y + cy;
197         int dst_x = sx * TILEX + cx;
198         int dst_y = sy * TILEY + cy;
199
200         if (draw_masked)
201         {
202           SetClipOrigin(g->crumbled_bitmap, g->crumbled_bitmap->stored_clip_gc,
203                         dst_x - src_x, dst_y - src_y);
204           BlitBitmapMasked(g->crumbled_bitmap, screenBitmap,
205                            src_x, src_y, width, height, dst_x, dst_y);
206         }
207         else
208           BlitBitmap(g->crumbled_bitmap, screenBitmap,
209                      src_x, src_y, width, height, dst_x, dst_y);
210       }
211     }
212   }
213 }
214
215 static void DrawLevelPlayer_EM(int x1, int y1, int player_nr, int anim,
216                                boolean draw_masked)
217 {
218   struct GraphicInfo_EM *g = &graphic_info_em_player[player_nr][anim][frame];
219
220   int src_x = g->src_x, src_y = g->src_y;
221   int dst_x, dst_y;
222
223   if (draw_masked)
224   {
225     /* draw the player to current location */
226     dst_x = x1;
227     dst_y = y1;
228     SetClipOrigin(g->bitmap, g->bitmap->stored_clip_gc,
229                   dst_x - src_x, dst_y - src_y);
230     BlitBitmapMasked(g->bitmap, screenBitmap,
231                      src_x, src_y, TILEX, TILEY, dst_x, dst_y);
232
233     /* draw the player to opposite wrap-around column */
234     dst_x = x1 - MAX_BUF_XSIZE * TILEX;
235     dst_y = y1;
236     SetClipOrigin(g->bitmap, g->bitmap->stored_clip_gc,
237                   dst_x - src_x, dst_y - src_y);
238     BlitBitmapMasked(g->bitmap, screenBitmap,
239                      g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
240
241     /* draw the player to opposite wrap-around row */
242     dst_x = x1;
243     dst_y = y1 - MAX_BUF_YSIZE * TILEY;
244     SetClipOrigin(g->bitmap, g->bitmap->stored_clip_gc,
245                   dst_x - src_x, dst_y - src_y);
246     BlitBitmapMasked(g->bitmap, screenBitmap,
247                      g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
248   }
249   else
250   {
251     /* draw the player to current location */
252     dst_x = x1;
253     dst_y = y1;
254     BlitBitmap(g->bitmap, screenBitmap,
255                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
256
257     /* draw the player to opposite wrap-around column */
258     dst_x = x1 - MAX_BUF_XSIZE * TILEX;
259     dst_y = y1;
260     BlitBitmap(g->bitmap, screenBitmap,
261                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
262
263     /* draw the player to opposite wrap-around row */
264     dst_x = x1;
265     dst_y = y1 - MAX_BUF_YSIZE * TILEY;
266     BlitBitmap(g->bitmap, screenBitmap,
267                g->src_x, g->src_y, TILEX, TILEY, dst_x, dst_y);
268   }
269 }
270
271 /* draw differences between game tiles and screen tiles
272  *
273  * implicitly handles scrolling and restoring background under the sprites
274  *
275  * perhaps use mit-shm to speed this up
276  */
277
278 static void animscreen(void)
279 {
280   unsigned int x, y, i;
281   unsigned int left = screen_x / TILEX;
282   unsigned int top  = screen_y / TILEY;
283   static int xy[4][2] =
284   {
285     { 0, -1 },
286     { -1, 0 },
287     { +1, 0 },
288     { 0, +1 }
289   };
290
291   for (y = top; y < top + MAX_BUF_YSIZE; y++)
292   {
293     for (x = left; x < left + MAX_BUF_XSIZE; x++)
294     {
295       int sx = x % MAX_BUF_XSIZE;
296       int sy = y % MAX_BUF_YSIZE;    
297       int tile = Draw[y][x];
298       struct GraphicInfo_EM *g = &graphic_info_em_object[tile][frame];
299       unsigned int obj = g->unique_identifier;
300       unsigned int crm = 0;
301
302       /* re-calculate crumbled state of this tile */
303       if (g->has_crumbled_graphics)
304       {
305         for (i = 0; i < 4; i++)
306         {
307           int xx = x + xy[i][0];
308           int yy = y + xy[i][1];
309           int tile_next;
310
311           if (xx < 0 || xx >= EM_MAX_CAVE_WIDTH ||
312               yy < 0 || yy >= EM_MAX_CAVE_HEIGHT)
313             continue;
314
315           tile_next = Draw[yy][xx];
316
317           if (!graphic_info_em_object[tile_next][frame].has_crumbled_graphics)
318             crm |= (1 << i);
319         }
320       }
321
322       /* only redraw screen tiles if they (or their crumbled state) changed */
323       if (screentiles[sy][sx] != obj || crumbled_state[sy][sx] != crm)
324       {
325         DrawLevelField_EM(x, y, sx, sy, FALSE);
326         DrawLevelFieldCrumbled_EM(x, y, sx, sy, crm, FALSE);
327
328         screentiles[sy][sx] = obj;
329         crumbled_state[sy][sx] = crm;
330
331         redraw[sx][sy] = TRUE;
332         redraw_tiles++;
333       }
334     }
335   }
336 }
337
338
339 /* blit players to the screen
340  *
341  * handles transparency and movement
342  */
343
344 static void blitplayer(struct PLAYER *ply)
345 {
346   unsigned int x1, y1, x2, y2;
347
348   if (!ply->alive)
349     return;
350
351   /* x1/y1 are left/top and x2/y2 are right/down part of the player movement */
352   x1 = (frame * ply->oldx + (8 - frame) * ply->x) * TILEX / 8;
353   y1 = (frame * ply->oldy + (8 - frame) * ply->y) * TILEY / 8;
354   x2 = x1 + TILEX - 1;
355   y2 = y1 + TILEY - 1;
356
357   if ((unsigned int)(x2 - screen_x) < ((MAX_BUF_XSIZE - 1) * TILEX - 1) &&
358       (unsigned int)(y2 - screen_y) < ((MAX_BUF_YSIZE - 1) * TILEY - 1))
359   {
360     /* some casts to "int" are needed because of negative calculation values */
361     int dx = (int)ply->x - (int)ply->oldx;
362     int dy = (int)ply->y - (int)ply->oldy;
363     int old_x = (int)ply->oldx + (7 - (int)frame) * dx / 8;
364     int old_y = (int)ply->oldy + (7 - (int)frame) * dy / 8;
365     int new_x = old_x + SIGN(dx);
366     int new_y = old_y + SIGN(dy);
367     int old_sx = old_x % MAX_BUF_XSIZE;
368     int old_sy = old_y % MAX_BUF_XSIZE;
369     int new_sx = new_x % MAX_BUF_XSIZE;
370     int new_sy = new_y % MAX_BUF_XSIZE;
371 #if 0
372     int old_crm = crumbled_state[old_sy][old_sx];
373 #endif
374     int new_crm = crumbled_state[new_sy][new_sx];
375
376     /* only diggable elements can be crumbled in the classic EM engine */
377     boolean player_is_digging = (new_crm != 0);
378
379     x1 %= MAX_BUF_XSIZE * TILEX;
380     y1 %= MAX_BUF_YSIZE * TILEY;
381     x2 %= MAX_BUF_XSIZE * TILEX;
382     y2 %= MAX_BUF_YSIZE * TILEY;
383
384     if (player_is_digging)
385     {
386 #if 0
387       /* draw the field the player is moving from (under the player) */
388       DrawLevelField_EM(old_x, old_y, old_sx, old_sy, FALSE);
389       DrawLevelFieldCrumbled_EM(old_x, old_y, old_sx, old_sy, old_crm, FALSE);
390 #endif
391
392       /* draw the field the player is moving to (under the player) */
393       DrawLevelField_EM(new_x, new_y, new_sx, new_sy, FALSE);
394       DrawLevelFieldCrumbled_EM(new_x, new_y, new_sx, new_sy, new_crm, FALSE);
395
396       /* draw the player (masked) over the element he is just digging away */
397       DrawLevelPlayer_EM(x1, y1, ply->num, ply->anim, TRUE);
398
399 #if 1
400       /* draw the field the player is moving from (masked over the player) */
401       DrawLevelField_EM(old_x, old_y, old_sx, old_sy, TRUE);
402 #endif
403     }
404     else
405     {
406       /* draw the player under the element which is on the same field */
407       DrawLevelPlayer_EM(x1, y1, ply->num, ply->anim, FALSE);
408
409       /* draw the field the player is moving from (masked over the player) */
410       DrawLevelField_EM(old_x, old_y, old_sx, old_sy, TRUE);
411
412       /* draw the field the player is moving to (masked over the player) */
413       DrawLevelField_EM(new_x, new_y, new_sx, new_sy, TRUE);
414     }
415
416     /* mark screen tiles as dirty */
417     screentiles[old_sy][old_sx] = -1;
418     screentiles[new_sy][new_sx] = -1;
419   }
420 }
421
422 void game_initscreen(void)
423 {
424   unsigned int x,y;
425   int dynamite_state = ply[0].dynamite;         /* !!! ONLY PLAYER 1 !!! */
426   int all_keys_state = ply[0].keys | ply[1].keys | ply[2].keys | ply[3].keys;
427
428   frame = 6;
429   screen_x = 0;
430   screen_y = 0;
431
432   for (y = 0; y < MAX_BUF_YSIZE; y++)
433   {
434     for (x = 0; x < MAX_BUF_XSIZE; x++)
435     {
436       screentiles[y][x] = -1;
437       crumbled_state[y][x] = 0;
438     }
439   }
440
441 #if 1
442   DrawAllGameValues(lev.required, dynamite_state, lev.score,
443                     lev.time, all_keys_state);
444 #else
445   DrawAllGameValues(lev.required, ply1.dynamite, lev.score,
446                     DISPLAY_TIME(lev.time + 4), ply1.keys | ply2.keys);
447 #endif
448 }
449
450 void RedrawPlayfield_EM()
451 {
452   int i, sx, sy;
453
454 #if 1
455
456   int screen_x_min = TILEX;
457   int screen_y_min = TILEY;
458   int screen_x_max = (lev.width  - (SCR_FIELDX - 1)) * TILEX;
459   int screen_y_max = (lev.height - (SCR_FIELDY - 1)) * TILEY;
460   int offset = (setup.scroll_delay ? 3 : 0) * TILEX;
461   int stepsize = TILEX / 8;
462   int nr = 0;           /* !!! FIX THIS (CENTERED TO PLAYER 1) !!! */
463
464   sx = (frame * ply[nr].oldx + (8 - frame) * ply[nr].x) * stepsize
465     - ((SCR_FIELDX - 1) * TILEX) / 2;
466   sy = (frame * ply[nr].oldy + (8 - frame) * ply[nr].y) * stepsize
467     - ((SCR_FIELDY - 1) * TILEY) / 2;
468
469   /* calculate new screen scrolling position, with regard to scroll delay */
470   screen_x = (sx < screen_x - offset ? sx + offset :
471               sx > screen_x + offset ? sx - offset : screen_x);
472   screen_y = (sy < screen_y - offset ? sy + offset :
473               sy > screen_y + offset ? sy - offset : screen_y);
474
475   /* check minimal and maximal boundaries for screen scrolling position */
476   screen_x = (screen_x < screen_x_min ? screen_x_min :
477               screen_x > screen_x_max ? screen_x_max : screen_x);
478   screen_y = (screen_y < screen_y_min ? screen_y_min :
479               screen_y > screen_y_max ? screen_y_max : screen_y);
480
481 #else
482
483   /* !!! FIX THIS (CENTERED TO PLAYER 1) !!! */
484   sx = (frame * ply[0].oldx + (8 - frame) * ply[0].x) * TILEX / 8
485     + ((SCR_FIELDX - 1) * TILEX) / 2;
486   sy = (frame * ply[0].oldy + (8 - frame) * ply[0].y) * TILEY / 8
487     + ((SCR_FIELDY - 1) * TILEY) / 2;
488
489   if (sx > lev.width * TILEX)
490     sx = lev.width * TILEX;
491   if (sy > lev.height * TILEY)
492     sy = lev.height * TILEY;
493
494   if (sx < SCR_FIELDX * TILEX)
495     sx = SCR_FIELDX * TILEY;
496   if (sy < SCR_FIELDY * TILEY)
497     sy = SCR_FIELDY * TILEY;
498
499   screen_x = sx - (SCR_FIELDX - 1) * TILEX;
500   screen_y = sy - (SCR_FIELDY - 1) * TILEY;
501
502 #endif
503
504   animscreen();
505
506   for (i = 0; i < MAX_PLAYERS; i++)
507     blitplayer(&ply[i]);
508
509   blitscreen();
510
511   FlushDisplay();
512 }
513
514 void game_animscreen(void)
515 {
516   RedrawPlayfield_EM();
517 }
518
519 void DrawGameDoorValues_EM()
520 {
521   int dynamite_state = ply[0].dynamite;         /* !!! ONLY PLAYER 1 !!! */
522   int all_keys_state = ply[0].keys | ply[1].keys | ply[2].keys | ply[3].keys;
523
524 #if 1
525   DrawAllGameValues(lev.required, dynamite_state, lev.score,
526                     lev.time, all_keys_state);
527 #else
528   DrawAllGameValues(lev.required, ply1.dynamite, lev.score,
529                     DISPLAY_TIME(lev.time), ply1.keys | ply2.keys);
530 #endif
531 }