improved code for final time calculation
[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_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 int NewHiScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813
3814   game.score = 0;
3815   game.score_final = 0;
3816
3817   game.health = MAX_HEALTH;
3818   game.health_final = MAX_HEALTH;
3819
3820   game.gems_still_needed = level.gems_needed;
3821   game.sokoban_fields_still_needed = 0;
3822   game.sokoban_objects_still_needed = 0;
3823   game.lights_still_needed = 0;
3824   game.players_still_needed = 0;
3825   game.friends_still_needed = 0;
3826
3827   game.lenses_time_left = 0;
3828   game.magnify_time_left = 0;
3829
3830   game.ball_active = level.ball_active_initial;
3831   game.ball_content_nr = 0;
3832
3833   game.explosions_delayed = TRUE;
3834
3835   game.envelope_active = FALSE;
3836
3837   for (i = 0; i < NUM_BELTS; i++)
3838   {
3839     game.belt_dir[i] = MV_NONE;
3840     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3841   }
3842
3843   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3844     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3845
3846 #if DEBUG_INIT_PLAYER
3847   DebugPrintPlayerStatus("Player status at level initialization");
3848 #endif
3849
3850   SCAN_PLAYFIELD(x, y)
3851   {
3852     Tile[x][y] = Last[x][y] = level.field[x][y];
3853     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3854     ChangeDelay[x][y] = 0;
3855     ChangePage[x][y] = -1;
3856     CustomValue[x][y] = 0;              // initialized in InitField()
3857     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3858     AmoebaNr[x][y] = 0;
3859     WasJustMoving[x][y] = 0;
3860     WasJustFalling[x][y] = 0;
3861     CheckCollision[x][y] = 0;
3862     CheckImpact[x][y] = 0;
3863     Stop[x][y] = FALSE;
3864     Pushed[x][y] = FALSE;
3865
3866     ChangeCount[x][y] = 0;
3867     ChangeEvent[x][y] = -1;
3868
3869     ExplodePhase[x][y] = 0;
3870     ExplodeDelay[x][y] = 0;
3871     ExplodeField[x][y] = EX_TYPE_NONE;
3872
3873     RunnerVisit[x][y] = 0;
3874     PlayerVisit[x][y] = 0;
3875
3876     GfxFrame[x][y] = 0;
3877     GfxRandom[x][y] = INIT_GFX_RANDOM();
3878     GfxElement[x][y] = EL_UNDEFINED;
3879     GfxAction[x][y] = ACTION_DEFAULT;
3880     GfxDir[x][y] = MV_NONE;
3881     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3882   }
3883
3884   SCAN_PLAYFIELD(x, y)
3885   {
3886     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3887       emulate_bd = FALSE;
3888     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3889       emulate_sb = FALSE;
3890     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3891       emulate_sp = FALSE;
3892
3893     InitField(x, y, TRUE);
3894
3895     ResetGfxAnimation(x, y);
3896   }
3897
3898   InitBeltMovement();
3899
3900   for (i = 0; i < MAX_PLAYERS; i++)
3901   {
3902     struct PlayerInfo *player = &stored_player[i];
3903
3904     // set number of special actions for bored and sleeping animation
3905     player->num_special_action_bored =
3906       get_num_special_action(player->artwork_element,
3907                              ACTION_BORING_1, ACTION_BORING_LAST);
3908     player->num_special_action_sleeping =
3909       get_num_special_action(player->artwork_element,
3910                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3911   }
3912
3913   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3914                     emulate_sb ? EMU_SOKOBAN :
3915                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3916
3917   // initialize type of slippery elements
3918   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3919   {
3920     if (!IS_CUSTOM_ELEMENT(i))
3921     {
3922       // default: elements slip down either to the left or right randomly
3923       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3924
3925       // SP style elements prefer to slip down on the left side
3926       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3927         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3928
3929       // BD style elements prefer to slip down on the left side
3930       if (game.emulation == EMU_BOULDERDASH)
3931         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3932     }
3933   }
3934
3935   // initialize explosion and ignition delay
3936   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3937   {
3938     if (!IS_CUSTOM_ELEMENT(i))
3939     {
3940       int num_phase = 8;
3941       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3942                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3943                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3944       int last_phase = (num_phase + 1) * delay;
3945       int half_phase = (num_phase / 2) * delay;
3946
3947       element_info[i].explosion_delay = last_phase - 1;
3948       element_info[i].ignition_delay = half_phase;
3949
3950       if (i == EL_BLACK_ORB)
3951         element_info[i].ignition_delay = 1;
3952     }
3953   }
3954
3955   // correct non-moving belts to start moving left
3956   for (i = 0; i < NUM_BELTS; i++)
3957     if (game.belt_dir[i] == MV_NONE)
3958       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3959
3960 #if USE_NEW_PLAYER_ASSIGNMENTS
3961   // use preferred player also in local single-player mode
3962   if (!network.enabled && !game.team_mode)
3963   {
3964     int new_index_nr = setup.network_player_nr;
3965
3966     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3967     {
3968       for (i = 0; i < MAX_PLAYERS; i++)
3969         stored_player[i].connected_locally = FALSE;
3970
3971       stored_player[new_index_nr].connected_locally = TRUE;
3972     }
3973   }
3974
3975   for (i = 0; i < MAX_PLAYERS; i++)
3976   {
3977     stored_player[i].connected = FALSE;
3978
3979     // in network game mode, the local player might not be the first player
3980     if (stored_player[i].connected_locally)
3981       local_player = &stored_player[i];
3982   }
3983
3984   if (!network.enabled)
3985     local_player->connected = TRUE;
3986
3987   if (tape.playing)
3988   {
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990       stored_player[i].connected = tape.player_participates[i];
3991   }
3992   else if (network.enabled)
3993   {
3994     // add team mode players connected over the network (needed for correct
3995     // assignment of player figures from level to locally playing players)
3996
3997     for (i = 0; i < MAX_PLAYERS; i++)
3998       if (stored_player[i].connected_network)
3999         stored_player[i].connected = TRUE;
4000   }
4001   else if (game.team_mode)
4002   {
4003     // try to guess locally connected team mode players (needed for correct
4004     // assignment of player figures from level to locally playing players)
4005
4006     for (i = 0; i < MAX_PLAYERS; i++)
4007       if (setup.input[i].use_joystick ||
4008           setup.input[i].key.left != KSYM_UNDEFINED)
4009         stored_player[i].connected = TRUE;
4010   }
4011
4012 #if DEBUG_INIT_PLAYER
4013   DebugPrintPlayerStatus("Player status after level initialization");
4014 #endif
4015
4016 #if DEBUG_INIT_PLAYER
4017   Debug("game:init:player", "Reassigning players ...");
4018 #endif
4019
4020   // check if any connected player was not found in playfield
4021   for (i = 0; i < MAX_PLAYERS; i++)
4022   {
4023     struct PlayerInfo *player = &stored_player[i];
4024
4025     if (player->connected && !player->present)
4026     {
4027       struct PlayerInfo *field_player = NULL;
4028
4029 #if DEBUG_INIT_PLAYER
4030       Debug("game:init:player",
4031             "- looking for field player for player %d ...", i + 1);
4032 #endif
4033
4034       // assign first free player found that is present in the playfield
4035
4036       // first try: look for unmapped playfield player that is not connected
4037       for (j = 0; j < MAX_PLAYERS; j++)
4038         if (field_player == NULL &&
4039             stored_player[j].present &&
4040             !stored_player[j].mapped &&
4041             !stored_player[j].connected)
4042           field_player = &stored_player[j];
4043
4044       // second try: look for *any* unmapped playfield player
4045       for (j = 0; j < MAX_PLAYERS; j++)
4046         if (field_player == NULL &&
4047             stored_player[j].present &&
4048             !stored_player[j].mapped)
4049           field_player = &stored_player[j];
4050
4051       if (field_player != NULL)
4052       {
4053         int jx = field_player->jx, jy = field_player->jy;
4054
4055 #if DEBUG_INIT_PLAYER
4056         Debug("game:init:player", "- found player %d",
4057               field_player->index_nr + 1);
4058 #endif
4059
4060         player->present = FALSE;
4061         player->active = FALSE;
4062
4063         field_player->present = TRUE;
4064         field_player->active = TRUE;
4065
4066         /*
4067         player->initial_element = field_player->initial_element;
4068         player->artwork_element = field_player->artwork_element;
4069
4070         player->block_last_field       = field_player->block_last_field;
4071         player->block_delay_adjustment = field_player->block_delay_adjustment;
4072         */
4073
4074         StorePlayer[jx][jy] = field_player->element_nr;
4075
4076         field_player->jx = field_player->last_jx = jx;
4077         field_player->jy = field_player->last_jy = jy;
4078
4079         if (local_player == player)
4080           local_player = field_player;
4081
4082         map_player_action[field_player->index_nr] = i;
4083
4084         field_player->mapped = TRUE;
4085
4086 #if DEBUG_INIT_PLAYER
4087         Debug("game:init:player", "- map_player_action[%d] == %d",
4088               field_player->index_nr + 1, i + 1);
4089 #endif
4090       }
4091     }
4092
4093     if (player->connected && player->present)
4094       player->mapped = TRUE;
4095   }
4096
4097 #if DEBUG_INIT_PLAYER
4098   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4099 #endif
4100
4101 #else
4102
4103   // check if any connected player was not found in playfield
4104   for (i = 0; i < MAX_PLAYERS; i++)
4105   {
4106     struct PlayerInfo *player = &stored_player[i];
4107
4108     if (player->connected && !player->present)
4109     {
4110       for (j = 0; j < MAX_PLAYERS; j++)
4111       {
4112         struct PlayerInfo *field_player = &stored_player[j];
4113         int jx = field_player->jx, jy = field_player->jy;
4114
4115         // assign first free player found that is present in the playfield
4116         if (field_player->present && !field_player->connected)
4117         {
4118           player->present = TRUE;
4119           player->active = TRUE;
4120
4121           field_player->present = FALSE;
4122           field_player->active = FALSE;
4123
4124           player->initial_element = field_player->initial_element;
4125           player->artwork_element = field_player->artwork_element;
4126
4127           player->block_last_field       = field_player->block_last_field;
4128           player->block_delay_adjustment = field_player->block_delay_adjustment;
4129
4130           StorePlayer[jx][jy] = player->element_nr;
4131
4132           player->jx = player->last_jx = jx;
4133           player->jy = player->last_jy = jy;
4134
4135           break;
4136         }
4137       }
4138     }
4139   }
4140 #endif
4141
4142 #if 0
4143   Debug("game:init:player", "local_player->present == %d",
4144         local_player->present);
4145 #endif
4146
4147   // set focus to local player for network games, else to all players
4148   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4149   game.centered_player_nr_next = game.centered_player_nr;
4150   game.set_centered_player = FALSE;
4151   game.set_centered_player_wrap = FALSE;
4152
4153   if (network_playing && tape.recording)
4154   {
4155     // store client dependent player focus when recording network games
4156     tape.centered_player_nr_next = game.centered_player_nr_next;
4157     tape.set_centered_player = TRUE;
4158   }
4159
4160   if (tape.playing)
4161   {
4162     // when playing a tape, eliminate all players who do not participate
4163
4164 #if USE_NEW_PLAYER_ASSIGNMENTS
4165
4166     if (!game.team_mode)
4167     {
4168       for (i = 0; i < MAX_PLAYERS; i++)
4169       {
4170         if (stored_player[i].active &&
4171             !tape.player_participates[map_player_action[i]])
4172         {
4173           struct PlayerInfo *player = &stored_player[i];
4174           int jx = player->jx, jy = player->jy;
4175
4176 #if DEBUG_INIT_PLAYER
4177           Debug("game:init:player", "Removing player %d at (%d, %d)",
4178                 i + 1, jx, jy);
4179 #endif
4180
4181           player->active = FALSE;
4182           StorePlayer[jx][jy] = 0;
4183           Tile[jx][jy] = EL_EMPTY;
4184         }
4185       }
4186     }
4187
4188 #else
4189
4190     for (i = 0; i < MAX_PLAYERS; i++)
4191     {
4192       if (stored_player[i].active &&
4193           !tape.player_participates[i])
4194       {
4195         struct PlayerInfo *player = &stored_player[i];
4196         int jx = player->jx, jy = player->jy;
4197
4198         player->active = FALSE;
4199         StorePlayer[jx][jy] = 0;
4200         Tile[jx][jy] = EL_EMPTY;
4201       }
4202     }
4203 #endif
4204   }
4205   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4206   {
4207     // when in single player mode, eliminate all but the local player
4208
4209     for (i = 0; i < MAX_PLAYERS; i++)
4210     {
4211       struct PlayerInfo *player = &stored_player[i];
4212
4213       if (player->active && player != local_player)
4214       {
4215         int jx = player->jx, jy = player->jy;
4216
4217         player->active = FALSE;
4218         player->present = FALSE;
4219
4220         StorePlayer[jx][jy] = 0;
4221         Tile[jx][jy] = EL_EMPTY;
4222       }
4223     }
4224   }
4225
4226   for (i = 0; i < MAX_PLAYERS; i++)
4227     if (stored_player[i].active)
4228       game.players_still_needed++;
4229
4230   if (level.solved_by_one_player)
4231     game.players_still_needed = 1;
4232
4233   // when recording the game, store which players take part in the game
4234   if (tape.recording)
4235   {
4236 #if USE_NEW_PLAYER_ASSIGNMENTS
4237     for (i = 0; i < MAX_PLAYERS; i++)
4238       if (stored_player[i].connected)
4239         tape.player_participates[i] = TRUE;
4240 #else
4241     for (i = 0; i < MAX_PLAYERS; i++)
4242       if (stored_player[i].active)
4243         tape.player_participates[i] = TRUE;
4244 #endif
4245   }
4246
4247 #if DEBUG_INIT_PLAYER
4248   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4249 #endif
4250
4251   if (BorderElement == EL_EMPTY)
4252   {
4253     SBX_Left = 0;
4254     SBX_Right = lev_fieldx - SCR_FIELDX;
4255     SBY_Upper = 0;
4256     SBY_Lower = lev_fieldy - SCR_FIELDY;
4257   }
4258   else
4259   {
4260     SBX_Left = -1;
4261     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4262     SBY_Upper = -1;
4263     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4264   }
4265
4266   if (full_lev_fieldx <= SCR_FIELDX)
4267     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4268   if (full_lev_fieldy <= SCR_FIELDY)
4269     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4270
4271   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4272     SBX_Left--;
4273   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4274     SBY_Upper--;
4275
4276   // if local player not found, look for custom element that might create
4277   // the player (make some assumptions about the right custom element)
4278   if (!local_player->present)
4279   {
4280     int start_x = 0, start_y = 0;
4281     int found_rating = 0;
4282     int found_element = EL_UNDEFINED;
4283     int player_nr = local_player->index_nr;
4284
4285     SCAN_PLAYFIELD(x, y)
4286     {
4287       int element = Tile[x][y];
4288       int content;
4289       int xx, yy;
4290       boolean is_player;
4291
4292       if (level.use_start_element[player_nr] &&
4293           level.start_element[player_nr] == element &&
4294           found_rating < 4)
4295       {
4296         start_x = x;
4297         start_y = y;
4298
4299         found_rating = 4;
4300         found_element = element;
4301       }
4302
4303       if (!IS_CUSTOM_ELEMENT(element))
4304         continue;
4305
4306       if (CAN_CHANGE(element))
4307       {
4308         for (i = 0; i < element_info[element].num_change_pages; i++)
4309         {
4310           // check for player created from custom element as single target
4311           content = element_info[element].change_page[i].target_element;
4312           is_player = ELEM_IS_PLAYER(content);
4313
4314           if (is_player && (found_rating < 3 ||
4315                             (found_rating == 3 && element < found_element)))
4316           {
4317             start_x = x;
4318             start_y = y;
4319
4320             found_rating = 3;
4321             found_element = element;
4322           }
4323         }
4324       }
4325
4326       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4327       {
4328         // check for player created from custom element as explosion content
4329         content = element_info[element].content.e[xx][yy];
4330         is_player = ELEM_IS_PLAYER(content);
4331
4332         if (is_player && (found_rating < 2 ||
4333                           (found_rating == 2 && element < found_element)))
4334         {
4335           start_x = x + xx - 1;
4336           start_y = y + yy - 1;
4337
4338           found_rating = 2;
4339           found_element = element;
4340         }
4341
4342         if (!CAN_CHANGE(element))
4343           continue;
4344
4345         for (i = 0; i < element_info[element].num_change_pages; i++)
4346         {
4347           // check for player created from custom element as extended target
4348           content =
4349             element_info[element].change_page[i].target_content.e[xx][yy];
4350
4351           is_player = ELEM_IS_PLAYER(content);
4352
4353           if (is_player && (found_rating < 1 ||
4354                             (found_rating == 1 && element < found_element)))
4355           {
4356             start_x = x + xx - 1;
4357             start_y = y + yy - 1;
4358
4359             found_rating = 1;
4360             found_element = element;
4361           }
4362         }
4363       }
4364     }
4365
4366     scroll_x = SCROLL_POSITION_X(start_x);
4367     scroll_y = SCROLL_POSITION_Y(start_y);
4368   }
4369   else
4370   {
4371     scroll_x = SCROLL_POSITION_X(local_player->jx);
4372     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4373   }
4374
4375   // !!! FIX THIS (START) !!!
4376   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4377   {
4378     InitGameEngine_EM();
4379   }
4380   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4381   {
4382     InitGameEngine_SP();
4383   }
4384   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4385   {
4386     InitGameEngine_MM();
4387   }
4388   else
4389   {
4390     DrawLevel(REDRAW_FIELD);
4391     DrawAllPlayers();
4392
4393     // after drawing the level, correct some elements
4394     if (game.timegate_time_left == 0)
4395       CloseAllOpenTimegates();
4396   }
4397
4398   // blit playfield from scroll buffer to normal back buffer for fading in
4399   BlitScreenToBitmap(backbuffer);
4400   // !!! FIX THIS (END) !!!
4401
4402   DrawMaskedBorder(fade_mask);
4403
4404   FadeIn(fade_mask);
4405
4406 #if 1
4407   // full screen redraw is required at this point in the following cases:
4408   // - special editor door undrawn when game was started from level editor
4409   // - drawing area (playfield) was changed and has to be removed completely
4410   redraw_mask = REDRAW_ALL;
4411   BackToFront();
4412 #endif
4413
4414   if (!game.restart_level)
4415   {
4416     // copy default game door content to main double buffer
4417
4418     // !!! CHECK AGAIN !!!
4419     SetPanelBackground();
4420     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4421     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4422   }
4423
4424   SetPanelBackground();
4425   SetDrawBackgroundMask(REDRAW_DOOR_1);
4426
4427   UpdateAndDisplayGameControlValues();
4428
4429   if (!game.restart_level)
4430   {
4431     UnmapGameButtons();
4432     UnmapTapeButtons();
4433
4434     FreeGameButtons();
4435     CreateGameButtons();
4436
4437     MapGameButtons();
4438     MapTapeButtons();
4439
4440     // copy actual game door content to door double buffer for OpenDoor()
4441     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4442
4443     OpenDoor(DOOR_OPEN_ALL);
4444
4445     KeyboardAutoRepeatOffUnlessAutoplay();
4446
4447 #if DEBUG_INIT_PLAYER
4448     DebugPrintPlayerStatus("Player status (final)");
4449 #endif
4450   }
4451
4452   UnmapAllGadgets();
4453
4454   MapGameButtons();
4455   MapTapeButtons();
4456
4457   if (!game.restart_level && !tape.playing)
4458   {
4459     LevelStats_incPlayed(level_nr);
4460
4461     SaveLevelSetup_SeriesInfo();
4462   }
4463
4464   game.restart_level = FALSE;
4465   game.restart_game_message = NULL;
4466
4467   game.request_active = FALSE;
4468   game.request_active_or_moving = FALSE;
4469
4470   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4471     InitGameActions_MM();
4472
4473   SaveEngineSnapshotToListInitial();
4474
4475   if (!game.restart_level)
4476   {
4477     PlaySound(SND_GAME_STARTING);
4478
4479     if (setup.sound_music)
4480       PlayLevelMusic();
4481   }
4482 }
4483
4484 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4485                         int actual_player_x, int actual_player_y)
4486 {
4487   // this is used for non-R'n'D game engines to update certain engine values
4488
4489   // needed to determine if sounds are played within the visible screen area
4490   scroll_x = actual_scroll_x;
4491   scroll_y = actual_scroll_y;
4492
4493   // needed to get player position for "follow finger" playing input method
4494   local_player->jx = actual_player_x;
4495   local_player->jy = actual_player_y;
4496 }
4497
4498 void InitMovDir(int x, int y)
4499 {
4500   int i, element = Tile[x][y];
4501   static int xy[4][2] =
4502   {
4503     {  0, +1 },
4504     { +1,  0 },
4505     {  0, -1 },
4506     { -1,  0 }
4507   };
4508   static int direction[3][4] =
4509   {
4510     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4511     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4512     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4513   };
4514
4515   switch (element)
4516   {
4517     case EL_BUG_RIGHT:
4518     case EL_BUG_UP:
4519     case EL_BUG_LEFT:
4520     case EL_BUG_DOWN:
4521       Tile[x][y] = EL_BUG;
4522       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4523       break;
4524
4525     case EL_SPACESHIP_RIGHT:
4526     case EL_SPACESHIP_UP:
4527     case EL_SPACESHIP_LEFT:
4528     case EL_SPACESHIP_DOWN:
4529       Tile[x][y] = EL_SPACESHIP;
4530       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4531       break;
4532
4533     case EL_BD_BUTTERFLY_RIGHT:
4534     case EL_BD_BUTTERFLY_UP:
4535     case EL_BD_BUTTERFLY_LEFT:
4536     case EL_BD_BUTTERFLY_DOWN:
4537       Tile[x][y] = EL_BD_BUTTERFLY;
4538       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4539       break;
4540
4541     case EL_BD_FIREFLY_RIGHT:
4542     case EL_BD_FIREFLY_UP:
4543     case EL_BD_FIREFLY_LEFT:
4544     case EL_BD_FIREFLY_DOWN:
4545       Tile[x][y] = EL_BD_FIREFLY;
4546       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4547       break;
4548
4549     case EL_PACMAN_RIGHT:
4550     case EL_PACMAN_UP:
4551     case EL_PACMAN_LEFT:
4552     case EL_PACMAN_DOWN:
4553       Tile[x][y] = EL_PACMAN;
4554       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4555       break;
4556
4557     case EL_YAMYAM_LEFT:
4558     case EL_YAMYAM_RIGHT:
4559     case EL_YAMYAM_UP:
4560     case EL_YAMYAM_DOWN:
4561       Tile[x][y] = EL_YAMYAM;
4562       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4563       break;
4564
4565     case EL_SP_SNIKSNAK:
4566       MovDir[x][y] = MV_UP;
4567       break;
4568
4569     case EL_SP_ELECTRON:
4570       MovDir[x][y] = MV_LEFT;
4571       break;
4572
4573     case EL_MOLE_LEFT:
4574     case EL_MOLE_RIGHT:
4575     case EL_MOLE_UP:
4576     case EL_MOLE_DOWN:
4577       Tile[x][y] = EL_MOLE;
4578       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4579       break;
4580
4581     case EL_SPRING_LEFT:
4582     case EL_SPRING_RIGHT:
4583       Tile[x][y] = EL_SPRING;
4584       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4585       break;
4586
4587     default:
4588       if (IS_CUSTOM_ELEMENT(element))
4589       {
4590         struct ElementInfo *ei = &element_info[element];
4591         int move_direction_initial = ei->move_direction_initial;
4592         int move_pattern = ei->move_pattern;
4593
4594         if (move_direction_initial == MV_START_PREVIOUS)
4595         {
4596           if (MovDir[x][y] != MV_NONE)
4597             return;
4598
4599           move_direction_initial = MV_START_AUTOMATIC;
4600         }
4601
4602         if (move_direction_initial == MV_START_RANDOM)
4603           MovDir[x][y] = 1 << RND(4);
4604         else if (move_direction_initial & MV_ANY_DIRECTION)
4605           MovDir[x][y] = move_direction_initial;
4606         else if (move_pattern == MV_ALL_DIRECTIONS ||
4607                  move_pattern == MV_TURNING_LEFT ||
4608                  move_pattern == MV_TURNING_RIGHT ||
4609                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4610                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4611                  move_pattern == MV_TURNING_RANDOM)
4612           MovDir[x][y] = 1 << RND(4);
4613         else if (move_pattern == MV_HORIZONTAL)
4614           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4615         else if (move_pattern == MV_VERTICAL)
4616           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4617         else if (move_pattern & MV_ANY_DIRECTION)
4618           MovDir[x][y] = element_info[element].move_pattern;
4619         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4620                  move_pattern == MV_ALONG_RIGHT_SIDE)
4621         {
4622           // use random direction as default start direction
4623           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4624             MovDir[x][y] = 1 << RND(4);
4625
4626           for (i = 0; i < NUM_DIRECTIONS; i++)
4627           {
4628             int x1 = x + xy[i][0];
4629             int y1 = y + xy[i][1];
4630
4631             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4632             {
4633               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4634                 MovDir[x][y] = direction[0][i];
4635               else
4636                 MovDir[x][y] = direction[1][i];
4637
4638               break;
4639             }
4640           }
4641         }                
4642       }
4643       else
4644       {
4645         MovDir[x][y] = 1 << RND(4);
4646
4647         if (element != EL_BUG &&
4648             element != EL_SPACESHIP &&
4649             element != EL_BD_BUTTERFLY &&
4650             element != EL_BD_FIREFLY)
4651           break;
4652
4653         for (i = 0; i < NUM_DIRECTIONS; i++)
4654         {
4655           int x1 = x + xy[i][0];
4656           int y1 = y + xy[i][1];
4657
4658           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4659           {
4660             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4661             {
4662               MovDir[x][y] = direction[0][i];
4663               break;
4664             }
4665             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4666                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4667             {
4668               MovDir[x][y] = direction[1][i];
4669               break;
4670             }
4671           }
4672         }
4673       }
4674       break;
4675   }
4676
4677   GfxDir[x][y] = MovDir[x][y];
4678 }
4679
4680 void InitAmoebaNr(int x, int y)
4681 {
4682   int i;
4683   int group_nr = AmoebaNeighbourNr(x, y);
4684
4685   if (group_nr == 0)
4686   {
4687     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4688     {
4689       if (AmoebaCnt[i] == 0)
4690       {
4691         group_nr = i;
4692         break;
4693       }
4694     }
4695   }
4696
4697   AmoebaNr[x][y] = group_nr;
4698   AmoebaCnt[group_nr]++;
4699   AmoebaCnt2[group_nr]++;
4700 }
4701
4702 static void LevelSolved(void)
4703 {
4704   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4705       game.players_still_needed > 0)
4706     return;
4707
4708   game.LevelSolved = TRUE;
4709   game.GameOver = TRUE;
4710
4711   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4712   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4713                       game_em.lev->score :
4714                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4715                       game_mm.score :
4716                       game.score);
4717   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4718                        MM_HEALTH(game_mm.laser_overload_value) :
4719                        game.health);
4720
4721   game.LevelSolved_CountingTime = game.time_final;
4722   game.LevelSolved_CountingScore = game.score_final;
4723   game.LevelSolved_CountingHealth = game.health_final;
4724 }
4725
4726 void GameWon(void)
4727 {
4728   static int time_count_steps;
4729   static int time, time_final;
4730   static float score, score_final; // needed for time score < 10 for 10 seconds
4731   static int health, health_final;
4732   static int game_over_delay_1 = 0;
4733   static int game_over_delay_2 = 0;
4734   static int game_over_delay_3 = 0;
4735   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4736   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4737
4738   if (!game.LevelSolved_GameWon)
4739   {
4740     int i;
4741
4742     // do not start end game actions before the player stops moving (to exit)
4743     if (local_player->active && local_player->MovPos)
4744       return;
4745
4746     game.LevelSolved_GameWon = TRUE;
4747     game.LevelSolved_SaveTape = tape.recording;
4748     game.LevelSolved_SaveScore = !tape.playing;
4749
4750     if (!tape.playing)
4751     {
4752       LevelStats_incSolved(level_nr);
4753
4754       SaveLevelSetup_SeriesInfo();
4755     }
4756
4757     if (tape.auto_play)         // tape might already be stopped here
4758       tape.auto_play_level_solved = TRUE;
4759
4760     TapeStop();
4761
4762     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4763     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4764     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4765
4766     time = time_final = game.time_final;
4767     score = score_final = game.score_final;
4768     health = health_final = game.health_final;
4769
4770     if (time_score > 0)
4771     {
4772       int time_final_max = 999;
4773       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4774       int time_frames = 0;
4775       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4776       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4777
4778       if (TimeLeft > 0)
4779       {
4780         time_final = 0;
4781         time_frames = time_frames_left;
4782       }
4783       else if (game.no_time_limit && TimePlayed < time_final_max)
4784       {
4785         time_final = time_final_max;
4786         time_frames = time_frames_final_max - time_frames_played;
4787       }
4788
4789       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4790
4791       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4792
4793       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4794       {
4795         health_final = 0;
4796         score_final += health * time_score;
4797       }
4798
4799       game.score_final = score_final;
4800       game.health_final = health_final;
4801     }
4802
4803     if (level_editor_test_game || !setup.count_score_after_game)
4804     {
4805       time = time_final;
4806       score = score_final;
4807
4808       game.LevelSolved_CountingTime = time;
4809       game.LevelSolved_CountingScore = score;
4810
4811       game_panel_controls[GAME_PANEL_TIME].value = time;
4812       game_panel_controls[GAME_PANEL_SCORE].value = score;
4813
4814       DisplayGameControlValues();
4815     }
4816
4817     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4818     {
4819       // check if last player has left the level
4820       if (game.exit_x >= 0 &&
4821           game.exit_y >= 0)
4822       {
4823         int x = game.exit_x;
4824         int y = game.exit_y;
4825         int element = Tile[x][y];
4826
4827         // close exit door after last player
4828         if ((game.all_players_gone &&
4829              (element == EL_EXIT_OPEN ||
4830               element == EL_SP_EXIT_OPEN ||
4831               element == EL_STEEL_EXIT_OPEN)) ||
4832             element == EL_EM_EXIT_OPEN ||
4833             element == EL_EM_STEEL_EXIT_OPEN)
4834         {
4835
4836           Tile[x][y] =
4837             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4838              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4839              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4840              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4841              EL_EM_STEEL_EXIT_CLOSING);
4842
4843           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4844         }
4845
4846         // player disappears
4847         DrawLevelField(x, y);
4848       }
4849
4850       for (i = 0; i < MAX_PLAYERS; i++)
4851       {
4852         struct PlayerInfo *player = &stored_player[i];
4853
4854         if (player->present)
4855         {
4856           RemovePlayer(player);
4857
4858           // player disappears
4859           DrawLevelField(player->jx, player->jy);
4860         }
4861       }
4862     }
4863
4864     PlaySound(SND_GAME_WINNING);
4865   }
4866
4867   if (setup.count_score_after_game)
4868   {
4869     if (time != time_final)
4870     {
4871       if (game_over_delay_1 > 0)
4872       {
4873         game_over_delay_1--;
4874
4875         return;
4876       }
4877
4878       int time_to_go = ABS(time_final - time);
4879       int time_count_dir = (time < time_final ? +1 : -1);
4880
4881       if (time_to_go < time_count_steps)
4882         time_count_steps = 1;
4883
4884       time  += time_count_steps * time_count_dir;
4885       score += time_count_steps * time_score;
4886
4887       // set final score to correct rounding differences after counting score
4888       if (time == time_final)
4889         score = score_final;
4890
4891       game.LevelSolved_CountingTime = time;
4892       game.LevelSolved_CountingScore = score;
4893
4894       game_panel_controls[GAME_PANEL_TIME].value = time;
4895       game_panel_controls[GAME_PANEL_SCORE].value = score;
4896
4897       DisplayGameControlValues();
4898
4899       if (time == time_final)
4900         StopSound(SND_GAME_LEVELTIME_BONUS);
4901       else if (setup.sound_loops)
4902         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4903       else
4904         PlaySound(SND_GAME_LEVELTIME_BONUS);
4905
4906       return;
4907     }
4908
4909     if (health != health_final)
4910     {
4911       if (game_over_delay_2 > 0)
4912       {
4913         game_over_delay_2--;
4914
4915         return;
4916       }
4917
4918       int health_count_dir = (health < health_final ? +1 : -1);
4919
4920       health += health_count_dir;
4921       score  += time_score;
4922
4923       game.LevelSolved_CountingHealth = health;
4924       game.LevelSolved_CountingScore = score;
4925
4926       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4927       game_panel_controls[GAME_PANEL_SCORE].value = score;
4928
4929       DisplayGameControlValues();
4930
4931       if (health == health_final)
4932         StopSound(SND_GAME_LEVELTIME_BONUS);
4933       else if (setup.sound_loops)
4934         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4935       else
4936         PlaySound(SND_GAME_LEVELTIME_BONUS);
4937
4938       return;
4939     }
4940   }
4941
4942   game.panel.active = FALSE;
4943
4944   if (game_over_delay_3 > 0)
4945   {
4946     game_over_delay_3--;
4947
4948     return;
4949   }
4950
4951   GameEnd();
4952 }
4953
4954 void GameEnd(void)
4955 {
4956   // used instead of "level_nr" (needed for network games)
4957   int last_level_nr = levelset.level_nr;
4958   int hi_pos;
4959
4960   game.LevelSolved_GameEnd = TRUE;
4961
4962   if (game.LevelSolved_SaveTape)
4963   {
4964     // make sure that request dialog to save tape does not open door again
4965     if (!global.use_envelope_request)
4966       CloseDoor(DOOR_CLOSE_1);
4967
4968     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4969   }
4970
4971   // if no tape is to be saved, close both doors simultaneously
4972   CloseDoor(DOOR_CLOSE_ALL);
4973
4974   if (level_editor_test_game)
4975   {
4976     SetGameStatus(GAME_MODE_MAIN);
4977
4978     DrawMainMenu();
4979
4980     return;
4981   }
4982
4983   if (!game.LevelSolved_SaveScore)
4984   {
4985     SetGameStatus(GAME_MODE_MAIN);
4986
4987     DrawMainMenu();
4988
4989     return;
4990   }
4991
4992   if (level_nr == leveldir_current->handicap_level)
4993   {
4994     leveldir_current->handicap_level++;
4995
4996     SaveLevelSetup_SeriesInfo();
4997   }
4998
4999   if (setup.increment_levels &&
5000       level_nr < leveldir_current->last_level &&
5001       !network_playing)
5002   {
5003     level_nr++;         // advance to next level
5004     TapeErase();        // start with empty tape
5005
5006     if (setup.auto_play_next_level)
5007     {
5008       LoadLevel(level_nr);
5009
5010       SaveLevelSetup_SeriesInfo();
5011     }
5012   }
5013
5014   hi_pos = NewHiScore(last_level_nr);
5015
5016   if (hi_pos >= 0 && setup.show_scores_after_game)
5017   {
5018     SetGameStatus(GAME_MODE_SCORES);
5019
5020     DrawHallOfFame(last_level_nr, hi_pos);
5021   }
5022   else if (setup.auto_play_next_level && setup.increment_levels &&
5023            last_level_nr < leveldir_current->last_level &&
5024            !network_playing)
5025   {
5026     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5027   }
5028   else
5029   {
5030     SetGameStatus(GAME_MODE_MAIN);
5031
5032     DrawMainMenu();
5033   }
5034 }
5035
5036 int NewHiScore(int level_nr)
5037 {
5038   int k, l;
5039   int position = -1;
5040   boolean one_score_entry_per_name = !program.many_scores_per_name;
5041
5042   LoadScore(level_nr);
5043
5044   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5045       game.score_final < scores.entry[MAX_SCORE_ENTRIES - 1].score)
5046     return -1;
5047
5048   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5049   {
5050     if (game.score_final > scores.entry[k].score)
5051     {
5052       // player has made it to the hall of fame
5053
5054       if (k < MAX_SCORE_ENTRIES - 1)
5055       {
5056         int m = MAX_SCORE_ENTRIES - 1;
5057
5058         if (one_score_entry_per_name)
5059         {
5060           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5061             if (strEqual(setup.player_name, scores.entry[l].name))
5062               m = l;
5063
5064           if (m == k)   // player's new highscore overwrites his old one
5065             goto put_into_list;
5066         }
5067
5068         for (l = m; l > k; l--)
5069         {
5070           strcpy(scores.entry[l].name, scores.entry[l - 1].name);
5071           scores.entry[l].score = scores.entry[l - 1].score;
5072         }
5073       }
5074
5075       put_into_list:
5076
5077       strncpy(scores.entry[k].name, setup.player_name, MAX_PLAYER_NAME_LEN);
5078       scores.entry[k].name[MAX_PLAYER_NAME_LEN] = '\0';
5079       scores.entry[k].score = game.score_final;
5080       position = k;
5081
5082       break;
5083     }
5084     else if (one_score_entry_per_name &&
5085              !strncmp(setup.player_name, scores.entry[k].name,
5086                       MAX_PLAYER_NAME_LEN))
5087       break;    // player already there with a higher score
5088   }
5089
5090   if (position >= 0) 
5091     SaveScore(level_nr);
5092
5093   return position;
5094 }
5095
5096 static int getElementMoveStepsizeExt(int x, int y, int direction)
5097 {
5098   int element = Tile[x][y];
5099   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5100   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5101   int horiz_move = (dx != 0);
5102   int sign = (horiz_move ? dx : dy);
5103   int step = sign * element_info[element].move_stepsize;
5104
5105   // special values for move stepsize for spring and things on conveyor belt
5106   if (horiz_move)
5107   {
5108     if (CAN_FALL(element) &&
5109         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5110       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5111     else if (element == EL_SPRING)
5112       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5113   }
5114
5115   return step;
5116 }
5117
5118 static int getElementMoveStepsize(int x, int y)
5119 {
5120   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5121 }
5122
5123 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5124 {
5125   if (player->GfxAction != action || player->GfxDir != dir)
5126   {
5127     player->GfxAction = action;
5128     player->GfxDir = dir;
5129     player->Frame = 0;
5130     player->StepFrame = 0;
5131   }
5132 }
5133
5134 static void ResetGfxFrame(int x, int y)
5135 {
5136   // profiling showed that "autotest" spends 10~20% of its time in this function
5137   if (DrawingDeactivatedField())
5138     return;
5139
5140   int element = Tile[x][y];
5141   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5142
5143   if (graphic_info[graphic].anim_global_sync)
5144     GfxFrame[x][y] = FrameCounter;
5145   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5146     GfxFrame[x][y] = CustomValue[x][y];
5147   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5148     GfxFrame[x][y] = element_info[element].collect_score;
5149   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5150     GfxFrame[x][y] = ChangeDelay[x][y];
5151 }
5152
5153 static void ResetGfxAnimation(int x, int y)
5154 {
5155   GfxAction[x][y] = ACTION_DEFAULT;
5156   GfxDir[x][y] = MovDir[x][y];
5157   GfxFrame[x][y] = 0;
5158
5159   ResetGfxFrame(x, y);
5160 }
5161
5162 static void ResetRandomAnimationValue(int x, int y)
5163 {
5164   GfxRandom[x][y] = INIT_GFX_RANDOM();
5165 }
5166
5167 static void InitMovingField(int x, int y, int direction)
5168 {
5169   int element = Tile[x][y];
5170   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5171   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5172   int newx = x + dx;
5173   int newy = y + dy;
5174   boolean is_moving_before, is_moving_after;
5175
5176   // check if element was/is moving or being moved before/after mode change
5177   is_moving_before = (WasJustMoving[x][y] != 0);
5178   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5179
5180   // reset animation only for moving elements which change direction of moving
5181   // or which just started or stopped moving
5182   // (else CEs with property "can move" / "not moving" are reset each frame)
5183   if (is_moving_before != is_moving_after ||
5184       direction != MovDir[x][y])
5185     ResetGfxAnimation(x, y);
5186
5187   MovDir[x][y] = direction;
5188   GfxDir[x][y] = direction;
5189
5190   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5191                      direction == MV_DOWN && CAN_FALL(element) ?
5192                      ACTION_FALLING : ACTION_MOVING);
5193
5194   // this is needed for CEs with property "can move" / "not moving"
5195
5196   if (is_moving_after)
5197   {
5198     if (Tile[newx][newy] == EL_EMPTY)
5199       Tile[newx][newy] = EL_BLOCKED;
5200
5201     MovDir[newx][newy] = MovDir[x][y];
5202
5203     CustomValue[newx][newy] = CustomValue[x][y];
5204
5205     GfxFrame[newx][newy] = GfxFrame[x][y];
5206     GfxRandom[newx][newy] = GfxRandom[x][y];
5207     GfxAction[newx][newy] = GfxAction[x][y];
5208     GfxDir[newx][newy] = GfxDir[x][y];
5209   }
5210 }
5211
5212 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5213 {
5214   int direction = MovDir[x][y];
5215   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5216   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5217
5218   *goes_to_x = newx;
5219   *goes_to_y = newy;
5220 }
5221
5222 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5223 {
5224   int oldx = x, oldy = y;
5225   int direction = MovDir[x][y];
5226
5227   if (direction == MV_LEFT)
5228     oldx++;
5229   else if (direction == MV_RIGHT)
5230     oldx--;
5231   else if (direction == MV_UP)
5232     oldy++;
5233   else if (direction == MV_DOWN)
5234     oldy--;
5235
5236   *comes_from_x = oldx;
5237   *comes_from_y = oldy;
5238 }
5239
5240 static int MovingOrBlocked2Element(int x, int y)
5241 {
5242   int element = Tile[x][y];
5243
5244   if (element == EL_BLOCKED)
5245   {
5246     int oldx, oldy;
5247
5248     Blocked2Moving(x, y, &oldx, &oldy);
5249     return Tile[oldx][oldy];
5250   }
5251   else
5252     return element;
5253 }
5254
5255 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5256 {
5257   // like MovingOrBlocked2Element(), but if element is moving
5258   // and (x,y) is the field the moving element is just leaving,
5259   // return EL_BLOCKED instead of the element value
5260   int element = Tile[x][y];
5261
5262   if (IS_MOVING(x, y))
5263   {
5264     if (element == EL_BLOCKED)
5265     {
5266       int oldx, oldy;
5267
5268       Blocked2Moving(x, y, &oldx, &oldy);
5269       return Tile[oldx][oldy];
5270     }
5271     else
5272       return EL_BLOCKED;
5273   }
5274   else
5275     return element;
5276 }
5277
5278 static void RemoveField(int x, int y)
5279 {
5280   Tile[x][y] = EL_EMPTY;
5281
5282   MovPos[x][y] = 0;
5283   MovDir[x][y] = 0;
5284   MovDelay[x][y] = 0;
5285
5286   CustomValue[x][y] = 0;
5287
5288   AmoebaNr[x][y] = 0;
5289   ChangeDelay[x][y] = 0;
5290   ChangePage[x][y] = -1;
5291   Pushed[x][y] = FALSE;
5292
5293   GfxElement[x][y] = EL_UNDEFINED;
5294   GfxAction[x][y] = ACTION_DEFAULT;
5295   GfxDir[x][y] = MV_NONE;
5296 }
5297
5298 static void RemoveMovingField(int x, int y)
5299 {
5300   int oldx = x, oldy = y, newx = x, newy = y;
5301   int element = Tile[x][y];
5302   int next_element = EL_UNDEFINED;
5303
5304   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5305     return;
5306
5307   if (IS_MOVING(x, y))
5308   {
5309     Moving2Blocked(x, y, &newx, &newy);
5310
5311     if (Tile[newx][newy] != EL_BLOCKED)
5312     {
5313       // element is moving, but target field is not free (blocked), but
5314       // already occupied by something different (example: acid pool);
5315       // in this case, only remove the moving field, but not the target
5316
5317       RemoveField(oldx, oldy);
5318
5319       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5320
5321       TEST_DrawLevelField(oldx, oldy);
5322
5323       return;
5324     }
5325   }
5326   else if (element == EL_BLOCKED)
5327   {
5328     Blocked2Moving(x, y, &oldx, &oldy);
5329     if (!IS_MOVING(oldx, oldy))
5330       return;
5331   }
5332
5333   if (element == EL_BLOCKED &&
5334       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5335        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5336        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5337        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5338        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5339        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5340     next_element = get_next_element(Tile[oldx][oldy]);
5341
5342   RemoveField(oldx, oldy);
5343   RemoveField(newx, newy);
5344
5345   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5346
5347   if (next_element != EL_UNDEFINED)
5348     Tile[oldx][oldy] = next_element;
5349
5350   TEST_DrawLevelField(oldx, oldy);
5351   TEST_DrawLevelField(newx, newy);
5352 }
5353
5354 void DrawDynamite(int x, int y)
5355 {
5356   int sx = SCREENX(x), sy = SCREENY(y);
5357   int graphic = el2img(Tile[x][y]);
5358   int frame;
5359
5360   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5361     return;
5362
5363   if (IS_WALKABLE_INSIDE(Back[x][y]))
5364     return;
5365
5366   if (Back[x][y])
5367     DrawLevelElement(x, y, Back[x][y]);
5368   else if (Store[x][y])
5369     DrawLevelElement(x, y, Store[x][y]);
5370   else if (game.use_masked_elements)
5371     DrawLevelElement(x, y, EL_EMPTY);
5372
5373   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5374
5375   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5376     DrawGraphicThruMask(sx, sy, graphic, frame);
5377   else
5378     DrawGraphic(sx, sy, graphic, frame);
5379 }
5380
5381 static void CheckDynamite(int x, int y)
5382 {
5383   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5384   {
5385     MovDelay[x][y]--;
5386
5387     if (MovDelay[x][y] != 0)
5388     {
5389       DrawDynamite(x, y);
5390       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5391
5392       return;
5393     }
5394   }
5395
5396   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5397
5398   Bang(x, y);
5399 }
5400
5401 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5402 {
5403   boolean num_checked_players = 0;
5404   int i;
5405
5406   for (i = 0; i < MAX_PLAYERS; i++)
5407   {
5408     if (stored_player[i].active)
5409     {
5410       int sx = stored_player[i].jx;
5411       int sy = stored_player[i].jy;
5412
5413       if (num_checked_players == 0)
5414       {
5415         *sx1 = *sx2 = sx;
5416         *sy1 = *sy2 = sy;
5417       }
5418       else
5419       {
5420         *sx1 = MIN(*sx1, sx);
5421         *sy1 = MIN(*sy1, sy);
5422         *sx2 = MAX(*sx2, sx);
5423         *sy2 = MAX(*sy2, sy);
5424       }
5425
5426       num_checked_players++;
5427     }
5428   }
5429 }
5430
5431 static boolean checkIfAllPlayersFitToScreen_RND(void)
5432 {
5433   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5434
5435   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5436
5437   return (sx2 - sx1 < SCR_FIELDX &&
5438           sy2 - sy1 < SCR_FIELDY);
5439 }
5440
5441 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5442 {
5443   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5444
5445   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5446
5447   *sx = (sx1 + sx2) / 2;
5448   *sy = (sy1 + sy2) / 2;
5449 }
5450
5451 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5452                                boolean center_screen, boolean quick_relocation)
5453 {
5454   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5455   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5456   boolean no_delay = (tape.warp_forward);
5457   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5458   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5459   int new_scroll_x, new_scroll_y;
5460
5461   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5462   {
5463     // case 1: quick relocation inside visible screen (without scrolling)
5464
5465     RedrawPlayfield();
5466
5467     return;
5468   }
5469
5470   if (!level.shifted_relocation || center_screen)
5471   {
5472     // relocation _with_ centering of screen
5473
5474     new_scroll_x = SCROLL_POSITION_X(x);
5475     new_scroll_y = SCROLL_POSITION_Y(y);
5476   }
5477   else
5478   {
5479     // relocation _without_ centering of screen
5480
5481     int center_scroll_x = SCROLL_POSITION_X(old_x);
5482     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5483     int offset_x = x + (scroll_x - center_scroll_x);
5484     int offset_y = y + (scroll_y - center_scroll_y);
5485
5486     // for new screen position, apply previous offset to center position
5487     new_scroll_x = SCROLL_POSITION_X(offset_x);
5488     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5489   }
5490
5491   if (quick_relocation)
5492   {
5493     // case 2: quick relocation (redraw without visible scrolling)
5494
5495     scroll_x = new_scroll_x;
5496     scroll_y = new_scroll_y;
5497
5498     RedrawPlayfield();
5499
5500     return;
5501   }
5502
5503   // case 3: visible relocation (with scrolling to new position)
5504
5505   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5506
5507   SetVideoFrameDelay(wait_delay_value);
5508
5509   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5510   {
5511     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5512     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5513
5514     if (dx == 0 && dy == 0)             // no scrolling needed at all
5515       break;
5516
5517     scroll_x -= dx;
5518     scroll_y -= dy;
5519
5520     // set values for horizontal/vertical screen scrolling (half tile size)
5521     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5522     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5523     int pos_x = dx * TILEX / 2;
5524     int pos_y = dy * TILEY / 2;
5525     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5526     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5527
5528     ScrollLevel(dx, dy);
5529     DrawAllPlayers();
5530
5531     // scroll in two steps of half tile size to make things smoother
5532     BlitScreenToBitmapExt_RND(window, fx, fy);
5533
5534     // scroll second step to align at full tile size
5535     BlitScreenToBitmap(window);
5536   }
5537
5538   DrawAllPlayers();
5539   BackToFront();
5540
5541   SetVideoFrameDelay(frame_delay_value_old);
5542 }
5543
5544 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5545 {
5546   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5547   int player_nr = GET_PLAYER_NR(el_player);
5548   struct PlayerInfo *player = &stored_player[player_nr];
5549   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5550   boolean no_delay = (tape.warp_forward);
5551   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5552   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5553   int old_jx = player->jx;
5554   int old_jy = player->jy;
5555   int old_element = Tile[old_jx][old_jy];
5556   int element = Tile[jx][jy];
5557   boolean player_relocated = (old_jx != jx || old_jy != jy);
5558
5559   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5560   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5561   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5562   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5563   int leave_side_horiz = move_dir_horiz;
5564   int leave_side_vert  = move_dir_vert;
5565   int enter_side = enter_side_horiz | enter_side_vert;
5566   int leave_side = leave_side_horiz | leave_side_vert;
5567
5568   if (player->buried)           // do not reanimate dead player
5569     return;
5570
5571   if (!player_relocated)        // no need to relocate the player
5572     return;
5573
5574   if (IS_PLAYER(jx, jy))        // player already placed at new position
5575   {
5576     RemoveField(jx, jy);        // temporarily remove newly placed player
5577     DrawLevelField(jx, jy);
5578   }
5579
5580   if (player->present)
5581   {
5582     while (player->MovPos)
5583     {
5584       ScrollPlayer(player, SCROLL_GO_ON);
5585       ScrollScreen(NULL, SCROLL_GO_ON);
5586
5587       AdvanceFrameAndPlayerCounters(player->index_nr);
5588
5589       DrawPlayer(player);
5590
5591       BackToFront_WithFrameDelay(wait_delay_value);
5592     }
5593
5594     DrawPlayer(player);         // needed here only to cleanup last field
5595     DrawLevelField(player->jx, player->jy);     // remove player graphic
5596
5597     player->is_moving = FALSE;
5598   }
5599
5600   if (IS_CUSTOM_ELEMENT(old_element))
5601     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5602                                CE_LEFT_BY_PLAYER,
5603                                player->index_bit, leave_side);
5604
5605   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5606                                       CE_PLAYER_LEAVES_X,
5607                                       player->index_bit, leave_side);
5608
5609   Tile[jx][jy] = el_player;
5610   InitPlayerField(jx, jy, el_player, TRUE);
5611
5612   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5613      possible that the relocation target field did not contain a player element,
5614      but a walkable element, to which the new player was relocated -- in this
5615      case, restore that (already initialized!) element on the player field */
5616   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5617   {
5618     Tile[jx][jy] = element;     // restore previously existing element
5619   }
5620
5621   // only visually relocate centered player
5622   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5623                      FALSE, level.instant_relocation);
5624
5625   TestIfPlayerTouchesBadThing(jx, jy);
5626   TestIfPlayerTouchesCustomElement(jx, jy);
5627
5628   if (IS_CUSTOM_ELEMENT(element))
5629     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5630                                player->index_bit, enter_side);
5631
5632   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5633                                       player->index_bit, enter_side);
5634
5635   if (player->is_switching)
5636   {
5637     /* ensure that relocation while still switching an element does not cause
5638        a new element to be treated as also switched directly after relocation
5639        (this is important for teleporter switches that teleport the player to
5640        a place where another teleporter switch is in the same direction, which
5641        would then incorrectly be treated as immediately switched before the
5642        direction key that caused the switch was released) */
5643
5644     player->switch_x += jx - old_jx;
5645     player->switch_y += jy - old_jy;
5646   }
5647 }
5648
5649 static void Explode(int ex, int ey, int phase, int mode)
5650 {
5651   int x, y;
5652   int last_phase;
5653   int border_element;
5654
5655   // !!! eliminate this variable !!!
5656   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5657
5658   if (game.explosions_delayed)
5659   {
5660     ExplodeField[ex][ey] = mode;
5661     return;
5662   }
5663
5664   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5665   {
5666     int center_element = Tile[ex][ey];
5667     int artwork_element, explosion_element;     // set these values later
5668
5669     // remove things displayed in background while burning dynamite
5670     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5671       Back[ex][ey] = 0;
5672
5673     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5674     {
5675       // put moving element to center field (and let it explode there)
5676       center_element = MovingOrBlocked2Element(ex, ey);
5677       RemoveMovingField(ex, ey);
5678       Tile[ex][ey] = center_element;
5679     }
5680
5681     // now "center_element" is finally determined -- set related values now
5682     artwork_element = center_element;           // for custom player artwork
5683     explosion_element = center_element;         // for custom player artwork
5684
5685     if (IS_PLAYER(ex, ey))
5686     {
5687       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5688
5689       artwork_element = stored_player[player_nr].artwork_element;
5690
5691       if (level.use_explosion_element[player_nr])
5692       {
5693         explosion_element = level.explosion_element[player_nr];
5694         artwork_element = explosion_element;
5695       }
5696     }
5697
5698     if (mode == EX_TYPE_NORMAL ||
5699         mode == EX_TYPE_CENTER ||
5700         mode == EX_TYPE_CROSS)
5701       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5702
5703     last_phase = element_info[explosion_element].explosion_delay + 1;
5704
5705     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5706     {
5707       int xx = x - ex + 1;
5708       int yy = y - ey + 1;
5709       int element;
5710
5711       if (!IN_LEV_FIELD(x, y) ||
5712           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5713           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5714         continue;
5715
5716       element = Tile[x][y];
5717
5718       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5719       {
5720         element = MovingOrBlocked2Element(x, y);
5721
5722         if (!IS_EXPLOSION_PROOF(element))
5723           RemoveMovingField(x, y);
5724       }
5725
5726       // indestructible elements can only explode in center (but not flames)
5727       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5728                                            mode == EX_TYPE_BORDER)) ||
5729           element == EL_FLAMES)
5730         continue;
5731
5732       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5733          behaviour, for example when touching a yamyam that explodes to rocks
5734          with active deadly shield, a rock is created under the player !!! */
5735       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5736 #if 0
5737       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5738           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5739            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5740 #else
5741       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5742 #endif
5743       {
5744         if (IS_ACTIVE_BOMB(element))
5745         {
5746           // re-activate things under the bomb like gate or penguin
5747           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5748           Back[x][y] = 0;
5749         }
5750
5751         continue;
5752       }
5753
5754       // save walkable background elements while explosion on same tile
5755       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5756           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5757         Back[x][y] = element;
5758
5759       // ignite explodable elements reached by other explosion
5760       if (element == EL_EXPLOSION)
5761         element = Store2[x][y];
5762
5763       if (AmoebaNr[x][y] &&
5764           (element == EL_AMOEBA_FULL ||
5765            element == EL_BD_AMOEBA ||
5766            element == EL_AMOEBA_GROWING))
5767       {
5768         AmoebaCnt[AmoebaNr[x][y]]--;
5769         AmoebaCnt2[AmoebaNr[x][y]]--;
5770       }
5771
5772       RemoveField(x, y);
5773
5774       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5775       {
5776         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5777
5778         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5779
5780         if (PLAYERINFO(ex, ey)->use_murphy)
5781           Store[x][y] = EL_EMPTY;
5782       }
5783
5784       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5785       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5786       else if (ELEM_IS_PLAYER(center_element))
5787         Store[x][y] = EL_EMPTY;
5788       else if (center_element == EL_YAMYAM)
5789         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5790       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5791         Store[x][y] = element_info[center_element].content.e[xx][yy];
5792 #if 1
5793       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5794       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5795       // otherwise) -- FIX THIS !!!
5796       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5797         Store[x][y] = element_info[element].content.e[1][1];
5798 #else
5799       else if (!CAN_EXPLODE(element))
5800         Store[x][y] = element_info[element].content.e[1][1];
5801 #endif
5802       else
5803         Store[x][y] = EL_EMPTY;
5804
5805       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5806           center_element == EL_AMOEBA_TO_DIAMOND)
5807         Store2[x][y] = element;
5808
5809       Tile[x][y] = EL_EXPLOSION;
5810       GfxElement[x][y] = artwork_element;
5811
5812       ExplodePhase[x][y] = 1;
5813       ExplodeDelay[x][y] = last_phase;
5814
5815       Stop[x][y] = TRUE;
5816     }
5817
5818     if (center_element == EL_YAMYAM)
5819       game.yamyam_content_nr =
5820         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5821
5822     return;
5823   }
5824
5825   if (Stop[ex][ey])
5826     return;
5827
5828   x = ex;
5829   y = ey;
5830
5831   if (phase == 1)
5832     GfxFrame[x][y] = 0;         // restart explosion animation
5833
5834   last_phase = ExplodeDelay[x][y];
5835
5836   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5837
5838   // this can happen if the player leaves an explosion just in time
5839   if (GfxElement[x][y] == EL_UNDEFINED)
5840     GfxElement[x][y] = EL_EMPTY;
5841
5842   border_element = Store2[x][y];
5843   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5844     border_element = StorePlayer[x][y];
5845
5846   if (phase == element_info[border_element].ignition_delay ||
5847       phase == last_phase)
5848   {
5849     boolean border_explosion = FALSE;
5850
5851     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5852         !PLAYER_EXPLOSION_PROTECTED(x, y))
5853     {
5854       KillPlayerUnlessExplosionProtected(x, y);
5855       border_explosion = TRUE;
5856     }
5857     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5858     {
5859       Tile[x][y] = Store2[x][y];
5860       Store2[x][y] = 0;
5861       Bang(x, y);
5862       border_explosion = TRUE;
5863     }
5864     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5865     {
5866       AmoebaToDiamond(x, y);
5867       Store2[x][y] = 0;
5868       border_explosion = TRUE;
5869     }
5870
5871     // if an element just explodes due to another explosion (chain-reaction),
5872     // do not immediately end the new explosion when it was the last frame of
5873     // the explosion (as it would be done in the following "if"-statement!)
5874     if (border_explosion && phase == last_phase)
5875       return;
5876   }
5877
5878   if (phase == last_phase)
5879   {
5880     int element;
5881
5882     element = Tile[x][y] = Store[x][y];
5883     Store[x][y] = Store2[x][y] = 0;
5884     GfxElement[x][y] = EL_UNDEFINED;
5885
5886     // player can escape from explosions and might therefore be still alive
5887     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5888         element <= EL_PLAYER_IS_EXPLODING_4)
5889     {
5890       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5891       int explosion_element = EL_PLAYER_1 + player_nr;
5892       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5893       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5894
5895       if (level.use_explosion_element[player_nr])
5896         explosion_element = level.explosion_element[player_nr];
5897
5898       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5899                     element_info[explosion_element].content.e[xx][yy]);
5900     }
5901
5902     // restore probably existing indestructible background element
5903     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5904       element = Tile[x][y] = Back[x][y];
5905     Back[x][y] = 0;
5906
5907     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5908     GfxDir[x][y] = MV_NONE;
5909     ChangeDelay[x][y] = 0;
5910     ChangePage[x][y] = -1;
5911
5912     CustomValue[x][y] = 0;
5913
5914     InitField_WithBug2(x, y, FALSE);
5915
5916     TEST_DrawLevelField(x, y);
5917
5918     TestIfElementTouchesCustomElement(x, y);
5919
5920     if (GFX_CRUMBLED(element))
5921       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5922
5923     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5924       StorePlayer[x][y] = 0;
5925
5926     if (ELEM_IS_PLAYER(element))
5927       RelocatePlayer(x, y, element);
5928   }
5929   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5930   {
5931     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5932     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5933
5934     if (phase == delay)
5935       TEST_DrawLevelFieldCrumbled(x, y);
5936
5937     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5938     {
5939       DrawLevelElement(x, y, Back[x][y]);
5940       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5941     }
5942     else if (IS_WALKABLE_UNDER(Back[x][y]))
5943     {
5944       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5945       DrawLevelElementThruMask(x, y, Back[x][y]);
5946     }
5947     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5948       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5949   }
5950 }
5951
5952 static void DynaExplode(int ex, int ey)
5953 {
5954   int i, j;
5955   int dynabomb_element = Tile[ex][ey];
5956   int dynabomb_size = 1;
5957   boolean dynabomb_xl = FALSE;
5958   struct PlayerInfo *player;
5959   static int xy[4][2] =
5960   {
5961     { 0, -1 },
5962     { -1, 0 },
5963     { +1, 0 },
5964     { 0, +1 }
5965   };
5966
5967   if (IS_ACTIVE_BOMB(dynabomb_element))
5968   {
5969     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5970     dynabomb_size = player->dynabomb_size;
5971     dynabomb_xl = player->dynabomb_xl;
5972     player->dynabombs_left++;
5973   }
5974
5975   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5976
5977   for (i = 0; i < NUM_DIRECTIONS; i++)
5978   {
5979     for (j = 1; j <= dynabomb_size; j++)
5980     {
5981       int x = ex + j * xy[i][0];
5982       int y = ey + j * xy[i][1];
5983       int element;
5984
5985       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5986         break;
5987
5988       element = Tile[x][y];
5989
5990       // do not restart explosions of fields with active bombs
5991       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5992         continue;
5993
5994       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5995
5996       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5997           !IS_DIGGABLE(element) && !dynabomb_xl)
5998         break;
5999     }
6000   }
6001 }
6002
6003 void Bang(int x, int y)
6004 {
6005   int element = MovingOrBlocked2Element(x, y);
6006   int explosion_type = EX_TYPE_NORMAL;
6007
6008   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6009   {
6010     struct PlayerInfo *player = PLAYERINFO(x, y);
6011
6012     element = Tile[x][y] = player->initial_element;
6013
6014     if (level.use_explosion_element[player->index_nr])
6015     {
6016       int explosion_element = level.explosion_element[player->index_nr];
6017
6018       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6019         explosion_type = EX_TYPE_CROSS;
6020       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6021         explosion_type = EX_TYPE_CENTER;
6022     }
6023   }
6024
6025   switch (element)
6026   {
6027     case EL_BUG:
6028     case EL_SPACESHIP:
6029     case EL_BD_BUTTERFLY:
6030     case EL_BD_FIREFLY:
6031     case EL_YAMYAM:
6032     case EL_DARK_YAMYAM:
6033     case EL_ROBOT:
6034     case EL_PACMAN:
6035     case EL_MOLE:
6036       RaiseScoreElement(element);
6037       break;
6038
6039     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6040     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6041     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6042     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6043     case EL_DYNABOMB_INCREASE_NUMBER:
6044     case EL_DYNABOMB_INCREASE_SIZE:
6045     case EL_DYNABOMB_INCREASE_POWER:
6046       explosion_type = EX_TYPE_DYNA;
6047       break;
6048
6049     case EL_DC_LANDMINE:
6050       explosion_type = EX_TYPE_CENTER;
6051       break;
6052
6053     case EL_PENGUIN:
6054     case EL_LAMP:
6055     case EL_LAMP_ACTIVE:
6056     case EL_AMOEBA_TO_DIAMOND:
6057       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6058         explosion_type = EX_TYPE_CENTER;
6059       break;
6060
6061     default:
6062       if (element_info[element].explosion_type == EXPLODES_CROSS)
6063         explosion_type = EX_TYPE_CROSS;
6064       else if (element_info[element].explosion_type == EXPLODES_1X1)
6065         explosion_type = EX_TYPE_CENTER;
6066       break;
6067   }
6068
6069   if (explosion_type == EX_TYPE_DYNA)
6070     DynaExplode(x, y);
6071   else
6072     Explode(x, y, EX_PHASE_START, explosion_type);
6073
6074   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6075 }
6076
6077 static void SplashAcid(int x, int y)
6078 {
6079   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6080       (!IN_LEV_FIELD(x - 1, y - 2) ||
6081        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6082     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6083
6084   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6085       (!IN_LEV_FIELD(x + 1, y - 2) ||
6086        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6087     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6088
6089   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6090 }
6091
6092 static void InitBeltMovement(void)
6093 {
6094   static int belt_base_element[4] =
6095   {
6096     EL_CONVEYOR_BELT_1_LEFT,
6097     EL_CONVEYOR_BELT_2_LEFT,
6098     EL_CONVEYOR_BELT_3_LEFT,
6099     EL_CONVEYOR_BELT_4_LEFT
6100   };
6101   static int belt_base_active_element[4] =
6102   {
6103     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6104     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6105     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6106     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6107   };
6108
6109   int x, y, i, j;
6110
6111   // set frame order for belt animation graphic according to belt direction
6112   for (i = 0; i < NUM_BELTS; i++)
6113   {
6114     int belt_nr = i;
6115
6116     for (j = 0; j < NUM_BELT_PARTS; j++)
6117     {
6118       int element = belt_base_active_element[belt_nr] + j;
6119       int graphic_1 = el2img(element);
6120       int graphic_2 = el2panelimg(element);
6121
6122       if (game.belt_dir[i] == MV_LEFT)
6123       {
6124         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6125         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6126       }
6127       else
6128       {
6129         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6130         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6131       }
6132     }
6133   }
6134
6135   SCAN_PLAYFIELD(x, y)
6136   {
6137     int element = Tile[x][y];
6138
6139     for (i = 0; i < NUM_BELTS; i++)
6140     {
6141       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6142       {
6143         int e_belt_nr = getBeltNrFromBeltElement(element);
6144         int belt_nr = i;
6145
6146         if (e_belt_nr == belt_nr)
6147         {
6148           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6149
6150           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6151         }
6152       }
6153     }
6154   }
6155 }
6156
6157 static void ToggleBeltSwitch(int x, int y)
6158 {
6159   static int belt_base_element[4] =
6160   {
6161     EL_CONVEYOR_BELT_1_LEFT,
6162     EL_CONVEYOR_BELT_2_LEFT,
6163     EL_CONVEYOR_BELT_3_LEFT,
6164     EL_CONVEYOR_BELT_4_LEFT
6165   };
6166   static int belt_base_active_element[4] =
6167   {
6168     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6169     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6170     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6171     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6172   };
6173   static int belt_base_switch_element[4] =
6174   {
6175     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6176     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6177     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6178     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6179   };
6180   static int belt_move_dir[4] =
6181   {
6182     MV_LEFT,
6183     MV_NONE,
6184     MV_RIGHT,
6185     MV_NONE,
6186   };
6187
6188   int element = Tile[x][y];
6189   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6190   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6191   int belt_dir = belt_move_dir[belt_dir_nr];
6192   int xx, yy, i;
6193
6194   if (!IS_BELT_SWITCH(element))
6195     return;
6196
6197   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6198   game.belt_dir[belt_nr] = belt_dir;
6199
6200   if (belt_dir_nr == 3)
6201     belt_dir_nr = 1;
6202
6203   // set frame order for belt animation graphic according to belt direction
6204   for (i = 0; i < NUM_BELT_PARTS; i++)
6205   {
6206     int element = belt_base_active_element[belt_nr] + i;
6207     int graphic_1 = el2img(element);
6208     int graphic_2 = el2panelimg(element);
6209
6210     if (belt_dir == MV_LEFT)
6211     {
6212       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6213       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6214     }
6215     else
6216     {
6217       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6218       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6219     }
6220   }
6221
6222   SCAN_PLAYFIELD(xx, yy)
6223   {
6224     int element = Tile[xx][yy];
6225
6226     if (IS_BELT_SWITCH(element))
6227     {
6228       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6229
6230       if (e_belt_nr == belt_nr)
6231       {
6232         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6233         TEST_DrawLevelField(xx, yy);
6234       }
6235     }
6236     else if (IS_BELT(element) && belt_dir != MV_NONE)
6237     {
6238       int e_belt_nr = getBeltNrFromBeltElement(element);
6239
6240       if (e_belt_nr == belt_nr)
6241       {
6242         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6243
6244         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6245         TEST_DrawLevelField(xx, yy);
6246       }
6247     }
6248     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6249     {
6250       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6251
6252       if (e_belt_nr == belt_nr)
6253       {
6254         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6255
6256         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6257         TEST_DrawLevelField(xx, yy);
6258       }
6259     }
6260   }
6261 }
6262
6263 static void ToggleSwitchgateSwitch(int x, int y)
6264 {
6265   int xx, yy;
6266
6267   game.switchgate_pos = !game.switchgate_pos;
6268
6269   SCAN_PLAYFIELD(xx, yy)
6270   {
6271     int element = Tile[xx][yy];
6272
6273     if (element == EL_SWITCHGATE_SWITCH_UP)
6274     {
6275       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6276       TEST_DrawLevelField(xx, yy);
6277     }
6278     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6279     {
6280       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6281       TEST_DrawLevelField(xx, yy);
6282     }
6283     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6284     {
6285       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6286       TEST_DrawLevelField(xx, yy);
6287     }
6288     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6289     {
6290       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6291       TEST_DrawLevelField(xx, yy);
6292     }
6293     else if (element == EL_SWITCHGATE_OPEN ||
6294              element == EL_SWITCHGATE_OPENING)
6295     {
6296       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6297
6298       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6299     }
6300     else if (element == EL_SWITCHGATE_CLOSED ||
6301              element == EL_SWITCHGATE_CLOSING)
6302     {
6303       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6304
6305       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6306     }
6307   }
6308 }
6309
6310 static int getInvisibleActiveFromInvisibleElement(int element)
6311 {
6312   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6313           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6314           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6315           element);
6316 }
6317
6318 static int getInvisibleFromInvisibleActiveElement(int element)
6319 {
6320   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6321           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6322           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6323           element);
6324 }
6325
6326 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6327 {
6328   int x, y;
6329
6330   SCAN_PLAYFIELD(x, y)
6331   {
6332     int element = Tile[x][y];
6333
6334     if (element == EL_LIGHT_SWITCH &&
6335         game.light_time_left > 0)
6336     {
6337       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6338       TEST_DrawLevelField(x, y);
6339     }
6340     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6341              game.light_time_left == 0)
6342     {
6343       Tile[x][y] = EL_LIGHT_SWITCH;
6344       TEST_DrawLevelField(x, y);
6345     }
6346     else if (element == EL_EMC_DRIPPER &&
6347              game.light_time_left > 0)
6348     {
6349       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6350       TEST_DrawLevelField(x, y);
6351     }
6352     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6353              game.light_time_left == 0)
6354     {
6355       Tile[x][y] = EL_EMC_DRIPPER;
6356       TEST_DrawLevelField(x, y);
6357     }
6358     else if (element == EL_INVISIBLE_STEELWALL ||
6359              element == EL_INVISIBLE_WALL ||
6360              element == EL_INVISIBLE_SAND)
6361     {
6362       if (game.light_time_left > 0)
6363         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6364
6365       TEST_DrawLevelField(x, y);
6366
6367       // uncrumble neighbour fields, if needed
6368       if (element == EL_INVISIBLE_SAND)
6369         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6370     }
6371     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6372              element == EL_INVISIBLE_WALL_ACTIVE ||
6373              element == EL_INVISIBLE_SAND_ACTIVE)
6374     {
6375       if (game.light_time_left == 0)
6376         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6377
6378       TEST_DrawLevelField(x, y);
6379
6380       // re-crumble neighbour fields, if needed
6381       if (element == EL_INVISIBLE_SAND)
6382         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6383     }
6384   }
6385 }
6386
6387 static void RedrawAllInvisibleElementsForLenses(void)
6388 {
6389   int x, y;
6390
6391   SCAN_PLAYFIELD(x, y)
6392   {
6393     int element = Tile[x][y];
6394
6395     if (element == EL_EMC_DRIPPER &&
6396         game.lenses_time_left > 0)
6397     {
6398       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6399       TEST_DrawLevelField(x, y);
6400     }
6401     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6402              game.lenses_time_left == 0)
6403     {
6404       Tile[x][y] = EL_EMC_DRIPPER;
6405       TEST_DrawLevelField(x, y);
6406     }
6407     else if (element == EL_INVISIBLE_STEELWALL ||
6408              element == EL_INVISIBLE_WALL ||
6409              element == EL_INVISIBLE_SAND)
6410     {
6411       if (game.lenses_time_left > 0)
6412         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6413
6414       TEST_DrawLevelField(x, y);
6415
6416       // uncrumble neighbour fields, if needed
6417       if (element == EL_INVISIBLE_SAND)
6418         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6419     }
6420     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6421              element == EL_INVISIBLE_WALL_ACTIVE ||
6422              element == EL_INVISIBLE_SAND_ACTIVE)
6423     {
6424       if (game.lenses_time_left == 0)
6425         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6426
6427       TEST_DrawLevelField(x, y);
6428
6429       // re-crumble neighbour fields, if needed
6430       if (element == EL_INVISIBLE_SAND)
6431         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6432     }
6433   }
6434 }
6435
6436 static void RedrawAllInvisibleElementsForMagnifier(void)
6437 {
6438   int x, y;
6439
6440   SCAN_PLAYFIELD(x, y)
6441   {
6442     int element = Tile[x][y];
6443
6444     if (element == EL_EMC_FAKE_GRASS &&
6445         game.magnify_time_left > 0)
6446     {
6447       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6448       TEST_DrawLevelField(x, y);
6449     }
6450     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6451              game.magnify_time_left == 0)
6452     {
6453       Tile[x][y] = EL_EMC_FAKE_GRASS;
6454       TEST_DrawLevelField(x, y);
6455     }
6456     else if (IS_GATE_GRAY(element) &&
6457              game.magnify_time_left > 0)
6458     {
6459       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6460                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6461                     IS_EM_GATE_GRAY(element) ?
6462                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6463                     IS_EMC_GATE_GRAY(element) ?
6464                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6465                     IS_DC_GATE_GRAY(element) ?
6466                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6467                     element);
6468       TEST_DrawLevelField(x, y);
6469     }
6470     else if (IS_GATE_GRAY_ACTIVE(element) &&
6471              game.magnify_time_left == 0)
6472     {
6473       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6474                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6475                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6476                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6477                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6478                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6479                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6480                     EL_DC_GATE_WHITE_GRAY :
6481                     element);
6482       TEST_DrawLevelField(x, y);
6483     }
6484   }
6485 }
6486
6487 static void ToggleLightSwitch(int x, int y)
6488 {
6489   int element = Tile[x][y];
6490
6491   game.light_time_left =
6492     (element == EL_LIGHT_SWITCH ?
6493      level.time_light * FRAMES_PER_SECOND : 0);
6494
6495   RedrawAllLightSwitchesAndInvisibleElements();
6496 }
6497
6498 static void ActivateTimegateSwitch(int x, int y)
6499 {
6500   int xx, yy;
6501
6502   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6503
6504   SCAN_PLAYFIELD(xx, yy)
6505   {
6506     int element = Tile[xx][yy];
6507
6508     if (element == EL_TIMEGATE_CLOSED ||
6509         element == EL_TIMEGATE_CLOSING)
6510     {
6511       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6512       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6513     }
6514
6515     /*
6516     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6517     {
6518       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6519       TEST_DrawLevelField(xx, yy);
6520     }
6521     */
6522
6523   }
6524
6525   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6526                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6527 }
6528
6529 static void Impact(int x, int y)
6530 {
6531   boolean last_line = (y == lev_fieldy - 1);
6532   boolean object_hit = FALSE;
6533   boolean impact = (last_line || object_hit);
6534   int element = Tile[x][y];
6535   int smashed = EL_STEELWALL;
6536
6537   if (!last_line)       // check if element below was hit
6538   {
6539     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6540       return;
6541
6542     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6543                                          MovDir[x][y + 1] != MV_DOWN ||
6544                                          MovPos[x][y + 1] <= TILEY / 2));
6545
6546     // do not smash moving elements that left the smashed field in time
6547     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6548         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6549       object_hit = FALSE;
6550
6551 #if USE_QUICKSAND_IMPACT_BUGFIX
6552     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6553     {
6554       RemoveMovingField(x, y + 1);
6555       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6556       Tile[x][y + 2] = EL_ROCK;
6557       TEST_DrawLevelField(x, y + 2);
6558
6559       object_hit = TRUE;
6560     }
6561
6562     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6563     {
6564       RemoveMovingField(x, y + 1);
6565       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6566       Tile[x][y + 2] = EL_ROCK;
6567       TEST_DrawLevelField(x, y + 2);
6568
6569       object_hit = TRUE;
6570     }
6571 #endif
6572
6573     if (object_hit)
6574       smashed = MovingOrBlocked2Element(x, y + 1);
6575
6576     impact = (last_line || object_hit);
6577   }
6578
6579   if (!last_line && smashed == EL_ACID) // element falls into acid
6580   {
6581     SplashAcid(x, y + 1);
6582     return;
6583   }
6584
6585   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6586   // only reset graphic animation if graphic really changes after impact
6587   if (impact &&
6588       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6589   {
6590     ResetGfxAnimation(x, y);
6591     TEST_DrawLevelField(x, y);
6592   }
6593
6594   if (impact && CAN_EXPLODE_IMPACT(element))
6595   {
6596     Bang(x, y);
6597     return;
6598   }
6599   else if (impact && element == EL_PEARL &&
6600            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6601   {
6602     ResetGfxAnimation(x, y);
6603
6604     Tile[x][y] = EL_PEARL_BREAKING;
6605     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6606     return;
6607   }
6608   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6609   {
6610     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6611
6612     return;
6613   }
6614
6615   if (impact && element == EL_AMOEBA_DROP)
6616   {
6617     if (object_hit && IS_PLAYER(x, y + 1))
6618       KillPlayerUnlessEnemyProtected(x, y + 1);
6619     else if (object_hit && smashed == EL_PENGUIN)
6620       Bang(x, y + 1);
6621     else
6622     {
6623       Tile[x][y] = EL_AMOEBA_GROWING;
6624       Store[x][y] = EL_AMOEBA_WET;
6625
6626       ResetRandomAnimationValue(x, y);
6627     }
6628     return;
6629   }
6630
6631   if (object_hit)               // check which object was hit
6632   {
6633     if ((CAN_PASS_MAGIC_WALL(element) && 
6634          (smashed == EL_MAGIC_WALL ||
6635           smashed == EL_BD_MAGIC_WALL)) ||
6636         (CAN_PASS_DC_MAGIC_WALL(element) &&
6637          smashed == EL_DC_MAGIC_WALL))
6638     {
6639       int xx, yy;
6640       int activated_magic_wall =
6641         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6642          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6643          EL_DC_MAGIC_WALL_ACTIVE);
6644
6645       // activate magic wall / mill
6646       SCAN_PLAYFIELD(xx, yy)
6647       {
6648         if (Tile[xx][yy] == smashed)
6649           Tile[xx][yy] = activated_magic_wall;
6650       }
6651
6652       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6653       game.magic_wall_active = TRUE;
6654
6655       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6656                             SND_MAGIC_WALL_ACTIVATING :
6657                             smashed == EL_BD_MAGIC_WALL ?
6658                             SND_BD_MAGIC_WALL_ACTIVATING :
6659                             SND_DC_MAGIC_WALL_ACTIVATING));
6660     }
6661
6662     if (IS_PLAYER(x, y + 1))
6663     {
6664       if (CAN_SMASH_PLAYER(element))
6665       {
6666         KillPlayerUnlessEnemyProtected(x, y + 1);
6667         return;
6668       }
6669     }
6670     else if (smashed == EL_PENGUIN)
6671     {
6672       if (CAN_SMASH_PLAYER(element))
6673       {
6674         Bang(x, y + 1);
6675         return;
6676       }
6677     }
6678     else if (element == EL_BD_DIAMOND)
6679     {
6680       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6681       {
6682         Bang(x, y + 1);
6683         return;
6684       }
6685     }
6686     else if (((element == EL_SP_INFOTRON ||
6687                element == EL_SP_ZONK) &&
6688               (smashed == EL_SP_SNIKSNAK ||
6689                smashed == EL_SP_ELECTRON ||
6690                smashed == EL_SP_DISK_ORANGE)) ||
6691              (element == EL_SP_INFOTRON &&
6692               smashed == EL_SP_DISK_YELLOW))
6693     {
6694       Bang(x, y + 1);
6695       return;
6696     }
6697     else if (CAN_SMASH_EVERYTHING(element))
6698     {
6699       if (IS_CLASSIC_ENEMY(smashed) ||
6700           CAN_EXPLODE_SMASHED(smashed))
6701       {
6702         Bang(x, y + 1);
6703         return;
6704       }
6705       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6706       {
6707         if (smashed == EL_LAMP ||
6708             smashed == EL_LAMP_ACTIVE)
6709         {
6710           Bang(x, y + 1);
6711           return;
6712         }
6713         else if (smashed == EL_NUT)
6714         {
6715           Tile[x][y + 1] = EL_NUT_BREAKING;
6716           PlayLevelSound(x, y, SND_NUT_BREAKING);
6717           RaiseScoreElement(EL_NUT);
6718           return;
6719         }
6720         else if (smashed == EL_PEARL)
6721         {
6722           ResetGfxAnimation(x, y);
6723
6724           Tile[x][y + 1] = EL_PEARL_BREAKING;
6725           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6726           return;
6727         }
6728         else if (smashed == EL_DIAMOND)
6729         {
6730           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6731           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6732           return;
6733         }
6734         else if (IS_BELT_SWITCH(smashed))
6735         {
6736           ToggleBeltSwitch(x, y + 1);
6737         }
6738         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6739                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6740                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6741                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6742         {
6743           ToggleSwitchgateSwitch(x, y + 1);
6744         }
6745         else if (smashed == EL_LIGHT_SWITCH ||
6746                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6747         {
6748           ToggleLightSwitch(x, y + 1);
6749         }
6750         else
6751         {
6752           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6753
6754           CheckElementChangeBySide(x, y + 1, smashed, element,
6755                                    CE_SWITCHED, CH_SIDE_TOP);
6756           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6757                                             CH_SIDE_TOP);
6758         }
6759       }
6760       else
6761       {
6762         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6763       }
6764     }
6765   }
6766
6767   // play sound of magic wall / mill
6768   if (!last_line &&
6769       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6770        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6771        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6772   {
6773     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6774       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6775     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6776       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6777     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6778       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6779
6780     return;
6781   }
6782
6783   // play sound of object that hits the ground
6784   if (last_line || object_hit)
6785     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6786 }
6787
6788 static void TurnRoundExt(int x, int y)
6789 {
6790   static struct
6791   {
6792     int dx, dy;
6793   } move_xy[] =
6794   {
6795     {  0,  0 },
6796     { -1,  0 },
6797     { +1,  0 },
6798     {  0,  0 },
6799     {  0, -1 },
6800     {  0,  0 }, { 0, 0 }, { 0, 0 },
6801     {  0, +1 }
6802   };
6803   static struct
6804   {
6805     int left, right, back;
6806   } turn[] =
6807   {
6808     { 0,        0,              0        },
6809     { MV_DOWN,  MV_UP,          MV_RIGHT },
6810     { MV_UP,    MV_DOWN,        MV_LEFT  },
6811     { 0,        0,              0        },
6812     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6813     { 0,        0,              0        },
6814     { 0,        0,              0        },
6815     { 0,        0,              0        },
6816     { MV_RIGHT, MV_LEFT,        MV_UP    }
6817   };
6818
6819   int element = Tile[x][y];
6820   int move_pattern = element_info[element].move_pattern;
6821
6822   int old_move_dir = MovDir[x][y];
6823   int left_dir  = turn[old_move_dir].left;
6824   int right_dir = turn[old_move_dir].right;
6825   int back_dir  = turn[old_move_dir].back;
6826
6827   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6828   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6829   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6830   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6831
6832   int left_x  = x + left_dx,  left_y  = y + left_dy;
6833   int right_x = x + right_dx, right_y = y + right_dy;
6834   int move_x  = x + move_dx,  move_y  = y + move_dy;
6835
6836   int xx, yy;
6837
6838   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6839   {
6840     TestIfBadThingTouchesOtherBadThing(x, y);
6841
6842     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6843       MovDir[x][y] = right_dir;
6844     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6845       MovDir[x][y] = left_dir;
6846
6847     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6848       MovDelay[x][y] = 9;
6849     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6850       MovDelay[x][y] = 1;
6851   }
6852   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6853   {
6854     TestIfBadThingTouchesOtherBadThing(x, y);
6855
6856     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6857       MovDir[x][y] = left_dir;
6858     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6859       MovDir[x][y] = right_dir;
6860
6861     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6862       MovDelay[x][y] = 9;
6863     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6864       MovDelay[x][y] = 1;
6865   }
6866   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6867   {
6868     TestIfBadThingTouchesOtherBadThing(x, y);
6869
6870     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6871       MovDir[x][y] = left_dir;
6872     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6873       MovDir[x][y] = right_dir;
6874
6875     if (MovDir[x][y] != old_move_dir)
6876       MovDelay[x][y] = 9;
6877   }
6878   else if (element == EL_YAMYAM)
6879   {
6880     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6881     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6882
6883     if (can_turn_left && can_turn_right)
6884       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6885     else if (can_turn_left)
6886       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6887     else if (can_turn_right)
6888       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6889     else
6890       MovDir[x][y] = back_dir;
6891
6892     MovDelay[x][y] = 16 + 16 * RND(3);
6893   }
6894   else if (element == EL_DARK_YAMYAM)
6895   {
6896     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6897                                                          left_x, left_y);
6898     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6899                                                          right_x, right_y);
6900
6901     if (can_turn_left && can_turn_right)
6902       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6903     else if (can_turn_left)
6904       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6905     else if (can_turn_right)
6906       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6907     else
6908       MovDir[x][y] = back_dir;
6909
6910     MovDelay[x][y] = 16 + 16 * RND(3);
6911   }
6912   else if (element == EL_PACMAN)
6913   {
6914     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6915     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6916
6917     if (can_turn_left && can_turn_right)
6918       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6919     else if (can_turn_left)
6920       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6921     else if (can_turn_right)
6922       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6923     else
6924       MovDir[x][y] = back_dir;
6925
6926     MovDelay[x][y] = 6 + RND(40);
6927   }
6928   else if (element == EL_PIG)
6929   {
6930     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6931     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6932     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6933     boolean should_turn_left, should_turn_right, should_move_on;
6934     int rnd_value = 24;
6935     int rnd = RND(rnd_value);
6936
6937     should_turn_left = (can_turn_left &&
6938                         (!can_move_on ||
6939                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6940                                                    y + back_dy + left_dy)));
6941     should_turn_right = (can_turn_right &&
6942                          (!can_move_on ||
6943                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6944                                                     y + back_dy + right_dy)));
6945     should_move_on = (can_move_on &&
6946                       (!can_turn_left ||
6947                        !can_turn_right ||
6948                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6949                                                  y + move_dy + left_dy) ||
6950                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6951                                                  y + move_dy + right_dy)));
6952
6953     if (should_turn_left || should_turn_right || should_move_on)
6954     {
6955       if (should_turn_left && should_turn_right && should_move_on)
6956         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6957                         rnd < 2 * rnd_value / 3 ? right_dir :
6958                         old_move_dir);
6959       else if (should_turn_left && should_turn_right)
6960         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6961       else if (should_turn_left && should_move_on)
6962         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6963       else if (should_turn_right && should_move_on)
6964         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6965       else if (should_turn_left)
6966         MovDir[x][y] = left_dir;
6967       else if (should_turn_right)
6968         MovDir[x][y] = right_dir;
6969       else if (should_move_on)
6970         MovDir[x][y] = old_move_dir;
6971     }
6972     else if (can_move_on && rnd > rnd_value / 8)
6973       MovDir[x][y] = old_move_dir;
6974     else if (can_turn_left && can_turn_right)
6975       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6976     else if (can_turn_left && rnd > rnd_value / 8)
6977       MovDir[x][y] = left_dir;
6978     else if (can_turn_right && rnd > rnd_value/8)
6979       MovDir[x][y] = right_dir;
6980     else
6981       MovDir[x][y] = back_dir;
6982
6983     xx = x + move_xy[MovDir[x][y]].dx;
6984     yy = y + move_xy[MovDir[x][y]].dy;
6985
6986     if (!IN_LEV_FIELD(xx, yy) ||
6987         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6988       MovDir[x][y] = old_move_dir;
6989
6990     MovDelay[x][y] = 0;
6991   }
6992   else if (element == EL_DRAGON)
6993   {
6994     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6995     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6996     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6997     int rnd_value = 24;
6998     int rnd = RND(rnd_value);
6999
7000     if (can_move_on && rnd > rnd_value / 8)
7001       MovDir[x][y] = old_move_dir;
7002     else if (can_turn_left && can_turn_right)
7003       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7004     else if (can_turn_left && rnd > rnd_value / 8)
7005       MovDir[x][y] = left_dir;
7006     else if (can_turn_right && rnd > rnd_value / 8)
7007       MovDir[x][y] = right_dir;
7008     else
7009       MovDir[x][y] = back_dir;
7010
7011     xx = x + move_xy[MovDir[x][y]].dx;
7012     yy = y + move_xy[MovDir[x][y]].dy;
7013
7014     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7015       MovDir[x][y] = old_move_dir;
7016
7017     MovDelay[x][y] = 0;
7018   }
7019   else if (element == EL_MOLE)
7020   {
7021     boolean can_move_on =
7022       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7023                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7024                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7025     if (!can_move_on)
7026     {
7027       boolean can_turn_left =
7028         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7029                               IS_AMOEBOID(Tile[left_x][left_y])));
7030
7031       boolean can_turn_right =
7032         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7033                               IS_AMOEBOID(Tile[right_x][right_y])));
7034
7035       if (can_turn_left && can_turn_right)
7036         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7037       else if (can_turn_left)
7038         MovDir[x][y] = left_dir;
7039       else
7040         MovDir[x][y] = right_dir;
7041     }
7042
7043     if (MovDir[x][y] != old_move_dir)
7044       MovDelay[x][y] = 9;
7045   }
7046   else if (element == EL_BALLOON)
7047   {
7048     MovDir[x][y] = game.wind_direction;
7049     MovDelay[x][y] = 0;
7050   }
7051   else if (element == EL_SPRING)
7052   {
7053     if (MovDir[x][y] & MV_HORIZONTAL)
7054     {
7055       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7056           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7057       {
7058         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7059         ResetGfxAnimation(move_x, move_y);
7060         TEST_DrawLevelField(move_x, move_y);
7061
7062         MovDir[x][y] = back_dir;
7063       }
7064       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7065                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7066         MovDir[x][y] = MV_NONE;
7067     }
7068
7069     MovDelay[x][y] = 0;
7070   }
7071   else if (element == EL_ROBOT ||
7072            element == EL_SATELLITE ||
7073            element == EL_PENGUIN ||
7074            element == EL_EMC_ANDROID)
7075   {
7076     int attr_x = -1, attr_y = -1;
7077
7078     if (game.all_players_gone)
7079     {
7080       attr_x = game.exit_x;
7081       attr_y = game.exit_y;
7082     }
7083     else
7084     {
7085       int i;
7086
7087       for (i = 0; i < MAX_PLAYERS; i++)
7088       {
7089         struct PlayerInfo *player = &stored_player[i];
7090         int jx = player->jx, jy = player->jy;
7091
7092         if (!player->active)
7093           continue;
7094
7095         if (attr_x == -1 ||
7096             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7097         {
7098           attr_x = jx;
7099           attr_y = jy;
7100         }
7101       }
7102     }
7103
7104     if (element == EL_ROBOT &&
7105         game.robot_wheel_x >= 0 &&
7106         game.robot_wheel_y >= 0 &&
7107         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7108          game.engine_version < VERSION_IDENT(3,1,0,0)))
7109     {
7110       attr_x = game.robot_wheel_x;
7111       attr_y = game.robot_wheel_y;
7112     }
7113
7114     if (element == EL_PENGUIN)
7115     {
7116       int i;
7117       static int xy[4][2] =
7118       {
7119         { 0, -1 },
7120         { -1, 0 },
7121         { +1, 0 },
7122         { 0, +1 }
7123       };
7124
7125       for (i = 0; i < NUM_DIRECTIONS; i++)
7126       {
7127         int ex = x + xy[i][0];
7128         int ey = y + xy[i][1];
7129
7130         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7131                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7132                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7133                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7134         {
7135           attr_x = ex;
7136           attr_y = ey;
7137           break;
7138         }
7139       }
7140     }
7141
7142     MovDir[x][y] = MV_NONE;
7143     if (attr_x < x)
7144       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7145     else if (attr_x > x)
7146       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7147     if (attr_y < y)
7148       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7149     else if (attr_y > y)
7150       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7151
7152     if (element == EL_ROBOT)
7153     {
7154       int newx, newy;
7155
7156       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7157         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7158       Moving2Blocked(x, y, &newx, &newy);
7159
7160       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7161         MovDelay[x][y] = 8 + 8 * !RND(3);
7162       else
7163         MovDelay[x][y] = 16;
7164     }
7165     else if (element == EL_PENGUIN)
7166     {
7167       int newx, newy;
7168
7169       MovDelay[x][y] = 1;
7170
7171       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7172       {
7173         boolean first_horiz = RND(2);
7174         int new_move_dir = MovDir[x][y];
7175
7176         MovDir[x][y] =
7177           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7178         Moving2Blocked(x, y, &newx, &newy);
7179
7180         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7181           return;
7182
7183         MovDir[x][y] =
7184           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7185         Moving2Blocked(x, y, &newx, &newy);
7186
7187         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7188           return;
7189
7190         MovDir[x][y] = old_move_dir;
7191         return;
7192       }
7193     }
7194     else if (element == EL_SATELLITE)
7195     {
7196       int newx, newy;
7197
7198       MovDelay[x][y] = 1;
7199
7200       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7201       {
7202         boolean first_horiz = RND(2);
7203         int new_move_dir = MovDir[x][y];
7204
7205         MovDir[x][y] =
7206           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7207         Moving2Blocked(x, y, &newx, &newy);
7208
7209         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7210           return;
7211
7212         MovDir[x][y] =
7213           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7214         Moving2Blocked(x, y, &newx, &newy);
7215
7216         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7217           return;
7218
7219         MovDir[x][y] = old_move_dir;
7220         return;
7221       }
7222     }
7223     else if (element == EL_EMC_ANDROID)
7224     {
7225       static int check_pos[16] =
7226       {
7227         -1,             //  0 => (invalid)
7228         7,              //  1 => MV_LEFT
7229         3,              //  2 => MV_RIGHT
7230         -1,             //  3 => (invalid)
7231         1,              //  4 =>            MV_UP
7232         0,              //  5 => MV_LEFT  | MV_UP
7233         2,              //  6 => MV_RIGHT | MV_UP
7234         -1,             //  7 => (invalid)
7235         5,              //  8 =>            MV_DOWN
7236         6,              //  9 => MV_LEFT  | MV_DOWN
7237         4,              // 10 => MV_RIGHT | MV_DOWN
7238         -1,             // 11 => (invalid)
7239         -1,             // 12 => (invalid)
7240         -1,             // 13 => (invalid)
7241         -1,             // 14 => (invalid)
7242         -1,             // 15 => (invalid)
7243       };
7244       static struct
7245       {
7246         int dx, dy;
7247         int dir;
7248       } check_xy[8] =
7249       {
7250         { -1, -1,       MV_LEFT  | MV_UP   },
7251         {  0, -1,                  MV_UP   },
7252         { +1, -1,       MV_RIGHT | MV_UP   },
7253         { +1,  0,       MV_RIGHT           },
7254         { +1, +1,       MV_RIGHT | MV_DOWN },
7255         {  0, +1,                  MV_DOWN },
7256         { -1, +1,       MV_LEFT  | MV_DOWN },
7257         { -1,  0,       MV_LEFT            },
7258       };
7259       int start_pos, check_order;
7260       boolean can_clone = FALSE;
7261       int i;
7262
7263       // check if there is any free field around current position
7264       for (i = 0; i < 8; i++)
7265       {
7266         int newx = x + check_xy[i].dx;
7267         int newy = y + check_xy[i].dy;
7268
7269         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7270         {
7271           can_clone = TRUE;
7272
7273           break;
7274         }
7275       }
7276
7277       if (can_clone)            // randomly find an element to clone
7278       {
7279         can_clone = FALSE;
7280
7281         start_pos = check_pos[RND(8)];
7282         check_order = (RND(2) ? -1 : +1);
7283
7284         for (i = 0; i < 8; i++)
7285         {
7286           int pos_raw = start_pos + i * check_order;
7287           int pos = (pos_raw + 8) % 8;
7288           int newx = x + check_xy[pos].dx;
7289           int newy = y + check_xy[pos].dy;
7290
7291           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7292           {
7293             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7294             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7295
7296             Store[x][y] = Tile[newx][newy];
7297
7298             can_clone = TRUE;
7299
7300             break;
7301           }
7302         }
7303       }
7304
7305       if (can_clone)            // randomly find a direction to move
7306       {
7307         can_clone = FALSE;
7308
7309         start_pos = check_pos[RND(8)];
7310         check_order = (RND(2) ? -1 : +1);
7311
7312         for (i = 0; i < 8; i++)
7313         {
7314           int pos_raw = start_pos + i * check_order;
7315           int pos = (pos_raw + 8) % 8;
7316           int newx = x + check_xy[pos].dx;
7317           int newy = y + check_xy[pos].dy;
7318           int new_move_dir = check_xy[pos].dir;
7319
7320           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7321           {
7322             MovDir[x][y] = new_move_dir;
7323             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7324
7325             can_clone = TRUE;
7326
7327             break;
7328           }
7329         }
7330       }
7331
7332       if (can_clone)            // cloning and moving successful
7333         return;
7334
7335       // cannot clone -- try to move towards player
7336
7337       start_pos = check_pos[MovDir[x][y] & 0x0f];
7338       check_order = (RND(2) ? -1 : +1);
7339
7340       for (i = 0; i < 3; i++)
7341       {
7342         // first check start_pos, then previous/next or (next/previous) pos
7343         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7344         int pos = (pos_raw + 8) % 8;
7345         int newx = x + check_xy[pos].dx;
7346         int newy = y + check_xy[pos].dy;
7347         int new_move_dir = check_xy[pos].dir;
7348
7349         if (IS_PLAYER(newx, newy))
7350           break;
7351
7352         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7353         {
7354           MovDir[x][y] = new_move_dir;
7355           MovDelay[x][y] = level.android_move_time * 8 + 1;
7356
7357           break;
7358         }
7359       }
7360     }
7361   }
7362   else if (move_pattern == MV_TURNING_LEFT ||
7363            move_pattern == MV_TURNING_RIGHT ||
7364            move_pattern == MV_TURNING_LEFT_RIGHT ||
7365            move_pattern == MV_TURNING_RIGHT_LEFT ||
7366            move_pattern == MV_TURNING_RANDOM ||
7367            move_pattern == MV_ALL_DIRECTIONS)
7368   {
7369     boolean can_turn_left =
7370       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7371     boolean can_turn_right =
7372       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7373
7374     if (element_info[element].move_stepsize == 0)       // "not moving"
7375       return;
7376
7377     if (move_pattern == MV_TURNING_LEFT)
7378       MovDir[x][y] = left_dir;
7379     else if (move_pattern == MV_TURNING_RIGHT)
7380       MovDir[x][y] = right_dir;
7381     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7382       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7383     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7384       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7385     else if (move_pattern == MV_TURNING_RANDOM)
7386       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7387                       can_turn_right && !can_turn_left ? right_dir :
7388                       RND(2) ? left_dir : right_dir);
7389     else if (can_turn_left && can_turn_right)
7390       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7391     else if (can_turn_left)
7392       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7393     else if (can_turn_right)
7394       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7395     else
7396       MovDir[x][y] = back_dir;
7397
7398     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7399   }
7400   else if (move_pattern == MV_HORIZONTAL ||
7401            move_pattern == MV_VERTICAL)
7402   {
7403     if (move_pattern & old_move_dir)
7404       MovDir[x][y] = back_dir;
7405     else if (move_pattern == MV_HORIZONTAL)
7406       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7407     else if (move_pattern == MV_VERTICAL)
7408       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7409
7410     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7411   }
7412   else if (move_pattern & MV_ANY_DIRECTION)
7413   {
7414     MovDir[x][y] = move_pattern;
7415     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7416   }
7417   else if (move_pattern & MV_WIND_DIRECTION)
7418   {
7419     MovDir[x][y] = game.wind_direction;
7420     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7421   }
7422   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7423   {
7424     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7425       MovDir[x][y] = left_dir;
7426     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7427       MovDir[x][y] = right_dir;
7428
7429     if (MovDir[x][y] != old_move_dir)
7430       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7431   }
7432   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7433   {
7434     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7435       MovDir[x][y] = right_dir;
7436     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7437       MovDir[x][y] = left_dir;
7438
7439     if (MovDir[x][y] != old_move_dir)
7440       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7441   }
7442   else if (move_pattern == MV_TOWARDS_PLAYER ||
7443            move_pattern == MV_AWAY_FROM_PLAYER)
7444   {
7445     int attr_x = -1, attr_y = -1;
7446     int newx, newy;
7447     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7448
7449     if (game.all_players_gone)
7450     {
7451       attr_x = game.exit_x;
7452       attr_y = game.exit_y;
7453     }
7454     else
7455     {
7456       int i;
7457
7458       for (i = 0; i < MAX_PLAYERS; i++)
7459       {
7460         struct PlayerInfo *player = &stored_player[i];
7461         int jx = player->jx, jy = player->jy;
7462
7463         if (!player->active)
7464           continue;
7465
7466         if (attr_x == -1 ||
7467             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7468         {
7469           attr_x = jx;
7470           attr_y = jy;
7471         }
7472       }
7473     }
7474
7475     MovDir[x][y] = MV_NONE;
7476     if (attr_x < x)
7477       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7478     else if (attr_x > x)
7479       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7480     if (attr_y < y)
7481       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7482     else if (attr_y > y)
7483       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7484
7485     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7486
7487     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7488     {
7489       boolean first_horiz = RND(2);
7490       int new_move_dir = MovDir[x][y];
7491
7492       if (element_info[element].move_stepsize == 0)     // "not moving"
7493       {
7494         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7495         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7496
7497         return;
7498       }
7499
7500       MovDir[x][y] =
7501         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7502       Moving2Blocked(x, y, &newx, &newy);
7503
7504       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7505         return;
7506
7507       MovDir[x][y] =
7508         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7509       Moving2Blocked(x, y, &newx, &newy);
7510
7511       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7512         return;
7513
7514       MovDir[x][y] = old_move_dir;
7515     }
7516   }
7517   else if (move_pattern == MV_WHEN_PUSHED ||
7518            move_pattern == MV_WHEN_DROPPED)
7519   {
7520     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7521       MovDir[x][y] = MV_NONE;
7522
7523     MovDelay[x][y] = 0;
7524   }
7525   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7526   {
7527     static int test_xy[7][2] =
7528     {
7529       { 0, -1 },
7530       { -1, 0 },
7531       { +1, 0 },
7532       { 0, +1 },
7533       { 0, -1 },
7534       { -1, 0 },
7535       { +1, 0 },
7536     };
7537     static int test_dir[7] =
7538     {
7539       MV_UP,
7540       MV_LEFT,
7541       MV_RIGHT,
7542       MV_DOWN,
7543       MV_UP,
7544       MV_LEFT,
7545       MV_RIGHT,
7546     };
7547     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7548     int move_preference = -1000000;     // start with very low preference
7549     int new_move_dir = MV_NONE;
7550     int start_test = RND(4);
7551     int i;
7552
7553     for (i = 0; i < NUM_DIRECTIONS; i++)
7554     {
7555       int move_dir = test_dir[start_test + i];
7556       int move_dir_preference;
7557
7558       xx = x + test_xy[start_test + i][0];
7559       yy = y + test_xy[start_test + i][1];
7560
7561       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7562           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7563       {
7564         new_move_dir = move_dir;
7565
7566         break;
7567       }
7568
7569       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7570         continue;
7571
7572       move_dir_preference = -1 * RunnerVisit[xx][yy];
7573       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7574         move_dir_preference = PlayerVisit[xx][yy];
7575
7576       if (move_dir_preference > move_preference)
7577       {
7578         // prefer field that has not been visited for the longest time
7579         move_preference = move_dir_preference;
7580         new_move_dir = move_dir;
7581       }
7582       else if (move_dir_preference == move_preference &&
7583                move_dir == old_move_dir)
7584       {
7585         // prefer last direction when all directions are preferred equally
7586         move_preference = move_dir_preference;
7587         new_move_dir = move_dir;
7588       }
7589     }
7590
7591     MovDir[x][y] = new_move_dir;
7592     if (old_move_dir != new_move_dir)
7593       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7594   }
7595 }
7596
7597 static void TurnRound(int x, int y)
7598 {
7599   int direction = MovDir[x][y];
7600
7601   TurnRoundExt(x, y);
7602
7603   GfxDir[x][y] = MovDir[x][y];
7604
7605   if (direction != MovDir[x][y])
7606     GfxFrame[x][y] = 0;
7607
7608   if (MovDelay[x][y])
7609     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7610
7611   ResetGfxFrame(x, y);
7612 }
7613
7614 static boolean JustBeingPushed(int x, int y)
7615 {
7616   int i;
7617
7618   for (i = 0; i < MAX_PLAYERS; i++)
7619   {
7620     struct PlayerInfo *player = &stored_player[i];
7621
7622     if (player->active && player->is_pushing && player->MovPos)
7623     {
7624       int next_jx = player->jx + (player->jx - player->last_jx);
7625       int next_jy = player->jy + (player->jy - player->last_jy);
7626
7627       if (x == next_jx && y == next_jy)
7628         return TRUE;
7629     }
7630   }
7631
7632   return FALSE;
7633 }
7634
7635 static void StartMoving(int x, int y)
7636 {
7637   boolean started_moving = FALSE;       // some elements can fall _and_ move
7638   int element = Tile[x][y];
7639
7640   if (Stop[x][y])
7641     return;
7642
7643   if (MovDelay[x][y] == 0)
7644     GfxAction[x][y] = ACTION_DEFAULT;
7645
7646   if (CAN_FALL(element) && y < lev_fieldy - 1)
7647   {
7648     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7649         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7650       if (JustBeingPushed(x, y))
7651         return;
7652
7653     if (element == EL_QUICKSAND_FULL)
7654     {
7655       if (IS_FREE(x, y + 1))
7656       {
7657         InitMovingField(x, y, MV_DOWN);
7658         started_moving = TRUE;
7659
7660         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7661 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7662         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7663           Store[x][y] = EL_ROCK;
7664 #else
7665         Store[x][y] = EL_ROCK;
7666 #endif
7667
7668         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7669       }
7670       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7671       {
7672         if (!MovDelay[x][y])
7673         {
7674           MovDelay[x][y] = TILEY + 1;
7675
7676           ResetGfxAnimation(x, y);
7677           ResetGfxAnimation(x, y + 1);
7678         }
7679
7680         if (MovDelay[x][y])
7681         {
7682           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7683           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7684
7685           MovDelay[x][y]--;
7686           if (MovDelay[x][y])
7687             return;
7688         }
7689
7690         Tile[x][y] = EL_QUICKSAND_EMPTY;
7691         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7692         Store[x][y + 1] = Store[x][y];
7693         Store[x][y] = 0;
7694
7695         PlayLevelSoundAction(x, y, ACTION_FILLING);
7696       }
7697       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7698       {
7699         if (!MovDelay[x][y])
7700         {
7701           MovDelay[x][y] = TILEY + 1;
7702
7703           ResetGfxAnimation(x, y);
7704           ResetGfxAnimation(x, y + 1);
7705         }
7706
7707         if (MovDelay[x][y])
7708         {
7709           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7710           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7711
7712           MovDelay[x][y]--;
7713           if (MovDelay[x][y])
7714             return;
7715         }
7716
7717         Tile[x][y] = EL_QUICKSAND_EMPTY;
7718         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7719         Store[x][y + 1] = Store[x][y];
7720         Store[x][y] = 0;
7721
7722         PlayLevelSoundAction(x, y, ACTION_FILLING);
7723       }
7724     }
7725     else if (element == EL_QUICKSAND_FAST_FULL)
7726     {
7727       if (IS_FREE(x, y + 1))
7728       {
7729         InitMovingField(x, y, MV_DOWN);
7730         started_moving = TRUE;
7731
7732         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7733 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7734         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7735           Store[x][y] = EL_ROCK;
7736 #else
7737         Store[x][y] = EL_ROCK;
7738 #endif
7739
7740         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7741       }
7742       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7743       {
7744         if (!MovDelay[x][y])
7745         {
7746           MovDelay[x][y] = TILEY + 1;
7747
7748           ResetGfxAnimation(x, y);
7749           ResetGfxAnimation(x, y + 1);
7750         }
7751
7752         if (MovDelay[x][y])
7753         {
7754           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7755           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7756
7757           MovDelay[x][y]--;
7758           if (MovDelay[x][y])
7759             return;
7760         }
7761
7762         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7763         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7764         Store[x][y + 1] = Store[x][y];
7765         Store[x][y] = 0;
7766
7767         PlayLevelSoundAction(x, y, ACTION_FILLING);
7768       }
7769       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7770       {
7771         if (!MovDelay[x][y])
7772         {
7773           MovDelay[x][y] = TILEY + 1;
7774
7775           ResetGfxAnimation(x, y);
7776           ResetGfxAnimation(x, y + 1);
7777         }
7778
7779         if (MovDelay[x][y])
7780         {
7781           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7782           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7783
7784           MovDelay[x][y]--;
7785           if (MovDelay[x][y])
7786             return;
7787         }
7788
7789         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7790         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7791         Store[x][y + 1] = Store[x][y];
7792         Store[x][y] = 0;
7793
7794         PlayLevelSoundAction(x, y, ACTION_FILLING);
7795       }
7796     }
7797     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7798              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7799     {
7800       InitMovingField(x, y, MV_DOWN);
7801       started_moving = TRUE;
7802
7803       Tile[x][y] = EL_QUICKSAND_FILLING;
7804       Store[x][y] = element;
7805
7806       PlayLevelSoundAction(x, y, ACTION_FILLING);
7807     }
7808     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7809              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7810     {
7811       InitMovingField(x, y, MV_DOWN);
7812       started_moving = TRUE;
7813
7814       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7815       Store[x][y] = element;
7816
7817       PlayLevelSoundAction(x, y, ACTION_FILLING);
7818     }
7819     else if (element == EL_MAGIC_WALL_FULL)
7820     {
7821       if (IS_FREE(x, y + 1))
7822       {
7823         InitMovingField(x, y, MV_DOWN);
7824         started_moving = TRUE;
7825
7826         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7827         Store[x][y] = EL_CHANGED(Store[x][y]);
7828       }
7829       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7830       {
7831         if (!MovDelay[x][y])
7832           MovDelay[x][y] = TILEY / 4 + 1;
7833
7834         if (MovDelay[x][y])
7835         {
7836           MovDelay[x][y]--;
7837           if (MovDelay[x][y])
7838             return;
7839         }
7840
7841         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7842         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7843         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7844         Store[x][y] = 0;
7845       }
7846     }
7847     else if (element == EL_BD_MAGIC_WALL_FULL)
7848     {
7849       if (IS_FREE(x, y + 1))
7850       {
7851         InitMovingField(x, y, MV_DOWN);
7852         started_moving = TRUE;
7853
7854         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7855         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7856       }
7857       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7858       {
7859         if (!MovDelay[x][y])
7860           MovDelay[x][y] = TILEY / 4 + 1;
7861
7862         if (MovDelay[x][y])
7863         {
7864           MovDelay[x][y]--;
7865           if (MovDelay[x][y])
7866             return;
7867         }
7868
7869         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7870         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7871         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7872         Store[x][y] = 0;
7873       }
7874     }
7875     else if (element == EL_DC_MAGIC_WALL_FULL)
7876     {
7877       if (IS_FREE(x, y + 1))
7878       {
7879         InitMovingField(x, y, MV_DOWN);
7880         started_moving = TRUE;
7881
7882         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7883         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7884       }
7885       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7886       {
7887         if (!MovDelay[x][y])
7888           MovDelay[x][y] = TILEY / 4 + 1;
7889
7890         if (MovDelay[x][y])
7891         {
7892           MovDelay[x][y]--;
7893           if (MovDelay[x][y])
7894             return;
7895         }
7896
7897         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7898         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7899         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7900         Store[x][y] = 0;
7901       }
7902     }
7903     else if ((CAN_PASS_MAGIC_WALL(element) &&
7904               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7905                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7906              (CAN_PASS_DC_MAGIC_WALL(element) &&
7907               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7908
7909     {
7910       InitMovingField(x, y, MV_DOWN);
7911       started_moving = TRUE;
7912
7913       Tile[x][y] =
7914         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7915          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7916          EL_DC_MAGIC_WALL_FILLING);
7917       Store[x][y] = element;
7918     }
7919     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7920     {
7921       SplashAcid(x, y + 1);
7922
7923       InitMovingField(x, y, MV_DOWN);
7924       started_moving = TRUE;
7925
7926       Store[x][y] = EL_ACID;
7927     }
7928     else if (
7929              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7930               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7931              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7932               CAN_FALL(element) && WasJustFalling[x][y] &&
7933               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7934
7935              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7936               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7937               (Tile[x][y + 1] == EL_BLOCKED)))
7938     {
7939       /* this is needed for a special case not covered by calling "Impact()"
7940          from "ContinueMoving()": if an element moves to a tile directly below
7941          another element which was just falling on that tile (which was empty
7942          in the previous frame), the falling element above would just stop
7943          instead of smashing the element below (in previous version, the above
7944          element was just checked for "moving" instead of "falling", resulting
7945          in incorrect smashes caused by horizontal movement of the above
7946          element; also, the case of the player being the element to smash was
7947          simply not covered here... :-/ ) */
7948
7949       CheckCollision[x][y] = 0;
7950       CheckImpact[x][y] = 0;
7951
7952       Impact(x, y);
7953     }
7954     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7955     {
7956       if (MovDir[x][y] == MV_NONE)
7957       {
7958         InitMovingField(x, y, MV_DOWN);
7959         started_moving = TRUE;
7960       }
7961     }
7962     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7963     {
7964       if (WasJustFalling[x][y]) // prevent animation from being restarted
7965         MovDir[x][y] = MV_DOWN;
7966
7967       InitMovingField(x, y, MV_DOWN);
7968       started_moving = TRUE;
7969     }
7970     else if (element == EL_AMOEBA_DROP)
7971     {
7972       Tile[x][y] = EL_AMOEBA_GROWING;
7973       Store[x][y] = EL_AMOEBA_WET;
7974     }
7975     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7976               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7977              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7978              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7979     {
7980       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7981                                 (IS_FREE(x - 1, y + 1) ||
7982                                  Tile[x - 1][y + 1] == EL_ACID));
7983       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7984                                 (IS_FREE(x + 1, y + 1) ||
7985                                  Tile[x + 1][y + 1] == EL_ACID));
7986       boolean can_fall_any  = (can_fall_left || can_fall_right);
7987       boolean can_fall_both = (can_fall_left && can_fall_right);
7988       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7989
7990       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7991       {
7992         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7993           can_fall_right = FALSE;
7994         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7995           can_fall_left = FALSE;
7996         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7997           can_fall_right = FALSE;
7998         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7999           can_fall_left = FALSE;
8000
8001         can_fall_any  = (can_fall_left || can_fall_right);
8002         can_fall_both = FALSE;
8003       }
8004
8005       if (can_fall_both)
8006       {
8007         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8008           can_fall_right = FALSE;       // slip down on left side
8009         else
8010           can_fall_left = !(can_fall_right = RND(2));
8011
8012         can_fall_both = FALSE;
8013       }
8014
8015       if (can_fall_any)
8016       {
8017         // if not determined otherwise, prefer left side for slipping down
8018         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8019         started_moving = TRUE;
8020       }
8021     }
8022     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8023     {
8024       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8025       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8026       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8027       int belt_dir = game.belt_dir[belt_nr];
8028
8029       if ((belt_dir == MV_LEFT  && left_is_free) ||
8030           (belt_dir == MV_RIGHT && right_is_free))
8031       {
8032         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8033
8034         InitMovingField(x, y, belt_dir);
8035         started_moving = TRUE;
8036
8037         Pushed[x][y] = TRUE;
8038         Pushed[nextx][y] = TRUE;
8039
8040         GfxAction[x][y] = ACTION_DEFAULT;
8041       }
8042       else
8043       {
8044         MovDir[x][y] = 0;       // if element was moving, stop it
8045       }
8046     }
8047   }
8048
8049   // not "else if" because of elements that can fall and move (EL_SPRING)
8050   if (CAN_MOVE(element) && !started_moving)
8051   {
8052     int move_pattern = element_info[element].move_pattern;
8053     int newx, newy;
8054
8055     Moving2Blocked(x, y, &newx, &newy);
8056
8057     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8058       return;
8059
8060     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8061         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8062     {
8063       WasJustMoving[x][y] = 0;
8064       CheckCollision[x][y] = 0;
8065
8066       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8067
8068       if (Tile[x][y] != element)        // element has changed
8069         return;
8070     }
8071
8072     if (!MovDelay[x][y])        // start new movement phase
8073     {
8074       // all objects that can change their move direction after each step
8075       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8076
8077       if (element != EL_YAMYAM &&
8078           element != EL_DARK_YAMYAM &&
8079           element != EL_PACMAN &&
8080           !(move_pattern & MV_ANY_DIRECTION) &&
8081           move_pattern != MV_TURNING_LEFT &&
8082           move_pattern != MV_TURNING_RIGHT &&
8083           move_pattern != MV_TURNING_LEFT_RIGHT &&
8084           move_pattern != MV_TURNING_RIGHT_LEFT &&
8085           move_pattern != MV_TURNING_RANDOM)
8086       {
8087         TurnRound(x, y);
8088
8089         if (MovDelay[x][y] && (element == EL_BUG ||
8090                                element == EL_SPACESHIP ||
8091                                element == EL_SP_SNIKSNAK ||
8092                                element == EL_SP_ELECTRON ||
8093                                element == EL_MOLE))
8094           TEST_DrawLevelField(x, y);
8095       }
8096     }
8097
8098     if (MovDelay[x][y])         // wait some time before next movement
8099     {
8100       MovDelay[x][y]--;
8101
8102       if (element == EL_ROBOT ||
8103           element == EL_YAMYAM ||
8104           element == EL_DARK_YAMYAM)
8105       {
8106         DrawLevelElementAnimationIfNeeded(x, y, element);
8107         PlayLevelSoundAction(x, y, ACTION_WAITING);
8108       }
8109       else if (element == EL_SP_ELECTRON)
8110         DrawLevelElementAnimationIfNeeded(x, y, element);
8111       else if (element == EL_DRAGON)
8112       {
8113         int i;
8114         int dir = MovDir[x][y];
8115         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8116         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8117         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8118                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8119                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8120                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8121         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8122
8123         GfxAction[x][y] = ACTION_ATTACKING;
8124
8125         if (IS_PLAYER(x, y))
8126           DrawPlayerField(x, y);
8127         else
8128           TEST_DrawLevelField(x, y);
8129
8130         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8131
8132         for (i = 1; i <= 3; i++)
8133         {
8134           int xx = x + i * dx;
8135           int yy = y + i * dy;
8136           int sx = SCREENX(xx);
8137           int sy = SCREENY(yy);
8138           int flame_graphic = graphic + (i - 1);
8139
8140           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8141             break;
8142
8143           if (MovDelay[x][y])
8144           {
8145             int flamed = MovingOrBlocked2Element(xx, yy);
8146
8147             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8148               Bang(xx, yy);
8149             else
8150               RemoveMovingField(xx, yy);
8151
8152             ChangeDelay[xx][yy] = 0;
8153
8154             Tile[xx][yy] = EL_FLAMES;
8155
8156             if (IN_SCR_FIELD(sx, sy))
8157             {
8158               TEST_DrawLevelFieldCrumbled(xx, yy);
8159               DrawGraphic(sx, sy, flame_graphic, frame);
8160             }
8161           }
8162           else
8163           {
8164             if (Tile[xx][yy] == EL_FLAMES)
8165               Tile[xx][yy] = EL_EMPTY;
8166             TEST_DrawLevelField(xx, yy);
8167           }
8168         }
8169       }
8170
8171       if (MovDelay[x][y])       // element still has to wait some time
8172       {
8173         PlayLevelSoundAction(x, y, ACTION_WAITING);
8174
8175         return;
8176       }
8177     }
8178
8179     // now make next step
8180
8181     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8182
8183     if (DONT_COLLIDE_WITH(element) &&
8184         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8185         !PLAYER_ENEMY_PROTECTED(newx, newy))
8186     {
8187       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8188
8189       return;
8190     }
8191
8192     else if (CAN_MOVE_INTO_ACID(element) &&
8193              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8194              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8195              (MovDir[x][y] == MV_DOWN ||
8196               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8197     {
8198       SplashAcid(newx, newy);
8199       Store[x][y] = EL_ACID;
8200     }
8201     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8202     {
8203       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8204           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8205           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8206           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8207       {
8208         RemoveField(x, y);
8209         TEST_DrawLevelField(x, y);
8210
8211         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8212         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8213           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8214
8215         game.friends_still_needed--;
8216         if (!game.friends_still_needed &&
8217             !game.GameOver &&
8218             game.all_players_gone)
8219           LevelSolved();
8220
8221         return;
8222       }
8223       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8224       {
8225         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8226           TEST_DrawLevelField(newx, newy);
8227         else
8228           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8229       }
8230       else if (!IS_FREE(newx, newy))
8231       {
8232         GfxAction[x][y] = ACTION_WAITING;
8233
8234         if (IS_PLAYER(x, y))
8235           DrawPlayerField(x, y);
8236         else
8237           TEST_DrawLevelField(x, y);
8238
8239         return;
8240       }
8241     }
8242     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8243     {
8244       if (IS_FOOD_PIG(Tile[newx][newy]))
8245       {
8246         if (IS_MOVING(newx, newy))
8247           RemoveMovingField(newx, newy);
8248         else
8249         {
8250           Tile[newx][newy] = EL_EMPTY;
8251           TEST_DrawLevelField(newx, newy);
8252         }
8253
8254         PlayLevelSound(x, y, SND_PIG_DIGGING);
8255       }
8256       else if (!IS_FREE(newx, newy))
8257       {
8258         if (IS_PLAYER(x, y))
8259           DrawPlayerField(x, y);
8260         else
8261           TEST_DrawLevelField(x, y);
8262
8263         return;
8264       }
8265     }
8266     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8267     {
8268       if (Store[x][y] != EL_EMPTY)
8269       {
8270         boolean can_clone = FALSE;
8271         int xx, yy;
8272
8273         // check if element to clone is still there
8274         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8275         {
8276           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8277           {
8278             can_clone = TRUE;
8279
8280             break;
8281           }
8282         }
8283
8284         // cannot clone or target field not free anymore -- do not clone
8285         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8286           Store[x][y] = EL_EMPTY;
8287       }
8288
8289       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8290       {
8291         if (IS_MV_DIAGONAL(MovDir[x][y]))
8292         {
8293           int diagonal_move_dir = MovDir[x][y];
8294           int stored = Store[x][y];
8295           int change_delay = 8;
8296           int graphic;
8297
8298           // android is moving diagonally
8299
8300           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8301
8302           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8303           GfxElement[x][y] = EL_EMC_ANDROID;
8304           GfxAction[x][y] = ACTION_SHRINKING;
8305           GfxDir[x][y] = diagonal_move_dir;
8306           ChangeDelay[x][y] = change_delay;
8307
8308           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8309                                    GfxDir[x][y]);
8310
8311           DrawLevelGraphicAnimation(x, y, graphic);
8312           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8313
8314           if (Tile[newx][newy] == EL_ACID)
8315           {
8316             SplashAcid(newx, newy);
8317
8318             return;
8319           }
8320
8321           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8322
8323           Store[newx][newy] = EL_EMC_ANDROID;
8324           GfxElement[newx][newy] = EL_EMC_ANDROID;
8325           GfxAction[newx][newy] = ACTION_GROWING;
8326           GfxDir[newx][newy] = diagonal_move_dir;
8327           ChangeDelay[newx][newy] = change_delay;
8328
8329           graphic = el_act_dir2img(GfxElement[newx][newy],
8330                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8331
8332           DrawLevelGraphicAnimation(newx, newy, graphic);
8333           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8334
8335           return;
8336         }
8337         else
8338         {
8339           Tile[newx][newy] = EL_EMPTY;
8340           TEST_DrawLevelField(newx, newy);
8341
8342           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8343         }
8344       }
8345       else if (!IS_FREE(newx, newy))
8346       {
8347         return;
8348       }
8349     }
8350     else if (IS_CUSTOM_ELEMENT(element) &&
8351              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8352     {
8353       if (!DigFieldByCE(newx, newy, element))
8354         return;
8355
8356       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8357       {
8358         RunnerVisit[x][y] = FrameCounter;
8359         PlayerVisit[x][y] /= 8;         // expire player visit path
8360       }
8361     }
8362     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8363     {
8364       if (!IS_FREE(newx, newy))
8365       {
8366         if (IS_PLAYER(x, y))
8367           DrawPlayerField(x, y);
8368         else
8369           TEST_DrawLevelField(x, y);
8370
8371         return;
8372       }
8373       else
8374       {
8375         boolean wanna_flame = !RND(10);
8376         int dx = newx - x, dy = newy - y;
8377         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8378         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8379         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8380                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8381         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8382                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8383
8384         if ((wanna_flame ||
8385              IS_CLASSIC_ENEMY(element1) ||
8386              IS_CLASSIC_ENEMY(element2)) &&
8387             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8388             element1 != EL_FLAMES && element2 != EL_FLAMES)
8389         {
8390           ResetGfxAnimation(x, y);
8391           GfxAction[x][y] = ACTION_ATTACKING;
8392
8393           if (IS_PLAYER(x, y))
8394             DrawPlayerField(x, y);
8395           else
8396             TEST_DrawLevelField(x, y);
8397
8398           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8399
8400           MovDelay[x][y] = 50;
8401
8402           Tile[newx][newy] = EL_FLAMES;
8403           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8404             Tile[newx1][newy1] = EL_FLAMES;
8405           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8406             Tile[newx2][newy2] = EL_FLAMES;
8407
8408           return;
8409         }
8410       }
8411     }
8412     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8413              Tile[newx][newy] == EL_DIAMOND)
8414     {
8415       if (IS_MOVING(newx, newy))
8416         RemoveMovingField(newx, newy);
8417       else
8418       {
8419         Tile[newx][newy] = EL_EMPTY;
8420         TEST_DrawLevelField(newx, newy);
8421       }
8422
8423       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8424     }
8425     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8426              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8427     {
8428       if (AmoebaNr[newx][newy])
8429       {
8430         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8431         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8432             Tile[newx][newy] == EL_BD_AMOEBA)
8433           AmoebaCnt[AmoebaNr[newx][newy]]--;
8434       }
8435
8436       if (IS_MOVING(newx, newy))
8437       {
8438         RemoveMovingField(newx, newy);
8439       }
8440       else
8441       {
8442         Tile[newx][newy] = EL_EMPTY;
8443         TEST_DrawLevelField(newx, newy);
8444       }
8445
8446       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8447     }
8448     else if ((element == EL_PACMAN || element == EL_MOLE)
8449              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8450     {
8451       if (AmoebaNr[newx][newy])
8452       {
8453         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8454         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8455             Tile[newx][newy] == EL_BD_AMOEBA)
8456           AmoebaCnt[AmoebaNr[newx][newy]]--;
8457       }
8458
8459       if (element == EL_MOLE)
8460       {
8461         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8462         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8463
8464         ResetGfxAnimation(x, y);
8465         GfxAction[x][y] = ACTION_DIGGING;
8466         TEST_DrawLevelField(x, y);
8467
8468         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8469
8470         return;                         // wait for shrinking amoeba
8471       }
8472       else      // element == EL_PACMAN
8473       {
8474         Tile[newx][newy] = EL_EMPTY;
8475         TEST_DrawLevelField(newx, newy);
8476         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8477       }
8478     }
8479     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8480              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8481               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8482     {
8483       // wait for shrinking amoeba to completely disappear
8484       return;
8485     }
8486     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8487     {
8488       // object was running against a wall
8489
8490       TurnRound(x, y);
8491
8492       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8493         DrawLevelElementAnimation(x, y, element);
8494
8495       if (DONT_TOUCH(element))
8496         TestIfBadThingTouchesPlayer(x, y);
8497
8498       return;
8499     }
8500
8501     InitMovingField(x, y, MovDir[x][y]);
8502
8503     PlayLevelSoundAction(x, y, ACTION_MOVING);
8504   }
8505
8506   if (MovDir[x][y])
8507     ContinueMoving(x, y);
8508 }
8509
8510 void ContinueMoving(int x, int y)
8511 {
8512   int element = Tile[x][y];
8513   struct ElementInfo *ei = &element_info[element];
8514   int direction = MovDir[x][y];
8515   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8516   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8517   int newx = x + dx, newy = y + dy;
8518   int stored = Store[x][y];
8519   int stored_new = Store[newx][newy];
8520   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8521   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8522   boolean last_line = (newy == lev_fieldy - 1);
8523   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8524
8525   if (pushed_by_player)         // special case: moving object pushed by player
8526   {
8527     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8528   }
8529   else if (use_step_delay)      // special case: moving object has step delay
8530   {
8531     if (!MovDelay[x][y])
8532       MovPos[x][y] += getElementMoveStepsize(x, y);
8533
8534     if (MovDelay[x][y])
8535       MovDelay[x][y]--;
8536     else
8537       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8538
8539     if (MovDelay[x][y])
8540     {
8541       TEST_DrawLevelField(x, y);
8542
8543       return;   // element is still waiting
8544     }
8545   }
8546   else                          // normal case: generically moving object
8547   {
8548     MovPos[x][y] += getElementMoveStepsize(x, y);
8549   }
8550
8551   if (ABS(MovPos[x][y]) < TILEX)
8552   {
8553     TEST_DrawLevelField(x, y);
8554
8555     return;     // element is still moving
8556   }
8557
8558   // element reached destination field
8559
8560   Tile[x][y] = EL_EMPTY;
8561   Tile[newx][newy] = element;
8562   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8563
8564   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8565   {
8566     element = Tile[newx][newy] = EL_ACID;
8567   }
8568   else if (element == EL_MOLE)
8569   {
8570     Tile[x][y] = EL_SAND;
8571
8572     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8573   }
8574   else if (element == EL_QUICKSAND_FILLING)
8575   {
8576     element = Tile[newx][newy] = get_next_element(element);
8577     Store[newx][newy] = Store[x][y];
8578   }
8579   else if (element == EL_QUICKSAND_EMPTYING)
8580   {
8581     Tile[x][y] = get_next_element(element);
8582     element = Tile[newx][newy] = Store[x][y];
8583   }
8584   else if (element == EL_QUICKSAND_FAST_FILLING)
8585   {
8586     element = Tile[newx][newy] = get_next_element(element);
8587     Store[newx][newy] = Store[x][y];
8588   }
8589   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8590   {
8591     Tile[x][y] = get_next_element(element);
8592     element = Tile[newx][newy] = Store[x][y];
8593   }
8594   else if (element == EL_MAGIC_WALL_FILLING)
8595   {
8596     element = Tile[newx][newy] = get_next_element(element);
8597     if (!game.magic_wall_active)
8598       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8599     Store[newx][newy] = Store[x][y];
8600   }
8601   else if (element == EL_MAGIC_WALL_EMPTYING)
8602   {
8603     Tile[x][y] = get_next_element(element);
8604     if (!game.magic_wall_active)
8605       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8606     element = Tile[newx][newy] = Store[x][y];
8607
8608     InitField(newx, newy, FALSE);
8609   }
8610   else if (element == EL_BD_MAGIC_WALL_FILLING)
8611   {
8612     element = Tile[newx][newy] = get_next_element(element);
8613     if (!game.magic_wall_active)
8614       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8615     Store[newx][newy] = Store[x][y];
8616   }
8617   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8618   {
8619     Tile[x][y] = get_next_element(element);
8620     if (!game.magic_wall_active)
8621       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8622     element = Tile[newx][newy] = Store[x][y];
8623
8624     InitField(newx, newy, FALSE);
8625   }
8626   else if (element == EL_DC_MAGIC_WALL_FILLING)
8627   {
8628     element = Tile[newx][newy] = get_next_element(element);
8629     if (!game.magic_wall_active)
8630       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8631     Store[newx][newy] = Store[x][y];
8632   }
8633   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8634   {
8635     Tile[x][y] = get_next_element(element);
8636     if (!game.magic_wall_active)
8637       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8638     element = Tile[newx][newy] = Store[x][y];
8639
8640     InitField(newx, newy, FALSE);
8641   }
8642   else if (element == EL_AMOEBA_DROPPING)
8643   {
8644     Tile[x][y] = get_next_element(element);
8645     element = Tile[newx][newy] = Store[x][y];
8646   }
8647   else if (element == EL_SOKOBAN_OBJECT)
8648   {
8649     if (Back[x][y])
8650       Tile[x][y] = Back[x][y];
8651
8652     if (Back[newx][newy])
8653       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8654
8655     Back[x][y] = Back[newx][newy] = 0;
8656   }
8657
8658   Store[x][y] = EL_EMPTY;
8659   MovPos[x][y] = 0;
8660   MovDir[x][y] = 0;
8661   MovDelay[x][y] = 0;
8662
8663   MovDelay[newx][newy] = 0;
8664
8665   if (CAN_CHANGE_OR_HAS_ACTION(element))
8666   {
8667     // copy element change control values to new field
8668     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8669     ChangePage[newx][newy]  = ChangePage[x][y];
8670     ChangeCount[newx][newy] = ChangeCount[x][y];
8671     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8672   }
8673
8674   CustomValue[newx][newy] = CustomValue[x][y];
8675
8676   ChangeDelay[x][y] = 0;
8677   ChangePage[x][y] = -1;
8678   ChangeCount[x][y] = 0;
8679   ChangeEvent[x][y] = -1;
8680
8681   CustomValue[x][y] = 0;
8682
8683   // copy animation control values to new field
8684   GfxFrame[newx][newy]  = GfxFrame[x][y];
8685   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8686   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8687   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8688
8689   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8690
8691   // some elements can leave other elements behind after moving
8692   if (ei->move_leave_element != EL_EMPTY &&
8693       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8694       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8695   {
8696     int move_leave_element = ei->move_leave_element;
8697
8698     // this makes it possible to leave the removed element again
8699     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8700       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8701
8702     Tile[x][y] = move_leave_element;
8703
8704     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8705       MovDir[x][y] = direction;
8706
8707     InitField(x, y, FALSE);
8708
8709     if (GFX_CRUMBLED(Tile[x][y]))
8710       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8711
8712     if (ELEM_IS_PLAYER(move_leave_element))
8713       RelocatePlayer(x, y, move_leave_element);
8714   }
8715
8716   // do this after checking for left-behind element
8717   ResetGfxAnimation(x, y);      // reset animation values for old field
8718
8719   if (!CAN_MOVE(element) ||
8720       (CAN_FALL(element) && direction == MV_DOWN &&
8721        (element == EL_SPRING ||
8722         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8723         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8724     GfxDir[x][y] = MovDir[newx][newy] = 0;
8725
8726   TEST_DrawLevelField(x, y);
8727   TEST_DrawLevelField(newx, newy);
8728
8729   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8730
8731   // prevent pushed element from moving on in pushed direction
8732   if (pushed_by_player && CAN_MOVE(element) &&
8733       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8734       !(element_info[element].move_pattern & direction))
8735     TurnRound(newx, newy);
8736
8737   // prevent elements on conveyor belt from moving on in last direction
8738   if (pushed_by_conveyor && CAN_FALL(element) &&
8739       direction & MV_HORIZONTAL)
8740     MovDir[newx][newy] = 0;
8741
8742   if (!pushed_by_player)
8743   {
8744     int nextx = newx + dx, nexty = newy + dy;
8745     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8746
8747     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8748
8749     if (CAN_FALL(element) && direction == MV_DOWN)
8750       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8751
8752     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8753       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8754
8755     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8756       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8757   }
8758
8759   if (DONT_TOUCH(element))      // object may be nasty to player or others
8760   {
8761     TestIfBadThingTouchesPlayer(newx, newy);
8762     TestIfBadThingTouchesFriend(newx, newy);
8763
8764     if (!IS_CUSTOM_ELEMENT(element))
8765       TestIfBadThingTouchesOtherBadThing(newx, newy);
8766   }
8767   else if (element == EL_PENGUIN)
8768     TestIfFriendTouchesBadThing(newx, newy);
8769
8770   if (DONT_GET_HIT_BY(element))
8771   {
8772     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8773   }
8774
8775   // give the player one last chance (one more frame) to move away
8776   if (CAN_FALL(element) && direction == MV_DOWN &&
8777       (last_line || (!IS_FREE(x, newy + 1) &&
8778                      (!IS_PLAYER(x, newy + 1) ||
8779                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8780     Impact(x, newy);
8781
8782   if (pushed_by_player && !game.use_change_when_pushing_bug)
8783   {
8784     int push_side = MV_DIR_OPPOSITE(direction);
8785     struct PlayerInfo *player = PLAYERINFO(x, y);
8786
8787     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8788                                player->index_bit, push_side);
8789     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8790                                         player->index_bit, push_side);
8791   }
8792
8793   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8794     MovDelay[newx][newy] = 1;
8795
8796   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8797
8798   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8799   TestIfElementHitsCustomElement(newx, newy, direction);
8800   TestIfPlayerTouchesCustomElement(newx, newy);
8801   TestIfElementTouchesCustomElement(newx, newy);
8802
8803   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8804       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8805     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8806                              MV_DIR_OPPOSITE(direction));
8807 }
8808
8809 int AmoebaNeighbourNr(int ax, int ay)
8810 {
8811   int i;
8812   int element = Tile[ax][ay];
8813   int group_nr = 0;
8814   static int xy[4][2] =
8815   {
8816     { 0, -1 },
8817     { -1, 0 },
8818     { +1, 0 },
8819     { 0, +1 }
8820   };
8821
8822   for (i = 0; i < NUM_DIRECTIONS; i++)
8823   {
8824     int x = ax + xy[i][0];
8825     int y = ay + xy[i][1];
8826
8827     if (!IN_LEV_FIELD(x, y))
8828       continue;
8829
8830     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8831       group_nr = AmoebaNr[x][y];
8832   }
8833
8834   return group_nr;
8835 }
8836
8837 static void AmoebaMerge(int ax, int ay)
8838 {
8839   int i, x, y, xx, yy;
8840   int new_group_nr = AmoebaNr[ax][ay];
8841   static int xy[4][2] =
8842   {
8843     { 0, -1 },
8844     { -1, 0 },
8845     { +1, 0 },
8846     { 0, +1 }
8847   };
8848
8849   if (new_group_nr == 0)
8850     return;
8851
8852   for (i = 0; i < NUM_DIRECTIONS; i++)
8853   {
8854     x = ax + xy[i][0];
8855     y = ay + xy[i][1];
8856
8857     if (!IN_LEV_FIELD(x, y))
8858       continue;
8859
8860     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8861          Tile[x][y] == EL_BD_AMOEBA ||
8862          Tile[x][y] == EL_AMOEBA_DEAD) &&
8863         AmoebaNr[x][y] != new_group_nr)
8864     {
8865       int old_group_nr = AmoebaNr[x][y];
8866
8867       if (old_group_nr == 0)
8868         return;
8869
8870       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8871       AmoebaCnt[old_group_nr] = 0;
8872       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8873       AmoebaCnt2[old_group_nr] = 0;
8874
8875       SCAN_PLAYFIELD(xx, yy)
8876       {
8877         if (AmoebaNr[xx][yy] == old_group_nr)
8878           AmoebaNr[xx][yy] = new_group_nr;
8879       }
8880     }
8881   }
8882 }
8883
8884 void AmoebaToDiamond(int ax, int ay)
8885 {
8886   int i, x, y;
8887
8888   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8889   {
8890     int group_nr = AmoebaNr[ax][ay];
8891
8892 #ifdef DEBUG
8893     if (group_nr == 0)
8894     {
8895       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8896       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8897
8898       return;
8899     }
8900 #endif
8901
8902     SCAN_PLAYFIELD(x, y)
8903     {
8904       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8905       {
8906         AmoebaNr[x][y] = 0;
8907         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8908       }
8909     }
8910
8911     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8912                             SND_AMOEBA_TURNING_TO_GEM :
8913                             SND_AMOEBA_TURNING_TO_ROCK));
8914     Bang(ax, ay);
8915   }
8916   else
8917   {
8918     static int xy[4][2] =
8919     {
8920       { 0, -1 },
8921       { -1, 0 },
8922       { +1, 0 },
8923       { 0, +1 }
8924     };
8925
8926     for (i = 0; i < NUM_DIRECTIONS; i++)
8927     {
8928       x = ax + xy[i][0];
8929       y = ay + xy[i][1];
8930
8931       if (!IN_LEV_FIELD(x, y))
8932         continue;
8933
8934       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8935       {
8936         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8937                               SND_AMOEBA_TURNING_TO_GEM :
8938                               SND_AMOEBA_TURNING_TO_ROCK));
8939         Bang(x, y);
8940       }
8941     }
8942   }
8943 }
8944
8945 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8946 {
8947   int x, y;
8948   int group_nr = AmoebaNr[ax][ay];
8949   boolean done = FALSE;
8950
8951 #ifdef DEBUG
8952   if (group_nr == 0)
8953   {
8954     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8955     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8956
8957     return;
8958   }
8959 #endif
8960
8961   SCAN_PLAYFIELD(x, y)
8962   {
8963     if (AmoebaNr[x][y] == group_nr &&
8964         (Tile[x][y] == EL_AMOEBA_DEAD ||
8965          Tile[x][y] == EL_BD_AMOEBA ||
8966          Tile[x][y] == EL_AMOEBA_GROWING))
8967     {
8968       AmoebaNr[x][y] = 0;
8969       Tile[x][y] = new_element;
8970       InitField(x, y, FALSE);
8971       TEST_DrawLevelField(x, y);
8972       done = TRUE;
8973     }
8974   }
8975
8976   if (done)
8977     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8978                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8979                             SND_BD_AMOEBA_TURNING_TO_GEM));
8980 }
8981
8982 static void AmoebaGrowing(int x, int y)
8983 {
8984   static unsigned int sound_delay = 0;
8985   static unsigned int sound_delay_value = 0;
8986
8987   if (!MovDelay[x][y])          // start new growing cycle
8988   {
8989     MovDelay[x][y] = 7;
8990
8991     if (DelayReached(&sound_delay, sound_delay_value))
8992     {
8993       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8994       sound_delay_value = 30;
8995     }
8996   }
8997
8998   if (MovDelay[x][y])           // wait some time before growing bigger
8999   {
9000     MovDelay[x][y]--;
9001     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9002     {
9003       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9004                                            6 - MovDelay[x][y]);
9005
9006       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9007     }
9008
9009     if (!MovDelay[x][y])
9010     {
9011       Tile[x][y] = Store[x][y];
9012       Store[x][y] = 0;
9013       TEST_DrawLevelField(x, y);
9014     }
9015   }
9016 }
9017
9018 static void AmoebaShrinking(int x, int y)
9019 {
9020   static unsigned int sound_delay = 0;
9021   static unsigned int sound_delay_value = 0;
9022
9023   if (!MovDelay[x][y])          // start new shrinking cycle
9024   {
9025     MovDelay[x][y] = 7;
9026
9027     if (DelayReached(&sound_delay, sound_delay_value))
9028       sound_delay_value = 30;
9029   }
9030
9031   if (MovDelay[x][y])           // wait some time before shrinking
9032   {
9033     MovDelay[x][y]--;
9034     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9035     {
9036       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9037                                            6 - MovDelay[x][y]);
9038
9039       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9040     }
9041
9042     if (!MovDelay[x][y])
9043     {
9044       Tile[x][y] = EL_EMPTY;
9045       TEST_DrawLevelField(x, y);
9046
9047       // don't let mole enter this field in this cycle;
9048       // (give priority to objects falling to this field from above)
9049       Stop[x][y] = TRUE;
9050     }
9051   }
9052 }
9053
9054 static void AmoebaReproduce(int ax, int ay)
9055 {
9056   int i;
9057   int element = Tile[ax][ay];
9058   int graphic = el2img(element);
9059   int newax = ax, neway = ay;
9060   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9061   static int xy[4][2] =
9062   {
9063     { 0, -1 },
9064     { -1, 0 },
9065     { +1, 0 },
9066     { 0, +1 }
9067   };
9068
9069   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9070   {
9071     Tile[ax][ay] = EL_AMOEBA_DEAD;
9072     TEST_DrawLevelField(ax, ay);
9073     return;
9074   }
9075
9076   if (IS_ANIMATED(graphic))
9077     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9078
9079   if (!MovDelay[ax][ay])        // start making new amoeba field
9080     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9081
9082   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9083   {
9084     MovDelay[ax][ay]--;
9085     if (MovDelay[ax][ay])
9086       return;
9087   }
9088
9089   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9090   {
9091     int start = RND(4);
9092     int x = ax + xy[start][0];
9093     int y = ay + xy[start][1];
9094
9095     if (!IN_LEV_FIELD(x, y))
9096       return;
9097
9098     if (IS_FREE(x, y) ||
9099         CAN_GROW_INTO(Tile[x][y]) ||
9100         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9101         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9102     {
9103       newax = x;
9104       neway = y;
9105     }
9106
9107     if (newax == ax && neway == ay)
9108       return;
9109   }
9110   else                          // normal or "filled" (BD style) amoeba
9111   {
9112     int start = RND(4);
9113     boolean waiting_for_player = FALSE;
9114
9115     for (i = 0; i < NUM_DIRECTIONS; i++)
9116     {
9117       int j = (start + i) % 4;
9118       int x = ax + xy[j][0];
9119       int y = ay + xy[j][1];
9120
9121       if (!IN_LEV_FIELD(x, y))
9122         continue;
9123
9124       if (IS_FREE(x, y) ||
9125           CAN_GROW_INTO(Tile[x][y]) ||
9126           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9127           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9128       {
9129         newax = x;
9130         neway = y;
9131         break;
9132       }
9133       else if (IS_PLAYER(x, y))
9134         waiting_for_player = TRUE;
9135     }
9136
9137     if (newax == ax && neway == ay)             // amoeba cannot grow
9138     {
9139       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9140       {
9141         Tile[ax][ay] = EL_AMOEBA_DEAD;
9142         TEST_DrawLevelField(ax, ay);
9143         AmoebaCnt[AmoebaNr[ax][ay]]--;
9144
9145         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9146         {
9147           if (element == EL_AMOEBA_FULL)
9148             AmoebaToDiamond(ax, ay);
9149           else if (element == EL_BD_AMOEBA)
9150             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9151         }
9152       }
9153       return;
9154     }
9155     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9156     {
9157       // amoeba gets larger by growing in some direction
9158
9159       int new_group_nr = AmoebaNr[ax][ay];
9160
9161 #ifdef DEBUG
9162   if (new_group_nr == 0)
9163   {
9164     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9165           newax, neway);
9166     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9167
9168     return;
9169   }
9170 #endif
9171
9172       AmoebaNr[newax][neway] = new_group_nr;
9173       AmoebaCnt[new_group_nr]++;
9174       AmoebaCnt2[new_group_nr]++;
9175
9176       // if amoeba touches other amoeba(s) after growing, unify them
9177       AmoebaMerge(newax, neway);
9178
9179       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9180       {
9181         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9182         return;
9183       }
9184     }
9185   }
9186
9187   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9188       (neway == lev_fieldy - 1 && newax != ax))
9189   {
9190     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9191     Store[newax][neway] = element;
9192   }
9193   else if (neway == ay || element == EL_EMC_DRIPPER)
9194   {
9195     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9196
9197     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9198   }
9199   else
9200   {
9201     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9202     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9203     Store[ax][ay] = EL_AMOEBA_DROP;
9204     ContinueMoving(ax, ay);
9205     return;
9206   }
9207
9208   TEST_DrawLevelField(newax, neway);
9209 }
9210
9211 static void Life(int ax, int ay)
9212 {
9213   int x1, y1, x2, y2;
9214   int life_time = 40;
9215   int element = Tile[ax][ay];
9216   int graphic = el2img(element);
9217   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9218                          level.biomaze);
9219   boolean changed = FALSE;
9220
9221   if (IS_ANIMATED(graphic))
9222     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9223
9224   if (Stop[ax][ay])
9225     return;
9226
9227   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9228     MovDelay[ax][ay] = life_time;
9229
9230   if (MovDelay[ax][ay])         // wait some time before next cycle
9231   {
9232     MovDelay[ax][ay]--;
9233     if (MovDelay[ax][ay])
9234       return;
9235   }
9236
9237   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9238   {
9239     int xx = ax+x1, yy = ay+y1;
9240     int old_element = Tile[xx][yy];
9241     int num_neighbours = 0;
9242
9243     if (!IN_LEV_FIELD(xx, yy))
9244       continue;
9245
9246     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9247     {
9248       int x = xx+x2, y = yy+y2;
9249
9250       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9251         continue;
9252
9253       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9254       boolean is_neighbour = FALSE;
9255
9256       if (level.use_life_bugs)
9257         is_neighbour =
9258           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9259            (IS_FREE(x, y)                             &&  Stop[x][y]));
9260       else
9261         is_neighbour =
9262           (Last[x][y] == element || is_player_cell);
9263
9264       if (is_neighbour)
9265         num_neighbours++;
9266     }
9267
9268     boolean is_free = FALSE;
9269
9270     if (level.use_life_bugs)
9271       is_free = (IS_FREE(xx, yy));
9272     else
9273       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9274
9275     if (xx == ax && yy == ay)           // field in the middle
9276     {
9277       if (num_neighbours < life_parameter[0] ||
9278           num_neighbours > life_parameter[1])
9279       {
9280         Tile[xx][yy] = EL_EMPTY;
9281         if (Tile[xx][yy] != old_element)
9282           TEST_DrawLevelField(xx, yy);
9283         Stop[xx][yy] = TRUE;
9284         changed = TRUE;
9285       }
9286     }
9287     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9288     {                                   // free border field
9289       if (num_neighbours >= life_parameter[2] &&
9290           num_neighbours <= life_parameter[3])
9291       {
9292         Tile[xx][yy] = element;
9293         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9294         if (Tile[xx][yy] != old_element)
9295           TEST_DrawLevelField(xx, yy);
9296         Stop[xx][yy] = TRUE;
9297         changed = TRUE;
9298       }
9299     }
9300   }
9301
9302   if (changed)
9303     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9304                    SND_GAME_OF_LIFE_GROWING);
9305 }
9306
9307 static void InitRobotWheel(int x, int y)
9308 {
9309   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9310 }
9311
9312 static void RunRobotWheel(int x, int y)
9313 {
9314   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9315 }
9316
9317 static void StopRobotWheel(int x, int y)
9318 {
9319   if (game.robot_wheel_x == x &&
9320       game.robot_wheel_y == y)
9321   {
9322     game.robot_wheel_x = -1;
9323     game.robot_wheel_y = -1;
9324     game.robot_wheel_active = FALSE;
9325   }
9326 }
9327
9328 static void InitTimegateWheel(int x, int y)
9329 {
9330   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9331 }
9332
9333 static void RunTimegateWheel(int x, int y)
9334 {
9335   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9336 }
9337
9338 static void InitMagicBallDelay(int x, int y)
9339 {
9340   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9341 }
9342
9343 static void ActivateMagicBall(int bx, int by)
9344 {
9345   int x, y;
9346
9347   if (level.ball_random)
9348   {
9349     int pos_border = RND(8);    // select one of the eight border elements
9350     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9351     int xx = pos_content % 3;
9352     int yy = pos_content / 3;
9353
9354     x = bx - 1 + xx;
9355     y = by - 1 + yy;
9356
9357     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9358       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9359   }
9360   else
9361   {
9362     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9363     {
9364       int xx = x - bx + 1;
9365       int yy = y - by + 1;
9366
9367       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9368         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9369     }
9370   }
9371
9372   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9373 }
9374
9375 static void CheckExit(int x, int y)
9376 {
9377   if (game.gems_still_needed > 0 ||
9378       game.sokoban_fields_still_needed > 0 ||
9379       game.sokoban_objects_still_needed > 0 ||
9380       game.lights_still_needed > 0)
9381   {
9382     int element = Tile[x][y];
9383     int graphic = el2img(element);
9384
9385     if (IS_ANIMATED(graphic))
9386       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9387
9388     return;
9389   }
9390
9391   // do not re-open exit door closed after last player
9392   if (game.all_players_gone)
9393     return;
9394
9395   Tile[x][y] = EL_EXIT_OPENING;
9396
9397   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9398 }
9399
9400 static void CheckExitEM(int x, int y)
9401 {
9402   if (game.gems_still_needed > 0 ||
9403       game.sokoban_fields_still_needed > 0 ||
9404       game.sokoban_objects_still_needed > 0 ||
9405       game.lights_still_needed > 0)
9406   {
9407     int element = Tile[x][y];
9408     int graphic = el2img(element);
9409
9410     if (IS_ANIMATED(graphic))
9411       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9412
9413     return;
9414   }
9415
9416   // do not re-open exit door closed after last player
9417   if (game.all_players_gone)
9418     return;
9419
9420   Tile[x][y] = EL_EM_EXIT_OPENING;
9421
9422   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9423 }
9424
9425 static void CheckExitSteel(int x, int y)
9426 {
9427   if (game.gems_still_needed > 0 ||
9428       game.sokoban_fields_still_needed > 0 ||
9429       game.sokoban_objects_still_needed > 0 ||
9430       game.lights_still_needed > 0)
9431   {
9432     int element = Tile[x][y];
9433     int graphic = el2img(element);
9434
9435     if (IS_ANIMATED(graphic))
9436       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9437
9438     return;
9439   }
9440
9441   // do not re-open exit door closed after last player
9442   if (game.all_players_gone)
9443     return;
9444
9445   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9446
9447   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9448 }
9449
9450 static void CheckExitSteelEM(int x, int y)
9451 {
9452   if (game.gems_still_needed > 0 ||
9453       game.sokoban_fields_still_needed > 0 ||
9454       game.sokoban_objects_still_needed > 0 ||
9455       game.lights_still_needed > 0)
9456   {
9457     int element = Tile[x][y];
9458     int graphic = el2img(element);
9459
9460     if (IS_ANIMATED(graphic))
9461       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9462
9463     return;
9464   }
9465
9466   // do not re-open exit door closed after last player
9467   if (game.all_players_gone)
9468     return;
9469
9470   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9471
9472   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9473 }
9474
9475 static void CheckExitSP(int x, int y)
9476 {
9477   if (game.gems_still_needed > 0)
9478   {
9479     int element = Tile[x][y];
9480     int graphic = el2img(element);
9481
9482     if (IS_ANIMATED(graphic))
9483       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9484
9485     return;
9486   }
9487
9488   // do not re-open exit door closed after last player
9489   if (game.all_players_gone)
9490     return;
9491
9492   Tile[x][y] = EL_SP_EXIT_OPENING;
9493
9494   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9495 }
9496
9497 static void CloseAllOpenTimegates(void)
9498 {
9499   int x, y;
9500
9501   SCAN_PLAYFIELD(x, y)
9502   {
9503     int element = Tile[x][y];
9504
9505     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9506     {
9507       Tile[x][y] = EL_TIMEGATE_CLOSING;
9508
9509       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9510     }
9511   }
9512 }
9513
9514 static void DrawTwinkleOnField(int x, int y)
9515 {
9516   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9517     return;
9518
9519   if (Tile[x][y] == EL_BD_DIAMOND)
9520     return;
9521
9522   if (MovDelay[x][y] == 0)      // next animation frame
9523     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9524
9525   if (MovDelay[x][y] != 0)      // wait some time before next frame
9526   {
9527     MovDelay[x][y]--;
9528
9529     DrawLevelElementAnimation(x, y, Tile[x][y]);
9530
9531     if (MovDelay[x][y] != 0)
9532     {
9533       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9534                                            10 - MovDelay[x][y]);
9535
9536       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9537     }
9538   }
9539 }
9540
9541 static void MauerWaechst(int x, int y)
9542 {
9543   int delay = 6;
9544
9545   if (!MovDelay[x][y])          // next animation frame
9546     MovDelay[x][y] = 3 * delay;
9547
9548   if (MovDelay[x][y])           // wait some time before next frame
9549   {
9550     MovDelay[x][y]--;
9551
9552     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9553     {
9554       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9555       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9556
9557       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9558     }
9559
9560     if (!MovDelay[x][y])
9561     {
9562       if (MovDir[x][y] == MV_LEFT)
9563       {
9564         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9565           TEST_DrawLevelField(x - 1, y);
9566       }
9567       else if (MovDir[x][y] == MV_RIGHT)
9568       {
9569         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9570           TEST_DrawLevelField(x + 1, y);
9571       }
9572       else if (MovDir[x][y] == MV_UP)
9573       {
9574         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9575           TEST_DrawLevelField(x, y - 1);
9576       }
9577       else
9578       {
9579         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9580           TEST_DrawLevelField(x, y + 1);
9581       }
9582
9583       Tile[x][y] = Store[x][y];
9584       Store[x][y] = 0;
9585       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9586       TEST_DrawLevelField(x, y);
9587     }
9588   }
9589 }
9590
9591 static void MauerAbleger(int ax, int ay)
9592 {
9593   int element = Tile[ax][ay];
9594   int graphic = el2img(element);
9595   boolean oben_frei = FALSE, unten_frei = FALSE;
9596   boolean links_frei = FALSE, rechts_frei = FALSE;
9597   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9598   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9599   boolean new_wall = FALSE;
9600
9601   if (IS_ANIMATED(graphic))
9602     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9603
9604   if (!MovDelay[ax][ay])        // start building new wall
9605     MovDelay[ax][ay] = 6;
9606
9607   if (MovDelay[ax][ay])         // wait some time before building new wall
9608   {
9609     MovDelay[ax][ay]--;
9610     if (MovDelay[ax][ay])
9611       return;
9612   }
9613
9614   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9615     oben_frei = TRUE;
9616   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9617     unten_frei = TRUE;
9618   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9619     links_frei = TRUE;
9620   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9621     rechts_frei = TRUE;
9622
9623   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9624       element == EL_EXPANDABLE_WALL_ANY)
9625   {
9626     if (oben_frei)
9627     {
9628       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9629       Store[ax][ay-1] = element;
9630       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9631       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9632         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9633                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9634       new_wall = TRUE;
9635     }
9636     if (unten_frei)
9637     {
9638       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9639       Store[ax][ay+1] = element;
9640       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9641       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9642         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9643                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9644       new_wall = TRUE;
9645     }
9646   }
9647
9648   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9649       element == EL_EXPANDABLE_WALL_ANY ||
9650       element == EL_EXPANDABLE_WALL ||
9651       element == EL_BD_EXPANDABLE_WALL)
9652   {
9653     if (links_frei)
9654     {
9655       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9656       Store[ax-1][ay] = element;
9657       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9658       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9659         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9660                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9661       new_wall = TRUE;
9662     }
9663
9664     if (rechts_frei)
9665     {
9666       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9667       Store[ax+1][ay] = element;
9668       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9669       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9670         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9671                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9672       new_wall = TRUE;
9673     }
9674   }
9675
9676   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9677     TEST_DrawLevelField(ax, ay);
9678
9679   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9680     oben_massiv = TRUE;
9681   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9682     unten_massiv = TRUE;
9683   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9684     links_massiv = TRUE;
9685   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9686     rechts_massiv = TRUE;
9687
9688   if (((oben_massiv && unten_massiv) ||
9689        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9690        element == EL_EXPANDABLE_WALL) &&
9691       ((links_massiv && rechts_massiv) ||
9692        element == EL_EXPANDABLE_WALL_VERTICAL))
9693     Tile[ax][ay] = EL_WALL;
9694
9695   if (new_wall)
9696     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9697 }
9698
9699 static void MauerAblegerStahl(int ax, int ay)
9700 {
9701   int element = Tile[ax][ay];
9702   int graphic = el2img(element);
9703   boolean oben_frei = FALSE, unten_frei = FALSE;
9704   boolean links_frei = FALSE, rechts_frei = FALSE;
9705   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9706   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9707   boolean new_wall = FALSE;
9708
9709   if (IS_ANIMATED(graphic))
9710     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9711
9712   if (!MovDelay[ax][ay])        // start building new wall
9713     MovDelay[ax][ay] = 6;
9714
9715   if (MovDelay[ax][ay])         // wait some time before building new wall
9716   {
9717     MovDelay[ax][ay]--;
9718     if (MovDelay[ax][ay])
9719       return;
9720   }
9721
9722   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9723     oben_frei = TRUE;
9724   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9725     unten_frei = TRUE;
9726   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9727     links_frei = TRUE;
9728   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9729     rechts_frei = TRUE;
9730
9731   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9732       element == EL_EXPANDABLE_STEELWALL_ANY)
9733   {
9734     if (oben_frei)
9735     {
9736       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9737       Store[ax][ay-1] = element;
9738       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9739       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9740         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9741                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9742       new_wall = TRUE;
9743     }
9744     if (unten_frei)
9745     {
9746       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9747       Store[ax][ay+1] = element;
9748       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9749       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9750         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9751                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9752       new_wall = TRUE;
9753     }
9754   }
9755
9756   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9757       element == EL_EXPANDABLE_STEELWALL_ANY)
9758   {
9759     if (links_frei)
9760     {
9761       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9762       Store[ax-1][ay] = element;
9763       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9764       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9765         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9766                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9767       new_wall = TRUE;
9768     }
9769
9770     if (rechts_frei)
9771     {
9772       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9773       Store[ax+1][ay] = element;
9774       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9775       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9776         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9777                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9778       new_wall = TRUE;
9779     }
9780   }
9781
9782   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9783     oben_massiv = TRUE;
9784   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9785     unten_massiv = TRUE;
9786   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9787     links_massiv = TRUE;
9788   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9789     rechts_massiv = TRUE;
9790
9791   if (((oben_massiv && unten_massiv) ||
9792        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9793       ((links_massiv && rechts_massiv) ||
9794        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9795     Tile[ax][ay] = EL_STEELWALL;
9796
9797   if (new_wall)
9798     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9799 }
9800
9801 static void CheckForDragon(int x, int y)
9802 {
9803   int i, j;
9804   boolean dragon_found = FALSE;
9805   static int xy[4][2] =
9806   {
9807     { 0, -1 },
9808     { -1, 0 },
9809     { +1, 0 },
9810     { 0, +1 }
9811   };
9812
9813   for (i = 0; i < NUM_DIRECTIONS; i++)
9814   {
9815     for (j = 0; j < 4; j++)
9816     {
9817       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9818
9819       if (IN_LEV_FIELD(xx, yy) &&
9820           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9821       {
9822         if (Tile[xx][yy] == EL_DRAGON)
9823           dragon_found = TRUE;
9824       }
9825       else
9826         break;
9827     }
9828   }
9829
9830   if (!dragon_found)
9831   {
9832     for (i = 0; i < NUM_DIRECTIONS; i++)
9833     {
9834       for (j = 0; j < 3; j++)
9835       {
9836         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9837   
9838         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9839         {
9840           Tile[xx][yy] = EL_EMPTY;
9841           TEST_DrawLevelField(xx, yy);
9842         }
9843         else
9844           break;
9845       }
9846     }
9847   }
9848 }
9849
9850 static void InitBuggyBase(int x, int y)
9851 {
9852   int element = Tile[x][y];
9853   int activating_delay = FRAMES_PER_SECOND / 4;
9854
9855   ChangeDelay[x][y] =
9856     (element == EL_SP_BUGGY_BASE ?
9857      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9858      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9859      activating_delay :
9860      element == EL_SP_BUGGY_BASE_ACTIVE ?
9861      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9862 }
9863
9864 static void WarnBuggyBase(int x, int y)
9865 {
9866   int i;
9867   static int xy[4][2] =
9868   {
9869     { 0, -1 },
9870     { -1, 0 },
9871     { +1, 0 },
9872     { 0, +1 }
9873   };
9874
9875   for (i = 0; i < NUM_DIRECTIONS; i++)
9876   {
9877     int xx = x + xy[i][0];
9878     int yy = y + xy[i][1];
9879
9880     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9881     {
9882       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9883
9884       break;
9885     }
9886   }
9887 }
9888
9889 static void InitTrap(int x, int y)
9890 {
9891   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9892 }
9893
9894 static void ActivateTrap(int x, int y)
9895 {
9896   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9897 }
9898
9899 static void ChangeActiveTrap(int x, int y)
9900 {
9901   int graphic = IMG_TRAP_ACTIVE;
9902
9903   // if new animation frame was drawn, correct crumbled sand border
9904   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9905     TEST_DrawLevelFieldCrumbled(x, y);
9906 }
9907
9908 static int getSpecialActionElement(int element, int number, int base_element)
9909 {
9910   return (element != EL_EMPTY ? element :
9911           number != -1 ? base_element + number - 1 :
9912           EL_EMPTY);
9913 }
9914
9915 static int getModifiedActionNumber(int value_old, int operator, int operand,
9916                                    int value_min, int value_max)
9917 {
9918   int value_new = (operator == CA_MODE_SET      ? operand :
9919                    operator == CA_MODE_ADD      ? value_old + operand :
9920                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9921                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9922                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9923                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9924                    value_old);
9925
9926   return (value_new < value_min ? value_min :
9927           value_new > value_max ? value_max :
9928           value_new);
9929 }
9930
9931 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9932 {
9933   struct ElementInfo *ei = &element_info[element];
9934   struct ElementChangeInfo *change = &ei->change_page[page];
9935   int target_element = change->target_element;
9936   int action_type = change->action_type;
9937   int action_mode = change->action_mode;
9938   int action_arg = change->action_arg;
9939   int action_element = change->action_element;
9940   int i;
9941
9942   if (!change->has_action)
9943     return;
9944
9945   // ---------- determine action paramater values -----------------------------
9946
9947   int level_time_value =
9948     (level.time > 0 ? TimeLeft :
9949      TimePlayed);
9950
9951   int action_arg_element_raw =
9952     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9953      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9954      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9955      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9956      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9957      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9958      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9959      EL_EMPTY);
9960   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9961
9962   int action_arg_direction =
9963     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9964      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9965      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9966      change->actual_trigger_side :
9967      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9968      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9969      MV_NONE);
9970
9971   int action_arg_number_min =
9972     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9973      CA_ARG_MIN);
9974
9975   int action_arg_number_max =
9976     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9977      action_type == CA_SET_LEVEL_GEMS ? 999 :
9978      action_type == CA_SET_LEVEL_TIME ? 9999 :
9979      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9980      action_type == CA_SET_CE_VALUE ? 9999 :
9981      action_type == CA_SET_CE_SCORE ? 9999 :
9982      CA_ARG_MAX);
9983
9984   int action_arg_number_reset =
9985     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9986      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9987      action_type == CA_SET_LEVEL_TIME ? level.time :
9988      action_type == CA_SET_LEVEL_SCORE ? 0 :
9989      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9990      action_type == CA_SET_CE_SCORE ? 0 :
9991      0);
9992
9993   int action_arg_number =
9994     (action_arg <= CA_ARG_MAX ? action_arg :
9995      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9996      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9997      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9998      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9999      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10000      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10001      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10002      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10003      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10004      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10005      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10006      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10007      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10008      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10009      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10010      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10011      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10012      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10013      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10014      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10015      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10016      -1);
10017
10018   int action_arg_number_old =
10019     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10020      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10021      action_type == CA_SET_LEVEL_SCORE ? game.score :
10022      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10023      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10024      0);
10025
10026   int action_arg_number_new =
10027     getModifiedActionNumber(action_arg_number_old,
10028                             action_mode, action_arg_number,
10029                             action_arg_number_min, action_arg_number_max);
10030
10031   int trigger_player_bits =
10032     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10033      change->actual_trigger_player_bits : change->trigger_player);
10034
10035   int action_arg_player_bits =
10036     (action_arg >= CA_ARG_PLAYER_1 &&
10037      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10038      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10039      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10040      PLAYER_BITS_ANY);
10041
10042   // ---------- execute action  -----------------------------------------------
10043
10044   switch (action_type)
10045   {
10046     case CA_NO_ACTION:
10047     {
10048       return;
10049     }
10050
10051     // ---------- level actions  ----------------------------------------------
10052
10053     case CA_RESTART_LEVEL:
10054     {
10055       game.restart_level = TRUE;
10056
10057       break;
10058     }
10059
10060     case CA_SHOW_ENVELOPE:
10061     {
10062       int element = getSpecialActionElement(action_arg_element,
10063                                             action_arg_number, EL_ENVELOPE_1);
10064
10065       if (IS_ENVELOPE(element))
10066         local_player->show_envelope = element;
10067
10068       break;
10069     }
10070
10071     case CA_SET_LEVEL_TIME:
10072     {
10073       if (level.time > 0)       // only modify limited time value
10074       {
10075         TimeLeft = action_arg_number_new;
10076
10077         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10078
10079         DisplayGameControlValues();
10080
10081         if (!TimeLeft && setup.time_limit)
10082           for (i = 0; i < MAX_PLAYERS; i++)
10083             KillPlayer(&stored_player[i]);
10084       }
10085
10086       break;
10087     }
10088
10089     case CA_SET_LEVEL_SCORE:
10090     {
10091       game.score = action_arg_number_new;
10092
10093       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10094
10095       DisplayGameControlValues();
10096
10097       break;
10098     }
10099
10100     case CA_SET_LEVEL_GEMS:
10101     {
10102       game.gems_still_needed = action_arg_number_new;
10103
10104       game.snapshot.collected_item = TRUE;
10105
10106       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10107
10108       DisplayGameControlValues();
10109
10110       break;
10111     }
10112
10113     case CA_SET_LEVEL_WIND:
10114     {
10115       game.wind_direction = action_arg_direction;
10116
10117       break;
10118     }
10119
10120     case CA_SET_LEVEL_RANDOM_SEED:
10121     {
10122       // ensure that setting a new random seed while playing is predictable
10123       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10124
10125       break;
10126     }
10127
10128     // ---------- player actions  ---------------------------------------------
10129
10130     case CA_MOVE_PLAYER:
10131     case CA_MOVE_PLAYER_NEW:
10132     {
10133       // automatically move to the next field in specified direction
10134       for (i = 0; i < MAX_PLAYERS; i++)
10135         if (trigger_player_bits & (1 << i))
10136           if (action_type == CA_MOVE_PLAYER ||
10137               stored_player[i].MovPos == 0)
10138             stored_player[i].programmed_action = action_arg_direction;
10139
10140       break;
10141     }
10142
10143     case CA_EXIT_PLAYER:
10144     {
10145       for (i = 0; i < MAX_PLAYERS; i++)
10146         if (action_arg_player_bits & (1 << i))
10147           ExitPlayer(&stored_player[i]);
10148
10149       if (game.players_still_needed == 0)
10150         LevelSolved();
10151
10152       break;
10153     }
10154
10155     case CA_KILL_PLAYER:
10156     {
10157       for (i = 0; i < MAX_PLAYERS; i++)
10158         if (action_arg_player_bits & (1 << i))
10159           KillPlayer(&stored_player[i]);
10160
10161       break;
10162     }
10163
10164     case CA_SET_PLAYER_KEYS:
10165     {
10166       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10167       int element = getSpecialActionElement(action_arg_element,
10168                                             action_arg_number, EL_KEY_1);
10169
10170       if (IS_KEY(element))
10171       {
10172         for (i = 0; i < MAX_PLAYERS; i++)
10173         {
10174           if (trigger_player_bits & (1 << i))
10175           {
10176             stored_player[i].key[KEY_NR(element)] = key_state;
10177
10178             DrawGameDoorValues();
10179           }
10180         }
10181       }
10182
10183       break;
10184     }
10185
10186     case CA_SET_PLAYER_SPEED:
10187     {
10188       for (i = 0; i < MAX_PLAYERS; i++)
10189       {
10190         if (trigger_player_bits & (1 << i))
10191         {
10192           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10193
10194           if (action_arg == CA_ARG_SPEED_FASTER &&
10195               stored_player[i].cannot_move)
10196           {
10197             action_arg_number = STEPSIZE_VERY_SLOW;
10198           }
10199           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10200                    action_arg == CA_ARG_SPEED_FASTER)
10201           {
10202             action_arg_number = 2;
10203             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10204                            CA_MODE_MULTIPLY);
10205           }
10206           else if (action_arg == CA_ARG_NUMBER_RESET)
10207           {
10208             action_arg_number = level.initial_player_stepsize[i];
10209           }
10210
10211           move_stepsize =
10212             getModifiedActionNumber(move_stepsize,
10213                                     action_mode,
10214                                     action_arg_number,
10215                                     action_arg_number_min,
10216                                     action_arg_number_max);
10217
10218           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10219         }
10220       }
10221
10222       break;
10223     }
10224
10225     case CA_SET_PLAYER_SHIELD:
10226     {
10227       for (i = 0; i < MAX_PLAYERS; i++)
10228       {
10229         if (trigger_player_bits & (1 << i))
10230         {
10231           if (action_arg == CA_ARG_SHIELD_OFF)
10232           {
10233             stored_player[i].shield_normal_time_left = 0;
10234             stored_player[i].shield_deadly_time_left = 0;
10235           }
10236           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10237           {
10238             stored_player[i].shield_normal_time_left = 999999;
10239           }
10240           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10241           {
10242             stored_player[i].shield_normal_time_left = 999999;
10243             stored_player[i].shield_deadly_time_left = 999999;
10244           }
10245         }
10246       }
10247
10248       break;
10249     }
10250
10251     case CA_SET_PLAYER_GRAVITY:
10252     {
10253       for (i = 0; i < MAX_PLAYERS; i++)
10254       {
10255         if (trigger_player_bits & (1 << i))
10256         {
10257           stored_player[i].gravity =
10258             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10259              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10260              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10261              stored_player[i].gravity);
10262         }
10263       }
10264
10265       break;
10266     }
10267
10268     case CA_SET_PLAYER_ARTWORK:
10269     {
10270       for (i = 0; i < MAX_PLAYERS; i++)
10271       {
10272         if (trigger_player_bits & (1 << i))
10273         {
10274           int artwork_element = action_arg_element;
10275
10276           if (action_arg == CA_ARG_ELEMENT_RESET)
10277             artwork_element =
10278               (level.use_artwork_element[i] ? level.artwork_element[i] :
10279                stored_player[i].element_nr);
10280
10281           if (stored_player[i].artwork_element != artwork_element)
10282             stored_player[i].Frame = 0;
10283
10284           stored_player[i].artwork_element = artwork_element;
10285
10286           SetPlayerWaiting(&stored_player[i], FALSE);
10287
10288           // set number of special actions for bored and sleeping animation
10289           stored_player[i].num_special_action_bored =
10290             get_num_special_action(artwork_element,
10291                                    ACTION_BORING_1, ACTION_BORING_LAST);
10292           stored_player[i].num_special_action_sleeping =
10293             get_num_special_action(artwork_element,
10294                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10295         }
10296       }
10297
10298       break;
10299     }
10300
10301     case CA_SET_PLAYER_INVENTORY:
10302     {
10303       for (i = 0; i < MAX_PLAYERS; i++)
10304       {
10305         struct PlayerInfo *player = &stored_player[i];
10306         int j, k;
10307
10308         if (trigger_player_bits & (1 << i))
10309         {
10310           int inventory_element = action_arg_element;
10311
10312           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10313               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10314               action_arg == CA_ARG_ELEMENT_ACTION)
10315           {
10316             int element = inventory_element;
10317             int collect_count = element_info[element].collect_count_initial;
10318
10319             if (!IS_CUSTOM_ELEMENT(element))
10320               collect_count = 1;
10321
10322             if (collect_count == 0)
10323               player->inventory_infinite_element = element;
10324             else
10325               for (k = 0; k < collect_count; k++)
10326                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10327                   player->inventory_element[player->inventory_size++] =
10328                     element;
10329           }
10330           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10331                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10332                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10333           {
10334             if (player->inventory_infinite_element != EL_UNDEFINED &&
10335                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10336                                      action_arg_element_raw))
10337               player->inventory_infinite_element = EL_UNDEFINED;
10338
10339             for (k = 0, j = 0; j < player->inventory_size; j++)
10340             {
10341               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10342                                         action_arg_element_raw))
10343                 player->inventory_element[k++] = player->inventory_element[j];
10344             }
10345
10346             player->inventory_size = k;
10347           }
10348           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10349           {
10350             if (player->inventory_size > 0)
10351             {
10352               for (j = 0; j < player->inventory_size - 1; j++)
10353                 player->inventory_element[j] = player->inventory_element[j + 1];
10354
10355               player->inventory_size--;
10356             }
10357           }
10358           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10359           {
10360             if (player->inventory_size > 0)
10361               player->inventory_size--;
10362           }
10363           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10364           {
10365             player->inventory_infinite_element = EL_UNDEFINED;
10366             player->inventory_size = 0;
10367           }
10368           else if (action_arg == CA_ARG_INVENTORY_RESET)
10369           {
10370             player->inventory_infinite_element = EL_UNDEFINED;
10371             player->inventory_size = 0;
10372
10373             if (level.use_initial_inventory[i])
10374             {
10375               for (j = 0; j < level.initial_inventory_size[i]; j++)
10376               {
10377                 int element = level.initial_inventory_content[i][j];
10378                 int collect_count = element_info[element].collect_count_initial;
10379
10380                 if (!IS_CUSTOM_ELEMENT(element))
10381                   collect_count = 1;
10382
10383                 if (collect_count == 0)
10384                   player->inventory_infinite_element = element;
10385                 else
10386                   for (k = 0; k < collect_count; k++)
10387                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10388                       player->inventory_element[player->inventory_size++] =
10389                         element;
10390               }
10391             }
10392           }
10393         }
10394       }
10395
10396       break;
10397     }
10398
10399     // ---------- CE actions  -------------------------------------------------
10400
10401     case CA_SET_CE_VALUE:
10402     {
10403       int last_ce_value = CustomValue[x][y];
10404
10405       CustomValue[x][y] = action_arg_number_new;
10406
10407       if (CustomValue[x][y] != last_ce_value)
10408       {
10409         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10410         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10411
10412         if (CustomValue[x][y] == 0)
10413         {
10414           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10415           ChangeCount[x][y] = 0;        // allow at least one more change
10416
10417           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10418           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10419         }
10420       }
10421
10422       break;
10423     }
10424
10425     case CA_SET_CE_SCORE:
10426     {
10427       int last_ce_score = ei->collect_score;
10428
10429       ei->collect_score = action_arg_number_new;
10430
10431       if (ei->collect_score != last_ce_score)
10432       {
10433         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10434         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10435
10436         if (ei->collect_score == 0)
10437         {
10438           int xx, yy;
10439
10440           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10441           ChangeCount[x][y] = 0;        // allow at least one more change
10442
10443           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10444           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10445
10446           /*
10447             This is a very special case that seems to be a mixture between
10448             CheckElementChange() and CheckTriggeredElementChange(): while
10449             the first one only affects single elements that are triggered
10450             directly, the second one affects multiple elements in the playfield
10451             that are triggered indirectly by another element. This is a third
10452             case: Changing the CE score always affects multiple identical CEs,
10453             so every affected CE must be checked, not only the single CE for
10454             which the CE score was changed in the first place (as every instance
10455             of that CE shares the same CE score, and therefore also can change)!
10456           */
10457           SCAN_PLAYFIELD(xx, yy)
10458           {
10459             if (Tile[xx][yy] == element)
10460               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10461                                  CE_SCORE_GETS_ZERO);
10462           }
10463         }
10464       }
10465
10466       break;
10467     }
10468
10469     case CA_SET_CE_ARTWORK:
10470     {
10471       int artwork_element = action_arg_element;
10472       boolean reset_frame = FALSE;
10473       int xx, yy;
10474
10475       if (action_arg == CA_ARG_ELEMENT_RESET)
10476         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10477                            element);
10478
10479       if (ei->gfx_element != artwork_element)
10480         reset_frame = TRUE;
10481
10482       ei->gfx_element = artwork_element;
10483
10484       SCAN_PLAYFIELD(xx, yy)
10485       {
10486         if (Tile[xx][yy] == element)
10487         {
10488           if (reset_frame)
10489           {
10490             ResetGfxAnimation(xx, yy);
10491             ResetRandomAnimationValue(xx, yy);
10492           }
10493
10494           TEST_DrawLevelField(xx, yy);
10495         }
10496       }
10497
10498       break;
10499     }
10500
10501     // ---------- engine actions  ---------------------------------------------
10502
10503     case CA_SET_ENGINE_SCAN_MODE:
10504     {
10505       InitPlayfieldScanMode(action_arg);
10506
10507       break;
10508     }
10509
10510     default:
10511       break;
10512   }
10513 }
10514
10515 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10516 {
10517   int old_element = Tile[x][y];
10518   int new_element = GetElementFromGroupElement(element);
10519   int previous_move_direction = MovDir[x][y];
10520   int last_ce_value = CustomValue[x][y];
10521   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10522   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10523   boolean add_player_onto_element = (new_element_is_player &&
10524                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10525                                      IS_WALKABLE(old_element));
10526
10527   if (!add_player_onto_element)
10528   {
10529     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10530       RemoveMovingField(x, y);
10531     else
10532       RemoveField(x, y);
10533
10534     Tile[x][y] = new_element;
10535
10536     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10537       MovDir[x][y] = previous_move_direction;
10538
10539     if (element_info[new_element].use_last_ce_value)
10540       CustomValue[x][y] = last_ce_value;
10541
10542     InitField_WithBug1(x, y, FALSE);
10543
10544     new_element = Tile[x][y];   // element may have changed
10545
10546     ResetGfxAnimation(x, y);
10547     ResetRandomAnimationValue(x, y);
10548
10549     TEST_DrawLevelField(x, y);
10550
10551     if (GFX_CRUMBLED(new_element))
10552       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10553   }
10554
10555   // check if element under the player changes from accessible to unaccessible
10556   // (needed for special case of dropping element which then changes)
10557   // (must be checked after creating new element for walkable group elements)
10558   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10559       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10560   {
10561     Bang(x, y);
10562
10563     return;
10564   }
10565
10566   // "ChangeCount" not set yet to allow "entered by player" change one time
10567   if (new_element_is_player)
10568     RelocatePlayer(x, y, new_element);
10569
10570   if (is_change)
10571     ChangeCount[x][y]++;        // count number of changes in the same frame
10572
10573   TestIfBadThingTouchesPlayer(x, y);
10574   TestIfPlayerTouchesCustomElement(x, y);
10575   TestIfElementTouchesCustomElement(x, y);
10576 }
10577
10578 static void CreateField(int x, int y, int element)
10579 {
10580   CreateFieldExt(x, y, element, FALSE);
10581 }
10582
10583 static void CreateElementFromChange(int x, int y, int element)
10584 {
10585   element = GET_VALID_RUNTIME_ELEMENT(element);
10586
10587   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10588   {
10589     int old_element = Tile[x][y];
10590
10591     // prevent changed element from moving in same engine frame
10592     // unless both old and new element can either fall or move
10593     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10594         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10595       Stop[x][y] = TRUE;
10596   }
10597
10598   CreateFieldExt(x, y, element, TRUE);
10599 }
10600
10601 static boolean ChangeElement(int x, int y, int element, int page)
10602 {
10603   struct ElementInfo *ei = &element_info[element];
10604   struct ElementChangeInfo *change = &ei->change_page[page];
10605   int ce_value = CustomValue[x][y];
10606   int ce_score = ei->collect_score;
10607   int target_element;
10608   int old_element = Tile[x][y];
10609
10610   // always use default change event to prevent running into a loop
10611   if (ChangeEvent[x][y] == -1)
10612     ChangeEvent[x][y] = CE_DELAY;
10613
10614   if (ChangeEvent[x][y] == CE_DELAY)
10615   {
10616     // reset actual trigger element, trigger player and action element
10617     change->actual_trigger_element = EL_EMPTY;
10618     change->actual_trigger_player = EL_EMPTY;
10619     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10620     change->actual_trigger_side = CH_SIDE_NONE;
10621     change->actual_trigger_ce_value = 0;
10622     change->actual_trigger_ce_score = 0;
10623   }
10624
10625   // do not change elements more than a specified maximum number of changes
10626   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10627     return FALSE;
10628
10629   ChangeCount[x][y]++;          // count number of changes in the same frame
10630
10631   if (change->explode)
10632   {
10633     Bang(x, y);
10634
10635     return TRUE;
10636   }
10637
10638   if (change->use_target_content)
10639   {
10640     boolean complete_replace = TRUE;
10641     boolean can_replace[3][3];
10642     int xx, yy;
10643
10644     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10645     {
10646       boolean is_empty;
10647       boolean is_walkable;
10648       boolean is_diggable;
10649       boolean is_collectible;
10650       boolean is_removable;
10651       boolean is_destructible;
10652       int ex = x + xx - 1;
10653       int ey = y + yy - 1;
10654       int content_element = change->target_content.e[xx][yy];
10655       int e;
10656
10657       can_replace[xx][yy] = TRUE;
10658
10659       if (ex == x && ey == y)   // do not check changing element itself
10660         continue;
10661
10662       if (content_element == EL_EMPTY_SPACE)
10663       {
10664         can_replace[xx][yy] = FALSE;    // do not replace border with space
10665
10666         continue;
10667       }
10668
10669       if (!IN_LEV_FIELD(ex, ey))
10670       {
10671         can_replace[xx][yy] = FALSE;
10672         complete_replace = FALSE;
10673
10674         continue;
10675       }
10676
10677       e = Tile[ex][ey];
10678
10679       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10680         e = MovingOrBlocked2Element(ex, ey);
10681
10682       is_empty = (IS_FREE(ex, ey) ||
10683                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10684
10685       is_walkable     = (is_empty || IS_WALKABLE(e));
10686       is_diggable     = (is_empty || IS_DIGGABLE(e));
10687       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10688       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10689       is_removable    = (is_diggable || is_collectible);
10690
10691       can_replace[xx][yy] =
10692         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10693           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10694           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10695           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10696           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10697           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10698          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10699
10700       if (!can_replace[xx][yy])
10701         complete_replace = FALSE;
10702     }
10703
10704     if (!change->only_if_complete || complete_replace)
10705     {
10706       boolean something_has_changed = FALSE;
10707
10708       if (change->only_if_complete && change->use_random_replace &&
10709           RND(100) < change->random_percentage)
10710         return FALSE;
10711
10712       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10713       {
10714         int ex = x + xx - 1;
10715         int ey = y + yy - 1;
10716         int content_element;
10717
10718         if (can_replace[xx][yy] && (!change->use_random_replace ||
10719                                     RND(100) < change->random_percentage))
10720         {
10721           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10722             RemoveMovingField(ex, ey);
10723
10724           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10725
10726           content_element = change->target_content.e[xx][yy];
10727           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10728                                               ce_value, ce_score);
10729
10730           CreateElementFromChange(ex, ey, target_element);
10731
10732           something_has_changed = TRUE;
10733
10734           // for symmetry reasons, freeze newly created border elements
10735           if (ex != x || ey != y)
10736             Stop[ex][ey] = TRUE;        // no more moving in this frame
10737         }
10738       }
10739
10740       if (something_has_changed)
10741       {
10742         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10743         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10744       }
10745     }
10746   }
10747   else
10748   {
10749     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10750                                         ce_value, ce_score);
10751
10752     if (element == EL_DIAGONAL_GROWING ||
10753         element == EL_DIAGONAL_SHRINKING)
10754     {
10755       target_element = Store[x][y];
10756
10757       Store[x][y] = EL_EMPTY;
10758     }
10759
10760     CreateElementFromChange(x, y, target_element);
10761
10762     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10763     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10764   }
10765
10766   // this uses direct change before indirect change
10767   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10768
10769   return TRUE;
10770 }
10771
10772 static void HandleElementChange(int x, int y, int page)
10773 {
10774   int element = MovingOrBlocked2Element(x, y);
10775   struct ElementInfo *ei = &element_info[element];
10776   struct ElementChangeInfo *change = &ei->change_page[page];
10777   boolean handle_action_before_change = FALSE;
10778
10779 #ifdef DEBUG
10780   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10781       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10782   {
10783     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10784           x, y, element, element_info[element].token_name);
10785     Debug("game:playing:HandleElementChange", "This should never happen!");
10786   }
10787 #endif
10788
10789   // this can happen with classic bombs on walkable, changing elements
10790   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10791   {
10792     return;
10793   }
10794
10795   if (ChangeDelay[x][y] == 0)           // initialize element change
10796   {
10797     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10798
10799     if (change->can_change)
10800     {
10801       // !!! not clear why graphic animation should be reset at all here !!!
10802       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10803       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10804
10805       /*
10806         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10807
10808         When using an animation frame delay of 1 (this only happens with
10809         "sp_zonk.moving.left/right" in the classic graphics), the default
10810         (non-moving) animation shows wrong animation frames (while the
10811         moving animation, like "sp_zonk.moving.left/right", is correct,
10812         so this graphical bug never shows up with the classic graphics).
10813         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10814         be drawn instead of the correct frames 0,1,2,3. This is caused by
10815         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10816         an element change: First when the change delay ("ChangeDelay[][]")
10817         counter has reached zero after decrementing, then a second time in
10818         the next frame (after "GfxFrame[][]" was already incremented) when
10819         "ChangeDelay[][]" is reset to the initial delay value again.
10820
10821         This causes frame 0 to be drawn twice, while the last frame won't
10822         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10823
10824         As some animations may already be cleverly designed around this bug
10825         (at least the "Snake Bite" snake tail animation does this), it cannot
10826         simply be fixed here without breaking such existing animations.
10827         Unfortunately, it cannot easily be detected if a graphics set was
10828         designed "before" or "after" the bug was fixed. As a workaround,
10829         a new graphics set option "game.graphics_engine_version" was added
10830         to be able to specify the game's major release version for which the
10831         graphics set was designed, which can then be used to decide if the
10832         bugfix should be used (version 4 and above) or not (version 3 or
10833         below, or if no version was specified at all, as with old sets).
10834
10835         (The wrong/fixed animation frames can be tested with the test level set
10836         "test_gfxframe" and level "000", which contains a specially prepared
10837         custom element at level position (x/y) == (11/9) which uses the zonk
10838         animation mentioned above. Using "game.graphics_engine_version: 4"
10839         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10840         This can also be seen from the debug output for this test element.)
10841       */
10842
10843       // when a custom element is about to change (for example by change delay),
10844       // do not reset graphic animation when the custom element is moving
10845       if (game.graphics_engine_version < 4 &&
10846           !IS_MOVING(x, y))
10847       {
10848         ResetGfxAnimation(x, y);
10849         ResetRandomAnimationValue(x, y);
10850       }
10851
10852       if (change->pre_change_function)
10853         change->pre_change_function(x, y);
10854     }
10855   }
10856
10857   ChangeDelay[x][y]--;
10858
10859   if (ChangeDelay[x][y] != 0)           // continue element change
10860   {
10861     if (change->can_change)
10862     {
10863       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10864
10865       if (IS_ANIMATED(graphic))
10866         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10867
10868       if (change->change_function)
10869         change->change_function(x, y);
10870     }
10871   }
10872   else                                  // finish element change
10873   {
10874     if (ChangePage[x][y] != -1)         // remember page from delayed change
10875     {
10876       page = ChangePage[x][y];
10877       ChangePage[x][y] = -1;
10878
10879       change = &ei->change_page[page];
10880     }
10881
10882     if (IS_MOVING(x, y))                // never change a running system ;-)
10883     {
10884       ChangeDelay[x][y] = 1;            // try change after next move step
10885       ChangePage[x][y] = page;          // remember page to use for change
10886
10887       return;
10888     }
10889
10890     // special case: set new level random seed before changing element
10891     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10892       handle_action_before_change = TRUE;
10893
10894     if (change->has_action && handle_action_before_change)
10895       ExecuteCustomElementAction(x, y, element, page);
10896
10897     if (change->can_change)
10898     {
10899       if (ChangeElement(x, y, element, page))
10900       {
10901         if (change->post_change_function)
10902           change->post_change_function(x, y);
10903       }
10904     }
10905
10906     if (change->has_action && !handle_action_before_change)
10907       ExecuteCustomElementAction(x, y, element, page);
10908   }
10909 }
10910
10911 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10912                                               int trigger_element,
10913                                               int trigger_event,
10914                                               int trigger_player,
10915                                               int trigger_side,
10916                                               int trigger_page)
10917 {
10918   boolean change_done_any = FALSE;
10919   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10920   int i;
10921
10922   if (!(trigger_events[trigger_element][trigger_event]))
10923     return FALSE;
10924
10925   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10926
10927   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10928   {
10929     int element = EL_CUSTOM_START + i;
10930     boolean change_done = FALSE;
10931     int p;
10932
10933     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10934         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10935       continue;
10936
10937     for (p = 0; p < element_info[element].num_change_pages; p++)
10938     {
10939       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10940
10941       if (change->can_change_or_has_action &&
10942           change->has_event[trigger_event] &&
10943           change->trigger_side & trigger_side &&
10944           change->trigger_player & trigger_player &&
10945           change->trigger_page & trigger_page_bits &&
10946           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10947       {
10948         change->actual_trigger_element = trigger_element;
10949         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10950         change->actual_trigger_player_bits = trigger_player;
10951         change->actual_trigger_side = trigger_side;
10952         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10953         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10954
10955         if ((change->can_change && !change_done) || change->has_action)
10956         {
10957           int x, y;
10958
10959           SCAN_PLAYFIELD(x, y)
10960           {
10961             if (Tile[x][y] == element)
10962             {
10963               if (change->can_change && !change_done)
10964               {
10965                 // if element already changed in this frame, not only prevent
10966                 // another element change (checked in ChangeElement()), but
10967                 // also prevent additional element actions for this element
10968
10969                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10970                     !level.use_action_after_change_bug)
10971                   continue;
10972
10973                 ChangeDelay[x][y] = 1;
10974                 ChangeEvent[x][y] = trigger_event;
10975
10976                 HandleElementChange(x, y, p);
10977               }
10978               else if (change->has_action)
10979               {
10980                 // if element already changed in this frame, not only prevent
10981                 // another element change (checked in ChangeElement()), but
10982                 // also prevent additional element actions for this element
10983
10984                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10985                     !level.use_action_after_change_bug)
10986                   continue;
10987
10988                 ExecuteCustomElementAction(x, y, element, p);
10989                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10990               }
10991             }
10992           }
10993
10994           if (change->can_change)
10995           {
10996             change_done = TRUE;
10997             change_done_any = TRUE;
10998           }
10999         }
11000       }
11001     }
11002   }
11003
11004   RECURSION_LOOP_DETECTION_END();
11005
11006   return change_done_any;
11007 }
11008
11009 static boolean CheckElementChangeExt(int x, int y,
11010                                      int element,
11011                                      int trigger_element,
11012                                      int trigger_event,
11013                                      int trigger_player,
11014                                      int trigger_side)
11015 {
11016   boolean change_done = FALSE;
11017   int p;
11018
11019   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11020       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11021     return FALSE;
11022
11023   if (Tile[x][y] == EL_BLOCKED)
11024   {
11025     Blocked2Moving(x, y, &x, &y);
11026     element = Tile[x][y];
11027   }
11028
11029   // check if element has already changed or is about to change after moving
11030   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11031        Tile[x][y] != element) ||
11032
11033       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11034        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11035         ChangePage[x][y] != -1)))
11036     return FALSE;
11037
11038   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11039
11040   for (p = 0; p < element_info[element].num_change_pages; p++)
11041   {
11042     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11043
11044     /* check trigger element for all events where the element that is checked
11045        for changing interacts with a directly adjacent element -- this is
11046        different to element changes that affect other elements to change on the
11047        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11048     boolean check_trigger_element =
11049       (trigger_event == CE_TOUCHING_X ||
11050        trigger_event == CE_HITTING_X ||
11051        trigger_event == CE_HIT_BY_X ||
11052        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11053
11054     if (change->can_change_or_has_action &&
11055         change->has_event[trigger_event] &&
11056         change->trigger_side & trigger_side &&
11057         change->trigger_player & trigger_player &&
11058         (!check_trigger_element ||
11059          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11060     {
11061       change->actual_trigger_element = trigger_element;
11062       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11063       change->actual_trigger_player_bits = trigger_player;
11064       change->actual_trigger_side = trigger_side;
11065       change->actual_trigger_ce_value = CustomValue[x][y];
11066       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11067
11068       // special case: trigger element not at (x,y) position for some events
11069       if (check_trigger_element)
11070       {
11071         static struct
11072         {
11073           int dx, dy;
11074         } move_xy[] =
11075           {
11076             {  0,  0 },
11077             { -1,  0 },
11078             { +1,  0 },
11079             {  0,  0 },
11080             {  0, -1 },
11081             {  0,  0 }, { 0, 0 }, { 0, 0 },
11082             {  0, +1 }
11083           };
11084
11085         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11086         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11087
11088         change->actual_trigger_ce_value = CustomValue[xx][yy];
11089         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11090       }
11091
11092       if (change->can_change && !change_done)
11093       {
11094         ChangeDelay[x][y] = 1;
11095         ChangeEvent[x][y] = trigger_event;
11096
11097         HandleElementChange(x, y, p);
11098
11099         change_done = TRUE;
11100       }
11101       else if (change->has_action)
11102       {
11103         ExecuteCustomElementAction(x, y, element, p);
11104         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11105       }
11106     }
11107   }
11108
11109   RECURSION_LOOP_DETECTION_END();
11110
11111   return change_done;
11112 }
11113
11114 static void PlayPlayerSound(struct PlayerInfo *player)
11115 {
11116   int jx = player->jx, jy = player->jy;
11117   int sound_element = player->artwork_element;
11118   int last_action = player->last_action_waiting;
11119   int action = player->action_waiting;
11120
11121   if (player->is_waiting)
11122   {
11123     if (action != last_action)
11124       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11125     else
11126       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11127   }
11128   else
11129   {
11130     if (action != last_action)
11131       StopSound(element_info[sound_element].sound[last_action]);
11132
11133     if (last_action == ACTION_SLEEPING)
11134       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11135   }
11136 }
11137
11138 static void PlayAllPlayersSound(void)
11139 {
11140   int i;
11141
11142   for (i = 0; i < MAX_PLAYERS; i++)
11143     if (stored_player[i].active)
11144       PlayPlayerSound(&stored_player[i]);
11145 }
11146
11147 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11148 {
11149   boolean last_waiting = player->is_waiting;
11150   int move_dir = player->MovDir;
11151
11152   player->dir_waiting = move_dir;
11153   player->last_action_waiting = player->action_waiting;
11154
11155   if (is_waiting)
11156   {
11157     if (!last_waiting)          // not waiting -> waiting
11158     {
11159       player->is_waiting = TRUE;
11160
11161       player->frame_counter_bored =
11162         FrameCounter +
11163         game.player_boring_delay_fixed +
11164         GetSimpleRandom(game.player_boring_delay_random);
11165       player->frame_counter_sleeping =
11166         FrameCounter +
11167         game.player_sleeping_delay_fixed +
11168         GetSimpleRandom(game.player_sleeping_delay_random);
11169
11170       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11171     }
11172
11173     if (game.player_sleeping_delay_fixed +
11174         game.player_sleeping_delay_random > 0 &&
11175         player->anim_delay_counter == 0 &&
11176         player->post_delay_counter == 0 &&
11177         FrameCounter >= player->frame_counter_sleeping)
11178       player->is_sleeping = TRUE;
11179     else if (game.player_boring_delay_fixed +
11180              game.player_boring_delay_random > 0 &&
11181              FrameCounter >= player->frame_counter_bored)
11182       player->is_bored = TRUE;
11183
11184     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11185                               player->is_bored ? ACTION_BORING :
11186                               ACTION_WAITING);
11187
11188     if (player->is_sleeping && player->use_murphy)
11189     {
11190       // special case for sleeping Murphy when leaning against non-free tile
11191
11192       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11193           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11194            !IS_MOVING(player->jx - 1, player->jy)))
11195         move_dir = MV_LEFT;
11196       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11197                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11198                 !IS_MOVING(player->jx + 1, player->jy)))
11199         move_dir = MV_RIGHT;
11200       else
11201         player->is_sleeping = FALSE;
11202
11203       player->dir_waiting = move_dir;
11204     }
11205
11206     if (player->is_sleeping)
11207     {
11208       if (player->num_special_action_sleeping > 0)
11209       {
11210         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11211         {
11212           int last_special_action = player->special_action_sleeping;
11213           int num_special_action = player->num_special_action_sleeping;
11214           int special_action =
11215             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11216              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11217              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11218              last_special_action + 1 : ACTION_SLEEPING);
11219           int special_graphic =
11220             el_act_dir2img(player->artwork_element, special_action, move_dir);
11221
11222           player->anim_delay_counter =
11223             graphic_info[special_graphic].anim_delay_fixed +
11224             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11225           player->post_delay_counter =
11226             graphic_info[special_graphic].post_delay_fixed +
11227             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11228
11229           player->special_action_sleeping = special_action;
11230         }
11231
11232         if (player->anim_delay_counter > 0)
11233         {
11234           player->action_waiting = player->special_action_sleeping;
11235           player->anim_delay_counter--;
11236         }
11237         else if (player->post_delay_counter > 0)
11238         {
11239           player->post_delay_counter--;
11240         }
11241       }
11242     }
11243     else if (player->is_bored)
11244     {
11245       if (player->num_special_action_bored > 0)
11246       {
11247         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11248         {
11249           int special_action =
11250             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11251           int special_graphic =
11252             el_act_dir2img(player->artwork_element, special_action, move_dir);
11253
11254           player->anim_delay_counter =
11255             graphic_info[special_graphic].anim_delay_fixed +
11256             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11257           player->post_delay_counter =
11258             graphic_info[special_graphic].post_delay_fixed +
11259             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11260
11261           player->special_action_bored = special_action;
11262         }
11263
11264         if (player->anim_delay_counter > 0)
11265         {
11266           player->action_waiting = player->special_action_bored;
11267           player->anim_delay_counter--;
11268         }
11269         else if (player->post_delay_counter > 0)
11270         {
11271           player->post_delay_counter--;
11272         }
11273       }
11274     }
11275   }
11276   else if (last_waiting)        // waiting -> not waiting
11277   {
11278     player->is_waiting = FALSE;
11279     player->is_bored = FALSE;
11280     player->is_sleeping = FALSE;
11281
11282     player->frame_counter_bored = -1;
11283     player->frame_counter_sleeping = -1;
11284
11285     player->anim_delay_counter = 0;
11286     player->post_delay_counter = 0;
11287
11288     player->dir_waiting = player->MovDir;
11289     player->action_waiting = ACTION_DEFAULT;
11290
11291     player->special_action_bored = ACTION_DEFAULT;
11292     player->special_action_sleeping = ACTION_DEFAULT;
11293   }
11294 }
11295
11296 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11297 {
11298   if ((!player->is_moving  && player->was_moving) ||
11299       (player->MovPos == 0 && player->was_moving) ||
11300       (player->is_snapping && !player->was_snapping) ||
11301       (player->is_dropping && !player->was_dropping))
11302   {
11303     if (!CheckSaveEngineSnapshotToList())
11304       return;
11305
11306     player->was_moving = FALSE;
11307     player->was_snapping = TRUE;
11308     player->was_dropping = TRUE;
11309   }
11310   else
11311   {
11312     if (player->is_moving)
11313       player->was_moving = TRUE;
11314
11315     if (!player->is_snapping)
11316       player->was_snapping = FALSE;
11317
11318     if (!player->is_dropping)
11319       player->was_dropping = FALSE;
11320   }
11321
11322   static struct MouseActionInfo mouse_action_last = { 0 };
11323   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11324   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11325
11326   if (new_released)
11327     CheckSaveEngineSnapshotToList();
11328
11329   mouse_action_last = mouse_action;
11330 }
11331
11332 static void CheckSingleStepMode(struct PlayerInfo *player)
11333 {
11334   if (tape.single_step && tape.recording && !tape.pausing)
11335   {
11336     // as it is called "single step mode", just return to pause mode when the
11337     // player stopped moving after one tile (or never starts moving at all)
11338     // (reverse logic needed here in case single step mode used in team mode)
11339     if (player->is_moving ||
11340         player->is_pushing ||
11341         player->is_dropping_pressed ||
11342         player->effective_mouse_action.button)
11343       game.enter_single_step_mode = FALSE;
11344   }
11345
11346   CheckSaveEngineSnapshot(player);
11347 }
11348
11349 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11350 {
11351   int left      = player_action & JOY_LEFT;
11352   int right     = player_action & JOY_RIGHT;
11353   int up        = player_action & JOY_UP;
11354   int down      = player_action & JOY_DOWN;
11355   int button1   = player_action & JOY_BUTTON_1;
11356   int button2   = player_action & JOY_BUTTON_2;
11357   int dx        = (left ? -1 : right ? 1 : 0);
11358   int dy        = (up   ? -1 : down  ? 1 : 0);
11359
11360   if (!player->active || tape.pausing)
11361     return 0;
11362
11363   if (player_action)
11364   {
11365     if (button1)
11366       SnapField(player, dx, dy);
11367     else
11368     {
11369       if (button2)
11370         DropElement(player);
11371
11372       MovePlayer(player, dx, dy);
11373     }
11374
11375     CheckSingleStepMode(player);
11376
11377     SetPlayerWaiting(player, FALSE);
11378
11379     return player_action;
11380   }
11381   else
11382   {
11383     // no actions for this player (no input at player's configured device)
11384
11385     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11386     SnapField(player, 0, 0);
11387     CheckGravityMovementWhenNotMoving(player);
11388
11389     if (player->MovPos == 0)
11390       SetPlayerWaiting(player, TRUE);
11391
11392     if (player->MovPos == 0)    // needed for tape.playing
11393       player->is_moving = FALSE;
11394
11395     player->is_dropping = FALSE;
11396     player->is_dropping_pressed = FALSE;
11397     player->drop_pressed_delay = 0;
11398
11399     CheckSingleStepMode(player);
11400
11401     return 0;
11402   }
11403 }
11404
11405 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11406                                          byte *tape_action)
11407 {
11408   if (!tape.use_mouse_actions)
11409     return;
11410
11411   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11412   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11413   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11414 }
11415
11416 static void SetTapeActionFromMouseAction(byte *tape_action,
11417                                          struct MouseActionInfo *mouse_action)
11418 {
11419   if (!tape.use_mouse_actions)
11420     return;
11421
11422   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11423   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11424   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11425 }
11426
11427 static void CheckLevelSolved(void)
11428 {
11429   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11430   {
11431     if (game_em.level_solved &&
11432         !game_em.game_over)                             // game won
11433     {
11434       LevelSolved();
11435
11436       game_em.game_over = TRUE;
11437
11438       game.all_players_gone = TRUE;
11439     }
11440
11441     if (game_em.game_over)                              // game lost
11442       game.all_players_gone = TRUE;
11443   }
11444   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11445   {
11446     if (game_sp.level_solved &&
11447         !game_sp.game_over)                             // game won
11448     {
11449       LevelSolved();
11450
11451       game_sp.game_over = TRUE;
11452
11453       game.all_players_gone = TRUE;
11454     }
11455
11456     if (game_sp.game_over)                              // game lost
11457       game.all_players_gone = TRUE;
11458   }
11459   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11460   {
11461     if (game_mm.level_solved &&
11462         !game_mm.game_over)                             // game won
11463     {
11464       LevelSolved();
11465
11466       game_mm.game_over = TRUE;
11467
11468       game.all_players_gone = TRUE;
11469     }
11470
11471     if (game_mm.game_over)                              // game lost
11472       game.all_players_gone = TRUE;
11473   }
11474 }
11475
11476 static void CheckLevelTime(void)
11477 {
11478   int i;
11479
11480   if (TimeFrames >= FRAMES_PER_SECOND)
11481   {
11482     TimeFrames = 0;
11483     TapeTime++;
11484
11485     for (i = 0; i < MAX_PLAYERS; i++)
11486     {
11487       struct PlayerInfo *player = &stored_player[i];
11488
11489       if (SHIELD_ON(player))
11490       {
11491         player->shield_normal_time_left--;
11492
11493         if (player->shield_deadly_time_left > 0)
11494           player->shield_deadly_time_left--;
11495       }
11496     }
11497
11498     if (!game.LevelSolved && !level.use_step_counter)
11499     {
11500       TimePlayed++;
11501
11502       if (TimeLeft > 0)
11503       {
11504         TimeLeft--;
11505
11506         if (TimeLeft <= 10 && setup.time_limit)
11507           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11508
11509         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11510            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11511
11512         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11513
11514         if (!TimeLeft && setup.time_limit)
11515         {
11516           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11517             game_em.lev->killed_out_of_time = TRUE;
11518           else
11519             for (i = 0; i < MAX_PLAYERS; i++)
11520               KillPlayer(&stored_player[i]);
11521         }
11522       }
11523       else if (game.no_time_limit && !game.all_players_gone)
11524       {
11525         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11526       }
11527
11528       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11529     }
11530
11531     if (tape.recording || tape.playing)
11532       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11533   }
11534
11535   if (tape.recording || tape.playing)
11536     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11537
11538   UpdateAndDisplayGameControlValues();
11539 }
11540
11541 void AdvanceFrameAndPlayerCounters(int player_nr)
11542 {
11543   int i;
11544
11545   // advance frame counters (global frame counter and time frame counter)
11546   FrameCounter++;
11547   TimeFrames++;
11548
11549   // advance player counters (counters for move delay, move animation etc.)
11550   for (i = 0; i < MAX_PLAYERS; i++)
11551   {
11552     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11553     int move_delay_value = stored_player[i].move_delay_value;
11554     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11555
11556     if (!advance_player_counters)       // not all players may be affected
11557       continue;
11558
11559     if (move_frames == 0)       // less than one move per game frame
11560     {
11561       int stepsize = TILEX / move_delay_value;
11562       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11563       int count = (stored_player[i].is_moving ?
11564                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11565
11566       if (count % delay == 0)
11567         move_frames = 1;
11568     }
11569
11570     stored_player[i].Frame += move_frames;
11571
11572     if (stored_player[i].MovPos != 0)
11573       stored_player[i].StepFrame += move_frames;
11574
11575     if (stored_player[i].move_delay > 0)
11576       stored_player[i].move_delay--;
11577
11578     // due to bugs in previous versions, counter must count up, not down
11579     if (stored_player[i].push_delay != -1)
11580       stored_player[i].push_delay++;
11581
11582     if (stored_player[i].drop_delay > 0)
11583       stored_player[i].drop_delay--;
11584
11585     if (stored_player[i].is_dropping_pressed)
11586       stored_player[i].drop_pressed_delay++;
11587   }
11588 }
11589
11590 void StartGameActions(boolean init_network_game, boolean record_tape,
11591                       int random_seed)
11592 {
11593   unsigned int new_random_seed = InitRND(random_seed);
11594
11595   if (record_tape)
11596     TapeStartRecording(new_random_seed);
11597
11598   if (init_network_game)
11599   {
11600     SendToServer_LevelFile();
11601     SendToServer_StartPlaying();
11602
11603     return;
11604   }
11605
11606   InitGame();
11607 }
11608
11609 static void GameActionsExt(void)
11610 {
11611 #if 0
11612   static unsigned int game_frame_delay = 0;
11613 #endif
11614   unsigned int game_frame_delay_value;
11615   byte *recorded_player_action;
11616   byte summarized_player_action = 0;
11617   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11618   int i;
11619
11620   // detect endless loops, caused by custom element programming
11621   if (recursion_loop_detected && recursion_loop_depth == 0)
11622   {
11623     char *message = getStringCat3("Internal Error! Element ",
11624                                   EL_NAME(recursion_loop_element),
11625                                   " caused endless loop! Quit the game?");
11626
11627     Warn("element '%s' caused endless loop in game engine",
11628          EL_NAME(recursion_loop_element));
11629
11630     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11631
11632     recursion_loop_detected = FALSE;    // if game should be continued
11633
11634     free(message);
11635
11636     return;
11637   }
11638
11639   if (game.restart_level)
11640     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11641
11642   CheckLevelSolved();
11643
11644   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11645     GameWon();
11646
11647   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11648     TapeStop();
11649
11650   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11651     return;
11652
11653   game_frame_delay_value =
11654     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11655
11656   if (tape.playing && tape.warp_forward && !tape.pausing)
11657     game_frame_delay_value = 0;
11658
11659   SetVideoFrameDelay(game_frame_delay_value);
11660
11661   // (de)activate virtual buttons depending on current game status
11662   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11663   {
11664     if (game.all_players_gone)  // if no players there to be controlled anymore
11665       SetOverlayActive(FALSE);
11666     else if (!tape.playing)     // if game continues after tape stopped playing
11667       SetOverlayActive(TRUE);
11668   }
11669
11670 #if 0
11671 #if 0
11672   // ---------- main game synchronization point ----------
11673
11674   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11675
11676   Debug("game:playing:skip", "skip == %d", skip);
11677
11678 #else
11679   // ---------- main game synchronization point ----------
11680
11681   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11682 #endif
11683 #endif
11684
11685   if (network_playing && !network_player_action_received)
11686   {
11687     // try to get network player actions in time
11688
11689     // last chance to get network player actions without main loop delay
11690     HandleNetworking();
11691
11692     // game was quit by network peer
11693     if (game_status != GAME_MODE_PLAYING)
11694       return;
11695
11696     // check if network player actions still missing and game still running
11697     if (!network_player_action_received && !checkGameEnded())
11698       return;           // failed to get network player actions in time
11699
11700     // do not yet reset "network_player_action_received" (for tape.pausing)
11701   }
11702
11703   if (tape.pausing)
11704     return;
11705
11706   // at this point we know that we really continue executing the game
11707
11708   network_player_action_received = FALSE;
11709
11710   // when playing tape, read previously recorded player input from tape data
11711   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11712
11713   local_player->effective_mouse_action = local_player->mouse_action;
11714
11715   if (recorded_player_action != NULL)
11716     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11717                                  recorded_player_action);
11718
11719   // TapePlayAction() may return NULL when toggling to "pause before death"
11720   if (tape.pausing)
11721     return;
11722
11723   if (tape.set_centered_player)
11724   {
11725     game.centered_player_nr_next = tape.centered_player_nr_next;
11726     game.set_centered_player = TRUE;
11727   }
11728
11729   for (i = 0; i < MAX_PLAYERS; i++)
11730   {
11731     summarized_player_action |= stored_player[i].action;
11732
11733     if (!network_playing && (game.team_mode || tape.playing))
11734       stored_player[i].effective_action = stored_player[i].action;
11735   }
11736
11737   if (network_playing && !checkGameEnded())
11738     SendToServer_MovePlayer(summarized_player_action);
11739
11740   // summarize all actions at local players mapped input device position
11741   // (this allows using different input devices in single player mode)
11742   if (!network.enabled && !game.team_mode)
11743     stored_player[map_player_action[local_player->index_nr]].effective_action =
11744       summarized_player_action;
11745
11746   // summarize all actions at centered player in local team mode
11747   if (tape.recording &&
11748       setup.team_mode && !network.enabled &&
11749       setup.input_on_focus &&
11750       game.centered_player_nr != -1)
11751   {
11752     for (i = 0; i < MAX_PLAYERS; i++)
11753       stored_player[map_player_action[i]].effective_action =
11754         (i == game.centered_player_nr ? summarized_player_action : 0);
11755   }
11756
11757   if (recorded_player_action != NULL)
11758     for (i = 0; i < MAX_PLAYERS; i++)
11759       stored_player[i].effective_action = recorded_player_action[i];
11760
11761   for (i = 0; i < MAX_PLAYERS; i++)
11762   {
11763     tape_action[i] = stored_player[i].effective_action;
11764
11765     /* (this may happen in the RND game engine if a player was not present on
11766        the playfield on level start, but appeared later from a custom element */
11767     if (setup.team_mode &&
11768         tape.recording &&
11769         tape_action[i] &&
11770         !tape.player_participates[i])
11771       tape.player_participates[i] = TRUE;
11772   }
11773
11774   SetTapeActionFromMouseAction(tape_action,
11775                                &local_player->effective_mouse_action);
11776
11777   // only record actions from input devices, but not programmed actions
11778   if (tape.recording)
11779     TapeRecordAction(tape_action);
11780
11781   // remember if game was played (especially after tape stopped playing)
11782   if (!tape.playing && summarized_player_action)
11783     game.GamePlayed = TRUE;
11784
11785 #if USE_NEW_PLAYER_ASSIGNMENTS
11786   // !!! also map player actions in single player mode !!!
11787   // if (game.team_mode)
11788   if (1)
11789   {
11790     byte mapped_action[MAX_PLAYERS];
11791
11792 #if DEBUG_PLAYER_ACTIONS
11793     for (i = 0; i < MAX_PLAYERS; i++)
11794       DebugContinued("", "%d, ", stored_player[i].effective_action);
11795 #endif
11796
11797     for (i = 0; i < MAX_PLAYERS; i++)
11798       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11799
11800     for (i = 0; i < MAX_PLAYERS; i++)
11801       stored_player[i].effective_action = mapped_action[i];
11802
11803 #if DEBUG_PLAYER_ACTIONS
11804     DebugContinued("", "=> ");
11805     for (i = 0; i < MAX_PLAYERS; i++)
11806       DebugContinued("", "%d, ", stored_player[i].effective_action);
11807     DebugContinued("game:playing:player", "\n");
11808 #endif
11809   }
11810 #if DEBUG_PLAYER_ACTIONS
11811   else
11812   {
11813     for (i = 0; i < MAX_PLAYERS; i++)
11814       DebugContinued("", "%d, ", stored_player[i].effective_action);
11815     DebugContinued("game:playing:player", "\n");
11816   }
11817 #endif
11818 #endif
11819
11820   for (i = 0; i < MAX_PLAYERS; i++)
11821   {
11822     // allow engine snapshot in case of changed movement attempt
11823     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11824         (stored_player[i].effective_action & KEY_MOTION))
11825       game.snapshot.changed_action = TRUE;
11826
11827     // allow engine snapshot in case of snapping/dropping attempt
11828     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11829         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11830       game.snapshot.changed_action = TRUE;
11831
11832     game.snapshot.last_action[i] = stored_player[i].effective_action;
11833   }
11834
11835   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11836   {
11837     GameActions_EM_Main();
11838   }
11839   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11840   {
11841     GameActions_SP_Main();
11842   }
11843   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11844   {
11845     GameActions_MM_Main();
11846   }
11847   else
11848   {
11849     GameActions_RND_Main();
11850   }
11851
11852   BlitScreenToBitmap(backbuffer);
11853
11854   CheckLevelSolved();
11855   CheckLevelTime();
11856
11857   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11858
11859   if (global.show_frames_per_second)
11860   {
11861     static unsigned int fps_counter = 0;
11862     static int fps_frames = 0;
11863     unsigned int fps_delay_ms = Counter() - fps_counter;
11864
11865     fps_frames++;
11866
11867     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11868     {
11869       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11870
11871       fps_frames = 0;
11872       fps_counter = Counter();
11873
11874       // always draw FPS to screen after FPS value was updated
11875       redraw_mask |= REDRAW_FPS;
11876     }
11877
11878     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11879     if (GetDrawDeactivationMask() == REDRAW_NONE)
11880       redraw_mask |= REDRAW_FPS;
11881   }
11882 }
11883
11884 static void GameActions_CheckSaveEngineSnapshot(void)
11885 {
11886   if (!game.snapshot.save_snapshot)
11887     return;
11888
11889   // clear flag for saving snapshot _before_ saving snapshot
11890   game.snapshot.save_snapshot = FALSE;
11891
11892   SaveEngineSnapshotToList();
11893 }
11894
11895 void GameActions(void)
11896 {
11897   GameActionsExt();
11898
11899   GameActions_CheckSaveEngineSnapshot();
11900 }
11901
11902 void GameActions_EM_Main(void)
11903 {
11904   byte effective_action[MAX_PLAYERS];
11905   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11906   int i;
11907
11908   for (i = 0; i < MAX_PLAYERS; i++)
11909     effective_action[i] = stored_player[i].effective_action;
11910
11911   GameActions_EM(effective_action, warp_mode);
11912 }
11913
11914 void GameActions_SP_Main(void)
11915 {
11916   byte effective_action[MAX_PLAYERS];
11917   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11918   int i;
11919
11920   for (i = 0; i < MAX_PLAYERS; i++)
11921     effective_action[i] = stored_player[i].effective_action;
11922
11923   GameActions_SP(effective_action, warp_mode);
11924
11925   for (i = 0; i < MAX_PLAYERS; i++)
11926   {
11927     if (stored_player[i].force_dropping)
11928       stored_player[i].action |= KEY_BUTTON_DROP;
11929
11930     stored_player[i].force_dropping = FALSE;
11931   }
11932 }
11933
11934 void GameActions_MM_Main(void)
11935 {
11936   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11937
11938   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11939 }
11940
11941 void GameActions_RND_Main(void)
11942 {
11943   GameActions_RND();
11944 }
11945
11946 void GameActions_RND(void)
11947 {
11948   static struct MouseActionInfo mouse_action_last = { 0 };
11949   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11950   int magic_wall_x = 0, magic_wall_y = 0;
11951   int i, x, y, element, graphic, last_gfx_frame;
11952
11953   InitPlayfieldScanModeVars();
11954
11955   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11956   {
11957     SCAN_PLAYFIELD(x, y)
11958     {
11959       ChangeCount[x][y] = 0;
11960       ChangeEvent[x][y] = -1;
11961     }
11962   }
11963
11964   if (game.set_centered_player)
11965   {
11966     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11967
11968     // switching to "all players" only possible if all players fit to screen
11969     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11970     {
11971       game.centered_player_nr_next = game.centered_player_nr;
11972       game.set_centered_player = FALSE;
11973     }
11974
11975     // do not switch focus to non-existing (or non-active) player
11976     if (game.centered_player_nr_next >= 0 &&
11977         !stored_player[game.centered_player_nr_next].active)
11978     {
11979       game.centered_player_nr_next = game.centered_player_nr;
11980       game.set_centered_player = FALSE;
11981     }
11982   }
11983
11984   if (game.set_centered_player &&
11985       ScreenMovPos == 0)        // screen currently aligned at tile position
11986   {
11987     int sx, sy;
11988
11989     if (game.centered_player_nr_next == -1)
11990     {
11991       setScreenCenteredToAllPlayers(&sx, &sy);
11992     }
11993     else
11994     {
11995       sx = stored_player[game.centered_player_nr_next].jx;
11996       sy = stored_player[game.centered_player_nr_next].jy;
11997     }
11998
11999     game.centered_player_nr = game.centered_player_nr_next;
12000     game.set_centered_player = FALSE;
12001
12002     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12003     DrawGameDoorValues();
12004   }
12005
12006   // check single step mode (set flag and clear again if any player is active)
12007   game.enter_single_step_mode =
12008     (tape.single_step && tape.recording && !tape.pausing);
12009
12010   for (i = 0; i < MAX_PLAYERS; i++)
12011   {
12012     int actual_player_action = stored_player[i].effective_action;
12013
12014 #if 1
12015     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12016        - rnd_equinox_tetrachloride 048
12017        - rnd_equinox_tetrachloride_ii 096
12018        - rnd_emanuel_schmieg 002
12019        - doctor_sloan_ww 001, 020
12020     */
12021     if (stored_player[i].MovPos == 0)
12022       CheckGravityMovement(&stored_player[i]);
12023 #endif
12024
12025     // overwrite programmed action with tape action
12026     if (stored_player[i].programmed_action)
12027       actual_player_action = stored_player[i].programmed_action;
12028
12029     PlayerActions(&stored_player[i], actual_player_action);
12030
12031     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12032   }
12033
12034   // single step pause mode may already have been toggled by "ScrollPlayer()"
12035   if (game.enter_single_step_mode && !tape.pausing)
12036     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12037
12038   ScrollScreen(NULL, SCROLL_GO_ON);
12039
12040   /* for backwards compatibility, the following code emulates a fixed bug that
12041      occured when pushing elements (causing elements that just made their last
12042      pushing step to already (if possible) make their first falling step in the
12043      same game frame, which is bad); this code is also needed to use the famous
12044      "spring push bug" which is used in older levels and might be wanted to be
12045      used also in newer levels, but in this case the buggy pushing code is only
12046      affecting the "spring" element and no other elements */
12047
12048   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12049   {
12050     for (i = 0; i < MAX_PLAYERS; i++)
12051     {
12052       struct PlayerInfo *player = &stored_player[i];
12053       int x = player->jx;
12054       int y = player->jy;
12055
12056       if (player->active && player->is_pushing && player->is_moving &&
12057           IS_MOVING(x, y) &&
12058           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12059            Tile[x][y] == EL_SPRING))
12060       {
12061         ContinueMoving(x, y);
12062
12063         // continue moving after pushing (this is actually a bug)
12064         if (!IS_MOVING(x, y))
12065           Stop[x][y] = FALSE;
12066       }
12067     }
12068   }
12069
12070   SCAN_PLAYFIELD(x, y)
12071   {
12072     Last[x][y] = Tile[x][y];
12073
12074     ChangeCount[x][y] = 0;
12075     ChangeEvent[x][y] = -1;
12076
12077     // this must be handled before main playfield loop
12078     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12079     {
12080       MovDelay[x][y]--;
12081       if (MovDelay[x][y] <= 0)
12082         RemoveField(x, y);
12083     }
12084
12085     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12086     {
12087       MovDelay[x][y]--;
12088       if (MovDelay[x][y] <= 0)
12089       {
12090         int element = Store[x][y];
12091         int move_direction = MovDir[x][y];
12092         int player_index_bit = Store2[x][y];
12093
12094         Store[x][y] = 0;
12095         Store2[x][y] = 0;
12096
12097         RemoveField(x, y);
12098         TEST_DrawLevelField(x, y);
12099
12100         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12101       }
12102     }
12103
12104 #if DEBUG
12105     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12106     {
12107       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12108             x, y);
12109       Debug("game:playing:GameActions_RND", "This should never happen!");
12110
12111       ChangePage[x][y] = -1;
12112     }
12113 #endif
12114
12115     Stop[x][y] = FALSE;
12116     if (WasJustMoving[x][y] > 0)
12117       WasJustMoving[x][y]--;
12118     if (WasJustFalling[x][y] > 0)
12119       WasJustFalling[x][y]--;
12120     if (CheckCollision[x][y] > 0)
12121       CheckCollision[x][y]--;
12122     if (CheckImpact[x][y] > 0)
12123       CheckImpact[x][y]--;
12124
12125     GfxFrame[x][y]++;
12126
12127     /* reset finished pushing action (not done in ContinueMoving() to allow
12128        continuous pushing animation for elements with zero push delay) */
12129     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12130     {
12131       ResetGfxAnimation(x, y);
12132       TEST_DrawLevelField(x, y);
12133     }
12134
12135 #if DEBUG
12136     if (IS_BLOCKED(x, y))
12137     {
12138       int oldx, oldy;
12139
12140       Blocked2Moving(x, y, &oldx, &oldy);
12141       if (!IS_MOVING(oldx, oldy))
12142       {
12143         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12144         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12145         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12146         Debug("game:playing:GameActions_RND", "This should never happen!");
12147       }
12148     }
12149 #endif
12150   }
12151
12152   if (mouse_action.button)
12153   {
12154     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12155     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12156
12157     x = mouse_action.lx;
12158     y = mouse_action.ly;
12159     element = Tile[x][y];
12160
12161     if (new_button)
12162     {
12163       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12164       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12165                                          ch_button);
12166     }
12167
12168     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12169     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12170                                        ch_button);
12171   }
12172
12173   SCAN_PLAYFIELD(x, y)
12174   {
12175     element = Tile[x][y];
12176     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12177     last_gfx_frame = GfxFrame[x][y];
12178
12179     ResetGfxFrame(x, y);
12180
12181     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12182       DrawLevelGraphicAnimation(x, y, graphic);
12183
12184     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12185         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12186       ResetRandomAnimationValue(x, y);
12187
12188     SetRandomAnimationValue(x, y);
12189
12190     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12191
12192     if (IS_INACTIVE(element))
12193     {
12194       if (IS_ANIMATED(graphic))
12195         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12196
12197       continue;
12198     }
12199
12200     // this may take place after moving, so 'element' may have changed
12201     if (IS_CHANGING(x, y) &&
12202         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12203     {
12204       int page = element_info[element].event_page_nr[CE_DELAY];
12205
12206       HandleElementChange(x, y, page);
12207
12208       element = Tile[x][y];
12209       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12210     }
12211
12212     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12213     {
12214       StartMoving(x, y);
12215
12216       element = Tile[x][y];
12217       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12218
12219       if (IS_ANIMATED(graphic) &&
12220           !IS_MOVING(x, y) &&
12221           !Stop[x][y])
12222         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12223
12224       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12225         TEST_DrawTwinkleOnField(x, y);
12226     }
12227     else if (element == EL_ACID)
12228     {
12229       if (!Stop[x][y])
12230         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12231     }
12232     else if ((element == EL_EXIT_OPEN ||
12233               element == EL_EM_EXIT_OPEN ||
12234               element == EL_SP_EXIT_OPEN ||
12235               element == EL_STEEL_EXIT_OPEN ||
12236               element == EL_EM_STEEL_EXIT_OPEN ||
12237               element == EL_SP_TERMINAL ||
12238               element == EL_SP_TERMINAL_ACTIVE ||
12239               element == EL_EXTRA_TIME ||
12240               element == EL_SHIELD_NORMAL ||
12241               element == EL_SHIELD_DEADLY) &&
12242              IS_ANIMATED(graphic))
12243       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12244     else if (IS_MOVING(x, y))
12245       ContinueMoving(x, y);
12246     else if (IS_ACTIVE_BOMB(element))
12247       CheckDynamite(x, y);
12248     else if (element == EL_AMOEBA_GROWING)
12249       AmoebaGrowing(x, y);
12250     else if (element == EL_AMOEBA_SHRINKING)
12251       AmoebaShrinking(x, y);
12252
12253 #if !USE_NEW_AMOEBA_CODE
12254     else if (IS_AMOEBALIVE(element))
12255       AmoebaReproduce(x, y);
12256 #endif
12257
12258     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12259       Life(x, y);
12260     else if (element == EL_EXIT_CLOSED)
12261       CheckExit(x, y);
12262     else if (element == EL_EM_EXIT_CLOSED)
12263       CheckExitEM(x, y);
12264     else if (element == EL_STEEL_EXIT_CLOSED)
12265       CheckExitSteel(x, y);
12266     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12267       CheckExitSteelEM(x, y);
12268     else if (element == EL_SP_EXIT_CLOSED)
12269       CheckExitSP(x, y);
12270     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12271              element == EL_EXPANDABLE_STEELWALL_GROWING)
12272       MauerWaechst(x, y);
12273     else if (element == EL_EXPANDABLE_WALL ||
12274              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12275              element == EL_EXPANDABLE_WALL_VERTICAL ||
12276              element == EL_EXPANDABLE_WALL_ANY ||
12277              element == EL_BD_EXPANDABLE_WALL)
12278       MauerAbleger(x, y);
12279     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12280              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12281              element == EL_EXPANDABLE_STEELWALL_ANY)
12282       MauerAblegerStahl(x, y);
12283     else if (element == EL_FLAMES)
12284       CheckForDragon(x, y);
12285     else if (element == EL_EXPLOSION)
12286       ; // drawing of correct explosion animation is handled separately
12287     else if (element == EL_ELEMENT_SNAPPING ||
12288              element == EL_DIAGONAL_SHRINKING ||
12289              element == EL_DIAGONAL_GROWING)
12290     {
12291       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12292
12293       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12294     }
12295     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12296       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12297
12298     if (IS_BELT_ACTIVE(element))
12299       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12300
12301     if (game.magic_wall_active)
12302     {
12303       int jx = local_player->jx, jy = local_player->jy;
12304
12305       // play the element sound at the position nearest to the player
12306       if ((element == EL_MAGIC_WALL_FULL ||
12307            element == EL_MAGIC_WALL_ACTIVE ||
12308            element == EL_MAGIC_WALL_EMPTYING ||
12309            element == EL_BD_MAGIC_WALL_FULL ||
12310            element == EL_BD_MAGIC_WALL_ACTIVE ||
12311            element == EL_BD_MAGIC_WALL_EMPTYING ||
12312            element == EL_DC_MAGIC_WALL_FULL ||
12313            element == EL_DC_MAGIC_WALL_ACTIVE ||
12314            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12315           ABS(x - jx) + ABS(y - jy) <
12316           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12317       {
12318         magic_wall_x = x;
12319         magic_wall_y = y;
12320       }
12321     }
12322   }
12323
12324 #if USE_NEW_AMOEBA_CODE
12325   // new experimental amoeba growth stuff
12326   if (!(FrameCounter % 8))
12327   {
12328     static unsigned int random = 1684108901;
12329
12330     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12331     {
12332       x = RND(lev_fieldx);
12333       y = RND(lev_fieldy);
12334       element = Tile[x][y];
12335
12336       if (!IS_PLAYER(x,y) &&
12337           (element == EL_EMPTY ||
12338            CAN_GROW_INTO(element) ||
12339            element == EL_QUICKSAND_EMPTY ||
12340            element == EL_QUICKSAND_FAST_EMPTY ||
12341            element == EL_ACID_SPLASH_LEFT ||
12342            element == EL_ACID_SPLASH_RIGHT))
12343       {
12344         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12345             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12346             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12347             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12348           Tile[x][y] = EL_AMOEBA_DROP;
12349       }
12350
12351       random = random * 129 + 1;
12352     }
12353   }
12354 #endif
12355
12356   game.explosions_delayed = FALSE;
12357
12358   SCAN_PLAYFIELD(x, y)
12359   {
12360     element = Tile[x][y];
12361
12362     if (ExplodeField[x][y])
12363       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12364     else if (element == EL_EXPLOSION)
12365       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12366
12367     ExplodeField[x][y] = EX_TYPE_NONE;
12368   }
12369
12370   game.explosions_delayed = TRUE;
12371
12372   if (game.magic_wall_active)
12373   {
12374     if (!(game.magic_wall_time_left % 4))
12375     {
12376       int element = Tile[magic_wall_x][magic_wall_y];
12377
12378       if (element == EL_BD_MAGIC_WALL_FULL ||
12379           element == EL_BD_MAGIC_WALL_ACTIVE ||
12380           element == EL_BD_MAGIC_WALL_EMPTYING)
12381         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12382       else if (element == EL_DC_MAGIC_WALL_FULL ||
12383                element == EL_DC_MAGIC_WALL_ACTIVE ||
12384                element == EL_DC_MAGIC_WALL_EMPTYING)
12385         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12386       else
12387         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12388     }
12389
12390     if (game.magic_wall_time_left > 0)
12391     {
12392       game.magic_wall_time_left--;
12393
12394       if (!game.magic_wall_time_left)
12395       {
12396         SCAN_PLAYFIELD(x, y)
12397         {
12398           element = Tile[x][y];
12399
12400           if (element == EL_MAGIC_WALL_ACTIVE ||
12401               element == EL_MAGIC_WALL_FULL)
12402           {
12403             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12404             TEST_DrawLevelField(x, y);
12405           }
12406           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12407                    element == EL_BD_MAGIC_WALL_FULL)
12408           {
12409             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12410             TEST_DrawLevelField(x, y);
12411           }
12412           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12413                    element == EL_DC_MAGIC_WALL_FULL)
12414           {
12415             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12416             TEST_DrawLevelField(x, y);
12417           }
12418         }
12419
12420         game.magic_wall_active = FALSE;
12421       }
12422     }
12423   }
12424
12425   if (game.light_time_left > 0)
12426   {
12427     game.light_time_left--;
12428
12429     if (game.light_time_left == 0)
12430       RedrawAllLightSwitchesAndInvisibleElements();
12431   }
12432
12433   if (game.timegate_time_left > 0)
12434   {
12435     game.timegate_time_left--;
12436
12437     if (game.timegate_time_left == 0)
12438       CloseAllOpenTimegates();
12439   }
12440
12441   if (game.lenses_time_left > 0)
12442   {
12443     game.lenses_time_left--;
12444
12445     if (game.lenses_time_left == 0)
12446       RedrawAllInvisibleElementsForLenses();
12447   }
12448
12449   if (game.magnify_time_left > 0)
12450   {
12451     game.magnify_time_left--;
12452
12453     if (game.magnify_time_left == 0)
12454       RedrawAllInvisibleElementsForMagnifier();
12455   }
12456
12457   for (i = 0; i < MAX_PLAYERS; i++)
12458   {
12459     struct PlayerInfo *player = &stored_player[i];
12460
12461     if (SHIELD_ON(player))
12462     {
12463       if (player->shield_deadly_time_left)
12464         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12465       else if (player->shield_normal_time_left)
12466         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12467     }
12468   }
12469
12470 #if USE_DELAYED_GFX_REDRAW
12471   SCAN_PLAYFIELD(x, y)
12472   {
12473     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12474     {
12475       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12476          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12477
12478       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12479         DrawLevelField(x, y);
12480
12481       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12482         DrawLevelFieldCrumbled(x, y);
12483
12484       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12485         DrawLevelFieldCrumbledNeighbours(x, y);
12486
12487       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12488         DrawTwinkleOnField(x, y);
12489     }
12490
12491     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12492   }
12493 #endif
12494
12495   DrawAllPlayers();
12496   PlayAllPlayersSound();
12497
12498   for (i = 0; i < MAX_PLAYERS; i++)
12499   {
12500     struct PlayerInfo *player = &stored_player[i];
12501
12502     if (player->show_envelope != 0 && (!player->active ||
12503                                        player->MovPos == 0))
12504     {
12505       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12506
12507       player->show_envelope = 0;
12508     }
12509   }
12510
12511   // use random number generator in every frame to make it less predictable
12512   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12513     RND(1);
12514
12515   mouse_action_last = mouse_action;
12516 }
12517
12518 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12519 {
12520   int min_x = x, min_y = y, max_x = x, max_y = y;
12521   int scr_fieldx = getScreenFieldSizeX();
12522   int scr_fieldy = getScreenFieldSizeY();
12523   int i;
12524
12525   for (i = 0; i < MAX_PLAYERS; i++)
12526   {
12527     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12528
12529     if (!stored_player[i].active || &stored_player[i] == player)
12530       continue;
12531
12532     min_x = MIN(min_x, jx);
12533     min_y = MIN(min_y, jy);
12534     max_x = MAX(max_x, jx);
12535     max_y = MAX(max_y, jy);
12536   }
12537
12538   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12539 }
12540
12541 static boolean AllPlayersInVisibleScreen(void)
12542 {
12543   int i;
12544
12545   for (i = 0; i < MAX_PLAYERS; i++)
12546   {
12547     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12548
12549     if (!stored_player[i].active)
12550       continue;
12551
12552     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12553       return FALSE;
12554   }
12555
12556   return TRUE;
12557 }
12558
12559 void ScrollLevel(int dx, int dy)
12560 {
12561   int scroll_offset = 2 * TILEX_VAR;
12562   int x, y;
12563
12564   BlitBitmap(drawto_field, drawto_field,
12565              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12566              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12567              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12568              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12569              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12570              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12571
12572   if (dx != 0)
12573   {
12574     x = (dx == 1 ? BX1 : BX2);
12575     for (y = BY1; y <= BY2; y++)
12576       DrawScreenField(x, y);
12577   }
12578
12579   if (dy != 0)
12580   {
12581     y = (dy == 1 ? BY1 : BY2);
12582     for (x = BX1; x <= BX2; x++)
12583       DrawScreenField(x, y);
12584   }
12585
12586   redraw_mask |= REDRAW_FIELD;
12587 }
12588
12589 static boolean canFallDown(struct PlayerInfo *player)
12590 {
12591   int jx = player->jx, jy = player->jy;
12592
12593   return (IN_LEV_FIELD(jx, jy + 1) &&
12594           (IS_FREE(jx, jy + 1) ||
12595            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12596           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12597           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12598 }
12599
12600 static boolean canPassField(int x, int y, int move_dir)
12601 {
12602   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12603   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12604   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12605   int nextx = x + dx;
12606   int nexty = y + dy;
12607   int element = Tile[x][y];
12608
12609   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12610           !CAN_MOVE(element) &&
12611           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12612           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12613           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12614 }
12615
12616 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12617 {
12618   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12619   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12620   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12621   int newx = x + dx;
12622   int newy = y + dy;
12623
12624   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12625           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12626           (IS_DIGGABLE(Tile[newx][newy]) ||
12627            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12628            canPassField(newx, newy, move_dir)));
12629 }
12630
12631 static void CheckGravityMovement(struct PlayerInfo *player)
12632 {
12633   if (player->gravity && !player->programmed_action)
12634   {
12635     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12636     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12637     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12638     int jx = player->jx, jy = player->jy;
12639     boolean player_is_moving_to_valid_field =
12640       (!player_is_snapping &&
12641        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12642         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12643     boolean player_can_fall_down = canFallDown(player);
12644
12645     if (player_can_fall_down &&
12646         !player_is_moving_to_valid_field)
12647       player->programmed_action = MV_DOWN;
12648   }
12649 }
12650
12651 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12652 {
12653   return CheckGravityMovement(player);
12654
12655   if (player->gravity && !player->programmed_action)
12656   {
12657     int jx = player->jx, jy = player->jy;
12658     boolean field_under_player_is_free =
12659       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12660     boolean player_is_standing_on_valid_field =
12661       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12662        (IS_WALKABLE(Tile[jx][jy]) &&
12663         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12664
12665     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12666       player->programmed_action = MV_DOWN;
12667   }
12668 }
12669
12670 /*
12671   MovePlayerOneStep()
12672   -----------------------------------------------------------------------------
12673   dx, dy:               direction (non-diagonal) to try to move the player to
12674   real_dx, real_dy:     direction as read from input device (can be diagonal)
12675 */
12676
12677 boolean MovePlayerOneStep(struct PlayerInfo *player,
12678                           int dx, int dy, int real_dx, int real_dy)
12679 {
12680   int jx = player->jx, jy = player->jy;
12681   int new_jx = jx + dx, new_jy = jy + dy;
12682   int can_move;
12683   boolean player_can_move = !player->cannot_move;
12684
12685   if (!player->active || (!dx && !dy))
12686     return MP_NO_ACTION;
12687
12688   player->MovDir = (dx < 0 ? MV_LEFT :
12689                     dx > 0 ? MV_RIGHT :
12690                     dy < 0 ? MV_UP :
12691                     dy > 0 ? MV_DOWN :  MV_NONE);
12692
12693   if (!IN_LEV_FIELD(new_jx, new_jy))
12694     return MP_NO_ACTION;
12695
12696   if (!player_can_move)
12697   {
12698     if (player->MovPos == 0)
12699     {
12700       player->is_moving = FALSE;
12701       player->is_digging = FALSE;
12702       player->is_collecting = FALSE;
12703       player->is_snapping = FALSE;
12704       player->is_pushing = FALSE;
12705     }
12706   }
12707
12708   if (!network.enabled && game.centered_player_nr == -1 &&
12709       !AllPlayersInSight(player, new_jx, new_jy))
12710     return MP_NO_ACTION;
12711
12712   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12713   if (can_move != MP_MOVING)
12714     return can_move;
12715
12716   // check if DigField() has caused relocation of the player
12717   if (player->jx != jx || player->jy != jy)
12718     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12719
12720   StorePlayer[jx][jy] = 0;
12721   player->last_jx = jx;
12722   player->last_jy = jy;
12723   player->jx = new_jx;
12724   player->jy = new_jy;
12725   StorePlayer[new_jx][new_jy] = player->element_nr;
12726
12727   if (player->move_delay_value_next != -1)
12728   {
12729     player->move_delay_value = player->move_delay_value_next;
12730     player->move_delay_value_next = -1;
12731   }
12732
12733   player->MovPos =
12734     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12735
12736   player->step_counter++;
12737
12738   PlayerVisit[jx][jy] = FrameCounter;
12739
12740   player->is_moving = TRUE;
12741
12742 #if 1
12743   // should better be called in MovePlayer(), but this breaks some tapes
12744   ScrollPlayer(player, SCROLL_INIT);
12745 #endif
12746
12747   return MP_MOVING;
12748 }
12749
12750 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12751 {
12752   int jx = player->jx, jy = player->jy;
12753   int old_jx = jx, old_jy = jy;
12754   int moved = MP_NO_ACTION;
12755
12756   if (!player->active)
12757     return FALSE;
12758
12759   if (!dx && !dy)
12760   {
12761     if (player->MovPos == 0)
12762     {
12763       player->is_moving = FALSE;
12764       player->is_digging = FALSE;
12765       player->is_collecting = FALSE;
12766       player->is_snapping = FALSE;
12767       player->is_pushing = FALSE;
12768     }
12769
12770     return FALSE;
12771   }
12772
12773   if (player->move_delay > 0)
12774     return FALSE;
12775
12776   player->move_delay = -1;              // set to "uninitialized" value
12777
12778   // store if player is automatically moved to next field
12779   player->is_auto_moving = (player->programmed_action != MV_NONE);
12780
12781   // remove the last programmed player action
12782   player->programmed_action = 0;
12783
12784   if (player->MovPos)
12785   {
12786     // should only happen if pre-1.2 tape recordings are played
12787     // this is only for backward compatibility
12788
12789     int original_move_delay_value = player->move_delay_value;
12790
12791 #if DEBUG
12792     Debug("game:playing:MovePlayer",
12793           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12794           tape.counter);
12795 #endif
12796
12797     // scroll remaining steps with finest movement resolution
12798     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12799
12800     while (player->MovPos)
12801     {
12802       ScrollPlayer(player, SCROLL_GO_ON);
12803       ScrollScreen(NULL, SCROLL_GO_ON);
12804
12805       AdvanceFrameAndPlayerCounters(player->index_nr);
12806
12807       DrawAllPlayers();
12808       BackToFront_WithFrameDelay(0);
12809     }
12810
12811     player->move_delay_value = original_move_delay_value;
12812   }
12813
12814   player->is_active = FALSE;
12815
12816   if (player->last_move_dir & MV_HORIZONTAL)
12817   {
12818     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12819       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12820   }
12821   else
12822   {
12823     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12824       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12825   }
12826
12827   if (!moved && !player->is_active)
12828   {
12829     player->is_moving = FALSE;
12830     player->is_digging = FALSE;
12831     player->is_collecting = FALSE;
12832     player->is_snapping = FALSE;
12833     player->is_pushing = FALSE;
12834   }
12835
12836   jx = player->jx;
12837   jy = player->jy;
12838
12839   if (moved & MP_MOVING && !ScreenMovPos &&
12840       (player->index_nr == game.centered_player_nr ||
12841        game.centered_player_nr == -1))
12842   {
12843     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12844
12845     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12846     {
12847       // actual player has left the screen -- scroll in that direction
12848       if (jx != old_jx)         // player has moved horizontally
12849         scroll_x += (jx - old_jx);
12850       else                      // player has moved vertically
12851         scroll_y += (jy - old_jy);
12852     }
12853     else
12854     {
12855       int offset_raw = game.scroll_delay_value;
12856
12857       if (jx != old_jx)         // player has moved horizontally
12858       {
12859         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12860         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12861         int new_scroll_x = jx - MIDPOSX + offset_x;
12862
12863         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12864             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12865           scroll_x = new_scroll_x;
12866
12867         // don't scroll over playfield boundaries
12868         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12869
12870         // don't scroll more than one field at a time
12871         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12872
12873         // don't scroll against the player's moving direction
12874         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12875             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12876           scroll_x = old_scroll_x;
12877       }
12878       else                      // player has moved vertically
12879       {
12880         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12881         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12882         int new_scroll_y = jy - MIDPOSY + offset_y;
12883
12884         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12885             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12886           scroll_y = new_scroll_y;
12887
12888         // don't scroll over playfield boundaries
12889         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12890
12891         // don't scroll more than one field at a time
12892         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12893
12894         // don't scroll against the player's moving direction
12895         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12896             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12897           scroll_y = old_scroll_y;
12898       }
12899     }
12900
12901     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12902     {
12903       if (!network.enabled && game.centered_player_nr == -1 &&
12904           !AllPlayersInVisibleScreen())
12905       {
12906         scroll_x = old_scroll_x;
12907         scroll_y = old_scroll_y;
12908       }
12909       else
12910       {
12911         ScrollScreen(player, SCROLL_INIT);
12912         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12913       }
12914     }
12915   }
12916
12917   player->StepFrame = 0;
12918
12919   if (moved & MP_MOVING)
12920   {
12921     if (old_jx != jx && old_jy == jy)
12922       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12923     else if (old_jx == jx && old_jy != jy)
12924       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12925
12926     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12927
12928     player->last_move_dir = player->MovDir;
12929     player->is_moving = TRUE;
12930     player->is_snapping = FALSE;
12931     player->is_switching = FALSE;
12932     player->is_dropping = FALSE;
12933     player->is_dropping_pressed = FALSE;
12934     player->drop_pressed_delay = 0;
12935
12936 #if 0
12937     // should better be called here than above, but this breaks some tapes
12938     ScrollPlayer(player, SCROLL_INIT);
12939 #endif
12940   }
12941   else
12942   {
12943     CheckGravityMovementWhenNotMoving(player);
12944
12945     player->is_moving = FALSE;
12946
12947     /* at this point, the player is allowed to move, but cannot move right now
12948        (e.g. because of something blocking the way) -- ensure that the player
12949        is also allowed to move in the next frame (in old versions before 3.1.1,
12950        the player was forced to wait again for eight frames before next try) */
12951
12952     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12953       player->move_delay = 0;   // allow direct movement in the next frame
12954   }
12955
12956   if (player->move_delay == -1)         // not yet initialized by DigField()
12957     player->move_delay = player->move_delay_value;
12958
12959   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12960   {
12961     TestIfPlayerTouchesBadThing(jx, jy);
12962     TestIfPlayerTouchesCustomElement(jx, jy);
12963   }
12964
12965   if (!player->active)
12966     RemovePlayer(player);
12967
12968   return moved;
12969 }
12970
12971 void ScrollPlayer(struct PlayerInfo *player, int mode)
12972 {
12973   int jx = player->jx, jy = player->jy;
12974   int last_jx = player->last_jx, last_jy = player->last_jy;
12975   int move_stepsize = TILEX / player->move_delay_value;
12976
12977   if (!player->active)
12978     return;
12979
12980   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12981     return;
12982
12983   if (mode == SCROLL_INIT)
12984   {
12985     player->actual_frame_counter = FrameCounter;
12986     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12987
12988     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12989         Tile[last_jx][last_jy] == EL_EMPTY)
12990     {
12991       int last_field_block_delay = 0;   // start with no blocking at all
12992       int block_delay_adjustment = player->block_delay_adjustment;
12993
12994       // if player blocks last field, add delay for exactly one move
12995       if (player->block_last_field)
12996       {
12997         last_field_block_delay += player->move_delay_value;
12998
12999         // when blocking enabled, prevent moving up despite gravity
13000         if (player->gravity && player->MovDir == MV_UP)
13001           block_delay_adjustment = -1;
13002       }
13003
13004       // add block delay adjustment (also possible when not blocking)
13005       last_field_block_delay += block_delay_adjustment;
13006
13007       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13008       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13009     }
13010
13011     if (player->MovPos != 0)    // player has not yet reached destination
13012       return;
13013   }
13014   else if (!FrameReached(&player->actual_frame_counter, 1))
13015     return;
13016
13017   if (player->MovPos != 0)
13018   {
13019     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13020     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13021
13022     // before DrawPlayer() to draw correct player graphic for this case
13023     if (player->MovPos == 0)
13024       CheckGravityMovement(player);
13025   }
13026
13027   if (player->MovPos == 0)      // player reached destination field
13028   {
13029     if (player->move_delay_reset_counter > 0)
13030     {
13031       player->move_delay_reset_counter--;
13032
13033       if (player->move_delay_reset_counter == 0)
13034       {
13035         // continue with normal speed after quickly moving through gate
13036         HALVE_PLAYER_SPEED(player);
13037
13038         // be able to make the next move without delay
13039         player->move_delay = 0;
13040       }
13041     }
13042
13043     player->last_jx = jx;
13044     player->last_jy = jy;
13045
13046     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13047         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13048         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13049         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13050         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13051         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13052         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13053         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13054     {
13055       ExitPlayer(player);
13056
13057       if (game.players_still_needed == 0 &&
13058           (game.friends_still_needed == 0 ||
13059            IS_SP_ELEMENT(Tile[jx][jy])))
13060         LevelSolved();
13061     }
13062
13063     // this breaks one level: "machine", level 000
13064     {
13065       int move_direction = player->MovDir;
13066       int enter_side = MV_DIR_OPPOSITE(move_direction);
13067       int leave_side = move_direction;
13068       int old_jx = last_jx;
13069       int old_jy = last_jy;
13070       int old_element = Tile[old_jx][old_jy];
13071       int new_element = Tile[jx][jy];
13072
13073       if (IS_CUSTOM_ELEMENT(old_element))
13074         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13075                                    CE_LEFT_BY_PLAYER,
13076                                    player->index_bit, leave_side);
13077
13078       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13079                                           CE_PLAYER_LEAVES_X,
13080                                           player->index_bit, leave_side);
13081
13082       if (IS_CUSTOM_ELEMENT(new_element))
13083         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13084                                    player->index_bit, enter_side);
13085
13086       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13087                                           CE_PLAYER_ENTERS_X,
13088                                           player->index_bit, enter_side);
13089
13090       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13091                                         CE_MOVE_OF_X, move_direction);
13092     }
13093
13094     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13095     {
13096       TestIfPlayerTouchesBadThing(jx, jy);
13097       TestIfPlayerTouchesCustomElement(jx, jy);
13098
13099       /* needed because pushed element has not yet reached its destination,
13100          so it would trigger a change event at its previous field location */
13101       if (!player->is_pushing)
13102         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13103
13104       if (level.finish_dig_collect &&
13105           (player->is_digging || player->is_collecting))
13106       {
13107         int last_element = player->last_removed_element;
13108         int move_direction = player->MovDir;
13109         int enter_side = MV_DIR_OPPOSITE(move_direction);
13110         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13111                             CE_PLAYER_COLLECTS_X);
13112
13113         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13114                                             player->index_bit, enter_side);
13115
13116         player->last_removed_element = EL_UNDEFINED;
13117       }
13118
13119       if (!player->active)
13120         RemovePlayer(player);
13121     }
13122
13123     if (!game.LevelSolved && level.use_step_counter)
13124     {
13125       int i;
13126
13127       TimePlayed++;
13128
13129       if (TimeLeft > 0)
13130       {
13131         TimeLeft--;
13132
13133         if (TimeLeft <= 10 && setup.time_limit)
13134           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13135
13136         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13137
13138         DisplayGameControlValues();
13139
13140         if (!TimeLeft && setup.time_limit)
13141           for (i = 0; i < MAX_PLAYERS; i++)
13142             KillPlayer(&stored_player[i]);
13143       }
13144       else if (game.no_time_limit && !game.all_players_gone)
13145       {
13146         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13147
13148         DisplayGameControlValues();
13149       }
13150     }
13151
13152     if (tape.single_step && tape.recording && !tape.pausing &&
13153         !player->programmed_action)
13154       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13155
13156     if (!player->programmed_action)
13157       CheckSaveEngineSnapshot(player);
13158   }
13159 }
13160
13161 void ScrollScreen(struct PlayerInfo *player, int mode)
13162 {
13163   static unsigned int screen_frame_counter = 0;
13164
13165   if (mode == SCROLL_INIT)
13166   {
13167     // set scrolling step size according to actual player's moving speed
13168     ScrollStepSize = TILEX / player->move_delay_value;
13169
13170     screen_frame_counter = FrameCounter;
13171     ScreenMovDir = player->MovDir;
13172     ScreenMovPos = player->MovPos;
13173     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13174     return;
13175   }
13176   else if (!FrameReached(&screen_frame_counter, 1))
13177     return;
13178
13179   if (ScreenMovPos)
13180   {
13181     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13182     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13183     redraw_mask |= REDRAW_FIELD;
13184   }
13185   else
13186     ScreenMovDir = MV_NONE;
13187 }
13188
13189 void TestIfPlayerTouchesCustomElement(int x, int y)
13190 {
13191   static int xy[4][2] =
13192   {
13193     { 0, -1 },
13194     { -1, 0 },
13195     { +1, 0 },
13196     { 0, +1 }
13197   };
13198   static int trigger_sides[4][2] =
13199   {
13200     // center side       border side
13201     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13202     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13203     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13204     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13205   };
13206   static int touch_dir[4] =
13207   {
13208     MV_LEFT | MV_RIGHT,
13209     MV_UP   | MV_DOWN,
13210     MV_UP   | MV_DOWN,
13211     MV_LEFT | MV_RIGHT
13212   };
13213   int center_element = Tile[x][y];      // should always be non-moving!
13214   int i;
13215
13216   for (i = 0; i < NUM_DIRECTIONS; i++)
13217   {
13218     int xx = x + xy[i][0];
13219     int yy = y + xy[i][1];
13220     int center_side = trigger_sides[i][0];
13221     int border_side = trigger_sides[i][1];
13222     int border_element;
13223
13224     if (!IN_LEV_FIELD(xx, yy))
13225       continue;
13226
13227     if (IS_PLAYER(x, y))                // player found at center element
13228     {
13229       struct PlayerInfo *player = PLAYERINFO(x, y);
13230
13231       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13232         border_element = Tile[xx][yy];          // may be moving!
13233       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13234         border_element = Tile[xx][yy];
13235       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13236         border_element = MovingOrBlocked2Element(xx, yy);
13237       else
13238         continue;               // center and border element do not touch
13239
13240       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13241                                  player->index_bit, border_side);
13242       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13243                                           CE_PLAYER_TOUCHES_X,
13244                                           player->index_bit, border_side);
13245
13246       {
13247         /* use player element that is initially defined in the level playfield,
13248            not the player element that corresponds to the runtime player number
13249            (example: a level that contains EL_PLAYER_3 as the only player would
13250            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13251         int player_element = PLAYERINFO(x, y)->initial_element;
13252
13253         CheckElementChangeBySide(xx, yy, border_element, player_element,
13254                                  CE_TOUCHING_X, border_side);
13255       }
13256     }
13257     else if (IS_PLAYER(xx, yy))         // player found at border element
13258     {
13259       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13260
13261       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13262       {
13263         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13264           continue;             // center and border element do not touch
13265       }
13266
13267       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13268                                  player->index_bit, center_side);
13269       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13270                                           CE_PLAYER_TOUCHES_X,
13271                                           player->index_bit, center_side);
13272
13273       {
13274         /* use player element that is initially defined in the level playfield,
13275            not the player element that corresponds to the runtime player number
13276            (example: a level that contains EL_PLAYER_3 as the only player would
13277            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13278         int player_element = PLAYERINFO(xx, yy)->initial_element;
13279
13280         CheckElementChangeBySide(x, y, center_element, player_element,
13281                                  CE_TOUCHING_X, center_side);
13282       }
13283
13284       break;
13285     }
13286   }
13287 }
13288
13289 void TestIfElementTouchesCustomElement(int x, int y)
13290 {
13291   static int xy[4][2] =
13292   {
13293     { 0, -1 },
13294     { -1, 0 },
13295     { +1, 0 },
13296     { 0, +1 }
13297   };
13298   static int trigger_sides[4][2] =
13299   {
13300     // center side      border side
13301     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13302     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13303     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13304     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13305   };
13306   static int touch_dir[4] =
13307   {
13308     MV_LEFT | MV_RIGHT,
13309     MV_UP   | MV_DOWN,
13310     MV_UP   | MV_DOWN,
13311     MV_LEFT | MV_RIGHT
13312   };
13313   boolean change_center_element = FALSE;
13314   int center_element = Tile[x][y];      // should always be non-moving!
13315   int border_element_old[NUM_DIRECTIONS];
13316   int i;
13317
13318   for (i = 0; i < NUM_DIRECTIONS; i++)
13319   {
13320     int xx = x + xy[i][0];
13321     int yy = y + xy[i][1];
13322     int border_element;
13323
13324     border_element_old[i] = -1;
13325
13326     if (!IN_LEV_FIELD(xx, yy))
13327       continue;
13328
13329     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13330       border_element = Tile[xx][yy];    // may be moving!
13331     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13332       border_element = Tile[xx][yy];
13333     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13334       border_element = MovingOrBlocked2Element(xx, yy);
13335     else
13336       continue;                 // center and border element do not touch
13337
13338     border_element_old[i] = border_element;
13339   }
13340
13341   for (i = 0; i < NUM_DIRECTIONS; i++)
13342   {
13343     int xx = x + xy[i][0];
13344     int yy = y + xy[i][1];
13345     int center_side = trigger_sides[i][0];
13346     int border_element = border_element_old[i];
13347
13348     if (border_element == -1)
13349       continue;
13350
13351     // check for change of border element
13352     CheckElementChangeBySide(xx, yy, border_element, center_element,
13353                              CE_TOUCHING_X, center_side);
13354
13355     // (center element cannot be player, so we dont have to check this here)
13356   }
13357
13358   for (i = 0; i < NUM_DIRECTIONS; i++)
13359   {
13360     int xx = x + xy[i][0];
13361     int yy = y + xy[i][1];
13362     int border_side = trigger_sides[i][1];
13363     int border_element = border_element_old[i];
13364
13365     if (border_element == -1)
13366       continue;
13367
13368     // check for change of center element (but change it only once)
13369     if (!change_center_element)
13370       change_center_element =
13371         CheckElementChangeBySide(x, y, center_element, border_element,
13372                                  CE_TOUCHING_X, border_side);
13373
13374     if (IS_PLAYER(xx, yy))
13375     {
13376       /* use player element that is initially defined in the level playfield,
13377          not the player element that corresponds to the runtime player number
13378          (example: a level that contains EL_PLAYER_3 as the only player would
13379          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13380       int player_element = PLAYERINFO(xx, yy)->initial_element;
13381
13382       CheckElementChangeBySide(x, y, center_element, player_element,
13383                                CE_TOUCHING_X, border_side);
13384     }
13385   }
13386 }
13387
13388 void TestIfElementHitsCustomElement(int x, int y, int direction)
13389 {
13390   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13391   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13392   int hitx = x + dx, hity = y + dy;
13393   int hitting_element = Tile[x][y];
13394   int touched_element;
13395
13396   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13397     return;
13398
13399   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13400                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13401
13402   if (IN_LEV_FIELD(hitx, hity))
13403   {
13404     int opposite_direction = MV_DIR_OPPOSITE(direction);
13405     int hitting_side = direction;
13406     int touched_side = opposite_direction;
13407     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13408                           MovDir[hitx][hity] != direction ||
13409                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13410
13411     object_hit = TRUE;
13412
13413     if (object_hit)
13414     {
13415       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13416                                CE_HITTING_X, touched_side);
13417
13418       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13419                                CE_HIT_BY_X, hitting_side);
13420
13421       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13422                                CE_HIT_BY_SOMETHING, opposite_direction);
13423
13424       if (IS_PLAYER(hitx, hity))
13425       {
13426         /* use player element that is initially defined in the level playfield,
13427            not the player element that corresponds to the runtime player number
13428            (example: a level that contains EL_PLAYER_3 as the only player would
13429            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13430         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13431
13432         CheckElementChangeBySide(x, y, hitting_element, player_element,
13433                                  CE_HITTING_X, touched_side);
13434       }
13435     }
13436   }
13437
13438   // "hitting something" is also true when hitting the playfield border
13439   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13440                            CE_HITTING_SOMETHING, direction);
13441 }
13442
13443 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13444 {
13445   int i, kill_x = -1, kill_y = -1;
13446
13447   int bad_element = -1;
13448   static int test_xy[4][2] =
13449   {
13450     { 0, -1 },
13451     { -1, 0 },
13452     { +1, 0 },
13453     { 0, +1 }
13454   };
13455   static int test_dir[4] =
13456   {
13457     MV_UP,
13458     MV_LEFT,
13459     MV_RIGHT,
13460     MV_DOWN
13461   };
13462
13463   for (i = 0; i < NUM_DIRECTIONS; i++)
13464   {
13465     int test_x, test_y, test_move_dir, test_element;
13466
13467     test_x = good_x + test_xy[i][0];
13468     test_y = good_y + test_xy[i][1];
13469
13470     if (!IN_LEV_FIELD(test_x, test_y))
13471       continue;
13472
13473     test_move_dir =
13474       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13475
13476     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13477
13478     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13479        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13480     */
13481     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13482         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13483     {
13484       kill_x = test_x;
13485       kill_y = test_y;
13486       bad_element = test_element;
13487
13488       break;
13489     }
13490   }
13491
13492   if (kill_x != -1 || kill_y != -1)
13493   {
13494     if (IS_PLAYER(good_x, good_y))
13495     {
13496       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13497
13498       if (player->shield_deadly_time_left > 0 &&
13499           !IS_INDESTRUCTIBLE(bad_element))
13500         Bang(kill_x, kill_y);
13501       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13502         KillPlayer(player);
13503     }
13504     else
13505       Bang(good_x, good_y);
13506   }
13507 }
13508
13509 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13510 {
13511   int i, kill_x = -1, kill_y = -1;
13512   int bad_element = Tile[bad_x][bad_y];
13513   static int test_xy[4][2] =
13514   {
13515     { 0, -1 },
13516     { -1, 0 },
13517     { +1, 0 },
13518     { 0, +1 }
13519   };
13520   static int touch_dir[4] =
13521   {
13522     MV_LEFT | MV_RIGHT,
13523     MV_UP   | MV_DOWN,
13524     MV_UP   | MV_DOWN,
13525     MV_LEFT | MV_RIGHT
13526   };
13527   static int test_dir[4] =
13528   {
13529     MV_UP,
13530     MV_LEFT,
13531     MV_RIGHT,
13532     MV_DOWN
13533   };
13534
13535   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13536     return;
13537
13538   for (i = 0; i < NUM_DIRECTIONS; i++)
13539   {
13540     int test_x, test_y, test_move_dir, test_element;
13541
13542     test_x = bad_x + test_xy[i][0];
13543     test_y = bad_y + test_xy[i][1];
13544
13545     if (!IN_LEV_FIELD(test_x, test_y))
13546       continue;
13547
13548     test_move_dir =
13549       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13550
13551     test_element = Tile[test_x][test_y];
13552
13553     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13554        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13555     */
13556     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13557         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13558     {
13559       // good thing is player or penguin that does not move away
13560       if (IS_PLAYER(test_x, test_y))
13561       {
13562         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13563
13564         if (bad_element == EL_ROBOT && player->is_moving)
13565           continue;     // robot does not kill player if he is moving
13566
13567         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13568         {
13569           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13570             continue;           // center and border element do not touch
13571         }
13572
13573         kill_x = test_x;
13574         kill_y = test_y;
13575
13576         break;
13577       }
13578       else if (test_element == EL_PENGUIN)
13579       {
13580         kill_x = test_x;
13581         kill_y = test_y;
13582
13583         break;
13584       }
13585     }
13586   }
13587
13588   if (kill_x != -1 || kill_y != -1)
13589   {
13590     if (IS_PLAYER(kill_x, kill_y))
13591     {
13592       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13593
13594       if (player->shield_deadly_time_left > 0 &&
13595           !IS_INDESTRUCTIBLE(bad_element))
13596         Bang(bad_x, bad_y);
13597       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13598         KillPlayer(player);
13599     }
13600     else
13601       Bang(kill_x, kill_y);
13602   }
13603 }
13604
13605 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13606 {
13607   int bad_element = Tile[bad_x][bad_y];
13608   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13609   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13610   int test_x = bad_x + dx, test_y = bad_y + dy;
13611   int test_move_dir, test_element;
13612   int kill_x = -1, kill_y = -1;
13613
13614   if (!IN_LEV_FIELD(test_x, test_y))
13615     return;
13616
13617   test_move_dir =
13618     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13619
13620   test_element = Tile[test_x][test_y];
13621
13622   if (test_move_dir != bad_move_dir)
13623   {
13624     // good thing can be player or penguin that does not move away
13625     if (IS_PLAYER(test_x, test_y))
13626     {
13627       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13628
13629       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13630          player as being hit when he is moving towards the bad thing, because
13631          the "get hit by" condition would be lost after the player stops) */
13632       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13633         return;         // player moves away from bad thing
13634
13635       kill_x = test_x;
13636       kill_y = test_y;
13637     }
13638     else if (test_element == EL_PENGUIN)
13639     {
13640       kill_x = test_x;
13641       kill_y = test_y;
13642     }
13643   }
13644
13645   if (kill_x != -1 || kill_y != -1)
13646   {
13647     if (IS_PLAYER(kill_x, kill_y))
13648     {
13649       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13650
13651       if (player->shield_deadly_time_left > 0 &&
13652           !IS_INDESTRUCTIBLE(bad_element))
13653         Bang(bad_x, bad_y);
13654       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13655         KillPlayer(player);
13656     }
13657     else
13658       Bang(kill_x, kill_y);
13659   }
13660 }
13661
13662 void TestIfPlayerTouchesBadThing(int x, int y)
13663 {
13664   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13665 }
13666
13667 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13668 {
13669   TestIfGoodThingHitsBadThing(x, y, move_dir);
13670 }
13671
13672 void TestIfBadThingTouchesPlayer(int x, int y)
13673 {
13674   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13675 }
13676
13677 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13678 {
13679   TestIfBadThingHitsGoodThing(x, y, move_dir);
13680 }
13681
13682 void TestIfFriendTouchesBadThing(int x, int y)
13683 {
13684   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13685 }
13686
13687 void TestIfBadThingTouchesFriend(int x, int y)
13688 {
13689   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13690 }
13691
13692 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13693 {
13694   int i, kill_x = bad_x, kill_y = bad_y;
13695   static int xy[4][2] =
13696   {
13697     { 0, -1 },
13698     { -1, 0 },
13699     { +1, 0 },
13700     { 0, +1 }
13701   };
13702
13703   for (i = 0; i < NUM_DIRECTIONS; i++)
13704   {
13705     int x, y, element;
13706
13707     x = bad_x + xy[i][0];
13708     y = bad_y + xy[i][1];
13709     if (!IN_LEV_FIELD(x, y))
13710       continue;
13711
13712     element = Tile[x][y];
13713     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13714         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13715     {
13716       kill_x = x;
13717       kill_y = y;
13718       break;
13719     }
13720   }
13721
13722   if (kill_x != bad_x || kill_y != bad_y)
13723     Bang(bad_x, bad_y);
13724 }
13725
13726 void KillPlayer(struct PlayerInfo *player)
13727 {
13728   int jx = player->jx, jy = player->jy;
13729
13730   if (!player->active)
13731     return;
13732
13733 #if 0
13734   Debug("game:playing:KillPlayer",
13735         "0: killed == %d, active == %d, reanimated == %d",
13736         player->killed, player->active, player->reanimated);
13737 #endif
13738
13739   /* the following code was introduced to prevent an infinite loop when calling
13740      -> Bang()
13741      -> CheckTriggeredElementChangeExt()
13742      -> ExecuteCustomElementAction()
13743      -> KillPlayer()
13744      -> (infinitely repeating the above sequence of function calls)
13745      which occurs when killing the player while having a CE with the setting
13746      "kill player X when explosion of <player X>"; the solution using a new
13747      field "player->killed" was chosen for backwards compatibility, although
13748      clever use of the fields "player->active" etc. would probably also work */
13749 #if 1
13750   if (player->killed)
13751     return;
13752 #endif
13753
13754   player->killed = TRUE;
13755
13756   // remove accessible field at the player's position
13757   Tile[jx][jy] = EL_EMPTY;
13758
13759   // deactivate shield (else Bang()/Explode() would not work right)
13760   player->shield_normal_time_left = 0;
13761   player->shield_deadly_time_left = 0;
13762
13763 #if 0
13764   Debug("game:playing:KillPlayer",
13765         "1: killed == %d, active == %d, reanimated == %d",
13766         player->killed, player->active, player->reanimated);
13767 #endif
13768
13769   Bang(jx, jy);
13770
13771 #if 0
13772   Debug("game:playing:KillPlayer",
13773         "2: killed == %d, active == %d, reanimated == %d",
13774         player->killed, player->active, player->reanimated);
13775 #endif
13776
13777   if (player->reanimated)       // killed player may have been reanimated
13778     player->killed = player->reanimated = FALSE;
13779   else
13780     BuryPlayer(player);
13781 }
13782
13783 static void KillPlayerUnlessEnemyProtected(int x, int y)
13784 {
13785   if (!PLAYER_ENEMY_PROTECTED(x, y))
13786     KillPlayer(PLAYERINFO(x, y));
13787 }
13788
13789 static void KillPlayerUnlessExplosionProtected(int x, int y)
13790 {
13791   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13792     KillPlayer(PLAYERINFO(x, y));
13793 }
13794
13795 void BuryPlayer(struct PlayerInfo *player)
13796 {
13797   int jx = player->jx, jy = player->jy;
13798
13799   if (!player->active)
13800     return;
13801
13802   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13803   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13804
13805   RemovePlayer(player);
13806
13807   player->buried = TRUE;
13808
13809   if (game.all_players_gone)
13810     game.GameOver = TRUE;
13811 }
13812
13813 void RemovePlayer(struct PlayerInfo *player)
13814 {
13815   int jx = player->jx, jy = player->jy;
13816   int i, found = FALSE;
13817
13818   player->present = FALSE;
13819   player->active = FALSE;
13820
13821   // required for some CE actions (even if the player is not active anymore)
13822   player->MovPos = 0;
13823
13824   if (!ExplodeField[jx][jy])
13825     StorePlayer[jx][jy] = 0;
13826
13827   if (player->is_moving)
13828     TEST_DrawLevelField(player->last_jx, player->last_jy);
13829
13830   for (i = 0; i < MAX_PLAYERS; i++)
13831     if (stored_player[i].active)
13832       found = TRUE;
13833
13834   if (!found)
13835   {
13836     game.all_players_gone = TRUE;
13837     game.GameOver = TRUE;
13838   }
13839
13840   game.exit_x = game.robot_wheel_x = jx;
13841   game.exit_y = game.robot_wheel_y = jy;
13842 }
13843
13844 void ExitPlayer(struct PlayerInfo *player)
13845 {
13846   DrawPlayer(player);   // needed here only to cleanup last field
13847   RemovePlayer(player);
13848
13849   if (game.players_still_needed > 0)
13850     game.players_still_needed--;
13851 }
13852
13853 static void SetFieldForSnapping(int x, int y, int element, int direction,
13854                                 int player_index_bit)
13855 {
13856   struct ElementInfo *ei = &element_info[element];
13857   int direction_bit = MV_DIR_TO_BIT(direction);
13858   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13859   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13860                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13861
13862   Tile[x][y] = EL_ELEMENT_SNAPPING;
13863   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13864   MovDir[x][y] = direction;
13865   Store[x][y] = element;
13866   Store2[x][y] = player_index_bit;
13867
13868   ResetGfxAnimation(x, y);
13869
13870   GfxElement[x][y] = element;
13871   GfxAction[x][y] = action;
13872   GfxDir[x][y] = direction;
13873   GfxFrame[x][y] = -1;
13874 }
13875
13876 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13877                                    int player_index_bit)
13878 {
13879   TestIfElementTouchesCustomElement(x, y);      // for empty space
13880
13881   if (level.finish_dig_collect)
13882   {
13883     int dig_side = MV_DIR_OPPOSITE(direction);
13884
13885     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13886                                         player_index_bit, dig_side);
13887   }
13888 }
13889
13890 /*
13891   =============================================================================
13892   checkDiagonalPushing()
13893   -----------------------------------------------------------------------------
13894   check if diagonal input device direction results in pushing of object
13895   (by checking if the alternative direction is walkable, diggable, ...)
13896   =============================================================================
13897 */
13898
13899 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13900                                     int x, int y, int real_dx, int real_dy)
13901 {
13902   int jx, jy, dx, dy, xx, yy;
13903
13904   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13905     return TRUE;
13906
13907   // diagonal direction: check alternative direction
13908   jx = player->jx;
13909   jy = player->jy;
13910   dx = x - jx;
13911   dy = y - jy;
13912   xx = jx + (dx == 0 ? real_dx : 0);
13913   yy = jy + (dy == 0 ? real_dy : 0);
13914
13915   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13916 }
13917
13918 /*
13919   =============================================================================
13920   DigField()
13921   -----------------------------------------------------------------------------
13922   x, y:                 field next to player (non-diagonal) to try to dig to
13923   real_dx, real_dy:     direction as read from input device (can be diagonal)
13924   =============================================================================
13925 */
13926
13927 static int DigField(struct PlayerInfo *player,
13928                     int oldx, int oldy, int x, int y,
13929                     int real_dx, int real_dy, int mode)
13930 {
13931   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13932   boolean player_was_pushing = player->is_pushing;
13933   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13934   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13935   int jx = oldx, jy = oldy;
13936   int dx = x - jx, dy = y - jy;
13937   int nextx = x + dx, nexty = y + dy;
13938   int move_direction = (dx == -1 ? MV_LEFT  :
13939                         dx == +1 ? MV_RIGHT :
13940                         dy == -1 ? MV_UP    :
13941                         dy == +1 ? MV_DOWN  : MV_NONE);
13942   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13943   int dig_side = MV_DIR_OPPOSITE(move_direction);
13944   int old_element = Tile[jx][jy];
13945   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13946   int collect_count;
13947
13948   if (is_player)                // function can also be called by EL_PENGUIN
13949   {
13950     if (player->MovPos == 0)
13951     {
13952       player->is_digging = FALSE;
13953       player->is_collecting = FALSE;
13954     }
13955
13956     if (player->MovPos == 0)    // last pushing move finished
13957       player->is_pushing = FALSE;
13958
13959     if (mode == DF_NO_PUSH)     // player just stopped pushing
13960     {
13961       player->is_switching = FALSE;
13962       player->push_delay = -1;
13963
13964       return MP_NO_ACTION;
13965     }
13966   }
13967
13968   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13969     old_element = Back[jx][jy];
13970
13971   // in case of element dropped at player position, check background
13972   else if (Back[jx][jy] != EL_EMPTY &&
13973            game.engine_version >= VERSION_IDENT(2,2,0,0))
13974     old_element = Back[jx][jy];
13975
13976   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13977     return MP_NO_ACTION;        // field has no opening in this direction
13978
13979   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13980     return MP_NO_ACTION;        // field has no opening in this direction
13981
13982   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13983   {
13984     SplashAcid(x, y);
13985
13986     Tile[jx][jy] = player->artwork_element;
13987     InitMovingField(jx, jy, MV_DOWN);
13988     Store[jx][jy] = EL_ACID;
13989     ContinueMoving(jx, jy);
13990     BuryPlayer(player);
13991
13992     return MP_DONT_RUN_INTO;
13993   }
13994
13995   if (player_can_move && DONT_RUN_INTO(element))
13996   {
13997     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13998
13999     return MP_DONT_RUN_INTO;
14000   }
14001
14002   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14003     return MP_NO_ACTION;
14004
14005   collect_count = element_info[element].collect_count_initial;
14006
14007   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14008     return MP_NO_ACTION;
14009
14010   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14011     player_can_move = player_can_move_or_snap;
14012
14013   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14014       game.engine_version >= VERSION_IDENT(2,2,0,0))
14015   {
14016     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14017                                player->index_bit, dig_side);
14018     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14019                                         player->index_bit, dig_side);
14020
14021     if (element == EL_DC_LANDMINE)
14022       Bang(x, y);
14023
14024     if (Tile[x][y] != element)          // field changed by snapping
14025       return MP_ACTION;
14026
14027     return MP_NO_ACTION;
14028   }
14029
14030   if (player->gravity && is_player && !player->is_auto_moving &&
14031       canFallDown(player) && move_direction != MV_DOWN &&
14032       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14033     return MP_NO_ACTION;        // player cannot walk here due to gravity
14034
14035   if (player_can_move &&
14036       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14037   {
14038     int sound_element = SND_ELEMENT(element);
14039     int sound_action = ACTION_WALKING;
14040
14041     if (IS_RND_GATE(element))
14042     {
14043       if (!player->key[RND_GATE_NR(element)])
14044         return MP_NO_ACTION;
14045     }
14046     else if (IS_RND_GATE_GRAY(element))
14047     {
14048       if (!player->key[RND_GATE_GRAY_NR(element)])
14049         return MP_NO_ACTION;
14050     }
14051     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14052     {
14053       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14054         return MP_NO_ACTION;
14055     }
14056     else if (element == EL_EXIT_OPEN ||
14057              element == EL_EM_EXIT_OPEN ||
14058              element == EL_EM_EXIT_OPENING ||
14059              element == EL_STEEL_EXIT_OPEN ||
14060              element == EL_EM_STEEL_EXIT_OPEN ||
14061              element == EL_EM_STEEL_EXIT_OPENING ||
14062              element == EL_SP_EXIT_OPEN ||
14063              element == EL_SP_EXIT_OPENING)
14064     {
14065       sound_action = ACTION_PASSING;    // player is passing exit
14066     }
14067     else if (element == EL_EMPTY)
14068     {
14069       sound_action = ACTION_MOVING;             // nothing to walk on
14070     }
14071
14072     // play sound from background or player, whatever is available
14073     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14074       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14075     else
14076       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14077   }
14078   else if (player_can_move &&
14079            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14080   {
14081     if (!ACCESS_FROM(element, opposite_direction))
14082       return MP_NO_ACTION;      // field not accessible from this direction
14083
14084     if (CAN_MOVE(element))      // only fixed elements can be passed!
14085       return MP_NO_ACTION;
14086
14087     if (IS_EM_GATE(element))
14088     {
14089       if (!player->key[EM_GATE_NR(element)])
14090         return MP_NO_ACTION;
14091     }
14092     else if (IS_EM_GATE_GRAY(element))
14093     {
14094       if (!player->key[EM_GATE_GRAY_NR(element)])
14095         return MP_NO_ACTION;
14096     }
14097     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14098     {
14099       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14100         return MP_NO_ACTION;
14101     }
14102     else if (IS_EMC_GATE(element))
14103     {
14104       if (!player->key[EMC_GATE_NR(element)])
14105         return MP_NO_ACTION;
14106     }
14107     else if (IS_EMC_GATE_GRAY(element))
14108     {
14109       if (!player->key[EMC_GATE_GRAY_NR(element)])
14110         return MP_NO_ACTION;
14111     }
14112     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14113     {
14114       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14115         return MP_NO_ACTION;
14116     }
14117     else if (element == EL_DC_GATE_WHITE ||
14118              element == EL_DC_GATE_WHITE_GRAY ||
14119              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14120     {
14121       if (player->num_white_keys == 0)
14122         return MP_NO_ACTION;
14123
14124       player->num_white_keys--;
14125     }
14126     else if (IS_SP_PORT(element))
14127     {
14128       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14129           element == EL_SP_GRAVITY_PORT_RIGHT ||
14130           element == EL_SP_GRAVITY_PORT_UP ||
14131           element == EL_SP_GRAVITY_PORT_DOWN)
14132         player->gravity = !player->gravity;
14133       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14134                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14135                element == EL_SP_GRAVITY_ON_PORT_UP ||
14136                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14137         player->gravity = TRUE;
14138       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14139                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14140                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14141                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14142         player->gravity = FALSE;
14143     }
14144
14145     // automatically move to the next field with double speed
14146     player->programmed_action = move_direction;
14147
14148     if (player->move_delay_reset_counter == 0)
14149     {
14150       player->move_delay_reset_counter = 2;     // two double speed steps
14151
14152       DOUBLE_PLAYER_SPEED(player);
14153     }
14154
14155     PlayLevelSoundAction(x, y, ACTION_PASSING);
14156   }
14157   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14158   {
14159     RemoveField(x, y);
14160
14161     if (mode != DF_SNAP)
14162     {
14163       GfxElement[x][y] = GFX_ELEMENT(element);
14164       player->is_digging = TRUE;
14165     }
14166
14167     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14168
14169     // use old behaviour for old levels (digging)
14170     if (!level.finish_dig_collect)
14171     {
14172       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14173                                           player->index_bit, dig_side);
14174
14175       // if digging triggered player relocation, finish digging tile
14176       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14177         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14178     }
14179
14180     if (mode == DF_SNAP)
14181     {
14182       if (level.block_snap_field)
14183         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14184       else
14185         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14186
14187       // use old behaviour for old levels (snapping)
14188       if (!level.finish_dig_collect)
14189         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14190                                             player->index_bit, dig_side);
14191     }
14192   }
14193   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14194   {
14195     RemoveField(x, y);
14196
14197     if (is_player && mode != DF_SNAP)
14198     {
14199       GfxElement[x][y] = element;
14200       player->is_collecting = TRUE;
14201     }
14202
14203     if (element == EL_SPEED_PILL)
14204     {
14205       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14206     }
14207     else if (element == EL_EXTRA_TIME && level.time > 0)
14208     {
14209       TimeLeft += level.extra_time;
14210
14211       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14212
14213       DisplayGameControlValues();
14214     }
14215     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14216     {
14217       player->shield_normal_time_left += level.shield_normal_time;
14218       if (element == EL_SHIELD_DEADLY)
14219         player->shield_deadly_time_left += level.shield_deadly_time;
14220     }
14221     else if (element == EL_DYNAMITE ||
14222              element == EL_EM_DYNAMITE ||
14223              element == EL_SP_DISK_RED)
14224     {
14225       if (player->inventory_size < MAX_INVENTORY_SIZE)
14226         player->inventory_element[player->inventory_size++] = element;
14227
14228       DrawGameDoorValues();
14229     }
14230     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14231     {
14232       player->dynabomb_count++;
14233       player->dynabombs_left++;
14234     }
14235     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14236     {
14237       player->dynabomb_size++;
14238     }
14239     else if (element == EL_DYNABOMB_INCREASE_POWER)
14240     {
14241       player->dynabomb_xl = TRUE;
14242     }
14243     else if (IS_KEY(element))
14244     {
14245       player->key[KEY_NR(element)] = TRUE;
14246
14247       DrawGameDoorValues();
14248     }
14249     else if (element == EL_DC_KEY_WHITE)
14250     {
14251       player->num_white_keys++;
14252
14253       // display white keys?
14254       // DrawGameDoorValues();
14255     }
14256     else if (IS_ENVELOPE(element))
14257     {
14258       player->show_envelope = element;
14259     }
14260     else if (element == EL_EMC_LENSES)
14261     {
14262       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14263
14264       RedrawAllInvisibleElementsForLenses();
14265     }
14266     else if (element == EL_EMC_MAGNIFIER)
14267     {
14268       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14269
14270       RedrawAllInvisibleElementsForMagnifier();
14271     }
14272     else if (IS_DROPPABLE(element) ||
14273              IS_THROWABLE(element))     // can be collected and dropped
14274     {
14275       int i;
14276
14277       if (collect_count == 0)
14278         player->inventory_infinite_element = element;
14279       else
14280         for (i = 0; i < collect_count; i++)
14281           if (player->inventory_size < MAX_INVENTORY_SIZE)
14282             player->inventory_element[player->inventory_size++] = element;
14283
14284       DrawGameDoorValues();
14285     }
14286     else if (collect_count > 0)
14287     {
14288       game.gems_still_needed -= collect_count;
14289       if (game.gems_still_needed < 0)
14290         game.gems_still_needed = 0;
14291
14292       game.snapshot.collected_item = TRUE;
14293
14294       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14295
14296       DisplayGameControlValues();
14297     }
14298
14299     RaiseScoreElement(element);
14300     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14301
14302     // use old behaviour for old levels (collecting)
14303     if (!level.finish_dig_collect && is_player)
14304     {
14305       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14306                                           player->index_bit, dig_side);
14307
14308       // if collecting triggered player relocation, finish collecting tile
14309       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14310         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14311     }
14312
14313     if (mode == DF_SNAP)
14314     {
14315       if (level.block_snap_field)
14316         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14317       else
14318         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14319
14320       // use old behaviour for old levels (snapping)
14321       if (!level.finish_dig_collect)
14322         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14323                                             player->index_bit, dig_side);
14324     }
14325   }
14326   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14327   {
14328     if (mode == DF_SNAP && element != EL_BD_ROCK)
14329       return MP_NO_ACTION;
14330
14331     if (CAN_FALL(element) && dy)
14332       return MP_NO_ACTION;
14333
14334     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14335         !(element == EL_SPRING && level.use_spring_bug))
14336       return MP_NO_ACTION;
14337
14338     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14339         ((move_direction & MV_VERTICAL &&
14340           ((element_info[element].move_pattern & MV_LEFT &&
14341             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14342            (element_info[element].move_pattern & MV_RIGHT &&
14343             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14344          (move_direction & MV_HORIZONTAL &&
14345           ((element_info[element].move_pattern & MV_UP &&
14346             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14347            (element_info[element].move_pattern & MV_DOWN &&
14348             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14349       return MP_NO_ACTION;
14350
14351     // do not push elements already moving away faster than player
14352     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14353         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14354       return MP_NO_ACTION;
14355
14356     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14357     {
14358       if (player->push_delay_value == -1 || !player_was_pushing)
14359         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14360     }
14361     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14362     {
14363       if (player->push_delay_value == -1)
14364         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14365     }
14366     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14367     {
14368       if (!player->is_pushing)
14369         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14370     }
14371
14372     player->is_pushing = TRUE;
14373     player->is_active = TRUE;
14374
14375     if (!(IN_LEV_FIELD(nextx, nexty) &&
14376           (IS_FREE(nextx, nexty) ||
14377            (IS_SB_ELEMENT(element) &&
14378             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14379            (IS_CUSTOM_ELEMENT(element) &&
14380             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14381       return MP_NO_ACTION;
14382
14383     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14384       return MP_NO_ACTION;
14385
14386     if (player->push_delay == -1)       // new pushing; restart delay
14387       player->push_delay = 0;
14388
14389     if (player->push_delay < player->push_delay_value &&
14390         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14391         element != EL_SPRING && element != EL_BALLOON)
14392     {
14393       // make sure that there is no move delay before next try to push
14394       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14395         player->move_delay = 0;
14396
14397       return MP_NO_ACTION;
14398     }
14399
14400     if (IS_CUSTOM_ELEMENT(element) &&
14401         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14402     {
14403       if (!DigFieldByCE(nextx, nexty, element))
14404         return MP_NO_ACTION;
14405     }
14406
14407     if (IS_SB_ELEMENT(element))
14408     {
14409       boolean sokoban_task_solved = FALSE;
14410
14411       if (element == EL_SOKOBAN_FIELD_FULL)
14412       {
14413         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14414
14415         IncrementSokobanFieldsNeeded();
14416         IncrementSokobanObjectsNeeded();
14417       }
14418
14419       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14420       {
14421         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14422
14423         DecrementSokobanFieldsNeeded();
14424         DecrementSokobanObjectsNeeded();
14425
14426         // sokoban object was pushed from empty field to sokoban field
14427         if (Back[x][y] == EL_EMPTY)
14428           sokoban_task_solved = TRUE;
14429       }
14430
14431       Tile[x][y] = EL_SOKOBAN_OBJECT;
14432
14433       if (Back[x][y] == Back[nextx][nexty])
14434         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14435       else if (Back[x][y] != 0)
14436         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14437                                     ACTION_EMPTYING);
14438       else
14439         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14440                                     ACTION_FILLING);
14441
14442       if (sokoban_task_solved &&
14443           game.sokoban_fields_still_needed == 0 &&
14444           game.sokoban_objects_still_needed == 0 &&
14445           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14446       {
14447         game.players_still_needed = 0;
14448
14449         LevelSolved();
14450
14451         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14452       }
14453     }
14454     else
14455       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14456
14457     InitMovingField(x, y, move_direction);
14458     GfxAction[x][y] = ACTION_PUSHING;
14459
14460     if (mode == DF_SNAP)
14461       ContinueMoving(x, y);
14462     else
14463       MovPos[x][y] = (dx != 0 ? dx : dy);
14464
14465     Pushed[x][y] = TRUE;
14466     Pushed[nextx][nexty] = TRUE;
14467
14468     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14469       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14470     else
14471       player->push_delay_value = -1;    // get new value later
14472
14473     // check for element change _after_ element has been pushed
14474     if (game.use_change_when_pushing_bug)
14475     {
14476       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14477                                  player->index_bit, dig_side);
14478       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14479                                           player->index_bit, dig_side);
14480     }
14481   }
14482   else if (IS_SWITCHABLE(element))
14483   {
14484     if (PLAYER_SWITCHING(player, x, y))
14485     {
14486       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14487                                           player->index_bit, dig_side);
14488
14489       return MP_ACTION;
14490     }
14491
14492     player->is_switching = TRUE;
14493     player->switch_x = x;
14494     player->switch_y = y;
14495
14496     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14497
14498     if (element == EL_ROBOT_WHEEL)
14499     {
14500       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14501
14502       game.robot_wheel_x = x;
14503       game.robot_wheel_y = y;
14504       game.robot_wheel_active = TRUE;
14505
14506       TEST_DrawLevelField(x, y);
14507     }
14508     else if (element == EL_SP_TERMINAL)
14509     {
14510       int xx, yy;
14511
14512       SCAN_PLAYFIELD(xx, yy)
14513       {
14514         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14515         {
14516           Bang(xx, yy);
14517         }
14518         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14519         {
14520           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14521
14522           ResetGfxAnimation(xx, yy);
14523           TEST_DrawLevelField(xx, yy);
14524         }
14525       }
14526     }
14527     else if (IS_BELT_SWITCH(element))
14528     {
14529       ToggleBeltSwitch(x, y);
14530     }
14531     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14532              element == EL_SWITCHGATE_SWITCH_DOWN ||
14533              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14534              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14535     {
14536       ToggleSwitchgateSwitch(x, y);
14537     }
14538     else if (element == EL_LIGHT_SWITCH ||
14539              element == EL_LIGHT_SWITCH_ACTIVE)
14540     {
14541       ToggleLightSwitch(x, y);
14542     }
14543     else if (element == EL_TIMEGATE_SWITCH ||
14544              element == EL_DC_TIMEGATE_SWITCH)
14545     {
14546       ActivateTimegateSwitch(x, y);
14547     }
14548     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14549              element == EL_BALLOON_SWITCH_RIGHT ||
14550              element == EL_BALLOON_SWITCH_UP    ||
14551              element == EL_BALLOON_SWITCH_DOWN  ||
14552              element == EL_BALLOON_SWITCH_NONE  ||
14553              element == EL_BALLOON_SWITCH_ANY)
14554     {
14555       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14556                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14557                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14558                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14559                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14560                              move_direction);
14561     }
14562     else if (element == EL_LAMP)
14563     {
14564       Tile[x][y] = EL_LAMP_ACTIVE;
14565       game.lights_still_needed--;
14566
14567       ResetGfxAnimation(x, y);
14568       TEST_DrawLevelField(x, y);
14569     }
14570     else if (element == EL_TIME_ORB_FULL)
14571     {
14572       Tile[x][y] = EL_TIME_ORB_EMPTY;
14573
14574       if (level.time > 0 || level.use_time_orb_bug)
14575       {
14576         TimeLeft += level.time_orb_time;
14577         game.no_time_limit = FALSE;
14578
14579         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14580
14581         DisplayGameControlValues();
14582       }
14583
14584       ResetGfxAnimation(x, y);
14585       TEST_DrawLevelField(x, y);
14586     }
14587     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14588              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14589     {
14590       int xx, yy;
14591
14592       game.ball_active = !game.ball_active;
14593
14594       SCAN_PLAYFIELD(xx, yy)
14595       {
14596         int e = Tile[xx][yy];
14597
14598         if (game.ball_active)
14599         {
14600           if (e == EL_EMC_MAGIC_BALL)
14601             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14602           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14603             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14604         }
14605         else
14606         {
14607           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14608             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14609           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14610             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14611         }
14612       }
14613     }
14614
14615     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14616                                         player->index_bit, dig_side);
14617
14618     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14619                                         player->index_bit, dig_side);
14620
14621     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14622                                         player->index_bit, dig_side);
14623
14624     return MP_ACTION;
14625   }
14626   else
14627   {
14628     if (!PLAYER_SWITCHING(player, x, y))
14629     {
14630       player->is_switching = TRUE;
14631       player->switch_x = x;
14632       player->switch_y = y;
14633
14634       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14635                                  player->index_bit, dig_side);
14636       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14637                                           player->index_bit, dig_side);
14638
14639       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14640                                  player->index_bit, dig_side);
14641       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14642                                           player->index_bit, dig_side);
14643     }
14644
14645     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14646                                player->index_bit, dig_side);
14647     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14648                                         player->index_bit, dig_side);
14649
14650     return MP_NO_ACTION;
14651   }
14652
14653   player->push_delay = -1;
14654
14655   if (is_player)                // function can also be called by EL_PENGUIN
14656   {
14657     if (Tile[x][y] != element)          // really digged/collected something
14658     {
14659       player->is_collecting = !player->is_digging;
14660       player->is_active = TRUE;
14661
14662       player->last_removed_element = element;
14663     }
14664   }
14665
14666   return MP_MOVING;
14667 }
14668
14669 static boolean DigFieldByCE(int x, int y, int digging_element)
14670 {
14671   int element = Tile[x][y];
14672
14673   if (!IS_FREE(x, y))
14674   {
14675     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14676                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14677                   ACTION_BREAKING);
14678
14679     // no element can dig solid indestructible elements
14680     if (IS_INDESTRUCTIBLE(element) &&
14681         !IS_DIGGABLE(element) &&
14682         !IS_COLLECTIBLE(element))
14683       return FALSE;
14684
14685     if (AmoebaNr[x][y] &&
14686         (element == EL_AMOEBA_FULL ||
14687          element == EL_BD_AMOEBA ||
14688          element == EL_AMOEBA_GROWING))
14689     {
14690       AmoebaCnt[AmoebaNr[x][y]]--;
14691       AmoebaCnt2[AmoebaNr[x][y]]--;
14692     }
14693
14694     if (IS_MOVING(x, y))
14695       RemoveMovingField(x, y);
14696     else
14697     {
14698       RemoveField(x, y);
14699       TEST_DrawLevelField(x, y);
14700     }
14701
14702     // if digged element was about to explode, prevent the explosion
14703     ExplodeField[x][y] = EX_TYPE_NONE;
14704
14705     PlayLevelSoundAction(x, y, action);
14706   }
14707
14708   Store[x][y] = EL_EMPTY;
14709
14710   // this makes it possible to leave the removed element again
14711   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14712     Store[x][y] = element;
14713
14714   return TRUE;
14715 }
14716
14717 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14718 {
14719   int jx = player->jx, jy = player->jy;
14720   int x = jx + dx, y = jy + dy;
14721   int snap_direction = (dx == -1 ? MV_LEFT  :
14722                         dx == +1 ? MV_RIGHT :
14723                         dy == -1 ? MV_UP    :
14724                         dy == +1 ? MV_DOWN  : MV_NONE);
14725   boolean can_continue_snapping = (level.continuous_snapping &&
14726                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14727
14728   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14729     return FALSE;
14730
14731   if (!player->active || !IN_LEV_FIELD(x, y))
14732     return FALSE;
14733
14734   if (dx && dy)
14735     return FALSE;
14736
14737   if (!dx && !dy)
14738   {
14739     if (player->MovPos == 0)
14740       player->is_pushing = FALSE;
14741
14742     player->is_snapping = FALSE;
14743
14744     if (player->MovPos == 0)
14745     {
14746       player->is_moving = FALSE;
14747       player->is_digging = FALSE;
14748       player->is_collecting = FALSE;
14749     }
14750
14751     return FALSE;
14752   }
14753
14754   // prevent snapping with already pressed snap key when not allowed
14755   if (player->is_snapping && !can_continue_snapping)
14756     return FALSE;
14757
14758   player->MovDir = snap_direction;
14759
14760   if (player->MovPos == 0)
14761   {
14762     player->is_moving = FALSE;
14763     player->is_digging = FALSE;
14764     player->is_collecting = FALSE;
14765   }
14766
14767   player->is_dropping = FALSE;
14768   player->is_dropping_pressed = FALSE;
14769   player->drop_pressed_delay = 0;
14770
14771   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14772     return FALSE;
14773
14774   player->is_snapping = TRUE;
14775   player->is_active = TRUE;
14776
14777   if (player->MovPos == 0)
14778   {
14779     player->is_moving = FALSE;
14780     player->is_digging = FALSE;
14781     player->is_collecting = FALSE;
14782   }
14783
14784   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14785     TEST_DrawLevelField(player->last_jx, player->last_jy);
14786
14787   TEST_DrawLevelField(x, y);
14788
14789   return TRUE;
14790 }
14791
14792 static boolean DropElement(struct PlayerInfo *player)
14793 {
14794   int old_element, new_element;
14795   int dropx = player->jx, dropy = player->jy;
14796   int drop_direction = player->MovDir;
14797   int drop_side = drop_direction;
14798   int drop_element = get_next_dropped_element(player);
14799
14800   /* do not drop an element on top of another element; when holding drop key
14801      pressed without moving, dropped element must move away before the next
14802      element can be dropped (this is especially important if the next element
14803      is dynamite, which can be placed on background for historical reasons) */
14804   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14805     return MP_ACTION;
14806
14807   if (IS_THROWABLE(drop_element))
14808   {
14809     dropx += GET_DX_FROM_DIR(drop_direction);
14810     dropy += GET_DY_FROM_DIR(drop_direction);
14811
14812     if (!IN_LEV_FIELD(dropx, dropy))
14813       return FALSE;
14814   }
14815
14816   old_element = Tile[dropx][dropy];     // old element at dropping position
14817   new_element = drop_element;           // default: no change when dropping
14818
14819   // check if player is active, not moving and ready to drop
14820   if (!player->active || player->MovPos || player->drop_delay > 0)
14821     return FALSE;
14822
14823   // check if player has anything that can be dropped
14824   if (new_element == EL_UNDEFINED)
14825     return FALSE;
14826
14827   // only set if player has anything that can be dropped
14828   player->is_dropping_pressed = TRUE;
14829
14830   // check if drop key was pressed long enough for EM style dynamite
14831   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14832     return FALSE;
14833
14834   // check if anything can be dropped at the current position
14835   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14836     return FALSE;
14837
14838   // collected custom elements can only be dropped on empty fields
14839   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14840     return FALSE;
14841
14842   if (old_element != EL_EMPTY)
14843     Back[dropx][dropy] = old_element;   // store old element on this field
14844
14845   ResetGfxAnimation(dropx, dropy);
14846   ResetRandomAnimationValue(dropx, dropy);
14847
14848   if (player->inventory_size > 0 ||
14849       player->inventory_infinite_element != EL_UNDEFINED)
14850   {
14851     if (player->inventory_size > 0)
14852     {
14853       player->inventory_size--;
14854
14855       DrawGameDoorValues();
14856
14857       if (new_element == EL_DYNAMITE)
14858         new_element = EL_DYNAMITE_ACTIVE;
14859       else if (new_element == EL_EM_DYNAMITE)
14860         new_element = EL_EM_DYNAMITE_ACTIVE;
14861       else if (new_element == EL_SP_DISK_RED)
14862         new_element = EL_SP_DISK_RED_ACTIVE;
14863     }
14864
14865     Tile[dropx][dropy] = new_element;
14866
14867     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14868       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14869                           el2img(Tile[dropx][dropy]), 0);
14870
14871     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14872
14873     // needed if previous element just changed to "empty" in the last frame
14874     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14875
14876     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14877                                player->index_bit, drop_side);
14878     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14879                                         CE_PLAYER_DROPS_X,
14880                                         player->index_bit, drop_side);
14881
14882     TestIfElementTouchesCustomElement(dropx, dropy);
14883   }
14884   else          // player is dropping a dyna bomb
14885   {
14886     player->dynabombs_left--;
14887
14888     Tile[dropx][dropy] = new_element;
14889
14890     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14891       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14892                           el2img(Tile[dropx][dropy]), 0);
14893
14894     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14895   }
14896
14897   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14898     InitField_WithBug1(dropx, dropy, FALSE);
14899
14900   new_element = Tile[dropx][dropy];     // element might have changed
14901
14902   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14903       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14904   {
14905     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14906       MovDir[dropx][dropy] = drop_direction;
14907
14908     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14909
14910     // do not cause impact style collision by dropping elements that can fall
14911     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14912   }
14913
14914   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14915   player->is_dropping = TRUE;
14916
14917   player->drop_pressed_delay = 0;
14918   player->is_dropping_pressed = FALSE;
14919
14920   player->drop_x = dropx;
14921   player->drop_y = dropy;
14922
14923   return TRUE;
14924 }
14925
14926 // ----------------------------------------------------------------------------
14927 // game sound playing functions
14928 // ----------------------------------------------------------------------------
14929
14930 static int *loop_sound_frame = NULL;
14931 static int *loop_sound_volume = NULL;
14932
14933 void InitPlayLevelSound(void)
14934 {
14935   int num_sounds = getSoundListSize();
14936
14937   checked_free(loop_sound_frame);
14938   checked_free(loop_sound_volume);
14939
14940   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14941   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14942 }
14943
14944 static void PlayLevelSound(int x, int y, int nr)
14945 {
14946   int sx = SCREENX(x), sy = SCREENY(y);
14947   int volume, stereo_position;
14948   int max_distance = 8;
14949   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14950
14951   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14952       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14953     return;
14954
14955   if (!IN_LEV_FIELD(x, y) ||
14956       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14957       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14958     return;
14959
14960   volume = SOUND_MAX_VOLUME;
14961
14962   if (!IN_SCR_FIELD(sx, sy))
14963   {
14964     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14965     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14966
14967     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14968   }
14969
14970   stereo_position = (SOUND_MAX_LEFT +
14971                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14972                      (SCR_FIELDX + 2 * max_distance));
14973
14974   if (IS_LOOP_SOUND(nr))
14975   {
14976     /* This assures that quieter loop sounds do not overwrite louder ones,
14977        while restarting sound volume comparison with each new game frame. */
14978
14979     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14980       return;
14981
14982     loop_sound_volume[nr] = volume;
14983     loop_sound_frame[nr] = FrameCounter;
14984   }
14985
14986   PlaySoundExt(nr, volume, stereo_position, type);
14987 }
14988
14989 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14990 {
14991   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14992                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14993                  y < LEVELY(BY1) ? LEVELY(BY1) :
14994                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14995                  sound_action);
14996 }
14997
14998 static void PlayLevelSoundAction(int x, int y, int action)
14999 {
15000   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15001 }
15002
15003 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15004 {
15005   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15006
15007   if (sound_effect != SND_UNDEFINED)
15008     PlayLevelSound(x, y, sound_effect);
15009 }
15010
15011 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15012                                               int action)
15013 {
15014   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15015
15016   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15017     PlayLevelSound(x, y, sound_effect);
15018 }
15019
15020 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15021 {
15022   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15023
15024   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15025     PlayLevelSound(x, y, sound_effect);
15026 }
15027
15028 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15029 {
15030   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15031
15032   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15033     StopSound(sound_effect);
15034 }
15035
15036 static int getLevelMusicNr(void)
15037 {
15038   if (levelset.music[level_nr] != MUS_UNDEFINED)
15039     return levelset.music[level_nr];            // from config file
15040   else
15041     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15042 }
15043
15044 static void FadeLevelSounds(void)
15045 {
15046   FadeSounds();
15047 }
15048
15049 static void FadeLevelMusic(void)
15050 {
15051   int music_nr = getLevelMusicNr();
15052   char *curr_music = getCurrentlyPlayingMusicFilename();
15053   char *next_music = getMusicInfoEntryFilename(music_nr);
15054
15055   if (!strEqual(curr_music, next_music))
15056     FadeMusic();
15057 }
15058
15059 void FadeLevelSoundsAndMusic(void)
15060 {
15061   FadeLevelSounds();
15062   FadeLevelMusic();
15063 }
15064
15065 static void PlayLevelMusic(void)
15066 {
15067   int music_nr = getLevelMusicNr();
15068   char *curr_music = getCurrentlyPlayingMusicFilename();
15069   char *next_music = getMusicInfoEntryFilename(music_nr);
15070
15071   if (!strEqual(curr_music, next_music))
15072     PlayMusicLoop(music_nr);
15073 }
15074
15075 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15076 {
15077   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15078   int offset = 0;
15079   int x = xx - offset;
15080   int y = yy - offset;
15081
15082   switch (sample)
15083   {
15084     case SOUND_blank:
15085       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15086       break;
15087
15088     case SOUND_roll:
15089       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15090       break;
15091
15092     case SOUND_stone:
15093       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15094       break;
15095
15096     case SOUND_nut:
15097       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15098       break;
15099
15100     case SOUND_crack:
15101       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15102       break;
15103
15104     case SOUND_bug:
15105       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15106       break;
15107
15108     case SOUND_tank:
15109       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15110       break;
15111
15112     case SOUND_android_clone:
15113       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15114       break;
15115
15116     case SOUND_android_move:
15117       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15118       break;
15119
15120     case SOUND_spring:
15121       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15122       break;
15123
15124     case SOUND_slurp:
15125       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15126       break;
15127
15128     case SOUND_eater:
15129       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15130       break;
15131
15132     case SOUND_eater_eat:
15133       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15134       break;
15135
15136     case SOUND_alien:
15137       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15138       break;
15139
15140     case SOUND_collect:
15141       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15142       break;
15143
15144     case SOUND_diamond:
15145       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15146       break;
15147
15148     case SOUND_squash:
15149       // !!! CHECK THIS !!!
15150 #if 1
15151       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15152 #else
15153       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15154 #endif
15155       break;
15156
15157     case SOUND_wonderfall:
15158       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15159       break;
15160
15161     case SOUND_drip:
15162       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15163       break;
15164
15165     case SOUND_push:
15166       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15167       break;
15168
15169     case SOUND_dirt:
15170       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15171       break;
15172
15173     case SOUND_acid:
15174       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15175       break;
15176
15177     case SOUND_ball:
15178       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15179       break;
15180
15181     case SOUND_slide:
15182       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15183       break;
15184
15185     case SOUND_wonder:
15186       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15187       break;
15188
15189     case SOUND_door:
15190       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15191       break;
15192
15193     case SOUND_exit_open:
15194       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15195       break;
15196
15197     case SOUND_exit_leave:
15198       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15199       break;
15200
15201     case SOUND_dynamite:
15202       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15203       break;
15204
15205     case SOUND_tick:
15206       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15207       break;
15208
15209     case SOUND_press:
15210       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15211       break;
15212
15213     case SOUND_wheel:
15214       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15215       break;
15216
15217     case SOUND_boom:
15218       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15219       break;
15220
15221     case SOUND_die:
15222       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15223       break;
15224
15225     case SOUND_time:
15226       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15227       break;
15228
15229     default:
15230       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15231       break;
15232   }
15233 }
15234
15235 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15236 {
15237   int element = map_element_SP_to_RND(element_sp);
15238   int action = map_action_SP_to_RND(action_sp);
15239   int offset = (setup.sp_show_border_elements ? 0 : 1);
15240   int x = xx - offset;
15241   int y = yy - offset;
15242
15243   PlayLevelSoundElementAction(x, y, element, action);
15244 }
15245
15246 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15247 {
15248   int element = map_element_MM_to_RND(element_mm);
15249   int action = map_action_MM_to_RND(action_mm);
15250   int offset = 0;
15251   int x = xx - offset;
15252   int y = yy - offset;
15253
15254   if (!IS_MM_ELEMENT(element))
15255     element = EL_MM_DEFAULT;
15256
15257   PlayLevelSoundElementAction(x, y, element, action);
15258 }
15259
15260 void PlaySound_MM(int sound_mm)
15261 {
15262   int sound = map_sound_MM_to_RND(sound_mm);
15263
15264   if (sound == SND_UNDEFINED)
15265     return;
15266
15267   PlaySound(sound);
15268 }
15269
15270 void PlaySoundLoop_MM(int sound_mm)
15271 {
15272   int sound = map_sound_MM_to_RND(sound_mm);
15273
15274   if (sound == SND_UNDEFINED)
15275     return;
15276
15277   PlaySoundLoop(sound);
15278 }
15279
15280 void StopSound_MM(int sound_mm)
15281 {
15282   int sound = map_sound_MM_to_RND(sound_mm);
15283
15284   if (sound == SND_UNDEFINED)
15285     return;
15286
15287   StopSound(sound);
15288 }
15289
15290 void RaiseScore(int value)
15291 {
15292   game.score += value;
15293
15294   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15295
15296   DisplayGameControlValues();
15297 }
15298
15299 void RaiseScoreElement(int element)
15300 {
15301   switch (element)
15302   {
15303     case EL_EMERALD:
15304     case EL_BD_DIAMOND:
15305     case EL_EMERALD_YELLOW:
15306     case EL_EMERALD_RED:
15307     case EL_EMERALD_PURPLE:
15308     case EL_SP_INFOTRON:
15309       RaiseScore(level.score[SC_EMERALD]);
15310       break;
15311     case EL_DIAMOND:
15312       RaiseScore(level.score[SC_DIAMOND]);
15313       break;
15314     case EL_CRYSTAL:
15315       RaiseScore(level.score[SC_CRYSTAL]);
15316       break;
15317     case EL_PEARL:
15318       RaiseScore(level.score[SC_PEARL]);
15319       break;
15320     case EL_BUG:
15321     case EL_BD_BUTTERFLY:
15322     case EL_SP_ELECTRON:
15323       RaiseScore(level.score[SC_BUG]);
15324       break;
15325     case EL_SPACESHIP:
15326     case EL_BD_FIREFLY:
15327     case EL_SP_SNIKSNAK:
15328       RaiseScore(level.score[SC_SPACESHIP]);
15329       break;
15330     case EL_YAMYAM:
15331     case EL_DARK_YAMYAM:
15332       RaiseScore(level.score[SC_YAMYAM]);
15333       break;
15334     case EL_ROBOT:
15335       RaiseScore(level.score[SC_ROBOT]);
15336       break;
15337     case EL_PACMAN:
15338       RaiseScore(level.score[SC_PACMAN]);
15339       break;
15340     case EL_NUT:
15341       RaiseScore(level.score[SC_NUT]);
15342       break;
15343     case EL_DYNAMITE:
15344     case EL_EM_DYNAMITE:
15345     case EL_SP_DISK_RED:
15346     case EL_DYNABOMB_INCREASE_NUMBER:
15347     case EL_DYNABOMB_INCREASE_SIZE:
15348     case EL_DYNABOMB_INCREASE_POWER:
15349       RaiseScore(level.score[SC_DYNAMITE]);
15350       break;
15351     case EL_SHIELD_NORMAL:
15352     case EL_SHIELD_DEADLY:
15353       RaiseScore(level.score[SC_SHIELD]);
15354       break;
15355     case EL_EXTRA_TIME:
15356       RaiseScore(level.extra_time_score);
15357       break;
15358     case EL_KEY_1:
15359     case EL_KEY_2:
15360     case EL_KEY_3:
15361     case EL_KEY_4:
15362     case EL_EM_KEY_1:
15363     case EL_EM_KEY_2:
15364     case EL_EM_KEY_3:
15365     case EL_EM_KEY_4:
15366     case EL_EMC_KEY_5:
15367     case EL_EMC_KEY_6:
15368     case EL_EMC_KEY_7:
15369     case EL_EMC_KEY_8:
15370     case EL_DC_KEY_WHITE:
15371       RaiseScore(level.score[SC_KEY]);
15372       break;
15373     default:
15374       RaiseScore(element_info[element].collect_score);
15375       break;
15376   }
15377 }
15378
15379 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15380 {
15381   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15382   {
15383     if (!quick_quit)
15384     {
15385       // prevent short reactivation of overlay buttons while closing door
15386       SetOverlayActive(FALSE);
15387
15388       // door may still be open due to skipped or envelope style request
15389       CloseDoor(DOOR_CLOSE_1);
15390     }
15391
15392     if (network.enabled)
15393       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15394     else
15395     {
15396       if (quick_quit)
15397         FadeSkipNextFadeIn();
15398
15399       SetGameStatus(GAME_MODE_MAIN);
15400
15401       DrawMainMenu();
15402     }
15403   }
15404   else          // continue playing the game
15405   {
15406     if (tape.playing && tape.deactivate_display)
15407       TapeDeactivateDisplayOff(TRUE);
15408
15409     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15410
15411     if (tape.playing && tape.deactivate_display)
15412       TapeDeactivateDisplayOn();
15413   }
15414 }
15415
15416 void RequestQuitGame(boolean escape_key_pressed)
15417 {
15418   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15419   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15420                         level_editor_test_game);
15421   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15422                           quick_quit);
15423
15424   RequestQuitGameExt(skip_request, quick_quit,
15425                      "Do you really want to quit the game?");
15426 }
15427
15428 void RequestRestartGame(char *message)
15429 {
15430   game.restart_game_message = NULL;
15431
15432   boolean has_started_game = hasStartedNetworkGame();
15433   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15434
15435   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15436   {
15437     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15438   }
15439   else
15440   {
15441     // needed in case of envelope request to close game panel
15442     CloseDoor(DOOR_CLOSE_1);
15443
15444     SetGameStatus(GAME_MODE_MAIN);
15445
15446     DrawMainMenu();
15447   }
15448 }
15449
15450 void CheckGameOver(void)
15451 {
15452   static boolean last_game_over = FALSE;
15453   static int game_over_delay = 0;
15454   int game_over_delay_value = 50;
15455   boolean game_over = checkGameFailed();
15456
15457   // do not handle game over if request dialog is already active
15458   if (game.request_active)
15459     return;
15460
15461   // do not ask to play again if game was never actually played
15462   if (!game.GamePlayed)
15463     return;
15464
15465   if (!game_over)
15466   {
15467     last_game_over = FALSE;
15468     game_over_delay = game_over_delay_value;
15469
15470     return;
15471   }
15472
15473   if (game_over_delay > 0)
15474   {
15475     game_over_delay--;
15476
15477     return;
15478   }
15479
15480   if (last_game_over != game_over)
15481     game.restart_game_message = (hasStartedNetworkGame() ?
15482                                  "Game over! Play it again?" :
15483                                  "Game over!");
15484
15485   last_game_over = game_over;
15486 }
15487
15488 boolean checkGameSolved(void)
15489 {
15490   // set for all game engines if level was solved
15491   return game.LevelSolved_GameEnd;
15492 }
15493
15494 boolean checkGameFailed(void)
15495 {
15496   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15497     return (game_em.game_over && !game_em.level_solved);
15498   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15499     return (game_sp.game_over && !game_sp.level_solved);
15500   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15501     return (game_mm.game_over && !game_mm.level_solved);
15502   else                          // GAME_ENGINE_TYPE_RND
15503     return (game.GameOver && !game.LevelSolved);
15504 }
15505
15506 boolean checkGameEnded(void)
15507 {
15508   return (checkGameSolved() || checkGameFailed());
15509 }
15510
15511
15512 // ----------------------------------------------------------------------------
15513 // random generator functions
15514 // ----------------------------------------------------------------------------
15515
15516 unsigned int InitEngineRandom_RND(int seed)
15517 {
15518   game.num_random_calls = 0;
15519
15520   return InitEngineRandom(seed);
15521 }
15522
15523 unsigned int RND(int max)
15524 {
15525   if (max > 0)
15526   {
15527     game.num_random_calls++;
15528
15529     return GetEngineRandom(max);
15530   }
15531
15532   return 0;
15533 }
15534
15535
15536 // ----------------------------------------------------------------------------
15537 // game engine snapshot handling functions
15538 // ----------------------------------------------------------------------------
15539
15540 struct EngineSnapshotInfo
15541 {
15542   // runtime values for custom element collect score
15543   int collect_score[NUM_CUSTOM_ELEMENTS];
15544
15545   // runtime values for group element choice position
15546   int choice_pos[NUM_GROUP_ELEMENTS];
15547
15548   // runtime values for belt position animations
15549   int belt_graphic[4][NUM_BELT_PARTS];
15550   int belt_anim_mode[4][NUM_BELT_PARTS];
15551 };
15552
15553 static struct EngineSnapshotInfo engine_snapshot_rnd;
15554 static char *snapshot_level_identifier = NULL;
15555 static int snapshot_level_nr = -1;
15556
15557 static void SaveEngineSnapshotValues_RND(void)
15558 {
15559   static int belt_base_active_element[4] =
15560   {
15561     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15562     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15563     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15564     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15565   };
15566   int i, j;
15567
15568   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15569   {
15570     int element = EL_CUSTOM_START + i;
15571
15572     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15573   }
15574
15575   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15576   {
15577     int element = EL_GROUP_START + i;
15578
15579     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15580   }
15581
15582   for (i = 0; i < 4; i++)
15583   {
15584     for (j = 0; j < NUM_BELT_PARTS; j++)
15585     {
15586       int element = belt_base_active_element[i] + j;
15587       int graphic = el2img(element);
15588       int anim_mode = graphic_info[graphic].anim_mode;
15589
15590       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15591       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15592     }
15593   }
15594 }
15595
15596 static void LoadEngineSnapshotValues_RND(void)
15597 {
15598   unsigned int num_random_calls = game.num_random_calls;
15599   int i, j;
15600
15601   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15602   {
15603     int element = EL_CUSTOM_START + i;
15604
15605     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15606   }
15607
15608   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15609   {
15610     int element = EL_GROUP_START + i;
15611
15612     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15613   }
15614
15615   for (i = 0; i < 4; i++)
15616   {
15617     for (j = 0; j < NUM_BELT_PARTS; j++)
15618     {
15619       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15620       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15621
15622       graphic_info[graphic].anim_mode = anim_mode;
15623     }
15624   }
15625
15626   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15627   {
15628     InitRND(tape.random_seed);
15629     for (i = 0; i < num_random_calls; i++)
15630       RND(1);
15631   }
15632
15633   if (game.num_random_calls != num_random_calls)
15634   {
15635     Error("number of random calls out of sync");
15636     Error("number of random calls should be %d", num_random_calls);
15637     Error("number of random calls is %d", game.num_random_calls);
15638
15639     Fail("this should not happen -- please debug");
15640   }
15641 }
15642
15643 void FreeEngineSnapshotSingle(void)
15644 {
15645   FreeSnapshotSingle();
15646
15647   setString(&snapshot_level_identifier, NULL);
15648   snapshot_level_nr = -1;
15649 }
15650
15651 void FreeEngineSnapshotList(void)
15652 {
15653   FreeSnapshotList();
15654 }
15655
15656 static ListNode *SaveEngineSnapshotBuffers(void)
15657 {
15658   ListNode *buffers = NULL;
15659
15660   // copy some special values to a structure better suited for the snapshot
15661
15662   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15663     SaveEngineSnapshotValues_RND();
15664   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15665     SaveEngineSnapshotValues_EM();
15666   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15667     SaveEngineSnapshotValues_SP(&buffers);
15668   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15669     SaveEngineSnapshotValues_MM(&buffers);
15670
15671   // save values stored in special snapshot structure
15672
15673   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15674     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15675   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15676     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15677   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15678     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15679   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15680     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15681
15682   // save further RND engine values
15683
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15687
15688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15693
15694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15697
15698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15699
15700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15702
15703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15721
15722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15724
15725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15728
15729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15731
15732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15737
15738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15740
15741 #if 0
15742   ListNode *node = engine_snapshot_list_rnd;
15743   int num_bytes = 0;
15744
15745   while (node != NULL)
15746   {
15747     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15748
15749     node = node->next;
15750   }
15751
15752   Debug("game:playing:SaveEngineSnapshotBuffers",
15753         "size of engine snapshot: %d bytes", num_bytes);
15754 #endif
15755
15756   return buffers;
15757 }
15758
15759 void SaveEngineSnapshotSingle(void)
15760 {
15761   ListNode *buffers = SaveEngineSnapshotBuffers();
15762
15763   // finally save all snapshot buffers to single snapshot
15764   SaveSnapshotSingle(buffers);
15765
15766   // save level identification information
15767   setString(&snapshot_level_identifier, leveldir_current->identifier);
15768   snapshot_level_nr = level_nr;
15769 }
15770
15771 boolean CheckSaveEngineSnapshotToList(void)
15772 {
15773   boolean save_snapshot =
15774     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15775      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15776       game.snapshot.changed_action) ||
15777      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15778       game.snapshot.collected_item));
15779
15780   game.snapshot.changed_action = FALSE;
15781   game.snapshot.collected_item = FALSE;
15782   game.snapshot.save_snapshot = save_snapshot;
15783
15784   return save_snapshot;
15785 }
15786
15787 void SaveEngineSnapshotToList(void)
15788 {
15789   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15790       tape.quick_resume)
15791     return;
15792
15793   ListNode *buffers = SaveEngineSnapshotBuffers();
15794
15795   // finally save all snapshot buffers to snapshot list
15796   SaveSnapshotToList(buffers);
15797 }
15798
15799 void SaveEngineSnapshotToListInitial(void)
15800 {
15801   FreeEngineSnapshotList();
15802
15803   SaveEngineSnapshotToList();
15804 }
15805
15806 static void LoadEngineSnapshotValues(void)
15807 {
15808   // restore special values from snapshot structure
15809
15810   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15811     LoadEngineSnapshotValues_RND();
15812   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15813     LoadEngineSnapshotValues_EM();
15814   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15815     LoadEngineSnapshotValues_SP();
15816   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15817     LoadEngineSnapshotValues_MM();
15818 }
15819
15820 void LoadEngineSnapshotSingle(void)
15821 {
15822   LoadSnapshotSingle();
15823
15824   LoadEngineSnapshotValues();
15825 }
15826
15827 static void LoadEngineSnapshot_Undo(int steps)
15828 {
15829   LoadSnapshotFromList_Older(steps);
15830
15831   LoadEngineSnapshotValues();
15832 }
15833
15834 static void LoadEngineSnapshot_Redo(int steps)
15835 {
15836   LoadSnapshotFromList_Newer(steps);
15837
15838   LoadEngineSnapshotValues();
15839 }
15840
15841 boolean CheckEngineSnapshotSingle(void)
15842 {
15843   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15844           snapshot_level_nr == level_nr);
15845 }
15846
15847 boolean CheckEngineSnapshotList(void)
15848 {
15849   return CheckSnapshotList();
15850 }
15851
15852
15853 // ---------- new game button stuff -------------------------------------------
15854
15855 static struct
15856 {
15857   int graphic;
15858   struct XY *pos;
15859   int gadget_id;
15860   boolean *setup_value;
15861   boolean allowed_on_tape;
15862   boolean is_touch_button;
15863   char *infotext;
15864 } gamebutton_info[NUM_GAME_BUTTONS] =
15865 {
15866   {
15867     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15868     GAME_CTRL_ID_STOP,                          NULL,
15869     TRUE, FALSE,                                "stop game"
15870   },
15871   {
15872     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15873     GAME_CTRL_ID_PAUSE,                         NULL,
15874     TRUE, FALSE,                                "pause game"
15875   },
15876   {
15877     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15878     GAME_CTRL_ID_PLAY,                          NULL,
15879     TRUE, FALSE,                                "play game"
15880   },
15881   {
15882     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15883     GAME_CTRL_ID_UNDO,                          NULL,
15884     TRUE, FALSE,                                "undo step"
15885   },
15886   {
15887     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15888     GAME_CTRL_ID_REDO,                          NULL,
15889     TRUE, FALSE,                                "redo step"
15890   },
15891   {
15892     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15893     GAME_CTRL_ID_SAVE,                          NULL,
15894     TRUE, FALSE,                                "save game"
15895   },
15896   {
15897     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15898     GAME_CTRL_ID_PAUSE2,                        NULL,
15899     TRUE, FALSE,                                "pause game"
15900   },
15901   {
15902     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15903     GAME_CTRL_ID_LOAD,                          NULL,
15904     TRUE, FALSE,                                "load game"
15905   },
15906   {
15907     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15908     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15909     FALSE, FALSE,                               "stop game"
15910   },
15911   {
15912     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15913     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15914     FALSE, FALSE,                               "pause game"
15915   },
15916   {
15917     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15918     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15919     FALSE, FALSE,                               "play game"
15920   },
15921   {
15922     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15923     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15924     FALSE, TRUE,                                "stop game"
15925   },
15926   {
15927     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15928     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15929     FALSE, TRUE,                                "pause game"
15930   },
15931   {
15932     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15933     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15934     TRUE, FALSE,                                "background music on/off"
15935   },
15936   {
15937     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15938     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15939     TRUE, FALSE,                                "sound loops on/off"
15940   },
15941   {
15942     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15943     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15944     TRUE, FALSE,                                "normal sounds on/off"
15945   },
15946   {
15947     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15948     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15949     FALSE, FALSE,                               "background music on/off"
15950   },
15951   {
15952     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15953     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15954     FALSE, FALSE,                               "sound loops on/off"
15955   },
15956   {
15957     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15958     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15959     FALSE, FALSE,                               "normal sounds on/off"
15960   }
15961 };
15962
15963 void CreateGameButtons(void)
15964 {
15965   int i;
15966
15967   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15968   {
15969     int graphic = gamebutton_info[i].graphic;
15970     struct GraphicInfo *gfx = &graphic_info[graphic];
15971     struct XY *pos = gamebutton_info[i].pos;
15972     struct GadgetInfo *gi;
15973     int button_type;
15974     boolean checked;
15975     unsigned int event_mask;
15976     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15977     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15978     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15979     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15980     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15981     int gd_x   = gfx->src_x;
15982     int gd_y   = gfx->src_y;
15983     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15984     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15985     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15986     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15987     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15988     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15989     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15990     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15991     int id = i;
15992
15993     if (gfx->bitmap == NULL)
15994     {
15995       game_gadget[id] = NULL;
15996
15997       continue;
15998     }
15999
16000     if (id == GAME_CTRL_ID_STOP ||
16001         id == GAME_CTRL_ID_PANEL_STOP ||
16002         id == GAME_CTRL_ID_TOUCH_STOP ||
16003         id == GAME_CTRL_ID_PLAY ||
16004         id == GAME_CTRL_ID_PANEL_PLAY ||
16005         id == GAME_CTRL_ID_SAVE ||
16006         id == GAME_CTRL_ID_LOAD)
16007     {
16008       button_type = GD_TYPE_NORMAL_BUTTON;
16009       checked = FALSE;
16010       event_mask = GD_EVENT_RELEASED;
16011     }
16012     else if (id == GAME_CTRL_ID_UNDO ||
16013              id == GAME_CTRL_ID_REDO)
16014     {
16015       button_type = GD_TYPE_NORMAL_BUTTON;
16016       checked = FALSE;
16017       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16018     }
16019     else
16020     {
16021       button_type = GD_TYPE_CHECK_BUTTON;
16022       checked = (gamebutton_info[i].setup_value != NULL ?
16023                  *gamebutton_info[i].setup_value : FALSE);
16024       event_mask = GD_EVENT_PRESSED;
16025     }
16026
16027     gi = CreateGadget(GDI_CUSTOM_ID, id,
16028                       GDI_IMAGE_ID, graphic,
16029                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16030                       GDI_X, base_x + x,
16031                       GDI_Y, base_y + y,
16032                       GDI_WIDTH, gfx->width,
16033                       GDI_HEIGHT, gfx->height,
16034                       GDI_TYPE, button_type,
16035                       GDI_STATE, GD_BUTTON_UNPRESSED,
16036                       GDI_CHECKED, checked,
16037                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16038                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16039                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16040                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16041                       GDI_DIRECT_DRAW, FALSE,
16042                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16043                       GDI_EVENT_MASK, event_mask,
16044                       GDI_CALLBACK_ACTION, HandleGameButtons,
16045                       GDI_END);
16046
16047     if (gi == NULL)
16048       Fail("cannot create gadget");
16049
16050     game_gadget[id] = gi;
16051   }
16052 }
16053
16054 void FreeGameButtons(void)
16055 {
16056   int i;
16057
16058   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16059     FreeGadget(game_gadget[i]);
16060 }
16061
16062 static void UnmapGameButtonsAtSamePosition(int id)
16063 {
16064   int i;
16065
16066   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16067     if (i != id &&
16068         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16069         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16070       UnmapGadget(game_gadget[i]);
16071 }
16072
16073 static void UnmapGameButtonsAtSamePosition_All(void)
16074 {
16075   if (setup.show_snapshot_buttons)
16076   {
16077     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16078     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16079     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16080   }
16081   else
16082   {
16083     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16084     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16085     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16086
16087     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16088     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16089     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16090   }
16091 }
16092
16093 static void MapGameButtonsAtSamePosition(int id)
16094 {
16095   int i;
16096
16097   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16098     if (i != id &&
16099         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16100         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16101       MapGadget(game_gadget[i]);
16102
16103   UnmapGameButtonsAtSamePosition_All();
16104 }
16105
16106 void MapUndoRedoButtons(void)
16107 {
16108   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16109   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16110
16111   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16112   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16113 }
16114
16115 void UnmapUndoRedoButtons(void)
16116 {
16117   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16118   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16119
16120   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16121   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16122 }
16123
16124 void ModifyPauseButtons(void)
16125 {
16126   static int ids[] =
16127   {
16128     GAME_CTRL_ID_PAUSE,
16129     GAME_CTRL_ID_PAUSE2,
16130     GAME_CTRL_ID_PANEL_PAUSE,
16131     GAME_CTRL_ID_TOUCH_PAUSE,
16132     -1
16133   };
16134   int i;
16135
16136   for (i = 0; ids[i] > -1; i++)
16137     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16138 }
16139
16140 static void MapGameButtonsExt(boolean on_tape)
16141 {
16142   int i;
16143
16144   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16145     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16146         i != GAME_CTRL_ID_UNDO &&
16147         i != GAME_CTRL_ID_REDO)
16148       MapGadget(game_gadget[i]);
16149
16150   UnmapGameButtonsAtSamePosition_All();
16151
16152   RedrawGameButtons();
16153 }
16154
16155 static void UnmapGameButtonsExt(boolean on_tape)
16156 {
16157   int i;
16158
16159   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16160     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16161       UnmapGadget(game_gadget[i]);
16162 }
16163
16164 static void RedrawGameButtonsExt(boolean on_tape)
16165 {
16166   int i;
16167
16168   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16169     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16170       RedrawGadget(game_gadget[i]);
16171 }
16172
16173 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16174 {
16175   if (gi == NULL)
16176     return;
16177
16178   gi->checked = state;
16179 }
16180
16181 static void RedrawSoundButtonGadget(int id)
16182 {
16183   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16184              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16185              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16186              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16187              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16188              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16189              id);
16190
16191   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16192   RedrawGadget(game_gadget[id2]);
16193 }
16194
16195 void MapGameButtons(void)
16196 {
16197   MapGameButtonsExt(FALSE);
16198 }
16199
16200 void UnmapGameButtons(void)
16201 {
16202   UnmapGameButtonsExt(FALSE);
16203 }
16204
16205 void RedrawGameButtons(void)
16206 {
16207   RedrawGameButtonsExt(FALSE);
16208 }
16209
16210 void MapGameButtonsOnTape(void)
16211 {
16212   MapGameButtonsExt(TRUE);
16213 }
16214
16215 void UnmapGameButtonsOnTape(void)
16216 {
16217   UnmapGameButtonsExt(TRUE);
16218 }
16219
16220 void RedrawGameButtonsOnTape(void)
16221 {
16222   RedrawGameButtonsExt(TRUE);
16223 }
16224
16225 static void GameUndoRedoExt(void)
16226 {
16227   ClearPlayerAction();
16228
16229   tape.pausing = TRUE;
16230
16231   RedrawPlayfield();
16232   UpdateAndDisplayGameControlValues();
16233
16234   DrawCompleteVideoDisplay();
16235   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16236   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16237   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16238
16239   BackToFront();
16240 }
16241
16242 static void GameUndo(int steps)
16243 {
16244   if (!CheckEngineSnapshotList())
16245     return;
16246
16247   LoadEngineSnapshot_Undo(steps);
16248
16249   GameUndoRedoExt();
16250 }
16251
16252 static void GameRedo(int steps)
16253 {
16254   if (!CheckEngineSnapshotList())
16255     return;
16256
16257   LoadEngineSnapshot_Redo(steps);
16258
16259   GameUndoRedoExt();
16260 }
16261
16262 static void HandleGameButtonsExt(int id, int button)
16263 {
16264   static boolean game_undo_executed = FALSE;
16265   int steps = BUTTON_STEPSIZE(button);
16266   boolean handle_game_buttons =
16267     (game_status == GAME_MODE_PLAYING ||
16268      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16269
16270   if (!handle_game_buttons)
16271     return;
16272
16273   switch (id)
16274   {
16275     case GAME_CTRL_ID_STOP:
16276     case GAME_CTRL_ID_PANEL_STOP:
16277     case GAME_CTRL_ID_TOUCH_STOP:
16278       if (game_status == GAME_MODE_MAIN)
16279         break;
16280
16281       if (tape.playing)
16282         TapeStop();
16283       else
16284         RequestQuitGame(FALSE);
16285
16286       break;
16287
16288     case GAME_CTRL_ID_PAUSE:
16289     case GAME_CTRL_ID_PAUSE2:
16290     case GAME_CTRL_ID_PANEL_PAUSE:
16291     case GAME_CTRL_ID_TOUCH_PAUSE:
16292       if (network.enabled && game_status == GAME_MODE_PLAYING)
16293       {
16294         if (tape.pausing)
16295           SendToServer_ContinuePlaying();
16296         else
16297           SendToServer_PausePlaying();
16298       }
16299       else
16300         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16301
16302       game_undo_executed = FALSE;
16303
16304       break;
16305
16306     case GAME_CTRL_ID_PLAY:
16307     case GAME_CTRL_ID_PANEL_PLAY:
16308       if (game_status == GAME_MODE_MAIN)
16309       {
16310         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16311       }
16312       else if (tape.pausing)
16313       {
16314         if (network.enabled)
16315           SendToServer_ContinuePlaying();
16316         else
16317           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16318       }
16319       break;
16320
16321     case GAME_CTRL_ID_UNDO:
16322       // Important: When using "save snapshot when collecting an item" mode,
16323       // load last (current) snapshot for first "undo" after pressing "pause"
16324       // (else the last-but-one snapshot would be loaded, because the snapshot
16325       // pointer already points to the last snapshot when pressing "pause",
16326       // which is fine for "every step/move" mode, but not for "every collect")
16327       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16328           !game_undo_executed)
16329         steps--;
16330
16331       game_undo_executed = TRUE;
16332
16333       GameUndo(steps);
16334       break;
16335
16336     case GAME_CTRL_ID_REDO:
16337       GameRedo(steps);
16338       break;
16339
16340     case GAME_CTRL_ID_SAVE:
16341       TapeQuickSave();
16342       break;
16343
16344     case GAME_CTRL_ID_LOAD:
16345       TapeQuickLoad();
16346       break;
16347
16348     case SOUND_CTRL_ID_MUSIC:
16349     case SOUND_CTRL_ID_PANEL_MUSIC:
16350       if (setup.sound_music)
16351       { 
16352         setup.sound_music = FALSE;
16353
16354         FadeMusic();
16355       }
16356       else if (audio.music_available)
16357       { 
16358         setup.sound = setup.sound_music = TRUE;
16359
16360         SetAudioMode(setup.sound);
16361
16362         if (game_status == GAME_MODE_PLAYING)
16363           PlayLevelMusic();
16364       }
16365
16366       RedrawSoundButtonGadget(id);
16367
16368       break;
16369
16370     case SOUND_CTRL_ID_LOOPS:
16371     case SOUND_CTRL_ID_PANEL_LOOPS:
16372       if (setup.sound_loops)
16373         setup.sound_loops = FALSE;
16374       else if (audio.loops_available)
16375       {
16376         setup.sound = setup.sound_loops = TRUE;
16377
16378         SetAudioMode(setup.sound);
16379       }
16380
16381       RedrawSoundButtonGadget(id);
16382
16383       break;
16384
16385     case SOUND_CTRL_ID_SIMPLE:
16386     case SOUND_CTRL_ID_PANEL_SIMPLE:
16387       if (setup.sound_simple)
16388         setup.sound_simple = FALSE;
16389       else if (audio.sound_available)
16390       {
16391         setup.sound = setup.sound_simple = TRUE;
16392
16393         SetAudioMode(setup.sound);
16394       }
16395
16396       RedrawSoundButtonGadget(id);
16397
16398       break;
16399
16400     default:
16401       break;
16402   }
16403 }
16404
16405 static void HandleGameButtons(struct GadgetInfo *gi)
16406 {
16407   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16408 }
16409
16410 void HandleSoundButtonKeys(Key key)
16411 {
16412   if (key == setup.shortcut.sound_simple)
16413     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16414   else if (key == setup.shortcut.sound_loops)
16415     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16416   else if (key == setup.shortcut.sound_music)
16417     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16418 }