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