fixed several bugs with shared playfield bitmap in MM game engine
[rocksndiamonds.git] / src / game_mm / mm_tools.c
1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // mm_tools.c
10 // ============================================================================
11
12 #include <time.h>
13
14 #include "main_mm.h"
15
16 #include "mm_main.h"
17 #include "mm_tools.h"
18
19
20 void SetDrawtoField_MM(int mode)
21 {
22   int full_xsize = lev_fieldx * TILESIZE_VAR;
23   int full_ysize = lev_fieldy * TILESIZE_VAR;
24
25   // distance (delta) from screen border (SX/SY) to centered level playfield
26   dSX = (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
27   dSY = (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
28
29   // for convenience, absolute screen position to centered level playfield
30   cSX = SX + dSX;
31   cSY = SY + dSY;
32   cSX2 = SX + dSX + 2;  // including playfield border
33   cSY2 = SY + dSY + 2;  // including playfield border
34
35   if (mode == DRAW_TO_BACKBUFFER)
36   {
37     cFX = FX + dSX;
38     cFY = FY + dSY;
39   }
40
41   SetTileCursorSXSY(cSX, cSY);
42 }
43
44 void BackToFront_MM(void)
45 {
46   BlitScreenToBitmap_MM(backbuffer);
47
48   BackToFront();
49 }
50
51 void ClearWindow(void)
52 {
53   ClearRectangle(drawto_mm, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
54
55   SetDrawtoField(DRAW_TO_BACKBUFFER);
56   SetDrawtoField_MM(DRAW_TO_BACKBUFFER);
57
58   redraw_mask |= REDRAW_FIELD;
59 }
60
61 void DrawGraphicAnimation_MM(int x, int y, int graphic, int frame)
62 {
63   Bitmap *bitmap;
64   int src_x, src_y;
65
66   getGraphicSource(graphic, frame, &bitmap, &src_x, &src_y);
67
68   BlitBitmap(bitmap, drawto_mm, src_x, src_y, TILEX, TILEY,
69              cFX + x * TILEX, cFY + y * TILEY);
70 }
71
72 void DrawGraphic_MM(int x, int y, int graphic)
73 {
74 #if DEBUG
75   if (!IN_SCR_FIELD(x,y))
76   {
77     Debug("game:mm:DrawGraphic_MM", "x = %d, y = %d, graphic = %d",
78           x, y, graphic);
79     Debug("game:mm:DrawGraphic_MM", "This should never happen!");
80
81     return;
82   }
83 #endif
84
85   int frame = getGraphicAnimationFrameXY(graphic, x, y);
86
87   DrawGraphicAnimation_MM(x, y, graphic, frame);
88
89   MarkTileDirty(x, y);
90 }
91
92 void DrawGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
93 {
94   Bitmap *bitmap;
95   int src_x, src_y;
96
97   getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
98
99   BlitBitmap(bitmap, d, src_x, src_y, TILEX, TILEY, x, y);
100 }
101
102 void DrawGraphicThruMask_MM(int x, int y, int graphic, int frame)
103 {
104 #if DEBUG
105   if (!IN_SCR_FIELD(x,y))
106   {
107     Debug("game:mm:DrawGraphicThruMask_MM", "x = %d,y = %d, graphic = %d",
108           x, y, graphic);
109     Debug("game:mm:DrawGraphicThruMask_MM", "This should never happen!");
110
111     return;
112   }
113 #endif
114
115   DrawGraphicThruMaskExt_MM(drawto_mm, cFX + x * TILEX, cFY + y * TILEY,
116                             graphic, frame);
117
118   MarkTileDirty(x,y);
119 }
120
121 void DrawGraphicThruMaskExt_MM(DrawBuffer *d, int dest_x, int dest_y,
122                                int graphic, int frame)
123 {
124   int src_x, src_y;
125   Bitmap *src_bitmap;
126
127   if (graphic == IMG_EMPTY)
128     return;
129
130   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
131
132   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
133 }
134
135 void DrawMiniGraphic_MM(int x, int y, int graphic)
136 {
137   DrawMiniGraphicExt_MM(drawto_mm, cSX + x * MINI_TILEX, cSY + y * MINI_TILEY,
138                         graphic);
139
140   MarkTileDirty(x / 2, y / 2);
141 }
142
143 void DrawMiniGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
144 {
145   Bitmap *bitmap;
146   int src_x, src_y;
147
148   getMiniGraphicSource(graphic, &bitmap, &src_x, &src_y);
149
150   BlitBitmap(bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
151 }
152
153 void DrawGraphicShifted_MM(int x,int y, int dx,int dy, int graphic,
154                         int cut_mode, int mask_mode)
155 {
156   int width = TILEX, height = TILEY;
157   int cx = 0, cy = 0;
158   int src_x, src_y, dest_x, dest_y;
159   Bitmap *src_bitmap;
160
161   if (graphic < 0)
162   {
163     DrawGraphic_MM(x, y, graphic);
164
165     return;
166   }
167
168   if (dx || dy)                 // Verschiebung der Grafik?
169   {
170     if (x < BX1)                // Element kommt von links ins Bild
171     {
172       x = BX1;
173       width = dx;
174       cx = TILEX - dx;
175       dx = 0;
176     }
177     else if (x > BX2)           // Element kommt von rechts ins Bild
178     {
179       x = BX2;
180       width = -dx;
181       dx = TILEX + dx;
182     }
183     else if (x==BX1 && dx < 0)  // Element verläßt links das Bild
184     {
185       width += dx;
186       cx = -dx;
187       dx = 0;
188     }
189     else if (x==BX2 && dx > 0)  // Element verläßt rechts das Bild
190       width -= dx;
191     else if (dx)                // allg. Bewegung in x-Richtung
192       MarkTileDirty(x + SIGN(dx), y);
193
194     if (y < BY1)                // Element kommt von oben ins Bild
195     {
196       if (cut_mode==CUT_BELOW)  // Element oberhalb des Bildes
197         return;
198
199       y = BY1;
200       height = dy;
201       cy = TILEY - dy;
202       dy = 0;
203     }
204     else if (y > BY2)           // Element kommt von unten ins Bild
205     {
206       y = BY2;
207       height = -dy;
208       dy = TILEY + dy;
209     }
210     else if (y==BY1 && dy < 0)  // Element verläßt oben das Bild
211     {
212       height += dy;
213       cy = -dy;
214       dy = 0;
215     }
216     else if (dy > 0 && cut_mode == CUT_ABOVE)
217     {
218       if (y == BY2)             // Element unterhalb des Bildes
219         return;
220
221       height = dy;
222       cy = TILEY - dy;
223       dy = TILEY;
224       MarkTileDirty(x, y + 1);
225     }                           // Element verläßt unten das Bild
226     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
227     {
228       height -= dy;
229     }
230     else if (dy)                // allg. Bewegung in y-Richtung
231     {
232       MarkTileDirty(x, y + SIGN(dy));
233     }
234   }
235
236   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
237
238   src_x += cx;
239   src_y += cy;
240
241   dest_x = cFX + x * TILEX + dx;
242   dest_y = cFY + y * TILEY + dy;
243
244 #if DEBUG
245   if (!IN_SCR_FIELD(x,y))
246   {
247     Debug("game:mm:DrawGraphicShifted_MM", "x = %d, y = %d, graphic = %d",
248           x, y, graphic);
249     Debug("game:mm:DrawGraphicShifted_MM", "This should never happen!");
250
251     return;
252   }
253 #endif
254
255   if (mask_mode == USE_MASKING)
256     BlitBitmapMasked(src_bitmap, drawto_mm,
257                      src_x, src_y, TILEX, TILEY, dest_x, dest_y);
258   else
259     BlitBitmap(src_bitmap, drawto_mm,
260                src_x, src_y, width, height, dest_x, dest_y);
261
262   MarkTileDirty(x,y);
263 }
264
265 void DrawScreenElementExt_MM(int x, int y, int dx, int dy, int element,
266                           int cut_mode, int mask_mode)
267 {
268   int ux = LEVELX(x), uy = LEVELY(y);
269   int graphic = el2gfx(element);
270   int phase8 = ABS(MovPos[ux][uy]) / (TILEX / 8);
271   int phase2  = phase8 / 4;
272   int dir = MovDir[ux][uy];
273
274   if (element == EL_PACMAN)
275   {
276     graphic = (phase2 ? IMG_MM_PACMAN_RIGHT : IMG_MM_PACMAN_EATING_RIGHT);
277
278     if (dir == MV_UP)
279       graphic += 1;
280     else if (dir == MV_LEFT)
281       graphic += 2;
282     else if (dir == MV_DOWN)
283       graphic += 3;
284   }
285
286   if (dx || dy)
287     DrawGraphicShifted_MM(x, y, dx, dy, graphic, cut_mode, mask_mode);
288   else if (mask_mode == USE_MASKING)
289     DrawGraphicThruMask_MM(x, y, graphic, 0);
290   else
291     DrawGraphic_MM(x, y, graphic);
292 }
293
294 void DrawLevelElementExt_MM(int x, int y, int dx, int dy, int element,
295                          int cut_mode, int mask_mode)
296 {
297   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
298     DrawScreenElementExt_MM(SCREENX(x), SCREENY(y), dx, dy, element,
299                          cut_mode, mask_mode);
300 }
301
302 void DrawScreenElementShifted_MM(int x, int y, int dx, int dy, int element,
303                               int cut_mode)
304 {
305   DrawScreenElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING);
306 }
307
308 void DrawScreenElement_MM(int x, int y, int element)
309 {
310   DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
311 }
312
313 void DrawScreenField_MM(int x, int y)
314 {
315   int element = Tile[x][y];
316
317   if (!IN_LEV_FIELD(x, y))
318     return;
319
320   if (IS_MOVING(x, y))
321   {
322     int horiz_move = (MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT);
323
324     DrawScreenElement_MM(x, y, EL_EMPTY);
325
326     if (horiz_move)
327       DrawScreenElementShifted_MM(x, y, MovPos[x][y], 0, element, NO_CUTTING);
328     else
329       DrawScreenElementShifted_MM(x, y, 0, MovPos[x][y], element, NO_CUTTING);
330   }
331   else if (IS_BLOCKED(x, y))
332   {
333     int oldx, oldy;
334     int sx, sy;
335     int horiz_move;
336
337     Blocked2Moving(x, y, &oldx, &oldy);
338
339     sx = SCREENX(oldx);
340     sy = SCREENY(oldy);
341     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
342                   MovDir[oldx][oldy] == MV_RIGHT);
343
344     DrawScreenElement_MM(x, y, EL_EMPTY);
345
346     element = Tile[oldx][oldy];
347
348     if (horiz_move)
349       DrawScreenElementShifted_MM(sx, sy, MovPos[oldx][oldy], 0, element,
350                                   NO_CUTTING);
351     else
352       DrawScreenElementShifted_MM(sx, sy, 0, MovPos[oldx][oldy], element,
353                                   NO_CUTTING);
354   }
355   else if (IS_DRAWABLE(element))
356   {
357     DrawScreenElement_MM(x, y, element);
358   }
359   else
360   {
361     DrawScreenElement_MM(x, y, EL_EMPTY);
362   }
363 }
364
365 void DrawLevelField_MM(int x, int y)
366 {
367   DrawScreenField_MM(x, y);
368 }
369
370 void DrawMiniElement_MM(int x, int y, int element)
371 {
372   int graphic;
373
374   if (!element)
375   {
376     DrawMiniGraphic_MM(x, y, IMG_EMPTY);
377
378     return;
379   }
380
381   graphic = el2gfx(element);
382
383   DrawMiniGraphic_MM(x, y, graphic);
384 }
385
386 void DrawMiniElementOrWall_MM(int sx, int sy, int scroll_x, int scroll_y)
387 {
388   int x = sx + scroll_x, y = sy + scroll_y;
389
390   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
391     DrawMiniElement_MM(sx, sy, EL_EMPTY);
392   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
393     DrawMiniElement_MM(sx, sy, Tile[x][y]);
394 }
395
396 void DrawField_MM(int x, int y)
397 {
398   int element = Tile[x][y];
399
400   DrawElement_MM(x, y, element);
401 }
402
403 void DrawLevel_MM(void)
404 {
405   int x,y;
406
407   ClearWindow();
408
409   for (x = 0; x < lev_fieldx; x++)
410     for (y = 0; y < lev_fieldy; y++)
411       DrawField_MM(x, y);
412
413   redraw_mask |= REDRAW_FIELD;
414 }
415
416 void DrawWallsExt_MM(int x, int y, int element, int draw_mask)
417 {
418   Bitmap *bitmap;
419   int graphic = el2gfx(WALL_BASE(element));
420   int gx, gy;
421   int i;
422
423   getMiniGraphicSource(graphic, &bitmap, &gx, &gy);
424
425   DrawGraphic_MM(x, y, IMG_EMPTY);
426
427   /*
428   if (IS_WALL_WOOD(element) || IS_WALL_AMOEBA(element) ||
429       IS_DF_WALL_WOOD(element))
430     gx += MINI_TILEX;
431   if (IS_WALL_ICE(element) || IS_WALL_AMOEBA(element))
432     gy += MINI_TILEY;
433   */
434
435   for (i = 0; i < 4; i++)
436   {
437     int dest_x = cSX + x * TILEX + MINI_TILEX * (i % 2);
438     int dest_y = cSY + y * TILEY + MINI_TILEY * (i / 2);
439
440     if (!((1 << i) & draw_mask))
441       continue;
442
443     if (element & (1 << i))
444       BlitBitmap(bitmap, drawto_mm, gx, gy, MINI_TILEX, MINI_TILEY,
445                  dest_x, dest_y);
446     else
447       ClearRectangle(drawto_mm, dest_x, dest_y, MINI_TILEX, MINI_TILEY);
448   }
449
450   MarkTileDirty(x, y);
451 }
452
453 void DrawWalls_MM(int x, int y, int element)
454 {
455   DrawWallsExt_MM(x, y, element, HIT_MASK_ALL);
456 }
457
458 void DrawWallsAnimation_MM(int x, int y, int element, int phase, int bit_mask)
459 {
460   int i;
461
462   if (phase == 0)
463   {
464     DrawWalls_MM(x, y, element);
465
466     return;
467   }
468
469   for (i = 0; i < 4; i++)
470   {
471     if (element & (1 << i))
472     {
473       int graphic;
474       int frame;
475       Bitmap *bitmap;
476       int src_x, src_y;
477       int dst_x = cSX + x * TILEX + (i % 2) * MINI_TILEX;
478       int dst_y = cSY + y * TILEY + (i / 2) * MINI_TILEY;
479
480       if (bit_mask & (1 << i))
481       {
482         graphic = (IS_WALL_AMOEBA(element) ?
483                    IMG_MM_AMOEBA_WALL_GROWING :
484                    IMG_MM_ICE_WALL_SHRINKING);
485         frame = phase;
486       }
487       else
488       {
489         graphic = (IS_WALL_AMOEBA(element) ?
490                    IMG_MM_AMOEBA_WALL :
491                    IMG_MM_ICE_WALL);
492         frame = 0;
493       }
494
495       getSizedGraphicSource(graphic, frame, MINI_TILESIZE, &bitmap,
496                             &src_x, &src_y);
497
498       BlitBitmap(bitmap, drawto_mm, src_x, src_y, MINI_TILEX, MINI_TILEY,
499                  dst_x, dst_y);
500     }
501   }
502
503   MarkTileDirty(x, y);
504 }
505
506 void DrawElement_MM(int x, int y, int element)
507 {
508   if (element == EL_EMPTY)
509     DrawGraphic_MM(x, y, IMG_EMPTY);
510   else if (IS_WALL(element))
511     DrawWalls_MM(x, y, element);
512 #if 0
513   else if (IS_WALL_CHANGING(element) && IS_WALL_CHANGING(Tile[x][y]))
514   {
515     int wall_element = Tile[x][y] - EL_WALL_CHANGING + Store[x][y];
516
517     DrawWalls_MM(x, y, wall_element);
518   }
519 #endif
520   else if (element == EL_PACMAN)
521     DrawLevelField_MM(x, y);
522   else if (element == EL_FUSE_ON &&
523            laser.fuse_off &&
524            laser.fuse_x == x &&
525            laser.fuse_y == y)
526     DrawGraphic_MM(x, y, IMG_MM_FUSE);
527   else if (element == EL_GRAY_BALL_ACTIVE)
528     DrawGraphic_MM(x, y, el_act2gfx(EL_GRAY_BALL, MM_ACTION_ACTIVE));
529   else if (element == EL_GRAY_BALL_OPENING)
530     DrawGraphic_MM(x, y, el_act2gfx(EL_GRAY_BALL, MM_ACTION_OPENING));
531   else if (element == EL_BOMB_ACTIVE)
532     DrawGraphic_MM(x, y, el_act2gfx(EL_BOMB, MM_ACTION_ACTIVE));
533   else if (element == EL_MINE_ACTIVE)
534     DrawGraphic_MM(x, y, el_act2gfx(EL_MINE, MM_ACTION_ACTIVE));
535   else
536     DrawGraphic_MM(x, y, el2gfx(element));
537 }
538
539
540 // ----------------------------------------------------------------------------
541 // XSN
542 // ----------------------------------------------------------------------------
543
544 #define XSN_RND(x)              ((x) != 0 ? rand() % (x) : 0)
545 #define XSN_ALPHA_VALUE(x)      (SDL_ALPHA_OPAQUE * (x) / 100)
546
547 #define XSN_MAX_ITEMS           100
548 #define XSN_MAX_HEIGHT          40
549 #define XSN_MAX_DX              2
550 #define XSN_MAX_DY              10
551 #define XSN_CHECK_DELAY         3
552 #define XSN_START_DELAY         60
553 #define XSN_UPDATE_DELAY        50
554 #define XSN_GROWTH_DELAY        3
555 #define XSN_GROWTH_RATE         3
556 #define XSN_CHANGE_DELAY        30
557 #define XSN_CHANGE_FACTOR       3
558 #define XSN_ALPHA_DEFAULT       XSN_ALPHA_VALUE(95)
559 #define XSN_ALPHA_VISIBLE       XSN_ALPHA_VALUE(50)
560 #define XSN_DEBUG_STEPS         5
561
562 static byte xsn_bits_0[] = { 0x05, 0x02, 0x05 };
563 static byte xsn_bits_1[] = { 0x22, 0x6b, 0x14, 0x2a, 0x14, 0x6b, 0x22 };
564 static byte xsn_bits_2[] = { 0x14, 0x08, 0x49, 0x36, 0x49, 0x08, 0x14 };
565
566 char debug_xsn_mode[] = { 76,101,116,32,105,116,32,115,110,111,119,33,0 };
567
568 void setHideSetupEntry(void *);
569 void removeHideSetupEntry(void *);
570
571 static struct
572 {
573   int size;
574   byte *bits;
575   Bitmap *bitmap;
576 }
577 xsn_data[] =
578 {
579   { ARRAY_SIZE(xsn_bits_0), xsn_bits_0 },
580   { ARRAY_SIZE(xsn_bits_1), xsn_bits_1 },
581   { ARRAY_SIZE(xsn_bits_2), xsn_bits_2 },
582   { ARRAY_SIZE(xsn_bits_2), xsn_bits_2 },
583   { ARRAY_SIZE(xsn_bits_1), xsn_bits_1 },
584   { ARRAY_SIZE(xsn_bits_2), xsn_bits_2 },
585   { ARRAY_SIZE(xsn_bits_0), xsn_bits_0 },
586 };
587 static int num_xsn_data = ARRAY_SIZE(xsn_data);
588
589 struct XsnItem
590 {
591   int x;
592   int y;
593   int dx;
594   int dy;
595   int type;
596   int active;
597 };
598
599 struct Xsn
600 {
601   int area_xsize;
602   int area_ysize;
603
604   int num_items;
605   int max_items;
606   int max_height;
607   int max_dx;
608   int max_dy;
609
610   int change_delay;
611   int change_type;
612   int change_dir;
613
614   int *height;
615
616   struct XsnItem items[XSN_MAX_ITEMS];
617
618   Bitmap *bitmap;
619
620   int alpha;
621 };
622
623 static struct Xsn xsn = { 0 };
624
625 static int xsn_percent(void)
626 {
627   int xsn_m0 = -3;
628   int xsn_m1 = xsn_m0 + 10;
629   int xsn_m2 = xsn_m1 + 10;
630   int xsn_m3 = xsn_m2 + 10;
631   time_t xsn_e0 = time(NULL);
632   struct tm *xsn_t0 = localtime(&xsn_e0);
633   struct tm xsn_t1 = { 0,0,0, xsn_m2*3, xsn_m3/3, xsn_t0->tm_year, 0,0,-1 };
634   time_t xsn_e1 = mktime(&xsn_t1);
635   int xsn_c0 = (25 * xsn_m3) << xsn_m1;
636   int xsn_c1 = (xsn_t1.tm_wday - xsn_m1) * !!xsn_t1.tm_wday;
637
638   for (xsn_m0 = 5; xsn_m0 > 0; xsn_m0--)
639   {
640     int xsn_c2 = (xsn_m0 > 4 ? 0 : xsn_c1) - xsn_m1 * xsn_m0;
641     int xsn_off = (xsn_m0 > 4 ? xsn_c0 : 0);
642     time_t xsn_e3 = xsn_e1 - xsn_c2 * xsn_c0;
643
644     if (xsn_e0 > xsn_e3 - xsn_off &&
645         xsn_e0 < xsn_e3 + xsn_off + xsn_c0)
646       return xsn_m0 * (xsn_m3 - xsn_m1);
647   }
648
649   return xsn_m0;
650 }
651
652 static void xsn_init_item(int nr)
653 {
654   struct XsnItem *item = &xsn.items[nr];
655
656   item->type = XSN_RND(num_xsn_data);
657
658   if (xsn.change_type != 0)
659   {
660     int new_x = XSN_RND(xsn.area_xsize / 3);
661
662     item->x = (xsn.change_dir == 1 ? new_x : xsn.area_xsize - new_x);
663     item->y = XSN_RND(xsn.area_ysize);
664   }
665   else
666   {
667     item->x = XSN_RND(xsn.area_xsize - xsn_data[item->type].size);
668     item->y = XSN_RND(xsn.area_ysize / 10);
669   }
670
671   item->dy = XSN_RND(xsn.max_dy + 1) + 1;
672   item->dx = XSN_RND(item->dy / 4 + 1) * (XSN_RND(1000) > 500 ? -1 : 1);
673
674   item->active = 1;
675 }
676
677 static void xsn_update_item(int nr)
678 {
679   struct XsnItem *item = &xsn.items[nr];
680
681   if (!item->active)
682     xsn_init_item(nr);
683
684   if (xsn.change_type != 0)
685   {
686     int dx_new = ABS(item->dx) +
687       (xsn.change_type == 1 ?
688        XSN_RND(XSN_CHANGE_FACTOR + 1) - XSN_CHANGE_FACTOR / 2 :
689        XSN_RND(20));
690
691     item->dx = MIN(MAX(-50, dx_new * xsn.change_dir), 50);
692   }
693
694   int new_x = item->x + item->dx;
695   int new_y = item->y + item->dy;
696
697   item->active = (new_y < xsn.area_ysize);
698
699   if (xsn.change_type != 0)
700     item->active = (item->active && new_x > 0 && new_x < xsn.area_xsize);
701
702   int item_size = xsn_data[item->type].size;
703   int half_item_size = item_size / 2;
704   int mid_x = new_x + half_item_size;
705   int mid_y = new_y + half_item_size;
706   int upper_border = xsn.area_ysize - xsn.max_height;
707
708   if (item->active &&
709       new_y >= upper_border &&
710       new_x >= 0 &&
711       new_x <= xsn.area_xsize - item_size &&
712       mid_y >= xsn.height[mid_x] &&
713       mid_y < xsn.area_ysize)
714   {
715     Bitmap *item_bitmap = xsn_data[item->type].bitmap;
716     SDL_Surface *surface = xsn.bitmap->surface;
717     SDL_Surface *surface_masked = xsn.bitmap->surface_masked;
718     int item_alpha = XSN_ALPHA_VALUE(81 + XSN_RND(20));
719     int shrink = 1;
720     int i;
721
722     xsn.bitmap->surface = surface_masked;
723
724     SDLSetAlpha(item_bitmap->surface_masked, TRUE, item_alpha);
725
726     // blit to masked surface instead of opaque surface
727     BlitBitmapMasked(item_bitmap, xsn.bitmap, 0, 0, item_size, item_size,
728                      new_x, new_y - upper_border);
729
730     SDLSetAlpha(item_bitmap->surface_masked, TRUE, XSN_ALPHA_DEFAULT);
731
732     for (i = -half_item_size; i <= half_item_size; i++)
733     {
734       int xpos = mid_x + i;
735
736       if (xpos >= 0 && xpos < xsn.area_xsize)
737         xsn.height[xpos] = MIN(new_y + ABS(i), xsn.height[xpos]);
738     }
739
740     if (xsn.height[mid_x] <= upper_border + shrink)
741     {
742       int xpos1 = MAX(0, new_x - half_item_size);
743       int xpos2 = MIN(new_x + 3 * half_item_size, xsn.area_xsize);
744       int xsize = xpos2 - xpos1;
745       int ysize1 = XSN_RND(xsn.max_height - shrink);
746       int ysize2 = xsn.max_height - ysize1;
747
748       SDLSetAlpha(surface_masked, FALSE, 0);
749
750       FillRectangle(xsn.bitmap, xpos1, xsn.max_height, xsize, xsn.max_height,
751                     BLACK_PIXEL);
752       BlitBitmapMasked(xsn.bitmap, xsn.bitmap, xpos1, 0, xsize, ysize1,
753                        xpos1, xsn.max_height + shrink);
754       BlitBitmapMasked(xsn.bitmap, xsn.bitmap, xpos1, ysize1, xsize, ysize2,
755                        xpos1, xsn.max_height + ysize1);
756       FillRectangle(xsn.bitmap, xpos1, 0, xsize, xsn.max_height,
757                     BLACK_PIXEL);
758       BlitBitmapMasked(xsn.bitmap, xsn.bitmap, xpos1, xsn.max_height,
759                        xsize, xsn.max_height, xpos1, 0);
760
761       SDLSetAlpha(surface_masked, TRUE, xsn.alpha);
762
763       for (i = xpos1; i < xpos2; i++)
764         xsn.height[i] = MIN(xsn.height[i] + shrink, xsn.area_ysize - 1);
765     }
766
767     SDLFreeBitmapTextures(xsn.bitmap);
768     SDLCreateBitmapTextures(xsn.bitmap);
769
770     xsn.bitmap->surface = surface;
771
772     item->active = 0;
773   }
774
775   item->dx += XSN_RND(XSN_CHANGE_FACTOR) * (XSN_RND(1000) > 500 ? -1 : 1);
776
777   if (xsn.change_type == 0)
778     item->dx = MIN(MAX(-xsn.max_dx, item->dx), xsn.max_dx);
779
780   item->x = new_x;
781   item->y = new_y;
782 }
783
784 static void xsn_update_change(void)
785 {
786   if (XSN_RND(100) > 65)
787   {
788     xsn.change_dir = (XSN_RND(10) > 4 ? 1 : -1);
789     xsn.change_delay = XSN_RND(5) + 1;
790     xsn.change_type = 2;
791   }
792   else if (xsn.change_type == 2)
793   {
794     xsn.change_delay = XSN_RND(3) + 1;
795     xsn.change_type = 1;
796   }
797   else
798   {
799     xsn.change_delay = XSN_CHANGE_DELAY;
800     xsn.change_type = 0;
801   }
802 }
803
804 static void DrawTileCursor_Xsn(int draw_target)
805 {
806   static boolean initialized = FALSE;
807   static boolean started = FALSE;
808   static boolean active = FALSE;
809   static boolean debug = FALSE;
810   static DelayCounter check_delay = { XSN_CHECK_DELAY * 1000 };
811   static DelayCounter start_delay = { 0 };
812   static DelayCounter growth_delay = { 0 };
813   static DelayCounter update_delay = { 0 };
814   static DelayCounter change_delay = { 0 };
815   static int percent = 0;
816   static int debug_value = 0;
817   boolean reinitialize = FALSE;
818   boolean active_last = active;
819   int i, x, y;
820
821   if (draw_target != DRAW_TO_SCREEN)
822     return;
823
824   if (DelayReached(&check_delay))
825   {
826     percent = (debug ? debug_value * 100 / XSN_DEBUG_STEPS : xsn_percent());
827
828     if (debug)
829       setup.debug.xsn_percent = percent;
830
831     if (setup.debug.xsn_mode != AUTO)
832       percent = setup.debug.xsn_percent;
833
834     setup.debug.xsn_percent = percent;
835
836     active = (percent > 0);
837
838     if ((active && !active_last) || setup.debug.xsn_mode != AUTO)
839       removeHideSetupEntry(&setup.debug.xsn_mode);
840     else if (!active && active_last)
841       setHideSetupEntry(&setup.debug.xsn_mode);
842
843     if (setup.debug.xsn_mode == FALSE)
844       active = FALSE;
845   }
846   else if (tile_cursor.xsn_debug)
847   {
848     debug_value = (active ? 0 : MIN(debug_value + 1, XSN_DEBUG_STEPS));
849     debug = TRUE;
850     active = FALSE;
851
852     ResetDelayCounter(&check_delay);
853
854     setup.debug.xsn_mode = (debug_value > 0);
855     tile_cursor.xsn_debug = FALSE;
856   }
857
858   if (!active)
859     return;
860
861   if (!initialized)
862   {
863     xsn.area_xsize = gfx.win_xsize;
864     xsn.area_ysize = gfx.win_ysize;
865
866     for (i = 0; i < num_xsn_data; i++)
867     {
868       int size = xsn_data[i].size;
869       byte *bits = xsn_data[i].bits;
870       Bitmap *bitmap = CreateBitmap(size, size, DEFAULT_DEPTH);
871
872       FillRectangle(bitmap, 0, 0, size, size, BLACK_PIXEL);
873
874       for (y = 0; y < size; y++)
875         for (x = 0; x < size; x++)
876           if ((bits[y] >> x) & 0x01)
877             SDLPutPixel(bitmap, x, y, WHITE_PIXEL);
878
879       SDL_Surface *surface = bitmap->surface;
880
881       if ((bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
882         Fail("SDLGetNativeSurface() failed");
883
884       SDL_Surface *surface_masked = bitmap->surface_masked;
885
886       SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
887                       SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
888
889       SDLSetAlpha(surface, TRUE, XSN_ALPHA_DEFAULT);
890       SDLSetAlpha(surface_masked, TRUE, XSN_ALPHA_DEFAULT);
891
892       xsn_data[i].bitmap = bitmap;
893     }
894
895     srand((unsigned int)time(NULL));
896
897     initialized = TRUE;
898   }
899
900   if (!active_last)
901   {
902     start_delay.value = (debug || setup.debug.xsn_mode == TRUE ? 0 :
903                          (XSN_START_DELAY + XSN_RND(XSN_START_DELAY)) * 1000);
904     started = FALSE;
905
906     ResetDelayCounter(&start_delay);
907
908     reinitialize = TRUE;
909   }
910
911   if (reinitialize)
912   {
913     xsn.num_items  = 0;
914     xsn.max_items  = percent * XSN_MAX_ITEMS / 100;
915     xsn.max_height = percent * XSN_MAX_HEIGHT / 100;
916
917     xsn.max_dx = XSN_MAX_DX;
918     xsn.max_dy = XSN_MAX_DY;
919
920     xsn.change_delay = XSN_CHANGE_DELAY;
921     xsn.change_type  = 0;
922     xsn.change_dir   = 0;
923
924     xsn.alpha = XSN_ALPHA_DEFAULT;
925
926     for (i = 0; i < xsn.max_items; i++)
927       xsn_init_item(i);
928   }
929
930   if (xsn.area_xsize != gfx.win_xsize ||
931       xsn.area_ysize != gfx.win_ysize ||
932       reinitialize)
933   {
934     xsn.area_xsize = gfx.win_xsize;
935     xsn.area_ysize = gfx.win_ysize;
936
937     if (xsn.bitmap != NULL)
938       FreeBitmap(xsn.bitmap);
939
940     xsn.bitmap = CreateBitmap(xsn.area_xsize, xsn.max_height * 2,
941                               DEFAULT_DEPTH);
942
943     FillRectangle(xsn.bitmap, 0, 0, xsn.area_xsize, xsn.max_height,
944                   BLACK_PIXEL);
945
946     SDL_Surface *surface = xsn.bitmap->surface;
947
948     if ((xsn.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
949       Fail("SDLGetNativeSurface() failed");
950
951     SDL_Surface *surface_masked = xsn.bitmap->surface_masked;
952
953     SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
954                     SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
955
956     SDLSetAlpha(surface, TRUE, xsn.alpha);
957     SDLSetAlpha(surface_masked, TRUE, xsn.alpha);
958
959     SDLCreateBitmapTextures(xsn.bitmap);
960
961     for (i = 0; i < num_xsn_data; i++)
962     {
963       SDLFreeBitmapTextures(xsn_data[i].bitmap);
964       SDLCreateBitmapTextures(xsn_data[i].bitmap);
965     }
966
967     if (xsn.height != NULL)
968       checked_free(xsn.height);
969
970     xsn.height = checked_calloc(xsn.area_xsize * sizeof(int));
971
972     for (i = 0; i < xsn.area_xsize; i++)
973       xsn.height[i] = xsn.area_ysize - 1;
974   }
975
976   if (!started)
977   {
978     if (!DelayReached(&start_delay))
979       return;
980
981     update_delay.value = XSN_UPDATE_DELAY;
982     growth_delay.value = XSN_GROWTH_DELAY * 1000;
983     change_delay.value = XSN_CHANGE_DELAY * 1000;
984
985     ResetDelayCounter(&growth_delay);
986     ResetDelayCounter(&update_delay);
987     ResetDelayCounter(&change_delay);
988
989     started = TRUE;
990   }
991
992   if (xsn.num_items < xsn.max_items)
993   {
994     if (DelayReached(&growth_delay))
995     {
996       xsn.num_items += XSN_RND(XSN_GROWTH_RATE * 2);
997       xsn.num_items = MIN(xsn.num_items, xsn.max_items);
998     }
999   }
1000
1001   if (DelayReached(&update_delay))
1002   {
1003     for (i = 0; i < xsn.num_items; i++)
1004       xsn_update_item(i);
1005   }
1006
1007   if (DelayReached(&change_delay))
1008   {
1009     xsn_update_change();
1010
1011     change_delay.value = xsn.change_delay * 1000;
1012   }
1013
1014   int xsn_alpha_dx = (gfx.mouse_y > xsn.area_ysize - xsn.max_height ?
1015                       (xsn.alpha > XSN_ALPHA_VISIBLE ? -1 : 0) :
1016                       (xsn.alpha < XSN_ALPHA_DEFAULT ? +1 : 0));
1017
1018   if (xsn_alpha_dx != 0)
1019   {
1020     xsn.alpha += xsn_alpha_dx;
1021
1022     SDLSetAlpha(xsn.bitmap->surface_masked, TRUE, xsn.alpha);
1023
1024     SDLFreeBitmapTextures(xsn.bitmap);
1025     SDLCreateBitmapTextures(xsn.bitmap);
1026   }
1027
1028   BlitToScreenMasked(xsn.bitmap, 0, 0, xsn.area_xsize, xsn.max_height,
1029                      0, xsn.area_ysize - xsn.max_height);
1030
1031   for (i = 0; i < xsn.num_items; i++)
1032   {
1033     int dst_x = xsn.items[i].x;
1034     int dst_y = xsn.items[i].y;
1035     int type = xsn.items[i].type;
1036     int size = xsn_data[type].size;
1037     Bitmap *bitmap = xsn_data[type].bitmap;
1038
1039     BlitToScreenMasked(bitmap, 0, 0, size, size, dst_x, dst_y);
1040   }
1041 }
1042
1043 void DrawTileCursor_MM(int draw_target, boolean tile_cursor_active)
1044 {
1045   if (program.headless)
1046     return;
1047
1048   Bitmap *fade_bitmap;
1049   Bitmap *src_bitmap;
1050   int src_x, src_y;
1051   int dst_x, dst_y;
1052   int graphic = IMG_GLOBAL_TILE_CURSOR;
1053   int frame = 0;
1054   int tilesize = TILESIZE_VAR;
1055   int width = tilesize;
1056   int height = tilesize;
1057
1058   DrawTileCursor_Xsn(draw_target);
1059
1060   if (!tile_cursor.enabled ||
1061       !tile_cursor.active ||
1062       !tile_cursor_active)
1063     return;
1064
1065   if (tile_cursor.moving)
1066   {
1067     int step = TILESIZE_VAR / 4;
1068     int dx = tile_cursor.target_x - tile_cursor.x;
1069     int dy = tile_cursor.target_y - tile_cursor.y;
1070
1071     if (ABS(dx) < step)
1072       tile_cursor.x = tile_cursor.target_x;
1073     else
1074       tile_cursor.x += SIGN(dx) * step;
1075
1076     if (ABS(dy) < step)
1077       tile_cursor.y = tile_cursor.target_y;
1078     else
1079       tile_cursor.y += SIGN(dy) * step;
1080
1081     if (tile_cursor.x == tile_cursor.target_x &&
1082         tile_cursor.y == tile_cursor.target_y)
1083       tile_cursor.moving = FALSE;
1084   }
1085
1086   dst_x = tile_cursor.x;
1087   dst_y = tile_cursor.y;
1088
1089   frame = getGraphicAnimationFrame(graphic, -1);
1090
1091   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1092
1093   fade_bitmap =
1094     (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
1095      draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
1096
1097   if (draw_target == DRAW_TO_SCREEN)
1098     BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
1099   else
1100     BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
1101                      dst_x, dst_y);
1102 }
1103
1104 Pixel ReadPixel(DrawBuffer *bitmap, int x, int y)
1105 {
1106   return GetPixel(bitmap, x, y);
1107 }
1108
1109 int get_base_element(int element)
1110 {
1111   if (IS_MIRROR(element))
1112     return EL_MIRROR_START;
1113   else if (IS_MIRROR_FIXED(element))
1114     return EL_MIRROR_FIXED_START;
1115   else if (IS_POLAR(element))
1116     return EL_POLAR_START;
1117   else if (IS_POLAR_CROSS(element))
1118     return EL_POLAR_CROSS_START;
1119   else if (IS_BEAMER(element))
1120     return EL_BEAMER_RED_START + BEAMER_NR(element) * 16;
1121   else if (IS_FIBRE_OPTIC(element))
1122     return EL_FIBRE_OPTIC_START + FIBRE_OPTIC_NR(element) * 2;
1123   else if (IS_MCDUFFIN(element))
1124     return EL_MCDUFFIN_START;
1125   else if (IS_LASER(element))
1126     return EL_LASER_START;
1127   else if (IS_RECEIVER(element))
1128     return EL_RECEIVER_START;
1129   else if (IS_DF_MIRROR(element))
1130     return EL_DF_MIRROR_START;
1131   else if (IS_DF_MIRROR_AUTO(element))
1132     return EL_DF_MIRROR_AUTO_START;
1133   else if (IS_PACMAN(element))
1134     return EL_PACMAN_START;
1135   else if (IS_GRID_STEEL(element))
1136     return EL_GRID_STEEL_START;
1137   else if (IS_GRID_WOOD(element))
1138     return EL_GRID_WOOD_START;
1139   else if (IS_GRID_STEEL_FIXED(element))
1140     return EL_GRID_STEEL_FIXED_START;
1141   else if (IS_GRID_WOOD_FIXED(element))
1142     return EL_GRID_WOOD_FIXED_START;
1143   else if (IS_GRID_STEEL_AUTO(element))
1144     return EL_GRID_STEEL_AUTO_START;
1145   else if (IS_GRID_WOOD_AUTO(element))
1146     return EL_GRID_WOOD_AUTO_START;
1147   else if (IS_WALL_STEEL(element))
1148     return EL_WALL_STEEL_START;
1149   else if (IS_WALL_WOOD(element))
1150     return EL_WALL_WOOD_START;
1151   else if (IS_WALL_ICE(element))
1152     return EL_WALL_ICE_START;
1153   else if (IS_WALL_AMOEBA(element))
1154     return EL_WALL_AMOEBA_START;
1155   else if (IS_DF_WALL_STEEL(element))
1156     return EL_DF_WALL_STEEL_START;
1157   else if (IS_DF_WALL_WOOD(element))
1158     return EL_DF_WALL_WOOD_START;
1159   else if (IS_CHAR(element))
1160     return EL_CHAR_START;
1161   else
1162     return element;
1163 }
1164
1165 int get_element_phase(int element)
1166 {
1167   return element - get_base_element(element);
1168 }
1169
1170 int get_num_elements(int element)
1171 {
1172   if (IS_MIRROR(element) ||
1173       IS_POLAR(element) ||
1174       IS_BEAMER(element) ||
1175       IS_DF_MIRROR(element) ||
1176       IS_DF_MIRROR_AUTO(element))
1177     return 16;
1178   else if (IS_GRID_STEEL_FIXED(element) ||
1179            IS_GRID_WOOD_FIXED(element) ||
1180            IS_GRID_STEEL_AUTO(element) ||
1181            IS_GRID_WOOD_AUTO(element))
1182     return 8;
1183   else if (IS_MIRROR_FIXED(element) ||
1184            IS_POLAR_CROSS(element) ||
1185            IS_MCDUFFIN(element) ||
1186            IS_LASER(element) ||
1187            IS_RECEIVER(element) ||
1188            IS_PACMAN(element) ||
1189            IS_GRID_STEEL(element) ||
1190            IS_GRID_WOOD(element))
1191     return 4;
1192   else
1193     return 1;
1194 }
1195
1196 int get_rotated_element(int element, int step)
1197 {
1198   int base_element = get_base_element(element);
1199   int num_elements = get_num_elements(element);
1200   int element_phase = element - base_element;
1201
1202   return base_element + (element_phase + step + num_elements) % num_elements;
1203 }
1204
1205 int map_wall_from_base_element(int element)
1206 {
1207   switch (element)
1208   {
1209     case EL_WALL_STEEL_BASE:    return EL_WALL_STEEL;
1210     case EL_WALL_WOOD_BASE:     return EL_WALL_WOOD;
1211     case EL_WALL_ICE_BASE:      return EL_WALL_ICE;
1212     case EL_WALL_AMOEBA_BASE:   return EL_WALL_AMOEBA;
1213     case EL_DF_WALL_STEEL_BASE: return EL_DF_WALL_STEEL;
1214     case EL_DF_WALL_WOOD_BASE:  return EL_DF_WALL_WOOD;
1215
1216     default:                    return element;
1217   }
1218 }
1219
1220 int map_wall_to_base_element(int element)
1221 {
1222   switch (element)
1223   {
1224     case EL_WALL_STEEL:         return EL_WALL_STEEL_BASE;
1225     case EL_WALL_WOOD:          return EL_WALL_WOOD_BASE;
1226     case EL_WALL_ICE:           return EL_WALL_ICE_BASE;
1227     case EL_WALL_AMOEBA:        return EL_WALL_AMOEBA_BASE;
1228     case EL_DF_WALL_STEEL:      return EL_DF_WALL_STEEL_BASE;
1229     case EL_DF_WALL_WOOD:       return EL_DF_WALL_WOOD_BASE;
1230
1231     default:                    return element;
1232   }
1233 }
1234
1235 int el2gfx(int element)
1236 {
1237   return el2img_mm(map_wall_from_base_element(element));
1238 }
1239
1240 int el_act2gfx(int element, int action)
1241 {
1242   return el_act2img_mm(map_wall_from_base_element(element), action);
1243 }
1244
1245 void RedrawPlayfield_MM(void)
1246 {
1247   DrawLevel_MM();
1248   DrawLaser_MM();
1249 }
1250
1251 void BlitScreenToBitmap_MM(Bitmap *target_bitmap)
1252 {
1253   BlitBitmap(drawto_mm, target_bitmap,
1254              REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
1255 }