47fa48ad60b63225d2e8579b86fd7c7c627d07f0
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Tile[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Tile[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Tile[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Tile[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       Debug("game:init:player", "- player element %d activated",
1773             player->element_nr);
1774       Debug("game:init:player", "  (local player is %d and currently %s)",
1775             local_player->element_nr,
1776             local_player->active ? "active" : "not active");
1777     }
1778 #endif
1779
1780     Tile[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   // always check if player was just killed and should be reanimated
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Tile[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Tile[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1828         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878     case EL_SPRING_LEFT:
1879     case EL_SPRING_RIGHT:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Tile[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Tile[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Tile[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Tile[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Tile[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Tile[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638 #if 0
2639         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2640               element, EL_NAME(element), size);
2641 #endif
2642
2643         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2644           size = TILESIZE;
2645
2646         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2647                               &src_x, &src_y);
2648
2649         width  = graphic_info[graphic].width  * size / TILESIZE;
2650         height = graphic_info[graphic].height * size / TILESIZE;
2651
2652         if (draw_masked)
2653           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2654                            dst_x, dst_y);
2655         else
2656           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2657                      dst_x, dst_y);
2658       }
2659     }
2660     else if (type == TYPE_GRAPHIC)
2661     {
2662       int graphic        = gpc->graphic;
2663       int graphic_active = gpc->graphic_active;
2664       Bitmap *src_bitmap;
2665       int src_x, src_y;
2666       int width, height;
2667       int dst_x = PANEL_XPOS(pos);
2668       int dst_y = PANEL_YPOS(pos);
2669       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2670                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2671
2672       if (graphic != IMG_UNDEFINED && !skip)
2673       {
2674         if (pos->style == STYLE_REVERSE)
2675           value = 100 - value;
2676
2677         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2678
2679         if (pos->direction & MV_HORIZONTAL)
2680         {
2681           width  = graphic_info[graphic_active].width * value / 100;
2682           height = graphic_info[graphic_active].height;
2683
2684           if (pos->direction == MV_LEFT)
2685           {
2686             src_x += graphic_info[graphic_active].width - width;
2687             dst_x += graphic_info[graphic_active].width - width;
2688           }
2689         }
2690         else
2691         {
2692           width  = graphic_info[graphic_active].width;
2693           height = graphic_info[graphic_active].height * value / 100;
2694
2695           if (pos->direction == MV_UP)
2696           {
2697             src_y += graphic_info[graphic_active].height - height;
2698             dst_y += graphic_info[graphic_active].height - height;
2699           }
2700         }
2701
2702         if (draw_masked)
2703           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2704                            dst_x, dst_y);
2705         else
2706           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2707                      dst_x, dst_y);
2708
2709         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2710
2711         if (pos->direction & MV_HORIZONTAL)
2712         {
2713           if (pos->direction == MV_RIGHT)
2714           {
2715             src_x += width;
2716             dst_x += width;
2717           }
2718           else
2719           {
2720             dst_x = PANEL_XPOS(pos);
2721           }
2722
2723           width = graphic_info[graphic].width - width;
2724         }
2725         else
2726         {
2727           if (pos->direction == MV_DOWN)
2728           {
2729             src_y += height;
2730             dst_y += height;
2731           }
2732           else
2733           {
2734             dst_y = PANEL_YPOS(pos);
2735           }
2736
2737           height = graphic_info[graphic].height - height;
2738         }
2739
2740         if (draw_masked)
2741           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2742                            dst_x, dst_y);
2743         else
2744           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2745                      dst_x, dst_y);
2746       }
2747     }
2748     else if (type == TYPE_STRING)
2749     {
2750       boolean active = (value != 0);
2751       char *state_normal = "off";
2752       char *state_active = "on";
2753       char *state = (active ? state_active : state_normal);
2754       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2755                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2756                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2757                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2758
2759       if (nr == GAME_PANEL_GRAVITY_STATE)
2760       {
2761         int font1 = pos->font;          // (used for normal state)
2762         int font2 = pos->font_alt;      // (used for active state)
2763
2764         font = (active ? font2 : font1);
2765       }
2766
2767       if (s != NULL)
2768       {
2769         char *s_cut;
2770
2771         if (size <= 0)
2772         {
2773           // don't truncate output if "chars" is zero or less
2774           size = strlen(s);
2775
2776           // dynamically correct text alignment
2777           pos->width = size * getFontWidth(font);
2778         }
2779
2780         s_cut = getStringCopyN(s, size);
2781
2782         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2783                     s_cut, font, mask_mode);
2784
2785         free(s_cut);
2786       }
2787     }
2788
2789     redraw_mask |= REDRAW_DOOR_1;
2790   }
2791
2792   SetGameStatus(GAME_MODE_PLAYING);
2793 }
2794
2795 void UpdateAndDisplayGameControlValues(void)
2796 {
2797   if (tape.deactivate_display)
2798     return;
2799
2800   UpdateGameControlValues();
2801   DisplayGameControlValues();
2802 }
2803
2804 #if 0
2805 static void UpdateGameDoorValues(void)
2806 {
2807   UpdateGameControlValues();
2808 }
2809 #endif
2810
2811 void DrawGameDoorValues(void)
2812 {
2813   DisplayGameControlValues();
2814 }
2815
2816
2817 // ============================================================================
2818 // InitGameEngine()
2819 // ----------------------------------------------------------------------------
2820 // initialize game engine due to level / tape version number
2821 // ============================================================================
2822
2823 static void InitGameEngine(void)
2824 {
2825   int i, j, k, l, x, y;
2826
2827   // set game engine from tape file when re-playing, else from level file
2828   game.engine_version = (tape.playing ? tape.engine_version :
2829                          level.game_version);
2830
2831   // set single or multi-player game mode (needed for re-playing tapes)
2832   game.team_mode = setup.team_mode;
2833
2834   if (tape.playing)
2835   {
2836     int num_players = 0;
2837
2838     for (i = 0; i < MAX_PLAYERS; i++)
2839       if (tape.player_participates[i])
2840         num_players++;
2841
2842     // multi-player tapes contain input data for more than one player
2843     game.team_mode = (num_players > 1);
2844   }
2845
2846 #if 0
2847   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2848         level.game_version);
2849   Debug("game:init:level", "          tape.file_version   == %06d",
2850         tape.file_version);
2851   Debug("game:init:level", "          tape.game_version   == %06d",
2852         tape.game_version);
2853   Debug("game:init:level", "          tape.engine_version == %06d",
2854         tape.engine_version);
2855   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2856         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2857 #endif
2858
2859   // --------------------------------------------------------------------------
2860   // set flags for bugs and changes according to active game engine version
2861   // --------------------------------------------------------------------------
2862
2863   /*
2864     Summary of bugfix:
2865     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2866
2867     Bug was introduced in version:
2868     2.0.1
2869
2870     Bug was fixed in version:
2871     4.2.0.0
2872
2873     Description:
2874     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2875     but the property "can fall" was missing, which caused some levels to be
2876     unsolvable. This was fixed in version 4.2.0.0.
2877
2878     Affected levels/tapes:
2879     An example for a tape that was fixed by this bugfix is tape 029 from the
2880     level set "rnd_sam_bateman".
2881     The wrong behaviour will still be used for all levels or tapes that were
2882     created/recorded with it. An example for this is tape 023 from the level
2883     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2884   */
2885
2886   boolean use_amoeba_dropping_cannot_fall_bug =
2887     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2888       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2889      (tape.playing &&
2890       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2891       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2892
2893   /*
2894     Summary of bugfix/change:
2895     Fixed move speed of elements entering or leaving magic wall.
2896
2897     Fixed/changed in version:
2898     2.0.1
2899
2900     Description:
2901     Before 2.0.1, move speed of elements entering or leaving magic wall was
2902     twice as fast as it is now.
2903     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2904
2905     Affected levels/tapes:
2906     The first condition is generally needed for all levels/tapes before version
2907     2.0.1, which might use the old behaviour before it was changed; known tapes
2908     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2909     The second condition is an exception from the above case and is needed for
2910     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2911     above, but before it was known that this change would break tapes like the
2912     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2913     although the engine version while recording maybe was before 2.0.1. There
2914     are a lot of tapes that are affected by this exception, like tape 006 from
2915     the level set "rnd_conor_mancone".
2916   */
2917
2918   boolean use_old_move_stepsize_for_magic_wall =
2919     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2920      !(tape.playing &&
2921        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2922        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2923
2924   /*
2925     Summary of bugfix/change:
2926     Fixed handling for custom elements that change when pushed by the player.
2927
2928     Fixed/changed in version:
2929     3.1.0
2930
2931     Description:
2932     Before 3.1.0, custom elements that "change when pushing" changed directly
2933     after the player started pushing them (until then handled in "DigField()").
2934     Since 3.1.0, these custom elements are not changed until the "pushing"
2935     move of the element is finished (now handled in "ContinueMoving()").
2936
2937     Affected levels/tapes:
2938     The first condition is generally needed for all levels/tapes before version
2939     3.1.0, which might use the old behaviour before it was changed; known tapes
2940     that are affected are some tapes from the level set "Walpurgis Gardens" by
2941     Jamie Cullen.
2942     The second condition is an exception from the above case and is needed for
2943     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2944     above (including some development versions of 3.1.0), but before it was
2945     known that this change would break tapes like the above and was fixed in
2946     3.1.1, so that the changed behaviour was active although the engine version
2947     while recording maybe was before 3.1.0. There is at least one tape that is
2948     affected by this exception, which is the tape for the one-level set "Bug
2949     Machine" by Juergen Bonhagen.
2950   */
2951
2952   game.use_change_when_pushing_bug =
2953     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2954      !(tape.playing &&
2955        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2956        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2957
2958   /*
2959     Summary of bugfix/change:
2960     Fixed handling for blocking the field the player leaves when moving.
2961
2962     Fixed/changed in version:
2963     3.1.1
2964
2965     Description:
2966     Before 3.1.1, when "block last field when moving" was enabled, the field
2967     the player is leaving when moving was blocked for the time of the move,
2968     and was directly unblocked afterwards. This resulted in the last field
2969     being blocked for exactly one less than the number of frames of one player
2970     move. Additionally, even when blocking was disabled, the last field was
2971     blocked for exactly one frame.
2972     Since 3.1.1, due to changes in player movement handling, the last field
2973     is not blocked at all when blocking is disabled. When blocking is enabled,
2974     the last field is blocked for exactly the number of frames of one player
2975     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2976     last field is blocked for exactly one more than the number of frames of
2977     one player move.
2978
2979     Affected levels/tapes:
2980     (!!! yet to be determined -- probably many !!!)
2981   */
2982
2983   game.use_block_last_field_bug =
2984     (game.engine_version < VERSION_IDENT(3,1,1,0));
2985
2986   /* various special flags and settings for native Emerald Mine game engine */
2987
2988   game_em.use_single_button =
2989     (game.engine_version > VERSION_IDENT(4,0,0,2));
2990
2991   game_em.use_snap_key_bug =
2992     (game.engine_version < VERSION_IDENT(4,0,1,0));
2993
2994   game_em.use_random_bug =
2995     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
2996
2997   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
2998
2999   game_em.use_old_explosions            = use_old_em_engine;
3000   game_em.use_old_android               = use_old_em_engine;
3001   game_em.use_old_push_elements         = use_old_em_engine;
3002   game_em.use_old_push_into_acid        = use_old_em_engine;
3003
3004   game_em.use_wrap_around               = !use_old_em_engine;
3005
3006   // --------------------------------------------------------------------------
3007
3008   // set maximal allowed number of custom element changes per game frame
3009   game.max_num_changes_per_frame = 1;
3010
3011   // default scan direction: scan playfield from top/left to bottom/right
3012   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3013
3014   // dynamically adjust element properties according to game engine version
3015   InitElementPropertiesEngine(game.engine_version);
3016
3017   // ---------- initialize special element properties -------------------------
3018
3019   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3020   if (use_amoeba_dropping_cannot_fall_bug)
3021     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3022
3023   // ---------- initialize player's initial move delay ------------------------
3024
3025   // dynamically adjust player properties according to level information
3026   for (i = 0; i < MAX_PLAYERS; i++)
3027     game.initial_move_delay_value[i] =
3028       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3029
3030   // dynamically adjust player properties according to game engine version
3031   for (i = 0; i < MAX_PLAYERS; i++)
3032     game.initial_move_delay[i] =
3033       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3034        game.initial_move_delay_value[i] : 0);
3035
3036   // ---------- initialize player's initial push delay ------------------------
3037
3038   // dynamically adjust player properties according to game engine version
3039   game.initial_push_delay_value =
3040     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3041
3042   // ---------- initialize changing elements ----------------------------------
3043
3044   // initialize changing elements information
3045   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3046   {
3047     struct ElementInfo *ei = &element_info[i];
3048
3049     // this pointer might have been changed in the level editor
3050     ei->change = &ei->change_page[0];
3051
3052     if (!IS_CUSTOM_ELEMENT(i))
3053     {
3054       ei->change->target_element = EL_EMPTY_SPACE;
3055       ei->change->delay_fixed = 0;
3056       ei->change->delay_random = 0;
3057       ei->change->delay_frames = 1;
3058     }
3059
3060     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3061     {
3062       ei->has_change_event[j] = FALSE;
3063
3064       ei->event_page_nr[j] = 0;
3065       ei->event_page[j] = &ei->change_page[0];
3066     }
3067   }
3068
3069   // add changing elements from pre-defined list
3070   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3071   {
3072     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3073     struct ElementInfo *ei = &element_info[ch_delay->element];
3074
3075     ei->change->target_element       = ch_delay->target_element;
3076     ei->change->delay_fixed          = ch_delay->change_delay;
3077
3078     ei->change->pre_change_function  = ch_delay->pre_change_function;
3079     ei->change->change_function      = ch_delay->change_function;
3080     ei->change->post_change_function = ch_delay->post_change_function;
3081
3082     ei->change->can_change = TRUE;
3083     ei->change->can_change_or_has_action = TRUE;
3084
3085     ei->has_change_event[CE_DELAY] = TRUE;
3086
3087     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3088     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3089   }
3090
3091   // ---------- initialize internal run-time variables ------------------------
3092
3093   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3094   {
3095     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3096
3097     for (j = 0; j < ei->num_change_pages; j++)
3098     {
3099       ei->change_page[j].can_change_or_has_action =
3100         (ei->change_page[j].can_change |
3101          ei->change_page[j].has_action);
3102     }
3103   }
3104
3105   // add change events from custom element configuration
3106   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3107   {
3108     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3109
3110     for (j = 0; j < ei->num_change_pages; j++)
3111     {
3112       if (!ei->change_page[j].can_change_or_has_action)
3113         continue;
3114
3115       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3116       {
3117         // only add event page for the first page found with this event
3118         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3119         {
3120           ei->has_change_event[k] = TRUE;
3121
3122           ei->event_page_nr[k] = j;
3123           ei->event_page[k] = &ei->change_page[j];
3124         }
3125       }
3126     }
3127   }
3128
3129   // ---------- initialize reference elements in change conditions ------------
3130
3131   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3132   {
3133     int element = EL_CUSTOM_START + i;
3134     struct ElementInfo *ei = &element_info[element];
3135
3136     for (j = 0; j < ei->num_change_pages; j++)
3137     {
3138       int trigger_element = ei->change_page[j].initial_trigger_element;
3139
3140       if (trigger_element >= EL_PREV_CE_8 &&
3141           trigger_element <= EL_NEXT_CE_8)
3142         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3143
3144       ei->change_page[j].trigger_element = trigger_element;
3145     }
3146   }
3147
3148   // ---------- initialize run-time trigger player and element ----------------
3149
3150   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3151   {
3152     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3153
3154     for (j = 0; j < ei->num_change_pages; j++)
3155     {
3156       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3157       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3158       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3159       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3160       ei->change_page[j].actual_trigger_ce_value = 0;
3161       ei->change_page[j].actual_trigger_ce_score = 0;
3162     }
3163   }
3164
3165   // ---------- initialize trigger events -------------------------------------
3166
3167   // initialize trigger events information
3168   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3169     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3170       trigger_events[i][j] = FALSE;
3171
3172   // add trigger events from element change event properties
3173   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3174   {
3175     struct ElementInfo *ei = &element_info[i];
3176
3177     for (j = 0; j < ei->num_change_pages; j++)
3178     {
3179       if (!ei->change_page[j].can_change_or_has_action)
3180         continue;
3181
3182       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3183       {
3184         int trigger_element = ei->change_page[j].trigger_element;
3185
3186         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3187         {
3188           if (ei->change_page[j].has_event[k])
3189           {
3190             if (IS_GROUP_ELEMENT(trigger_element))
3191             {
3192               struct ElementGroupInfo *group =
3193                 element_info[trigger_element].group;
3194
3195               for (l = 0; l < group->num_elements_resolved; l++)
3196                 trigger_events[group->element_resolved[l]][k] = TRUE;
3197             }
3198             else if (trigger_element == EL_ANY_ELEMENT)
3199               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3200                 trigger_events[l][k] = TRUE;
3201             else
3202               trigger_events[trigger_element][k] = TRUE;
3203           }
3204         }
3205       }
3206     }
3207   }
3208
3209   // ---------- initialize push delay -----------------------------------------
3210
3211   // initialize push delay values to default
3212   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3213   {
3214     if (!IS_CUSTOM_ELEMENT(i))
3215     {
3216       // set default push delay values (corrected since version 3.0.7-1)
3217       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3218       {
3219         element_info[i].push_delay_fixed = 2;
3220         element_info[i].push_delay_random = 8;
3221       }
3222       else
3223       {
3224         element_info[i].push_delay_fixed = 8;
3225         element_info[i].push_delay_random = 8;
3226       }
3227     }
3228   }
3229
3230   // set push delay value for certain elements from pre-defined list
3231   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3232   {
3233     int e = push_delay_list[i].element;
3234
3235     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3236     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3237   }
3238
3239   // set push delay value for Supaplex elements for newer engine versions
3240   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3241   {
3242     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     {
3244       if (IS_SP_ELEMENT(i))
3245       {
3246         // set SP push delay to just enough to push under a falling zonk
3247         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3248
3249         element_info[i].push_delay_fixed  = delay;
3250         element_info[i].push_delay_random = 0;
3251       }
3252     }
3253   }
3254
3255   // ---------- initialize move stepsize --------------------------------------
3256
3257   // initialize move stepsize values to default
3258   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3259     if (!IS_CUSTOM_ELEMENT(i))
3260       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3261
3262   // set move stepsize value for certain elements from pre-defined list
3263   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3264   {
3265     int e = move_stepsize_list[i].element;
3266
3267     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3268
3269     // set move stepsize value for certain elements for older engine versions
3270     if (use_old_move_stepsize_for_magic_wall)
3271     {
3272       if (e == EL_MAGIC_WALL_FILLING ||
3273           e == EL_MAGIC_WALL_EMPTYING ||
3274           e == EL_BD_MAGIC_WALL_FILLING ||
3275           e == EL_BD_MAGIC_WALL_EMPTYING)
3276         element_info[e].move_stepsize *= 2;
3277     }
3278   }
3279
3280   // ---------- initialize collect score --------------------------------------
3281
3282   // initialize collect score values for custom elements from initial value
3283   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3284     if (IS_CUSTOM_ELEMENT(i))
3285       element_info[i].collect_score = element_info[i].collect_score_initial;
3286
3287   // ---------- initialize collect count --------------------------------------
3288
3289   // initialize collect count values for non-custom elements
3290   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3291     if (!IS_CUSTOM_ELEMENT(i))
3292       element_info[i].collect_count_initial = 0;
3293
3294   // add collect count values for all elements from pre-defined list
3295   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3296     element_info[collect_count_list[i].element].collect_count_initial =
3297       collect_count_list[i].count;
3298
3299   // ---------- initialize access direction -----------------------------------
3300
3301   // initialize access direction values to default (access from every side)
3302   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3303     if (!IS_CUSTOM_ELEMENT(i))
3304       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3305
3306   // set access direction value for certain elements from pre-defined list
3307   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3308     element_info[access_direction_list[i].element].access_direction =
3309       access_direction_list[i].direction;
3310
3311   // ---------- initialize explosion content ----------------------------------
3312   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3313   {
3314     if (IS_CUSTOM_ELEMENT(i))
3315       continue;
3316
3317     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3318     {
3319       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3320
3321       element_info[i].content.e[x][y] =
3322         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3323          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3324          i == EL_PLAYER_3 ? EL_EMERALD :
3325          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3326          i == EL_MOLE ? EL_EMERALD_RED :
3327          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3328          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3329          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3330          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3331          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3332          i == EL_WALL_EMERALD ? EL_EMERALD :
3333          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3334          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3335          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3336          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3337          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3338          i == EL_WALL_PEARL ? EL_PEARL :
3339          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3340          EL_EMPTY);
3341     }
3342   }
3343
3344   // ---------- initialize recursion detection --------------------------------
3345   recursion_loop_depth = 0;
3346   recursion_loop_detected = FALSE;
3347   recursion_loop_element = EL_UNDEFINED;
3348
3349   // ---------- initialize graphics engine ------------------------------------
3350   game.scroll_delay_value =
3351     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3352      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3353      !setup.forced_scroll_delay           ? 0 :
3354      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3355   game.scroll_delay_value =
3356     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3357
3358   // ---------- initialize game engine snapshots ------------------------------
3359   for (i = 0; i < MAX_PLAYERS; i++)
3360     game.snapshot.last_action[i] = 0;
3361   game.snapshot.changed_action = FALSE;
3362   game.snapshot.collected_item = FALSE;
3363   game.snapshot.mode =
3364     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3365      SNAPSHOT_MODE_EVERY_STEP :
3366      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3367      SNAPSHOT_MODE_EVERY_MOVE :
3368      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3369      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3370   game.snapshot.save_snapshot = FALSE;
3371
3372   // ---------- initialize level time for Supaplex engine ---------------------
3373   // Supaplex levels with time limit currently unsupported -- should be added
3374   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3375     level.time = 0;
3376
3377   // ---------- initialize flags for handling game actions --------------------
3378
3379   // set flags for game actions to default values
3380   game.use_key_actions = TRUE;
3381   game.use_mouse_actions = FALSE;
3382
3383   // when using Mirror Magic game engine, handle mouse events only
3384   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3385   {
3386     game.use_key_actions = FALSE;
3387     game.use_mouse_actions = TRUE;
3388   }
3389
3390   // check for custom elements with mouse click events
3391   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3392   {
3393     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3394     {
3395       int element = EL_CUSTOM_START + i;
3396
3397       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3398           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3399           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3400           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3401         game.use_mouse_actions = TRUE;
3402     }
3403   }
3404 }
3405
3406 static int get_num_special_action(int element, int action_first,
3407                                   int action_last)
3408 {
3409   int num_special_action = 0;
3410   int i, j;
3411
3412   for (i = action_first; i <= action_last; i++)
3413   {
3414     boolean found = FALSE;
3415
3416     for (j = 0; j < NUM_DIRECTIONS; j++)
3417       if (el_act_dir2img(element, i, j) !=
3418           el_act_dir2img(element, ACTION_DEFAULT, j))
3419         found = TRUE;
3420
3421     if (found)
3422       num_special_action++;
3423     else
3424       break;
3425   }
3426
3427   return num_special_action;
3428 }
3429
3430
3431 // ============================================================================
3432 // InitGame()
3433 // ----------------------------------------------------------------------------
3434 // initialize and start new game
3435 // ============================================================================
3436
3437 #if DEBUG_INIT_PLAYER
3438 static void DebugPrintPlayerStatus(char *message)
3439 {
3440   int i;
3441
3442   if (!options.debug)
3443     return;
3444
3445   Debug("game:init:player", "%s:", message);
3446
3447   for (i = 0; i < MAX_PLAYERS; i++)
3448   {
3449     struct PlayerInfo *player = &stored_player[i];
3450
3451     Debug("game:init:player",
3452           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3453           i + 1,
3454           player->present,
3455           player->connected,
3456           player->connected_locally,
3457           player->connected_network,
3458           player->active,
3459           (local_player == player ? " (local player)" : ""));
3460   }
3461 }
3462 #endif
3463
3464 void InitGame(void)
3465 {
3466   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3467   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3468   int fade_mask = REDRAW_FIELD;
3469
3470   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3471   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3472   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3473   int initial_move_dir = MV_DOWN;
3474   int i, j, x, y;
3475
3476   // required here to update video display before fading (FIX THIS)
3477   DrawMaskedBorder(REDRAW_DOOR_2);
3478
3479   if (!game.restart_level)
3480     CloseDoor(DOOR_CLOSE_1);
3481
3482   SetGameStatus(GAME_MODE_PLAYING);
3483
3484   if (level_editor_test_game)
3485     FadeSkipNextFadeOut();
3486   else
3487     FadeSetEnterScreen();
3488
3489   if (CheckFadeAll())
3490     fade_mask = REDRAW_ALL;
3491
3492   FadeLevelSoundsAndMusic();
3493
3494   ExpireSoundLoops(TRUE);
3495
3496   FadeOut(fade_mask);
3497
3498   if (level_editor_test_game)
3499     FadeSkipNextFadeIn();
3500
3501   // needed if different viewport properties defined for playing
3502   ChangeViewportPropertiesIfNeeded();
3503
3504   ClearField();
3505
3506   DrawCompleteVideoDisplay();
3507
3508   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3509
3510   InitGameEngine();
3511   InitGameControlValues();
3512
3513   // initialize tape actions from game when recording tape
3514   if (tape.recording)
3515   {
3516     tape.use_key_actions   = game.use_key_actions;
3517     tape.use_mouse_actions = game.use_mouse_actions;
3518   }
3519
3520   // don't play tapes over network
3521   network_playing = (network.enabled && !tape.playing);
3522
3523   for (i = 0; i < MAX_PLAYERS; i++)
3524   {
3525     struct PlayerInfo *player = &stored_player[i];
3526
3527     player->index_nr = i;
3528     player->index_bit = (1 << i);
3529     player->element_nr = EL_PLAYER_1 + i;
3530
3531     player->present = FALSE;
3532     player->active = FALSE;
3533     player->mapped = FALSE;
3534
3535     player->killed = FALSE;
3536     player->reanimated = FALSE;
3537     player->buried = FALSE;
3538
3539     player->action = 0;
3540     player->effective_action = 0;
3541     player->programmed_action = 0;
3542     player->snap_action = 0;
3543
3544     player->mouse_action.lx = 0;
3545     player->mouse_action.ly = 0;
3546     player->mouse_action.button = 0;
3547     player->mouse_action.button_hint = 0;
3548
3549     player->effective_mouse_action.lx = 0;
3550     player->effective_mouse_action.ly = 0;
3551     player->effective_mouse_action.button = 0;
3552     player->effective_mouse_action.button_hint = 0;
3553
3554     for (j = 0; j < MAX_NUM_KEYS; j++)
3555       player->key[j] = FALSE;
3556
3557     player->num_white_keys = 0;
3558
3559     player->dynabomb_count = 0;
3560     player->dynabomb_size = 1;
3561     player->dynabombs_left = 0;
3562     player->dynabomb_xl = FALSE;
3563
3564     player->MovDir = initial_move_dir;
3565     player->MovPos = 0;
3566     player->GfxPos = 0;
3567     player->GfxDir = initial_move_dir;
3568     player->GfxAction = ACTION_DEFAULT;
3569     player->Frame = 0;
3570     player->StepFrame = 0;
3571
3572     player->initial_element = player->element_nr;
3573     player->artwork_element =
3574       (level.use_artwork_element[i] ? level.artwork_element[i] :
3575        player->element_nr);
3576     player->use_murphy = FALSE;
3577
3578     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3579     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3580
3581     player->gravity = level.initial_player_gravity[i];
3582
3583     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3584
3585     player->actual_frame_counter = 0;
3586
3587     player->step_counter = 0;
3588
3589     player->last_move_dir = initial_move_dir;
3590
3591     player->is_active = FALSE;
3592
3593     player->is_waiting = FALSE;
3594     player->is_moving = FALSE;
3595     player->is_auto_moving = FALSE;
3596     player->is_digging = FALSE;
3597     player->is_snapping = FALSE;
3598     player->is_collecting = FALSE;
3599     player->is_pushing = FALSE;
3600     player->is_switching = FALSE;
3601     player->is_dropping = FALSE;
3602     player->is_dropping_pressed = FALSE;
3603
3604     player->is_bored = FALSE;
3605     player->is_sleeping = FALSE;
3606
3607     player->was_waiting = TRUE;
3608     player->was_moving = FALSE;
3609     player->was_snapping = FALSE;
3610     player->was_dropping = FALSE;
3611
3612     player->force_dropping = FALSE;
3613
3614     player->frame_counter_bored = -1;
3615     player->frame_counter_sleeping = -1;
3616
3617     player->anim_delay_counter = 0;
3618     player->post_delay_counter = 0;
3619
3620     player->dir_waiting = initial_move_dir;
3621     player->action_waiting = ACTION_DEFAULT;
3622     player->last_action_waiting = ACTION_DEFAULT;
3623     player->special_action_bored = ACTION_DEFAULT;
3624     player->special_action_sleeping = ACTION_DEFAULT;
3625
3626     player->switch_x = -1;
3627     player->switch_y = -1;
3628
3629     player->drop_x = -1;
3630     player->drop_y = -1;
3631
3632     player->show_envelope = 0;
3633
3634     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3635
3636     player->push_delay       = -1;      // initialized when pushing starts
3637     player->push_delay_value = game.initial_push_delay_value;
3638
3639     player->drop_delay = 0;
3640     player->drop_pressed_delay = 0;
3641
3642     player->last_jx = -1;
3643     player->last_jy = -1;
3644     player->jx = -1;
3645     player->jy = -1;
3646
3647     player->shield_normal_time_left = 0;
3648     player->shield_deadly_time_left = 0;
3649
3650     player->inventory_infinite_element = EL_UNDEFINED;
3651     player->inventory_size = 0;
3652
3653     if (level.use_initial_inventory[i])
3654     {
3655       for (j = 0; j < level.initial_inventory_size[i]; j++)
3656       {
3657         int element = level.initial_inventory_content[i][j];
3658         int collect_count = element_info[element].collect_count_initial;
3659         int k;
3660
3661         if (!IS_CUSTOM_ELEMENT(element))
3662           collect_count = 1;
3663
3664         if (collect_count == 0)
3665           player->inventory_infinite_element = element;
3666         else
3667           for (k = 0; k < collect_count; k++)
3668             if (player->inventory_size < MAX_INVENTORY_SIZE)
3669               player->inventory_element[player->inventory_size++] = element;
3670       }
3671     }
3672
3673     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3674     SnapField(player, 0, 0);
3675
3676     map_player_action[i] = i;
3677   }
3678
3679   network_player_action_received = FALSE;
3680
3681   // initial null action
3682   if (network_playing)
3683     SendToServer_MovePlayer(MV_NONE);
3684
3685   FrameCounter = 0;
3686   TimeFrames = 0;
3687   TimePlayed = 0;
3688   TimeLeft = level.time;
3689   TapeTime = 0;
3690
3691   ScreenMovDir = MV_NONE;
3692   ScreenMovPos = 0;
3693   ScreenGfxPos = 0;
3694
3695   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3696
3697   game.robot_wheel_x = -1;
3698   game.robot_wheel_y = -1;
3699
3700   game.exit_x = -1;
3701   game.exit_y = -1;
3702
3703   game.all_players_gone = FALSE;
3704
3705   game.LevelSolved = FALSE;
3706   game.GameOver = FALSE;
3707
3708   game.GamePlayed = !tape.playing;
3709
3710   game.LevelSolved_GameWon = FALSE;
3711   game.LevelSolved_GameEnd = FALSE;
3712   game.LevelSolved_SaveTape = FALSE;
3713   game.LevelSolved_SaveScore = FALSE;
3714
3715   game.LevelSolved_CountingTime = 0;
3716   game.LevelSolved_CountingScore = 0;
3717   game.LevelSolved_CountingHealth = 0;
3718
3719   game.panel.active = TRUE;
3720
3721   game.no_time_limit = (level.time == 0);
3722
3723   game.yamyam_content_nr = 0;
3724   game.robot_wheel_active = FALSE;
3725   game.magic_wall_active = FALSE;
3726   game.magic_wall_time_left = 0;
3727   game.light_time_left = 0;
3728   game.timegate_time_left = 0;
3729   game.switchgate_pos = 0;
3730   game.wind_direction = level.wind_direction_initial;
3731
3732   game.score = 0;
3733   game.score_final = 0;
3734
3735   game.health = MAX_HEALTH;
3736   game.health_final = MAX_HEALTH;
3737
3738   game.gems_still_needed = level.gems_needed;
3739   game.sokoban_fields_still_needed = 0;
3740   game.sokoban_objects_still_needed = 0;
3741   game.lights_still_needed = 0;
3742   game.players_still_needed = 0;
3743   game.friends_still_needed = 0;
3744
3745   game.lenses_time_left = 0;
3746   game.magnify_time_left = 0;
3747
3748   game.ball_active = level.ball_active_initial;
3749   game.ball_content_nr = 0;
3750
3751   game.explosions_delayed = TRUE;
3752
3753   game.envelope_active = FALSE;
3754
3755   for (i = 0; i < NUM_BELTS; i++)
3756   {
3757     game.belt_dir[i] = MV_NONE;
3758     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3759   }
3760
3761   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3762     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3763
3764 #if DEBUG_INIT_PLAYER
3765   DebugPrintPlayerStatus("Player status at level initialization");
3766 #endif
3767
3768   SCAN_PLAYFIELD(x, y)
3769   {
3770     Tile[x][y] = Last[x][y] = level.field[x][y];
3771     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3772     ChangeDelay[x][y] = 0;
3773     ChangePage[x][y] = -1;
3774     CustomValue[x][y] = 0;              // initialized in InitField()
3775     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3776     AmoebaNr[x][y] = 0;
3777     WasJustMoving[x][y] = 0;
3778     WasJustFalling[x][y] = 0;
3779     CheckCollision[x][y] = 0;
3780     CheckImpact[x][y] = 0;
3781     Stop[x][y] = FALSE;
3782     Pushed[x][y] = FALSE;
3783
3784     ChangeCount[x][y] = 0;
3785     ChangeEvent[x][y] = -1;
3786
3787     ExplodePhase[x][y] = 0;
3788     ExplodeDelay[x][y] = 0;
3789     ExplodeField[x][y] = EX_TYPE_NONE;
3790
3791     RunnerVisit[x][y] = 0;
3792     PlayerVisit[x][y] = 0;
3793
3794     GfxFrame[x][y] = 0;
3795     GfxRandom[x][y] = INIT_GFX_RANDOM();
3796     GfxElement[x][y] = EL_UNDEFINED;
3797     GfxAction[x][y] = ACTION_DEFAULT;
3798     GfxDir[x][y] = MV_NONE;
3799     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3800   }
3801
3802   SCAN_PLAYFIELD(x, y)
3803   {
3804     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3805       emulate_bd = FALSE;
3806     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3807       emulate_sb = FALSE;
3808     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3809       emulate_sp = FALSE;
3810
3811     InitField(x, y, TRUE);
3812
3813     ResetGfxAnimation(x, y);
3814   }
3815
3816   InitBeltMovement();
3817
3818   for (i = 0; i < MAX_PLAYERS; i++)
3819   {
3820     struct PlayerInfo *player = &stored_player[i];
3821
3822     // set number of special actions for bored and sleeping animation
3823     player->num_special_action_bored =
3824       get_num_special_action(player->artwork_element,
3825                              ACTION_BORING_1, ACTION_BORING_LAST);
3826     player->num_special_action_sleeping =
3827       get_num_special_action(player->artwork_element,
3828                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3829   }
3830
3831   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3832                     emulate_sb ? EMU_SOKOBAN :
3833                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3834
3835   // initialize type of slippery elements
3836   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3837   {
3838     if (!IS_CUSTOM_ELEMENT(i))
3839     {
3840       // default: elements slip down either to the left or right randomly
3841       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3842
3843       // SP style elements prefer to slip down on the left side
3844       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3845         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3846
3847       // BD style elements prefer to slip down on the left side
3848       if (game.emulation == EMU_BOULDERDASH)
3849         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3850     }
3851   }
3852
3853   // initialize explosion and ignition delay
3854   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3855   {
3856     if (!IS_CUSTOM_ELEMENT(i))
3857     {
3858       int num_phase = 8;
3859       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3860                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3861                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3862       int last_phase = (num_phase + 1) * delay;
3863       int half_phase = (num_phase / 2) * delay;
3864
3865       element_info[i].explosion_delay = last_phase - 1;
3866       element_info[i].ignition_delay = half_phase;
3867
3868       if (i == EL_BLACK_ORB)
3869         element_info[i].ignition_delay = 1;
3870     }
3871   }
3872
3873   // correct non-moving belts to start moving left
3874   for (i = 0; i < NUM_BELTS; i++)
3875     if (game.belt_dir[i] == MV_NONE)
3876       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3877
3878 #if USE_NEW_PLAYER_ASSIGNMENTS
3879   // use preferred player also in local single-player mode
3880   if (!network.enabled && !game.team_mode)
3881   {
3882     int new_index_nr = setup.network_player_nr;
3883
3884     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3885     {
3886       for (i = 0; i < MAX_PLAYERS; i++)
3887         stored_player[i].connected_locally = FALSE;
3888
3889       stored_player[new_index_nr].connected_locally = TRUE;
3890     }
3891   }
3892
3893   for (i = 0; i < MAX_PLAYERS; i++)
3894   {
3895     stored_player[i].connected = FALSE;
3896
3897     // in network game mode, the local player might not be the first player
3898     if (stored_player[i].connected_locally)
3899       local_player = &stored_player[i];
3900   }
3901
3902   if (!network.enabled)
3903     local_player->connected = TRUE;
3904
3905   if (tape.playing)
3906   {
3907     for (i = 0; i < MAX_PLAYERS; i++)
3908       stored_player[i].connected = tape.player_participates[i];
3909   }
3910   else if (network.enabled)
3911   {
3912     // add team mode players connected over the network (needed for correct
3913     // assignment of player figures from level to locally playing players)
3914
3915     for (i = 0; i < MAX_PLAYERS; i++)
3916       if (stored_player[i].connected_network)
3917         stored_player[i].connected = TRUE;
3918   }
3919   else if (game.team_mode)
3920   {
3921     // try to guess locally connected team mode players (needed for correct
3922     // assignment of player figures from level to locally playing players)
3923
3924     for (i = 0; i < MAX_PLAYERS; i++)
3925       if (setup.input[i].use_joystick ||
3926           setup.input[i].key.left != KSYM_UNDEFINED)
3927         stored_player[i].connected = TRUE;
3928   }
3929
3930 #if DEBUG_INIT_PLAYER
3931   DebugPrintPlayerStatus("Player status after level initialization");
3932 #endif
3933
3934 #if DEBUG_INIT_PLAYER
3935   Debug("game:init:player", "Reassigning players ...");
3936 #endif
3937
3938   // check if any connected player was not found in playfield
3939   for (i = 0; i < MAX_PLAYERS; i++)
3940   {
3941     struct PlayerInfo *player = &stored_player[i];
3942
3943     if (player->connected && !player->present)
3944     {
3945       struct PlayerInfo *field_player = NULL;
3946
3947 #if DEBUG_INIT_PLAYER
3948       Debug("game:init:player",
3949             "- looking for field player for player %d ...", i + 1);
3950 #endif
3951
3952       // assign first free player found that is present in the playfield
3953
3954       // first try: look for unmapped playfield player that is not connected
3955       for (j = 0; j < MAX_PLAYERS; j++)
3956         if (field_player == NULL &&
3957             stored_player[j].present &&
3958             !stored_player[j].mapped &&
3959             !stored_player[j].connected)
3960           field_player = &stored_player[j];
3961
3962       // second try: look for *any* unmapped playfield player
3963       for (j = 0; j < MAX_PLAYERS; j++)
3964         if (field_player == NULL &&
3965             stored_player[j].present &&
3966             !stored_player[j].mapped)
3967           field_player = &stored_player[j];
3968
3969       if (field_player != NULL)
3970       {
3971         int jx = field_player->jx, jy = field_player->jy;
3972
3973 #if DEBUG_INIT_PLAYER
3974         Debug("game:init:player", "- found player %d",
3975               field_player->index_nr + 1);
3976 #endif
3977
3978         player->present = FALSE;
3979         player->active = FALSE;
3980
3981         field_player->present = TRUE;
3982         field_player->active = TRUE;
3983
3984         /*
3985         player->initial_element = field_player->initial_element;
3986         player->artwork_element = field_player->artwork_element;
3987
3988         player->block_last_field       = field_player->block_last_field;
3989         player->block_delay_adjustment = field_player->block_delay_adjustment;
3990         */
3991
3992         StorePlayer[jx][jy] = field_player->element_nr;
3993
3994         field_player->jx = field_player->last_jx = jx;
3995         field_player->jy = field_player->last_jy = jy;
3996
3997         if (local_player == player)
3998           local_player = field_player;
3999
4000         map_player_action[field_player->index_nr] = i;
4001
4002         field_player->mapped = TRUE;
4003
4004 #if DEBUG_INIT_PLAYER
4005         Debug("game:init:player", "- map_player_action[%d] == %d",
4006               field_player->index_nr + 1, i + 1);
4007 #endif
4008       }
4009     }
4010
4011     if (player->connected && player->present)
4012       player->mapped = TRUE;
4013   }
4014
4015 #if DEBUG_INIT_PLAYER
4016   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4017 #endif
4018
4019 #else
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       for (j = 0; j < MAX_PLAYERS; j++)
4029       {
4030         struct PlayerInfo *field_player = &stored_player[j];
4031         int jx = field_player->jx, jy = field_player->jy;
4032
4033         // assign first free player found that is present in the playfield
4034         if (field_player->present && !field_player->connected)
4035         {
4036           player->present = TRUE;
4037           player->active = TRUE;
4038
4039           field_player->present = FALSE;
4040           field_player->active = FALSE;
4041
4042           player->initial_element = field_player->initial_element;
4043           player->artwork_element = field_player->artwork_element;
4044
4045           player->block_last_field       = field_player->block_last_field;
4046           player->block_delay_adjustment = field_player->block_delay_adjustment;
4047
4048           StorePlayer[jx][jy] = player->element_nr;
4049
4050           player->jx = player->last_jx = jx;
4051           player->jy = player->last_jy = jy;
4052
4053           break;
4054         }
4055       }
4056     }
4057   }
4058 #endif
4059
4060 #if 0
4061   Debug("game:init:player", "local_player->present == %d",
4062         local_player->present);
4063 #endif
4064
4065   // set focus to local player for network games, else to all players
4066   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4067   game.centered_player_nr_next = game.centered_player_nr;
4068   game.set_centered_player = FALSE;
4069   game.set_centered_player_wrap = FALSE;
4070
4071   if (network_playing && tape.recording)
4072   {
4073     // store client dependent player focus when recording network games
4074     tape.centered_player_nr_next = game.centered_player_nr_next;
4075     tape.set_centered_player = TRUE;
4076   }
4077
4078   if (tape.playing)
4079   {
4080     // when playing a tape, eliminate all players who do not participate
4081
4082 #if USE_NEW_PLAYER_ASSIGNMENTS
4083
4084     if (!game.team_mode)
4085     {
4086       for (i = 0; i < MAX_PLAYERS; i++)
4087       {
4088         if (stored_player[i].active &&
4089             !tape.player_participates[map_player_action[i]])
4090         {
4091           struct PlayerInfo *player = &stored_player[i];
4092           int jx = player->jx, jy = player->jy;
4093
4094 #if DEBUG_INIT_PLAYER
4095           Debug("game:init:player", "Removing player %d at (%d, %d)",
4096                 i + 1, jx, jy);
4097 #endif
4098
4099           player->active = FALSE;
4100           StorePlayer[jx][jy] = 0;
4101           Tile[jx][jy] = EL_EMPTY;
4102         }
4103       }
4104     }
4105
4106 #else
4107
4108     for (i = 0; i < MAX_PLAYERS; i++)
4109     {
4110       if (stored_player[i].active &&
4111           !tape.player_participates[i])
4112       {
4113         struct PlayerInfo *player = &stored_player[i];
4114         int jx = player->jx, jy = player->jy;
4115
4116         player->active = FALSE;
4117         StorePlayer[jx][jy] = 0;
4118         Tile[jx][jy] = EL_EMPTY;
4119       }
4120     }
4121 #endif
4122   }
4123   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4124   {
4125     // when in single player mode, eliminate all but the local player
4126
4127     for (i = 0; i < MAX_PLAYERS; i++)
4128     {
4129       struct PlayerInfo *player = &stored_player[i];
4130
4131       if (player->active && player != local_player)
4132       {
4133         int jx = player->jx, jy = player->jy;
4134
4135         player->active = FALSE;
4136         player->present = FALSE;
4137
4138         StorePlayer[jx][jy] = 0;
4139         Tile[jx][jy] = EL_EMPTY;
4140       }
4141     }
4142   }
4143
4144   for (i = 0; i < MAX_PLAYERS; i++)
4145     if (stored_player[i].active)
4146       game.players_still_needed++;
4147
4148   if (level.solved_by_one_player)
4149     game.players_still_needed = 1;
4150
4151   // when recording the game, store which players take part in the game
4152   if (tape.recording)
4153   {
4154 #if USE_NEW_PLAYER_ASSIGNMENTS
4155     for (i = 0; i < MAX_PLAYERS; i++)
4156       if (stored_player[i].connected)
4157         tape.player_participates[i] = TRUE;
4158 #else
4159     for (i = 0; i < MAX_PLAYERS; i++)
4160       if (stored_player[i].active)
4161         tape.player_participates[i] = TRUE;
4162 #endif
4163   }
4164
4165 #if DEBUG_INIT_PLAYER
4166   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4167 #endif
4168
4169   if (BorderElement == EL_EMPTY)
4170   {
4171     SBX_Left = 0;
4172     SBX_Right = lev_fieldx - SCR_FIELDX;
4173     SBY_Upper = 0;
4174     SBY_Lower = lev_fieldy - SCR_FIELDY;
4175   }
4176   else
4177   {
4178     SBX_Left = -1;
4179     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4180     SBY_Upper = -1;
4181     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4182   }
4183
4184   if (full_lev_fieldx <= SCR_FIELDX)
4185     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4186   if (full_lev_fieldy <= SCR_FIELDY)
4187     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4188
4189   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4190     SBX_Left--;
4191   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4192     SBY_Upper--;
4193
4194   // if local player not found, look for custom element that might create
4195   // the player (make some assumptions about the right custom element)
4196   if (!local_player->present)
4197   {
4198     int start_x = 0, start_y = 0;
4199     int found_rating = 0;
4200     int found_element = EL_UNDEFINED;
4201     int player_nr = local_player->index_nr;
4202
4203     SCAN_PLAYFIELD(x, y)
4204     {
4205       int element = Tile[x][y];
4206       int content;
4207       int xx, yy;
4208       boolean is_player;
4209
4210       if (level.use_start_element[player_nr] &&
4211           level.start_element[player_nr] == element &&
4212           found_rating < 4)
4213       {
4214         start_x = x;
4215         start_y = y;
4216
4217         found_rating = 4;
4218         found_element = element;
4219       }
4220
4221       if (!IS_CUSTOM_ELEMENT(element))
4222         continue;
4223
4224       if (CAN_CHANGE(element))
4225       {
4226         for (i = 0; i < element_info[element].num_change_pages; i++)
4227         {
4228           // check for player created from custom element as single target
4229           content = element_info[element].change_page[i].target_element;
4230           is_player = ELEM_IS_PLAYER(content);
4231
4232           if (is_player && (found_rating < 3 ||
4233                             (found_rating == 3 && element < found_element)))
4234           {
4235             start_x = x;
4236             start_y = y;
4237
4238             found_rating = 3;
4239             found_element = element;
4240           }
4241         }
4242       }
4243
4244       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4245       {
4246         // check for player created from custom element as explosion content
4247         content = element_info[element].content.e[xx][yy];
4248         is_player = ELEM_IS_PLAYER(content);
4249
4250         if (is_player && (found_rating < 2 ||
4251                           (found_rating == 2 && element < found_element)))
4252         {
4253           start_x = x + xx - 1;
4254           start_y = y + yy - 1;
4255
4256           found_rating = 2;
4257           found_element = element;
4258         }
4259
4260         if (!CAN_CHANGE(element))
4261           continue;
4262
4263         for (i = 0; i < element_info[element].num_change_pages; i++)
4264         {
4265           // check for player created from custom element as extended target
4266           content =
4267             element_info[element].change_page[i].target_content.e[xx][yy];
4268
4269           is_player = ELEM_IS_PLAYER(content);
4270
4271           if (is_player && (found_rating < 1 ||
4272                             (found_rating == 1 && element < found_element)))
4273           {
4274             start_x = x + xx - 1;
4275             start_y = y + yy - 1;
4276
4277             found_rating = 1;
4278             found_element = element;
4279           }
4280         }
4281       }
4282     }
4283
4284     scroll_x = SCROLL_POSITION_X(start_x);
4285     scroll_y = SCROLL_POSITION_Y(start_y);
4286   }
4287   else
4288   {
4289     scroll_x = SCROLL_POSITION_X(local_player->jx);
4290     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4291   }
4292
4293   // !!! FIX THIS (START) !!!
4294   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4295   {
4296     InitGameEngine_EM();
4297   }
4298   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4299   {
4300     InitGameEngine_SP();
4301   }
4302   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4303   {
4304     InitGameEngine_MM();
4305   }
4306   else
4307   {
4308     DrawLevel(REDRAW_FIELD);
4309     DrawAllPlayers();
4310
4311     // after drawing the level, correct some elements
4312     if (game.timegate_time_left == 0)
4313       CloseAllOpenTimegates();
4314   }
4315
4316   // blit playfield from scroll buffer to normal back buffer for fading in
4317   BlitScreenToBitmap(backbuffer);
4318   // !!! FIX THIS (END) !!!
4319
4320   DrawMaskedBorder(fade_mask);
4321
4322   FadeIn(fade_mask);
4323
4324 #if 1
4325   // full screen redraw is required at this point in the following cases:
4326   // - special editor door undrawn when game was started from level editor
4327   // - drawing area (playfield) was changed and has to be removed completely
4328   redraw_mask = REDRAW_ALL;
4329   BackToFront();
4330 #endif
4331
4332   if (!game.restart_level)
4333   {
4334     // copy default game door content to main double buffer
4335
4336     // !!! CHECK AGAIN !!!
4337     SetPanelBackground();
4338     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4339     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4340   }
4341
4342   SetPanelBackground();
4343   SetDrawBackgroundMask(REDRAW_DOOR_1);
4344
4345   UpdateAndDisplayGameControlValues();
4346
4347   if (!game.restart_level)
4348   {
4349     UnmapGameButtons();
4350     UnmapTapeButtons();
4351
4352     FreeGameButtons();
4353     CreateGameButtons();
4354
4355     MapGameButtons();
4356     MapTapeButtons();
4357
4358     // copy actual game door content to door double buffer for OpenDoor()
4359     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4360
4361     OpenDoor(DOOR_OPEN_ALL);
4362
4363     KeyboardAutoRepeatOffUnlessAutoplay();
4364
4365 #if DEBUG_INIT_PLAYER
4366     DebugPrintPlayerStatus("Player status (final)");
4367 #endif
4368   }
4369
4370   UnmapAllGadgets();
4371
4372   MapGameButtons();
4373   MapTapeButtons();
4374
4375   if (!game.restart_level && !tape.playing)
4376   {
4377     LevelStats_incPlayed(level_nr);
4378
4379     SaveLevelSetup_SeriesInfo();
4380   }
4381
4382   game.restart_level = FALSE;
4383   game.restart_game_message = NULL;
4384   game.request_active = FALSE;
4385
4386   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4387     InitGameActions_MM();
4388
4389   SaveEngineSnapshotToListInitial();
4390
4391   if (!game.restart_level)
4392   {
4393     PlaySound(SND_GAME_STARTING);
4394
4395     if (setup.sound_music)
4396       PlayLevelMusic();
4397   }
4398 }
4399
4400 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4401                         int actual_player_x, int actual_player_y)
4402 {
4403   // this is used for non-R'n'D game engines to update certain engine values
4404
4405   // needed to determine if sounds are played within the visible screen area
4406   scroll_x = actual_scroll_x;
4407   scroll_y = actual_scroll_y;
4408
4409   // needed to get player position for "follow finger" playing input method
4410   local_player->jx = actual_player_x;
4411   local_player->jy = actual_player_y;
4412 }
4413
4414 void InitMovDir(int x, int y)
4415 {
4416   int i, element = Tile[x][y];
4417   static int xy[4][2] =
4418   {
4419     {  0, +1 },
4420     { +1,  0 },
4421     {  0, -1 },
4422     { -1,  0 }
4423   };
4424   static int direction[3][4] =
4425   {
4426     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4427     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4428     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4429   };
4430
4431   switch (element)
4432   {
4433     case EL_BUG_RIGHT:
4434     case EL_BUG_UP:
4435     case EL_BUG_LEFT:
4436     case EL_BUG_DOWN:
4437       Tile[x][y] = EL_BUG;
4438       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4439       break;
4440
4441     case EL_SPACESHIP_RIGHT:
4442     case EL_SPACESHIP_UP:
4443     case EL_SPACESHIP_LEFT:
4444     case EL_SPACESHIP_DOWN:
4445       Tile[x][y] = EL_SPACESHIP;
4446       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4447       break;
4448
4449     case EL_BD_BUTTERFLY_RIGHT:
4450     case EL_BD_BUTTERFLY_UP:
4451     case EL_BD_BUTTERFLY_LEFT:
4452     case EL_BD_BUTTERFLY_DOWN:
4453       Tile[x][y] = EL_BD_BUTTERFLY;
4454       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4455       break;
4456
4457     case EL_BD_FIREFLY_RIGHT:
4458     case EL_BD_FIREFLY_UP:
4459     case EL_BD_FIREFLY_LEFT:
4460     case EL_BD_FIREFLY_DOWN:
4461       Tile[x][y] = EL_BD_FIREFLY;
4462       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4463       break;
4464
4465     case EL_PACMAN_RIGHT:
4466     case EL_PACMAN_UP:
4467     case EL_PACMAN_LEFT:
4468     case EL_PACMAN_DOWN:
4469       Tile[x][y] = EL_PACMAN;
4470       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4471       break;
4472
4473     case EL_YAMYAM_LEFT:
4474     case EL_YAMYAM_RIGHT:
4475     case EL_YAMYAM_UP:
4476     case EL_YAMYAM_DOWN:
4477       Tile[x][y] = EL_YAMYAM;
4478       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4479       break;
4480
4481     case EL_SP_SNIKSNAK:
4482       MovDir[x][y] = MV_UP;
4483       break;
4484
4485     case EL_SP_ELECTRON:
4486       MovDir[x][y] = MV_LEFT;
4487       break;
4488
4489     case EL_MOLE_LEFT:
4490     case EL_MOLE_RIGHT:
4491     case EL_MOLE_UP:
4492     case EL_MOLE_DOWN:
4493       Tile[x][y] = EL_MOLE;
4494       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4495       break;
4496
4497     case EL_SPRING_LEFT:
4498     case EL_SPRING_RIGHT:
4499       Tile[x][y] = EL_SPRING;
4500       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4501       break;
4502
4503     default:
4504       if (IS_CUSTOM_ELEMENT(element))
4505       {
4506         struct ElementInfo *ei = &element_info[element];
4507         int move_direction_initial = ei->move_direction_initial;
4508         int move_pattern = ei->move_pattern;
4509
4510         if (move_direction_initial == MV_START_PREVIOUS)
4511         {
4512           if (MovDir[x][y] != MV_NONE)
4513             return;
4514
4515           move_direction_initial = MV_START_AUTOMATIC;
4516         }
4517
4518         if (move_direction_initial == MV_START_RANDOM)
4519           MovDir[x][y] = 1 << RND(4);
4520         else if (move_direction_initial & MV_ANY_DIRECTION)
4521           MovDir[x][y] = move_direction_initial;
4522         else if (move_pattern == MV_ALL_DIRECTIONS ||
4523                  move_pattern == MV_TURNING_LEFT ||
4524                  move_pattern == MV_TURNING_RIGHT ||
4525                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4526                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4527                  move_pattern == MV_TURNING_RANDOM)
4528           MovDir[x][y] = 1 << RND(4);
4529         else if (move_pattern == MV_HORIZONTAL)
4530           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4531         else if (move_pattern == MV_VERTICAL)
4532           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4533         else if (move_pattern & MV_ANY_DIRECTION)
4534           MovDir[x][y] = element_info[element].move_pattern;
4535         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4536                  move_pattern == MV_ALONG_RIGHT_SIDE)
4537         {
4538           // use random direction as default start direction
4539           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4540             MovDir[x][y] = 1 << RND(4);
4541
4542           for (i = 0; i < NUM_DIRECTIONS; i++)
4543           {
4544             int x1 = x + xy[i][0];
4545             int y1 = y + xy[i][1];
4546
4547             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4548             {
4549               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4550                 MovDir[x][y] = direction[0][i];
4551               else
4552                 MovDir[x][y] = direction[1][i];
4553
4554               break;
4555             }
4556           }
4557         }                
4558       }
4559       else
4560       {
4561         MovDir[x][y] = 1 << RND(4);
4562
4563         if (element != EL_BUG &&
4564             element != EL_SPACESHIP &&
4565             element != EL_BD_BUTTERFLY &&
4566             element != EL_BD_FIREFLY)
4567           break;
4568
4569         for (i = 0; i < NUM_DIRECTIONS; i++)
4570         {
4571           int x1 = x + xy[i][0];
4572           int y1 = y + xy[i][1];
4573
4574           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4575           {
4576             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4577             {
4578               MovDir[x][y] = direction[0][i];
4579               break;
4580             }
4581             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4582                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4583             {
4584               MovDir[x][y] = direction[1][i];
4585               break;
4586             }
4587           }
4588         }
4589       }
4590       break;
4591   }
4592
4593   GfxDir[x][y] = MovDir[x][y];
4594 }
4595
4596 void InitAmoebaNr(int x, int y)
4597 {
4598   int i;
4599   int group_nr = AmoebaNeighbourNr(x, y);
4600
4601   if (group_nr == 0)
4602   {
4603     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4604     {
4605       if (AmoebaCnt[i] == 0)
4606       {
4607         group_nr = i;
4608         break;
4609       }
4610     }
4611   }
4612
4613   AmoebaNr[x][y] = group_nr;
4614   AmoebaCnt[group_nr]++;
4615   AmoebaCnt2[group_nr]++;
4616 }
4617
4618 static void LevelSolved(void)
4619 {
4620   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4621       game.players_still_needed > 0)
4622     return;
4623
4624   game.LevelSolved = TRUE;
4625   game.GameOver = TRUE;
4626
4627   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4628                       game_em.lev->score :
4629                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4630                       game_mm.score :
4631                       game.score);
4632   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4633                        MM_HEALTH(game_mm.laser_overload_value) :
4634                        game.health);
4635
4636   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4637   game.LevelSolved_CountingScore = game.score_final;
4638   game.LevelSolved_CountingHealth = game.health_final;
4639 }
4640
4641 void GameWon(void)
4642 {
4643   static int time_count_steps;
4644   static int time, time_final;
4645   static int score, score_final;
4646   static int health, health_final;
4647   static int game_over_delay_1 = 0;
4648   static int game_over_delay_2 = 0;
4649   static int game_over_delay_3 = 0;
4650   int game_over_delay_value_1 = 50;
4651   int game_over_delay_value_2 = 25;
4652   int game_over_delay_value_3 = 50;
4653
4654   if (!game.LevelSolved_GameWon)
4655   {
4656     int i;
4657
4658     // do not start end game actions before the player stops moving (to exit)
4659     if (local_player->active && local_player->MovPos)
4660       return;
4661
4662     game.LevelSolved_GameWon = TRUE;
4663     game.LevelSolved_SaveTape = tape.recording;
4664     game.LevelSolved_SaveScore = !tape.playing;
4665
4666     if (!tape.playing)
4667     {
4668       LevelStats_incSolved(level_nr);
4669
4670       SaveLevelSetup_SeriesInfo();
4671     }
4672
4673     if (tape.auto_play)         // tape might already be stopped here
4674       tape.auto_play_level_solved = TRUE;
4675
4676     TapeStop();
4677
4678     game_over_delay_1 = 0;
4679     game_over_delay_2 = 0;
4680     game_over_delay_3 = game_over_delay_value_3;
4681
4682     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4683     score = score_final = game.score_final;
4684     health = health_final = game.health_final;
4685
4686     if (level.score[SC_TIME_BONUS] > 0)
4687     {
4688       if (TimeLeft > 0)
4689       {
4690         time_final = 0;
4691         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4692       }
4693       else if (game.no_time_limit && TimePlayed < 999)
4694       {
4695         time_final = 999;
4696         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4697       }
4698
4699       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4700
4701       game_over_delay_1 = game_over_delay_value_1;
4702
4703       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4704       {
4705         health_final = 0;
4706         score_final += health * level.score[SC_TIME_BONUS];
4707
4708         game_over_delay_2 = game_over_delay_value_2;
4709       }
4710
4711       game.score_final = score_final;
4712       game.health_final = health_final;
4713     }
4714
4715     if (level_editor_test_game)
4716     {
4717       time = time_final;
4718       score = score_final;
4719
4720       game.LevelSolved_CountingTime = time;
4721       game.LevelSolved_CountingScore = score;
4722
4723       game_panel_controls[GAME_PANEL_TIME].value = time;
4724       game_panel_controls[GAME_PANEL_SCORE].value = score;
4725
4726       DisplayGameControlValues();
4727     }
4728
4729     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4730     {
4731       // check if last player has left the level
4732       if (game.exit_x >= 0 &&
4733           game.exit_y >= 0)
4734       {
4735         int x = game.exit_x;
4736         int y = game.exit_y;
4737         int element = Tile[x][y];
4738
4739         // close exit door after last player
4740         if ((game.all_players_gone &&
4741              (element == EL_EXIT_OPEN ||
4742               element == EL_SP_EXIT_OPEN ||
4743               element == EL_STEEL_EXIT_OPEN)) ||
4744             element == EL_EM_EXIT_OPEN ||
4745             element == EL_EM_STEEL_EXIT_OPEN)
4746         {
4747
4748           Tile[x][y] =
4749             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4750              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4751              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4752              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4753              EL_EM_STEEL_EXIT_CLOSING);
4754
4755           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4756         }
4757
4758         // player disappears
4759         DrawLevelField(x, y);
4760       }
4761
4762       for (i = 0; i < MAX_PLAYERS; i++)
4763       {
4764         struct PlayerInfo *player = &stored_player[i];
4765
4766         if (player->present)
4767         {
4768           RemovePlayer(player);
4769
4770           // player disappears
4771           DrawLevelField(player->jx, player->jy);
4772         }
4773       }
4774     }
4775
4776     PlaySound(SND_GAME_WINNING);
4777   }
4778
4779   if (game_over_delay_1 > 0)
4780   {
4781     game_over_delay_1--;
4782
4783     return;
4784   }
4785
4786   if (time != time_final)
4787   {
4788     int time_to_go = ABS(time_final - time);
4789     int time_count_dir = (time < time_final ? +1 : -1);
4790
4791     if (time_to_go < time_count_steps)
4792       time_count_steps = 1;
4793
4794     time  += time_count_steps * time_count_dir;
4795     score += time_count_steps * level.score[SC_TIME_BONUS];
4796
4797     game.LevelSolved_CountingTime = time;
4798     game.LevelSolved_CountingScore = score;
4799
4800     game_panel_controls[GAME_PANEL_TIME].value = time;
4801     game_panel_controls[GAME_PANEL_SCORE].value = score;
4802
4803     DisplayGameControlValues();
4804
4805     if (time == time_final)
4806       StopSound(SND_GAME_LEVELTIME_BONUS);
4807     else if (setup.sound_loops)
4808       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4809     else
4810       PlaySound(SND_GAME_LEVELTIME_BONUS);
4811
4812     return;
4813   }
4814
4815   if (game_over_delay_2 > 0)
4816   {
4817     game_over_delay_2--;
4818
4819     return;
4820   }
4821
4822   if (health != health_final)
4823   {
4824     int health_count_dir = (health < health_final ? +1 : -1);
4825
4826     health += health_count_dir;
4827     score  += level.score[SC_TIME_BONUS];
4828
4829     game.LevelSolved_CountingHealth = health;
4830     game.LevelSolved_CountingScore = score;
4831
4832     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4833     game_panel_controls[GAME_PANEL_SCORE].value = score;
4834
4835     DisplayGameControlValues();
4836
4837     if (health == health_final)
4838       StopSound(SND_GAME_LEVELTIME_BONUS);
4839     else if (setup.sound_loops)
4840       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4841     else
4842       PlaySound(SND_GAME_LEVELTIME_BONUS);
4843
4844     return;
4845   }
4846
4847   game.panel.active = FALSE;
4848
4849   if (game_over_delay_3 > 0)
4850   {
4851     game_over_delay_3--;
4852
4853     return;
4854   }
4855
4856   GameEnd();
4857 }
4858
4859 void GameEnd(void)
4860 {
4861   // used instead of "level_nr" (needed for network games)
4862   int last_level_nr = levelset.level_nr;
4863   int hi_pos;
4864
4865   game.LevelSolved_GameEnd = TRUE;
4866
4867   if (game.LevelSolved_SaveTape)
4868   {
4869     // make sure that request dialog to save tape does not open door again
4870     if (!global.use_envelope_request)
4871       CloseDoor(DOOR_CLOSE_1);
4872
4873     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4874   }
4875
4876   // if no tape is to be saved, close both doors simultaneously
4877   CloseDoor(DOOR_CLOSE_ALL);
4878
4879   if (level_editor_test_game)
4880   {
4881     SetGameStatus(GAME_MODE_MAIN);
4882
4883     DrawMainMenu();
4884
4885     return;
4886   }
4887
4888   if (!game.LevelSolved_SaveScore)
4889   {
4890     SetGameStatus(GAME_MODE_MAIN);
4891
4892     DrawMainMenu();
4893
4894     return;
4895   }
4896
4897   if (level_nr == leveldir_current->handicap_level)
4898   {
4899     leveldir_current->handicap_level++;
4900
4901     SaveLevelSetup_SeriesInfo();
4902   }
4903
4904   if (setup.increment_levels &&
4905       level_nr < leveldir_current->last_level &&
4906       !network_playing)
4907   {
4908     level_nr++;         // advance to next level
4909     TapeErase();        // start with empty tape
4910
4911     if (setup.auto_play_next_level)
4912     {
4913       LoadLevel(level_nr);
4914
4915       SaveLevelSetup_SeriesInfo();
4916     }
4917   }
4918
4919   hi_pos = NewHiScore(last_level_nr);
4920
4921   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4922   {
4923     SetGameStatus(GAME_MODE_SCORES);
4924
4925     DrawHallOfFame(last_level_nr, hi_pos);
4926   }
4927   else if (setup.auto_play_next_level && setup.increment_levels &&
4928            last_level_nr < leveldir_current->last_level &&
4929            !network_playing)
4930   {
4931     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4932   }
4933   else
4934   {
4935     SetGameStatus(GAME_MODE_MAIN);
4936
4937     DrawMainMenu();
4938   }
4939 }
4940
4941 int NewHiScore(int level_nr)
4942 {
4943   int k, l;
4944   int position = -1;
4945   boolean one_score_entry_per_name = !program.many_scores_per_name;
4946
4947   LoadScore(level_nr);
4948
4949   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4950       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4951     return -1;
4952
4953   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4954   {
4955     if (game.score_final > highscore[k].Score)
4956     {
4957       // player has made it to the hall of fame
4958
4959       if (k < MAX_SCORE_ENTRIES - 1)
4960       {
4961         int m = MAX_SCORE_ENTRIES - 1;
4962
4963         if (one_score_entry_per_name)
4964         {
4965           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4966             if (strEqual(setup.player_name, highscore[l].Name))
4967               m = l;
4968
4969           if (m == k)   // player's new highscore overwrites his old one
4970             goto put_into_list;
4971         }
4972
4973         for (l = m; l > k; l--)
4974         {
4975           strcpy(highscore[l].Name, highscore[l - 1].Name);
4976           highscore[l].Score = highscore[l - 1].Score;
4977         }
4978       }
4979
4980       put_into_list:
4981
4982       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4983       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4984       highscore[k].Score = game.score_final;
4985       position = k;
4986
4987       break;
4988     }
4989     else if (one_score_entry_per_name &&
4990              !strncmp(setup.player_name, highscore[k].Name,
4991                       MAX_PLAYER_NAME_LEN))
4992       break;    // player already there with a higher score
4993   }
4994
4995   if (position >= 0) 
4996     SaveScore(level_nr);
4997
4998   return position;
4999 }
5000
5001 static int getElementMoveStepsizeExt(int x, int y, int direction)
5002 {
5003   int element = Tile[x][y];
5004   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5005   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5006   int horiz_move = (dx != 0);
5007   int sign = (horiz_move ? dx : dy);
5008   int step = sign * element_info[element].move_stepsize;
5009
5010   // special values for move stepsize for spring and things on conveyor belt
5011   if (horiz_move)
5012   {
5013     if (CAN_FALL(element) &&
5014         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5015       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5016     else if (element == EL_SPRING)
5017       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5018   }
5019
5020   return step;
5021 }
5022
5023 static int getElementMoveStepsize(int x, int y)
5024 {
5025   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5026 }
5027
5028 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5029 {
5030   if (player->GfxAction != action || player->GfxDir != dir)
5031   {
5032     player->GfxAction = action;
5033     player->GfxDir = dir;
5034     player->Frame = 0;
5035     player->StepFrame = 0;
5036   }
5037 }
5038
5039 static void ResetGfxFrame(int x, int y)
5040 {
5041   // profiling showed that "autotest" spends 10~20% of its time in this function
5042   if (DrawingDeactivatedField())
5043     return;
5044
5045   int element = Tile[x][y];
5046   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5047
5048   if (graphic_info[graphic].anim_global_sync)
5049     GfxFrame[x][y] = FrameCounter;
5050   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5051     GfxFrame[x][y] = CustomValue[x][y];
5052   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5053     GfxFrame[x][y] = element_info[element].collect_score;
5054   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5055     GfxFrame[x][y] = ChangeDelay[x][y];
5056 }
5057
5058 static void ResetGfxAnimation(int x, int y)
5059 {
5060   GfxAction[x][y] = ACTION_DEFAULT;
5061   GfxDir[x][y] = MovDir[x][y];
5062   GfxFrame[x][y] = 0;
5063
5064   ResetGfxFrame(x, y);
5065 }
5066
5067 static void ResetRandomAnimationValue(int x, int y)
5068 {
5069   GfxRandom[x][y] = INIT_GFX_RANDOM();
5070 }
5071
5072 static void InitMovingField(int x, int y, int direction)
5073 {
5074   int element = Tile[x][y];
5075   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5076   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5077   int newx = x + dx;
5078   int newy = y + dy;
5079   boolean is_moving_before, is_moving_after;
5080
5081   // check if element was/is moving or being moved before/after mode change
5082   is_moving_before = (WasJustMoving[x][y] != 0);
5083   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5084
5085   // reset animation only for moving elements which change direction of moving
5086   // or which just started or stopped moving
5087   // (else CEs with property "can move" / "not moving" are reset each frame)
5088   if (is_moving_before != is_moving_after ||
5089       direction != MovDir[x][y])
5090     ResetGfxAnimation(x, y);
5091
5092   MovDir[x][y] = direction;
5093   GfxDir[x][y] = direction;
5094
5095   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5096                      direction == MV_DOWN && CAN_FALL(element) ?
5097                      ACTION_FALLING : ACTION_MOVING);
5098
5099   // this is needed for CEs with property "can move" / "not moving"
5100
5101   if (is_moving_after)
5102   {
5103     if (Tile[newx][newy] == EL_EMPTY)
5104       Tile[newx][newy] = EL_BLOCKED;
5105
5106     MovDir[newx][newy] = MovDir[x][y];
5107
5108     CustomValue[newx][newy] = CustomValue[x][y];
5109
5110     GfxFrame[newx][newy] = GfxFrame[x][y];
5111     GfxRandom[newx][newy] = GfxRandom[x][y];
5112     GfxAction[newx][newy] = GfxAction[x][y];
5113     GfxDir[newx][newy] = GfxDir[x][y];
5114   }
5115 }
5116
5117 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5118 {
5119   int direction = MovDir[x][y];
5120   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5121   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5122
5123   *goes_to_x = newx;
5124   *goes_to_y = newy;
5125 }
5126
5127 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5128 {
5129   int oldx = x, oldy = y;
5130   int direction = MovDir[x][y];
5131
5132   if (direction == MV_LEFT)
5133     oldx++;
5134   else if (direction == MV_RIGHT)
5135     oldx--;
5136   else if (direction == MV_UP)
5137     oldy++;
5138   else if (direction == MV_DOWN)
5139     oldy--;
5140
5141   *comes_from_x = oldx;
5142   *comes_from_y = oldy;
5143 }
5144
5145 static int MovingOrBlocked2Element(int x, int y)
5146 {
5147   int element = Tile[x][y];
5148
5149   if (element == EL_BLOCKED)
5150   {
5151     int oldx, oldy;
5152
5153     Blocked2Moving(x, y, &oldx, &oldy);
5154     return Tile[oldx][oldy];
5155   }
5156   else
5157     return element;
5158 }
5159
5160 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5161 {
5162   // like MovingOrBlocked2Element(), but if element is moving
5163   // and (x,y) is the field the moving element is just leaving,
5164   // return EL_BLOCKED instead of the element value
5165   int element = Tile[x][y];
5166
5167   if (IS_MOVING(x, y))
5168   {
5169     if (element == EL_BLOCKED)
5170     {
5171       int oldx, oldy;
5172
5173       Blocked2Moving(x, y, &oldx, &oldy);
5174       return Tile[oldx][oldy];
5175     }
5176     else
5177       return EL_BLOCKED;
5178   }
5179   else
5180     return element;
5181 }
5182
5183 static void RemoveField(int x, int y)
5184 {
5185   Tile[x][y] = EL_EMPTY;
5186
5187   MovPos[x][y] = 0;
5188   MovDir[x][y] = 0;
5189   MovDelay[x][y] = 0;
5190
5191   CustomValue[x][y] = 0;
5192
5193   AmoebaNr[x][y] = 0;
5194   ChangeDelay[x][y] = 0;
5195   ChangePage[x][y] = -1;
5196   Pushed[x][y] = FALSE;
5197
5198   GfxElement[x][y] = EL_UNDEFINED;
5199   GfxAction[x][y] = ACTION_DEFAULT;
5200   GfxDir[x][y] = MV_NONE;
5201 }
5202
5203 static void RemoveMovingField(int x, int y)
5204 {
5205   int oldx = x, oldy = y, newx = x, newy = y;
5206   int element = Tile[x][y];
5207   int next_element = EL_UNDEFINED;
5208
5209   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5210     return;
5211
5212   if (IS_MOVING(x, y))
5213   {
5214     Moving2Blocked(x, y, &newx, &newy);
5215
5216     if (Tile[newx][newy] != EL_BLOCKED)
5217     {
5218       // element is moving, but target field is not free (blocked), but
5219       // already occupied by something different (example: acid pool);
5220       // in this case, only remove the moving field, but not the target
5221
5222       RemoveField(oldx, oldy);
5223
5224       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5225
5226       TEST_DrawLevelField(oldx, oldy);
5227
5228       return;
5229     }
5230   }
5231   else if (element == EL_BLOCKED)
5232   {
5233     Blocked2Moving(x, y, &oldx, &oldy);
5234     if (!IS_MOVING(oldx, oldy))
5235       return;
5236   }
5237
5238   if (element == EL_BLOCKED &&
5239       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5240        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5241        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5242        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5243        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5244        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5245     next_element = get_next_element(Tile[oldx][oldy]);
5246
5247   RemoveField(oldx, oldy);
5248   RemoveField(newx, newy);
5249
5250   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5251
5252   if (next_element != EL_UNDEFINED)
5253     Tile[oldx][oldy] = next_element;
5254
5255   TEST_DrawLevelField(oldx, oldy);
5256   TEST_DrawLevelField(newx, newy);
5257 }
5258
5259 void DrawDynamite(int x, int y)
5260 {
5261   int sx = SCREENX(x), sy = SCREENY(y);
5262   int graphic = el2img(Tile[x][y]);
5263   int frame;
5264
5265   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5266     return;
5267
5268   if (IS_WALKABLE_INSIDE(Back[x][y]))
5269     return;
5270
5271   if (Back[x][y])
5272     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5273   else if (Store[x][y])
5274     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5275
5276   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5277
5278   if (Back[x][y] || Store[x][y])
5279     DrawGraphicThruMask(sx, sy, graphic, frame);
5280   else
5281     DrawGraphic(sx, sy, graphic, frame);
5282 }
5283
5284 static void CheckDynamite(int x, int y)
5285 {
5286   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5287   {
5288     MovDelay[x][y]--;
5289
5290     if (MovDelay[x][y] != 0)
5291     {
5292       DrawDynamite(x, y);
5293       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5294
5295       return;
5296     }
5297   }
5298
5299   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5300
5301   Bang(x, y);
5302 }
5303
5304 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5305 {
5306   boolean num_checked_players = 0;
5307   int i;
5308
5309   for (i = 0; i < MAX_PLAYERS; i++)
5310   {
5311     if (stored_player[i].active)
5312     {
5313       int sx = stored_player[i].jx;
5314       int sy = stored_player[i].jy;
5315
5316       if (num_checked_players == 0)
5317       {
5318         *sx1 = *sx2 = sx;
5319         *sy1 = *sy2 = sy;
5320       }
5321       else
5322       {
5323         *sx1 = MIN(*sx1, sx);
5324         *sy1 = MIN(*sy1, sy);
5325         *sx2 = MAX(*sx2, sx);
5326         *sy2 = MAX(*sy2, sy);
5327       }
5328
5329       num_checked_players++;
5330     }
5331   }
5332 }
5333
5334 static boolean checkIfAllPlayersFitToScreen_RND(void)
5335 {
5336   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5337
5338   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5339
5340   return (sx2 - sx1 < SCR_FIELDX &&
5341           sy2 - sy1 < SCR_FIELDY);
5342 }
5343
5344 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5345 {
5346   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5347
5348   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5349
5350   *sx = (sx1 + sx2) / 2;
5351   *sy = (sy1 + sy2) / 2;
5352 }
5353
5354 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5355                                boolean center_screen, boolean quick_relocation)
5356 {
5357   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5358   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5359   boolean no_delay = (tape.warp_forward);
5360   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5361   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5362   int new_scroll_x, new_scroll_y;
5363
5364   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5365   {
5366     // case 1: quick relocation inside visible screen (without scrolling)
5367
5368     RedrawPlayfield();
5369
5370     return;
5371   }
5372
5373   if (!level.shifted_relocation || center_screen)
5374   {
5375     // relocation _with_ centering of screen
5376
5377     new_scroll_x = SCROLL_POSITION_X(x);
5378     new_scroll_y = SCROLL_POSITION_Y(y);
5379   }
5380   else
5381   {
5382     // relocation _without_ centering of screen
5383
5384     int center_scroll_x = SCROLL_POSITION_X(old_x);
5385     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5386     int offset_x = x + (scroll_x - center_scroll_x);
5387     int offset_y = y + (scroll_y - center_scroll_y);
5388
5389     // for new screen position, apply previous offset to center position
5390     new_scroll_x = SCROLL_POSITION_X(offset_x);
5391     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5392   }
5393
5394   if (quick_relocation)
5395   {
5396     // case 2: quick relocation (redraw without visible scrolling)
5397
5398     scroll_x = new_scroll_x;
5399     scroll_y = new_scroll_y;
5400
5401     RedrawPlayfield();
5402
5403     return;
5404   }
5405
5406   // case 3: visible relocation (with scrolling to new position)
5407
5408   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5409
5410   SetVideoFrameDelay(wait_delay_value);
5411
5412   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5413   {
5414     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5415     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5416
5417     if (dx == 0 && dy == 0)             // no scrolling needed at all
5418       break;
5419
5420     scroll_x -= dx;
5421     scroll_y -= dy;
5422
5423     // set values for horizontal/vertical screen scrolling (half tile size)
5424     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5425     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5426     int pos_x = dx * TILEX / 2;
5427     int pos_y = dy * TILEY / 2;
5428     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5429     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5430
5431     ScrollLevel(dx, dy);
5432     DrawAllPlayers();
5433
5434     // scroll in two steps of half tile size to make things smoother
5435     BlitScreenToBitmapExt_RND(window, fx, fy);
5436
5437     // scroll second step to align at full tile size
5438     BlitScreenToBitmap(window);
5439   }
5440
5441   DrawAllPlayers();
5442   BackToFront();
5443
5444   SetVideoFrameDelay(frame_delay_value_old);
5445 }
5446
5447 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5448 {
5449   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5450   int player_nr = GET_PLAYER_NR(el_player);
5451   struct PlayerInfo *player = &stored_player[player_nr];
5452   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5453   boolean no_delay = (tape.warp_forward);
5454   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5455   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5456   int old_jx = player->jx;
5457   int old_jy = player->jy;
5458   int old_element = Tile[old_jx][old_jy];
5459   int element = Tile[jx][jy];
5460   boolean player_relocated = (old_jx != jx || old_jy != jy);
5461
5462   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5463   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5464   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5465   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5466   int leave_side_horiz = move_dir_horiz;
5467   int leave_side_vert  = move_dir_vert;
5468   int enter_side = enter_side_horiz | enter_side_vert;
5469   int leave_side = leave_side_horiz | leave_side_vert;
5470
5471   if (player->buried)           // do not reanimate dead player
5472     return;
5473
5474   if (!player_relocated)        // no need to relocate the player
5475     return;
5476
5477   if (IS_PLAYER(jx, jy))        // player already placed at new position
5478   {
5479     RemoveField(jx, jy);        // temporarily remove newly placed player
5480     DrawLevelField(jx, jy);
5481   }
5482
5483   if (player->present)
5484   {
5485     while (player->MovPos)
5486     {
5487       ScrollPlayer(player, SCROLL_GO_ON);
5488       ScrollScreen(NULL, SCROLL_GO_ON);
5489
5490       AdvanceFrameAndPlayerCounters(player->index_nr);
5491
5492       DrawPlayer(player);
5493
5494       BackToFront_WithFrameDelay(wait_delay_value);
5495     }
5496
5497     DrawPlayer(player);         // needed here only to cleanup last field
5498     DrawLevelField(player->jx, player->jy);     // remove player graphic
5499
5500     player->is_moving = FALSE;
5501   }
5502
5503   if (IS_CUSTOM_ELEMENT(old_element))
5504     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5505                                CE_LEFT_BY_PLAYER,
5506                                player->index_bit, leave_side);
5507
5508   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5509                                       CE_PLAYER_LEAVES_X,
5510                                       player->index_bit, leave_side);
5511
5512   Tile[jx][jy] = el_player;
5513   InitPlayerField(jx, jy, el_player, TRUE);
5514
5515   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5516      possible that the relocation target field did not contain a player element,
5517      but a walkable element, to which the new player was relocated -- in this
5518      case, restore that (already initialized!) element on the player field */
5519   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5520   {
5521     Tile[jx][jy] = element;     // restore previously existing element
5522   }
5523
5524   // only visually relocate centered player
5525   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5526                      FALSE, level.instant_relocation);
5527
5528   TestIfPlayerTouchesBadThing(jx, jy);
5529   TestIfPlayerTouchesCustomElement(jx, jy);
5530
5531   if (IS_CUSTOM_ELEMENT(element))
5532     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5533                                player->index_bit, enter_side);
5534
5535   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5536                                       player->index_bit, enter_side);
5537
5538   if (player->is_switching)
5539   {
5540     /* ensure that relocation while still switching an element does not cause
5541        a new element to be treated as also switched directly after relocation
5542        (this is important for teleporter switches that teleport the player to
5543        a place where another teleporter switch is in the same direction, which
5544        would then incorrectly be treated as immediately switched before the
5545        direction key that caused the switch was released) */
5546
5547     player->switch_x += jx - old_jx;
5548     player->switch_y += jy - old_jy;
5549   }
5550 }
5551
5552 static void Explode(int ex, int ey, int phase, int mode)
5553 {
5554   int x, y;
5555   int last_phase;
5556   int border_element;
5557
5558   // !!! eliminate this variable !!!
5559   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5560
5561   if (game.explosions_delayed)
5562   {
5563     ExplodeField[ex][ey] = mode;
5564     return;
5565   }
5566
5567   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5568   {
5569     int center_element = Tile[ex][ey];
5570     int artwork_element, explosion_element;     // set these values later
5571
5572     // remove things displayed in background while burning dynamite
5573     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5574       Back[ex][ey] = 0;
5575
5576     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5577     {
5578       // put moving element to center field (and let it explode there)
5579       center_element = MovingOrBlocked2Element(ex, ey);
5580       RemoveMovingField(ex, ey);
5581       Tile[ex][ey] = center_element;
5582     }
5583
5584     // now "center_element" is finally determined -- set related values now
5585     artwork_element = center_element;           // for custom player artwork
5586     explosion_element = center_element;         // for custom player artwork
5587
5588     if (IS_PLAYER(ex, ey))
5589     {
5590       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5591
5592       artwork_element = stored_player[player_nr].artwork_element;
5593
5594       if (level.use_explosion_element[player_nr])
5595       {
5596         explosion_element = level.explosion_element[player_nr];
5597         artwork_element = explosion_element;
5598       }
5599     }
5600
5601     if (mode == EX_TYPE_NORMAL ||
5602         mode == EX_TYPE_CENTER ||
5603         mode == EX_TYPE_CROSS)
5604       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5605
5606     last_phase = element_info[explosion_element].explosion_delay + 1;
5607
5608     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5609     {
5610       int xx = x - ex + 1;
5611       int yy = y - ey + 1;
5612       int element;
5613
5614       if (!IN_LEV_FIELD(x, y) ||
5615           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5616           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5617         continue;
5618
5619       element = Tile[x][y];
5620
5621       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5622       {
5623         element = MovingOrBlocked2Element(x, y);
5624
5625         if (!IS_EXPLOSION_PROOF(element))
5626           RemoveMovingField(x, y);
5627       }
5628
5629       // indestructible elements can only explode in center (but not flames)
5630       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5631                                            mode == EX_TYPE_BORDER)) ||
5632           element == EL_FLAMES)
5633         continue;
5634
5635       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5636          behaviour, for example when touching a yamyam that explodes to rocks
5637          with active deadly shield, a rock is created under the player !!! */
5638       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5639 #if 0
5640       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5641           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5642            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5643 #else
5644       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5645 #endif
5646       {
5647         if (IS_ACTIVE_BOMB(element))
5648         {
5649           // re-activate things under the bomb like gate or penguin
5650           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5651           Back[x][y] = 0;
5652         }
5653
5654         continue;
5655       }
5656
5657       // save walkable background elements while explosion on same tile
5658       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5659           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5660         Back[x][y] = element;
5661
5662       // ignite explodable elements reached by other explosion
5663       if (element == EL_EXPLOSION)
5664         element = Store2[x][y];
5665
5666       if (AmoebaNr[x][y] &&
5667           (element == EL_AMOEBA_FULL ||
5668            element == EL_BD_AMOEBA ||
5669            element == EL_AMOEBA_GROWING))
5670       {
5671         AmoebaCnt[AmoebaNr[x][y]]--;
5672         AmoebaCnt2[AmoebaNr[x][y]]--;
5673       }
5674
5675       RemoveField(x, y);
5676
5677       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5678       {
5679         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5680
5681         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5682
5683         if (PLAYERINFO(ex, ey)->use_murphy)
5684           Store[x][y] = EL_EMPTY;
5685       }
5686
5687       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5688       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5689       else if (ELEM_IS_PLAYER(center_element))
5690         Store[x][y] = EL_EMPTY;
5691       else if (center_element == EL_YAMYAM)
5692         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5693       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5694         Store[x][y] = element_info[center_element].content.e[xx][yy];
5695 #if 1
5696       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5697       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5698       // otherwise) -- FIX THIS !!!
5699       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5700         Store[x][y] = element_info[element].content.e[1][1];
5701 #else
5702       else if (!CAN_EXPLODE(element))
5703         Store[x][y] = element_info[element].content.e[1][1];
5704 #endif
5705       else
5706         Store[x][y] = EL_EMPTY;
5707
5708       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5709           center_element == EL_AMOEBA_TO_DIAMOND)
5710         Store2[x][y] = element;
5711
5712       Tile[x][y] = EL_EXPLOSION;
5713       GfxElement[x][y] = artwork_element;
5714
5715       ExplodePhase[x][y] = 1;
5716       ExplodeDelay[x][y] = last_phase;
5717
5718       Stop[x][y] = TRUE;
5719     }
5720
5721     if (center_element == EL_YAMYAM)
5722       game.yamyam_content_nr =
5723         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5724
5725     return;
5726   }
5727
5728   if (Stop[ex][ey])
5729     return;
5730
5731   x = ex;
5732   y = ey;
5733
5734   if (phase == 1)
5735     GfxFrame[x][y] = 0;         // restart explosion animation
5736
5737   last_phase = ExplodeDelay[x][y];
5738
5739   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5740
5741   // this can happen if the player leaves an explosion just in time
5742   if (GfxElement[x][y] == EL_UNDEFINED)
5743     GfxElement[x][y] = EL_EMPTY;
5744
5745   border_element = Store2[x][y];
5746   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5747     border_element = StorePlayer[x][y];
5748
5749   if (phase == element_info[border_element].ignition_delay ||
5750       phase == last_phase)
5751   {
5752     boolean border_explosion = FALSE;
5753
5754     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5755         !PLAYER_EXPLOSION_PROTECTED(x, y))
5756     {
5757       KillPlayerUnlessExplosionProtected(x, y);
5758       border_explosion = TRUE;
5759     }
5760     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5761     {
5762       Tile[x][y] = Store2[x][y];
5763       Store2[x][y] = 0;
5764       Bang(x, y);
5765       border_explosion = TRUE;
5766     }
5767     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5768     {
5769       AmoebaToDiamond(x, y);
5770       Store2[x][y] = 0;
5771       border_explosion = TRUE;
5772     }
5773
5774     // if an element just explodes due to another explosion (chain-reaction),
5775     // do not immediately end the new explosion when it was the last frame of
5776     // the explosion (as it would be done in the following "if"-statement!)
5777     if (border_explosion && phase == last_phase)
5778       return;
5779   }
5780
5781   if (phase == last_phase)
5782   {
5783     int element;
5784
5785     element = Tile[x][y] = Store[x][y];
5786     Store[x][y] = Store2[x][y] = 0;
5787     GfxElement[x][y] = EL_UNDEFINED;
5788
5789     // player can escape from explosions and might therefore be still alive
5790     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5791         element <= EL_PLAYER_IS_EXPLODING_4)
5792     {
5793       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5794       int explosion_element = EL_PLAYER_1 + player_nr;
5795       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5796       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5797
5798       if (level.use_explosion_element[player_nr])
5799         explosion_element = level.explosion_element[player_nr];
5800
5801       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5802                     element_info[explosion_element].content.e[xx][yy]);
5803     }
5804
5805     // restore probably existing indestructible background element
5806     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5807       element = Tile[x][y] = Back[x][y];
5808     Back[x][y] = 0;
5809
5810     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5811     GfxDir[x][y] = MV_NONE;
5812     ChangeDelay[x][y] = 0;
5813     ChangePage[x][y] = -1;
5814
5815     CustomValue[x][y] = 0;
5816
5817     InitField_WithBug2(x, y, FALSE);
5818
5819     TEST_DrawLevelField(x, y);
5820
5821     TestIfElementTouchesCustomElement(x, y);
5822
5823     if (GFX_CRUMBLED(element))
5824       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5825
5826     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5827       StorePlayer[x][y] = 0;
5828
5829     if (ELEM_IS_PLAYER(element))
5830       RelocatePlayer(x, y, element);
5831   }
5832   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5833   {
5834     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5835     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5836
5837     if (phase == delay)
5838       TEST_DrawLevelFieldCrumbled(x, y);
5839
5840     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5841     {
5842       DrawLevelElement(x, y, Back[x][y]);
5843       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5844     }
5845     else if (IS_WALKABLE_UNDER(Back[x][y]))
5846     {
5847       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5848       DrawLevelElementThruMask(x, y, Back[x][y]);
5849     }
5850     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5851       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5852   }
5853 }
5854
5855 static void DynaExplode(int ex, int ey)
5856 {
5857   int i, j;
5858   int dynabomb_element = Tile[ex][ey];
5859   int dynabomb_size = 1;
5860   boolean dynabomb_xl = FALSE;
5861   struct PlayerInfo *player;
5862   static int xy[4][2] =
5863   {
5864     { 0, -1 },
5865     { -1, 0 },
5866     { +1, 0 },
5867     { 0, +1 }
5868   };
5869
5870   if (IS_ACTIVE_BOMB(dynabomb_element))
5871   {
5872     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5873     dynabomb_size = player->dynabomb_size;
5874     dynabomb_xl = player->dynabomb_xl;
5875     player->dynabombs_left++;
5876   }
5877
5878   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5879
5880   for (i = 0; i < NUM_DIRECTIONS; i++)
5881   {
5882     for (j = 1; j <= dynabomb_size; j++)
5883     {
5884       int x = ex + j * xy[i][0];
5885       int y = ey + j * xy[i][1];
5886       int element;
5887
5888       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5889         break;
5890
5891       element = Tile[x][y];
5892
5893       // do not restart explosions of fields with active bombs
5894       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5895         continue;
5896
5897       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5898
5899       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5900           !IS_DIGGABLE(element) && !dynabomb_xl)
5901         break;
5902     }
5903   }
5904 }
5905
5906 void Bang(int x, int y)
5907 {
5908   int element = MovingOrBlocked2Element(x, y);
5909   int explosion_type = EX_TYPE_NORMAL;
5910
5911   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5912   {
5913     struct PlayerInfo *player = PLAYERINFO(x, y);
5914
5915     element = Tile[x][y] = player->initial_element;
5916
5917     if (level.use_explosion_element[player->index_nr])
5918     {
5919       int explosion_element = level.explosion_element[player->index_nr];
5920
5921       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5922         explosion_type = EX_TYPE_CROSS;
5923       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5924         explosion_type = EX_TYPE_CENTER;
5925     }
5926   }
5927
5928   switch (element)
5929   {
5930     case EL_BUG:
5931     case EL_SPACESHIP:
5932     case EL_BD_BUTTERFLY:
5933     case EL_BD_FIREFLY:
5934     case EL_YAMYAM:
5935     case EL_DARK_YAMYAM:
5936     case EL_ROBOT:
5937     case EL_PACMAN:
5938     case EL_MOLE:
5939       RaiseScoreElement(element);
5940       break;
5941
5942     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5943     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5944     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5945     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5946     case EL_DYNABOMB_INCREASE_NUMBER:
5947     case EL_DYNABOMB_INCREASE_SIZE:
5948     case EL_DYNABOMB_INCREASE_POWER:
5949       explosion_type = EX_TYPE_DYNA;
5950       break;
5951
5952     case EL_DC_LANDMINE:
5953       explosion_type = EX_TYPE_CENTER;
5954       break;
5955
5956     case EL_PENGUIN:
5957     case EL_LAMP:
5958     case EL_LAMP_ACTIVE:
5959     case EL_AMOEBA_TO_DIAMOND:
5960       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5961         explosion_type = EX_TYPE_CENTER;
5962       break;
5963
5964     default:
5965       if (element_info[element].explosion_type == EXPLODES_CROSS)
5966         explosion_type = EX_TYPE_CROSS;
5967       else if (element_info[element].explosion_type == EXPLODES_1X1)
5968         explosion_type = EX_TYPE_CENTER;
5969       break;
5970   }
5971
5972   if (explosion_type == EX_TYPE_DYNA)
5973     DynaExplode(x, y);
5974   else
5975     Explode(x, y, EX_PHASE_START, explosion_type);
5976
5977   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5978 }
5979
5980 static void SplashAcid(int x, int y)
5981 {
5982   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5983       (!IN_LEV_FIELD(x - 1, y - 2) ||
5984        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5985     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5986
5987   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5988       (!IN_LEV_FIELD(x + 1, y - 2) ||
5989        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5990     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5991
5992   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5993 }
5994
5995 static void InitBeltMovement(void)
5996 {
5997   static int belt_base_element[4] =
5998   {
5999     EL_CONVEYOR_BELT_1_LEFT,
6000     EL_CONVEYOR_BELT_2_LEFT,
6001     EL_CONVEYOR_BELT_3_LEFT,
6002     EL_CONVEYOR_BELT_4_LEFT
6003   };
6004   static int belt_base_active_element[4] =
6005   {
6006     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6007     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6008     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6009     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6010   };
6011
6012   int x, y, i, j;
6013
6014   // set frame order for belt animation graphic according to belt direction
6015   for (i = 0; i < NUM_BELTS; i++)
6016   {
6017     int belt_nr = i;
6018
6019     for (j = 0; j < NUM_BELT_PARTS; j++)
6020     {
6021       int element = belt_base_active_element[belt_nr] + j;
6022       int graphic_1 = el2img(element);
6023       int graphic_2 = el2panelimg(element);
6024
6025       if (game.belt_dir[i] == MV_LEFT)
6026       {
6027         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6028         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6029       }
6030       else
6031       {
6032         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6033         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6034       }
6035     }
6036   }
6037
6038   SCAN_PLAYFIELD(x, y)
6039   {
6040     int element = Tile[x][y];
6041
6042     for (i = 0; i < NUM_BELTS; i++)
6043     {
6044       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6045       {
6046         int e_belt_nr = getBeltNrFromBeltElement(element);
6047         int belt_nr = i;
6048
6049         if (e_belt_nr == belt_nr)
6050         {
6051           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6052
6053           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6054         }
6055       }
6056     }
6057   }
6058 }
6059
6060 static void ToggleBeltSwitch(int x, int y)
6061 {
6062   static int belt_base_element[4] =
6063   {
6064     EL_CONVEYOR_BELT_1_LEFT,
6065     EL_CONVEYOR_BELT_2_LEFT,
6066     EL_CONVEYOR_BELT_3_LEFT,
6067     EL_CONVEYOR_BELT_4_LEFT
6068   };
6069   static int belt_base_active_element[4] =
6070   {
6071     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6072     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6073     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6074     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6075   };
6076   static int belt_base_switch_element[4] =
6077   {
6078     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6079     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6080     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6081     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6082   };
6083   static int belt_move_dir[4] =
6084   {
6085     MV_LEFT,
6086     MV_NONE,
6087     MV_RIGHT,
6088     MV_NONE,
6089   };
6090
6091   int element = Tile[x][y];
6092   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6093   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6094   int belt_dir = belt_move_dir[belt_dir_nr];
6095   int xx, yy, i;
6096
6097   if (!IS_BELT_SWITCH(element))
6098     return;
6099
6100   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6101   game.belt_dir[belt_nr] = belt_dir;
6102
6103   if (belt_dir_nr == 3)
6104     belt_dir_nr = 1;
6105
6106   // set frame order for belt animation graphic according to belt direction
6107   for (i = 0; i < NUM_BELT_PARTS; i++)
6108   {
6109     int element = belt_base_active_element[belt_nr] + i;
6110     int graphic_1 = el2img(element);
6111     int graphic_2 = el2panelimg(element);
6112
6113     if (belt_dir == MV_LEFT)
6114     {
6115       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6116       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6117     }
6118     else
6119     {
6120       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6121       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6122     }
6123   }
6124
6125   SCAN_PLAYFIELD(xx, yy)
6126   {
6127     int element = Tile[xx][yy];
6128
6129     if (IS_BELT_SWITCH(element))
6130     {
6131       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6132
6133       if (e_belt_nr == belt_nr)
6134       {
6135         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6136         TEST_DrawLevelField(xx, yy);
6137       }
6138     }
6139     else if (IS_BELT(element) && belt_dir != MV_NONE)
6140     {
6141       int e_belt_nr = getBeltNrFromBeltElement(element);
6142
6143       if (e_belt_nr == belt_nr)
6144       {
6145         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6146
6147         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6148         TEST_DrawLevelField(xx, yy);
6149       }
6150     }
6151     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6152     {
6153       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6154
6155       if (e_belt_nr == belt_nr)
6156       {
6157         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6158
6159         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6160         TEST_DrawLevelField(xx, yy);
6161       }
6162     }
6163   }
6164 }
6165
6166 static void ToggleSwitchgateSwitch(int x, int y)
6167 {
6168   int xx, yy;
6169
6170   game.switchgate_pos = !game.switchgate_pos;
6171
6172   SCAN_PLAYFIELD(xx, yy)
6173   {
6174     int element = Tile[xx][yy];
6175
6176     if (element == EL_SWITCHGATE_SWITCH_UP)
6177     {
6178       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6179       TEST_DrawLevelField(xx, yy);
6180     }
6181     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6182     {
6183       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6184       TEST_DrawLevelField(xx, yy);
6185     }
6186     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6187     {
6188       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6189       TEST_DrawLevelField(xx, yy);
6190     }
6191     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6192     {
6193       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6194       TEST_DrawLevelField(xx, yy);
6195     }
6196     else if (element == EL_SWITCHGATE_OPEN ||
6197              element == EL_SWITCHGATE_OPENING)
6198     {
6199       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6200
6201       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6202     }
6203     else if (element == EL_SWITCHGATE_CLOSED ||
6204              element == EL_SWITCHGATE_CLOSING)
6205     {
6206       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6207
6208       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6209     }
6210   }
6211 }
6212
6213 static int getInvisibleActiveFromInvisibleElement(int element)
6214 {
6215   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6216           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6217           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6218           element);
6219 }
6220
6221 static int getInvisibleFromInvisibleActiveElement(int element)
6222 {
6223   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6224           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6225           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6226           element);
6227 }
6228
6229 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6230 {
6231   int x, y;
6232
6233   SCAN_PLAYFIELD(x, y)
6234   {
6235     int element = Tile[x][y];
6236
6237     if (element == EL_LIGHT_SWITCH &&
6238         game.light_time_left > 0)
6239     {
6240       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6241       TEST_DrawLevelField(x, y);
6242     }
6243     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6244              game.light_time_left == 0)
6245     {
6246       Tile[x][y] = EL_LIGHT_SWITCH;
6247       TEST_DrawLevelField(x, y);
6248     }
6249     else if (element == EL_EMC_DRIPPER &&
6250              game.light_time_left > 0)
6251     {
6252       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6253       TEST_DrawLevelField(x, y);
6254     }
6255     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6256              game.light_time_left == 0)
6257     {
6258       Tile[x][y] = EL_EMC_DRIPPER;
6259       TEST_DrawLevelField(x, y);
6260     }
6261     else if (element == EL_INVISIBLE_STEELWALL ||
6262              element == EL_INVISIBLE_WALL ||
6263              element == EL_INVISIBLE_SAND)
6264     {
6265       if (game.light_time_left > 0)
6266         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6267
6268       TEST_DrawLevelField(x, y);
6269
6270       // uncrumble neighbour fields, if needed
6271       if (element == EL_INVISIBLE_SAND)
6272         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6273     }
6274     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6275              element == EL_INVISIBLE_WALL_ACTIVE ||
6276              element == EL_INVISIBLE_SAND_ACTIVE)
6277     {
6278       if (game.light_time_left == 0)
6279         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6280
6281       TEST_DrawLevelField(x, y);
6282
6283       // re-crumble neighbour fields, if needed
6284       if (element == EL_INVISIBLE_SAND)
6285         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6286     }
6287   }
6288 }
6289
6290 static void RedrawAllInvisibleElementsForLenses(void)
6291 {
6292   int x, y;
6293
6294   SCAN_PLAYFIELD(x, y)
6295   {
6296     int element = Tile[x][y];
6297
6298     if (element == EL_EMC_DRIPPER &&
6299         game.lenses_time_left > 0)
6300     {
6301       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6302       TEST_DrawLevelField(x, y);
6303     }
6304     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6305              game.lenses_time_left == 0)
6306     {
6307       Tile[x][y] = EL_EMC_DRIPPER;
6308       TEST_DrawLevelField(x, y);
6309     }
6310     else if (element == EL_INVISIBLE_STEELWALL ||
6311              element == EL_INVISIBLE_WALL ||
6312              element == EL_INVISIBLE_SAND)
6313     {
6314       if (game.lenses_time_left > 0)
6315         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6316
6317       TEST_DrawLevelField(x, y);
6318
6319       // uncrumble neighbour fields, if needed
6320       if (element == EL_INVISIBLE_SAND)
6321         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6322     }
6323     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6324              element == EL_INVISIBLE_WALL_ACTIVE ||
6325              element == EL_INVISIBLE_SAND_ACTIVE)
6326     {
6327       if (game.lenses_time_left == 0)
6328         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6329
6330       TEST_DrawLevelField(x, y);
6331
6332       // re-crumble neighbour fields, if needed
6333       if (element == EL_INVISIBLE_SAND)
6334         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6335     }
6336   }
6337 }
6338
6339 static void RedrawAllInvisibleElementsForMagnifier(void)
6340 {
6341   int x, y;
6342
6343   SCAN_PLAYFIELD(x, y)
6344   {
6345     int element = Tile[x][y];
6346
6347     if (element == EL_EMC_FAKE_GRASS &&
6348         game.magnify_time_left > 0)
6349     {
6350       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6351       TEST_DrawLevelField(x, y);
6352     }
6353     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6354              game.magnify_time_left == 0)
6355     {
6356       Tile[x][y] = EL_EMC_FAKE_GRASS;
6357       TEST_DrawLevelField(x, y);
6358     }
6359     else if (IS_GATE_GRAY(element) &&
6360              game.magnify_time_left > 0)
6361     {
6362       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6363                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6364                     IS_EM_GATE_GRAY(element) ?
6365                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6366                     IS_EMC_GATE_GRAY(element) ?
6367                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6368                     IS_DC_GATE_GRAY(element) ?
6369                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6370                     element);
6371       TEST_DrawLevelField(x, y);
6372     }
6373     else if (IS_GATE_GRAY_ACTIVE(element) &&
6374              game.magnify_time_left == 0)
6375     {
6376       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6377                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6378                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6379                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6380                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6381                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6382                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6383                     EL_DC_GATE_WHITE_GRAY :
6384                     element);
6385       TEST_DrawLevelField(x, y);
6386     }
6387   }
6388 }
6389
6390 static void ToggleLightSwitch(int x, int y)
6391 {
6392   int element = Tile[x][y];
6393
6394   game.light_time_left =
6395     (element == EL_LIGHT_SWITCH ?
6396      level.time_light * FRAMES_PER_SECOND : 0);
6397
6398   RedrawAllLightSwitchesAndInvisibleElements();
6399 }
6400
6401 static void ActivateTimegateSwitch(int x, int y)
6402 {
6403   int xx, yy;
6404
6405   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6406
6407   SCAN_PLAYFIELD(xx, yy)
6408   {
6409     int element = Tile[xx][yy];
6410
6411     if (element == EL_TIMEGATE_CLOSED ||
6412         element == EL_TIMEGATE_CLOSING)
6413     {
6414       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6415       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6416     }
6417
6418     /*
6419     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6420     {
6421       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6422       TEST_DrawLevelField(xx, yy);
6423     }
6424     */
6425
6426   }
6427
6428   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6429                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6430 }
6431
6432 static void Impact(int x, int y)
6433 {
6434   boolean last_line = (y == lev_fieldy - 1);
6435   boolean object_hit = FALSE;
6436   boolean impact = (last_line || object_hit);
6437   int element = Tile[x][y];
6438   int smashed = EL_STEELWALL;
6439
6440   if (!last_line)       // check if element below was hit
6441   {
6442     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6443       return;
6444
6445     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6446                                          MovDir[x][y + 1] != MV_DOWN ||
6447                                          MovPos[x][y + 1] <= TILEY / 2));
6448
6449     // do not smash moving elements that left the smashed field in time
6450     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6451         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6452       object_hit = FALSE;
6453
6454 #if USE_QUICKSAND_IMPACT_BUGFIX
6455     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6456     {
6457       RemoveMovingField(x, y + 1);
6458       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6459       Tile[x][y + 2] = EL_ROCK;
6460       TEST_DrawLevelField(x, y + 2);
6461
6462       object_hit = TRUE;
6463     }
6464
6465     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6466     {
6467       RemoveMovingField(x, y + 1);
6468       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6469       Tile[x][y + 2] = EL_ROCK;
6470       TEST_DrawLevelField(x, y + 2);
6471
6472       object_hit = TRUE;
6473     }
6474 #endif
6475
6476     if (object_hit)
6477       smashed = MovingOrBlocked2Element(x, y + 1);
6478
6479     impact = (last_line || object_hit);
6480   }
6481
6482   if (!last_line && smashed == EL_ACID) // element falls into acid
6483   {
6484     SplashAcid(x, y + 1);
6485     return;
6486   }
6487
6488   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6489   // only reset graphic animation if graphic really changes after impact
6490   if (impact &&
6491       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6492   {
6493     ResetGfxAnimation(x, y);
6494     TEST_DrawLevelField(x, y);
6495   }
6496
6497   if (impact && CAN_EXPLODE_IMPACT(element))
6498   {
6499     Bang(x, y);
6500     return;
6501   }
6502   else if (impact && element == EL_PEARL &&
6503            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6504   {
6505     ResetGfxAnimation(x, y);
6506
6507     Tile[x][y] = EL_PEARL_BREAKING;
6508     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6509     return;
6510   }
6511   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6512   {
6513     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6514
6515     return;
6516   }
6517
6518   if (impact && element == EL_AMOEBA_DROP)
6519   {
6520     if (object_hit && IS_PLAYER(x, y + 1))
6521       KillPlayerUnlessEnemyProtected(x, y + 1);
6522     else if (object_hit && smashed == EL_PENGUIN)
6523       Bang(x, y + 1);
6524     else
6525     {
6526       Tile[x][y] = EL_AMOEBA_GROWING;
6527       Store[x][y] = EL_AMOEBA_WET;
6528
6529       ResetRandomAnimationValue(x, y);
6530     }
6531     return;
6532   }
6533
6534   if (object_hit)               // check which object was hit
6535   {
6536     if ((CAN_PASS_MAGIC_WALL(element) && 
6537          (smashed == EL_MAGIC_WALL ||
6538           smashed == EL_BD_MAGIC_WALL)) ||
6539         (CAN_PASS_DC_MAGIC_WALL(element) &&
6540          smashed == EL_DC_MAGIC_WALL))
6541     {
6542       int xx, yy;
6543       int activated_magic_wall =
6544         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6545          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6546          EL_DC_MAGIC_WALL_ACTIVE);
6547
6548       // activate magic wall / mill
6549       SCAN_PLAYFIELD(xx, yy)
6550       {
6551         if (Tile[xx][yy] == smashed)
6552           Tile[xx][yy] = activated_magic_wall;
6553       }
6554
6555       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6556       game.magic_wall_active = TRUE;
6557
6558       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6559                             SND_MAGIC_WALL_ACTIVATING :
6560                             smashed == EL_BD_MAGIC_WALL ?
6561                             SND_BD_MAGIC_WALL_ACTIVATING :
6562                             SND_DC_MAGIC_WALL_ACTIVATING));
6563     }
6564
6565     if (IS_PLAYER(x, y + 1))
6566     {
6567       if (CAN_SMASH_PLAYER(element))
6568       {
6569         KillPlayerUnlessEnemyProtected(x, y + 1);
6570         return;
6571       }
6572     }
6573     else if (smashed == EL_PENGUIN)
6574     {
6575       if (CAN_SMASH_PLAYER(element))
6576       {
6577         Bang(x, y + 1);
6578         return;
6579       }
6580     }
6581     else if (element == EL_BD_DIAMOND)
6582     {
6583       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6584       {
6585         Bang(x, y + 1);
6586         return;
6587       }
6588     }
6589     else if (((element == EL_SP_INFOTRON ||
6590                element == EL_SP_ZONK) &&
6591               (smashed == EL_SP_SNIKSNAK ||
6592                smashed == EL_SP_ELECTRON ||
6593                smashed == EL_SP_DISK_ORANGE)) ||
6594              (element == EL_SP_INFOTRON &&
6595               smashed == EL_SP_DISK_YELLOW))
6596     {
6597       Bang(x, y + 1);
6598       return;
6599     }
6600     else if (CAN_SMASH_EVERYTHING(element))
6601     {
6602       if (IS_CLASSIC_ENEMY(smashed) ||
6603           CAN_EXPLODE_SMASHED(smashed))
6604       {
6605         Bang(x, y + 1);
6606         return;
6607       }
6608       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6609       {
6610         if (smashed == EL_LAMP ||
6611             smashed == EL_LAMP_ACTIVE)
6612         {
6613           Bang(x, y + 1);
6614           return;
6615         }
6616         else if (smashed == EL_NUT)
6617         {
6618           Tile[x][y + 1] = EL_NUT_BREAKING;
6619           PlayLevelSound(x, y, SND_NUT_BREAKING);
6620           RaiseScoreElement(EL_NUT);
6621           return;
6622         }
6623         else if (smashed == EL_PEARL)
6624         {
6625           ResetGfxAnimation(x, y);
6626
6627           Tile[x][y + 1] = EL_PEARL_BREAKING;
6628           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6629           return;
6630         }
6631         else if (smashed == EL_DIAMOND)
6632         {
6633           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6634           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6635           return;
6636         }
6637         else if (IS_BELT_SWITCH(smashed))
6638         {
6639           ToggleBeltSwitch(x, y + 1);
6640         }
6641         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6642                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6643                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6644                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6645         {
6646           ToggleSwitchgateSwitch(x, y + 1);
6647         }
6648         else if (smashed == EL_LIGHT_SWITCH ||
6649                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6650         {
6651           ToggleLightSwitch(x, y + 1);
6652         }
6653         else
6654         {
6655           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6656
6657           CheckElementChangeBySide(x, y + 1, smashed, element,
6658                                    CE_SWITCHED, CH_SIDE_TOP);
6659           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6660                                             CH_SIDE_TOP);
6661         }
6662       }
6663       else
6664       {
6665         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6666       }
6667     }
6668   }
6669
6670   // play sound of magic wall / mill
6671   if (!last_line &&
6672       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6673        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6674        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6675   {
6676     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6677       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6678     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6679       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6680     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6681       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6682
6683     return;
6684   }
6685
6686   // play sound of object that hits the ground
6687   if (last_line || object_hit)
6688     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6689 }
6690
6691 static void TurnRoundExt(int x, int y)
6692 {
6693   static struct
6694   {
6695     int dx, dy;
6696   } move_xy[] =
6697   {
6698     {  0,  0 },
6699     { -1,  0 },
6700     { +1,  0 },
6701     {  0,  0 },
6702     {  0, -1 },
6703     {  0,  0 }, { 0, 0 }, { 0, 0 },
6704     {  0, +1 }
6705   };
6706   static struct
6707   {
6708     int left, right, back;
6709   } turn[] =
6710   {
6711     { 0,        0,              0        },
6712     { MV_DOWN,  MV_UP,          MV_RIGHT },
6713     { MV_UP,    MV_DOWN,        MV_LEFT  },
6714     { 0,        0,              0        },
6715     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6716     { 0,        0,              0        },
6717     { 0,        0,              0        },
6718     { 0,        0,              0        },
6719     { MV_RIGHT, MV_LEFT,        MV_UP    }
6720   };
6721
6722   int element = Tile[x][y];
6723   int move_pattern = element_info[element].move_pattern;
6724
6725   int old_move_dir = MovDir[x][y];
6726   int left_dir  = turn[old_move_dir].left;
6727   int right_dir = turn[old_move_dir].right;
6728   int back_dir  = turn[old_move_dir].back;
6729
6730   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6731   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6732   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6733   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6734
6735   int left_x  = x + left_dx,  left_y  = y + left_dy;
6736   int right_x = x + right_dx, right_y = y + right_dy;
6737   int move_x  = x + move_dx,  move_y  = y + move_dy;
6738
6739   int xx, yy;
6740
6741   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6742   {
6743     TestIfBadThingTouchesOtherBadThing(x, y);
6744
6745     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6746       MovDir[x][y] = right_dir;
6747     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6748       MovDir[x][y] = left_dir;
6749
6750     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6751       MovDelay[x][y] = 9;
6752     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6753       MovDelay[x][y] = 1;
6754   }
6755   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6756   {
6757     TestIfBadThingTouchesOtherBadThing(x, y);
6758
6759     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6760       MovDir[x][y] = left_dir;
6761     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6762       MovDir[x][y] = right_dir;
6763
6764     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6765       MovDelay[x][y] = 9;
6766     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6767       MovDelay[x][y] = 1;
6768   }
6769   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6770   {
6771     TestIfBadThingTouchesOtherBadThing(x, y);
6772
6773     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6774       MovDir[x][y] = left_dir;
6775     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6776       MovDir[x][y] = right_dir;
6777
6778     if (MovDir[x][y] != old_move_dir)
6779       MovDelay[x][y] = 9;
6780   }
6781   else if (element == EL_YAMYAM)
6782   {
6783     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6784     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6785
6786     if (can_turn_left && can_turn_right)
6787       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6788     else if (can_turn_left)
6789       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6790     else if (can_turn_right)
6791       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6792     else
6793       MovDir[x][y] = back_dir;
6794
6795     MovDelay[x][y] = 16 + 16 * RND(3);
6796   }
6797   else if (element == EL_DARK_YAMYAM)
6798   {
6799     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6800                                                          left_x, left_y);
6801     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6802                                                          right_x, right_y);
6803
6804     if (can_turn_left && can_turn_right)
6805       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6806     else if (can_turn_left)
6807       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6808     else if (can_turn_right)
6809       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6810     else
6811       MovDir[x][y] = back_dir;
6812
6813     MovDelay[x][y] = 16 + 16 * RND(3);
6814   }
6815   else if (element == EL_PACMAN)
6816   {
6817     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6818     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6819
6820     if (can_turn_left && can_turn_right)
6821       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6822     else if (can_turn_left)
6823       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6824     else if (can_turn_right)
6825       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6826     else
6827       MovDir[x][y] = back_dir;
6828
6829     MovDelay[x][y] = 6 + RND(40);
6830   }
6831   else if (element == EL_PIG)
6832   {
6833     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6834     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6835     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6836     boolean should_turn_left, should_turn_right, should_move_on;
6837     int rnd_value = 24;
6838     int rnd = RND(rnd_value);
6839
6840     should_turn_left = (can_turn_left &&
6841                         (!can_move_on ||
6842                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6843                                                    y + back_dy + left_dy)));
6844     should_turn_right = (can_turn_right &&
6845                          (!can_move_on ||
6846                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6847                                                     y + back_dy + right_dy)));
6848     should_move_on = (can_move_on &&
6849                       (!can_turn_left ||
6850                        !can_turn_right ||
6851                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6852                                                  y + move_dy + left_dy) ||
6853                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6854                                                  y + move_dy + right_dy)));
6855
6856     if (should_turn_left || should_turn_right || should_move_on)
6857     {
6858       if (should_turn_left && should_turn_right && should_move_on)
6859         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6860                         rnd < 2 * rnd_value / 3 ? right_dir :
6861                         old_move_dir);
6862       else if (should_turn_left && should_turn_right)
6863         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6864       else if (should_turn_left && should_move_on)
6865         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6866       else if (should_turn_right && should_move_on)
6867         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6868       else if (should_turn_left)
6869         MovDir[x][y] = left_dir;
6870       else if (should_turn_right)
6871         MovDir[x][y] = right_dir;
6872       else if (should_move_on)
6873         MovDir[x][y] = old_move_dir;
6874     }
6875     else if (can_move_on && rnd > rnd_value / 8)
6876       MovDir[x][y] = old_move_dir;
6877     else if (can_turn_left && can_turn_right)
6878       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6879     else if (can_turn_left && rnd > rnd_value / 8)
6880       MovDir[x][y] = left_dir;
6881     else if (can_turn_right && rnd > rnd_value/8)
6882       MovDir[x][y] = right_dir;
6883     else
6884       MovDir[x][y] = back_dir;
6885
6886     xx = x + move_xy[MovDir[x][y]].dx;
6887     yy = y + move_xy[MovDir[x][y]].dy;
6888
6889     if (!IN_LEV_FIELD(xx, yy) ||
6890         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6891       MovDir[x][y] = old_move_dir;
6892
6893     MovDelay[x][y] = 0;
6894   }
6895   else if (element == EL_DRAGON)
6896   {
6897     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6898     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6899     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6900     int rnd_value = 24;
6901     int rnd = RND(rnd_value);
6902
6903     if (can_move_on && rnd > rnd_value / 8)
6904       MovDir[x][y] = old_move_dir;
6905     else if (can_turn_left && can_turn_right)
6906       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6907     else if (can_turn_left && rnd > rnd_value / 8)
6908       MovDir[x][y] = left_dir;
6909     else if (can_turn_right && rnd > rnd_value / 8)
6910       MovDir[x][y] = right_dir;
6911     else
6912       MovDir[x][y] = back_dir;
6913
6914     xx = x + move_xy[MovDir[x][y]].dx;
6915     yy = y + move_xy[MovDir[x][y]].dy;
6916
6917     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6918       MovDir[x][y] = old_move_dir;
6919
6920     MovDelay[x][y] = 0;
6921   }
6922   else if (element == EL_MOLE)
6923   {
6924     boolean can_move_on =
6925       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6926                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6927                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6928     if (!can_move_on)
6929     {
6930       boolean can_turn_left =
6931         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6932                               IS_AMOEBOID(Tile[left_x][left_y])));
6933
6934       boolean can_turn_right =
6935         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6936                               IS_AMOEBOID(Tile[right_x][right_y])));
6937
6938       if (can_turn_left && can_turn_right)
6939         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6940       else if (can_turn_left)
6941         MovDir[x][y] = left_dir;
6942       else
6943         MovDir[x][y] = right_dir;
6944     }
6945
6946     if (MovDir[x][y] != old_move_dir)
6947       MovDelay[x][y] = 9;
6948   }
6949   else if (element == EL_BALLOON)
6950   {
6951     MovDir[x][y] = game.wind_direction;
6952     MovDelay[x][y] = 0;
6953   }
6954   else if (element == EL_SPRING)
6955   {
6956     if (MovDir[x][y] & MV_HORIZONTAL)
6957     {
6958       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6959           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6960       {
6961         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6962         ResetGfxAnimation(move_x, move_y);
6963         TEST_DrawLevelField(move_x, move_y);
6964
6965         MovDir[x][y] = back_dir;
6966       }
6967       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6968                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6969         MovDir[x][y] = MV_NONE;
6970     }
6971
6972     MovDelay[x][y] = 0;
6973   }
6974   else if (element == EL_ROBOT ||
6975            element == EL_SATELLITE ||
6976            element == EL_PENGUIN ||
6977            element == EL_EMC_ANDROID)
6978   {
6979     int attr_x = -1, attr_y = -1;
6980
6981     if (game.all_players_gone)
6982     {
6983       attr_x = game.exit_x;
6984       attr_y = game.exit_y;
6985     }
6986     else
6987     {
6988       int i;
6989
6990       for (i = 0; i < MAX_PLAYERS; i++)
6991       {
6992         struct PlayerInfo *player = &stored_player[i];
6993         int jx = player->jx, jy = player->jy;
6994
6995         if (!player->active)
6996           continue;
6997
6998         if (attr_x == -1 ||
6999             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7000         {
7001           attr_x = jx;
7002           attr_y = jy;
7003         }
7004       }
7005     }
7006
7007     if (element == EL_ROBOT &&
7008         game.robot_wheel_x >= 0 &&
7009         game.robot_wheel_y >= 0 &&
7010         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7011          game.engine_version < VERSION_IDENT(3,1,0,0)))
7012     {
7013       attr_x = game.robot_wheel_x;
7014       attr_y = game.robot_wheel_y;
7015     }
7016
7017     if (element == EL_PENGUIN)
7018     {
7019       int i;
7020       static int xy[4][2] =
7021       {
7022         { 0, -1 },
7023         { -1, 0 },
7024         { +1, 0 },
7025         { 0, +1 }
7026       };
7027
7028       for (i = 0; i < NUM_DIRECTIONS; i++)
7029       {
7030         int ex = x + xy[i][0];
7031         int ey = y + xy[i][1];
7032
7033         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7034                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7035                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7036                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7037         {
7038           attr_x = ex;
7039           attr_y = ey;
7040           break;
7041         }
7042       }
7043     }
7044
7045     MovDir[x][y] = MV_NONE;
7046     if (attr_x < x)
7047       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7048     else if (attr_x > x)
7049       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7050     if (attr_y < y)
7051       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7052     else if (attr_y > y)
7053       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7054
7055     if (element == EL_ROBOT)
7056     {
7057       int newx, newy;
7058
7059       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7060         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7061       Moving2Blocked(x, y, &newx, &newy);
7062
7063       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7064         MovDelay[x][y] = 8 + 8 * !RND(3);
7065       else
7066         MovDelay[x][y] = 16;
7067     }
7068     else if (element == EL_PENGUIN)
7069     {
7070       int newx, newy;
7071
7072       MovDelay[x][y] = 1;
7073
7074       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7075       {
7076         boolean first_horiz = RND(2);
7077         int new_move_dir = MovDir[x][y];
7078
7079         MovDir[x][y] =
7080           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7081         Moving2Blocked(x, y, &newx, &newy);
7082
7083         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7084           return;
7085
7086         MovDir[x][y] =
7087           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7088         Moving2Blocked(x, y, &newx, &newy);
7089
7090         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7091           return;
7092
7093         MovDir[x][y] = old_move_dir;
7094         return;
7095       }
7096     }
7097     else if (element == EL_SATELLITE)
7098     {
7099       int newx, newy;
7100
7101       MovDelay[x][y] = 1;
7102
7103       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7104       {
7105         boolean first_horiz = RND(2);
7106         int new_move_dir = MovDir[x][y];
7107
7108         MovDir[x][y] =
7109           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7110         Moving2Blocked(x, y, &newx, &newy);
7111
7112         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7113           return;
7114
7115         MovDir[x][y] =
7116           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7117         Moving2Blocked(x, y, &newx, &newy);
7118
7119         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7120           return;
7121
7122         MovDir[x][y] = old_move_dir;
7123         return;
7124       }
7125     }
7126     else if (element == EL_EMC_ANDROID)
7127     {
7128       static int check_pos[16] =
7129       {
7130         -1,             //  0 => (invalid)
7131         7,              //  1 => MV_LEFT
7132         3,              //  2 => MV_RIGHT
7133         -1,             //  3 => (invalid)
7134         1,              //  4 =>            MV_UP
7135         0,              //  5 => MV_LEFT  | MV_UP
7136         2,              //  6 => MV_RIGHT | MV_UP
7137         -1,             //  7 => (invalid)
7138         5,              //  8 =>            MV_DOWN
7139         6,              //  9 => MV_LEFT  | MV_DOWN
7140         4,              // 10 => MV_RIGHT | MV_DOWN
7141         -1,             // 11 => (invalid)
7142         -1,             // 12 => (invalid)
7143         -1,             // 13 => (invalid)
7144         -1,             // 14 => (invalid)
7145         -1,             // 15 => (invalid)
7146       };
7147       static struct
7148       {
7149         int dx, dy;
7150         int dir;
7151       } check_xy[8] =
7152       {
7153         { -1, -1,       MV_LEFT  | MV_UP   },
7154         {  0, -1,                  MV_UP   },
7155         { +1, -1,       MV_RIGHT | MV_UP   },
7156         { +1,  0,       MV_RIGHT           },
7157         { +1, +1,       MV_RIGHT | MV_DOWN },
7158         {  0, +1,                  MV_DOWN },
7159         { -1, +1,       MV_LEFT  | MV_DOWN },
7160         { -1,  0,       MV_LEFT            },
7161       };
7162       int start_pos, check_order;
7163       boolean can_clone = FALSE;
7164       int i;
7165
7166       // check if there is any free field around current position
7167       for (i = 0; i < 8; i++)
7168       {
7169         int newx = x + check_xy[i].dx;
7170         int newy = y + check_xy[i].dy;
7171
7172         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7173         {
7174           can_clone = TRUE;
7175
7176           break;
7177         }
7178       }
7179
7180       if (can_clone)            // randomly find an element to clone
7181       {
7182         can_clone = FALSE;
7183
7184         start_pos = check_pos[RND(8)];
7185         check_order = (RND(2) ? -1 : +1);
7186
7187         for (i = 0; i < 8; i++)
7188         {
7189           int pos_raw = start_pos + i * check_order;
7190           int pos = (pos_raw + 8) % 8;
7191           int newx = x + check_xy[pos].dx;
7192           int newy = y + check_xy[pos].dy;
7193
7194           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7195           {
7196             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7197             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7198
7199             Store[x][y] = Tile[newx][newy];
7200
7201             can_clone = TRUE;
7202
7203             break;
7204           }
7205         }
7206       }
7207
7208       if (can_clone)            // randomly find a direction to move
7209       {
7210         can_clone = FALSE;
7211
7212         start_pos = check_pos[RND(8)];
7213         check_order = (RND(2) ? -1 : +1);
7214
7215         for (i = 0; i < 8; i++)
7216         {
7217           int pos_raw = start_pos + i * check_order;
7218           int pos = (pos_raw + 8) % 8;
7219           int newx = x + check_xy[pos].dx;
7220           int newy = y + check_xy[pos].dy;
7221           int new_move_dir = check_xy[pos].dir;
7222
7223           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7224           {
7225             MovDir[x][y] = new_move_dir;
7226             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7227
7228             can_clone = TRUE;
7229
7230             break;
7231           }
7232         }
7233       }
7234
7235       if (can_clone)            // cloning and moving successful
7236         return;
7237
7238       // cannot clone -- try to move towards player
7239
7240       start_pos = check_pos[MovDir[x][y] & 0x0f];
7241       check_order = (RND(2) ? -1 : +1);
7242
7243       for (i = 0; i < 3; i++)
7244       {
7245         // first check start_pos, then previous/next or (next/previous) pos
7246         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7247         int pos = (pos_raw + 8) % 8;
7248         int newx = x + check_xy[pos].dx;
7249         int newy = y + check_xy[pos].dy;
7250         int new_move_dir = check_xy[pos].dir;
7251
7252         if (IS_PLAYER(newx, newy))
7253           break;
7254
7255         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7256         {
7257           MovDir[x][y] = new_move_dir;
7258           MovDelay[x][y] = level.android_move_time * 8 + 1;
7259
7260           break;
7261         }
7262       }
7263     }
7264   }
7265   else if (move_pattern == MV_TURNING_LEFT ||
7266            move_pattern == MV_TURNING_RIGHT ||
7267            move_pattern == MV_TURNING_LEFT_RIGHT ||
7268            move_pattern == MV_TURNING_RIGHT_LEFT ||
7269            move_pattern == MV_TURNING_RANDOM ||
7270            move_pattern == MV_ALL_DIRECTIONS)
7271   {
7272     boolean can_turn_left =
7273       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7274     boolean can_turn_right =
7275       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7276
7277     if (element_info[element].move_stepsize == 0)       // "not moving"
7278       return;
7279
7280     if (move_pattern == MV_TURNING_LEFT)
7281       MovDir[x][y] = left_dir;
7282     else if (move_pattern == MV_TURNING_RIGHT)
7283       MovDir[x][y] = right_dir;
7284     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7285       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7286     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7287       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7288     else if (move_pattern == MV_TURNING_RANDOM)
7289       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7290                       can_turn_right && !can_turn_left ? right_dir :
7291                       RND(2) ? left_dir : right_dir);
7292     else if (can_turn_left && can_turn_right)
7293       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7294     else if (can_turn_left)
7295       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7296     else if (can_turn_right)
7297       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7298     else
7299       MovDir[x][y] = back_dir;
7300
7301     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7302   }
7303   else if (move_pattern == MV_HORIZONTAL ||
7304            move_pattern == MV_VERTICAL)
7305   {
7306     if (move_pattern & old_move_dir)
7307       MovDir[x][y] = back_dir;
7308     else if (move_pattern == MV_HORIZONTAL)
7309       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7310     else if (move_pattern == MV_VERTICAL)
7311       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7312
7313     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7314   }
7315   else if (move_pattern & MV_ANY_DIRECTION)
7316   {
7317     MovDir[x][y] = move_pattern;
7318     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7319   }
7320   else if (move_pattern & MV_WIND_DIRECTION)
7321   {
7322     MovDir[x][y] = game.wind_direction;
7323     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7324   }
7325   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7326   {
7327     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7328       MovDir[x][y] = left_dir;
7329     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7330       MovDir[x][y] = right_dir;
7331
7332     if (MovDir[x][y] != old_move_dir)
7333       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7334   }
7335   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7336   {
7337     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7338       MovDir[x][y] = right_dir;
7339     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7340       MovDir[x][y] = left_dir;
7341
7342     if (MovDir[x][y] != old_move_dir)
7343       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7344   }
7345   else if (move_pattern == MV_TOWARDS_PLAYER ||
7346            move_pattern == MV_AWAY_FROM_PLAYER)
7347   {
7348     int attr_x = -1, attr_y = -1;
7349     int newx, newy;
7350     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7351
7352     if (game.all_players_gone)
7353     {
7354       attr_x = game.exit_x;
7355       attr_y = game.exit_y;
7356     }
7357     else
7358     {
7359       int i;
7360
7361       for (i = 0; i < MAX_PLAYERS; i++)
7362       {
7363         struct PlayerInfo *player = &stored_player[i];
7364         int jx = player->jx, jy = player->jy;
7365
7366         if (!player->active)
7367           continue;
7368
7369         if (attr_x == -1 ||
7370             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7371         {
7372           attr_x = jx;
7373           attr_y = jy;
7374         }
7375       }
7376     }
7377
7378     MovDir[x][y] = MV_NONE;
7379     if (attr_x < x)
7380       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7381     else if (attr_x > x)
7382       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7383     if (attr_y < y)
7384       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7385     else if (attr_y > y)
7386       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7387
7388     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7389
7390     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7391     {
7392       boolean first_horiz = RND(2);
7393       int new_move_dir = MovDir[x][y];
7394
7395       if (element_info[element].move_stepsize == 0)     // "not moving"
7396       {
7397         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7398         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7399
7400         return;
7401       }
7402
7403       MovDir[x][y] =
7404         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7405       Moving2Blocked(x, y, &newx, &newy);
7406
7407       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7408         return;
7409
7410       MovDir[x][y] =
7411         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7412       Moving2Blocked(x, y, &newx, &newy);
7413
7414       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7415         return;
7416
7417       MovDir[x][y] = old_move_dir;
7418     }
7419   }
7420   else if (move_pattern == MV_WHEN_PUSHED ||
7421            move_pattern == MV_WHEN_DROPPED)
7422   {
7423     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7424       MovDir[x][y] = MV_NONE;
7425
7426     MovDelay[x][y] = 0;
7427   }
7428   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7429   {
7430     static int test_xy[7][2] =
7431     {
7432       { 0, -1 },
7433       { -1, 0 },
7434       { +1, 0 },
7435       { 0, +1 },
7436       { 0, -1 },
7437       { -1, 0 },
7438       { +1, 0 },
7439     };
7440     static int test_dir[7] =
7441     {
7442       MV_UP,
7443       MV_LEFT,
7444       MV_RIGHT,
7445       MV_DOWN,
7446       MV_UP,
7447       MV_LEFT,
7448       MV_RIGHT,
7449     };
7450     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7451     int move_preference = -1000000;     // start with very low preference
7452     int new_move_dir = MV_NONE;
7453     int start_test = RND(4);
7454     int i;
7455
7456     for (i = 0; i < NUM_DIRECTIONS; i++)
7457     {
7458       int move_dir = test_dir[start_test + i];
7459       int move_dir_preference;
7460
7461       xx = x + test_xy[start_test + i][0];
7462       yy = y + test_xy[start_test + i][1];
7463
7464       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7465           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7466       {
7467         new_move_dir = move_dir;
7468
7469         break;
7470       }
7471
7472       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7473         continue;
7474
7475       move_dir_preference = -1 * RunnerVisit[xx][yy];
7476       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7477         move_dir_preference = PlayerVisit[xx][yy];
7478
7479       if (move_dir_preference > move_preference)
7480       {
7481         // prefer field that has not been visited for the longest time
7482         move_preference = move_dir_preference;
7483         new_move_dir = move_dir;
7484       }
7485       else if (move_dir_preference == move_preference &&
7486                move_dir == old_move_dir)
7487       {
7488         // prefer last direction when all directions are preferred equally
7489         move_preference = move_dir_preference;
7490         new_move_dir = move_dir;
7491       }
7492     }
7493
7494     MovDir[x][y] = new_move_dir;
7495     if (old_move_dir != new_move_dir)
7496       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7497   }
7498 }
7499
7500 static void TurnRound(int x, int y)
7501 {
7502   int direction = MovDir[x][y];
7503
7504   TurnRoundExt(x, y);
7505
7506   GfxDir[x][y] = MovDir[x][y];
7507
7508   if (direction != MovDir[x][y])
7509     GfxFrame[x][y] = 0;
7510
7511   if (MovDelay[x][y])
7512     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7513
7514   ResetGfxFrame(x, y);
7515 }
7516
7517 static boolean JustBeingPushed(int x, int y)
7518 {
7519   int i;
7520
7521   for (i = 0; i < MAX_PLAYERS; i++)
7522   {
7523     struct PlayerInfo *player = &stored_player[i];
7524
7525     if (player->active && player->is_pushing && player->MovPos)
7526     {
7527       int next_jx = player->jx + (player->jx - player->last_jx);
7528       int next_jy = player->jy + (player->jy - player->last_jy);
7529
7530       if (x == next_jx && y == next_jy)
7531         return TRUE;
7532     }
7533   }
7534
7535   return FALSE;
7536 }
7537
7538 static void StartMoving(int x, int y)
7539 {
7540   boolean started_moving = FALSE;       // some elements can fall _and_ move
7541   int element = Tile[x][y];
7542
7543   if (Stop[x][y])
7544     return;
7545
7546   if (MovDelay[x][y] == 0)
7547     GfxAction[x][y] = ACTION_DEFAULT;
7548
7549   if (CAN_FALL(element) && y < lev_fieldy - 1)
7550   {
7551     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7552         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7553       if (JustBeingPushed(x, y))
7554         return;
7555
7556     if (element == EL_QUICKSAND_FULL)
7557     {
7558       if (IS_FREE(x, y + 1))
7559       {
7560         InitMovingField(x, y, MV_DOWN);
7561         started_moving = TRUE;
7562
7563         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7564 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7565         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7566           Store[x][y] = EL_ROCK;
7567 #else
7568         Store[x][y] = EL_ROCK;
7569 #endif
7570
7571         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7572       }
7573       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7574       {
7575         if (!MovDelay[x][y])
7576         {
7577           MovDelay[x][y] = TILEY + 1;
7578
7579           ResetGfxAnimation(x, y);
7580           ResetGfxAnimation(x, y + 1);
7581         }
7582
7583         if (MovDelay[x][y])
7584         {
7585           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7586           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7587
7588           MovDelay[x][y]--;
7589           if (MovDelay[x][y])
7590             return;
7591         }
7592
7593         Tile[x][y] = EL_QUICKSAND_EMPTY;
7594         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7595         Store[x][y + 1] = Store[x][y];
7596         Store[x][y] = 0;
7597
7598         PlayLevelSoundAction(x, y, ACTION_FILLING);
7599       }
7600       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7601       {
7602         if (!MovDelay[x][y])
7603         {
7604           MovDelay[x][y] = TILEY + 1;
7605
7606           ResetGfxAnimation(x, y);
7607           ResetGfxAnimation(x, y + 1);
7608         }
7609
7610         if (MovDelay[x][y])
7611         {
7612           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7613           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7614
7615           MovDelay[x][y]--;
7616           if (MovDelay[x][y])
7617             return;
7618         }
7619
7620         Tile[x][y] = EL_QUICKSAND_EMPTY;
7621         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7622         Store[x][y + 1] = Store[x][y];
7623         Store[x][y] = 0;
7624
7625         PlayLevelSoundAction(x, y, ACTION_FILLING);
7626       }
7627     }
7628     else if (element == EL_QUICKSAND_FAST_FULL)
7629     {
7630       if (IS_FREE(x, y + 1))
7631       {
7632         InitMovingField(x, y, MV_DOWN);
7633         started_moving = TRUE;
7634
7635         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7636 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7637         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7638           Store[x][y] = EL_ROCK;
7639 #else
7640         Store[x][y] = EL_ROCK;
7641 #endif
7642
7643         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7644       }
7645       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7646       {
7647         if (!MovDelay[x][y])
7648         {
7649           MovDelay[x][y] = TILEY + 1;
7650
7651           ResetGfxAnimation(x, y);
7652           ResetGfxAnimation(x, y + 1);
7653         }
7654
7655         if (MovDelay[x][y])
7656         {
7657           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7658           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7659
7660           MovDelay[x][y]--;
7661           if (MovDelay[x][y])
7662             return;
7663         }
7664
7665         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7666         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7667         Store[x][y + 1] = Store[x][y];
7668         Store[x][y] = 0;
7669
7670         PlayLevelSoundAction(x, y, ACTION_FILLING);
7671       }
7672       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7673       {
7674         if (!MovDelay[x][y])
7675         {
7676           MovDelay[x][y] = TILEY + 1;
7677
7678           ResetGfxAnimation(x, y);
7679           ResetGfxAnimation(x, y + 1);
7680         }
7681
7682         if (MovDelay[x][y])
7683         {
7684           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7685           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7686
7687           MovDelay[x][y]--;
7688           if (MovDelay[x][y])
7689             return;
7690         }
7691
7692         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7693         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7694         Store[x][y + 1] = Store[x][y];
7695         Store[x][y] = 0;
7696
7697         PlayLevelSoundAction(x, y, ACTION_FILLING);
7698       }
7699     }
7700     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7701              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7702     {
7703       InitMovingField(x, y, MV_DOWN);
7704       started_moving = TRUE;
7705
7706       Tile[x][y] = EL_QUICKSAND_FILLING;
7707       Store[x][y] = element;
7708
7709       PlayLevelSoundAction(x, y, ACTION_FILLING);
7710     }
7711     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7712              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7713     {
7714       InitMovingField(x, y, MV_DOWN);
7715       started_moving = TRUE;
7716
7717       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7718       Store[x][y] = element;
7719
7720       PlayLevelSoundAction(x, y, ACTION_FILLING);
7721     }
7722     else if (element == EL_MAGIC_WALL_FULL)
7723     {
7724       if (IS_FREE(x, y + 1))
7725       {
7726         InitMovingField(x, y, MV_DOWN);
7727         started_moving = TRUE;
7728
7729         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7730         Store[x][y] = EL_CHANGED(Store[x][y]);
7731       }
7732       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7733       {
7734         if (!MovDelay[x][y])
7735           MovDelay[x][y] = TILEY / 4 + 1;
7736
7737         if (MovDelay[x][y])
7738         {
7739           MovDelay[x][y]--;
7740           if (MovDelay[x][y])
7741             return;
7742         }
7743
7744         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7745         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7746         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7747         Store[x][y] = 0;
7748       }
7749     }
7750     else if (element == EL_BD_MAGIC_WALL_FULL)
7751     {
7752       if (IS_FREE(x, y + 1))
7753       {
7754         InitMovingField(x, y, MV_DOWN);
7755         started_moving = TRUE;
7756
7757         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7758         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7759       }
7760       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7761       {
7762         if (!MovDelay[x][y])
7763           MovDelay[x][y] = TILEY / 4 + 1;
7764
7765         if (MovDelay[x][y])
7766         {
7767           MovDelay[x][y]--;
7768           if (MovDelay[x][y])
7769             return;
7770         }
7771
7772         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7773         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7774         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7775         Store[x][y] = 0;
7776       }
7777     }
7778     else if (element == EL_DC_MAGIC_WALL_FULL)
7779     {
7780       if (IS_FREE(x, y + 1))
7781       {
7782         InitMovingField(x, y, MV_DOWN);
7783         started_moving = TRUE;
7784
7785         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7786         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7787       }
7788       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7789       {
7790         if (!MovDelay[x][y])
7791           MovDelay[x][y] = TILEY / 4 + 1;
7792
7793         if (MovDelay[x][y])
7794         {
7795           MovDelay[x][y]--;
7796           if (MovDelay[x][y])
7797             return;
7798         }
7799
7800         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7801         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7802         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7803         Store[x][y] = 0;
7804       }
7805     }
7806     else if ((CAN_PASS_MAGIC_WALL(element) &&
7807               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7808                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7809              (CAN_PASS_DC_MAGIC_WALL(element) &&
7810               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7811
7812     {
7813       InitMovingField(x, y, MV_DOWN);
7814       started_moving = TRUE;
7815
7816       Tile[x][y] =
7817         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7818          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7819          EL_DC_MAGIC_WALL_FILLING);
7820       Store[x][y] = element;
7821     }
7822     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7823     {
7824       SplashAcid(x, y + 1);
7825
7826       InitMovingField(x, y, MV_DOWN);
7827       started_moving = TRUE;
7828
7829       Store[x][y] = EL_ACID;
7830     }
7831     else if (
7832              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7833               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7834              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7835               CAN_FALL(element) && WasJustFalling[x][y] &&
7836               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7837
7838              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7839               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7840               (Tile[x][y + 1] == EL_BLOCKED)))
7841     {
7842       /* this is needed for a special case not covered by calling "Impact()"
7843          from "ContinueMoving()": if an element moves to a tile directly below
7844          another element which was just falling on that tile (which was empty
7845          in the previous frame), the falling element above would just stop
7846          instead of smashing the element below (in previous version, the above
7847          element was just checked for "moving" instead of "falling", resulting
7848          in incorrect smashes caused by horizontal movement of the above
7849          element; also, the case of the player being the element to smash was
7850          simply not covered here... :-/ ) */
7851
7852       CheckCollision[x][y] = 0;
7853       CheckImpact[x][y] = 0;
7854
7855       Impact(x, y);
7856     }
7857     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7858     {
7859       if (MovDir[x][y] == MV_NONE)
7860       {
7861         InitMovingField(x, y, MV_DOWN);
7862         started_moving = TRUE;
7863       }
7864     }
7865     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7866     {
7867       if (WasJustFalling[x][y]) // prevent animation from being restarted
7868         MovDir[x][y] = MV_DOWN;
7869
7870       InitMovingField(x, y, MV_DOWN);
7871       started_moving = TRUE;
7872     }
7873     else if (element == EL_AMOEBA_DROP)
7874     {
7875       Tile[x][y] = EL_AMOEBA_GROWING;
7876       Store[x][y] = EL_AMOEBA_WET;
7877     }
7878     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7879               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7880              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7881              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7882     {
7883       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7884                                 (IS_FREE(x - 1, y + 1) ||
7885                                  Tile[x - 1][y + 1] == EL_ACID));
7886       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7887                                 (IS_FREE(x + 1, y + 1) ||
7888                                  Tile[x + 1][y + 1] == EL_ACID));
7889       boolean can_fall_any  = (can_fall_left || can_fall_right);
7890       boolean can_fall_both = (can_fall_left && can_fall_right);
7891       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7892
7893       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7894       {
7895         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7896           can_fall_right = FALSE;
7897         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7898           can_fall_left = FALSE;
7899         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7900           can_fall_right = FALSE;
7901         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7902           can_fall_left = FALSE;
7903
7904         can_fall_any  = (can_fall_left || can_fall_right);
7905         can_fall_both = FALSE;
7906       }
7907
7908       if (can_fall_both)
7909       {
7910         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7911           can_fall_right = FALSE;       // slip down on left side
7912         else
7913           can_fall_left = !(can_fall_right = RND(2));
7914
7915         can_fall_both = FALSE;
7916       }
7917
7918       if (can_fall_any)
7919       {
7920         // if not determined otherwise, prefer left side for slipping down
7921         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7922         started_moving = TRUE;
7923       }
7924     }
7925     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7926     {
7927       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7928       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7929       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7930       int belt_dir = game.belt_dir[belt_nr];
7931
7932       if ((belt_dir == MV_LEFT  && left_is_free) ||
7933           (belt_dir == MV_RIGHT && right_is_free))
7934       {
7935         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7936
7937         InitMovingField(x, y, belt_dir);
7938         started_moving = TRUE;
7939
7940         Pushed[x][y] = TRUE;
7941         Pushed[nextx][y] = TRUE;
7942
7943         GfxAction[x][y] = ACTION_DEFAULT;
7944       }
7945       else
7946       {
7947         MovDir[x][y] = 0;       // if element was moving, stop it
7948       }
7949     }
7950   }
7951
7952   // not "else if" because of elements that can fall and move (EL_SPRING)
7953   if (CAN_MOVE(element) && !started_moving)
7954   {
7955     int move_pattern = element_info[element].move_pattern;
7956     int newx, newy;
7957
7958     Moving2Blocked(x, y, &newx, &newy);
7959
7960     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7961       return;
7962
7963     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7964         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7965     {
7966       WasJustMoving[x][y] = 0;
7967       CheckCollision[x][y] = 0;
7968
7969       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7970
7971       if (Tile[x][y] != element)        // element has changed
7972         return;
7973     }
7974
7975     if (!MovDelay[x][y])        // start new movement phase
7976     {
7977       // all objects that can change their move direction after each step
7978       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7979
7980       if (element != EL_YAMYAM &&
7981           element != EL_DARK_YAMYAM &&
7982           element != EL_PACMAN &&
7983           !(move_pattern & MV_ANY_DIRECTION) &&
7984           move_pattern != MV_TURNING_LEFT &&
7985           move_pattern != MV_TURNING_RIGHT &&
7986           move_pattern != MV_TURNING_LEFT_RIGHT &&
7987           move_pattern != MV_TURNING_RIGHT_LEFT &&
7988           move_pattern != MV_TURNING_RANDOM)
7989       {
7990         TurnRound(x, y);
7991
7992         if (MovDelay[x][y] && (element == EL_BUG ||
7993                                element == EL_SPACESHIP ||
7994                                element == EL_SP_SNIKSNAK ||
7995                                element == EL_SP_ELECTRON ||
7996                                element == EL_MOLE))
7997           TEST_DrawLevelField(x, y);
7998       }
7999     }
8000
8001     if (MovDelay[x][y])         // wait some time before next movement
8002     {
8003       MovDelay[x][y]--;
8004
8005       if (element == EL_ROBOT ||
8006           element == EL_YAMYAM ||
8007           element == EL_DARK_YAMYAM)
8008       {
8009         DrawLevelElementAnimationIfNeeded(x, y, element);
8010         PlayLevelSoundAction(x, y, ACTION_WAITING);
8011       }
8012       else if (element == EL_SP_ELECTRON)
8013         DrawLevelElementAnimationIfNeeded(x, y, element);
8014       else if (element == EL_DRAGON)
8015       {
8016         int i;
8017         int dir = MovDir[x][y];
8018         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8019         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8020         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8021                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8022                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8023                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8024         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8025
8026         GfxAction[x][y] = ACTION_ATTACKING;
8027
8028         if (IS_PLAYER(x, y))
8029           DrawPlayerField(x, y);
8030         else
8031           TEST_DrawLevelField(x, y);
8032
8033         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8034
8035         for (i = 1; i <= 3; i++)
8036         {
8037           int xx = x + i * dx;
8038           int yy = y + i * dy;
8039           int sx = SCREENX(xx);
8040           int sy = SCREENY(yy);
8041           int flame_graphic = graphic + (i - 1);
8042
8043           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8044             break;
8045
8046           if (MovDelay[x][y])
8047           {
8048             int flamed = MovingOrBlocked2Element(xx, yy);
8049
8050             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8051               Bang(xx, yy);
8052             else
8053               RemoveMovingField(xx, yy);
8054
8055             ChangeDelay[xx][yy] = 0;
8056
8057             Tile[xx][yy] = EL_FLAMES;
8058
8059             if (IN_SCR_FIELD(sx, sy))
8060             {
8061               TEST_DrawLevelFieldCrumbled(xx, yy);
8062               DrawGraphic(sx, sy, flame_graphic, frame);
8063             }
8064           }
8065           else
8066           {
8067             if (Tile[xx][yy] == EL_FLAMES)
8068               Tile[xx][yy] = EL_EMPTY;
8069             TEST_DrawLevelField(xx, yy);
8070           }
8071         }
8072       }
8073
8074       if (MovDelay[x][y])       // element still has to wait some time
8075       {
8076         PlayLevelSoundAction(x, y, ACTION_WAITING);
8077
8078         return;
8079       }
8080     }
8081
8082     // now make next step
8083
8084     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8085
8086     if (DONT_COLLIDE_WITH(element) &&
8087         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8088         !PLAYER_ENEMY_PROTECTED(newx, newy))
8089     {
8090       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8091
8092       return;
8093     }
8094
8095     else if (CAN_MOVE_INTO_ACID(element) &&
8096              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8097              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8098              (MovDir[x][y] == MV_DOWN ||
8099               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8100     {
8101       SplashAcid(newx, newy);
8102       Store[x][y] = EL_ACID;
8103     }
8104     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8105     {
8106       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8107           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8108           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8109           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8110       {
8111         RemoveField(x, y);
8112         TEST_DrawLevelField(x, y);
8113
8114         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8115         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8116           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8117
8118         game.friends_still_needed--;
8119         if (!game.friends_still_needed &&
8120             !game.GameOver &&
8121             game.all_players_gone)
8122           LevelSolved();
8123
8124         return;
8125       }
8126       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8127       {
8128         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8129           TEST_DrawLevelField(newx, newy);
8130         else
8131           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8132       }
8133       else if (!IS_FREE(newx, newy))
8134       {
8135         GfxAction[x][y] = ACTION_WAITING;
8136
8137         if (IS_PLAYER(x, y))
8138           DrawPlayerField(x, y);
8139         else
8140           TEST_DrawLevelField(x, y);
8141
8142         return;
8143       }
8144     }
8145     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8146     {
8147       if (IS_FOOD_PIG(Tile[newx][newy]))
8148       {
8149         if (IS_MOVING(newx, newy))
8150           RemoveMovingField(newx, newy);
8151         else
8152         {
8153           Tile[newx][newy] = EL_EMPTY;
8154           TEST_DrawLevelField(newx, newy);
8155         }
8156
8157         PlayLevelSound(x, y, SND_PIG_DIGGING);
8158       }
8159       else if (!IS_FREE(newx, newy))
8160       {
8161         if (IS_PLAYER(x, y))
8162           DrawPlayerField(x, y);
8163         else
8164           TEST_DrawLevelField(x, y);
8165
8166         return;
8167       }
8168     }
8169     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8170     {
8171       if (Store[x][y] != EL_EMPTY)
8172       {
8173         boolean can_clone = FALSE;
8174         int xx, yy;
8175
8176         // check if element to clone is still there
8177         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8178         {
8179           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8180           {
8181             can_clone = TRUE;
8182
8183             break;
8184           }
8185         }
8186
8187         // cannot clone or target field not free anymore -- do not clone
8188         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8189           Store[x][y] = EL_EMPTY;
8190       }
8191
8192       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8193       {
8194         if (IS_MV_DIAGONAL(MovDir[x][y]))
8195         {
8196           int diagonal_move_dir = MovDir[x][y];
8197           int stored = Store[x][y];
8198           int change_delay = 8;
8199           int graphic;
8200
8201           // android is moving diagonally
8202
8203           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8204
8205           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8206           GfxElement[x][y] = EL_EMC_ANDROID;
8207           GfxAction[x][y] = ACTION_SHRINKING;
8208           GfxDir[x][y] = diagonal_move_dir;
8209           ChangeDelay[x][y] = change_delay;
8210
8211           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8212                                    GfxDir[x][y]);
8213
8214           DrawLevelGraphicAnimation(x, y, graphic);
8215           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8216
8217           if (Tile[newx][newy] == EL_ACID)
8218           {
8219             SplashAcid(newx, newy);
8220
8221             return;
8222           }
8223
8224           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8225
8226           Store[newx][newy] = EL_EMC_ANDROID;
8227           GfxElement[newx][newy] = EL_EMC_ANDROID;
8228           GfxAction[newx][newy] = ACTION_GROWING;
8229           GfxDir[newx][newy] = diagonal_move_dir;
8230           ChangeDelay[newx][newy] = change_delay;
8231
8232           graphic = el_act_dir2img(GfxElement[newx][newy],
8233                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8234
8235           DrawLevelGraphicAnimation(newx, newy, graphic);
8236           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8237
8238           return;
8239         }
8240         else
8241         {
8242           Tile[newx][newy] = EL_EMPTY;
8243           TEST_DrawLevelField(newx, newy);
8244
8245           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8246         }
8247       }
8248       else if (!IS_FREE(newx, newy))
8249       {
8250         return;
8251       }
8252     }
8253     else if (IS_CUSTOM_ELEMENT(element) &&
8254              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8255     {
8256       if (!DigFieldByCE(newx, newy, element))
8257         return;
8258
8259       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8260       {
8261         RunnerVisit[x][y] = FrameCounter;
8262         PlayerVisit[x][y] /= 8;         // expire player visit path
8263       }
8264     }
8265     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8266     {
8267       if (!IS_FREE(newx, newy))
8268       {
8269         if (IS_PLAYER(x, y))
8270           DrawPlayerField(x, y);
8271         else
8272           TEST_DrawLevelField(x, y);
8273
8274         return;
8275       }
8276       else
8277       {
8278         boolean wanna_flame = !RND(10);
8279         int dx = newx - x, dy = newy - y;
8280         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8281         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8282         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8283                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8284         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8285                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8286
8287         if ((wanna_flame ||
8288              IS_CLASSIC_ENEMY(element1) ||
8289              IS_CLASSIC_ENEMY(element2)) &&
8290             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8291             element1 != EL_FLAMES && element2 != EL_FLAMES)
8292         {
8293           ResetGfxAnimation(x, y);
8294           GfxAction[x][y] = ACTION_ATTACKING;
8295
8296           if (IS_PLAYER(x, y))
8297             DrawPlayerField(x, y);
8298           else
8299             TEST_DrawLevelField(x, y);
8300
8301           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8302
8303           MovDelay[x][y] = 50;
8304
8305           Tile[newx][newy] = EL_FLAMES;
8306           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8307             Tile[newx1][newy1] = EL_FLAMES;
8308           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8309             Tile[newx2][newy2] = EL_FLAMES;
8310
8311           return;
8312         }
8313       }
8314     }
8315     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8316              Tile[newx][newy] == EL_DIAMOND)
8317     {
8318       if (IS_MOVING(newx, newy))
8319         RemoveMovingField(newx, newy);
8320       else
8321       {
8322         Tile[newx][newy] = EL_EMPTY;
8323         TEST_DrawLevelField(newx, newy);
8324       }
8325
8326       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8327     }
8328     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8329              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8330     {
8331       if (AmoebaNr[newx][newy])
8332       {
8333         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8334         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8335             Tile[newx][newy] == EL_BD_AMOEBA)
8336           AmoebaCnt[AmoebaNr[newx][newy]]--;
8337       }
8338
8339       if (IS_MOVING(newx, newy))
8340       {
8341         RemoveMovingField(newx, newy);
8342       }
8343       else
8344       {
8345         Tile[newx][newy] = EL_EMPTY;
8346         TEST_DrawLevelField(newx, newy);
8347       }
8348
8349       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8350     }
8351     else if ((element == EL_PACMAN || element == EL_MOLE)
8352              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8353     {
8354       if (AmoebaNr[newx][newy])
8355       {
8356         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8357         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8358             Tile[newx][newy] == EL_BD_AMOEBA)
8359           AmoebaCnt[AmoebaNr[newx][newy]]--;
8360       }
8361
8362       if (element == EL_MOLE)
8363       {
8364         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8365         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8366
8367         ResetGfxAnimation(x, y);
8368         GfxAction[x][y] = ACTION_DIGGING;
8369         TEST_DrawLevelField(x, y);
8370
8371         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8372
8373         return;                         // wait for shrinking amoeba
8374       }
8375       else      // element == EL_PACMAN
8376       {
8377         Tile[newx][newy] = EL_EMPTY;
8378         TEST_DrawLevelField(newx, newy);
8379         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8380       }
8381     }
8382     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8383              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8384               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8385     {
8386       // wait for shrinking amoeba to completely disappear
8387       return;
8388     }
8389     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8390     {
8391       // object was running against a wall
8392
8393       TurnRound(x, y);
8394
8395       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8396         DrawLevelElementAnimation(x, y, element);
8397
8398       if (DONT_TOUCH(element))
8399         TestIfBadThingTouchesPlayer(x, y);
8400
8401       return;
8402     }
8403
8404     InitMovingField(x, y, MovDir[x][y]);
8405
8406     PlayLevelSoundAction(x, y, ACTION_MOVING);
8407   }
8408
8409   if (MovDir[x][y])
8410     ContinueMoving(x, y);
8411 }
8412
8413 void ContinueMoving(int x, int y)
8414 {
8415   int element = Tile[x][y];
8416   struct ElementInfo *ei = &element_info[element];
8417   int direction = MovDir[x][y];
8418   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8419   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8420   int newx = x + dx, newy = y + dy;
8421   int stored = Store[x][y];
8422   int stored_new = Store[newx][newy];
8423   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8424   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8425   boolean last_line = (newy == lev_fieldy - 1);
8426
8427   MovPos[x][y] += getElementMoveStepsize(x, y);
8428
8429   if (pushed_by_player) // special case: moving object pushed by player
8430     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8431
8432   if (ABS(MovPos[x][y]) < TILEX)
8433   {
8434     TEST_DrawLevelField(x, y);
8435
8436     return;     // element is still moving
8437   }
8438
8439   // element reached destination field
8440
8441   Tile[x][y] = EL_EMPTY;
8442   Tile[newx][newy] = element;
8443   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8444
8445   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8446   {
8447     element = Tile[newx][newy] = EL_ACID;
8448   }
8449   else if (element == EL_MOLE)
8450   {
8451     Tile[x][y] = EL_SAND;
8452
8453     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8454   }
8455   else if (element == EL_QUICKSAND_FILLING)
8456   {
8457     element = Tile[newx][newy] = get_next_element(element);
8458     Store[newx][newy] = Store[x][y];
8459   }
8460   else if (element == EL_QUICKSAND_EMPTYING)
8461   {
8462     Tile[x][y] = get_next_element(element);
8463     element = Tile[newx][newy] = Store[x][y];
8464   }
8465   else if (element == EL_QUICKSAND_FAST_FILLING)
8466   {
8467     element = Tile[newx][newy] = get_next_element(element);
8468     Store[newx][newy] = Store[x][y];
8469   }
8470   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8471   {
8472     Tile[x][y] = get_next_element(element);
8473     element = Tile[newx][newy] = Store[x][y];
8474   }
8475   else if (element == EL_MAGIC_WALL_FILLING)
8476   {
8477     element = Tile[newx][newy] = get_next_element(element);
8478     if (!game.magic_wall_active)
8479       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8480     Store[newx][newy] = Store[x][y];
8481   }
8482   else if (element == EL_MAGIC_WALL_EMPTYING)
8483   {
8484     Tile[x][y] = get_next_element(element);
8485     if (!game.magic_wall_active)
8486       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8487     element = Tile[newx][newy] = Store[x][y];
8488
8489     InitField(newx, newy, FALSE);
8490   }
8491   else if (element == EL_BD_MAGIC_WALL_FILLING)
8492   {
8493     element = Tile[newx][newy] = get_next_element(element);
8494     if (!game.magic_wall_active)
8495       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8496     Store[newx][newy] = Store[x][y];
8497   }
8498   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8499   {
8500     Tile[x][y] = get_next_element(element);
8501     if (!game.magic_wall_active)
8502       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8503     element = Tile[newx][newy] = Store[x][y];
8504
8505     InitField(newx, newy, FALSE);
8506   }
8507   else if (element == EL_DC_MAGIC_WALL_FILLING)
8508   {
8509     element = Tile[newx][newy] = get_next_element(element);
8510     if (!game.magic_wall_active)
8511       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8512     Store[newx][newy] = Store[x][y];
8513   }
8514   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8515   {
8516     Tile[x][y] = get_next_element(element);
8517     if (!game.magic_wall_active)
8518       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8519     element = Tile[newx][newy] = Store[x][y];
8520
8521     InitField(newx, newy, FALSE);
8522   }
8523   else if (element == EL_AMOEBA_DROPPING)
8524   {
8525     Tile[x][y] = get_next_element(element);
8526     element = Tile[newx][newy] = Store[x][y];
8527   }
8528   else if (element == EL_SOKOBAN_OBJECT)
8529   {
8530     if (Back[x][y])
8531       Tile[x][y] = Back[x][y];
8532
8533     if (Back[newx][newy])
8534       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8535
8536     Back[x][y] = Back[newx][newy] = 0;
8537   }
8538
8539   Store[x][y] = EL_EMPTY;
8540   MovPos[x][y] = 0;
8541   MovDir[x][y] = 0;
8542   MovDelay[x][y] = 0;
8543
8544   MovDelay[newx][newy] = 0;
8545
8546   if (CAN_CHANGE_OR_HAS_ACTION(element))
8547   {
8548     // copy element change control values to new field
8549     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8550     ChangePage[newx][newy]  = ChangePage[x][y];
8551     ChangeCount[newx][newy] = ChangeCount[x][y];
8552     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8553   }
8554
8555   CustomValue[newx][newy] = CustomValue[x][y];
8556
8557   ChangeDelay[x][y] = 0;
8558   ChangePage[x][y] = -1;
8559   ChangeCount[x][y] = 0;
8560   ChangeEvent[x][y] = -1;
8561
8562   CustomValue[x][y] = 0;
8563
8564   // copy animation control values to new field
8565   GfxFrame[newx][newy]  = GfxFrame[x][y];
8566   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8567   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8568   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8569
8570   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8571
8572   // some elements can leave other elements behind after moving
8573   if (ei->move_leave_element != EL_EMPTY &&
8574       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8575       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8576   {
8577     int move_leave_element = ei->move_leave_element;
8578
8579     // this makes it possible to leave the removed element again
8580     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8581       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8582
8583     Tile[x][y] = move_leave_element;
8584
8585     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8586       MovDir[x][y] = direction;
8587
8588     InitField(x, y, FALSE);
8589
8590     if (GFX_CRUMBLED(Tile[x][y]))
8591       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8592
8593     if (ELEM_IS_PLAYER(move_leave_element))
8594       RelocatePlayer(x, y, move_leave_element);
8595   }
8596
8597   // do this after checking for left-behind element
8598   ResetGfxAnimation(x, y);      // reset animation values for old field
8599
8600   if (!CAN_MOVE(element) ||
8601       (CAN_FALL(element) && direction == MV_DOWN &&
8602        (element == EL_SPRING ||
8603         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8604         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8605     GfxDir[x][y] = MovDir[newx][newy] = 0;
8606
8607   TEST_DrawLevelField(x, y);
8608   TEST_DrawLevelField(newx, newy);
8609
8610   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8611
8612   // prevent pushed element from moving on in pushed direction
8613   if (pushed_by_player && CAN_MOVE(element) &&
8614       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8615       !(element_info[element].move_pattern & direction))
8616     TurnRound(newx, newy);
8617
8618   // prevent elements on conveyor belt from moving on in last direction
8619   if (pushed_by_conveyor && CAN_FALL(element) &&
8620       direction & MV_HORIZONTAL)
8621     MovDir[newx][newy] = 0;
8622
8623   if (!pushed_by_player)
8624   {
8625     int nextx = newx + dx, nexty = newy + dy;
8626     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8627
8628     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8629
8630     if (CAN_FALL(element) && direction == MV_DOWN)
8631       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8632
8633     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8634       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8635
8636     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8637       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8638   }
8639
8640   if (DONT_TOUCH(element))      // object may be nasty to player or others
8641   {
8642     TestIfBadThingTouchesPlayer(newx, newy);
8643     TestIfBadThingTouchesFriend(newx, newy);
8644
8645     if (!IS_CUSTOM_ELEMENT(element))
8646       TestIfBadThingTouchesOtherBadThing(newx, newy);
8647   }
8648   else if (element == EL_PENGUIN)
8649     TestIfFriendTouchesBadThing(newx, newy);
8650
8651   if (DONT_GET_HIT_BY(element))
8652   {
8653     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8654   }
8655
8656   // give the player one last chance (one more frame) to move away
8657   if (CAN_FALL(element) && direction == MV_DOWN &&
8658       (last_line || (!IS_FREE(x, newy + 1) &&
8659                      (!IS_PLAYER(x, newy + 1) ||
8660                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8661     Impact(x, newy);
8662
8663   if (pushed_by_player && !game.use_change_when_pushing_bug)
8664   {
8665     int push_side = MV_DIR_OPPOSITE(direction);
8666     struct PlayerInfo *player = PLAYERINFO(x, y);
8667
8668     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8669                                player->index_bit, push_side);
8670     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8671                                         player->index_bit, push_side);
8672   }
8673
8674   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8675     MovDelay[newx][newy] = 1;
8676
8677   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8678
8679   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8680   TestIfElementHitsCustomElement(newx, newy, direction);
8681   TestIfPlayerTouchesCustomElement(newx, newy);
8682   TestIfElementTouchesCustomElement(newx, newy);
8683
8684   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8685       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8686     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8687                              MV_DIR_OPPOSITE(direction));
8688 }
8689
8690 int AmoebaNeighbourNr(int ax, int ay)
8691 {
8692   int i;
8693   int element = Tile[ax][ay];
8694   int group_nr = 0;
8695   static int xy[4][2] =
8696   {
8697     { 0, -1 },
8698     { -1, 0 },
8699     { +1, 0 },
8700     { 0, +1 }
8701   };
8702
8703   for (i = 0; i < NUM_DIRECTIONS; i++)
8704   {
8705     int x = ax + xy[i][0];
8706     int y = ay + xy[i][1];
8707
8708     if (!IN_LEV_FIELD(x, y))
8709       continue;
8710
8711     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8712       group_nr = AmoebaNr[x][y];
8713   }
8714
8715   return group_nr;
8716 }
8717
8718 static void AmoebaMerge(int ax, int ay)
8719 {
8720   int i, x, y, xx, yy;
8721   int new_group_nr = AmoebaNr[ax][ay];
8722   static int xy[4][2] =
8723   {
8724     { 0, -1 },
8725     { -1, 0 },
8726     { +1, 0 },
8727     { 0, +1 }
8728   };
8729
8730   if (new_group_nr == 0)
8731     return;
8732
8733   for (i = 0; i < NUM_DIRECTIONS; i++)
8734   {
8735     x = ax + xy[i][0];
8736     y = ay + xy[i][1];
8737
8738     if (!IN_LEV_FIELD(x, y))
8739       continue;
8740
8741     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8742          Tile[x][y] == EL_BD_AMOEBA ||
8743          Tile[x][y] == EL_AMOEBA_DEAD) &&
8744         AmoebaNr[x][y] != new_group_nr)
8745     {
8746       int old_group_nr = AmoebaNr[x][y];
8747
8748       if (old_group_nr == 0)
8749         return;
8750
8751       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8752       AmoebaCnt[old_group_nr] = 0;
8753       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8754       AmoebaCnt2[old_group_nr] = 0;
8755
8756       SCAN_PLAYFIELD(xx, yy)
8757       {
8758         if (AmoebaNr[xx][yy] == old_group_nr)
8759           AmoebaNr[xx][yy] = new_group_nr;
8760       }
8761     }
8762   }
8763 }
8764
8765 void AmoebaToDiamond(int ax, int ay)
8766 {
8767   int i, x, y;
8768
8769   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8770   {
8771     int group_nr = AmoebaNr[ax][ay];
8772
8773 #ifdef DEBUG
8774     if (group_nr == 0)
8775     {
8776       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8777       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8778
8779       return;
8780     }
8781 #endif
8782
8783     SCAN_PLAYFIELD(x, y)
8784     {
8785       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8786       {
8787         AmoebaNr[x][y] = 0;
8788         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8789       }
8790     }
8791
8792     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8793                             SND_AMOEBA_TURNING_TO_GEM :
8794                             SND_AMOEBA_TURNING_TO_ROCK));
8795     Bang(ax, ay);
8796   }
8797   else
8798   {
8799     static int xy[4][2] =
8800     {
8801       { 0, -1 },
8802       { -1, 0 },
8803       { +1, 0 },
8804       { 0, +1 }
8805     };
8806
8807     for (i = 0; i < NUM_DIRECTIONS; i++)
8808     {
8809       x = ax + xy[i][0];
8810       y = ay + xy[i][1];
8811
8812       if (!IN_LEV_FIELD(x, y))
8813         continue;
8814
8815       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8816       {
8817         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8818                               SND_AMOEBA_TURNING_TO_GEM :
8819                               SND_AMOEBA_TURNING_TO_ROCK));
8820         Bang(x, y);
8821       }
8822     }
8823   }
8824 }
8825
8826 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8827 {
8828   int x, y;
8829   int group_nr = AmoebaNr[ax][ay];
8830   boolean done = FALSE;
8831
8832 #ifdef DEBUG
8833   if (group_nr == 0)
8834   {
8835     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8836     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8837
8838     return;
8839   }
8840 #endif
8841
8842   SCAN_PLAYFIELD(x, y)
8843   {
8844     if (AmoebaNr[x][y] == group_nr &&
8845         (Tile[x][y] == EL_AMOEBA_DEAD ||
8846          Tile[x][y] == EL_BD_AMOEBA ||
8847          Tile[x][y] == EL_AMOEBA_GROWING))
8848     {
8849       AmoebaNr[x][y] = 0;
8850       Tile[x][y] = new_element;
8851       InitField(x, y, FALSE);
8852       TEST_DrawLevelField(x, y);
8853       done = TRUE;
8854     }
8855   }
8856
8857   if (done)
8858     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8859                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8860                             SND_BD_AMOEBA_TURNING_TO_GEM));
8861 }
8862
8863 static void AmoebaGrowing(int x, int y)
8864 {
8865   static unsigned int sound_delay = 0;
8866   static unsigned int sound_delay_value = 0;
8867
8868   if (!MovDelay[x][y])          // start new growing cycle
8869   {
8870     MovDelay[x][y] = 7;
8871
8872     if (DelayReached(&sound_delay, sound_delay_value))
8873     {
8874       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8875       sound_delay_value = 30;
8876     }
8877   }
8878
8879   if (MovDelay[x][y])           // wait some time before growing bigger
8880   {
8881     MovDelay[x][y]--;
8882     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8883     {
8884       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8885                                            6 - MovDelay[x][y]);
8886
8887       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8888     }
8889
8890     if (!MovDelay[x][y])
8891     {
8892       Tile[x][y] = Store[x][y];
8893       Store[x][y] = 0;
8894       TEST_DrawLevelField(x, y);
8895     }
8896   }
8897 }
8898
8899 static void AmoebaShrinking(int x, int y)
8900 {
8901   static unsigned int sound_delay = 0;
8902   static unsigned int sound_delay_value = 0;
8903
8904   if (!MovDelay[x][y])          // start new shrinking cycle
8905   {
8906     MovDelay[x][y] = 7;
8907
8908     if (DelayReached(&sound_delay, sound_delay_value))
8909       sound_delay_value = 30;
8910   }
8911
8912   if (MovDelay[x][y])           // wait some time before shrinking
8913   {
8914     MovDelay[x][y]--;
8915     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8916     {
8917       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8918                                            6 - MovDelay[x][y]);
8919
8920       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8921     }
8922
8923     if (!MovDelay[x][y])
8924     {
8925       Tile[x][y] = EL_EMPTY;
8926       TEST_DrawLevelField(x, y);
8927
8928       // don't let mole enter this field in this cycle;
8929       // (give priority to objects falling to this field from above)
8930       Stop[x][y] = TRUE;
8931     }
8932   }
8933 }
8934
8935 static void AmoebaReproduce(int ax, int ay)
8936 {
8937   int i;
8938   int element = Tile[ax][ay];
8939   int graphic = el2img(element);
8940   int newax = ax, neway = ay;
8941   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8942   static int xy[4][2] =
8943   {
8944     { 0, -1 },
8945     { -1, 0 },
8946     { +1, 0 },
8947     { 0, +1 }
8948   };
8949
8950   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8951   {
8952     Tile[ax][ay] = EL_AMOEBA_DEAD;
8953     TEST_DrawLevelField(ax, ay);
8954     return;
8955   }
8956
8957   if (IS_ANIMATED(graphic))
8958     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8959
8960   if (!MovDelay[ax][ay])        // start making new amoeba field
8961     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8962
8963   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8964   {
8965     MovDelay[ax][ay]--;
8966     if (MovDelay[ax][ay])
8967       return;
8968   }
8969
8970   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8971   {
8972     int start = RND(4);
8973     int x = ax + xy[start][0];
8974     int y = ay + xy[start][1];
8975
8976     if (!IN_LEV_FIELD(x, y))
8977       return;
8978
8979     if (IS_FREE(x, y) ||
8980         CAN_GROW_INTO(Tile[x][y]) ||
8981         Tile[x][y] == EL_QUICKSAND_EMPTY ||
8982         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
8983     {
8984       newax = x;
8985       neway = y;
8986     }
8987
8988     if (newax == ax && neway == ay)
8989       return;
8990   }
8991   else                          // normal or "filled" (BD style) amoeba
8992   {
8993     int start = RND(4);
8994     boolean waiting_for_player = FALSE;
8995
8996     for (i = 0; i < NUM_DIRECTIONS; i++)
8997     {
8998       int j = (start + i) % 4;
8999       int x = ax + xy[j][0];
9000       int y = ay + xy[j][1];
9001
9002       if (!IN_LEV_FIELD(x, y))
9003         continue;
9004
9005       if (IS_FREE(x, y) ||
9006           CAN_GROW_INTO(Tile[x][y]) ||
9007           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9008           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9009       {
9010         newax = x;
9011         neway = y;
9012         break;
9013       }
9014       else if (IS_PLAYER(x, y))
9015         waiting_for_player = TRUE;
9016     }
9017
9018     if (newax == ax && neway == ay)             // amoeba cannot grow
9019     {
9020       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9021       {
9022         Tile[ax][ay] = EL_AMOEBA_DEAD;
9023         TEST_DrawLevelField(ax, ay);
9024         AmoebaCnt[AmoebaNr[ax][ay]]--;
9025
9026         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9027         {
9028           if (element == EL_AMOEBA_FULL)
9029             AmoebaToDiamond(ax, ay);
9030           else if (element == EL_BD_AMOEBA)
9031             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9032         }
9033       }
9034       return;
9035     }
9036     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9037     {
9038       // amoeba gets larger by growing in some direction
9039
9040       int new_group_nr = AmoebaNr[ax][ay];
9041
9042 #ifdef DEBUG
9043   if (new_group_nr == 0)
9044   {
9045     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9046           newax, neway);
9047     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9048
9049     return;
9050   }
9051 #endif
9052
9053       AmoebaNr[newax][neway] = new_group_nr;
9054       AmoebaCnt[new_group_nr]++;
9055       AmoebaCnt2[new_group_nr]++;
9056
9057       // if amoeba touches other amoeba(s) after growing, unify them
9058       AmoebaMerge(newax, neway);
9059
9060       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9061       {
9062         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9063         return;
9064       }
9065     }
9066   }
9067
9068   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9069       (neway == lev_fieldy - 1 && newax != ax))
9070   {
9071     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9072     Store[newax][neway] = element;
9073   }
9074   else if (neway == ay || element == EL_EMC_DRIPPER)
9075   {
9076     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9077
9078     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9079   }
9080   else
9081   {
9082     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9083     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9084     Store[ax][ay] = EL_AMOEBA_DROP;
9085     ContinueMoving(ax, ay);
9086     return;
9087   }
9088
9089   TEST_DrawLevelField(newax, neway);
9090 }
9091
9092 static void Life(int ax, int ay)
9093 {
9094   int x1, y1, x2, y2;
9095   int life_time = 40;
9096   int element = Tile[ax][ay];
9097   int graphic = el2img(element);
9098   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9099                          level.biomaze);
9100   boolean changed = FALSE;
9101
9102   if (IS_ANIMATED(graphic))
9103     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9104
9105   if (Stop[ax][ay])
9106     return;
9107
9108   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9109     MovDelay[ax][ay] = life_time;
9110
9111   if (MovDelay[ax][ay])         // wait some time before next cycle
9112   {
9113     MovDelay[ax][ay]--;
9114     if (MovDelay[ax][ay])
9115       return;
9116   }
9117
9118   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9119   {
9120     int xx = ax+x1, yy = ay+y1;
9121     int old_element = Tile[xx][yy];
9122     int num_neighbours = 0;
9123
9124     if (!IN_LEV_FIELD(xx, yy))
9125       continue;
9126
9127     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9128     {
9129       int x = xx+x2, y = yy+y2;
9130
9131       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9132         continue;
9133
9134       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9135       boolean is_neighbour = FALSE;
9136
9137       if (level.use_life_bugs)
9138         is_neighbour =
9139           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9140            (IS_FREE(x, y)                             &&  Stop[x][y]));
9141       else
9142         is_neighbour =
9143           (Last[x][y] == element || is_player_cell);
9144
9145       if (is_neighbour)
9146         num_neighbours++;
9147     }
9148
9149     boolean is_free = FALSE;
9150
9151     if (level.use_life_bugs)
9152       is_free = (IS_FREE(xx, yy));
9153     else
9154       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9155
9156     if (xx == ax && yy == ay)           // field in the middle
9157     {
9158       if (num_neighbours < life_parameter[0] ||
9159           num_neighbours > life_parameter[1])
9160       {
9161         Tile[xx][yy] = EL_EMPTY;
9162         if (Tile[xx][yy] != old_element)
9163           TEST_DrawLevelField(xx, yy);
9164         Stop[xx][yy] = TRUE;
9165         changed = TRUE;
9166       }
9167     }
9168     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9169     {                                   // free border field
9170       if (num_neighbours >= life_parameter[2] &&
9171           num_neighbours <= life_parameter[3])
9172       {
9173         Tile[xx][yy] = element;
9174         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9175         if (Tile[xx][yy] != old_element)
9176           TEST_DrawLevelField(xx, yy);
9177         Stop[xx][yy] = TRUE;
9178         changed = TRUE;
9179       }
9180     }
9181   }
9182
9183   if (changed)
9184     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9185                    SND_GAME_OF_LIFE_GROWING);
9186 }
9187
9188 static void InitRobotWheel(int x, int y)
9189 {
9190   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9191 }
9192
9193 static void RunRobotWheel(int x, int y)
9194 {
9195   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9196 }
9197
9198 static void StopRobotWheel(int x, int y)
9199 {
9200   if (game.robot_wheel_x == x &&
9201       game.robot_wheel_y == y)
9202   {
9203     game.robot_wheel_x = -1;
9204     game.robot_wheel_y = -1;
9205     game.robot_wheel_active = FALSE;
9206   }
9207 }
9208
9209 static void InitTimegateWheel(int x, int y)
9210 {
9211   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9212 }
9213
9214 static void RunTimegateWheel(int x, int y)
9215 {
9216   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9217 }
9218
9219 static void InitMagicBallDelay(int x, int y)
9220 {
9221   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9222 }
9223
9224 static void ActivateMagicBall(int bx, int by)
9225 {
9226   int x, y;
9227
9228   if (level.ball_random)
9229   {
9230     int pos_border = RND(8);    // select one of the eight border elements
9231     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9232     int xx = pos_content % 3;
9233     int yy = pos_content / 3;
9234
9235     x = bx - 1 + xx;
9236     y = by - 1 + yy;
9237
9238     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9239       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9240   }
9241   else
9242   {
9243     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9244     {
9245       int xx = x - bx + 1;
9246       int yy = y - by + 1;
9247
9248       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9249         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9250     }
9251   }
9252
9253   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9254 }
9255
9256 static void CheckExit(int x, int y)
9257 {
9258   if (game.gems_still_needed > 0 ||
9259       game.sokoban_fields_still_needed > 0 ||
9260       game.sokoban_objects_still_needed > 0 ||
9261       game.lights_still_needed > 0)
9262   {
9263     int element = Tile[x][y];
9264     int graphic = el2img(element);
9265
9266     if (IS_ANIMATED(graphic))
9267       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9268
9269     return;
9270   }
9271
9272   // do not re-open exit door closed after last player
9273   if (game.all_players_gone)
9274     return;
9275
9276   Tile[x][y] = EL_EXIT_OPENING;
9277
9278   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9279 }
9280
9281 static void CheckExitEM(int x, int y)
9282 {
9283   if (game.gems_still_needed > 0 ||
9284       game.sokoban_fields_still_needed > 0 ||
9285       game.sokoban_objects_still_needed > 0 ||
9286       game.lights_still_needed > 0)
9287   {
9288     int element = Tile[x][y];
9289     int graphic = el2img(element);
9290
9291     if (IS_ANIMATED(graphic))
9292       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9293
9294     return;
9295   }
9296
9297   // do not re-open exit door closed after last player
9298   if (game.all_players_gone)
9299     return;
9300
9301   Tile[x][y] = EL_EM_EXIT_OPENING;
9302
9303   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9304 }
9305
9306 static void CheckExitSteel(int x, int y)
9307 {
9308   if (game.gems_still_needed > 0 ||
9309       game.sokoban_fields_still_needed > 0 ||
9310       game.sokoban_objects_still_needed > 0 ||
9311       game.lights_still_needed > 0)
9312   {
9313     int element = Tile[x][y];
9314     int graphic = el2img(element);
9315
9316     if (IS_ANIMATED(graphic))
9317       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9318
9319     return;
9320   }
9321
9322   // do not re-open exit door closed after last player
9323   if (game.all_players_gone)
9324     return;
9325
9326   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9327
9328   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9329 }
9330
9331 static void CheckExitSteelEM(int x, int y)
9332 {
9333   if (game.gems_still_needed > 0 ||
9334       game.sokoban_fields_still_needed > 0 ||
9335       game.sokoban_objects_still_needed > 0 ||
9336       game.lights_still_needed > 0)
9337   {
9338     int element = Tile[x][y];
9339     int graphic = el2img(element);
9340
9341     if (IS_ANIMATED(graphic))
9342       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9343
9344     return;
9345   }
9346
9347   // do not re-open exit door closed after last player
9348   if (game.all_players_gone)
9349     return;
9350
9351   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9352
9353   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9354 }
9355
9356 static void CheckExitSP(int x, int y)
9357 {
9358   if (game.gems_still_needed > 0)
9359   {
9360     int element = Tile[x][y];
9361     int graphic = el2img(element);
9362
9363     if (IS_ANIMATED(graphic))
9364       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9365
9366     return;
9367   }
9368
9369   // do not re-open exit door closed after last player
9370   if (game.all_players_gone)
9371     return;
9372
9373   Tile[x][y] = EL_SP_EXIT_OPENING;
9374
9375   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9376 }
9377
9378 static void CloseAllOpenTimegates(void)
9379 {
9380   int x, y;
9381
9382   SCAN_PLAYFIELD(x, y)
9383   {
9384     int element = Tile[x][y];
9385
9386     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9387     {
9388       Tile[x][y] = EL_TIMEGATE_CLOSING;
9389
9390       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9391     }
9392   }
9393 }
9394
9395 static void DrawTwinkleOnField(int x, int y)
9396 {
9397   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9398     return;
9399
9400   if (Tile[x][y] == EL_BD_DIAMOND)
9401     return;
9402
9403   if (MovDelay[x][y] == 0)      // next animation frame
9404     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9405
9406   if (MovDelay[x][y] != 0)      // wait some time before next frame
9407   {
9408     MovDelay[x][y]--;
9409
9410     DrawLevelElementAnimation(x, y, Tile[x][y]);
9411
9412     if (MovDelay[x][y] != 0)
9413     {
9414       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9415                                            10 - MovDelay[x][y]);
9416
9417       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9418     }
9419   }
9420 }
9421
9422 static void MauerWaechst(int x, int y)
9423 {
9424   int delay = 6;
9425
9426   if (!MovDelay[x][y])          // next animation frame
9427     MovDelay[x][y] = 3 * delay;
9428
9429   if (MovDelay[x][y])           // wait some time before next frame
9430   {
9431     MovDelay[x][y]--;
9432
9433     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9434     {
9435       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9436       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9437
9438       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9439     }
9440
9441     if (!MovDelay[x][y])
9442     {
9443       if (MovDir[x][y] == MV_LEFT)
9444       {
9445         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9446           TEST_DrawLevelField(x - 1, y);
9447       }
9448       else if (MovDir[x][y] == MV_RIGHT)
9449       {
9450         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9451           TEST_DrawLevelField(x + 1, y);
9452       }
9453       else if (MovDir[x][y] == MV_UP)
9454       {
9455         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9456           TEST_DrawLevelField(x, y - 1);
9457       }
9458       else
9459       {
9460         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9461           TEST_DrawLevelField(x, y + 1);
9462       }
9463
9464       Tile[x][y] = Store[x][y];
9465       Store[x][y] = 0;
9466       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9467       TEST_DrawLevelField(x, y);
9468     }
9469   }
9470 }
9471
9472 static void MauerAbleger(int ax, int ay)
9473 {
9474   int element = Tile[ax][ay];
9475   int graphic = el2img(element);
9476   boolean oben_frei = FALSE, unten_frei = FALSE;
9477   boolean links_frei = FALSE, rechts_frei = FALSE;
9478   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9479   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9480   boolean new_wall = FALSE;
9481
9482   if (IS_ANIMATED(graphic))
9483     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9484
9485   if (!MovDelay[ax][ay])        // start building new wall
9486     MovDelay[ax][ay] = 6;
9487
9488   if (MovDelay[ax][ay])         // wait some time before building new wall
9489   {
9490     MovDelay[ax][ay]--;
9491     if (MovDelay[ax][ay])
9492       return;
9493   }
9494
9495   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9496     oben_frei = TRUE;
9497   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9498     unten_frei = TRUE;
9499   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9500     links_frei = TRUE;
9501   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9502     rechts_frei = TRUE;
9503
9504   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9505       element == EL_EXPANDABLE_WALL_ANY)
9506   {
9507     if (oben_frei)
9508     {
9509       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9510       Store[ax][ay-1] = element;
9511       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9512       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9513         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9514                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9515       new_wall = TRUE;
9516     }
9517     if (unten_frei)
9518     {
9519       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9520       Store[ax][ay+1] = element;
9521       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9522       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9523         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9524                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9525       new_wall = TRUE;
9526     }
9527   }
9528
9529   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9530       element == EL_EXPANDABLE_WALL_ANY ||
9531       element == EL_EXPANDABLE_WALL ||
9532       element == EL_BD_EXPANDABLE_WALL)
9533   {
9534     if (links_frei)
9535     {
9536       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9537       Store[ax-1][ay] = element;
9538       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9539       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9540         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9541                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9542       new_wall = TRUE;
9543     }
9544
9545     if (rechts_frei)
9546     {
9547       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9548       Store[ax+1][ay] = element;
9549       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9550       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9551         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9552                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9553       new_wall = TRUE;
9554     }
9555   }
9556
9557   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9558     TEST_DrawLevelField(ax, ay);
9559
9560   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9561     oben_massiv = TRUE;
9562   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9563     unten_massiv = TRUE;
9564   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9565     links_massiv = TRUE;
9566   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9567     rechts_massiv = TRUE;
9568
9569   if (((oben_massiv && unten_massiv) ||
9570        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9571        element == EL_EXPANDABLE_WALL) &&
9572       ((links_massiv && rechts_massiv) ||
9573        element == EL_EXPANDABLE_WALL_VERTICAL))
9574     Tile[ax][ay] = EL_WALL;
9575
9576   if (new_wall)
9577     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9578 }
9579
9580 static void MauerAblegerStahl(int ax, int ay)
9581 {
9582   int element = Tile[ax][ay];
9583   int graphic = el2img(element);
9584   boolean oben_frei = FALSE, unten_frei = FALSE;
9585   boolean links_frei = FALSE, rechts_frei = FALSE;
9586   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9587   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9588   boolean new_wall = FALSE;
9589
9590   if (IS_ANIMATED(graphic))
9591     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9592
9593   if (!MovDelay[ax][ay])        // start building new wall
9594     MovDelay[ax][ay] = 6;
9595
9596   if (MovDelay[ax][ay])         // wait some time before building new wall
9597   {
9598     MovDelay[ax][ay]--;
9599     if (MovDelay[ax][ay])
9600       return;
9601   }
9602
9603   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9604     oben_frei = TRUE;
9605   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9606     unten_frei = TRUE;
9607   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9608     links_frei = TRUE;
9609   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9610     rechts_frei = TRUE;
9611
9612   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9613       element == EL_EXPANDABLE_STEELWALL_ANY)
9614   {
9615     if (oben_frei)
9616     {
9617       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9618       Store[ax][ay-1] = element;
9619       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9620       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9621         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9622                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9623       new_wall = TRUE;
9624     }
9625     if (unten_frei)
9626     {
9627       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9628       Store[ax][ay+1] = element;
9629       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9630       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9631         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9632                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9633       new_wall = TRUE;
9634     }
9635   }
9636
9637   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9638       element == EL_EXPANDABLE_STEELWALL_ANY)
9639   {
9640     if (links_frei)
9641     {
9642       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9643       Store[ax-1][ay] = element;
9644       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9645       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9646         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9647                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9648       new_wall = TRUE;
9649     }
9650
9651     if (rechts_frei)
9652     {
9653       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9654       Store[ax+1][ay] = element;
9655       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9656       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9657         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9658                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9659       new_wall = TRUE;
9660     }
9661   }
9662
9663   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9664     oben_massiv = TRUE;
9665   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9666     unten_massiv = TRUE;
9667   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9668     links_massiv = TRUE;
9669   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9670     rechts_massiv = TRUE;
9671
9672   if (((oben_massiv && unten_massiv) ||
9673        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9674       ((links_massiv && rechts_massiv) ||
9675        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9676     Tile[ax][ay] = EL_STEELWALL;
9677
9678   if (new_wall)
9679     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9680 }
9681
9682 static void CheckForDragon(int x, int y)
9683 {
9684   int i, j;
9685   boolean dragon_found = FALSE;
9686   static int xy[4][2] =
9687   {
9688     { 0, -1 },
9689     { -1, 0 },
9690     { +1, 0 },
9691     { 0, +1 }
9692   };
9693
9694   for (i = 0; i < NUM_DIRECTIONS; i++)
9695   {
9696     for (j = 0; j < 4; j++)
9697     {
9698       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9699
9700       if (IN_LEV_FIELD(xx, yy) &&
9701           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9702       {
9703         if (Tile[xx][yy] == EL_DRAGON)
9704           dragon_found = TRUE;
9705       }
9706       else
9707         break;
9708     }
9709   }
9710
9711   if (!dragon_found)
9712   {
9713     for (i = 0; i < NUM_DIRECTIONS; i++)
9714     {
9715       for (j = 0; j < 3; j++)
9716       {
9717         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9718   
9719         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9720         {
9721           Tile[xx][yy] = EL_EMPTY;
9722           TEST_DrawLevelField(xx, yy);
9723         }
9724         else
9725           break;
9726       }
9727     }
9728   }
9729 }
9730
9731 static void InitBuggyBase(int x, int y)
9732 {
9733   int element = Tile[x][y];
9734   int activating_delay = FRAMES_PER_SECOND / 4;
9735
9736   ChangeDelay[x][y] =
9737     (element == EL_SP_BUGGY_BASE ?
9738      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9739      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9740      activating_delay :
9741      element == EL_SP_BUGGY_BASE_ACTIVE ?
9742      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9743 }
9744
9745 static void WarnBuggyBase(int x, int y)
9746 {
9747   int i;
9748   static int xy[4][2] =
9749   {
9750     { 0, -1 },
9751     { -1, 0 },
9752     { +1, 0 },
9753     { 0, +1 }
9754   };
9755
9756   for (i = 0; i < NUM_DIRECTIONS; i++)
9757   {
9758     int xx = x + xy[i][0];
9759     int yy = y + xy[i][1];
9760
9761     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9762     {
9763       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9764
9765       break;
9766     }
9767   }
9768 }
9769
9770 static void InitTrap(int x, int y)
9771 {
9772   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9773 }
9774
9775 static void ActivateTrap(int x, int y)
9776 {
9777   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9778 }
9779
9780 static void ChangeActiveTrap(int x, int y)
9781 {
9782   int graphic = IMG_TRAP_ACTIVE;
9783
9784   // if new animation frame was drawn, correct crumbled sand border
9785   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9786     TEST_DrawLevelFieldCrumbled(x, y);
9787 }
9788
9789 static int getSpecialActionElement(int element, int number, int base_element)
9790 {
9791   return (element != EL_EMPTY ? element :
9792           number != -1 ? base_element + number - 1 :
9793           EL_EMPTY);
9794 }
9795
9796 static int getModifiedActionNumber(int value_old, int operator, int operand,
9797                                    int value_min, int value_max)
9798 {
9799   int value_new = (operator == CA_MODE_SET      ? operand :
9800                    operator == CA_MODE_ADD      ? value_old + operand :
9801                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9802                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9803                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9804                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9805                    value_old);
9806
9807   return (value_new < value_min ? value_min :
9808           value_new > value_max ? value_max :
9809           value_new);
9810 }
9811
9812 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9813 {
9814   struct ElementInfo *ei = &element_info[element];
9815   struct ElementChangeInfo *change = &ei->change_page[page];
9816   int target_element = change->target_element;
9817   int action_type = change->action_type;
9818   int action_mode = change->action_mode;
9819   int action_arg = change->action_arg;
9820   int action_element = change->action_element;
9821   int i;
9822
9823   if (!change->has_action)
9824     return;
9825
9826   // ---------- determine action paramater values -----------------------------
9827
9828   int level_time_value =
9829     (level.time > 0 ? TimeLeft :
9830      TimePlayed);
9831
9832   int action_arg_element_raw =
9833     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9834      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9835      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9836      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9837      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9838      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9839      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9840      EL_EMPTY);
9841   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9842
9843   int action_arg_direction =
9844     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9845      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9846      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9847      change->actual_trigger_side :
9848      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9849      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9850      MV_NONE);
9851
9852   int action_arg_number_min =
9853     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9854      CA_ARG_MIN);
9855
9856   int action_arg_number_max =
9857     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9858      action_type == CA_SET_LEVEL_GEMS ? 999 :
9859      action_type == CA_SET_LEVEL_TIME ? 9999 :
9860      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9861      action_type == CA_SET_CE_VALUE ? 9999 :
9862      action_type == CA_SET_CE_SCORE ? 9999 :
9863      CA_ARG_MAX);
9864
9865   int action_arg_number_reset =
9866     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9867      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9868      action_type == CA_SET_LEVEL_TIME ? level.time :
9869      action_type == CA_SET_LEVEL_SCORE ? 0 :
9870      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9871      action_type == CA_SET_CE_SCORE ? 0 :
9872      0);
9873
9874   int action_arg_number =
9875     (action_arg <= CA_ARG_MAX ? action_arg :
9876      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9877      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9878      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9879      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9880      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9881      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9882      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9883      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9884      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9885      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9886      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9887      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9888      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9889      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9890      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9891      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9892      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9893      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9894      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9895      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9896      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9897      -1);
9898
9899   int action_arg_number_old =
9900     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9901      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9902      action_type == CA_SET_LEVEL_SCORE ? game.score :
9903      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9904      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9905      0);
9906
9907   int action_arg_number_new =
9908     getModifiedActionNumber(action_arg_number_old,
9909                             action_mode, action_arg_number,
9910                             action_arg_number_min, action_arg_number_max);
9911
9912   int trigger_player_bits =
9913     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9914      change->actual_trigger_player_bits : change->trigger_player);
9915
9916   int action_arg_player_bits =
9917     (action_arg >= CA_ARG_PLAYER_1 &&
9918      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9919      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9920      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9921      PLAYER_BITS_ANY);
9922
9923   // ---------- execute action  -----------------------------------------------
9924
9925   switch (action_type)
9926   {
9927     case CA_NO_ACTION:
9928     {
9929       return;
9930     }
9931
9932     // ---------- level actions  ----------------------------------------------
9933
9934     case CA_RESTART_LEVEL:
9935     {
9936       game.restart_level = TRUE;
9937
9938       break;
9939     }
9940
9941     case CA_SHOW_ENVELOPE:
9942     {
9943       int element = getSpecialActionElement(action_arg_element,
9944                                             action_arg_number, EL_ENVELOPE_1);
9945
9946       if (IS_ENVELOPE(element))
9947         local_player->show_envelope = element;
9948
9949       break;
9950     }
9951
9952     case CA_SET_LEVEL_TIME:
9953     {
9954       if (level.time > 0)       // only modify limited time value
9955       {
9956         TimeLeft = action_arg_number_new;
9957
9958         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9959
9960         DisplayGameControlValues();
9961
9962         if (!TimeLeft && setup.time_limit)
9963           for (i = 0; i < MAX_PLAYERS; i++)
9964             KillPlayer(&stored_player[i]);
9965       }
9966
9967       break;
9968     }
9969
9970     case CA_SET_LEVEL_SCORE:
9971     {
9972       game.score = action_arg_number_new;
9973
9974       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9975
9976       DisplayGameControlValues();
9977
9978       break;
9979     }
9980
9981     case CA_SET_LEVEL_GEMS:
9982     {
9983       game.gems_still_needed = action_arg_number_new;
9984
9985       game.snapshot.collected_item = TRUE;
9986
9987       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9988
9989       DisplayGameControlValues();
9990
9991       break;
9992     }
9993
9994     case CA_SET_LEVEL_WIND:
9995     {
9996       game.wind_direction = action_arg_direction;
9997
9998       break;
9999     }
10000
10001     case CA_SET_LEVEL_RANDOM_SEED:
10002     {
10003       // ensure that setting a new random seed while playing is predictable
10004       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10005
10006       break;
10007     }
10008
10009     // ---------- player actions  ---------------------------------------------
10010
10011     case CA_MOVE_PLAYER:
10012     case CA_MOVE_PLAYER_NEW:
10013     {
10014       // automatically move to the next field in specified direction
10015       for (i = 0; i < MAX_PLAYERS; i++)
10016         if (trigger_player_bits & (1 << i))
10017           if (action_type == CA_MOVE_PLAYER ||
10018               stored_player[i].MovPos == 0)
10019             stored_player[i].programmed_action = action_arg_direction;
10020
10021       break;
10022     }
10023
10024     case CA_EXIT_PLAYER:
10025     {
10026       for (i = 0; i < MAX_PLAYERS; i++)
10027         if (action_arg_player_bits & (1 << i))
10028           ExitPlayer(&stored_player[i]);
10029
10030       if (game.players_still_needed == 0)
10031         LevelSolved();
10032
10033       break;
10034     }
10035
10036     case CA_KILL_PLAYER:
10037     {
10038       for (i = 0; i < MAX_PLAYERS; i++)
10039         if (action_arg_player_bits & (1 << i))
10040           KillPlayer(&stored_player[i]);
10041
10042       break;
10043     }
10044
10045     case CA_SET_PLAYER_KEYS:
10046     {
10047       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10048       int element = getSpecialActionElement(action_arg_element,
10049                                             action_arg_number, EL_KEY_1);
10050
10051       if (IS_KEY(element))
10052       {
10053         for (i = 0; i < MAX_PLAYERS; i++)
10054         {
10055           if (trigger_player_bits & (1 << i))
10056           {
10057             stored_player[i].key[KEY_NR(element)] = key_state;
10058
10059             DrawGameDoorValues();
10060           }
10061         }
10062       }
10063
10064       break;
10065     }
10066
10067     case CA_SET_PLAYER_SPEED:
10068     {
10069       for (i = 0; i < MAX_PLAYERS; i++)
10070       {
10071         if (trigger_player_bits & (1 << i))
10072         {
10073           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10074
10075           if (action_arg == CA_ARG_SPEED_FASTER &&
10076               stored_player[i].cannot_move)
10077           {
10078             action_arg_number = STEPSIZE_VERY_SLOW;
10079           }
10080           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10081                    action_arg == CA_ARG_SPEED_FASTER)
10082           {
10083             action_arg_number = 2;
10084             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10085                            CA_MODE_MULTIPLY);
10086           }
10087           else if (action_arg == CA_ARG_NUMBER_RESET)
10088           {
10089             action_arg_number = level.initial_player_stepsize[i];
10090           }
10091
10092           move_stepsize =
10093             getModifiedActionNumber(move_stepsize,
10094                                     action_mode,
10095                                     action_arg_number,
10096                                     action_arg_number_min,
10097                                     action_arg_number_max);
10098
10099           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10100         }
10101       }
10102
10103       break;
10104     }
10105
10106     case CA_SET_PLAYER_SHIELD:
10107     {
10108       for (i = 0; i < MAX_PLAYERS; i++)
10109       {
10110         if (trigger_player_bits & (1 << i))
10111         {
10112           if (action_arg == CA_ARG_SHIELD_OFF)
10113           {
10114             stored_player[i].shield_normal_time_left = 0;
10115             stored_player[i].shield_deadly_time_left = 0;
10116           }
10117           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10118           {
10119             stored_player[i].shield_normal_time_left = 999999;
10120           }
10121           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10122           {
10123             stored_player[i].shield_normal_time_left = 999999;
10124             stored_player[i].shield_deadly_time_left = 999999;
10125           }
10126         }
10127       }
10128
10129       break;
10130     }
10131
10132     case CA_SET_PLAYER_GRAVITY:
10133     {
10134       for (i = 0; i < MAX_PLAYERS; i++)
10135       {
10136         if (trigger_player_bits & (1 << i))
10137         {
10138           stored_player[i].gravity =
10139             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10140              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10141              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10142              stored_player[i].gravity);
10143         }
10144       }
10145
10146       break;
10147     }
10148
10149     case CA_SET_PLAYER_ARTWORK:
10150     {
10151       for (i = 0; i < MAX_PLAYERS; i++)
10152       {
10153         if (trigger_player_bits & (1 << i))
10154         {
10155           int artwork_element = action_arg_element;
10156
10157           if (action_arg == CA_ARG_ELEMENT_RESET)
10158             artwork_element =
10159               (level.use_artwork_element[i] ? level.artwork_element[i] :
10160                stored_player[i].element_nr);
10161
10162           if (stored_player[i].artwork_element != artwork_element)
10163             stored_player[i].Frame = 0;
10164
10165           stored_player[i].artwork_element = artwork_element;
10166
10167           SetPlayerWaiting(&stored_player[i], FALSE);
10168
10169           // set number of special actions for bored and sleeping animation
10170           stored_player[i].num_special_action_bored =
10171             get_num_special_action(artwork_element,
10172                                    ACTION_BORING_1, ACTION_BORING_LAST);
10173           stored_player[i].num_special_action_sleeping =
10174             get_num_special_action(artwork_element,
10175                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10176         }
10177       }
10178
10179       break;
10180     }
10181
10182     case CA_SET_PLAYER_INVENTORY:
10183     {
10184       for (i = 0; i < MAX_PLAYERS; i++)
10185       {
10186         struct PlayerInfo *player = &stored_player[i];
10187         int j, k;
10188
10189         if (trigger_player_bits & (1 << i))
10190         {
10191           int inventory_element = action_arg_element;
10192
10193           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10194               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10195               action_arg == CA_ARG_ELEMENT_ACTION)
10196           {
10197             int element = inventory_element;
10198             int collect_count = element_info[element].collect_count_initial;
10199
10200             if (!IS_CUSTOM_ELEMENT(element))
10201               collect_count = 1;
10202
10203             if (collect_count == 0)
10204               player->inventory_infinite_element = element;
10205             else
10206               for (k = 0; k < collect_count; k++)
10207                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10208                   player->inventory_element[player->inventory_size++] =
10209                     element;
10210           }
10211           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10212                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10213                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10214           {
10215             if (player->inventory_infinite_element != EL_UNDEFINED &&
10216                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10217                                      action_arg_element_raw))
10218               player->inventory_infinite_element = EL_UNDEFINED;
10219
10220             for (k = 0, j = 0; j < player->inventory_size; j++)
10221             {
10222               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10223                                         action_arg_element_raw))
10224                 player->inventory_element[k++] = player->inventory_element[j];
10225             }
10226
10227             player->inventory_size = k;
10228           }
10229           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10230           {
10231             if (player->inventory_size > 0)
10232             {
10233               for (j = 0; j < player->inventory_size - 1; j++)
10234                 player->inventory_element[j] = player->inventory_element[j + 1];
10235
10236               player->inventory_size--;
10237             }
10238           }
10239           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10240           {
10241             if (player->inventory_size > 0)
10242               player->inventory_size--;
10243           }
10244           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10245           {
10246             player->inventory_infinite_element = EL_UNDEFINED;
10247             player->inventory_size = 0;
10248           }
10249           else if (action_arg == CA_ARG_INVENTORY_RESET)
10250           {
10251             player->inventory_infinite_element = EL_UNDEFINED;
10252             player->inventory_size = 0;
10253
10254             if (level.use_initial_inventory[i])
10255             {
10256               for (j = 0; j < level.initial_inventory_size[i]; j++)
10257               {
10258                 int element = level.initial_inventory_content[i][j];
10259                 int collect_count = element_info[element].collect_count_initial;
10260
10261                 if (!IS_CUSTOM_ELEMENT(element))
10262                   collect_count = 1;
10263
10264                 if (collect_count == 0)
10265                   player->inventory_infinite_element = element;
10266                 else
10267                   for (k = 0; k < collect_count; k++)
10268                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10269                       player->inventory_element[player->inventory_size++] =
10270                         element;
10271               }
10272             }
10273           }
10274         }
10275       }
10276
10277       break;
10278     }
10279
10280     // ---------- CE actions  -------------------------------------------------
10281
10282     case CA_SET_CE_VALUE:
10283     {
10284       int last_ce_value = CustomValue[x][y];
10285
10286       CustomValue[x][y] = action_arg_number_new;
10287
10288       if (CustomValue[x][y] != last_ce_value)
10289       {
10290         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10291         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10292
10293         if (CustomValue[x][y] == 0)
10294         {
10295           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10296           ChangeCount[x][y] = 0;        // allow at least one more change
10297
10298           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10299           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10300         }
10301       }
10302
10303       break;
10304     }
10305
10306     case CA_SET_CE_SCORE:
10307     {
10308       int last_ce_score = ei->collect_score;
10309
10310       ei->collect_score = action_arg_number_new;
10311
10312       if (ei->collect_score != last_ce_score)
10313       {
10314         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10315         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10316
10317         if (ei->collect_score == 0)
10318         {
10319           int xx, yy;
10320
10321           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10322           ChangeCount[x][y] = 0;        // allow at least one more change
10323
10324           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10325           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10326
10327           /*
10328             This is a very special case that seems to be a mixture between
10329             CheckElementChange() and CheckTriggeredElementChange(): while
10330             the first one only affects single elements that are triggered
10331             directly, the second one affects multiple elements in the playfield
10332             that are triggered indirectly by another element. This is a third
10333             case: Changing the CE score always affects multiple identical CEs,
10334             so every affected CE must be checked, not only the single CE for
10335             which the CE score was changed in the first place (as every instance
10336             of that CE shares the same CE score, and therefore also can change)!
10337           */
10338           SCAN_PLAYFIELD(xx, yy)
10339           {
10340             if (Tile[xx][yy] == element)
10341               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10342                                  CE_SCORE_GETS_ZERO);
10343           }
10344         }
10345       }
10346
10347       break;
10348     }
10349
10350     case CA_SET_CE_ARTWORK:
10351     {
10352       int artwork_element = action_arg_element;
10353       boolean reset_frame = FALSE;
10354       int xx, yy;
10355
10356       if (action_arg == CA_ARG_ELEMENT_RESET)
10357         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10358                            element);
10359
10360       if (ei->gfx_element != artwork_element)
10361         reset_frame = TRUE;
10362
10363       ei->gfx_element = artwork_element;
10364
10365       SCAN_PLAYFIELD(xx, yy)
10366       {
10367         if (Tile[xx][yy] == element)
10368         {
10369           if (reset_frame)
10370           {
10371             ResetGfxAnimation(xx, yy);
10372             ResetRandomAnimationValue(xx, yy);
10373           }
10374
10375           TEST_DrawLevelField(xx, yy);
10376         }
10377       }
10378
10379       break;
10380     }
10381
10382     // ---------- engine actions  ---------------------------------------------
10383
10384     case CA_SET_ENGINE_SCAN_MODE:
10385     {
10386       InitPlayfieldScanMode(action_arg);
10387
10388       break;
10389     }
10390
10391     default:
10392       break;
10393   }
10394 }
10395
10396 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10397 {
10398   int old_element = Tile[x][y];
10399   int new_element = GetElementFromGroupElement(element);
10400   int previous_move_direction = MovDir[x][y];
10401   int last_ce_value = CustomValue[x][y];
10402   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10403   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10404   boolean add_player_onto_element = (new_element_is_player &&
10405                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10406                                      IS_WALKABLE(old_element));
10407
10408   if (!add_player_onto_element)
10409   {
10410     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10411       RemoveMovingField(x, y);
10412     else
10413       RemoveField(x, y);
10414
10415     Tile[x][y] = new_element;
10416
10417     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10418       MovDir[x][y] = previous_move_direction;
10419
10420     if (element_info[new_element].use_last_ce_value)
10421       CustomValue[x][y] = last_ce_value;
10422
10423     InitField_WithBug1(x, y, FALSE);
10424
10425     new_element = Tile[x][y];   // element may have changed
10426
10427     ResetGfxAnimation(x, y);
10428     ResetRandomAnimationValue(x, y);
10429
10430     TEST_DrawLevelField(x, y);
10431
10432     if (GFX_CRUMBLED(new_element))
10433       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10434   }
10435
10436   // check if element under the player changes from accessible to unaccessible
10437   // (needed for special case of dropping element which then changes)
10438   // (must be checked after creating new element for walkable group elements)
10439   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10440       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10441   {
10442     Bang(x, y);
10443
10444     return;
10445   }
10446
10447   // "ChangeCount" not set yet to allow "entered by player" change one time
10448   if (new_element_is_player)
10449     RelocatePlayer(x, y, new_element);
10450
10451   if (is_change)
10452     ChangeCount[x][y]++;        // count number of changes in the same frame
10453
10454   TestIfBadThingTouchesPlayer(x, y);
10455   TestIfPlayerTouchesCustomElement(x, y);
10456   TestIfElementTouchesCustomElement(x, y);
10457 }
10458
10459 static void CreateField(int x, int y, int element)
10460 {
10461   CreateFieldExt(x, y, element, FALSE);
10462 }
10463
10464 static void CreateElementFromChange(int x, int y, int element)
10465 {
10466   element = GET_VALID_RUNTIME_ELEMENT(element);
10467
10468   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10469   {
10470     int old_element = Tile[x][y];
10471
10472     // prevent changed element from moving in same engine frame
10473     // unless both old and new element can either fall or move
10474     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10475         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10476       Stop[x][y] = TRUE;
10477   }
10478
10479   CreateFieldExt(x, y, element, TRUE);
10480 }
10481
10482 static boolean ChangeElement(int x, int y, int element, int page)
10483 {
10484   struct ElementInfo *ei = &element_info[element];
10485   struct ElementChangeInfo *change = &ei->change_page[page];
10486   int ce_value = CustomValue[x][y];
10487   int ce_score = ei->collect_score;
10488   int target_element;
10489   int old_element = Tile[x][y];
10490
10491   // always use default change event to prevent running into a loop
10492   if (ChangeEvent[x][y] == -1)
10493     ChangeEvent[x][y] = CE_DELAY;
10494
10495   if (ChangeEvent[x][y] == CE_DELAY)
10496   {
10497     // reset actual trigger element, trigger player and action element
10498     change->actual_trigger_element = EL_EMPTY;
10499     change->actual_trigger_player = EL_EMPTY;
10500     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10501     change->actual_trigger_side = CH_SIDE_NONE;
10502     change->actual_trigger_ce_value = 0;
10503     change->actual_trigger_ce_score = 0;
10504   }
10505
10506   // do not change elements more than a specified maximum number of changes
10507   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10508     return FALSE;
10509
10510   ChangeCount[x][y]++;          // count number of changes in the same frame
10511
10512   if (change->explode)
10513   {
10514     Bang(x, y);
10515
10516     return TRUE;
10517   }
10518
10519   if (change->use_target_content)
10520   {
10521     boolean complete_replace = TRUE;
10522     boolean can_replace[3][3];
10523     int xx, yy;
10524
10525     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10526     {
10527       boolean is_empty;
10528       boolean is_walkable;
10529       boolean is_diggable;
10530       boolean is_collectible;
10531       boolean is_removable;
10532       boolean is_destructible;
10533       int ex = x + xx - 1;
10534       int ey = y + yy - 1;
10535       int content_element = change->target_content.e[xx][yy];
10536       int e;
10537
10538       can_replace[xx][yy] = TRUE;
10539
10540       if (ex == x && ey == y)   // do not check changing element itself
10541         continue;
10542
10543       if (content_element == EL_EMPTY_SPACE)
10544       {
10545         can_replace[xx][yy] = FALSE;    // do not replace border with space
10546
10547         continue;
10548       }
10549
10550       if (!IN_LEV_FIELD(ex, ey))
10551       {
10552         can_replace[xx][yy] = FALSE;
10553         complete_replace = FALSE;
10554
10555         continue;
10556       }
10557
10558       e = Tile[ex][ey];
10559
10560       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10561         e = MovingOrBlocked2Element(ex, ey);
10562
10563       is_empty = (IS_FREE(ex, ey) ||
10564                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10565
10566       is_walkable     = (is_empty || IS_WALKABLE(e));
10567       is_diggable     = (is_empty || IS_DIGGABLE(e));
10568       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10569       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10570       is_removable    = (is_diggable || is_collectible);
10571
10572       can_replace[xx][yy] =
10573         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10574           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10575           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10576           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10577           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10578           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10579          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10580
10581       if (!can_replace[xx][yy])
10582         complete_replace = FALSE;
10583     }
10584
10585     if (!change->only_if_complete || complete_replace)
10586     {
10587       boolean something_has_changed = FALSE;
10588
10589       if (change->only_if_complete && change->use_random_replace &&
10590           RND(100) < change->random_percentage)
10591         return FALSE;
10592
10593       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10594       {
10595         int ex = x + xx - 1;
10596         int ey = y + yy - 1;
10597         int content_element;
10598
10599         if (can_replace[xx][yy] && (!change->use_random_replace ||
10600                                     RND(100) < change->random_percentage))
10601         {
10602           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10603             RemoveMovingField(ex, ey);
10604
10605           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10606
10607           content_element = change->target_content.e[xx][yy];
10608           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10609                                               ce_value, ce_score);
10610
10611           CreateElementFromChange(ex, ey, target_element);
10612
10613           something_has_changed = TRUE;
10614
10615           // for symmetry reasons, freeze newly created border elements
10616           if (ex != x || ey != y)
10617             Stop[ex][ey] = TRUE;        // no more moving in this frame
10618         }
10619       }
10620
10621       if (something_has_changed)
10622       {
10623         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10624         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10625       }
10626     }
10627   }
10628   else
10629   {
10630     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10631                                         ce_value, ce_score);
10632
10633     if (element == EL_DIAGONAL_GROWING ||
10634         element == EL_DIAGONAL_SHRINKING)
10635     {
10636       target_element = Store[x][y];
10637
10638       Store[x][y] = EL_EMPTY;
10639     }
10640
10641     CreateElementFromChange(x, y, target_element);
10642
10643     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10644     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10645   }
10646
10647   // this uses direct change before indirect change
10648   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10649
10650   return TRUE;
10651 }
10652
10653 static void HandleElementChange(int x, int y, int page)
10654 {
10655   int element = MovingOrBlocked2Element(x, y);
10656   struct ElementInfo *ei = &element_info[element];
10657   struct ElementChangeInfo *change = &ei->change_page[page];
10658   boolean handle_action_before_change = FALSE;
10659
10660 #ifdef DEBUG
10661   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10662       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10663   {
10664     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10665           x, y, element, element_info[element].token_name);
10666     Debug("game:playing:HandleElementChange", "This should never happen!");
10667   }
10668 #endif
10669
10670   // this can happen with classic bombs on walkable, changing elements
10671   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10672   {
10673     return;
10674   }
10675
10676   if (ChangeDelay[x][y] == 0)           // initialize element change
10677   {
10678     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10679
10680     if (change->can_change)
10681     {
10682       // !!! not clear why graphic animation should be reset at all here !!!
10683       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10684       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10685
10686       /*
10687         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10688
10689         When using an animation frame delay of 1 (this only happens with
10690         "sp_zonk.moving.left/right" in the classic graphics), the default
10691         (non-moving) animation shows wrong animation frames (while the
10692         moving animation, like "sp_zonk.moving.left/right", is correct,
10693         so this graphical bug never shows up with the classic graphics).
10694         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10695         be drawn instead of the correct frames 0,1,2,3. This is caused by
10696         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10697         an element change: First when the change delay ("ChangeDelay[][]")
10698         counter has reached zero after decrementing, then a second time in
10699         the next frame (after "GfxFrame[][]" was already incremented) when
10700         "ChangeDelay[][]" is reset to the initial delay value again.
10701
10702         This causes frame 0 to be drawn twice, while the last frame won't
10703         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10704
10705         As some animations may already be cleverly designed around this bug
10706         (at least the "Snake Bite" snake tail animation does this), it cannot
10707         simply be fixed here without breaking such existing animations.
10708         Unfortunately, it cannot easily be detected if a graphics set was
10709         designed "before" or "after" the bug was fixed. As a workaround,
10710         a new graphics set option "game.graphics_engine_version" was added
10711         to be able to specify the game's major release version for which the
10712         graphics set was designed, which can then be used to decide if the
10713         bugfix should be used (version 4 and above) or not (version 3 or
10714         below, or if no version was specified at all, as with old sets).
10715
10716         (The wrong/fixed animation frames can be tested with the test level set
10717         "test_gfxframe" and level "000", which contains a specially prepared
10718         custom element at level position (x/y) == (11/9) which uses the zonk
10719         animation mentioned above. Using "game.graphics_engine_version: 4"
10720         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10721         This can also be seen from the debug output for this test element.)
10722       */
10723
10724       // when a custom element is about to change (for example by change delay),
10725       // do not reset graphic animation when the custom element is moving
10726       if (game.graphics_engine_version < 4 &&
10727           !IS_MOVING(x, y))
10728       {
10729         ResetGfxAnimation(x, y);
10730         ResetRandomAnimationValue(x, y);
10731       }
10732
10733       if (change->pre_change_function)
10734         change->pre_change_function(x, y);
10735     }
10736   }
10737
10738   ChangeDelay[x][y]--;
10739
10740   if (ChangeDelay[x][y] != 0)           // continue element change
10741   {
10742     if (change->can_change)
10743     {
10744       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10745
10746       if (IS_ANIMATED(graphic))
10747         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10748
10749       if (change->change_function)
10750         change->change_function(x, y);
10751     }
10752   }
10753   else                                  // finish element change
10754   {
10755     if (ChangePage[x][y] != -1)         // remember page from delayed change
10756     {
10757       page = ChangePage[x][y];
10758       ChangePage[x][y] = -1;
10759
10760       change = &ei->change_page[page];
10761     }
10762
10763     if (IS_MOVING(x, y))                // never change a running system ;-)
10764     {
10765       ChangeDelay[x][y] = 1;            // try change after next move step
10766       ChangePage[x][y] = page;          // remember page to use for change
10767
10768       return;
10769     }
10770
10771     // special case: set new level random seed before changing element
10772     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10773       handle_action_before_change = TRUE;
10774
10775     if (change->has_action && handle_action_before_change)
10776       ExecuteCustomElementAction(x, y, element, page);
10777
10778     if (change->can_change)
10779     {
10780       if (ChangeElement(x, y, element, page))
10781       {
10782         if (change->post_change_function)
10783           change->post_change_function(x, y);
10784       }
10785     }
10786
10787     if (change->has_action && !handle_action_before_change)
10788       ExecuteCustomElementAction(x, y, element, page);
10789   }
10790 }
10791
10792 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10793                                               int trigger_element,
10794                                               int trigger_event,
10795                                               int trigger_player,
10796                                               int trigger_side,
10797                                               int trigger_page)
10798 {
10799   boolean change_done_any = FALSE;
10800   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10801   int i;
10802
10803   if (!(trigger_events[trigger_element][trigger_event]))
10804     return FALSE;
10805
10806   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10807
10808   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10809   {
10810     int element = EL_CUSTOM_START + i;
10811     boolean change_done = FALSE;
10812     int p;
10813
10814     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10815         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10816       continue;
10817
10818     for (p = 0; p < element_info[element].num_change_pages; p++)
10819     {
10820       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10821
10822       if (change->can_change_or_has_action &&
10823           change->has_event[trigger_event] &&
10824           change->trigger_side & trigger_side &&
10825           change->trigger_player & trigger_player &&
10826           change->trigger_page & trigger_page_bits &&
10827           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10828       {
10829         change->actual_trigger_element = trigger_element;
10830         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10831         change->actual_trigger_player_bits = trigger_player;
10832         change->actual_trigger_side = trigger_side;
10833         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10834         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10835
10836         if ((change->can_change && !change_done) || change->has_action)
10837         {
10838           int x, y;
10839
10840           SCAN_PLAYFIELD(x, y)
10841           {
10842             if (Tile[x][y] == element)
10843             {
10844               if (change->can_change && !change_done)
10845               {
10846                 // if element already changed in this frame, not only prevent
10847                 // another element change (checked in ChangeElement()), but
10848                 // also prevent additional element actions for this element
10849
10850                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10851                     !level.use_action_after_change_bug)
10852                   continue;
10853
10854                 ChangeDelay[x][y] = 1;
10855                 ChangeEvent[x][y] = trigger_event;
10856
10857                 HandleElementChange(x, y, p);
10858               }
10859               else if (change->has_action)
10860               {
10861                 // if element already changed in this frame, not only prevent
10862                 // another element change (checked in ChangeElement()), but
10863                 // also prevent additional element actions for this element
10864
10865                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10866                     !level.use_action_after_change_bug)
10867                   continue;
10868
10869                 ExecuteCustomElementAction(x, y, element, p);
10870                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10871               }
10872             }
10873           }
10874
10875           if (change->can_change)
10876           {
10877             change_done = TRUE;
10878             change_done_any = TRUE;
10879           }
10880         }
10881       }
10882     }
10883   }
10884
10885   RECURSION_LOOP_DETECTION_END();
10886
10887   return change_done_any;
10888 }
10889
10890 static boolean CheckElementChangeExt(int x, int y,
10891                                      int element,
10892                                      int trigger_element,
10893                                      int trigger_event,
10894                                      int trigger_player,
10895                                      int trigger_side)
10896 {
10897   boolean change_done = FALSE;
10898   int p;
10899
10900   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10901       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10902     return FALSE;
10903
10904   if (Tile[x][y] == EL_BLOCKED)
10905   {
10906     Blocked2Moving(x, y, &x, &y);
10907     element = Tile[x][y];
10908   }
10909
10910   // check if element has already changed or is about to change after moving
10911   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10912        Tile[x][y] != element) ||
10913
10914       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10915        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10916         ChangePage[x][y] != -1)))
10917     return FALSE;
10918
10919   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10920
10921   for (p = 0; p < element_info[element].num_change_pages; p++)
10922   {
10923     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10924
10925     /* check trigger element for all events where the element that is checked
10926        for changing interacts with a directly adjacent element -- this is
10927        different to element changes that affect other elements to change on the
10928        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10929     boolean check_trigger_element =
10930       (trigger_event == CE_TOUCHING_X ||
10931        trigger_event == CE_HITTING_X ||
10932        trigger_event == CE_HIT_BY_X ||
10933        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10934
10935     if (change->can_change_or_has_action &&
10936         change->has_event[trigger_event] &&
10937         change->trigger_side & trigger_side &&
10938         change->trigger_player & trigger_player &&
10939         (!check_trigger_element ||
10940          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10941     {
10942       change->actual_trigger_element = trigger_element;
10943       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10944       change->actual_trigger_player_bits = trigger_player;
10945       change->actual_trigger_side = trigger_side;
10946       change->actual_trigger_ce_value = CustomValue[x][y];
10947       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10948
10949       // special case: trigger element not at (x,y) position for some events
10950       if (check_trigger_element)
10951       {
10952         static struct
10953         {
10954           int dx, dy;
10955         } move_xy[] =
10956           {
10957             {  0,  0 },
10958             { -1,  0 },
10959             { +1,  0 },
10960             {  0,  0 },
10961             {  0, -1 },
10962             {  0,  0 }, { 0, 0 }, { 0, 0 },
10963             {  0, +1 }
10964           };
10965
10966         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10967         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10968
10969         change->actual_trigger_ce_value = CustomValue[xx][yy];
10970         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10971       }
10972
10973       if (change->can_change && !change_done)
10974       {
10975         ChangeDelay[x][y] = 1;
10976         ChangeEvent[x][y] = trigger_event;
10977
10978         HandleElementChange(x, y, p);
10979
10980         change_done = TRUE;
10981       }
10982       else if (change->has_action)
10983       {
10984         ExecuteCustomElementAction(x, y, element, p);
10985         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10986       }
10987     }
10988   }
10989
10990   RECURSION_LOOP_DETECTION_END();
10991
10992   return change_done;
10993 }
10994
10995 static void PlayPlayerSound(struct PlayerInfo *player)
10996 {
10997   int jx = player->jx, jy = player->jy;
10998   int sound_element = player->artwork_element;
10999   int last_action = player->last_action_waiting;
11000   int action = player->action_waiting;
11001
11002   if (player->is_waiting)
11003   {
11004     if (action != last_action)
11005       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11006     else
11007       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11008   }
11009   else
11010   {
11011     if (action != last_action)
11012       StopSound(element_info[sound_element].sound[last_action]);
11013
11014     if (last_action == ACTION_SLEEPING)
11015       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11016   }
11017 }
11018
11019 static void PlayAllPlayersSound(void)
11020 {
11021   int i;
11022
11023   for (i = 0; i < MAX_PLAYERS; i++)
11024     if (stored_player[i].active)
11025       PlayPlayerSound(&stored_player[i]);
11026 }
11027
11028 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11029 {
11030   boolean last_waiting = player->is_waiting;
11031   int move_dir = player->MovDir;
11032
11033   player->dir_waiting = move_dir;
11034   player->last_action_waiting = player->action_waiting;
11035
11036   if (is_waiting)
11037   {
11038     if (!last_waiting)          // not waiting -> waiting
11039     {
11040       player->is_waiting = TRUE;
11041
11042       player->frame_counter_bored =
11043         FrameCounter +
11044         game.player_boring_delay_fixed +
11045         GetSimpleRandom(game.player_boring_delay_random);
11046       player->frame_counter_sleeping =
11047         FrameCounter +
11048         game.player_sleeping_delay_fixed +
11049         GetSimpleRandom(game.player_sleeping_delay_random);
11050
11051       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11052     }
11053
11054     if (game.player_sleeping_delay_fixed +
11055         game.player_sleeping_delay_random > 0 &&
11056         player->anim_delay_counter == 0 &&
11057         player->post_delay_counter == 0 &&
11058         FrameCounter >= player->frame_counter_sleeping)
11059       player->is_sleeping = TRUE;
11060     else if (game.player_boring_delay_fixed +
11061              game.player_boring_delay_random > 0 &&
11062              FrameCounter >= player->frame_counter_bored)
11063       player->is_bored = TRUE;
11064
11065     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11066                               player->is_bored ? ACTION_BORING :
11067                               ACTION_WAITING);
11068
11069     if (player->is_sleeping && player->use_murphy)
11070     {
11071       // special case for sleeping Murphy when leaning against non-free tile
11072
11073       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11074           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11075            !IS_MOVING(player->jx - 1, player->jy)))
11076         move_dir = MV_LEFT;
11077       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11078                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11079                 !IS_MOVING(player->jx + 1, player->jy)))
11080         move_dir = MV_RIGHT;
11081       else
11082         player->is_sleeping = FALSE;
11083
11084       player->dir_waiting = move_dir;
11085     }
11086
11087     if (player->is_sleeping)
11088     {
11089       if (player->num_special_action_sleeping > 0)
11090       {
11091         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11092         {
11093           int last_special_action = player->special_action_sleeping;
11094           int num_special_action = player->num_special_action_sleeping;
11095           int special_action =
11096             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11097              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11098              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11099              last_special_action + 1 : ACTION_SLEEPING);
11100           int special_graphic =
11101             el_act_dir2img(player->artwork_element, special_action, move_dir);
11102
11103           player->anim_delay_counter =
11104             graphic_info[special_graphic].anim_delay_fixed +
11105             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11106           player->post_delay_counter =
11107             graphic_info[special_graphic].post_delay_fixed +
11108             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11109
11110           player->special_action_sleeping = special_action;
11111         }
11112
11113         if (player->anim_delay_counter > 0)
11114         {
11115           player->action_waiting = player->special_action_sleeping;
11116           player->anim_delay_counter--;
11117         }
11118         else if (player->post_delay_counter > 0)
11119         {
11120           player->post_delay_counter--;
11121         }
11122       }
11123     }
11124     else if (player->is_bored)
11125     {
11126       if (player->num_special_action_bored > 0)
11127       {
11128         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11129         {
11130           int special_action =
11131             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11132           int special_graphic =
11133             el_act_dir2img(player->artwork_element, special_action, move_dir);
11134
11135           player->anim_delay_counter =
11136             graphic_info[special_graphic].anim_delay_fixed +
11137             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11138           player->post_delay_counter =
11139             graphic_info[special_graphic].post_delay_fixed +
11140             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11141
11142           player->special_action_bored = special_action;
11143         }
11144
11145         if (player->anim_delay_counter > 0)
11146         {
11147           player->action_waiting = player->special_action_bored;
11148           player->anim_delay_counter--;
11149         }
11150         else if (player->post_delay_counter > 0)
11151         {
11152           player->post_delay_counter--;
11153         }
11154       }
11155     }
11156   }
11157   else if (last_waiting)        // waiting -> not waiting
11158   {
11159     player->is_waiting = FALSE;
11160     player->is_bored = FALSE;
11161     player->is_sleeping = FALSE;
11162
11163     player->frame_counter_bored = -1;
11164     player->frame_counter_sleeping = -1;
11165
11166     player->anim_delay_counter = 0;
11167     player->post_delay_counter = 0;
11168
11169     player->dir_waiting = player->MovDir;
11170     player->action_waiting = ACTION_DEFAULT;
11171
11172     player->special_action_bored = ACTION_DEFAULT;
11173     player->special_action_sleeping = ACTION_DEFAULT;
11174   }
11175 }
11176
11177 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11178 {
11179   if ((!player->is_moving  && player->was_moving) ||
11180       (player->MovPos == 0 && player->was_moving) ||
11181       (player->is_snapping && !player->was_snapping) ||
11182       (player->is_dropping && !player->was_dropping))
11183   {
11184     if (!CheckSaveEngineSnapshotToList())
11185       return;
11186
11187     player->was_moving = FALSE;
11188     player->was_snapping = TRUE;
11189     player->was_dropping = TRUE;
11190   }
11191   else
11192   {
11193     if (player->is_moving)
11194       player->was_moving = TRUE;
11195
11196     if (!player->is_snapping)
11197       player->was_snapping = FALSE;
11198
11199     if (!player->is_dropping)
11200       player->was_dropping = FALSE;
11201   }
11202 }
11203
11204 static void CheckSingleStepMode(struct PlayerInfo *player)
11205 {
11206   if (tape.single_step && tape.recording && !tape.pausing)
11207   {
11208     /* as it is called "single step mode", just return to pause mode when the
11209        player stopped moving after one tile (or never starts moving at all) */
11210     if (!player->is_moving &&
11211         !player->is_pushing &&
11212         !player->is_dropping_pressed)
11213       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11214   }
11215
11216   CheckSaveEngineSnapshot(player);
11217 }
11218
11219 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11220 {
11221   int left      = player_action & JOY_LEFT;
11222   int right     = player_action & JOY_RIGHT;
11223   int up        = player_action & JOY_UP;
11224   int down      = player_action & JOY_DOWN;
11225   int button1   = player_action & JOY_BUTTON_1;
11226   int button2   = player_action & JOY_BUTTON_2;
11227   int dx        = (left ? -1 : right ? 1 : 0);
11228   int dy        = (up   ? -1 : down  ? 1 : 0);
11229
11230   if (!player->active || tape.pausing)
11231     return 0;
11232
11233   if (player_action)
11234   {
11235     if (button1)
11236       SnapField(player, dx, dy);
11237     else
11238     {
11239       if (button2)
11240         DropElement(player);
11241
11242       MovePlayer(player, dx, dy);
11243     }
11244
11245     CheckSingleStepMode(player);
11246
11247     SetPlayerWaiting(player, FALSE);
11248
11249     return player_action;
11250   }
11251   else
11252   {
11253     // no actions for this player (no input at player's configured device)
11254
11255     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11256     SnapField(player, 0, 0);
11257     CheckGravityMovementWhenNotMoving(player);
11258
11259     if (player->MovPos == 0)
11260       SetPlayerWaiting(player, TRUE);
11261
11262     if (player->MovPos == 0)    // needed for tape.playing
11263       player->is_moving = FALSE;
11264
11265     player->is_dropping = FALSE;
11266     player->is_dropping_pressed = FALSE;
11267     player->drop_pressed_delay = 0;
11268
11269     CheckSingleStepMode(player);
11270
11271     return 0;
11272   }
11273 }
11274
11275 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11276                                          byte *tape_action)
11277 {
11278   if (!tape.use_mouse_actions)
11279     return;
11280
11281   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11282   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11283   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11284 }
11285
11286 static void SetTapeActionFromMouseAction(byte *tape_action,
11287                                          struct MouseActionInfo *mouse_action)
11288 {
11289   if (!tape.use_mouse_actions)
11290     return;
11291
11292   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11293   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11294   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11295 }
11296
11297 static void CheckLevelSolved(void)
11298 {
11299   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11300   {
11301     if (game_em.level_solved &&
11302         !game_em.game_over)                             // game won
11303     {
11304       LevelSolved();
11305
11306       game_em.game_over = TRUE;
11307
11308       game.all_players_gone = TRUE;
11309     }
11310
11311     if (game_em.game_over)                              // game lost
11312       game.all_players_gone = TRUE;
11313   }
11314   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11315   {
11316     if (game_sp.level_solved &&
11317         !game_sp.game_over)                             // game won
11318     {
11319       LevelSolved();
11320
11321       game_sp.game_over = TRUE;
11322
11323       game.all_players_gone = TRUE;
11324     }
11325
11326     if (game_sp.game_over)                              // game lost
11327       game.all_players_gone = TRUE;
11328   }
11329   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11330   {
11331     if (game_mm.level_solved &&
11332         !game_mm.game_over)                             // game won
11333     {
11334       LevelSolved();
11335
11336       game_mm.game_over = TRUE;
11337
11338       game.all_players_gone = TRUE;
11339     }
11340
11341     if (game_mm.game_over)                              // game lost
11342       game.all_players_gone = TRUE;
11343   }
11344 }
11345
11346 static void CheckLevelTime(void)
11347 {
11348   int i;
11349
11350   if (TimeFrames >= FRAMES_PER_SECOND)
11351   {
11352     TimeFrames = 0;
11353     TapeTime++;
11354
11355     for (i = 0; i < MAX_PLAYERS; i++)
11356     {
11357       struct PlayerInfo *player = &stored_player[i];
11358
11359       if (SHIELD_ON(player))
11360       {
11361         player->shield_normal_time_left--;
11362
11363         if (player->shield_deadly_time_left > 0)
11364           player->shield_deadly_time_left--;
11365       }
11366     }
11367
11368     if (!game.LevelSolved && !level.use_step_counter)
11369     {
11370       TimePlayed++;
11371
11372       if (TimeLeft > 0)
11373       {
11374         TimeLeft--;
11375
11376         if (TimeLeft <= 10 && setup.time_limit)
11377           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11378
11379         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11380            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11381
11382         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11383
11384         if (!TimeLeft && setup.time_limit)
11385         {
11386           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11387             game_em.lev->killed_out_of_time = TRUE;
11388           else
11389             for (i = 0; i < MAX_PLAYERS; i++)
11390               KillPlayer(&stored_player[i]);
11391         }
11392       }
11393       else if (game.no_time_limit && !game.all_players_gone)
11394       {
11395         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11396       }
11397
11398       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11399     }
11400
11401     if (tape.recording || tape.playing)
11402       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11403   }
11404
11405   if (tape.recording || tape.playing)
11406     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11407
11408   UpdateAndDisplayGameControlValues();
11409 }
11410
11411 void AdvanceFrameAndPlayerCounters(int player_nr)
11412 {
11413   int i;
11414
11415   // advance frame counters (global frame counter and time frame counter)
11416   FrameCounter++;
11417   TimeFrames++;
11418
11419   // advance player counters (counters for move delay, move animation etc.)
11420   for (i = 0; i < MAX_PLAYERS; i++)
11421   {
11422     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11423     int move_delay_value = stored_player[i].move_delay_value;
11424     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11425
11426     if (!advance_player_counters)       // not all players may be affected
11427       continue;
11428
11429     if (move_frames == 0)       // less than one move per game frame
11430     {
11431       int stepsize = TILEX / move_delay_value;
11432       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11433       int count = (stored_player[i].is_moving ?
11434                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11435
11436       if (count % delay == 0)
11437         move_frames = 1;
11438     }
11439
11440     stored_player[i].Frame += move_frames;
11441
11442     if (stored_player[i].MovPos != 0)
11443       stored_player[i].StepFrame += move_frames;
11444
11445     if (stored_player[i].move_delay > 0)
11446       stored_player[i].move_delay--;
11447
11448     // due to bugs in previous versions, counter must count up, not down
11449     if (stored_player[i].push_delay != -1)
11450       stored_player[i].push_delay++;
11451
11452     if (stored_player[i].drop_delay > 0)
11453       stored_player[i].drop_delay--;
11454
11455     if (stored_player[i].is_dropping_pressed)
11456       stored_player[i].drop_pressed_delay++;
11457   }
11458 }
11459
11460 void StartGameActions(boolean init_network_game, boolean record_tape,
11461                       int random_seed)
11462 {
11463   unsigned int new_random_seed = InitRND(random_seed);
11464
11465   if (record_tape)
11466     TapeStartRecording(new_random_seed);
11467
11468   if (init_network_game)
11469   {
11470     SendToServer_LevelFile();
11471     SendToServer_StartPlaying();
11472
11473     return;
11474   }
11475
11476   InitGame();
11477 }
11478
11479 static void GameActionsExt(void)
11480 {
11481 #if 0
11482   static unsigned int game_frame_delay = 0;
11483 #endif
11484   unsigned int game_frame_delay_value;
11485   byte *recorded_player_action;
11486   byte summarized_player_action = 0;
11487   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11488   int i;
11489
11490   // detect endless loops, caused by custom element programming
11491   if (recursion_loop_detected && recursion_loop_depth == 0)
11492   {
11493     char *message = getStringCat3("Internal Error! Element ",
11494                                   EL_NAME(recursion_loop_element),
11495                                   " caused endless loop! Quit the game?");
11496
11497     Warn("element '%s' caused endless loop in game engine",
11498          EL_NAME(recursion_loop_element));
11499
11500     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11501
11502     recursion_loop_detected = FALSE;    // if game should be continued
11503
11504     free(message);
11505
11506     return;
11507   }
11508
11509   if (game.restart_level)
11510     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11511
11512   CheckLevelSolved();
11513
11514   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11515     GameWon();
11516
11517   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11518     TapeStop();
11519
11520   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11521     return;
11522
11523   game_frame_delay_value =
11524     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11525
11526   if (tape.playing && tape.warp_forward && !tape.pausing)
11527     game_frame_delay_value = 0;
11528
11529   SetVideoFrameDelay(game_frame_delay_value);
11530
11531   // (de)activate virtual buttons depending on current game status
11532   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11533   {
11534     if (game.all_players_gone)  // if no players there to be controlled anymore
11535       SetOverlayActive(FALSE);
11536     else if (!tape.playing)     // if game continues after tape stopped playing
11537       SetOverlayActive(TRUE);
11538   }
11539
11540 #if 0
11541 #if 0
11542   // ---------- main game synchronization point ----------
11543
11544   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11545
11546   Debug("game:playing:skip", "skip == %d", skip);
11547
11548 #else
11549   // ---------- main game synchronization point ----------
11550
11551   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11552 #endif
11553 #endif
11554
11555   if (network_playing && !network_player_action_received)
11556   {
11557     // try to get network player actions in time
11558
11559     // last chance to get network player actions without main loop delay
11560     HandleNetworking();
11561
11562     // game was quit by network peer
11563     if (game_status != GAME_MODE_PLAYING)
11564       return;
11565
11566     // check if network player actions still missing and game still running
11567     if (!network_player_action_received && !checkGameEnded())
11568       return;           // failed to get network player actions in time
11569
11570     // do not yet reset "network_player_action_received" (for tape.pausing)
11571   }
11572
11573   if (tape.pausing)
11574     return;
11575
11576   // at this point we know that we really continue executing the game
11577
11578   network_player_action_received = FALSE;
11579
11580   // when playing tape, read previously recorded player input from tape data
11581   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11582
11583   local_player->effective_mouse_action = local_player->mouse_action;
11584
11585   if (recorded_player_action != NULL)
11586     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11587                                  recorded_player_action);
11588
11589   // TapePlayAction() may return NULL when toggling to "pause before death"
11590   if (tape.pausing)
11591     return;
11592
11593   if (tape.set_centered_player)
11594   {
11595     game.centered_player_nr_next = tape.centered_player_nr_next;
11596     game.set_centered_player = TRUE;
11597   }
11598
11599   for (i = 0; i < MAX_PLAYERS; i++)
11600   {
11601     summarized_player_action |= stored_player[i].action;
11602
11603     if (!network_playing && (game.team_mode || tape.playing))
11604       stored_player[i].effective_action = stored_player[i].action;
11605   }
11606
11607   if (network_playing && !checkGameEnded())
11608     SendToServer_MovePlayer(summarized_player_action);
11609
11610   // summarize all actions at local players mapped input device position
11611   // (this allows using different input devices in single player mode)
11612   if (!network.enabled && !game.team_mode)
11613     stored_player[map_player_action[local_player->index_nr]].effective_action =
11614       summarized_player_action;
11615
11616   // summarize all actions at centered player in local team mode
11617   if (tape.recording &&
11618       setup.team_mode && !network.enabled &&
11619       setup.input_on_focus &&
11620       game.centered_player_nr != -1)
11621   {
11622     for (i = 0; i < MAX_PLAYERS; i++)
11623       stored_player[map_player_action[i]].effective_action =
11624         (i == game.centered_player_nr ? summarized_player_action : 0);
11625   }
11626
11627   if (recorded_player_action != NULL)
11628     for (i = 0; i < MAX_PLAYERS; i++)
11629       stored_player[i].effective_action = recorded_player_action[i];
11630
11631   for (i = 0; i < MAX_PLAYERS; i++)
11632   {
11633     tape_action[i] = stored_player[i].effective_action;
11634
11635     /* (this may happen in the RND game engine if a player was not present on
11636        the playfield on level start, but appeared later from a custom element */
11637     if (setup.team_mode &&
11638         tape.recording &&
11639         tape_action[i] &&
11640         !tape.player_participates[i])
11641       tape.player_participates[i] = TRUE;
11642   }
11643
11644   SetTapeActionFromMouseAction(tape_action,
11645                                &local_player->effective_mouse_action);
11646
11647   // only record actions from input devices, but not programmed actions
11648   if (tape.recording)
11649     TapeRecordAction(tape_action);
11650
11651   // remember if game was played (especially after tape stopped playing)
11652   if (!tape.playing && summarized_player_action)
11653     game.GamePlayed = TRUE;
11654
11655 #if USE_NEW_PLAYER_ASSIGNMENTS
11656   // !!! also map player actions in single player mode !!!
11657   // if (game.team_mode)
11658   if (1)
11659   {
11660     byte mapped_action[MAX_PLAYERS];
11661
11662 #if DEBUG_PLAYER_ACTIONS
11663     Print(":::");
11664     for (i = 0; i < MAX_PLAYERS; i++)
11665       Print(" %d, ", stored_player[i].effective_action);
11666 #endif
11667
11668     for (i = 0; i < MAX_PLAYERS; i++)
11669       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11670
11671     for (i = 0; i < MAX_PLAYERS; i++)
11672       stored_player[i].effective_action = mapped_action[i];
11673
11674 #if DEBUG_PLAYER_ACTIONS
11675     Print(" =>");
11676     for (i = 0; i < MAX_PLAYERS; i++)
11677       Print(" %d, ", stored_player[i].effective_action);
11678     Print("\n");
11679 #endif
11680   }
11681 #if DEBUG_PLAYER_ACTIONS
11682   else
11683   {
11684     Print(":::");
11685     for (i = 0; i < MAX_PLAYERS; i++)
11686       Print(" %d, ", stored_player[i].effective_action);
11687     Print("\n");
11688   }
11689 #endif
11690 #endif
11691
11692   for (i = 0; i < MAX_PLAYERS; i++)
11693   {
11694     // allow engine snapshot in case of changed movement attempt
11695     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11696         (stored_player[i].effective_action & KEY_MOTION))
11697       game.snapshot.changed_action = TRUE;
11698
11699     // allow engine snapshot in case of snapping/dropping attempt
11700     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11701         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11702       game.snapshot.changed_action = TRUE;
11703
11704     game.snapshot.last_action[i] = stored_player[i].effective_action;
11705   }
11706
11707   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11708   {
11709     GameActions_EM_Main();
11710   }
11711   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11712   {
11713     GameActions_SP_Main();
11714   }
11715   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11716   {
11717     GameActions_MM_Main();
11718   }
11719   else
11720   {
11721     GameActions_RND_Main();
11722   }
11723
11724   BlitScreenToBitmap(backbuffer);
11725
11726   CheckLevelSolved();
11727   CheckLevelTime();
11728
11729   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11730
11731   if (global.show_frames_per_second)
11732   {
11733     static unsigned int fps_counter = 0;
11734     static int fps_frames = 0;
11735     unsigned int fps_delay_ms = Counter() - fps_counter;
11736
11737     fps_frames++;
11738
11739     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11740     {
11741       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11742
11743       fps_frames = 0;
11744       fps_counter = Counter();
11745
11746       // always draw FPS to screen after FPS value was updated
11747       redraw_mask |= REDRAW_FPS;
11748     }
11749
11750     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11751     if (GetDrawDeactivationMask() == REDRAW_NONE)
11752       redraw_mask |= REDRAW_FPS;
11753   }
11754 }
11755
11756 static void GameActions_CheckSaveEngineSnapshot(void)
11757 {
11758   if (!game.snapshot.save_snapshot)
11759     return;
11760
11761   // clear flag for saving snapshot _before_ saving snapshot
11762   game.snapshot.save_snapshot = FALSE;
11763
11764   SaveEngineSnapshotToList();
11765 }
11766
11767 void GameActions(void)
11768 {
11769   GameActionsExt();
11770
11771   GameActions_CheckSaveEngineSnapshot();
11772 }
11773
11774 void GameActions_EM_Main(void)
11775 {
11776   byte effective_action[MAX_PLAYERS];
11777   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11778   int i;
11779
11780   for (i = 0; i < MAX_PLAYERS; i++)
11781     effective_action[i] = stored_player[i].effective_action;
11782
11783   GameActions_EM(effective_action, warp_mode);
11784 }
11785
11786 void GameActions_SP_Main(void)
11787 {
11788   byte effective_action[MAX_PLAYERS];
11789   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11790   int i;
11791
11792   for (i = 0; i < MAX_PLAYERS; i++)
11793     effective_action[i] = stored_player[i].effective_action;
11794
11795   GameActions_SP(effective_action, warp_mode);
11796
11797   for (i = 0; i < MAX_PLAYERS; i++)
11798   {
11799     if (stored_player[i].force_dropping)
11800       stored_player[i].action |= KEY_BUTTON_DROP;
11801
11802     stored_player[i].force_dropping = FALSE;
11803   }
11804 }
11805
11806 void GameActions_MM_Main(void)
11807 {
11808   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11809
11810   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11811 }
11812
11813 void GameActions_RND_Main(void)
11814 {
11815   GameActions_RND();
11816 }
11817
11818 void GameActions_RND(void)
11819 {
11820   static struct MouseActionInfo mouse_action_last = { 0 };
11821   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11822   int magic_wall_x = 0, magic_wall_y = 0;
11823   int i, x, y, element, graphic, last_gfx_frame;
11824
11825   InitPlayfieldScanModeVars();
11826
11827   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11828   {
11829     SCAN_PLAYFIELD(x, y)
11830     {
11831       ChangeCount[x][y] = 0;
11832       ChangeEvent[x][y] = -1;
11833     }
11834   }
11835
11836   if (game.set_centered_player)
11837   {
11838     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11839
11840     // switching to "all players" only possible if all players fit to screen
11841     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11842     {
11843       game.centered_player_nr_next = game.centered_player_nr;
11844       game.set_centered_player = FALSE;
11845     }
11846
11847     // do not switch focus to non-existing (or non-active) player
11848     if (game.centered_player_nr_next >= 0 &&
11849         !stored_player[game.centered_player_nr_next].active)
11850     {
11851       game.centered_player_nr_next = game.centered_player_nr;
11852       game.set_centered_player = FALSE;
11853     }
11854   }
11855
11856   if (game.set_centered_player &&
11857       ScreenMovPos == 0)        // screen currently aligned at tile position
11858   {
11859     int sx, sy;
11860
11861     if (game.centered_player_nr_next == -1)
11862     {
11863       setScreenCenteredToAllPlayers(&sx, &sy);
11864     }
11865     else
11866     {
11867       sx = stored_player[game.centered_player_nr_next].jx;
11868       sy = stored_player[game.centered_player_nr_next].jy;
11869     }
11870
11871     game.centered_player_nr = game.centered_player_nr_next;
11872     game.set_centered_player = FALSE;
11873
11874     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11875     DrawGameDoorValues();
11876   }
11877
11878   for (i = 0; i < MAX_PLAYERS; i++)
11879   {
11880     int actual_player_action = stored_player[i].effective_action;
11881
11882 #if 1
11883     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11884        - rnd_equinox_tetrachloride 048
11885        - rnd_equinox_tetrachloride_ii 096
11886        - rnd_emanuel_schmieg 002
11887        - doctor_sloan_ww 001, 020
11888     */
11889     if (stored_player[i].MovPos == 0)
11890       CheckGravityMovement(&stored_player[i]);
11891 #endif
11892
11893     // overwrite programmed action with tape action
11894     if (stored_player[i].programmed_action)
11895       actual_player_action = stored_player[i].programmed_action;
11896
11897     PlayerActions(&stored_player[i], actual_player_action);
11898
11899     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11900   }
11901
11902   ScrollScreen(NULL, SCROLL_GO_ON);
11903
11904   /* for backwards compatibility, the following code emulates a fixed bug that
11905      occured when pushing elements (causing elements that just made their last
11906      pushing step to already (if possible) make their first falling step in the
11907      same game frame, which is bad); this code is also needed to use the famous
11908      "spring push bug" which is used in older levels and might be wanted to be
11909      used also in newer levels, but in this case the buggy pushing code is only
11910      affecting the "spring" element and no other elements */
11911
11912   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11913   {
11914     for (i = 0; i < MAX_PLAYERS; i++)
11915     {
11916       struct PlayerInfo *player = &stored_player[i];
11917       int x = player->jx;
11918       int y = player->jy;
11919
11920       if (player->active && player->is_pushing && player->is_moving &&
11921           IS_MOVING(x, y) &&
11922           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11923            Tile[x][y] == EL_SPRING))
11924       {
11925         ContinueMoving(x, y);
11926
11927         // continue moving after pushing (this is actually a bug)
11928         if (!IS_MOVING(x, y))
11929           Stop[x][y] = FALSE;
11930       }
11931     }
11932   }
11933
11934   SCAN_PLAYFIELD(x, y)
11935   {
11936     Last[x][y] = Tile[x][y];
11937
11938     ChangeCount[x][y] = 0;
11939     ChangeEvent[x][y] = -1;
11940
11941     // this must be handled before main playfield loop
11942     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
11943     {
11944       MovDelay[x][y]--;
11945       if (MovDelay[x][y] <= 0)
11946         RemoveField(x, y);
11947     }
11948
11949     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
11950     {
11951       MovDelay[x][y]--;
11952       if (MovDelay[x][y] <= 0)
11953       {
11954         RemoveField(x, y);
11955         TEST_DrawLevelField(x, y);
11956
11957         TestIfElementTouchesCustomElement(x, y);        // for empty space
11958       }
11959     }
11960
11961 #if DEBUG
11962     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11963     {
11964       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
11965             x, y);
11966       Debug("game:playing:GameActions_RND", "This should never happen!");
11967
11968       ChangePage[x][y] = -1;
11969     }
11970 #endif
11971
11972     Stop[x][y] = FALSE;
11973     if (WasJustMoving[x][y] > 0)
11974       WasJustMoving[x][y]--;
11975     if (WasJustFalling[x][y] > 0)
11976       WasJustFalling[x][y]--;
11977     if (CheckCollision[x][y] > 0)
11978       CheckCollision[x][y]--;
11979     if (CheckImpact[x][y] > 0)
11980       CheckImpact[x][y]--;
11981
11982     GfxFrame[x][y]++;
11983
11984     /* reset finished pushing action (not done in ContinueMoving() to allow
11985        continuous pushing animation for elements with zero push delay) */
11986     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11987     {
11988       ResetGfxAnimation(x, y);
11989       TEST_DrawLevelField(x, y);
11990     }
11991
11992 #if DEBUG
11993     if (IS_BLOCKED(x, y))
11994     {
11995       int oldx, oldy;
11996
11997       Blocked2Moving(x, y, &oldx, &oldy);
11998       if (!IS_MOVING(oldx, oldy))
11999       {
12000         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12001         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12002         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12003         Debug("game:playing:GameActions_RND", "This should never happen!");
12004       }
12005     }
12006 #endif
12007   }
12008
12009   if (mouse_action.button)
12010   {
12011     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12012
12013     x = mouse_action.lx;
12014     y = mouse_action.ly;
12015     element = Tile[x][y];
12016
12017     if (new_button)
12018     {
12019       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12020       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12021     }
12022
12023     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12024     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12025   }
12026
12027   SCAN_PLAYFIELD(x, y)
12028   {
12029     element = Tile[x][y];
12030     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12031     last_gfx_frame = GfxFrame[x][y];
12032
12033     ResetGfxFrame(x, y);
12034
12035     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12036       DrawLevelGraphicAnimation(x, y, graphic);
12037
12038     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12039         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12040       ResetRandomAnimationValue(x, y);
12041
12042     SetRandomAnimationValue(x, y);
12043
12044     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12045
12046     if (IS_INACTIVE(element))
12047     {
12048       if (IS_ANIMATED(graphic))
12049         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12050
12051       continue;
12052     }
12053
12054     // this may take place after moving, so 'element' may have changed
12055     if (IS_CHANGING(x, y) &&
12056         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12057     {
12058       int page = element_info[element].event_page_nr[CE_DELAY];
12059
12060       HandleElementChange(x, y, page);
12061
12062       element = Tile[x][y];
12063       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12064     }
12065
12066     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12067     {
12068       StartMoving(x, y);
12069
12070       element = Tile[x][y];
12071       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12072
12073       if (IS_ANIMATED(graphic) &&
12074           !IS_MOVING(x, y) &&
12075           !Stop[x][y])
12076         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12077
12078       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12079         TEST_DrawTwinkleOnField(x, y);
12080     }
12081     else if (element == EL_ACID)
12082     {
12083       if (!Stop[x][y])
12084         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12085     }
12086     else if ((element == EL_EXIT_OPEN ||
12087               element == EL_EM_EXIT_OPEN ||
12088               element == EL_SP_EXIT_OPEN ||
12089               element == EL_STEEL_EXIT_OPEN ||
12090               element == EL_EM_STEEL_EXIT_OPEN ||
12091               element == EL_SP_TERMINAL ||
12092               element == EL_SP_TERMINAL_ACTIVE ||
12093               element == EL_EXTRA_TIME ||
12094               element == EL_SHIELD_NORMAL ||
12095               element == EL_SHIELD_DEADLY) &&
12096              IS_ANIMATED(graphic))
12097       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12098     else if (IS_MOVING(x, y))
12099       ContinueMoving(x, y);
12100     else if (IS_ACTIVE_BOMB(element))
12101       CheckDynamite(x, y);
12102     else if (element == EL_AMOEBA_GROWING)
12103       AmoebaGrowing(x, y);
12104     else if (element == EL_AMOEBA_SHRINKING)
12105       AmoebaShrinking(x, y);
12106
12107 #if !USE_NEW_AMOEBA_CODE
12108     else if (IS_AMOEBALIVE(element))
12109       AmoebaReproduce(x, y);
12110 #endif
12111
12112     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12113       Life(x, y);
12114     else if (element == EL_EXIT_CLOSED)
12115       CheckExit(x, y);
12116     else if (element == EL_EM_EXIT_CLOSED)
12117       CheckExitEM(x, y);
12118     else if (element == EL_STEEL_EXIT_CLOSED)
12119       CheckExitSteel(x, y);
12120     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12121       CheckExitSteelEM(x, y);
12122     else if (element == EL_SP_EXIT_CLOSED)
12123       CheckExitSP(x, y);
12124     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12125              element == EL_EXPANDABLE_STEELWALL_GROWING)
12126       MauerWaechst(x, y);
12127     else if (element == EL_EXPANDABLE_WALL ||
12128              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12129              element == EL_EXPANDABLE_WALL_VERTICAL ||
12130              element == EL_EXPANDABLE_WALL_ANY ||
12131              element == EL_BD_EXPANDABLE_WALL)
12132       MauerAbleger(x, y);
12133     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12134              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12135              element == EL_EXPANDABLE_STEELWALL_ANY)
12136       MauerAblegerStahl(x, y);
12137     else if (element == EL_FLAMES)
12138       CheckForDragon(x, y);
12139     else if (element == EL_EXPLOSION)
12140       ; // drawing of correct explosion animation is handled separately
12141     else if (element == EL_ELEMENT_SNAPPING ||
12142              element == EL_DIAGONAL_SHRINKING ||
12143              element == EL_DIAGONAL_GROWING)
12144     {
12145       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12146
12147       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12148     }
12149     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12150       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12151
12152     if (IS_BELT_ACTIVE(element))
12153       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12154
12155     if (game.magic_wall_active)
12156     {
12157       int jx = local_player->jx, jy = local_player->jy;
12158
12159       // play the element sound at the position nearest to the player
12160       if ((element == EL_MAGIC_WALL_FULL ||
12161            element == EL_MAGIC_WALL_ACTIVE ||
12162            element == EL_MAGIC_WALL_EMPTYING ||
12163            element == EL_BD_MAGIC_WALL_FULL ||
12164            element == EL_BD_MAGIC_WALL_ACTIVE ||
12165            element == EL_BD_MAGIC_WALL_EMPTYING ||
12166            element == EL_DC_MAGIC_WALL_FULL ||
12167            element == EL_DC_MAGIC_WALL_ACTIVE ||
12168            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12169           ABS(x - jx) + ABS(y - jy) <
12170           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12171       {
12172         magic_wall_x = x;
12173         magic_wall_y = y;
12174       }
12175     }
12176   }
12177
12178 #if USE_NEW_AMOEBA_CODE
12179   // new experimental amoeba growth stuff
12180   if (!(FrameCounter % 8))
12181   {
12182     static unsigned int random = 1684108901;
12183
12184     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12185     {
12186       x = RND(lev_fieldx);
12187       y = RND(lev_fieldy);
12188       element = Tile[x][y];
12189
12190       if (!IS_PLAYER(x,y) &&
12191           (element == EL_EMPTY ||
12192            CAN_GROW_INTO(element) ||
12193            element == EL_QUICKSAND_EMPTY ||
12194            element == EL_QUICKSAND_FAST_EMPTY ||
12195            element == EL_ACID_SPLASH_LEFT ||
12196            element == EL_ACID_SPLASH_RIGHT))
12197       {
12198         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12199             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12200             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12201             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12202           Tile[x][y] = EL_AMOEBA_DROP;
12203       }
12204
12205       random = random * 129 + 1;
12206     }
12207   }
12208 #endif
12209
12210   game.explosions_delayed = FALSE;
12211
12212   SCAN_PLAYFIELD(x, y)
12213   {
12214     element = Tile[x][y];
12215
12216     if (ExplodeField[x][y])
12217       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12218     else if (element == EL_EXPLOSION)
12219       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12220
12221     ExplodeField[x][y] = EX_TYPE_NONE;
12222   }
12223
12224   game.explosions_delayed = TRUE;
12225
12226   if (game.magic_wall_active)
12227   {
12228     if (!(game.magic_wall_time_left % 4))
12229     {
12230       int element = Tile[magic_wall_x][magic_wall_y];
12231
12232       if (element == EL_BD_MAGIC_WALL_FULL ||
12233           element == EL_BD_MAGIC_WALL_ACTIVE ||
12234           element == EL_BD_MAGIC_WALL_EMPTYING)
12235         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12236       else if (element == EL_DC_MAGIC_WALL_FULL ||
12237                element == EL_DC_MAGIC_WALL_ACTIVE ||
12238                element == EL_DC_MAGIC_WALL_EMPTYING)
12239         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12240       else
12241         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12242     }
12243
12244     if (game.magic_wall_time_left > 0)
12245     {
12246       game.magic_wall_time_left--;
12247
12248       if (!game.magic_wall_time_left)
12249       {
12250         SCAN_PLAYFIELD(x, y)
12251         {
12252           element = Tile[x][y];
12253
12254           if (element == EL_MAGIC_WALL_ACTIVE ||
12255               element == EL_MAGIC_WALL_FULL)
12256           {
12257             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12258             TEST_DrawLevelField(x, y);
12259           }
12260           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12261                    element == EL_BD_MAGIC_WALL_FULL)
12262           {
12263             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12264             TEST_DrawLevelField(x, y);
12265           }
12266           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12267                    element == EL_DC_MAGIC_WALL_FULL)
12268           {
12269             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12270             TEST_DrawLevelField(x, y);
12271           }
12272         }
12273
12274         game.magic_wall_active = FALSE;
12275       }
12276     }
12277   }
12278
12279   if (game.light_time_left > 0)
12280   {
12281     game.light_time_left--;
12282
12283     if (game.light_time_left == 0)
12284       RedrawAllLightSwitchesAndInvisibleElements();
12285   }
12286
12287   if (game.timegate_time_left > 0)
12288   {
12289     game.timegate_time_left--;
12290
12291     if (game.timegate_time_left == 0)
12292       CloseAllOpenTimegates();
12293   }
12294
12295   if (game.lenses_time_left > 0)
12296   {
12297     game.lenses_time_left--;
12298
12299     if (game.lenses_time_left == 0)
12300       RedrawAllInvisibleElementsForLenses();
12301   }
12302
12303   if (game.magnify_time_left > 0)
12304   {
12305     game.magnify_time_left--;
12306
12307     if (game.magnify_time_left == 0)
12308       RedrawAllInvisibleElementsForMagnifier();
12309   }
12310
12311   for (i = 0; i < MAX_PLAYERS; i++)
12312   {
12313     struct PlayerInfo *player = &stored_player[i];
12314
12315     if (SHIELD_ON(player))
12316     {
12317       if (player->shield_deadly_time_left)
12318         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12319       else if (player->shield_normal_time_left)
12320         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12321     }
12322   }
12323
12324 #if USE_DELAYED_GFX_REDRAW
12325   SCAN_PLAYFIELD(x, y)
12326   {
12327     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12328     {
12329       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12330          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12331
12332       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12333         DrawLevelField(x, y);
12334
12335       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12336         DrawLevelFieldCrumbled(x, y);
12337
12338       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12339         DrawLevelFieldCrumbledNeighbours(x, y);
12340
12341       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12342         DrawTwinkleOnField(x, y);
12343     }
12344
12345     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12346   }
12347 #endif
12348
12349   DrawAllPlayers();
12350   PlayAllPlayersSound();
12351
12352   for (i = 0; i < MAX_PLAYERS; i++)
12353   {
12354     struct PlayerInfo *player = &stored_player[i];
12355
12356     if (player->show_envelope != 0 && (!player->active ||
12357                                        player->MovPos == 0))
12358     {
12359       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12360
12361       player->show_envelope = 0;
12362     }
12363   }
12364
12365   // use random number generator in every frame to make it less predictable
12366   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12367     RND(1);
12368
12369   mouse_action_last = mouse_action;
12370 }
12371
12372 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12373 {
12374   int min_x = x, min_y = y, max_x = x, max_y = y;
12375   int i;
12376
12377   for (i = 0; i < MAX_PLAYERS; i++)
12378   {
12379     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12380
12381     if (!stored_player[i].active || &stored_player[i] == player)
12382       continue;
12383
12384     min_x = MIN(min_x, jx);
12385     min_y = MIN(min_y, jy);
12386     max_x = MAX(max_x, jx);
12387     max_y = MAX(max_y, jy);
12388   }
12389
12390   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12391 }
12392
12393 static boolean AllPlayersInVisibleScreen(void)
12394 {
12395   int i;
12396
12397   for (i = 0; i < MAX_PLAYERS; i++)
12398   {
12399     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12400
12401     if (!stored_player[i].active)
12402       continue;
12403
12404     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12405       return FALSE;
12406   }
12407
12408   return TRUE;
12409 }
12410
12411 void ScrollLevel(int dx, int dy)
12412 {
12413   int scroll_offset = 2 * TILEX_VAR;
12414   int x, y;
12415
12416   BlitBitmap(drawto_field, drawto_field,
12417              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12418              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12419              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12420              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12421              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12422              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12423
12424   if (dx != 0)
12425   {
12426     x = (dx == 1 ? BX1 : BX2);
12427     for (y = BY1; y <= BY2; y++)
12428       DrawScreenField(x, y);
12429   }
12430
12431   if (dy != 0)
12432   {
12433     y = (dy == 1 ? BY1 : BY2);
12434     for (x = BX1; x <= BX2; x++)
12435       DrawScreenField(x, y);
12436   }
12437
12438   redraw_mask |= REDRAW_FIELD;
12439 }
12440
12441 static boolean canFallDown(struct PlayerInfo *player)
12442 {
12443   int jx = player->jx, jy = player->jy;
12444
12445   return (IN_LEV_FIELD(jx, jy + 1) &&
12446           (IS_FREE(jx, jy + 1) ||
12447            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12448           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12449           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12450 }
12451
12452 static boolean canPassField(int x, int y, int move_dir)
12453 {
12454   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12455   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12456   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12457   int nextx = x + dx;
12458   int nexty = y + dy;
12459   int element = Tile[x][y];
12460
12461   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12462           !CAN_MOVE(element) &&
12463           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12464           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12465           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12466 }
12467
12468 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12469 {
12470   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12471   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12472   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12473   int newx = x + dx;
12474   int newy = y + dy;
12475
12476   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12477           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12478           (IS_DIGGABLE(Tile[newx][newy]) ||
12479            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12480            canPassField(newx, newy, move_dir)));
12481 }
12482
12483 static void CheckGravityMovement(struct PlayerInfo *player)
12484 {
12485   if (player->gravity && !player->programmed_action)
12486   {
12487     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12488     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12489     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12490     int jx = player->jx, jy = player->jy;
12491     boolean player_is_moving_to_valid_field =
12492       (!player_is_snapping &&
12493        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12494         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12495     boolean player_can_fall_down = canFallDown(player);
12496
12497     if (player_can_fall_down &&
12498         !player_is_moving_to_valid_field)
12499       player->programmed_action = MV_DOWN;
12500   }
12501 }
12502
12503 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12504 {
12505   return CheckGravityMovement(player);
12506
12507   if (player->gravity && !player->programmed_action)
12508   {
12509     int jx = player->jx, jy = player->jy;
12510     boolean field_under_player_is_free =
12511       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12512     boolean player_is_standing_on_valid_field =
12513       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12514        (IS_WALKABLE(Tile[jx][jy]) &&
12515         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12516
12517     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12518       player->programmed_action = MV_DOWN;
12519   }
12520 }
12521
12522 /*
12523   MovePlayerOneStep()
12524   -----------------------------------------------------------------------------
12525   dx, dy:               direction (non-diagonal) to try to move the player to
12526   real_dx, real_dy:     direction as read from input device (can be diagonal)
12527 */
12528
12529 boolean MovePlayerOneStep(struct PlayerInfo *player,
12530                           int dx, int dy, int real_dx, int real_dy)
12531 {
12532   int jx = player->jx, jy = player->jy;
12533   int new_jx = jx + dx, new_jy = jy + dy;
12534   int can_move;
12535   boolean player_can_move = !player->cannot_move;
12536
12537   if (!player->active || (!dx && !dy))
12538     return MP_NO_ACTION;
12539
12540   player->MovDir = (dx < 0 ? MV_LEFT :
12541                     dx > 0 ? MV_RIGHT :
12542                     dy < 0 ? MV_UP :
12543                     dy > 0 ? MV_DOWN :  MV_NONE);
12544
12545   if (!IN_LEV_FIELD(new_jx, new_jy))
12546     return MP_NO_ACTION;
12547
12548   if (!player_can_move)
12549   {
12550     if (player->MovPos == 0)
12551     {
12552       player->is_moving = FALSE;
12553       player->is_digging = FALSE;
12554       player->is_collecting = FALSE;
12555       player->is_snapping = FALSE;
12556       player->is_pushing = FALSE;
12557     }
12558   }
12559
12560   if (!network.enabled && game.centered_player_nr == -1 &&
12561       !AllPlayersInSight(player, new_jx, new_jy))
12562     return MP_NO_ACTION;
12563
12564   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12565   if (can_move != MP_MOVING)
12566     return can_move;
12567
12568   // check if DigField() has caused relocation of the player
12569   if (player->jx != jx || player->jy != jy)
12570     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12571
12572   StorePlayer[jx][jy] = 0;
12573   player->last_jx = jx;
12574   player->last_jy = jy;
12575   player->jx = new_jx;
12576   player->jy = new_jy;
12577   StorePlayer[new_jx][new_jy] = player->element_nr;
12578
12579   if (player->move_delay_value_next != -1)
12580   {
12581     player->move_delay_value = player->move_delay_value_next;
12582     player->move_delay_value_next = -1;
12583   }
12584
12585   player->MovPos =
12586     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12587
12588   player->step_counter++;
12589
12590   PlayerVisit[jx][jy] = FrameCounter;
12591
12592   player->is_moving = TRUE;
12593
12594 #if 1
12595   // should better be called in MovePlayer(), but this breaks some tapes
12596   ScrollPlayer(player, SCROLL_INIT);
12597 #endif
12598
12599   return MP_MOVING;
12600 }
12601
12602 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12603 {
12604   int jx = player->jx, jy = player->jy;
12605   int old_jx = jx, old_jy = jy;
12606   int moved = MP_NO_ACTION;
12607
12608   if (!player->active)
12609     return FALSE;
12610
12611   if (!dx && !dy)
12612   {
12613     if (player->MovPos == 0)
12614     {
12615       player->is_moving = FALSE;
12616       player->is_digging = FALSE;
12617       player->is_collecting = FALSE;
12618       player->is_snapping = FALSE;
12619       player->is_pushing = FALSE;
12620     }
12621
12622     return FALSE;
12623   }
12624
12625   if (player->move_delay > 0)
12626     return FALSE;
12627
12628   player->move_delay = -1;              // set to "uninitialized" value
12629
12630   // store if player is automatically moved to next field
12631   player->is_auto_moving = (player->programmed_action != MV_NONE);
12632
12633   // remove the last programmed player action
12634   player->programmed_action = 0;
12635
12636   if (player->MovPos)
12637   {
12638     // should only happen if pre-1.2 tape recordings are played
12639     // this is only for backward compatibility
12640
12641     int original_move_delay_value = player->move_delay_value;
12642
12643 #if DEBUG
12644     Debug("game:playing:MovePlayer",
12645           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12646           tape.counter);
12647 #endif
12648
12649     // scroll remaining steps with finest movement resolution
12650     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12651
12652     while (player->MovPos)
12653     {
12654       ScrollPlayer(player, SCROLL_GO_ON);
12655       ScrollScreen(NULL, SCROLL_GO_ON);
12656
12657       AdvanceFrameAndPlayerCounters(player->index_nr);
12658
12659       DrawAllPlayers();
12660       BackToFront_WithFrameDelay(0);
12661     }
12662
12663     player->move_delay_value = original_move_delay_value;
12664   }
12665
12666   player->is_active = FALSE;
12667
12668   if (player->last_move_dir & MV_HORIZONTAL)
12669   {
12670     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12671       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12672   }
12673   else
12674   {
12675     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12676       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12677   }
12678
12679   if (!moved && !player->is_active)
12680   {
12681     player->is_moving = FALSE;
12682     player->is_digging = FALSE;
12683     player->is_collecting = FALSE;
12684     player->is_snapping = FALSE;
12685     player->is_pushing = FALSE;
12686   }
12687
12688   jx = player->jx;
12689   jy = player->jy;
12690
12691   if (moved & MP_MOVING && !ScreenMovPos &&
12692       (player->index_nr == game.centered_player_nr ||
12693        game.centered_player_nr == -1))
12694   {
12695     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12696
12697     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12698     {
12699       // actual player has left the screen -- scroll in that direction
12700       if (jx != old_jx)         // player has moved horizontally
12701         scroll_x += (jx - old_jx);
12702       else                      // player has moved vertically
12703         scroll_y += (jy - old_jy);
12704     }
12705     else
12706     {
12707       int offset_raw = game.scroll_delay_value;
12708
12709       if (jx != old_jx)         // player has moved horizontally
12710       {
12711         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12712         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12713         int new_scroll_x = jx - MIDPOSX + offset_x;
12714
12715         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12716             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12717           scroll_x = new_scroll_x;
12718
12719         // don't scroll over playfield boundaries
12720         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12721
12722         // don't scroll more than one field at a time
12723         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12724
12725         // don't scroll against the player's moving direction
12726         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12727             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12728           scroll_x = old_scroll_x;
12729       }
12730       else                      // player has moved vertically
12731       {
12732         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12733         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12734         int new_scroll_y = jy - MIDPOSY + offset_y;
12735
12736         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12737             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12738           scroll_y = new_scroll_y;
12739
12740         // don't scroll over playfield boundaries
12741         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12742
12743         // don't scroll more than one field at a time
12744         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12745
12746         // don't scroll against the player's moving direction
12747         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12748             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12749           scroll_y = old_scroll_y;
12750       }
12751     }
12752
12753     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12754     {
12755       if (!network.enabled && game.centered_player_nr == -1 &&
12756           !AllPlayersInVisibleScreen())
12757       {
12758         scroll_x = old_scroll_x;
12759         scroll_y = old_scroll_y;
12760       }
12761       else
12762       {
12763         ScrollScreen(player, SCROLL_INIT);
12764         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12765       }
12766     }
12767   }
12768
12769   player->StepFrame = 0;
12770
12771   if (moved & MP_MOVING)
12772   {
12773     if (old_jx != jx && old_jy == jy)
12774       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12775     else if (old_jx == jx && old_jy != jy)
12776       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12777
12778     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12779
12780     player->last_move_dir = player->MovDir;
12781     player->is_moving = TRUE;
12782     player->is_snapping = FALSE;
12783     player->is_switching = FALSE;
12784     player->is_dropping = FALSE;
12785     player->is_dropping_pressed = FALSE;
12786     player->drop_pressed_delay = 0;
12787
12788 #if 0
12789     // should better be called here than above, but this breaks some tapes
12790     ScrollPlayer(player, SCROLL_INIT);
12791 #endif
12792   }
12793   else
12794   {
12795     CheckGravityMovementWhenNotMoving(player);
12796
12797     player->is_moving = FALSE;
12798
12799     /* at this point, the player is allowed to move, but cannot move right now
12800        (e.g. because of something blocking the way) -- ensure that the player
12801        is also allowed to move in the next frame (in old versions before 3.1.1,
12802        the player was forced to wait again for eight frames before next try) */
12803
12804     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12805       player->move_delay = 0;   // allow direct movement in the next frame
12806   }
12807
12808   if (player->move_delay == -1)         // not yet initialized by DigField()
12809     player->move_delay = player->move_delay_value;
12810
12811   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12812   {
12813     TestIfPlayerTouchesBadThing(jx, jy);
12814     TestIfPlayerTouchesCustomElement(jx, jy);
12815   }
12816
12817   if (!player->active)
12818     RemovePlayer(player);
12819
12820   return moved;
12821 }
12822
12823 void ScrollPlayer(struct PlayerInfo *player, int mode)
12824 {
12825   int jx = player->jx, jy = player->jy;
12826   int last_jx = player->last_jx, last_jy = player->last_jy;
12827   int move_stepsize = TILEX / player->move_delay_value;
12828
12829   if (!player->active)
12830     return;
12831
12832   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12833     return;
12834
12835   if (mode == SCROLL_INIT)
12836   {
12837     player->actual_frame_counter = FrameCounter;
12838     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12839
12840     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12841         Tile[last_jx][last_jy] == EL_EMPTY)
12842     {
12843       int last_field_block_delay = 0;   // start with no blocking at all
12844       int block_delay_adjustment = player->block_delay_adjustment;
12845
12846       // if player blocks last field, add delay for exactly one move
12847       if (player->block_last_field)
12848       {
12849         last_field_block_delay += player->move_delay_value;
12850
12851         // when blocking enabled, prevent moving up despite gravity
12852         if (player->gravity && player->MovDir == MV_UP)
12853           block_delay_adjustment = -1;
12854       }
12855
12856       // add block delay adjustment (also possible when not blocking)
12857       last_field_block_delay += block_delay_adjustment;
12858
12859       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12860       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12861     }
12862
12863     if (player->MovPos != 0)    // player has not yet reached destination
12864       return;
12865   }
12866   else if (!FrameReached(&player->actual_frame_counter, 1))
12867     return;
12868
12869   if (player->MovPos != 0)
12870   {
12871     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12872     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12873
12874     // before DrawPlayer() to draw correct player graphic for this case
12875     if (player->MovPos == 0)
12876       CheckGravityMovement(player);
12877   }
12878
12879   if (player->MovPos == 0)      // player reached destination field
12880   {
12881     if (player->move_delay_reset_counter > 0)
12882     {
12883       player->move_delay_reset_counter--;
12884
12885       if (player->move_delay_reset_counter == 0)
12886       {
12887         // continue with normal speed after quickly moving through gate
12888         HALVE_PLAYER_SPEED(player);
12889
12890         // be able to make the next move without delay
12891         player->move_delay = 0;
12892       }
12893     }
12894
12895     player->last_jx = jx;
12896     player->last_jy = jy;
12897
12898     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12899         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12900         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12901         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12902         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12903         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12904         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12905         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12906     {
12907       ExitPlayer(player);
12908
12909       if (game.players_still_needed == 0 &&
12910           (game.friends_still_needed == 0 ||
12911            IS_SP_ELEMENT(Tile[jx][jy])))
12912         LevelSolved();
12913     }
12914
12915     // this breaks one level: "machine", level 000
12916     {
12917       int move_direction = player->MovDir;
12918       int enter_side = MV_DIR_OPPOSITE(move_direction);
12919       int leave_side = move_direction;
12920       int old_jx = last_jx;
12921       int old_jy = last_jy;
12922       int old_element = Tile[old_jx][old_jy];
12923       int new_element = Tile[jx][jy];
12924
12925       if (IS_CUSTOM_ELEMENT(old_element))
12926         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12927                                    CE_LEFT_BY_PLAYER,
12928                                    player->index_bit, leave_side);
12929
12930       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12931                                           CE_PLAYER_LEAVES_X,
12932                                           player->index_bit, leave_side);
12933
12934       if (IS_CUSTOM_ELEMENT(new_element))
12935         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12936                                    player->index_bit, enter_side);
12937
12938       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12939                                           CE_PLAYER_ENTERS_X,
12940                                           player->index_bit, enter_side);
12941
12942       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12943                                         CE_MOVE_OF_X, move_direction);
12944     }
12945
12946     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12947     {
12948       TestIfPlayerTouchesBadThing(jx, jy);
12949       TestIfPlayerTouchesCustomElement(jx, jy);
12950
12951       /* needed because pushed element has not yet reached its destination,
12952          so it would trigger a change event at its previous field location */
12953       if (!player->is_pushing)
12954         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12955
12956       if (!player->active)
12957         RemovePlayer(player);
12958     }
12959
12960     if (!game.LevelSolved && level.use_step_counter)
12961     {
12962       int i;
12963
12964       TimePlayed++;
12965
12966       if (TimeLeft > 0)
12967       {
12968         TimeLeft--;
12969
12970         if (TimeLeft <= 10 && setup.time_limit)
12971           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12972
12973         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12974
12975         DisplayGameControlValues();
12976
12977         if (!TimeLeft && setup.time_limit)
12978           for (i = 0; i < MAX_PLAYERS; i++)
12979             KillPlayer(&stored_player[i]);
12980       }
12981       else if (game.no_time_limit && !game.all_players_gone)
12982       {
12983         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12984
12985         DisplayGameControlValues();
12986       }
12987     }
12988
12989     if (tape.single_step && tape.recording && !tape.pausing &&
12990         !player->programmed_action)
12991       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12992
12993     if (!player->programmed_action)
12994       CheckSaveEngineSnapshot(player);
12995   }
12996 }
12997
12998 void ScrollScreen(struct PlayerInfo *player, int mode)
12999 {
13000   static unsigned int screen_frame_counter = 0;
13001
13002   if (mode == SCROLL_INIT)
13003   {
13004     // set scrolling step size according to actual player's moving speed
13005     ScrollStepSize = TILEX / player->move_delay_value;
13006
13007     screen_frame_counter = FrameCounter;
13008     ScreenMovDir = player->MovDir;
13009     ScreenMovPos = player->MovPos;
13010     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13011     return;
13012   }
13013   else if (!FrameReached(&screen_frame_counter, 1))
13014     return;
13015
13016   if (ScreenMovPos)
13017   {
13018     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13019     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13020     redraw_mask |= REDRAW_FIELD;
13021   }
13022   else
13023     ScreenMovDir = MV_NONE;
13024 }
13025
13026 void TestIfPlayerTouchesCustomElement(int x, int y)
13027 {
13028   static int xy[4][2] =
13029   {
13030     { 0, -1 },
13031     { -1, 0 },
13032     { +1, 0 },
13033     { 0, +1 }
13034   };
13035   static int trigger_sides[4][2] =
13036   {
13037     // center side       border side
13038     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13039     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13040     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13041     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13042   };
13043   static int touch_dir[4] =
13044   {
13045     MV_LEFT | MV_RIGHT,
13046     MV_UP   | MV_DOWN,
13047     MV_UP   | MV_DOWN,
13048     MV_LEFT | MV_RIGHT
13049   };
13050   int center_element = Tile[x][y];      // should always be non-moving!
13051   int i;
13052
13053   for (i = 0; i < NUM_DIRECTIONS; i++)
13054   {
13055     int xx = x + xy[i][0];
13056     int yy = y + xy[i][1];
13057     int center_side = trigger_sides[i][0];
13058     int border_side = trigger_sides[i][1];
13059     int border_element;
13060
13061     if (!IN_LEV_FIELD(xx, yy))
13062       continue;
13063
13064     if (IS_PLAYER(x, y))                // player found at center element
13065     {
13066       struct PlayerInfo *player = PLAYERINFO(x, y);
13067
13068       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13069         border_element = Tile[xx][yy];          // may be moving!
13070       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13071         border_element = Tile[xx][yy];
13072       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13073         border_element = MovingOrBlocked2Element(xx, yy);
13074       else
13075         continue;               // center and border element do not touch
13076
13077       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13078                                  player->index_bit, border_side);
13079       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13080                                           CE_PLAYER_TOUCHES_X,
13081                                           player->index_bit, border_side);
13082
13083       {
13084         /* use player element that is initially defined in the level playfield,
13085            not the player element that corresponds to the runtime player number
13086            (example: a level that contains EL_PLAYER_3 as the only player would
13087            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13088         int player_element = PLAYERINFO(x, y)->initial_element;
13089
13090         CheckElementChangeBySide(xx, yy, border_element, player_element,
13091                                  CE_TOUCHING_X, border_side);
13092       }
13093     }
13094     else if (IS_PLAYER(xx, yy))         // player found at border element
13095     {
13096       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13097
13098       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13099       {
13100         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13101           continue;             // center and border element do not touch
13102       }
13103
13104       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13105                                  player->index_bit, center_side);
13106       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13107                                           CE_PLAYER_TOUCHES_X,
13108                                           player->index_bit, center_side);
13109
13110       {
13111         /* use player element that is initially defined in the level playfield,
13112            not the player element that corresponds to the runtime player number
13113            (example: a level that contains EL_PLAYER_3 as the only player would
13114            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13115         int player_element = PLAYERINFO(xx, yy)->initial_element;
13116
13117         CheckElementChangeBySide(x, y, center_element, player_element,
13118                                  CE_TOUCHING_X, center_side);
13119       }
13120
13121       break;
13122     }
13123   }
13124 }
13125
13126 void TestIfElementTouchesCustomElement(int x, int y)
13127 {
13128   static int xy[4][2] =
13129   {
13130     { 0, -1 },
13131     { -1, 0 },
13132     { +1, 0 },
13133     { 0, +1 }
13134   };
13135   static int trigger_sides[4][2] =
13136   {
13137     // center side      border side
13138     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13139     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13140     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13141     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13142   };
13143   static int touch_dir[4] =
13144   {
13145     MV_LEFT | MV_RIGHT,
13146     MV_UP   | MV_DOWN,
13147     MV_UP   | MV_DOWN,
13148     MV_LEFT | MV_RIGHT
13149   };
13150   boolean change_center_element = FALSE;
13151   int center_element = Tile[x][y];      // should always be non-moving!
13152   int border_element_old[NUM_DIRECTIONS];
13153   int i;
13154
13155   for (i = 0; i < NUM_DIRECTIONS; i++)
13156   {
13157     int xx = x + xy[i][0];
13158     int yy = y + xy[i][1];
13159     int border_element;
13160
13161     border_element_old[i] = -1;
13162
13163     if (!IN_LEV_FIELD(xx, yy))
13164       continue;
13165
13166     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13167       border_element = Tile[xx][yy];    // may be moving!
13168     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13169       border_element = Tile[xx][yy];
13170     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13171       border_element = MovingOrBlocked2Element(xx, yy);
13172     else
13173       continue;                 // center and border element do not touch
13174
13175     border_element_old[i] = border_element;
13176   }
13177
13178   for (i = 0; i < NUM_DIRECTIONS; i++)
13179   {
13180     int xx = x + xy[i][0];
13181     int yy = y + xy[i][1];
13182     int center_side = trigger_sides[i][0];
13183     int border_element = border_element_old[i];
13184
13185     if (border_element == -1)
13186       continue;
13187
13188     // check for change of border element
13189     CheckElementChangeBySide(xx, yy, border_element, center_element,
13190                              CE_TOUCHING_X, center_side);
13191
13192     // (center element cannot be player, so we dont have to check this here)
13193   }
13194
13195   for (i = 0; i < NUM_DIRECTIONS; i++)
13196   {
13197     int xx = x + xy[i][0];
13198     int yy = y + xy[i][1];
13199     int border_side = trigger_sides[i][1];
13200     int border_element = border_element_old[i];
13201
13202     if (border_element == -1)
13203       continue;
13204
13205     // check for change of center element (but change it only once)
13206     if (!change_center_element)
13207       change_center_element =
13208         CheckElementChangeBySide(x, y, center_element, border_element,
13209                                  CE_TOUCHING_X, border_side);
13210
13211     if (IS_PLAYER(xx, yy))
13212     {
13213       /* use player element that is initially defined in the level playfield,
13214          not the player element that corresponds to the runtime player number
13215          (example: a level that contains EL_PLAYER_3 as the only player would
13216          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13217       int player_element = PLAYERINFO(xx, yy)->initial_element;
13218
13219       CheckElementChangeBySide(x, y, center_element, player_element,
13220                                CE_TOUCHING_X, border_side);
13221     }
13222   }
13223 }
13224
13225 void TestIfElementHitsCustomElement(int x, int y, int direction)
13226 {
13227   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13228   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13229   int hitx = x + dx, hity = y + dy;
13230   int hitting_element = Tile[x][y];
13231   int touched_element;
13232
13233   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13234     return;
13235
13236   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13237                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13238
13239   if (IN_LEV_FIELD(hitx, hity))
13240   {
13241     int opposite_direction = MV_DIR_OPPOSITE(direction);
13242     int hitting_side = direction;
13243     int touched_side = opposite_direction;
13244     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13245                           MovDir[hitx][hity] != direction ||
13246                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13247
13248     object_hit = TRUE;
13249
13250     if (object_hit)
13251     {
13252       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13253                                CE_HITTING_X, touched_side);
13254
13255       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13256                                CE_HIT_BY_X, hitting_side);
13257
13258       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13259                                CE_HIT_BY_SOMETHING, opposite_direction);
13260
13261       if (IS_PLAYER(hitx, hity))
13262       {
13263         /* use player element that is initially defined in the level playfield,
13264            not the player element that corresponds to the runtime player number
13265            (example: a level that contains EL_PLAYER_3 as the only player would
13266            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13267         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13268
13269         CheckElementChangeBySide(x, y, hitting_element, player_element,
13270                                  CE_HITTING_X, touched_side);
13271       }
13272     }
13273   }
13274
13275   // "hitting something" is also true when hitting the playfield border
13276   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13277                            CE_HITTING_SOMETHING, direction);
13278 }
13279
13280 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13281 {
13282   int i, kill_x = -1, kill_y = -1;
13283
13284   int bad_element = -1;
13285   static int test_xy[4][2] =
13286   {
13287     { 0, -1 },
13288     { -1, 0 },
13289     { +1, 0 },
13290     { 0, +1 }
13291   };
13292   static int test_dir[4] =
13293   {
13294     MV_UP,
13295     MV_LEFT,
13296     MV_RIGHT,
13297     MV_DOWN
13298   };
13299
13300   for (i = 0; i < NUM_DIRECTIONS; i++)
13301   {
13302     int test_x, test_y, test_move_dir, test_element;
13303
13304     test_x = good_x + test_xy[i][0];
13305     test_y = good_y + test_xy[i][1];
13306
13307     if (!IN_LEV_FIELD(test_x, test_y))
13308       continue;
13309
13310     test_move_dir =
13311       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13312
13313     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13314
13315     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13316        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13317     */
13318     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13319         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13320     {
13321       kill_x = test_x;
13322       kill_y = test_y;
13323       bad_element = test_element;
13324
13325       break;
13326     }
13327   }
13328
13329   if (kill_x != -1 || kill_y != -1)
13330   {
13331     if (IS_PLAYER(good_x, good_y))
13332     {
13333       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13334
13335       if (player->shield_deadly_time_left > 0 &&
13336           !IS_INDESTRUCTIBLE(bad_element))
13337         Bang(kill_x, kill_y);
13338       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13339         KillPlayer(player);
13340     }
13341     else
13342       Bang(good_x, good_y);
13343   }
13344 }
13345
13346 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13347 {
13348   int i, kill_x = -1, kill_y = -1;
13349   int bad_element = Tile[bad_x][bad_y];
13350   static int test_xy[4][2] =
13351   {
13352     { 0, -1 },
13353     { -1, 0 },
13354     { +1, 0 },
13355     { 0, +1 }
13356   };
13357   static int touch_dir[4] =
13358   {
13359     MV_LEFT | MV_RIGHT,
13360     MV_UP   | MV_DOWN,
13361     MV_UP   | MV_DOWN,
13362     MV_LEFT | MV_RIGHT
13363   };
13364   static int test_dir[4] =
13365   {
13366     MV_UP,
13367     MV_LEFT,
13368     MV_RIGHT,
13369     MV_DOWN
13370   };
13371
13372   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13373     return;
13374
13375   for (i = 0; i < NUM_DIRECTIONS; i++)
13376   {
13377     int test_x, test_y, test_move_dir, test_element;
13378
13379     test_x = bad_x + test_xy[i][0];
13380     test_y = bad_y + test_xy[i][1];
13381
13382     if (!IN_LEV_FIELD(test_x, test_y))
13383       continue;
13384
13385     test_move_dir =
13386       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13387
13388     test_element = Tile[test_x][test_y];
13389
13390     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13391        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13392     */
13393     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13394         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13395     {
13396       // good thing is player or penguin that does not move away
13397       if (IS_PLAYER(test_x, test_y))
13398       {
13399         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13400
13401         if (bad_element == EL_ROBOT && player->is_moving)
13402           continue;     // robot does not kill player if he is moving
13403
13404         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13405         {
13406           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13407             continue;           // center and border element do not touch
13408         }
13409
13410         kill_x = test_x;
13411         kill_y = test_y;
13412
13413         break;
13414       }
13415       else if (test_element == EL_PENGUIN)
13416       {
13417         kill_x = test_x;
13418         kill_y = test_y;
13419
13420         break;
13421       }
13422     }
13423   }
13424
13425   if (kill_x != -1 || kill_y != -1)
13426   {
13427     if (IS_PLAYER(kill_x, kill_y))
13428     {
13429       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13430
13431       if (player->shield_deadly_time_left > 0 &&
13432           !IS_INDESTRUCTIBLE(bad_element))
13433         Bang(bad_x, bad_y);
13434       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13435         KillPlayer(player);
13436     }
13437     else
13438       Bang(kill_x, kill_y);
13439   }
13440 }
13441
13442 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13443 {
13444   int bad_element = Tile[bad_x][bad_y];
13445   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13446   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13447   int test_x = bad_x + dx, test_y = bad_y + dy;
13448   int test_move_dir, test_element;
13449   int kill_x = -1, kill_y = -1;
13450
13451   if (!IN_LEV_FIELD(test_x, test_y))
13452     return;
13453
13454   test_move_dir =
13455     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13456
13457   test_element = Tile[test_x][test_y];
13458
13459   if (test_move_dir != bad_move_dir)
13460   {
13461     // good thing can be player or penguin that does not move away
13462     if (IS_PLAYER(test_x, test_y))
13463     {
13464       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13465
13466       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13467          player as being hit when he is moving towards the bad thing, because
13468          the "get hit by" condition would be lost after the player stops) */
13469       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13470         return;         // player moves away from bad thing
13471
13472       kill_x = test_x;
13473       kill_y = test_y;
13474     }
13475     else if (test_element == EL_PENGUIN)
13476     {
13477       kill_x = test_x;
13478       kill_y = test_y;
13479     }
13480   }
13481
13482   if (kill_x != -1 || kill_y != -1)
13483   {
13484     if (IS_PLAYER(kill_x, kill_y))
13485     {
13486       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13487
13488       if (player->shield_deadly_time_left > 0 &&
13489           !IS_INDESTRUCTIBLE(bad_element))
13490         Bang(bad_x, bad_y);
13491       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13492         KillPlayer(player);
13493     }
13494     else
13495       Bang(kill_x, kill_y);
13496   }
13497 }
13498
13499 void TestIfPlayerTouchesBadThing(int x, int y)
13500 {
13501   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13502 }
13503
13504 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13505 {
13506   TestIfGoodThingHitsBadThing(x, y, move_dir);
13507 }
13508
13509 void TestIfBadThingTouchesPlayer(int x, int y)
13510 {
13511   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13512 }
13513
13514 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13515 {
13516   TestIfBadThingHitsGoodThing(x, y, move_dir);
13517 }
13518
13519 void TestIfFriendTouchesBadThing(int x, int y)
13520 {
13521   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13522 }
13523
13524 void TestIfBadThingTouchesFriend(int x, int y)
13525 {
13526   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13527 }
13528
13529 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13530 {
13531   int i, kill_x = bad_x, kill_y = bad_y;
13532   static int xy[4][2] =
13533   {
13534     { 0, -1 },
13535     { -1, 0 },
13536     { +1, 0 },
13537     { 0, +1 }
13538   };
13539
13540   for (i = 0; i < NUM_DIRECTIONS; i++)
13541   {
13542     int x, y, element;
13543
13544     x = bad_x + xy[i][0];
13545     y = bad_y + xy[i][1];
13546     if (!IN_LEV_FIELD(x, y))
13547       continue;
13548
13549     element = Tile[x][y];
13550     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13551         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13552     {
13553       kill_x = x;
13554       kill_y = y;
13555       break;
13556     }
13557   }
13558
13559   if (kill_x != bad_x || kill_y != bad_y)
13560     Bang(bad_x, bad_y);
13561 }
13562
13563 void KillPlayer(struct PlayerInfo *player)
13564 {
13565   int jx = player->jx, jy = player->jy;
13566
13567   if (!player->active)
13568     return;
13569
13570 #if 0
13571   Debug("game:playing:KillPlayer",
13572         "0: killed == %d, active == %d, reanimated == %d",
13573         player->killed, player->active, player->reanimated);
13574 #endif
13575
13576   /* the following code was introduced to prevent an infinite loop when calling
13577      -> Bang()
13578      -> CheckTriggeredElementChangeExt()
13579      -> ExecuteCustomElementAction()
13580      -> KillPlayer()
13581      -> (infinitely repeating the above sequence of function calls)
13582      which occurs when killing the player while having a CE with the setting
13583      "kill player X when explosion of <player X>"; the solution using a new
13584      field "player->killed" was chosen for backwards compatibility, although
13585      clever use of the fields "player->active" etc. would probably also work */
13586 #if 1
13587   if (player->killed)
13588     return;
13589 #endif
13590
13591   player->killed = TRUE;
13592
13593   // remove accessible field at the player's position
13594   Tile[jx][jy] = EL_EMPTY;
13595
13596   // deactivate shield (else Bang()/Explode() would not work right)
13597   player->shield_normal_time_left = 0;
13598   player->shield_deadly_time_left = 0;
13599
13600 #if 0
13601   Debug("game:playing:KillPlayer",
13602         "1: killed == %d, active == %d, reanimated == %d",
13603         player->killed, player->active, player->reanimated);
13604 #endif
13605
13606   Bang(jx, jy);
13607
13608 #if 0
13609   Debug("game:playing:KillPlayer",
13610         "2: killed == %d, active == %d, reanimated == %d",
13611         player->killed, player->active, player->reanimated);
13612 #endif
13613
13614   if (player->reanimated)       // killed player may have been reanimated
13615     player->killed = player->reanimated = FALSE;
13616   else
13617     BuryPlayer(player);
13618 }
13619
13620 static void KillPlayerUnlessEnemyProtected(int x, int y)
13621 {
13622   if (!PLAYER_ENEMY_PROTECTED(x, y))
13623     KillPlayer(PLAYERINFO(x, y));
13624 }
13625
13626 static void KillPlayerUnlessExplosionProtected(int x, int y)
13627 {
13628   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13629     KillPlayer(PLAYERINFO(x, y));
13630 }
13631
13632 void BuryPlayer(struct PlayerInfo *player)
13633 {
13634   int jx = player->jx, jy = player->jy;
13635
13636   if (!player->active)
13637     return;
13638
13639   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13640   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13641
13642   RemovePlayer(player);
13643
13644   player->buried = TRUE;
13645
13646   if (game.all_players_gone)
13647     game.GameOver = TRUE;
13648 }
13649
13650 void RemovePlayer(struct PlayerInfo *player)
13651 {
13652   int jx = player->jx, jy = player->jy;
13653   int i, found = FALSE;
13654
13655   player->present = FALSE;
13656   player->active = FALSE;
13657
13658   // required for some CE actions (even if the player is not active anymore)
13659   player->MovPos = 0;
13660
13661   if (!ExplodeField[jx][jy])
13662     StorePlayer[jx][jy] = 0;
13663
13664   if (player->is_moving)
13665     TEST_DrawLevelField(player->last_jx, player->last_jy);
13666
13667   for (i = 0; i < MAX_PLAYERS; i++)
13668     if (stored_player[i].active)
13669       found = TRUE;
13670
13671   if (!found)
13672   {
13673     game.all_players_gone = TRUE;
13674     game.GameOver = TRUE;
13675   }
13676
13677   game.exit_x = game.robot_wheel_x = jx;
13678   game.exit_y = game.robot_wheel_y = jy;
13679 }
13680
13681 void ExitPlayer(struct PlayerInfo *player)
13682 {
13683   DrawPlayer(player);   // needed here only to cleanup last field
13684   RemovePlayer(player);
13685
13686   if (game.players_still_needed > 0)
13687     game.players_still_needed--;
13688 }
13689
13690 static void setFieldForSnapping(int x, int y, int element, int direction)
13691 {
13692   struct ElementInfo *ei = &element_info[element];
13693   int direction_bit = MV_DIR_TO_BIT(direction);
13694   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13695   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13696                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13697
13698   Tile[x][y] = EL_ELEMENT_SNAPPING;
13699   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13700
13701   ResetGfxAnimation(x, y);
13702
13703   GfxElement[x][y] = element;
13704   GfxAction[x][y] = action;
13705   GfxDir[x][y] = direction;
13706   GfxFrame[x][y] = -1;
13707 }
13708
13709 /*
13710   =============================================================================
13711   checkDiagonalPushing()
13712   -----------------------------------------------------------------------------
13713   check if diagonal input device direction results in pushing of object
13714   (by checking if the alternative direction is walkable, diggable, ...)
13715   =============================================================================
13716 */
13717
13718 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13719                                     int x, int y, int real_dx, int real_dy)
13720 {
13721   int jx, jy, dx, dy, xx, yy;
13722
13723   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13724     return TRUE;
13725
13726   // diagonal direction: check alternative direction
13727   jx = player->jx;
13728   jy = player->jy;
13729   dx = x - jx;
13730   dy = y - jy;
13731   xx = jx + (dx == 0 ? real_dx : 0);
13732   yy = jy + (dy == 0 ? real_dy : 0);
13733
13734   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13735 }
13736
13737 /*
13738   =============================================================================
13739   DigField()
13740   -----------------------------------------------------------------------------
13741   x, y:                 field next to player (non-diagonal) to try to dig to
13742   real_dx, real_dy:     direction as read from input device (can be diagonal)
13743   =============================================================================
13744 */
13745
13746 static int DigField(struct PlayerInfo *player,
13747                     int oldx, int oldy, int x, int y,
13748                     int real_dx, int real_dy, int mode)
13749 {
13750   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13751   boolean player_was_pushing = player->is_pushing;
13752   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13753   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13754   int jx = oldx, jy = oldy;
13755   int dx = x - jx, dy = y - jy;
13756   int nextx = x + dx, nexty = y + dy;
13757   int move_direction = (dx == -1 ? MV_LEFT  :
13758                         dx == +1 ? MV_RIGHT :
13759                         dy == -1 ? MV_UP    :
13760                         dy == +1 ? MV_DOWN  : MV_NONE);
13761   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13762   int dig_side = MV_DIR_OPPOSITE(move_direction);
13763   int old_element = Tile[jx][jy];
13764   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13765   int collect_count;
13766
13767   if (is_player)                // function can also be called by EL_PENGUIN
13768   {
13769     if (player->MovPos == 0)
13770     {
13771       player->is_digging = FALSE;
13772       player->is_collecting = FALSE;
13773     }
13774
13775     if (player->MovPos == 0)    // last pushing move finished
13776       player->is_pushing = FALSE;
13777
13778     if (mode == DF_NO_PUSH)     // player just stopped pushing
13779     {
13780       player->is_switching = FALSE;
13781       player->push_delay = -1;
13782
13783       return MP_NO_ACTION;
13784     }
13785   }
13786
13787   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13788     old_element = Back[jx][jy];
13789
13790   // in case of element dropped at player position, check background
13791   else if (Back[jx][jy] != EL_EMPTY &&
13792            game.engine_version >= VERSION_IDENT(2,2,0,0))
13793     old_element = Back[jx][jy];
13794
13795   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13796     return MP_NO_ACTION;        // field has no opening in this direction
13797
13798   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13799     return MP_NO_ACTION;        // field has no opening in this direction
13800
13801   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13802   {
13803     SplashAcid(x, y);
13804
13805     Tile[jx][jy] = player->artwork_element;
13806     InitMovingField(jx, jy, MV_DOWN);
13807     Store[jx][jy] = EL_ACID;
13808     ContinueMoving(jx, jy);
13809     BuryPlayer(player);
13810
13811     return MP_DONT_RUN_INTO;
13812   }
13813
13814   if (player_can_move && DONT_RUN_INTO(element))
13815   {
13816     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13817
13818     return MP_DONT_RUN_INTO;
13819   }
13820
13821   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13822     return MP_NO_ACTION;
13823
13824   collect_count = element_info[element].collect_count_initial;
13825
13826   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13827     return MP_NO_ACTION;
13828
13829   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13830     player_can_move = player_can_move_or_snap;
13831
13832   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13833       game.engine_version >= VERSION_IDENT(2,2,0,0))
13834   {
13835     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13836                                player->index_bit, dig_side);
13837     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13838                                         player->index_bit, dig_side);
13839
13840     if (element == EL_DC_LANDMINE)
13841       Bang(x, y);
13842
13843     if (Tile[x][y] != element)          // field changed by snapping
13844       return MP_ACTION;
13845
13846     return MP_NO_ACTION;
13847   }
13848
13849   if (player->gravity && is_player && !player->is_auto_moving &&
13850       canFallDown(player) && move_direction != MV_DOWN &&
13851       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13852     return MP_NO_ACTION;        // player cannot walk here due to gravity
13853
13854   if (player_can_move &&
13855       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13856   {
13857     int sound_element = SND_ELEMENT(element);
13858     int sound_action = ACTION_WALKING;
13859
13860     if (IS_RND_GATE(element))
13861     {
13862       if (!player->key[RND_GATE_NR(element)])
13863         return MP_NO_ACTION;
13864     }
13865     else if (IS_RND_GATE_GRAY(element))
13866     {
13867       if (!player->key[RND_GATE_GRAY_NR(element)])
13868         return MP_NO_ACTION;
13869     }
13870     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13871     {
13872       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13873         return MP_NO_ACTION;
13874     }
13875     else if (element == EL_EXIT_OPEN ||
13876              element == EL_EM_EXIT_OPEN ||
13877              element == EL_EM_EXIT_OPENING ||
13878              element == EL_STEEL_EXIT_OPEN ||
13879              element == EL_EM_STEEL_EXIT_OPEN ||
13880              element == EL_EM_STEEL_EXIT_OPENING ||
13881              element == EL_SP_EXIT_OPEN ||
13882              element == EL_SP_EXIT_OPENING)
13883     {
13884       sound_action = ACTION_PASSING;    // player is passing exit
13885     }
13886     else if (element == EL_EMPTY)
13887     {
13888       sound_action = ACTION_MOVING;             // nothing to walk on
13889     }
13890
13891     // play sound from background or player, whatever is available
13892     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13893       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13894     else
13895       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13896   }
13897   else if (player_can_move &&
13898            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13899   {
13900     if (!ACCESS_FROM(element, opposite_direction))
13901       return MP_NO_ACTION;      // field not accessible from this direction
13902
13903     if (CAN_MOVE(element))      // only fixed elements can be passed!
13904       return MP_NO_ACTION;
13905
13906     if (IS_EM_GATE(element))
13907     {
13908       if (!player->key[EM_GATE_NR(element)])
13909         return MP_NO_ACTION;
13910     }
13911     else if (IS_EM_GATE_GRAY(element))
13912     {
13913       if (!player->key[EM_GATE_GRAY_NR(element)])
13914         return MP_NO_ACTION;
13915     }
13916     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13917     {
13918       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13919         return MP_NO_ACTION;
13920     }
13921     else if (IS_EMC_GATE(element))
13922     {
13923       if (!player->key[EMC_GATE_NR(element)])
13924         return MP_NO_ACTION;
13925     }
13926     else if (IS_EMC_GATE_GRAY(element))
13927     {
13928       if (!player->key[EMC_GATE_GRAY_NR(element)])
13929         return MP_NO_ACTION;
13930     }
13931     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13932     {
13933       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13934         return MP_NO_ACTION;
13935     }
13936     else if (element == EL_DC_GATE_WHITE ||
13937              element == EL_DC_GATE_WHITE_GRAY ||
13938              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13939     {
13940       if (player->num_white_keys == 0)
13941         return MP_NO_ACTION;
13942
13943       player->num_white_keys--;
13944     }
13945     else if (IS_SP_PORT(element))
13946     {
13947       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13948           element == EL_SP_GRAVITY_PORT_RIGHT ||
13949           element == EL_SP_GRAVITY_PORT_UP ||
13950           element == EL_SP_GRAVITY_PORT_DOWN)
13951         player->gravity = !player->gravity;
13952       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13953                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13954                element == EL_SP_GRAVITY_ON_PORT_UP ||
13955                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13956         player->gravity = TRUE;
13957       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13958                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13959                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13960                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13961         player->gravity = FALSE;
13962     }
13963
13964     // automatically move to the next field with double speed
13965     player->programmed_action = move_direction;
13966
13967     if (player->move_delay_reset_counter == 0)
13968     {
13969       player->move_delay_reset_counter = 2;     // two double speed steps
13970
13971       DOUBLE_PLAYER_SPEED(player);
13972     }
13973
13974     PlayLevelSoundAction(x, y, ACTION_PASSING);
13975   }
13976   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13977   {
13978     RemoveField(x, y);
13979
13980     if (mode != DF_SNAP)
13981     {
13982       GfxElement[x][y] = GFX_ELEMENT(element);
13983       player->is_digging = TRUE;
13984     }
13985
13986     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13987
13988     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13989                                         player->index_bit, dig_side);
13990
13991     if (mode == DF_SNAP)
13992     {
13993       if (level.block_snap_field)
13994         setFieldForSnapping(x, y, element, move_direction);
13995       else
13996         TestIfElementTouchesCustomElement(x, y);        // for empty space
13997
13998       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13999                                           player->index_bit, dig_side);
14000     }
14001   }
14002   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14003   {
14004     RemoveField(x, y);
14005
14006     if (is_player && mode != DF_SNAP)
14007     {
14008       GfxElement[x][y] = element;
14009       player->is_collecting = TRUE;
14010     }
14011
14012     if (element == EL_SPEED_PILL)
14013     {
14014       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14015     }
14016     else if (element == EL_EXTRA_TIME && level.time > 0)
14017     {
14018       TimeLeft += level.extra_time;
14019
14020       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14021
14022       DisplayGameControlValues();
14023     }
14024     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14025     {
14026       player->shield_normal_time_left += level.shield_normal_time;
14027       if (element == EL_SHIELD_DEADLY)
14028         player->shield_deadly_time_left += level.shield_deadly_time;
14029     }
14030     else if (element == EL_DYNAMITE ||
14031              element == EL_EM_DYNAMITE ||
14032              element == EL_SP_DISK_RED)
14033     {
14034       if (player->inventory_size < MAX_INVENTORY_SIZE)
14035         player->inventory_element[player->inventory_size++] = element;
14036
14037       DrawGameDoorValues();
14038     }
14039     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14040     {
14041       player->dynabomb_count++;
14042       player->dynabombs_left++;
14043     }
14044     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14045     {
14046       player->dynabomb_size++;
14047     }
14048     else if (element == EL_DYNABOMB_INCREASE_POWER)
14049     {
14050       player->dynabomb_xl = TRUE;
14051     }
14052     else if (IS_KEY(element))
14053     {
14054       player->key[KEY_NR(element)] = TRUE;
14055
14056       DrawGameDoorValues();
14057     }
14058     else if (element == EL_DC_KEY_WHITE)
14059     {
14060       player->num_white_keys++;
14061
14062       // display white keys?
14063       // DrawGameDoorValues();
14064     }
14065     else if (IS_ENVELOPE(element))
14066     {
14067       player->show_envelope = element;
14068     }
14069     else if (element == EL_EMC_LENSES)
14070     {
14071       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14072
14073       RedrawAllInvisibleElementsForLenses();
14074     }
14075     else if (element == EL_EMC_MAGNIFIER)
14076     {
14077       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14078
14079       RedrawAllInvisibleElementsForMagnifier();
14080     }
14081     else if (IS_DROPPABLE(element) ||
14082              IS_THROWABLE(element))     // can be collected and dropped
14083     {
14084       int i;
14085
14086       if (collect_count == 0)
14087         player->inventory_infinite_element = element;
14088       else
14089         for (i = 0; i < collect_count; i++)
14090           if (player->inventory_size < MAX_INVENTORY_SIZE)
14091             player->inventory_element[player->inventory_size++] = element;
14092
14093       DrawGameDoorValues();
14094     }
14095     else if (collect_count > 0)
14096     {
14097       game.gems_still_needed -= collect_count;
14098       if (game.gems_still_needed < 0)
14099         game.gems_still_needed = 0;
14100
14101       game.snapshot.collected_item = TRUE;
14102
14103       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14104
14105       DisplayGameControlValues();
14106     }
14107
14108     RaiseScoreElement(element);
14109     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14110
14111     if (is_player)
14112       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14113                                           player->index_bit, dig_side);
14114
14115     if (mode == DF_SNAP)
14116     {
14117       if (level.block_snap_field)
14118         setFieldForSnapping(x, y, element, move_direction);
14119       else
14120         TestIfElementTouchesCustomElement(x, y);        // for empty space
14121
14122       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14123                                           player->index_bit, dig_side);
14124     }
14125   }
14126   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14127   {
14128     if (mode == DF_SNAP && element != EL_BD_ROCK)
14129       return MP_NO_ACTION;
14130
14131     if (CAN_FALL(element) && dy)
14132       return MP_NO_ACTION;
14133
14134     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14135         !(element == EL_SPRING && level.use_spring_bug))
14136       return MP_NO_ACTION;
14137
14138     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14139         ((move_direction & MV_VERTICAL &&
14140           ((element_info[element].move_pattern & MV_LEFT &&
14141             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14142            (element_info[element].move_pattern & MV_RIGHT &&
14143             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14144          (move_direction & MV_HORIZONTAL &&
14145           ((element_info[element].move_pattern & MV_UP &&
14146             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14147            (element_info[element].move_pattern & MV_DOWN &&
14148             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14149       return MP_NO_ACTION;
14150
14151     // do not push elements already moving away faster than player
14152     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14153         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14154       return MP_NO_ACTION;
14155
14156     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14157     {
14158       if (player->push_delay_value == -1 || !player_was_pushing)
14159         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14160     }
14161     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14162     {
14163       if (player->push_delay_value == -1)
14164         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14165     }
14166     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14167     {
14168       if (!player->is_pushing)
14169         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14170     }
14171
14172     player->is_pushing = TRUE;
14173     player->is_active = TRUE;
14174
14175     if (!(IN_LEV_FIELD(nextx, nexty) &&
14176           (IS_FREE(nextx, nexty) ||
14177            (IS_SB_ELEMENT(element) &&
14178             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14179            (IS_CUSTOM_ELEMENT(element) &&
14180             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14181       return MP_NO_ACTION;
14182
14183     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14184       return MP_NO_ACTION;
14185
14186     if (player->push_delay == -1)       // new pushing; restart delay
14187       player->push_delay = 0;
14188
14189     if (player->push_delay < player->push_delay_value &&
14190         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14191         element != EL_SPRING && element != EL_BALLOON)
14192     {
14193       // make sure that there is no move delay before next try to push
14194       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14195         player->move_delay = 0;
14196
14197       return MP_NO_ACTION;
14198     }
14199
14200     if (IS_CUSTOM_ELEMENT(element) &&
14201         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14202     {
14203       if (!DigFieldByCE(nextx, nexty, element))
14204         return MP_NO_ACTION;
14205     }
14206
14207     if (IS_SB_ELEMENT(element))
14208     {
14209       boolean sokoban_task_solved = FALSE;
14210
14211       if (element == EL_SOKOBAN_FIELD_FULL)
14212       {
14213         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14214
14215         IncrementSokobanFieldsNeeded();
14216         IncrementSokobanObjectsNeeded();
14217       }
14218
14219       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14220       {
14221         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14222
14223         DecrementSokobanFieldsNeeded();
14224         DecrementSokobanObjectsNeeded();
14225
14226         // sokoban object was pushed from empty field to sokoban field
14227         if (Back[x][y] == EL_EMPTY)
14228           sokoban_task_solved = TRUE;
14229       }
14230
14231       Tile[x][y] = EL_SOKOBAN_OBJECT;
14232
14233       if (Back[x][y] == Back[nextx][nexty])
14234         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14235       else if (Back[x][y] != 0)
14236         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14237                                     ACTION_EMPTYING);
14238       else
14239         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14240                                     ACTION_FILLING);
14241
14242       if (sokoban_task_solved &&
14243           game.sokoban_fields_still_needed == 0 &&
14244           game.sokoban_objects_still_needed == 0 &&
14245           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14246       {
14247         game.players_still_needed = 0;
14248
14249         LevelSolved();
14250
14251         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14252       }
14253     }
14254     else
14255       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14256
14257     InitMovingField(x, y, move_direction);
14258     GfxAction[x][y] = ACTION_PUSHING;
14259
14260     if (mode == DF_SNAP)
14261       ContinueMoving(x, y);
14262     else
14263       MovPos[x][y] = (dx != 0 ? dx : dy);
14264
14265     Pushed[x][y] = TRUE;
14266     Pushed[nextx][nexty] = TRUE;
14267
14268     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14269       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14270     else
14271       player->push_delay_value = -1;    // get new value later
14272
14273     // check for element change _after_ element has been pushed
14274     if (game.use_change_when_pushing_bug)
14275     {
14276       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14277                                  player->index_bit, dig_side);
14278       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14279                                           player->index_bit, dig_side);
14280     }
14281   }
14282   else if (IS_SWITCHABLE(element))
14283   {
14284     if (PLAYER_SWITCHING(player, x, y))
14285     {
14286       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14287                                           player->index_bit, dig_side);
14288
14289       return MP_ACTION;
14290     }
14291
14292     player->is_switching = TRUE;
14293     player->switch_x = x;
14294     player->switch_y = y;
14295
14296     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14297
14298     if (element == EL_ROBOT_WHEEL)
14299     {
14300       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14301
14302       game.robot_wheel_x = x;
14303       game.robot_wheel_y = y;
14304       game.robot_wheel_active = TRUE;
14305
14306       TEST_DrawLevelField(x, y);
14307     }
14308     else if (element == EL_SP_TERMINAL)
14309     {
14310       int xx, yy;
14311
14312       SCAN_PLAYFIELD(xx, yy)
14313       {
14314         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14315         {
14316           Bang(xx, yy);
14317         }
14318         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14319         {
14320           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14321
14322           ResetGfxAnimation(xx, yy);
14323           TEST_DrawLevelField(xx, yy);
14324         }
14325       }
14326     }
14327     else if (IS_BELT_SWITCH(element))
14328     {
14329       ToggleBeltSwitch(x, y);
14330     }
14331     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14332              element == EL_SWITCHGATE_SWITCH_DOWN ||
14333              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14334              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14335     {
14336       ToggleSwitchgateSwitch(x, y);
14337     }
14338     else if (element == EL_LIGHT_SWITCH ||
14339              element == EL_LIGHT_SWITCH_ACTIVE)
14340     {
14341       ToggleLightSwitch(x, y);
14342     }
14343     else if (element == EL_TIMEGATE_SWITCH ||
14344              element == EL_DC_TIMEGATE_SWITCH)
14345     {
14346       ActivateTimegateSwitch(x, y);
14347     }
14348     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14349              element == EL_BALLOON_SWITCH_RIGHT ||
14350              element == EL_BALLOON_SWITCH_UP    ||
14351              element == EL_BALLOON_SWITCH_DOWN  ||
14352              element == EL_BALLOON_SWITCH_NONE  ||
14353              element == EL_BALLOON_SWITCH_ANY)
14354     {
14355       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14356                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14357                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14358                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14359                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14360                              move_direction);
14361     }
14362     else if (element == EL_LAMP)
14363     {
14364       Tile[x][y] = EL_LAMP_ACTIVE;
14365       game.lights_still_needed--;
14366
14367       ResetGfxAnimation(x, y);
14368       TEST_DrawLevelField(x, y);
14369     }
14370     else if (element == EL_TIME_ORB_FULL)
14371     {
14372       Tile[x][y] = EL_TIME_ORB_EMPTY;
14373
14374       if (level.time > 0 || level.use_time_orb_bug)
14375       {
14376         TimeLeft += level.time_orb_time;
14377         game.no_time_limit = FALSE;
14378
14379         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14380
14381         DisplayGameControlValues();
14382       }
14383
14384       ResetGfxAnimation(x, y);
14385       TEST_DrawLevelField(x, y);
14386     }
14387     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14388              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14389     {
14390       int xx, yy;
14391
14392       game.ball_active = !game.ball_active;
14393
14394       SCAN_PLAYFIELD(xx, yy)
14395       {
14396         int e = Tile[xx][yy];
14397
14398         if (game.ball_active)
14399         {
14400           if (e == EL_EMC_MAGIC_BALL)
14401             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14402           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14403             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14404         }
14405         else
14406         {
14407           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14408             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14409           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14410             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14411         }
14412       }
14413     }
14414
14415     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14416                                         player->index_bit, dig_side);
14417
14418     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14419                                         player->index_bit, dig_side);
14420
14421     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14422                                         player->index_bit, dig_side);
14423
14424     return MP_ACTION;
14425   }
14426   else
14427   {
14428     if (!PLAYER_SWITCHING(player, x, y))
14429     {
14430       player->is_switching = TRUE;
14431       player->switch_x = x;
14432       player->switch_y = y;
14433
14434       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14435                                  player->index_bit, dig_side);
14436       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14437                                           player->index_bit, dig_side);
14438
14439       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14440                                  player->index_bit, dig_side);
14441       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14442                                           player->index_bit, dig_side);
14443     }
14444
14445     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14446                                player->index_bit, dig_side);
14447     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14448                                         player->index_bit, dig_side);
14449
14450     return MP_NO_ACTION;
14451   }
14452
14453   player->push_delay = -1;
14454
14455   if (is_player)                // function can also be called by EL_PENGUIN
14456   {
14457     if (Tile[x][y] != element)          // really digged/collected something
14458     {
14459       player->is_collecting = !player->is_digging;
14460       player->is_active = TRUE;
14461     }
14462   }
14463
14464   return MP_MOVING;
14465 }
14466
14467 static boolean DigFieldByCE(int x, int y, int digging_element)
14468 {
14469   int element = Tile[x][y];
14470
14471   if (!IS_FREE(x, y))
14472   {
14473     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14474                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14475                   ACTION_BREAKING);
14476
14477     // no element can dig solid indestructible elements
14478     if (IS_INDESTRUCTIBLE(element) &&
14479         !IS_DIGGABLE(element) &&
14480         !IS_COLLECTIBLE(element))
14481       return FALSE;
14482
14483     if (AmoebaNr[x][y] &&
14484         (element == EL_AMOEBA_FULL ||
14485          element == EL_BD_AMOEBA ||
14486          element == EL_AMOEBA_GROWING))
14487     {
14488       AmoebaCnt[AmoebaNr[x][y]]--;
14489       AmoebaCnt2[AmoebaNr[x][y]]--;
14490     }
14491
14492     if (IS_MOVING(x, y))
14493       RemoveMovingField(x, y);
14494     else
14495     {
14496       RemoveField(x, y);
14497       TEST_DrawLevelField(x, y);
14498     }
14499
14500     // if digged element was about to explode, prevent the explosion
14501     ExplodeField[x][y] = EX_TYPE_NONE;
14502
14503     PlayLevelSoundAction(x, y, action);
14504   }
14505
14506   Store[x][y] = EL_EMPTY;
14507
14508   // this makes it possible to leave the removed element again
14509   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14510     Store[x][y] = element;
14511
14512   return TRUE;
14513 }
14514
14515 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14516 {
14517   int jx = player->jx, jy = player->jy;
14518   int x = jx + dx, y = jy + dy;
14519   int snap_direction = (dx == -1 ? MV_LEFT  :
14520                         dx == +1 ? MV_RIGHT :
14521                         dy == -1 ? MV_UP    :
14522                         dy == +1 ? MV_DOWN  : MV_NONE);
14523   boolean can_continue_snapping = (level.continuous_snapping &&
14524                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14525
14526   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14527     return FALSE;
14528
14529   if (!player->active || !IN_LEV_FIELD(x, y))
14530     return FALSE;
14531
14532   if (dx && dy)
14533     return FALSE;
14534
14535   if (!dx && !dy)
14536   {
14537     if (player->MovPos == 0)
14538       player->is_pushing = FALSE;
14539
14540     player->is_snapping = FALSE;
14541
14542     if (player->MovPos == 0)
14543     {
14544       player->is_moving = FALSE;
14545       player->is_digging = FALSE;
14546       player->is_collecting = FALSE;
14547     }
14548
14549     return FALSE;
14550   }
14551
14552   // prevent snapping with already pressed snap key when not allowed
14553   if (player->is_snapping && !can_continue_snapping)
14554     return FALSE;
14555
14556   player->MovDir = snap_direction;
14557
14558   if (player->MovPos == 0)
14559   {
14560     player->is_moving = FALSE;
14561     player->is_digging = FALSE;
14562     player->is_collecting = FALSE;
14563   }
14564
14565   player->is_dropping = FALSE;
14566   player->is_dropping_pressed = FALSE;
14567   player->drop_pressed_delay = 0;
14568
14569   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14570     return FALSE;
14571
14572   player->is_snapping = TRUE;
14573   player->is_active = TRUE;
14574
14575   if (player->MovPos == 0)
14576   {
14577     player->is_moving = FALSE;
14578     player->is_digging = FALSE;
14579     player->is_collecting = FALSE;
14580   }
14581
14582   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14583     TEST_DrawLevelField(player->last_jx, player->last_jy);
14584
14585   TEST_DrawLevelField(x, y);
14586
14587   return TRUE;
14588 }
14589
14590 static boolean DropElement(struct PlayerInfo *player)
14591 {
14592   int old_element, new_element;
14593   int dropx = player->jx, dropy = player->jy;
14594   int drop_direction = player->MovDir;
14595   int drop_side = drop_direction;
14596   int drop_element = get_next_dropped_element(player);
14597
14598   /* do not drop an element on top of another element; when holding drop key
14599      pressed without moving, dropped element must move away before the next
14600      element can be dropped (this is especially important if the next element
14601      is dynamite, which can be placed on background for historical reasons) */
14602   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14603     return MP_ACTION;
14604
14605   if (IS_THROWABLE(drop_element))
14606   {
14607     dropx += GET_DX_FROM_DIR(drop_direction);
14608     dropy += GET_DY_FROM_DIR(drop_direction);
14609
14610     if (!IN_LEV_FIELD(dropx, dropy))
14611       return FALSE;
14612   }
14613
14614   old_element = Tile[dropx][dropy];     // old element at dropping position
14615   new_element = drop_element;           // default: no change when dropping
14616
14617   // check if player is active, not moving and ready to drop
14618   if (!player->active || player->MovPos || player->drop_delay > 0)
14619     return FALSE;
14620
14621   // check if player has anything that can be dropped
14622   if (new_element == EL_UNDEFINED)
14623     return FALSE;
14624
14625   // only set if player has anything that can be dropped
14626   player->is_dropping_pressed = TRUE;
14627
14628   // check if drop key was pressed long enough for EM style dynamite
14629   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14630     return FALSE;
14631
14632   // check if anything can be dropped at the current position
14633   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14634     return FALSE;
14635
14636   // collected custom elements can only be dropped on empty fields
14637   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14638     return FALSE;
14639
14640   if (old_element != EL_EMPTY)
14641     Back[dropx][dropy] = old_element;   // store old element on this field
14642
14643   ResetGfxAnimation(dropx, dropy);
14644   ResetRandomAnimationValue(dropx, dropy);
14645
14646   if (player->inventory_size > 0 ||
14647       player->inventory_infinite_element != EL_UNDEFINED)
14648   {
14649     if (player->inventory_size > 0)
14650     {
14651       player->inventory_size--;
14652
14653       DrawGameDoorValues();
14654
14655       if (new_element == EL_DYNAMITE)
14656         new_element = EL_DYNAMITE_ACTIVE;
14657       else if (new_element == EL_EM_DYNAMITE)
14658         new_element = EL_EM_DYNAMITE_ACTIVE;
14659       else if (new_element == EL_SP_DISK_RED)
14660         new_element = EL_SP_DISK_RED_ACTIVE;
14661     }
14662
14663     Tile[dropx][dropy] = new_element;
14664
14665     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14666       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14667                           el2img(Tile[dropx][dropy]), 0);
14668
14669     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14670
14671     // needed if previous element just changed to "empty" in the last frame
14672     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14673
14674     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14675                                player->index_bit, drop_side);
14676     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14677                                         CE_PLAYER_DROPS_X,
14678                                         player->index_bit, drop_side);
14679
14680     TestIfElementTouchesCustomElement(dropx, dropy);
14681   }
14682   else          // player is dropping a dyna bomb
14683   {
14684     player->dynabombs_left--;
14685
14686     Tile[dropx][dropy] = new_element;
14687
14688     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14689       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14690                           el2img(Tile[dropx][dropy]), 0);
14691
14692     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14693   }
14694
14695   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14696     InitField_WithBug1(dropx, dropy, FALSE);
14697
14698   new_element = Tile[dropx][dropy];     // element might have changed
14699
14700   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14701       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14702   {
14703     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14704       MovDir[dropx][dropy] = drop_direction;
14705
14706     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14707
14708     // do not cause impact style collision by dropping elements that can fall
14709     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14710   }
14711
14712   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14713   player->is_dropping = TRUE;
14714
14715   player->drop_pressed_delay = 0;
14716   player->is_dropping_pressed = FALSE;
14717
14718   player->drop_x = dropx;
14719   player->drop_y = dropy;
14720
14721   return TRUE;
14722 }
14723
14724 // ----------------------------------------------------------------------------
14725 // game sound playing functions
14726 // ----------------------------------------------------------------------------
14727
14728 static int *loop_sound_frame = NULL;
14729 static int *loop_sound_volume = NULL;
14730
14731 void InitPlayLevelSound(void)
14732 {
14733   int num_sounds = getSoundListSize();
14734
14735   checked_free(loop_sound_frame);
14736   checked_free(loop_sound_volume);
14737
14738   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14739   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14740 }
14741
14742 static void PlayLevelSound(int x, int y, int nr)
14743 {
14744   int sx = SCREENX(x), sy = SCREENY(y);
14745   int volume, stereo_position;
14746   int max_distance = 8;
14747   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14748
14749   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14750       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14751     return;
14752
14753   if (!IN_LEV_FIELD(x, y) ||
14754       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14755       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14756     return;
14757
14758   volume = SOUND_MAX_VOLUME;
14759
14760   if (!IN_SCR_FIELD(sx, sy))
14761   {
14762     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14763     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14764
14765     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14766   }
14767
14768   stereo_position = (SOUND_MAX_LEFT +
14769                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14770                      (SCR_FIELDX + 2 * max_distance));
14771
14772   if (IS_LOOP_SOUND(nr))
14773   {
14774     /* This assures that quieter loop sounds do not overwrite louder ones,
14775        while restarting sound volume comparison with each new game frame. */
14776
14777     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14778       return;
14779
14780     loop_sound_volume[nr] = volume;
14781     loop_sound_frame[nr] = FrameCounter;
14782   }
14783
14784   PlaySoundExt(nr, volume, stereo_position, type);
14785 }
14786
14787 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14788 {
14789   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14790                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14791                  y < LEVELY(BY1) ? LEVELY(BY1) :
14792                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14793                  sound_action);
14794 }
14795
14796 static void PlayLevelSoundAction(int x, int y, int action)
14797 {
14798   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14799 }
14800
14801 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14802 {
14803   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14804
14805   if (sound_effect != SND_UNDEFINED)
14806     PlayLevelSound(x, y, sound_effect);
14807 }
14808
14809 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14810                                               int action)
14811 {
14812   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14813
14814   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14815     PlayLevelSound(x, y, sound_effect);
14816 }
14817
14818 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14819 {
14820   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14821
14822   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14823     PlayLevelSound(x, y, sound_effect);
14824 }
14825
14826 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14827 {
14828   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14829
14830   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14831     StopSound(sound_effect);
14832 }
14833
14834 static int getLevelMusicNr(void)
14835 {
14836   if (levelset.music[level_nr] != MUS_UNDEFINED)
14837     return levelset.music[level_nr];            // from config file
14838   else
14839     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14840 }
14841
14842 static void FadeLevelSounds(void)
14843 {
14844   FadeSounds();
14845 }
14846
14847 static void FadeLevelMusic(void)
14848 {
14849   int music_nr = getLevelMusicNr();
14850   char *curr_music = getCurrentlyPlayingMusicFilename();
14851   char *next_music = getMusicInfoEntryFilename(music_nr);
14852
14853   if (!strEqual(curr_music, next_music))
14854     FadeMusic();
14855 }
14856
14857 void FadeLevelSoundsAndMusic(void)
14858 {
14859   FadeLevelSounds();
14860   FadeLevelMusic();
14861 }
14862
14863 static void PlayLevelMusic(void)
14864 {
14865   int music_nr = getLevelMusicNr();
14866   char *curr_music = getCurrentlyPlayingMusicFilename();
14867   char *next_music = getMusicInfoEntryFilename(music_nr);
14868
14869   if (!strEqual(curr_music, next_music))
14870     PlayMusicLoop(music_nr);
14871 }
14872
14873 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14874 {
14875   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14876   int offset = 0;
14877   int x = xx - offset;
14878   int y = yy - offset;
14879
14880   switch (sample)
14881   {
14882     case SOUND_blank:
14883       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14884       break;
14885
14886     case SOUND_roll:
14887       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14888       break;
14889
14890     case SOUND_stone:
14891       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14892       break;
14893
14894     case SOUND_nut:
14895       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14896       break;
14897
14898     case SOUND_crack:
14899       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14900       break;
14901
14902     case SOUND_bug:
14903       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14904       break;
14905
14906     case SOUND_tank:
14907       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14908       break;
14909
14910     case SOUND_android_clone:
14911       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14912       break;
14913
14914     case SOUND_android_move:
14915       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14916       break;
14917
14918     case SOUND_spring:
14919       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14920       break;
14921
14922     case SOUND_slurp:
14923       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14924       break;
14925
14926     case SOUND_eater:
14927       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14928       break;
14929
14930     case SOUND_eater_eat:
14931       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14932       break;
14933
14934     case SOUND_alien:
14935       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14936       break;
14937
14938     case SOUND_collect:
14939       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14940       break;
14941
14942     case SOUND_diamond:
14943       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14944       break;
14945
14946     case SOUND_squash:
14947       // !!! CHECK THIS !!!
14948 #if 1
14949       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14950 #else
14951       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14952 #endif
14953       break;
14954
14955     case SOUND_wonderfall:
14956       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14957       break;
14958
14959     case SOUND_drip:
14960       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14961       break;
14962
14963     case SOUND_push:
14964       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14965       break;
14966
14967     case SOUND_dirt:
14968       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14969       break;
14970
14971     case SOUND_acid:
14972       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14973       break;
14974
14975     case SOUND_ball:
14976       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14977       break;
14978
14979     case SOUND_slide:
14980       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14981       break;
14982
14983     case SOUND_wonder:
14984       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14985       break;
14986
14987     case SOUND_door:
14988       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14989       break;
14990
14991     case SOUND_exit_open:
14992       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14993       break;
14994
14995     case SOUND_exit_leave:
14996       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14997       break;
14998
14999     case SOUND_dynamite:
15000       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15001       break;
15002
15003     case SOUND_tick:
15004       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15005       break;
15006
15007     case SOUND_press:
15008       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15009       break;
15010
15011     case SOUND_wheel:
15012       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15013       break;
15014
15015     case SOUND_boom:
15016       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15017       break;
15018
15019     case SOUND_die:
15020       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15021       break;
15022
15023     case SOUND_time:
15024       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15025       break;
15026
15027     default:
15028       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15029       break;
15030   }
15031 }
15032
15033 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15034 {
15035   int element = map_element_SP_to_RND(element_sp);
15036   int action = map_action_SP_to_RND(action_sp);
15037   int offset = (setup.sp_show_border_elements ? 0 : 1);
15038   int x = xx - offset;
15039   int y = yy - offset;
15040
15041   PlayLevelSoundElementAction(x, y, element, action);
15042 }
15043
15044 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15045 {
15046   int element = map_element_MM_to_RND(element_mm);
15047   int action = map_action_MM_to_RND(action_mm);
15048   int offset = 0;
15049   int x = xx - offset;
15050   int y = yy - offset;
15051
15052   if (!IS_MM_ELEMENT(element))
15053     element = EL_MM_DEFAULT;
15054
15055   PlayLevelSoundElementAction(x, y, element, action);
15056 }
15057
15058 void PlaySound_MM(int sound_mm)
15059 {
15060   int sound = map_sound_MM_to_RND(sound_mm);
15061
15062   if (sound == SND_UNDEFINED)
15063     return;
15064
15065   PlaySound(sound);
15066 }
15067
15068 void PlaySoundLoop_MM(int sound_mm)
15069 {
15070   int sound = map_sound_MM_to_RND(sound_mm);
15071
15072   if (sound == SND_UNDEFINED)
15073     return;
15074
15075   PlaySoundLoop(sound);
15076 }
15077
15078 void StopSound_MM(int sound_mm)
15079 {
15080   int sound = map_sound_MM_to_RND(sound_mm);
15081
15082   if (sound == SND_UNDEFINED)
15083     return;
15084
15085   StopSound(sound);
15086 }
15087
15088 void RaiseScore(int value)
15089 {
15090   game.score += value;
15091
15092   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15093
15094   DisplayGameControlValues();
15095 }
15096
15097 void RaiseScoreElement(int element)
15098 {
15099   switch (element)
15100   {
15101     case EL_EMERALD:
15102     case EL_BD_DIAMOND:
15103     case EL_EMERALD_YELLOW:
15104     case EL_EMERALD_RED:
15105     case EL_EMERALD_PURPLE:
15106     case EL_SP_INFOTRON:
15107       RaiseScore(level.score[SC_EMERALD]);
15108       break;
15109     case EL_DIAMOND:
15110       RaiseScore(level.score[SC_DIAMOND]);
15111       break;
15112     case EL_CRYSTAL:
15113       RaiseScore(level.score[SC_CRYSTAL]);
15114       break;
15115     case EL_PEARL:
15116       RaiseScore(level.score[SC_PEARL]);
15117       break;
15118     case EL_BUG:
15119     case EL_BD_BUTTERFLY:
15120     case EL_SP_ELECTRON:
15121       RaiseScore(level.score[SC_BUG]);
15122       break;
15123     case EL_SPACESHIP:
15124     case EL_BD_FIREFLY:
15125     case EL_SP_SNIKSNAK:
15126       RaiseScore(level.score[SC_SPACESHIP]);
15127       break;
15128     case EL_YAMYAM:
15129     case EL_DARK_YAMYAM:
15130       RaiseScore(level.score[SC_YAMYAM]);
15131       break;
15132     case EL_ROBOT:
15133       RaiseScore(level.score[SC_ROBOT]);
15134       break;
15135     case EL_PACMAN:
15136       RaiseScore(level.score[SC_PACMAN]);
15137       break;
15138     case EL_NUT:
15139       RaiseScore(level.score[SC_NUT]);
15140       break;
15141     case EL_DYNAMITE:
15142     case EL_EM_DYNAMITE:
15143     case EL_SP_DISK_RED:
15144     case EL_DYNABOMB_INCREASE_NUMBER:
15145     case EL_DYNABOMB_INCREASE_SIZE:
15146     case EL_DYNABOMB_INCREASE_POWER:
15147       RaiseScore(level.score[SC_DYNAMITE]);
15148       break;
15149     case EL_SHIELD_NORMAL:
15150     case EL_SHIELD_DEADLY:
15151       RaiseScore(level.score[SC_SHIELD]);
15152       break;
15153     case EL_EXTRA_TIME:
15154       RaiseScore(level.extra_time_score);
15155       break;
15156     case EL_KEY_1:
15157     case EL_KEY_2:
15158     case EL_KEY_3:
15159     case EL_KEY_4:
15160     case EL_EM_KEY_1:
15161     case EL_EM_KEY_2:
15162     case EL_EM_KEY_3:
15163     case EL_EM_KEY_4:
15164     case EL_EMC_KEY_5:
15165     case EL_EMC_KEY_6:
15166     case EL_EMC_KEY_7:
15167     case EL_EMC_KEY_8:
15168     case EL_DC_KEY_WHITE:
15169       RaiseScore(level.score[SC_KEY]);
15170       break;
15171     default:
15172       RaiseScore(element_info[element].collect_score);
15173       break;
15174   }
15175 }
15176
15177 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15178 {
15179   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15180   {
15181     // closing door required in case of envelope style request dialogs
15182     if (!skip_request)
15183     {
15184       // prevent short reactivation of overlay buttons while closing door
15185       SetOverlayActive(FALSE);
15186
15187       CloseDoor(DOOR_CLOSE_1);
15188     }
15189
15190     if (network.enabled)
15191       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15192     else
15193     {
15194       if (quick_quit)
15195         FadeSkipNextFadeIn();
15196
15197       SetGameStatus(GAME_MODE_MAIN);
15198
15199       DrawMainMenu();
15200     }
15201   }
15202   else          // continue playing the game
15203   {
15204     if (tape.playing && tape.deactivate_display)
15205       TapeDeactivateDisplayOff(TRUE);
15206
15207     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15208
15209     if (tape.playing && tape.deactivate_display)
15210       TapeDeactivateDisplayOn();
15211   }
15212 }
15213
15214 void RequestQuitGame(boolean ask_if_really_quit)
15215 {
15216   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15217   boolean skip_request = game.all_players_gone || quick_quit;
15218
15219   RequestQuitGameExt(skip_request, quick_quit,
15220                      "Do you really want to quit the game?");
15221 }
15222
15223 void RequestRestartGame(char *message)
15224 {
15225   game.restart_game_message = NULL;
15226
15227   boolean has_started_game = hasStartedNetworkGame();
15228   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15229
15230   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15231   {
15232     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15233   }
15234   else
15235   {
15236     SetGameStatus(GAME_MODE_MAIN);
15237
15238     DrawMainMenu();
15239   }
15240 }
15241
15242 void CheckGameOver(void)
15243 {
15244   static boolean last_game_over = FALSE;
15245   static int game_over_delay = 0;
15246   int game_over_delay_value = 50;
15247   boolean game_over = checkGameFailed();
15248
15249   // do not handle game over if request dialog is already active
15250   if (game.request_active)
15251     return;
15252
15253   // do not ask to play again if game was never actually played
15254   if (!game.GamePlayed)
15255     return;
15256
15257   if (!game_over)
15258   {
15259     last_game_over = FALSE;
15260     game_over_delay = game_over_delay_value;
15261
15262     return;
15263   }
15264
15265   if (game_over_delay > 0)
15266   {
15267     game_over_delay--;
15268
15269     return;
15270   }
15271
15272   if (last_game_over != game_over)
15273     game.restart_game_message = (hasStartedNetworkGame() ?
15274                                  "Game over! Play it again?" :
15275                                  "Game over!");
15276
15277   last_game_over = game_over;
15278 }
15279
15280 boolean checkGameSolved(void)
15281 {
15282   // set for all game engines if level was solved
15283   return game.LevelSolved_GameEnd;
15284 }
15285
15286 boolean checkGameFailed(void)
15287 {
15288   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15289     return (game_em.game_over && !game_em.level_solved);
15290   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15291     return (game_sp.game_over && !game_sp.level_solved);
15292   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15293     return (game_mm.game_over && !game_mm.level_solved);
15294   else                          // GAME_ENGINE_TYPE_RND
15295     return (game.GameOver && !game.LevelSolved);
15296 }
15297
15298 boolean checkGameEnded(void)
15299 {
15300   return (checkGameSolved() || checkGameFailed());
15301 }
15302
15303
15304 // ----------------------------------------------------------------------------
15305 // random generator functions
15306 // ----------------------------------------------------------------------------
15307
15308 unsigned int InitEngineRandom_RND(int seed)
15309 {
15310   game.num_random_calls = 0;
15311
15312   return InitEngineRandom(seed);
15313 }
15314
15315 unsigned int RND(int max)
15316 {
15317   if (max > 0)
15318   {
15319     game.num_random_calls++;
15320
15321     return GetEngineRandom(max);
15322   }
15323
15324   return 0;
15325 }
15326
15327
15328 // ----------------------------------------------------------------------------
15329 // game engine snapshot handling functions
15330 // ----------------------------------------------------------------------------
15331
15332 struct EngineSnapshotInfo
15333 {
15334   // runtime values for custom element collect score
15335   int collect_score[NUM_CUSTOM_ELEMENTS];
15336
15337   // runtime values for group element choice position
15338   int choice_pos[NUM_GROUP_ELEMENTS];
15339
15340   // runtime values for belt position animations
15341   int belt_graphic[4][NUM_BELT_PARTS];
15342   int belt_anim_mode[4][NUM_BELT_PARTS];
15343 };
15344
15345 static struct EngineSnapshotInfo engine_snapshot_rnd;
15346 static char *snapshot_level_identifier = NULL;
15347 static int snapshot_level_nr = -1;
15348
15349 static void SaveEngineSnapshotValues_RND(void)
15350 {
15351   static int belt_base_active_element[4] =
15352   {
15353     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15354     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15355     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15356     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15357   };
15358   int i, j;
15359
15360   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15361   {
15362     int element = EL_CUSTOM_START + i;
15363
15364     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15365   }
15366
15367   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15368   {
15369     int element = EL_GROUP_START + i;
15370
15371     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15372   }
15373
15374   for (i = 0; i < 4; i++)
15375   {
15376     for (j = 0; j < NUM_BELT_PARTS; j++)
15377     {
15378       int element = belt_base_active_element[i] + j;
15379       int graphic = el2img(element);
15380       int anim_mode = graphic_info[graphic].anim_mode;
15381
15382       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15383       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15384     }
15385   }
15386 }
15387
15388 static void LoadEngineSnapshotValues_RND(void)
15389 {
15390   unsigned int num_random_calls = game.num_random_calls;
15391   int i, j;
15392
15393   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15394   {
15395     int element = EL_CUSTOM_START + i;
15396
15397     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15398   }
15399
15400   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15401   {
15402     int element = EL_GROUP_START + i;
15403
15404     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15405   }
15406
15407   for (i = 0; i < 4; i++)
15408   {
15409     for (j = 0; j < NUM_BELT_PARTS; j++)
15410     {
15411       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15412       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15413
15414       graphic_info[graphic].anim_mode = anim_mode;
15415     }
15416   }
15417
15418   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15419   {
15420     InitRND(tape.random_seed);
15421     for (i = 0; i < num_random_calls; i++)
15422       RND(1);
15423   }
15424
15425   if (game.num_random_calls != num_random_calls)
15426   {
15427     Error(ERR_INFO, "number of random calls out of sync");
15428     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15429     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15430     Error(ERR_EXIT, "this should not happen -- please debug");
15431   }
15432 }
15433
15434 void FreeEngineSnapshotSingle(void)
15435 {
15436   FreeSnapshotSingle();
15437
15438   setString(&snapshot_level_identifier, NULL);
15439   snapshot_level_nr = -1;
15440 }
15441
15442 void FreeEngineSnapshotList(void)
15443 {
15444   FreeSnapshotList();
15445 }
15446
15447 static ListNode *SaveEngineSnapshotBuffers(void)
15448 {
15449   ListNode *buffers = NULL;
15450
15451   // copy some special values to a structure better suited for the snapshot
15452
15453   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15454     SaveEngineSnapshotValues_RND();
15455   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15456     SaveEngineSnapshotValues_EM();
15457   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15458     SaveEngineSnapshotValues_SP(&buffers);
15459   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15460     SaveEngineSnapshotValues_MM(&buffers);
15461
15462   // save values stored in special snapshot structure
15463
15464   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15465     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15466   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15467     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15468   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15469     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15470   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15471     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15472
15473   // save further RND engine values
15474
15475   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15478
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15480   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15481   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15484
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15486   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15488
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15490
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15493
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15510   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15512
15513   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15514   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15515
15516   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15517   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15518   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15519
15520   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15521   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15522
15523   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15524   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15525   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15526   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15527   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15528
15529   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15530   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15531
15532 #if 0
15533   ListNode *node = engine_snapshot_list_rnd;
15534   int num_bytes = 0;
15535
15536   while (node != NULL)
15537   {
15538     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15539
15540     node = node->next;
15541   }
15542
15543   Debug("game:playing:SaveEngineSnapshotBuffers",
15544         "size of engine snapshot: %d bytes", num_bytes);
15545 #endif
15546
15547   return buffers;
15548 }
15549
15550 void SaveEngineSnapshotSingle(void)
15551 {
15552   ListNode *buffers = SaveEngineSnapshotBuffers();
15553
15554   // finally save all snapshot buffers to single snapshot
15555   SaveSnapshotSingle(buffers);
15556
15557   // save level identification information
15558   setString(&snapshot_level_identifier, leveldir_current->identifier);
15559   snapshot_level_nr = level_nr;
15560 }
15561
15562 boolean CheckSaveEngineSnapshotToList(void)
15563 {
15564   boolean save_snapshot =
15565     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15566      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15567       game.snapshot.changed_action) ||
15568      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15569       game.snapshot.collected_item));
15570
15571   game.snapshot.changed_action = FALSE;
15572   game.snapshot.collected_item = FALSE;
15573   game.snapshot.save_snapshot = save_snapshot;
15574
15575   return save_snapshot;
15576 }
15577
15578 void SaveEngineSnapshotToList(void)
15579 {
15580   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15581       tape.quick_resume)
15582     return;
15583
15584   ListNode *buffers = SaveEngineSnapshotBuffers();
15585
15586   // finally save all snapshot buffers to snapshot list
15587   SaveSnapshotToList(buffers);
15588 }
15589
15590 void SaveEngineSnapshotToListInitial(void)
15591 {
15592   FreeEngineSnapshotList();
15593
15594   SaveEngineSnapshotToList();
15595 }
15596
15597 static void LoadEngineSnapshotValues(void)
15598 {
15599   // restore special values from snapshot structure
15600
15601   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15602     LoadEngineSnapshotValues_RND();
15603   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15604     LoadEngineSnapshotValues_EM();
15605   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15606     LoadEngineSnapshotValues_SP();
15607   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15608     LoadEngineSnapshotValues_MM();
15609 }
15610
15611 void LoadEngineSnapshotSingle(void)
15612 {
15613   LoadSnapshotSingle();
15614
15615   LoadEngineSnapshotValues();
15616 }
15617
15618 static void LoadEngineSnapshot_Undo(int steps)
15619 {
15620   LoadSnapshotFromList_Older(steps);
15621
15622   LoadEngineSnapshotValues();
15623 }
15624
15625 static void LoadEngineSnapshot_Redo(int steps)
15626 {
15627   LoadSnapshotFromList_Newer(steps);
15628
15629   LoadEngineSnapshotValues();
15630 }
15631
15632 boolean CheckEngineSnapshotSingle(void)
15633 {
15634   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15635           snapshot_level_nr == level_nr);
15636 }
15637
15638 boolean CheckEngineSnapshotList(void)
15639 {
15640   return CheckSnapshotList();
15641 }
15642
15643
15644 // ---------- new game button stuff -------------------------------------------
15645
15646 static struct
15647 {
15648   int graphic;
15649   struct XY *pos;
15650   int gadget_id;
15651   boolean *setup_value;
15652   boolean allowed_on_tape;
15653   boolean is_touch_button;
15654   char *infotext;
15655 } gamebutton_info[NUM_GAME_BUTTONS] =
15656 {
15657   {
15658     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15659     GAME_CTRL_ID_STOP,                          NULL,
15660     TRUE, FALSE,                                "stop game"
15661   },
15662   {
15663     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15664     GAME_CTRL_ID_PAUSE,                         NULL,
15665     TRUE, FALSE,                                "pause game"
15666   },
15667   {
15668     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15669     GAME_CTRL_ID_PLAY,                          NULL,
15670     TRUE, FALSE,                                "play game"
15671   },
15672   {
15673     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15674     GAME_CTRL_ID_UNDO,                          NULL,
15675     TRUE, FALSE,                                "undo step"
15676   },
15677   {
15678     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15679     GAME_CTRL_ID_REDO,                          NULL,
15680     TRUE, FALSE,                                "redo step"
15681   },
15682   {
15683     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15684     GAME_CTRL_ID_SAVE,                          NULL,
15685     TRUE, FALSE,                                "save game"
15686   },
15687   {
15688     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15689     GAME_CTRL_ID_PAUSE2,                        NULL,
15690     TRUE, FALSE,                                "pause game"
15691   },
15692   {
15693     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15694     GAME_CTRL_ID_LOAD,                          NULL,
15695     TRUE, FALSE,                                "load game"
15696   },
15697   {
15698     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15699     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15700     FALSE, FALSE,                               "stop game"
15701   },
15702   {
15703     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15704     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15705     FALSE, FALSE,                               "pause game"
15706   },
15707   {
15708     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15709     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15710     FALSE, FALSE,                               "play game"
15711   },
15712   {
15713     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15714     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15715     FALSE, TRUE,                                "stop game"
15716   },
15717   {
15718     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15719     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15720     FALSE, TRUE,                                "pause game"
15721   },
15722   {
15723     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15724     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15725     TRUE, FALSE,                                "background music on/off"
15726   },
15727   {
15728     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15729     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15730     TRUE, FALSE,                                "sound loops on/off"
15731   },
15732   {
15733     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15734     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15735     TRUE, FALSE,                                "normal sounds on/off"
15736   },
15737   {
15738     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15739     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15740     FALSE, FALSE,                               "background music on/off"
15741   },
15742   {
15743     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15744     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15745     FALSE, FALSE,                               "sound loops on/off"
15746   },
15747   {
15748     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15749     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15750     FALSE, FALSE,                               "normal sounds on/off"
15751   }
15752 };
15753
15754 void CreateGameButtons(void)
15755 {
15756   int i;
15757
15758   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15759   {
15760     int graphic = gamebutton_info[i].graphic;
15761     struct GraphicInfo *gfx = &graphic_info[graphic];
15762     struct XY *pos = gamebutton_info[i].pos;
15763     struct GadgetInfo *gi;
15764     int button_type;
15765     boolean checked;
15766     unsigned int event_mask;
15767     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15768     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15769     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15770     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15771     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15772     int gd_x   = gfx->src_x;
15773     int gd_y   = gfx->src_y;
15774     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15775     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15776     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15777     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15778     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15779     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15780     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15781     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15782     int id = i;
15783
15784     if (gfx->bitmap == NULL)
15785     {
15786       game_gadget[id] = NULL;
15787
15788       continue;
15789     }
15790
15791     if (id == GAME_CTRL_ID_STOP ||
15792         id == GAME_CTRL_ID_PANEL_STOP ||
15793         id == GAME_CTRL_ID_TOUCH_STOP ||
15794         id == GAME_CTRL_ID_PLAY ||
15795         id == GAME_CTRL_ID_PANEL_PLAY ||
15796         id == GAME_CTRL_ID_SAVE ||
15797         id == GAME_CTRL_ID_LOAD)
15798     {
15799       button_type = GD_TYPE_NORMAL_BUTTON;
15800       checked = FALSE;
15801       event_mask = GD_EVENT_RELEASED;
15802     }
15803     else if (id == GAME_CTRL_ID_UNDO ||
15804              id == GAME_CTRL_ID_REDO)
15805     {
15806       button_type = GD_TYPE_NORMAL_BUTTON;
15807       checked = FALSE;
15808       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15809     }
15810     else
15811     {
15812       button_type = GD_TYPE_CHECK_BUTTON;
15813       checked = (gamebutton_info[i].setup_value != NULL ?
15814                  *gamebutton_info[i].setup_value : FALSE);
15815       event_mask = GD_EVENT_PRESSED;
15816     }
15817
15818     gi = CreateGadget(GDI_CUSTOM_ID, id,
15819                       GDI_IMAGE_ID, graphic,
15820                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15821                       GDI_X, base_x + x,
15822                       GDI_Y, base_y + y,
15823                       GDI_WIDTH, gfx->width,
15824                       GDI_HEIGHT, gfx->height,
15825                       GDI_TYPE, button_type,
15826                       GDI_STATE, GD_BUTTON_UNPRESSED,
15827                       GDI_CHECKED, checked,
15828                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15829                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15830                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15831                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15832                       GDI_DIRECT_DRAW, FALSE,
15833                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15834                       GDI_EVENT_MASK, event_mask,
15835                       GDI_CALLBACK_ACTION, HandleGameButtons,
15836                       GDI_END);
15837
15838     if (gi == NULL)
15839       Fail("cannot create gadget");
15840
15841     game_gadget[id] = gi;
15842   }
15843 }
15844
15845 void FreeGameButtons(void)
15846 {
15847   int i;
15848
15849   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15850     FreeGadget(game_gadget[i]);
15851 }
15852
15853 static void UnmapGameButtonsAtSamePosition(int id)
15854 {
15855   int i;
15856
15857   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15858     if (i != id &&
15859         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15860         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15861       UnmapGadget(game_gadget[i]);
15862 }
15863
15864 static void UnmapGameButtonsAtSamePosition_All(void)
15865 {
15866   if (setup.show_snapshot_buttons)
15867   {
15868     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15869     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15870     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15871   }
15872   else
15873   {
15874     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15875     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15876     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15877
15878     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15879     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15880     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15881   }
15882 }
15883
15884 static void MapGameButtonsAtSamePosition(int id)
15885 {
15886   int i;
15887
15888   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15889     if (i != id &&
15890         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15891         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15892       MapGadget(game_gadget[i]);
15893
15894   UnmapGameButtonsAtSamePosition_All();
15895 }
15896
15897 void MapUndoRedoButtons(void)
15898 {
15899   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15900   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15901
15902   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15903   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15904 }
15905
15906 void UnmapUndoRedoButtons(void)
15907 {
15908   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15909   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15910
15911   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15912   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15913 }
15914
15915 void ModifyPauseButtons(void)
15916 {
15917   static int ids[] =
15918   {
15919     GAME_CTRL_ID_PAUSE,
15920     GAME_CTRL_ID_PAUSE2,
15921     GAME_CTRL_ID_PANEL_PAUSE,
15922     GAME_CTRL_ID_TOUCH_PAUSE,
15923     -1
15924   };
15925   int i;
15926
15927   for (i = 0; ids[i] > -1; i++)
15928     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15929 }
15930
15931 static void MapGameButtonsExt(boolean on_tape)
15932 {
15933   int i;
15934
15935   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15936     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15937         i != GAME_CTRL_ID_UNDO &&
15938         i != GAME_CTRL_ID_REDO)
15939       MapGadget(game_gadget[i]);
15940
15941   UnmapGameButtonsAtSamePosition_All();
15942
15943   RedrawGameButtons();
15944 }
15945
15946 static void UnmapGameButtonsExt(boolean on_tape)
15947 {
15948   int i;
15949
15950   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15951     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15952       UnmapGadget(game_gadget[i]);
15953 }
15954
15955 static void RedrawGameButtonsExt(boolean on_tape)
15956 {
15957   int i;
15958
15959   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15960     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15961       RedrawGadget(game_gadget[i]);
15962 }
15963
15964 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15965 {
15966   if (gi == NULL)
15967     return;
15968
15969   gi->checked = state;
15970 }
15971
15972 static void RedrawSoundButtonGadget(int id)
15973 {
15974   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15975              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15976              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15977              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15978              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15979              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15980              id);
15981
15982   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15983   RedrawGadget(game_gadget[id2]);
15984 }
15985
15986 void MapGameButtons(void)
15987 {
15988   MapGameButtonsExt(FALSE);
15989 }
15990
15991 void UnmapGameButtons(void)
15992 {
15993   UnmapGameButtonsExt(FALSE);
15994 }
15995
15996 void RedrawGameButtons(void)
15997 {
15998   RedrawGameButtonsExt(FALSE);
15999 }
16000
16001 void MapGameButtonsOnTape(void)
16002 {
16003   MapGameButtonsExt(TRUE);
16004 }
16005
16006 void UnmapGameButtonsOnTape(void)
16007 {
16008   UnmapGameButtonsExt(TRUE);
16009 }
16010
16011 void RedrawGameButtonsOnTape(void)
16012 {
16013   RedrawGameButtonsExt(TRUE);
16014 }
16015
16016 static void GameUndoRedoExt(void)
16017 {
16018   ClearPlayerAction();
16019
16020   tape.pausing = TRUE;
16021
16022   RedrawPlayfield();
16023   UpdateAndDisplayGameControlValues();
16024
16025   DrawCompleteVideoDisplay();
16026   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16027   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16028   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16029
16030   BackToFront();
16031 }
16032
16033 static void GameUndo(int steps)
16034 {
16035   if (!CheckEngineSnapshotList())
16036     return;
16037
16038   LoadEngineSnapshot_Undo(steps);
16039
16040   GameUndoRedoExt();
16041 }
16042
16043 static void GameRedo(int steps)
16044 {
16045   if (!CheckEngineSnapshotList())
16046     return;
16047
16048   LoadEngineSnapshot_Redo(steps);
16049
16050   GameUndoRedoExt();
16051 }
16052
16053 static void HandleGameButtonsExt(int id, int button)
16054 {
16055   static boolean game_undo_executed = FALSE;
16056   int steps = BUTTON_STEPSIZE(button);
16057   boolean handle_game_buttons =
16058     (game_status == GAME_MODE_PLAYING ||
16059      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16060
16061   if (!handle_game_buttons)
16062     return;
16063
16064   switch (id)
16065   {
16066     case GAME_CTRL_ID_STOP:
16067     case GAME_CTRL_ID_PANEL_STOP:
16068     case GAME_CTRL_ID_TOUCH_STOP:
16069       if (game_status == GAME_MODE_MAIN)
16070         break;
16071
16072       if (tape.playing)
16073         TapeStop();
16074       else
16075         RequestQuitGame(TRUE);
16076
16077       break;
16078
16079     case GAME_CTRL_ID_PAUSE:
16080     case GAME_CTRL_ID_PAUSE2:
16081     case GAME_CTRL_ID_PANEL_PAUSE:
16082     case GAME_CTRL_ID_TOUCH_PAUSE:
16083       if (network.enabled && game_status == GAME_MODE_PLAYING)
16084       {
16085         if (tape.pausing)
16086           SendToServer_ContinuePlaying();
16087         else
16088           SendToServer_PausePlaying();
16089       }
16090       else
16091         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16092
16093       game_undo_executed = FALSE;
16094
16095       break;
16096
16097     case GAME_CTRL_ID_PLAY:
16098     case GAME_CTRL_ID_PANEL_PLAY:
16099       if (game_status == GAME_MODE_MAIN)
16100       {
16101         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16102       }
16103       else if (tape.pausing)
16104       {
16105         if (network.enabled)
16106           SendToServer_ContinuePlaying();
16107         else
16108           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16109       }
16110       break;
16111
16112     case GAME_CTRL_ID_UNDO:
16113       // Important: When using "save snapshot when collecting an item" mode,
16114       // load last (current) snapshot for first "undo" after pressing "pause"
16115       // (else the last-but-one snapshot would be loaded, because the snapshot
16116       // pointer already points to the last snapshot when pressing "pause",
16117       // which is fine for "every step/move" mode, but not for "every collect")
16118       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16119           !game_undo_executed)
16120         steps--;
16121
16122       game_undo_executed = TRUE;
16123
16124       GameUndo(steps);
16125       break;
16126
16127     case GAME_CTRL_ID_REDO:
16128       GameRedo(steps);
16129       break;
16130
16131     case GAME_CTRL_ID_SAVE:
16132       TapeQuickSave();
16133       break;
16134
16135     case GAME_CTRL_ID_LOAD:
16136       TapeQuickLoad();
16137       break;
16138
16139     case SOUND_CTRL_ID_MUSIC:
16140     case SOUND_CTRL_ID_PANEL_MUSIC:
16141       if (setup.sound_music)
16142       { 
16143         setup.sound_music = FALSE;
16144
16145         FadeMusic();
16146       }
16147       else if (audio.music_available)
16148       { 
16149         setup.sound = setup.sound_music = TRUE;
16150
16151         SetAudioMode(setup.sound);
16152
16153         if (game_status == GAME_MODE_PLAYING)
16154           PlayLevelMusic();
16155       }
16156
16157       RedrawSoundButtonGadget(id);
16158
16159       break;
16160
16161     case SOUND_CTRL_ID_LOOPS:
16162     case SOUND_CTRL_ID_PANEL_LOOPS:
16163       if (setup.sound_loops)
16164         setup.sound_loops = FALSE;
16165       else if (audio.loops_available)
16166       {
16167         setup.sound = setup.sound_loops = TRUE;
16168
16169         SetAudioMode(setup.sound);
16170       }
16171
16172       RedrawSoundButtonGadget(id);
16173
16174       break;
16175
16176     case SOUND_CTRL_ID_SIMPLE:
16177     case SOUND_CTRL_ID_PANEL_SIMPLE:
16178       if (setup.sound_simple)
16179         setup.sound_simple = FALSE;
16180       else if (audio.sound_available)
16181       {
16182         setup.sound = setup.sound_simple = TRUE;
16183
16184         SetAudioMode(setup.sound);
16185       }
16186
16187       RedrawSoundButtonGadget(id);
16188
16189       break;
16190
16191     default:
16192       break;
16193   }
16194 }
16195
16196 static void HandleGameButtons(struct GadgetInfo *gi)
16197 {
16198   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16199 }
16200
16201 void HandleSoundButtonKeys(Key key)
16202 {
16203   if (key == setup.shortcut.sound_simple)
16204     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16205   else if (key == setup.shortcut.sound_loops)
16206     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16207   else if (key == setup.shortcut.sound_music)
16208     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16209 }