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