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