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