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