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