14dc59eb8b3d4afb3d0612acee25b480cbf53f8b
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Tile[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Tile[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Tile[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Tile[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       Debug("game:init:player", "- player element %d activated",
1773             player->element_nr);
1774       Debug("game:init:player", "  (local player is %d and currently %s)",
1775             local_player->element_nr,
1776             local_player->active ? "active" : "not active");
1777     }
1778 #endif
1779
1780     Tile[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   // always check if player was just killed and should be reanimated
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Tile[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Tile[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1828         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878     case EL_SPRING_LEFT:
1879     case EL_SPRING_RIGHT:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Tile[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Tile[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Tile[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Tile[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Tile[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error("'game_panel_controls' structure corrupted at %d", i);
2151
2152       Fail("this should not happen -- please debug");
2153     }
2154
2155     // force update of game controls after initialization
2156     gpc->value = gpc->last_value = -1;
2157     gpc->frame = gpc->last_frame = -1;
2158     gpc->gfx_frame = -1;
2159
2160     // determine panel value width for later calculation of alignment
2161     if (type == TYPE_INTEGER || type == TYPE_STRING)
2162     {
2163       pos->width = pos->size * getFontWidth(pos->font);
2164       pos->height = getFontHeight(pos->font);
2165     }
2166     else if (type == TYPE_ELEMENT)
2167     {
2168       pos->width = pos->size;
2169       pos->height = pos->size;
2170     }
2171
2172     // fill structure for game panel draw order
2173     gpo->nr = gpc->nr;
2174     gpo->sort_priority = pos->sort_priority;
2175   }
2176
2177   // sort game panel controls according to sort_priority and control number
2178   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2179         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2180 }
2181
2182 static void UpdatePlayfieldElementCount(void)
2183 {
2184   boolean use_element_count = FALSE;
2185   int i, j, x, y;
2186
2187   // first check if it is needed at all to calculate playfield element count
2188   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2189     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2190       use_element_count = TRUE;
2191
2192   if (!use_element_count)
2193     return;
2194
2195   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2196     element_info[i].element_count = 0;
2197
2198   SCAN_PLAYFIELD(x, y)
2199   {
2200     element_info[Tile[x][y]].element_count++;
2201   }
2202
2203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2204     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2205       if (IS_IN_GROUP(j, i))
2206         element_info[EL_GROUP_START + i].element_count +=
2207           element_info[j].element_count;
2208 }
2209
2210 static void UpdateGameControlValues(void)
2211 {
2212   int i, k;
2213   int time = (game.LevelSolved ?
2214               game.LevelSolved_CountingTime :
2215               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216               game_em.lev->time :
2217               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2218               game_sp.time_played :
2219               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220               game_mm.energy_left :
2221               game.no_time_limit ? TimePlayed : TimeLeft);
2222   int score = (game.LevelSolved ?
2223                game.LevelSolved_CountingScore :
2224                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2225                game_em.lev->score :
2226                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2227                game_sp.score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2229                game_mm.score :
2230                game.score);
2231   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232               game_em.lev->gems_needed :
2233               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2234               game_sp.infotrons_still_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2236               game_mm.kettles_still_needed :
2237               game.gems_still_needed);
2238   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2239                      game_em.lev->gems_needed > 0 :
2240                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2241                      game_sp.infotrons_still_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2243                      game_mm.kettles_still_needed > 0 ||
2244                      game_mm.lights_still_needed > 0 :
2245                      game.gems_still_needed > 0 ||
2246                      game.sokoban_fields_still_needed > 0 ||
2247                      game.sokoban_objects_still_needed > 0 ||
2248                      game.lights_still_needed > 0);
2249   int health = (game.LevelSolved ?
2250                 game.LevelSolved_CountingHealth :
2251                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252                 MM_HEALTH(game_mm.laser_overload_value) :
2253                 game.health);
2254
2255   UpdatePlayfieldElementCount();
2256
2257   // update game panel control values
2258
2259   // used instead of "level_nr" (for network games)
2260   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2261   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2262
2263   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2264   for (i = 0; i < MAX_NUM_KEYS; i++)
2265     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2268
2269   if (game.centered_player_nr == -1)
2270   {
2271     for (i = 0; i < MAX_PLAYERS; i++)
2272     {
2273       // only one player in Supaplex game engine
2274       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2275         break;
2276
2277       for (k = 0; k < MAX_NUM_KEYS; k++)
2278       {
2279         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2280         {
2281           if (game_em.ply[i]->keys & (1 << k))
2282             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283               get_key_element_from_nr(k);
2284         }
2285         else if (stored_player[i].key[k])
2286           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2287             get_key_element_from_nr(k);
2288       }
2289
2290       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291         getPlayerInventorySize(i);
2292
2293       if (stored_player[i].num_white_keys > 0)
2294         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2295           EL_DC_KEY_WHITE;
2296
2297       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2298         stored_player[i].num_white_keys;
2299     }
2300   }
2301   else
2302   {
2303     int player_nr = game.centered_player_nr;
2304
2305     for (k = 0; k < MAX_NUM_KEYS; k++)
2306     {
2307       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308       {
2309         if (game_em.ply[player_nr]->keys & (1 << k))
2310           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2311             get_key_element_from_nr(k);
2312       }
2313       else if (stored_player[player_nr].key[k])
2314         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315           get_key_element_from_nr(k);
2316     }
2317
2318     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2319       getPlayerInventorySize(player_nr);
2320
2321     if (stored_player[player_nr].num_white_keys > 0)
2322       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2323
2324     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2325       stored_player[player_nr].num_white_keys;
2326   }
2327
2328   // try to display as many collected keys as possible in the default game panel
2329   for (i = STD_NUM_KEYS; i < MAX_NUM_KEYS + 1; i++)     // EMC keys + white key
2330   {
2331     int nr = GAME_PANEL_KEY_1 + i;
2332     int emc_key = get_key_element_from_nr(i);
2333     int element = (i < MAX_NUM_KEYS ? emc_key : EL_DC_KEY_WHITE);
2334     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2335     struct TextPosInfo *pos = gpc->pos;
2336
2337     // check if panel position is undefined for a certain EMC key or white key
2338     if (gpc->value != EL_EMPTY && pos->x == -1 && pos->y == -1)
2339     {
2340       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2341
2342       // 1st try: display key at the same position as normal or EM keys
2343       if (game_panel_controls[nr_new].value == EL_EMPTY)
2344       {
2345         game_panel_controls[nr_new].value = element;
2346       }
2347       else
2348       {
2349         // 2nd try: display key at the next free position in the key panel
2350         for (k = 0; k < STD_NUM_KEYS; k++)
2351         {
2352           nr_new = GAME_PANEL_KEY_1 + k;
2353
2354           if (game_panel_controls[nr_new].value == EL_EMPTY)
2355           {
2356             game_panel_controls[nr_new].value = element;
2357
2358             break;
2359           }
2360         }
2361       }
2362     }
2363   }
2364
2365   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2366   {
2367     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2368       get_inventory_element_from_pos(local_player, i);
2369     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2370       get_inventory_element_from_pos(local_player, -i - 1);
2371   }
2372
2373   game_panel_controls[GAME_PANEL_SCORE].value = score;
2374   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2375
2376   game_panel_controls[GAME_PANEL_TIME].value = time;
2377
2378   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2379   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2380   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2381
2382   if (level.time == 0)
2383     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2384   else
2385     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2386
2387   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2388   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2389
2390   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2391
2392   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2393     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2394      EL_EMPTY);
2395   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2396     local_player->shield_normal_time_left;
2397   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2398     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2399      EL_EMPTY);
2400   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2401     local_player->shield_deadly_time_left;
2402
2403   game_panel_controls[GAME_PANEL_EXIT].value =
2404     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2405
2406   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2407     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2408   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2409     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2410      EL_EMC_MAGIC_BALL_SWITCH);
2411
2412   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2413     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2414   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2415     game.light_time_left;
2416
2417   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2418     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2419   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2420     game.timegate_time_left;
2421
2422   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2423     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2424
2425   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2426     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2427   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2428     game.lenses_time_left;
2429
2430   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2431     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2432   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2433     game.magnify_time_left;
2434
2435   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2436     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2437      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2438      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2439      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2440      EL_BALLOON_SWITCH_NONE);
2441
2442   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2443     local_player->dynabomb_count;
2444   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2445     local_player->dynabomb_size;
2446   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2447     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2448
2449   game_panel_controls[GAME_PANEL_PENGUINS].value =
2450     game.friends_still_needed;
2451
2452   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2453     game.sokoban_objects_still_needed;
2454   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2455     game.sokoban_fields_still_needed;
2456
2457   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2458     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2459
2460   for (i = 0; i < NUM_BELTS; i++)
2461   {
2462     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2463       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2464        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2465     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2466       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2467   }
2468
2469   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2470     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2471   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2472     game.magic_wall_time_left;
2473
2474   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2475     local_player->gravity;
2476
2477   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2478     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2479
2480   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2481     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2482       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2483        game.panel.element[i].id : EL_UNDEFINED);
2484
2485   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2486     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2487       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2488        element_info[game.panel.element_count[i].id].element_count : 0);
2489
2490   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2491     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2492       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2493        element_info[game.panel.ce_score[i].id].collect_score : 0);
2494
2495   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2496     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2497       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2498        element_info[game.panel.ce_score_element[i].id].collect_score :
2499        EL_UNDEFINED);
2500
2501   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2502   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2503   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2504
2505   // update game panel control frames
2506
2507   for (i = 0; game_panel_controls[i].nr != -1; i++)
2508   {
2509     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2510
2511     if (gpc->type == TYPE_ELEMENT)
2512     {
2513       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2514       {
2515         int last_anim_random_frame = gfx.anim_random_frame;
2516         int element = gpc->value;
2517         int graphic = el2panelimg(element);
2518
2519         if (gpc->value != gpc->last_value)
2520         {
2521           gpc->gfx_frame = 0;
2522           gpc->gfx_random = INIT_GFX_RANDOM();
2523         }
2524         else
2525         {
2526           gpc->gfx_frame++;
2527
2528           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2529               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2530             gpc->gfx_random = INIT_GFX_RANDOM();
2531         }
2532
2533         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2534           gfx.anim_random_frame = gpc->gfx_random;
2535
2536         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2537           gpc->gfx_frame = element_info[element].collect_score;
2538
2539         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2540                                               gpc->gfx_frame);
2541
2542         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2543           gfx.anim_random_frame = last_anim_random_frame;
2544       }
2545     }
2546     else if (gpc->type == TYPE_GRAPHIC)
2547     {
2548       if (gpc->graphic != IMG_UNDEFINED)
2549       {
2550         int last_anim_random_frame = gfx.anim_random_frame;
2551         int graphic = gpc->graphic;
2552
2553         if (gpc->value != gpc->last_value)
2554         {
2555           gpc->gfx_frame = 0;
2556           gpc->gfx_random = INIT_GFX_RANDOM();
2557         }
2558         else
2559         {
2560           gpc->gfx_frame++;
2561
2562           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2563               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2564             gpc->gfx_random = INIT_GFX_RANDOM();
2565         }
2566
2567         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2568           gfx.anim_random_frame = gpc->gfx_random;
2569
2570         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2571
2572         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2573           gfx.anim_random_frame = last_anim_random_frame;
2574       }
2575     }
2576   }
2577 }
2578
2579 static void DisplayGameControlValues(void)
2580 {
2581   boolean redraw_panel = FALSE;
2582   int i;
2583
2584   for (i = 0; game_panel_controls[i].nr != -1; i++)
2585   {
2586     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2587
2588     if (PANEL_DEACTIVATED(gpc->pos))
2589       continue;
2590
2591     if (gpc->value == gpc->last_value &&
2592         gpc->frame == gpc->last_frame)
2593       continue;
2594
2595     redraw_panel = TRUE;
2596   }
2597
2598   if (!redraw_panel)
2599     return;
2600
2601   // copy default game door content to main double buffer
2602
2603   // !!! CHECK AGAIN !!!
2604   SetPanelBackground();
2605   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2606   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2607
2608   // redraw game control buttons
2609   RedrawGameButtons();
2610
2611   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2612
2613   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2614   {
2615     int nr = game_panel_order[i].nr;
2616     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2617     struct TextPosInfo *pos = gpc->pos;
2618     int type = gpc->type;
2619     int value = gpc->value;
2620     int frame = gpc->frame;
2621     int size = pos->size;
2622     int font = pos->font;
2623     boolean draw_masked = pos->draw_masked;
2624     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2625
2626     if (PANEL_DEACTIVATED(pos))
2627       continue;
2628
2629     gpc->last_value = value;
2630     gpc->last_frame = frame;
2631
2632     if (type == TYPE_INTEGER)
2633     {
2634       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2635           nr == GAME_PANEL_TIME)
2636       {
2637         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2638
2639         if (use_dynamic_size)           // use dynamic number of digits
2640         {
2641           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2642           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2643           int size2 = size1 + 1;
2644           int font1 = pos->font;
2645           int font2 = pos->font_alt;
2646
2647           size = (value < value_change ? size1 : size2);
2648           font = (value < value_change ? font1 : font2);
2649         }
2650       }
2651
2652       // correct text size if "digits" is zero or less
2653       if (size <= 0)
2654         size = strlen(int2str(value, size));
2655
2656       // dynamically correct text alignment
2657       pos->width = size * getFontWidth(font);
2658
2659       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2660                   int2str(value, size), font, mask_mode);
2661     }
2662     else if (type == TYPE_ELEMENT)
2663     {
2664       int element, graphic;
2665       Bitmap *src_bitmap;
2666       int src_x, src_y;
2667       int width, height;
2668       int dst_x = PANEL_XPOS(pos);
2669       int dst_y = PANEL_YPOS(pos);
2670
2671       if (value != EL_UNDEFINED && value != EL_EMPTY)
2672       {
2673         element = value;
2674         graphic = el2panelimg(value);
2675
2676 #if 0
2677         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2678               element, EL_NAME(element), size);
2679 #endif
2680
2681         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2682           size = TILESIZE;
2683
2684         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2685                               &src_x, &src_y);
2686
2687         width  = graphic_info[graphic].width  * size / TILESIZE;
2688         height = graphic_info[graphic].height * size / TILESIZE;
2689
2690         if (draw_masked)
2691           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2692                            dst_x, dst_y);
2693         else
2694           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2695                      dst_x, dst_y);
2696       }
2697     }
2698     else if (type == TYPE_GRAPHIC)
2699     {
2700       int graphic        = gpc->graphic;
2701       int graphic_active = gpc->graphic_active;
2702       Bitmap *src_bitmap;
2703       int src_x, src_y;
2704       int width, height;
2705       int dst_x = PANEL_XPOS(pos);
2706       int dst_y = PANEL_YPOS(pos);
2707       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2708                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2709
2710       if (graphic != IMG_UNDEFINED && !skip)
2711       {
2712         if (pos->style == STYLE_REVERSE)
2713           value = 100 - value;
2714
2715         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2716
2717         if (pos->direction & MV_HORIZONTAL)
2718         {
2719           width  = graphic_info[graphic_active].width * value / 100;
2720           height = graphic_info[graphic_active].height;
2721
2722           if (pos->direction == MV_LEFT)
2723           {
2724             src_x += graphic_info[graphic_active].width - width;
2725             dst_x += graphic_info[graphic_active].width - width;
2726           }
2727         }
2728         else
2729         {
2730           width  = graphic_info[graphic_active].width;
2731           height = graphic_info[graphic_active].height * value / 100;
2732
2733           if (pos->direction == MV_UP)
2734           {
2735             src_y += graphic_info[graphic_active].height - height;
2736             dst_y += graphic_info[graphic_active].height - height;
2737           }
2738         }
2739
2740         if (draw_masked)
2741           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2742                            dst_x, dst_y);
2743         else
2744           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2745                      dst_x, dst_y);
2746
2747         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2748
2749         if (pos->direction & MV_HORIZONTAL)
2750         {
2751           if (pos->direction == MV_RIGHT)
2752           {
2753             src_x += width;
2754             dst_x += width;
2755           }
2756           else
2757           {
2758             dst_x = PANEL_XPOS(pos);
2759           }
2760
2761           width = graphic_info[graphic].width - width;
2762         }
2763         else
2764         {
2765           if (pos->direction == MV_DOWN)
2766           {
2767             src_y += height;
2768             dst_y += height;
2769           }
2770           else
2771           {
2772             dst_y = PANEL_YPOS(pos);
2773           }
2774
2775           height = graphic_info[graphic].height - height;
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     }
2786     else if (type == TYPE_STRING)
2787     {
2788       boolean active = (value != 0);
2789       char *state_normal = "off";
2790       char *state_active = "on";
2791       char *state = (active ? state_active : state_normal);
2792       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2793                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2794                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2795                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2796
2797       if (nr == GAME_PANEL_GRAVITY_STATE)
2798       {
2799         int font1 = pos->font;          // (used for normal state)
2800         int font2 = pos->font_alt;      // (used for active state)
2801
2802         font = (active ? font2 : font1);
2803       }
2804
2805       if (s != NULL)
2806       {
2807         char *s_cut;
2808
2809         if (size <= 0)
2810         {
2811           // don't truncate output if "chars" is zero or less
2812           size = strlen(s);
2813
2814           // dynamically correct text alignment
2815           pos->width = size * getFontWidth(font);
2816         }
2817
2818         s_cut = getStringCopyN(s, size);
2819
2820         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2821                     s_cut, font, mask_mode);
2822
2823         free(s_cut);
2824       }
2825     }
2826
2827     redraw_mask |= REDRAW_DOOR_1;
2828   }
2829
2830   SetGameStatus(GAME_MODE_PLAYING);
2831 }
2832
2833 void UpdateAndDisplayGameControlValues(void)
2834 {
2835   if (tape.deactivate_display)
2836     return;
2837
2838   UpdateGameControlValues();
2839   DisplayGameControlValues();
2840 }
2841
2842 #if 0
2843 static void UpdateGameDoorValues(void)
2844 {
2845   UpdateGameControlValues();
2846 }
2847 #endif
2848
2849 void DrawGameDoorValues(void)
2850 {
2851   DisplayGameControlValues();
2852 }
2853
2854
2855 // ============================================================================
2856 // InitGameEngine()
2857 // ----------------------------------------------------------------------------
2858 // initialize game engine due to level / tape version number
2859 // ============================================================================
2860
2861 static void InitGameEngine(void)
2862 {
2863   int i, j, k, l, x, y;
2864
2865   // set game engine from tape file when re-playing, else from level file
2866   game.engine_version = (tape.playing ? tape.engine_version :
2867                          level.game_version);
2868
2869   // set single or multi-player game mode (needed for re-playing tapes)
2870   game.team_mode = setup.team_mode;
2871
2872   if (tape.playing)
2873   {
2874     int num_players = 0;
2875
2876     for (i = 0; i < MAX_PLAYERS; i++)
2877       if (tape.player_participates[i])
2878         num_players++;
2879
2880     // multi-player tapes contain input data for more than one player
2881     game.team_mode = (num_players > 1);
2882   }
2883
2884 #if 0
2885   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2886         level.game_version);
2887   Debug("game:init:level", "          tape.file_version   == %06d",
2888         tape.file_version);
2889   Debug("game:init:level", "          tape.game_version   == %06d",
2890         tape.game_version);
2891   Debug("game:init:level", "          tape.engine_version == %06d",
2892         tape.engine_version);
2893   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2894         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2895 #endif
2896
2897   // --------------------------------------------------------------------------
2898   // set flags for bugs and changes according to active game engine version
2899   // --------------------------------------------------------------------------
2900
2901   /*
2902     Summary of bugfix:
2903     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2904
2905     Bug was introduced in version:
2906     2.0.1
2907
2908     Bug was fixed in version:
2909     4.2.0.0
2910
2911     Description:
2912     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2913     but the property "can fall" was missing, which caused some levels to be
2914     unsolvable. This was fixed in version 4.2.0.0.
2915
2916     Affected levels/tapes:
2917     An example for a tape that was fixed by this bugfix is tape 029 from the
2918     level set "rnd_sam_bateman".
2919     The wrong behaviour will still be used for all levels or tapes that were
2920     created/recorded with it. An example for this is tape 023 from the level
2921     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2922   */
2923
2924   boolean use_amoeba_dropping_cannot_fall_bug =
2925     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2926       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2927      (tape.playing &&
2928       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2929       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2930
2931   /*
2932     Summary of bugfix/change:
2933     Fixed move speed of elements entering or leaving magic wall.
2934
2935     Fixed/changed in version:
2936     2.0.1
2937
2938     Description:
2939     Before 2.0.1, move speed of elements entering or leaving magic wall was
2940     twice as fast as it is now.
2941     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2942
2943     Affected levels/tapes:
2944     The first condition is generally needed for all levels/tapes before version
2945     2.0.1, which might use the old behaviour before it was changed; known tapes
2946     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2947     The second condition is an exception from the above case and is needed for
2948     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2949     above, but before it was known that this change would break tapes like the
2950     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2951     although the engine version while recording maybe was before 2.0.1. There
2952     are a lot of tapes that are affected by this exception, like tape 006 from
2953     the level set "rnd_conor_mancone".
2954   */
2955
2956   boolean use_old_move_stepsize_for_magic_wall =
2957     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2958      !(tape.playing &&
2959        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2960        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2961
2962   /*
2963     Summary of bugfix/change:
2964     Fixed handling for custom elements that change when pushed by the player.
2965
2966     Fixed/changed in version:
2967     3.1.0
2968
2969     Description:
2970     Before 3.1.0, custom elements that "change when pushing" changed directly
2971     after the player started pushing them (until then handled in "DigField()").
2972     Since 3.1.0, these custom elements are not changed until the "pushing"
2973     move of the element is finished (now handled in "ContinueMoving()").
2974
2975     Affected levels/tapes:
2976     The first condition is generally needed for all levels/tapes before version
2977     3.1.0, which might use the old behaviour before it was changed; known tapes
2978     that are affected are some tapes from the level set "Walpurgis Gardens" by
2979     Jamie Cullen.
2980     The second condition is an exception from the above case and is needed for
2981     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2982     above (including some development versions of 3.1.0), but before it was
2983     known that this change would break tapes like the above and was fixed in
2984     3.1.1, so that the changed behaviour was active although the engine version
2985     while recording maybe was before 3.1.0. There is at least one tape that is
2986     affected by this exception, which is the tape for the one-level set "Bug
2987     Machine" by Juergen Bonhagen.
2988   */
2989
2990   game.use_change_when_pushing_bug =
2991     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2992      !(tape.playing &&
2993        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2994        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2995
2996   /*
2997     Summary of bugfix/change:
2998     Fixed handling for blocking the field the player leaves when moving.
2999
3000     Fixed/changed in version:
3001     3.1.1
3002
3003     Description:
3004     Before 3.1.1, when "block last field when moving" was enabled, the field
3005     the player is leaving when moving was blocked for the time of the move,
3006     and was directly unblocked afterwards. This resulted in the last field
3007     being blocked for exactly one less than the number of frames of one player
3008     move. Additionally, even when blocking was disabled, the last field was
3009     blocked for exactly one frame.
3010     Since 3.1.1, due to changes in player movement handling, the last field
3011     is not blocked at all when blocking is disabled. When blocking is enabled,
3012     the last field is blocked for exactly the number of frames of one player
3013     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3014     last field is blocked for exactly one more than the number of frames of
3015     one player move.
3016
3017     Affected levels/tapes:
3018     (!!! yet to be determined -- probably many !!!)
3019   */
3020
3021   game.use_block_last_field_bug =
3022     (game.engine_version < VERSION_IDENT(3,1,1,0));
3023
3024   /* various special flags and settings for native Emerald Mine game engine */
3025
3026   game_em.use_single_button =
3027     (game.engine_version > VERSION_IDENT(4,0,0,2));
3028
3029   game_em.use_snap_key_bug =
3030     (game.engine_version < VERSION_IDENT(4,0,1,0));
3031
3032   game_em.use_random_bug =
3033     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3034
3035   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3036
3037   game_em.use_old_explosions            = use_old_em_engine;
3038   game_em.use_old_android               = use_old_em_engine;
3039   game_em.use_old_push_elements         = use_old_em_engine;
3040   game_em.use_old_push_into_acid        = use_old_em_engine;
3041
3042   game_em.use_wrap_around               = !use_old_em_engine;
3043
3044   // --------------------------------------------------------------------------
3045
3046   // set maximal allowed number of custom element changes per game frame
3047   game.max_num_changes_per_frame = 1;
3048
3049   // default scan direction: scan playfield from top/left to bottom/right
3050   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3051
3052   // dynamically adjust element properties according to game engine version
3053   InitElementPropertiesEngine(game.engine_version);
3054
3055   // ---------- initialize special element properties -------------------------
3056
3057   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3058   if (use_amoeba_dropping_cannot_fall_bug)
3059     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3060
3061   // ---------- initialize player's initial move delay ------------------------
3062
3063   // dynamically adjust player properties according to level information
3064   for (i = 0; i < MAX_PLAYERS; i++)
3065     game.initial_move_delay_value[i] =
3066       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3067
3068   // dynamically adjust player properties according to game engine version
3069   for (i = 0; i < MAX_PLAYERS; i++)
3070     game.initial_move_delay[i] =
3071       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3072        game.initial_move_delay_value[i] : 0);
3073
3074   // ---------- initialize player's initial push delay ------------------------
3075
3076   // dynamically adjust player properties according to game engine version
3077   game.initial_push_delay_value =
3078     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3079
3080   // ---------- initialize changing elements ----------------------------------
3081
3082   // initialize changing elements information
3083   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3084   {
3085     struct ElementInfo *ei = &element_info[i];
3086
3087     // this pointer might have been changed in the level editor
3088     ei->change = &ei->change_page[0];
3089
3090     if (!IS_CUSTOM_ELEMENT(i))
3091     {
3092       ei->change->target_element = EL_EMPTY_SPACE;
3093       ei->change->delay_fixed = 0;
3094       ei->change->delay_random = 0;
3095       ei->change->delay_frames = 1;
3096     }
3097
3098     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3099     {
3100       ei->has_change_event[j] = FALSE;
3101
3102       ei->event_page_nr[j] = 0;
3103       ei->event_page[j] = &ei->change_page[0];
3104     }
3105   }
3106
3107   // add changing elements from pre-defined list
3108   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3109   {
3110     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3111     struct ElementInfo *ei = &element_info[ch_delay->element];
3112
3113     ei->change->target_element       = ch_delay->target_element;
3114     ei->change->delay_fixed          = ch_delay->change_delay;
3115
3116     ei->change->pre_change_function  = ch_delay->pre_change_function;
3117     ei->change->change_function      = ch_delay->change_function;
3118     ei->change->post_change_function = ch_delay->post_change_function;
3119
3120     ei->change->can_change = TRUE;
3121     ei->change->can_change_or_has_action = TRUE;
3122
3123     ei->has_change_event[CE_DELAY] = TRUE;
3124
3125     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3126     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3127   }
3128
3129   // ---------- initialize internal run-time variables ------------------------
3130
3131   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3132   {
3133     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3134
3135     for (j = 0; j < ei->num_change_pages; j++)
3136     {
3137       ei->change_page[j].can_change_or_has_action =
3138         (ei->change_page[j].can_change |
3139          ei->change_page[j].has_action);
3140     }
3141   }
3142
3143   // add change events from custom element configuration
3144   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3145   {
3146     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3147
3148     for (j = 0; j < ei->num_change_pages; j++)
3149     {
3150       if (!ei->change_page[j].can_change_or_has_action)
3151         continue;
3152
3153       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3154       {
3155         // only add event page for the first page found with this event
3156         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3157         {
3158           ei->has_change_event[k] = TRUE;
3159
3160           ei->event_page_nr[k] = j;
3161           ei->event_page[k] = &ei->change_page[j];
3162         }
3163       }
3164     }
3165   }
3166
3167   // ---------- initialize reference elements in change conditions ------------
3168
3169   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3170   {
3171     int element = EL_CUSTOM_START + i;
3172     struct ElementInfo *ei = &element_info[element];
3173
3174     for (j = 0; j < ei->num_change_pages; j++)
3175     {
3176       int trigger_element = ei->change_page[j].initial_trigger_element;
3177
3178       if (trigger_element >= EL_PREV_CE_8 &&
3179           trigger_element <= EL_NEXT_CE_8)
3180         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3181
3182       ei->change_page[j].trigger_element = trigger_element;
3183     }
3184   }
3185
3186   // ---------- initialize run-time trigger player and element ----------------
3187
3188   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3189   {
3190     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3191
3192     for (j = 0; j < ei->num_change_pages; j++)
3193     {
3194       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3195       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3196       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3197       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3198       ei->change_page[j].actual_trigger_ce_value = 0;
3199       ei->change_page[j].actual_trigger_ce_score = 0;
3200     }
3201   }
3202
3203   // ---------- initialize trigger events -------------------------------------
3204
3205   // initialize trigger events information
3206   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3207     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3208       trigger_events[i][j] = FALSE;
3209
3210   // add trigger events from element change event properties
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     struct ElementInfo *ei = &element_info[i];
3214
3215     for (j = 0; j < ei->num_change_pages; j++)
3216     {
3217       if (!ei->change_page[j].can_change_or_has_action)
3218         continue;
3219
3220       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3221       {
3222         int trigger_element = ei->change_page[j].trigger_element;
3223
3224         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3225         {
3226           if (ei->change_page[j].has_event[k])
3227           {
3228             if (IS_GROUP_ELEMENT(trigger_element))
3229             {
3230               struct ElementGroupInfo *group =
3231                 element_info[trigger_element].group;
3232
3233               for (l = 0; l < group->num_elements_resolved; l++)
3234                 trigger_events[group->element_resolved[l]][k] = TRUE;
3235             }
3236             else if (trigger_element == EL_ANY_ELEMENT)
3237               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3238                 trigger_events[l][k] = TRUE;
3239             else
3240               trigger_events[trigger_element][k] = TRUE;
3241           }
3242         }
3243       }
3244     }
3245   }
3246
3247   // ---------- initialize push delay -----------------------------------------
3248
3249   // initialize push delay values to default
3250   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3251   {
3252     if (!IS_CUSTOM_ELEMENT(i))
3253     {
3254       // set default push delay values (corrected since version 3.0.7-1)
3255       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3256       {
3257         element_info[i].push_delay_fixed = 2;
3258         element_info[i].push_delay_random = 8;
3259       }
3260       else
3261       {
3262         element_info[i].push_delay_fixed = 8;
3263         element_info[i].push_delay_random = 8;
3264       }
3265     }
3266   }
3267
3268   // set push delay value for certain elements from pre-defined list
3269   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3270   {
3271     int e = push_delay_list[i].element;
3272
3273     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3274     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3275   }
3276
3277   // set push delay value for Supaplex elements for newer engine versions
3278   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3279   {
3280     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281     {
3282       if (IS_SP_ELEMENT(i))
3283       {
3284         // set SP push delay to just enough to push under a falling zonk
3285         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3286
3287         element_info[i].push_delay_fixed  = delay;
3288         element_info[i].push_delay_random = 0;
3289       }
3290     }
3291   }
3292
3293   // ---------- initialize move stepsize --------------------------------------
3294
3295   // initialize move stepsize values to default
3296   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3297     if (!IS_CUSTOM_ELEMENT(i))
3298       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3299
3300   // set move stepsize value for certain elements from pre-defined list
3301   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3302   {
3303     int e = move_stepsize_list[i].element;
3304
3305     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3306
3307     // set move stepsize value for certain elements for older engine versions
3308     if (use_old_move_stepsize_for_magic_wall)
3309     {
3310       if (e == EL_MAGIC_WALL_FILLING ||
3311           e == EL_MAGIC_WALL_EMPTYING ||
3312           e == EL_BD_MAGIC_WALL_FILLING ||
3313           e == EL_BD_MAGIC_WALL_EMPTYING)
3314         element_info[e].move_stepsize *= 2;
3315     }
3316   }
3317
3318   // ---------- initialize collect score --------------------------------------
3319
3320   // initialize collect score values for custom elements from initial value
3321   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3322     if (IS_CUSTOM_ELEMENT(i))
3323       element_info[i].collect_score = element_info[i].collect_score_initial;
3324
3325   // ---------- initialize collect count --------------------------------------
3326
3327   // initialize collect count values for non-custom elements
3328   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3329     if (!IS_CUSTOM_ELEMENT(i))
3330       element_info[i].collect_count_initial = 0;
3331
3332   // add collect count values for all elements from pre-defined list
3333   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3334     element_info[collect_count_list[i].element].collect_count_initial =
3335       collect_count_list[i].count;
3336
3337   // ---------- initialize access direction -----------------------------------
3338
3339   // initialize access direction values to default (access from every side)
3340   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3341     if (!IS_CUSTOM_ELEMENT(i))
3342       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3343
3344   // set access direction value for certain elements from pre-defined list
3345   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3346     element_info[access_direction_list[i].element].access_direction =
3347       access_direction_list[i].direction;
3348
3349   // ---------- initialize explosion content ----------------------------------
3350   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3351   {
3352     if (IS_CUSTOM_ELEMENT(i))
3353       continue;
3354
3355     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3356     {
3357       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3358
3359       element_info[i].content.e[x][y] =
3360         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3361          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3362          i == EL_PLAYER_3 ? EL_EMERALD :
3363          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3364          i == EL_MOLE ? EL_EMERALD_RED :
3365          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3366          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3367          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3368          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3369          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3370          i == EL_WALL_EMERALD ? EL_EMERALD :
3371          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3372          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3373          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3374          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3375          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3376          i == EL_WALL_PEARL ? EL_PEARL :
3377          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3378          EL_EMPTY);
3379     }
3380   }
3381
3382   // ---------- initialize recursion detection --------------------------------
3383   recursion_loop_depth = 0;
3384   recursion_loop_detected = FALSE;
3385   recursion_loop_element = EL_UNDEFINED;
3386
3387   // ---------- initialize graphics engine ------------------------------------
3388   game.scroll_delay_value =
3389     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3390      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3391      !setup.forced_scroll_delay           ? 0 :
3392      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3393   game.scroll_delay_value =
3394     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3395
3396   // ---------- initialize game engine snapshots ------------------------------
3397   for (i = 0; i < MAX_PLAYERS; i++)
3398     game.snapshot.last_action[i] = 0;
3399   game.snapshot.changed_action = FALSE;
3400   game.snapshot.collected_item = FALSE;
3401   game.snapshot.mode =
3402     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3403      SNAPSHOT_MODE_EVERY_STEP :
3404      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3405      SNAPSHOT_MODE_EVERY_MOVE :
3406      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3407      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3408   game.snapshot.save_snapshot = FALSE;
3409
3410   // ---------- initialize level time for Supaplex engine ---------------------
3411   // Supaplex levels with time limit currently unsupported -- should be added
3412   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3413     level.time = 0;
3414
3415   // ---------- initialize flags for handling game actions --------------------
3416
3417   // set flags for game actions to default values
3418   game.use_key_actions = TRUE;
3419   game.use_mouse_actions = FALSE;
3420
3421   // when using Mirror Magic game engine, handle mouse events only
3422   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3423   {
3424     game.use_key_actions = FALSE;
3425     game.use_mouse_actions = TRUE;
3426   }
3427
3428   // check for custom elements with mouse click events
3429   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3430   {
3431     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432     {
3433       int element = EL_CUSTOM_START + i;
3434
3435       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3436           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3437           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3438           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3439         game.use_mouse_actions = TRUE;
3440     }
3441   }
3442 }
3443
3444 static int get_num_special_action(int element, int action_first,
3445                                   int action_last)
3446 {
3447   int num_special_action = 0;
3448   int i, j;
3449
3450   for (i = action_first; i <= action_last; i++)
3451   {
3452     boolean found = FALSE;
3453
3454     for (j = 0; j < NUM_DIRECTIONS; j++)
3455       if (el_act_dir2img(element, i, j) !=
3456           el_act_dir2img(element, ACTION_DEFAULT, j))
3457         found = TRUE;
3458
3459     if (found)
3460       num_special_action++;
3461     else
3462       break;
3463   }
3464
3465   return num_special_action;
3466 }
3467
3468
3469 // ============================================================================
3470 // InitGame()
3471 // ----------------------------------------------------------------------------
3472 // initialize and start new game
3473 // ============================================================================
3474
3475 #if DEBUG_INIT_PLAYER
3476 static void DebugPrintPlayerStatus(char *message)
3477 {
3478   int i;
3479
3480   if (!options.debug)
3481     return;
3482
3483   Debug("game:init:player", "%s:", message);
3484
3485   for (i = 0; i < MAX_PLAYERS; i++)
3486   {
3487     struct PlayerInfo *player = &stored_player[i];
3488
3489     Debug("game:init:player",
3490           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3491           i + 1,
3492           player->present,
3493           player->connected,
3494           player->connected_locally,
3495           player->connected_network,
3496           player->active,
3497           (local_player == player ? " (local player)" : ""));
3498   }
3499 }
3500 #endif
3501
3502 void InitGame(void)
3503 {
3504   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3505   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3506   int fade_mask = REDRAW_FIELD;
3507
3508   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3509   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3510   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3511   int initial_move_dir = MV_DOWN;
3512   int i, j, x, y;
3513
3514   // required here to update video display before fading (FIX THIS)
3515   DrawMaskedBorder(REDRAW_DOOR_2);
3516
3517   if (!game.restart_level)
3518     CloseDoor(DOOR_CLOSE_1);
3519
3520   SetGameStatus(GAME_MODE_PLAYING);
3521
3522   if (level_editor_test_game)
3523     FadeSkipNextFadeOut();
3524   else
3525     FadeSetEnterScreen();
3526
3527   if (CheckFadeAll())
3528     fade_mask = REDRAW_ALL;
3529
3530   FadeLevelSoundsAndMusic();
3531
3532   ExpireSoundLoops(TRUE);
3533
3534   FadeOut(fade_mask);
3535
3536   if (level_editor_test_game)
3537     FadeSkipNextFadeIn();
3538
3539   // needed if different viewport properties defined for playing
3540   ChangeViewportPropertiesIfNeeded();
3541
3542   ClearField();
3543
3544   DrawCompleteVideoDisplay();
3545
3546   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3547
3548   InitGameEngine();
3549   InitGameControlValues();
3550
3551   // initialize tape actions from game when recording tape
3552   if (tape.recording)
3553   {
3554     tape.use_key_actions   = game.use_key_actions;
3555     tape.use_mouse_actions = game.use_mouse_actions;
3556   }
3557
3558   // don't play tapes over network
3559   network_playing = (network.enabled && !tape.playing);
3560
3561   for (i = 0; i < MAX_PLAYERS; i++)
3562   {
3563     struct PlayerInfo *player = &stored_player[i];
3564
3565     player->index_nr = i;
3566     player->index_bit = (1 << i);
3567     player->element_nr = EL_PLAYER_1 + i;
3568
3569     player->present = FALSE;
3570     player->active = FALSE;
3571     player->mapped = FALSE;
3572
3573     player->killed = FALSE;
3574     player->reanimated = FALSE;
3575     player->buried = FALSE;
3576
3577     player->action = 0;
3578     player->effective_action = 0;
3579     player->programmed_action = 0;
3580     player->snap_action = 0;
3581
3582     player->mouse_action.lx = 0;
3583     player->mouse_action.ly = 0;
3584     player->mouse_action.button = 0;
3585     player->mouse_action.button_hint = 0;
3586
3587     player->effective_mouse_action.lx = 0;
3588     player->effective_mouse_action.ly = 0;
3589     player->effective_mouse_action.button = 0;
3590     player->effective_mouse_action.button_hint = 0;
3591
3592     for (j = 0; j < MAX_NUM_KEYS; j++)
3593       player->key[j] = FALSE;
3594
3595     player->num_white_keys = 0;
3596
3597     player->dynabomb_count = 0;
3598     player->dynabomb_size = 1;
3599     player->dynabombs_left = 0;
3600     player->dynabomb_xl = FALSE;
3601
3602     player->MovDir = initial_move_dir;
3603     player->MovPos = 0;
3604     player->GfxPos = 0;
3605     player->GfxDir = initial_move_dir;
3606     player->GfxAction = ACTION_DEFAULT;
3607     player->Frame = 0;
3608     player->StepFrame = 0;
3609
3610     player->initial_element = player->element_nr;
3611     player->artwork_element =
3612       (level.use_artwork_element[i] ? level.artwork_element[i] :
3613        player->element_nr);
3614     player->use_murphy = FALSE;
3615
3616     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3617     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3618
3619     player->gravity = level.initial_player_gravity[i];
3620
3621     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3622
3623     player->actual_frame_counter = 0;
3624
3625     player->step_counter = 0;
3626
3627     player->last_move_dir = initial_move_dir;
3628
3629     player->is_active = FALSE;
3630
3631     player->is_waiting = FALSE;
3632     player->is_moving = FALSE;
3633     player->is_auto_moving = FALSE;
3634     player->is_digging = FALSE;
3635     player->is_snapping = FALSE;
3636     player->is_collecting = FALSE;
3637     player->is_pushing = FALSE;
3638     player->is_switching = FALSE;
3639     player->is_dropping = FALSE;
3640     player->is_dropping_pressed = FALSE;
3641
3642     player->is_bored = FALSE;
3643     player->is_sleeping = FALSE;
3644
3645     player->was_waiting = TRUE;
3646     player->was_moving = FALSE;
3647     player->was_snapping = FALSE;
3648     player->was_dropping = FALSE;
3649
3650     player->force_dropping = FALSE;
3651
3652     player->frame_counter_bored = -1;
3653     player->frame_counter_sleeping = -1;
3654
3655     player->anim_delay_counter = 0;
3656     player->post_delay_counter = 0;
3657
3658     player->dir_waiting = initial_move_dir;
3659     player->action_waiting = ACTION_DEFAULT;
3660     player->last_action_waiting = ACTION_DEFAULT;
3661     player->special_action_bored = ACTION_DEFAULT;
3662     player->special_action_sleeping = ACTION_DEFAULT;
3663
3664     player->switch_x = -1;
3665     player->switch_y = -1;
3666
3667     player->drop_x = -1;
3668     player->drop_y = -1;
3669
3670     player->show_envelope = 0;
3671
3672     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3673
3674     player->push_delay       = -1;      // initialized when pushing starts
3675     player->push_delay_value = game.initial_push_delay_value;
3676
3677     player->drop_delay = 0;
3678     player->drop_pressed_delay = 0;
3679
3680     player->last_jx = -1;
3681     player->last_jy = -1;
3682     player->jx = -1;
3683     player->jy = -1;
3684
3685     player->shield_normal_time_left = 0;
3686     player->shield_deadly_time_left = 0;
3687
3688     player->inventory_infinite_element = EL_UNDEFINED;
3689     player->inventory_size = 0;
3690
3691     if (level.use_initial_inventory[i])
3692     {
3693       for (j = 0; j < level.initial_inventory_size[i]; j++)
3694       {
3695         int element = level.initial_inventory_content[i][j];
3696         int collect_count = element_info[element].collect_count_initial;
3697         int k;
3698
3699         if (!IS_CUSTOM_ELEMENT(element))
3700           collect_count = 1;
3701
3702         if (collect_count == 0)
3703           player->inventory_infinite_element = element;
3704         else
3705           for (k = 0; k < collect_count; k++)
3706             if (player->inventory_size < MAX_INVENTORY_SIZE)
3707               player->inventory_element[player->inventory_size++] = element;
3708       }
3709     }
3710
3711     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3712     SnapField(player, 0, 0);
3713
3714     map_player_action[i] = i;
3715   }
3716
3717   network_player_action_received = FALSE;
3718
3719   // initial null action
3720   if (network_playing)
3721     SendToServer_MovePlayer(MV_NONE);
3722
3723   FrameCounter = 0;
3724   TimeFrames = 0;
3725   TimePlayed = 0;
3726   TimeLeft = level.time;
3727   TapeTime = 0;
3728
3729   ScreenMovDir = MV_NONE;
3730   ScreenMovPos = 0;
3731   ScreenGfxPos = 0;
3732
3733   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3734
3735   game.robot_wheel_x = -1;
3736   game.robot_wheel_y = -1;
3737
3738   game.exit_x = -1;
3739   game.exit_y = -1;
3740
3741   game.all_players_gone = FALSE;
3742
3743   game.LevelSolved = FALSE;
3744   game.GameOver = FALSE;
3745
3746   game.GamePlayed = !tape.playing;
3747
3748   game.LevelSolved_GameWon = FALSE;
3749   game.LevelSolved_GameEnd = FALSE;
3750   game.LevelSolved_SaveTape = FALSE;
3751   game.LevelSolved_SaveScore = FALSE;
3752
3753   game.LevelSolved_CountingTime = 0;
3754   game.LevelSolved_CountingScore = 0;
3755   game.LevelSolved_CountingHealth = 0;
3756
3757   game.panel.active = TRUE;
3758
3759   game.no_time_limit = (level.time == 0);
3760
3761   game.yamyam_content_nr = 0;
3762   game.robot_wheel_active = FALSE;
3763   game.magic_wall_active = FALSE;
3764   game.magic_wall_time_left = 0;
3765   game.light_time_left = 0;
3766   game.timegate_time_left = 0;
3767   game.switchgate_pos = 0;
3768   game.wind_direction = level.wind_direction_initial;
3769
3770   game.score = 0;
3771   game.score_final = 0;
3772
3773   game.health = MAX_HEALTH;
3774   game.health_final = MAX_HEALTH;
3775
3776   game.gems_still_needed = level.gems_needed;
3777   game.sokoban_fields_still_needed = 0;
3778   game.sokoban_objects_still_needed = 0;
3779   game.lights_still_needed = 0;
3780   game.players_still_needed = 0;
3781   game.friends_still_needed = 0;
3782
3783   game.lenses_time_left = 0;
3784   game.magnify_time_left = 0;
3785
3786   game.ball_active = level.ball_active_initial;
3787   game.ball_content_nr = 0;
3788
3789   game.explosions_delayed = TRUE;
3790
3791   game.envelope_active = FALSE;
3792
3793   for (i = 0; i < NUM_BELTS; i++)
3794   {
3795     game.belt_dir[i] = MV_NONE;
3796     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3797   }
3798
3799   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3800     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3801
3802 #if DEBUG_INIT_PLAYER
3803   DebugPrintPlayerStatus("Player status at level initialization");
3804 #endif
3805
3806   SCAN_PLAYFIELD(x, y)
3807   {
3808     Tile[x][y] = Last[x][y] = level.field[x][y];
3809     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3810     ChangeDelay[x][y] = 0;
3811     ChangePage[x][y] = -1;
3812     CustomValue[x][y] = 0;              // initialized in InitField()
3813     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3814     AmoebaNr[x][y] = 0;
3815     WasJustMoving[x][y] = 0;
3816     WasJustFalling[x][y] = 0;
3817     CheckCollision[x][y] = 0;
3818     CheckImpact[x][y] = 0;
3819     Stop[x][y] = FALSE;
3820     Pushed[x][y] = FALSE;
3821
3822     ChangeCount[x][y] = 0;
3823     ChangeEvent[x][y] = -1;
3824
3825     ExplodePhase[x][y] = 0;
3826     ExplodeDelay[x][y] = 0;
3827     ExplodeField[x][y] = EX_TYPE_NONE;
3828
3829     RunnerVisit[x][y] = 0;
3830     PlayerVisit[x][y] = 0;
3831
3832     GfxFrame[x][y] = 0;
3833     GfxRandom[x][y] = INIT_GFX_RANDOM();
3834     GfxElement[x][y] = EL_UNDEFINED;
3835     GfxAction[x][y] = ACTION_DEFAULT;
3836     GfxDir[x][y] = MV_NONE;
3837     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3838   }
3839
3840   SCAN_PLAYFIELD(x, y)
3841   {
3842     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3843       emulate_bd = FALSE;
3844     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3845       emulate_sb = FALSE;
3846     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3847       emulate_sp = FALSE;
3848
3849     InitField(x, y, TRUE);
3850
3851     ResetGfxAnimation(x, y);
3852   }
3853
3854   InitBeltMovement();
3855
3856   for (i = 0; i < MAX_PLAYERS; i++)
3857   {
3858     struct PlayerInfo *player = &stored_player[i];
3859
3860     // set number of special actions for bored and sleeping animation
3861     player->num_special_action_bored =
3862       get_num_special_action(player->artwork_element,
3863                              ACTION_BORING_1, ACTION_BORING_LAST);
3864     player->num_special_action_sleeping =
3865       get_num_special_action(player->artwork_element,
3866                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3867   }
3868
3869   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3870                     emulate_sb ? EMU_SOKOBAN :
3871                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3872
3873   // initialize type of slippery elements
3874   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3875   {
3876     if (!IS_CUSTOM_ELEMENT(i))
3877     {
3878       // default: elements slip down either to the left or right randomly
3879       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3880
3881       // SP style elements prefer to slip down on the left side
3882       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3883         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3884
3885       // BD style elements prefer to slip down on the left side
3886       if (game.emulation == EMU_BOULDERDASH)
3887         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3888     }
3889   }
3890
3891   // initialize explosion and ignition delay
3892   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3893   {
3894     if (!IS_CUSTOM_ELEMENT(i))
3895     {
3896       int num_phase = 8;
3897       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3898                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3899                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3900       int last_phase = (num_phase + 1) * delay;
3901       int half_phase = (num_phase / 2) * delay;
3902
3903       element_info[i].explosion_delay = last_phase - 1;
3904       element_info[i].ignition_delay = half_phase;
3905
3906       if (i == EL_BLACK_ORB)
3907         element_info[i].ignition_delay = 1;
3908     }
3909   }
3910
3911   // correct non-moving belts to start moving left
3912   for (i = 0; i < NUM_BELTS; i++)
3913     if (game.belt_dir[i] == MV_NONE)
3914       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3915
3916 #if USE_NEW_PLAYER_ASSIGNMENTS
3917   // use preferred player also in local single-player mode
3918   if (!network.enabled && !game.team_mode)
3919   {
3920     int new_index_nr = setup.network_player_nr;
3921
3922     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3923     {
3924       for (i = 0; i < MAX_PLAYERS; i++)
3925         stored_player[i].connected_locally = FALSE;
3926
3927       stored_player[new_index_nr].connected_locally = TRUE;
3928     }
3929   }
3930
3931   for (i = 0; i < MAX_PLAYERS; i++)
3932   {
3933     stored_player[i].connected = FALSE;
3934
3935     // in network game mode, the local player might not be the first player
3936     if (stored_player[i].connected_locally)
3937       local_player = &stored_player[i];
3938   }
3939
3940   if (!network.enabled)
3941     local_player->connected = TRUE;
3942
3943   if (tape.playing)
3944   {
3945     for (i = 0; i < MAX_PLAYERS; i++)
3946       stored_player[i].connected = tape.player_participates[i];
3947   }
3948   else if (network.enabled)
3949   {
3950     // add team mode players connected over the network (needed for correct
3951     // assignment of player figures from level to locally playing players)
3952
3953     for (i = 0; i < MAX_PLAYERS; i++)
3954       if (stored_player[i].connected_network)
3955         stored_player[i].connected = TRUE;
3956   }
3957   else if (game.team_mode)
3958   {
3959     // try to guess locally connected team mode players (needed for correct
3960     // assignment of player figures from level to locally playing players)
3961
3962     for (i = 0; i < MAX_PLAYERS; i++)
3963       if (setup.input[i].use_joystick ||
3964           setup.input[i].key.left != KSYM_UNDEFINED)
3965         stored_player[i].connected = TRUE;
3966   }
3967
3968 #if DEBUG_INIT_PLAYER
3969   DebugPrintPlayerStatus("Player status after level initialization");
3970 #endif
3971
3972 #if DEBUG_INIT_PLAYER
3973   Debug("game:init:player", "Reassigning players ...");
3974 #endif
3975
3976   // check if any connected player was not found in playfield
3977   for (i = 0; i < MAX_PLAYERS; i++)
3978   {
3979     struct PlayerInfo *player = &stored_player[i];
3980
3981     if (player->connected && !player->present)
3982     {
3983       struct PlayerInfo *field_player = NULL;
3984
3985 #if DEBUG_INIT_PLAYER
3986       Debug("game:init:player",
3987             "- looking for field player for player %d ...", i + 1);
3988 #endif
3989
3990       // assign first free player found that is present in the playfield
3991
3992       // first try: look for unmapped playfield player that is not connected
3993       for (j = 0; j < MAX_PLAYERS; j++)
3994         if (field_player == NULL &&
3995             stored_player[j].present &&
3996             !stored_player[j].mapped &&
3997             !stored_player[j].connected)
3998           field_player = &stored_player[j];
3999
4000       // second try: look for *any* unmapped playfield player
4001       for (j = 0; j < MAX_PLAYERS; j++)
4002         if (field_player == NULL &&
4003             stored_player[j].present &&
4004             !stored_player[j].mapped)
4005           field_player = &stored_player[j];
4006
4007       if (field_player != NULL)
4008       {
4009         int jx = field_player->jx, jy = field_player->jy;
4010
4011 #if DEBUG_INIT_PLAYER
4012         Debug("game:init:player", "- found player %d",
4013               field_player->index_nr + 1);
4014 #endif
4015
4016         player->present = FALSE;
4017         player->active = FALSE;
4018
4019         field_player->present = TRUE;
4020         field_player->active = TRUE;
4021
4022         /*
4023         player->initial_element = field_player->initial_element;
4024         player->artwork_element = field_player->artwork_element;
4025
4026         player->block_last_field       = field_player->block_last_field;
4027         player->block_delay_adjustment = field_player->block_delay_adjustment;
4028         */
4029
4030         StorePlayer[jx][jy] = field_player->element_nr;
4031
4032         field_player->jx = field_player->last_jx = jx;
4033         field_player->jy = field_player->last_jy = jy;
4034
4035         if (local_player == player)
4036           local_player = field_player;
4037
4038         map_player_action[field_player->index_nr] = i;
4039
4040         field_player->mapped = TRUE;
4041
4042 #if DEBUG_INIT_PLAYER
4043         Debug("game:init:player", "- map_player_action[%d] == %d",
4044               field_player->index_nr + 1, i + 1);
4045 #endif
4046       }
4047     }
4048
4049     if (player->connected && player->present)
4050       player->mapped = TRUE;
4051   }
4052
4053 #if DEBUG_INIT_PLAYER
4054   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4055 #endif
4056
4057 #else
4058
4059   // check if any connected player was not found in playfield
4060   for (i = 0; i < MAX_PLAYERS; i++)
4061   {
4062     struct PlayerInfo *player = &stored_player[i];
4063
4064     if (player->connected && !player->present)
4065     {
4066       for (j = 0; j < MAX_PLAYERS; j++)
4067       {
4068         struct PlayerInfo *field_player = &stored_player[j];
4069         int jx = field_player->jx, jy = field_player->jy;
4070
4071         // assign first free player found that is present in the playfield
4072         if (field_player->present && !field_player->connected)
4073         {
4074           player->present = TRUE;
4075           player->active = TRUE;
4076
4077           field_player->present = FALSE;
4078           field_player->active = FALSE;
4079
4080           player->initial_element = field_player->initial_element;
4081           player->artwork_element = field_player->artwork_element;
4082
4083           player->block_last_field       = field_player->block_last_field;
4084           player->block_delay_adjustment = field_player->block_delay_adjustment;
4085
4086           StorePlayer[jx][jy] = player->element_nr;
4087
4088           player->jx = player->last_jx = jx;
4089           player->jy = player->last_jy = jy;
4090
4091           break;
4092         }
4093       }
4094     }
4095   }
4096 #endif
4097
4098 #if 0
4099   Debug("game:init:player", "local_player->present == %d",
4100         local_player->present);
4101 #endif
4102
4103   // set focus to local player for network games, else to all players
4104   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4105   game.centered_player_nr_next = game.centered_player_nr;
4106   game.set_centered_player = FALSE;
4107   game.set_centered_player_wrap = FALSE;
4108
4109   if (network_playing && tape.recording)
4110   {
4111     // store client dependent player focus when recording network games
4112     tape.centered_player_nr_next = game.centered_player_nr_next;
4113     tape.set_centered_player = TRUE;
4114   }
4115
4116   if (tape.playing)
4117   {
4118     // when playing a tape, eliminate all players who do not participate
4119
4120 #if USE_NEW_PLAYER_ASSIGNMENTS
4121
4122     if (!game.team_mode)
4123     {
4124       for (i = 0; i < MAX_PLAYERS; i++)
4125       {
4126         if (stored_player[i].active &&
4127             !tape.player_participates[map_player_action[i]])
4128         {
4129           struct PlayerInfo *player = &stored_player[i];
4130           int jx = player->jx, jy = player->jy;
4131
4132 #if DEBUG_INIT_PLAYER
4133           Debug("game:init:player", "Removing player %d at (%d, %d)",
4134                 i + 1, jx, jy);
4135 #endif
4136
4137           player->active = FALSE;
4138           StorePlayer[jx][jy] = 0;
4139           Tile[jx][jy] = EL_EMPTY;
4140         }
4141       }
4142     }
4143
4144 #else
4145
4146     for (i = 0; i < MAX_PLAYERS; i++)
4147     {
4148       if (stored_player[i].active &&
4149           !tape.player_participates[i])
4150       {
4151         struct PlayerInfo *player = &stored_player[i];
4152         int jx = player->jx, jy = player->jy;
4153
4154         player->active = FALSE;
4155         StorePlayer[jx][jy] = 0;
4156         Tile[jx][jy] = EL_EMPTY;
4157       }
4158     }
4159 #endif
4160   }
4161   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4162   {
4163     // when in single player mode, eliminate all but the local player
4164
4165     for (i = 0; i < MAX_PLAYERS; i++)
4166     {
4167       struct PlayerInfo *player = &stored_player[i];
4168
4169       if (player->active && player != local_player)
4170       {
4171         int jx = player->jx, jy = player->jy;
4172
4173         player->active = FALSE;
4174         player->present = FALSE;
4175
4176         StorePlayer[jx][jy] = 0;
4177         Tile[jx][jy] = EL_EMPTY;
4178       }
4179     }
4180   }
4181
4182   for (i = 0; i < MAX_PLAYERS; i++)
4183     if (stored_player[i].active)
4184       game.players_still_needed++;
4185
4186   if (level.solved_by_one_player)
4187     game.players_still_needed = 1;
4188
4189   // when recording the game, store which players take part in the game
4190   if (tape.recording)
4191   {
4192 #if USE_NEW_PLAYER_ASSIGNMENTS
4193     for (i = 0; i < MAX_PLAYERS; i++)
4194       if (stored_player[i].connected)
4195         tape.player_participates[i] = TRUE;
4196 #else
4197     for (i = 0; i < MAX_PLAYERS; i++)
4198       if (stored_player[i].active)
4199         tape.player_participates[i] = TRUE;
4200 #endif
4201   }
4202
4203 #if DEBUG_INIT_PLAYER
4204   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4205 #endif
4206
4207   if (BorderElement == EL_EMPTY)
4208   {
4209     SBX_Left = 0;
4210     SBX_Right = lev_fieldx - SCR_FIELDX;
4211     SBY_Upper = 0;
4212     SBY_Lower = lev_fieldy - SCR_FIELDY;
4213   }
4214   else
4215   {
4216     SBX_Left = -1;
4217     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4218     SBY_Upper = -1;
4219     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4220   }
4221
4222   if (full_lev_fieldx <= SCR_FIELDX)
4223     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4224   if (full_lev_fieldy <= SCR_FIELDY)
4225     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4226
4227   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4228     SBX_Left--;
4229   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4230     SBY_Upper--;
4231
4232   // if local player not found, look for custom element that might create
4233   // the player (make some assumptions about the right custom element)
4234   if (!local_player->present)
4235   {
4236     int start_x = 0, start_y = 0;
4237     int found_rating = 0;
4238     int found_element = EL_UNDEFINED;
4239     int player_nr = local_player->index_nr;
4240
4241     SCAN_PLAYFIELD(x, y)
4242     {
4243       int element = Tile[x][y];
4244       int content;
4245       int xx, yy;
4246       boolean is_player;
4247
4248       if (level.use_start_element[player_nr] &&
4249           level.start_element[player_nr] == element &&
4250           found_rating < 4)
4251       {
4252         start_x = x;
4253         start_y = y;
4254
4255         found_rating = 4;
4256         found_element = element;
4257       }
4258
4259       if (!IS_CUSTOM_ELEMENT(element))
4260         continue;
4261
4262       if (CAN_CHANGE(element))
4263       {
4264         for (i = 0; i < element_info[element].num_change_pages; i++)
4265         {
4266           // check for player created from custom element as single target
4267           content = element_info[element].change_page[i].target_element;
4268           is_player = ELEM_IS_PLAYER(content);
4269
4270           if (is_player && (found_rating < 3 ||
4271                             (found_rating == 3 && element < found_element)))
4272           {
4273             start_x = x;
4274             start_y = y;
4275
4276             found_rating = 3;
4277             found_element = element;
4278           }
4279         }
4280       }
4281
4282       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4283       {
4284         // check for player created from custom element as explosion content
4285         content = element_info[element].content.e[xx][yy];
4286         is_player = ELEM_IS_PLAYER(content);
4287
4288         if (is_player && (found_rating < 2 ||
4289                           (found_rating == 2 && element < found_element)))
4290         {
4291           start_x = x + xx - 1;
4292           start_y = y + yy - 1;
4293
4294           found_rating = 2;
4295           found_element = element;
4296         }
4297
4298         if (!CAN_CHANGE(element))
4299           continue;
4300
4301         for (i = 0; i < element_info[element].num_change_pages; i++)
4302         {
4303           // check for player created from custom element as extended target
4304           content =
4305             element_info[element].change_page[i].target_content.e[xx][yy];
4306
4307           is_player = ELEM_IS_PLAYER(content);
4308
4309           if (is_player && (found_rating < 1 ||
4310                             (found_rating == 1 && element < found_element)))
4311           {
4312             start_x = x + xx - 1;
4313             start_y = y + yy - 1;
4314
4315             found_rating = 1;
4316             found_element = element;
4317           }
4318         }
4319       }
4320     }
4321
4322     scroll_x = SCROLL_POSITION_X(start_x);
4323     scroll_y = SCROLL_POSITION_Y(start_y);
4324   }
4325   else
4326   {
4327     scroll_x = SCROLL_POSITION_X(local_player->jx);
4328     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4329   }
4330
4331   // !!! FIX THIS (START) !!!
4332   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4333   {
4334     InitGameEngine_EM();
4335   }
4336   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4337   {
4338     InitGameEngine_SP();
4339   }
4340   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4341   {
4342     InitGameEngine_MM();
4343   }
4344   else
4345   {
4346     DrawLevel(REDRAW_FIELD);
4347     DrawAllPlayers();
4348
4349     // after drawing the level, correct some elements
4350     if (game.timegate_time_left == 0)
4351       CloseAllOpenTimegates();
4352   }
4353
4354   // blit playfield from scroll buffer to normal back buffer for fading in
4355   BlitScreenToBitmap(backbuffer);
4356   // !!! FIX THIS (END) !!!
4357
4358   DrawMaskedBorder(fade_mask);
4359
4360   FadeIn(fade_mask);
4361
4362 #if 1
4363   // full screen redraw is required at this point in the following cases:
4364   // - special editor door undrawn when game was started from level editor
4365   // - drawing area (playfield) was changed and has to be removed completely
4366   redraw_mask = REDRAW_ALL;
4367   BackToFront();
4368 #endif
4369
4370   if (!game.restart_level)
4371   {
4372     // copy default game door content to main double buffer
4373
4374     // !!! CHECK AGAIN !!!
4375     SetPanelBackground();
4376     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4377     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4378   }
4379
4380   SetPanelBackground();
4381   SetDrawBackgroundMask(REDRAW_DOOR_1);
4382
4383   UpdateAndDisplayGameControlValues();
4384
4385   if (!game.restart_level)
4386   {
4387     UnmapGameButtons();
4388     UnmapTapeButtons();
4389
4390     FreeGameButtons();
4391     CreateGameButtons();
4392
4393     MapGameButtons();
4394     MapTapeButtons();
4395
4396     // copy actual game door content to door double buffer for OpenDoor()
4397     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4398
4399     OpenDoor(DOOR_OPEN_ALL);
4400
4401     KeyboardAutoRepeatOffUnlessAutoplay();
4402
4403 #if DEBUG_INIT_PLAYER
4404     DebugPrintPlayerStatus("Player status (final)");
4405 #endif
4406   }
4407
4408   UnmapAllGadgets();
4409
4410   MapGameButtons();
4411   MapTapeButtons();
4412
4413   if (!game.restart_level && !tape.playing)
4414   {
4415     LevelStats_incPlayed(level_nr);
4416
4417     SaveLevelSetup_SeriesInfo();
4418   }
4419
4420   game.restart_level = FALSE;
4421   game.restart_game_message = NULL;
4422   game.request_active = FALSE;
4423
4424   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4425     InitGameActions_MM();
4426
4427   SaveEngineSnapshotToListInitial();
4428
4429   if (!game.restart_level)
4430   {
4431     PlaySound(SND_GAME_STARTING);
4432
4433     if (setup.sound_music)
4434       PlayLevelMusic();
4435   }
4436 }
4437
4438 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4439                         int actual_player_x, int actual_player_y)
4440 {
4441   // this is used for non-R'n'D game engines to update certain engine values
4442
4443   // needed to determine if sounds are played within the visible screen area
4444   scroll_x = actual_scroll_x;
4445   scroll_y = actual_scroll_y;
4446
4447   // needed to get player position for "follow finger" playing input method
4448   local_player->jx = actual_player_x;
4449   local_player->jy = actual_player_y;
4450 }
4451
4452 void InitMovDir(int x, int y)
4453 {
4454   int i, element = Tile[x][y];
4455   static int xy[4][2] =
4456   {
4457     {  0, +1 },
4458     { +1,  0 },
4459     {  0, -1 },
4460     { -1,  0 }
4461   };
4462   static int direction[3][4] =
4463   {
4464     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4465     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4466     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4467   };
4468
4469   switch (element)
4470   {
4471     case EL_BUG_RIGHT:
4472     case EL_BUG_UP:
4473     case EL_BUG_LEFT:
4474     case EL_BUG_DOWN:
4475       Tile[x][y] = EL_BUG;
4476       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4477       break;
4478
4479     case EL_SPACESHIP_RIGHT:
4480     case EL_SPACESHIP_UP:
4481     case EL_SPACESHIP_LEFT:
4482     case EL_SPACESHIP_DOWN:
4483       Tile[x][y] = EL_SPACESHIP;
4484       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4485       break;
4486
4487     case EL_BD_BUTTERFLY_RIGHT:
4488     case EL_BD_BUTTERFLY_UP:
4489     case EL_BD_BUTTERFLY_LEFT:
4490     case EL_BD_BUTTERFLY_DOWN:
4491       Tile[x][y] = EL_BD_BUTTERFLY;
4492       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4493       break;
4494
4495     case EL_BD_FIREFLY_RIGHT:
4496     case EL_BD_FIREFLY_UP:
4497     case EL_BD_FIREFLY_LEFT:
4498     case EL_BD_FIREFLY_DOWN:
4499       Tile[x][y] = EL_BD_FIREFLY;
4500       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4501       break;
4502
4503     case EL_PACMAN_RIGHT:
4504     case EL_PACMAN_UP:
4505     case EL_PACMAN_LEFT:
4506     case EL_PACMAN_DOWN:
4507       Tile[x][y] = EL_PACMAN;
4508       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4509       break;
4510
4511     case EL_YAMYAM_LEFT:
4512     case EL_YAMYAM_RIGHT:
4513     case EL_YAMYAM_UP:
4514     case EL_YAMYAM_DOWN:
4515       Tile[x][y] = EL_YAMYAM;
4516       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4517       break;
4518
4519     case EL_SP_SNIKSNAK:
4520       MovDir[x][y] = MV_UP;
4521       break;
4522
4523     case EL_SP_ELECTRON:
4524       MovDir[x][y] = MV_LEFT;
4525       break;
4526
4527     case EL_MOLE_LEFT:
4528     case EL_MOLE_RIGHT:
4529     case EL_MOLE_UP:
4530     case EL_MOLE_DOWN:
4531       Tile[x][y] = EL_MOLE;
4532       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4533       break;
4534
4535     case EL_SPRING_LEFT:
4536     case EL_SPRING_RIGHT:
4537       Tile[x][y] = EL_SPRING;
4538       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4539       break;
4540
4541     default:
4542       if (IS_CUSTOM_ELEMENT(element))
4543       {
4544         struct ElementInfo *ei = &element_info[element];
4545         int move_direction_initial = ei->move_direction_initial;
4546         int move_pattern = ei->move_pattern;
4547
4548         if (move_direction_initial == MV_START_PREVIOUS)
4549         {
4550           if (MovDir[x][y] != MV_NONE)
4551             return;
4552
4553           move_direction_initial = MV_START_AUTOMATIC;
4554         }
4555
4556         if (move_direction_initial == MV_START_RANDOM)
4557           MovDir[x][y] = 1 << RND(4);
4558         else if (move_direction_initial & MV_ANY_DIRECTION)
4559           MovDir[x][y] = move_direction_initial;
4560         else if (move_pattern == MV_ALL_DIRECTIONS ||
4561                  move_pattern == MV_TURNING_LEFT ||
4562                  move_pattern == MV_TURNING_RIGHT ||
4563                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4564                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4565                  move_pattern == MV_TURNING_RANDOM)
4566           MovDir[x][y] = 1 << RND(4);
4567         else if (move_pattern == MV_HORIZONTAL)
4568           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4569         else if (move_pattern == MV_VERTICAL)
4570           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4571         else if (move_pattern & MV_ANY_DIRECTION)
4572           MovDir[x][y] = element_info[element].move_pattern;
4573         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4574                  move_pattern == MV_ALONG_RIGHT_SIDE)
4575         {
4576           // use random direction as default start direction
4577           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4578             MovDir[x][y] = 1 << RND(4);
4579
4580           for (i = 0; i < NUM_DIRECTIONS; i++)
4581           {
4582             int x1 = x + xy[i][0];
4583             int y1 = y + xy[i][1];
4584
4585             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4586             {
4587               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4588                 MovDir[x][y] = direction[0][i];
4589               else
4590                 MovDir[x][y] = direction[1][i];
4591
4592               break;
4593             }
4594           }
4595         }                
4596       }
4597       else
4598       {
4599         MovDir[x][y] = 1 << RND(4);
4600
4601         if (element != EL_BUG &&
4602             element != EL_SPACESHIP &&
4603             element != EL_BD_BUTTERFLY &&
4604             element != EL_BD_FIREFLY)
4605           break;
4606
4607         for (i = 0; i < NUM_DIRECTIONS; i++)
4608         {
4609           int x1 = x + xy[i][0];
4610           int y1 = y + xy[i][1];
4611
4612           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4613           {
4614             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4615             {
4616               MovDir[x][y] = direction[0][i];
4617               break;
4618             }
4619             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4620                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4621             {
4622               MovDir[x][y] = direction[1][i];
4623               break;
4624             }
4625           }
4626         }
4627       }
4628       break;
4629   }
4630
4631   GfxDir[x][y] = MovDir[x][y];
4632 }
4633
4634 void InitAmoebaNr(int x, int y)
4635 {
4636   int i;
4637   int group_nr = AmoebaNeighbourNr(x, y);
4638
4639   if (group_nr == 0)
4640   {
4641     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4642     {
4643       if (AmoebaCnt[i] == 0)
4644       {
4645         group_nr = i;
4646         break;
4647       }
4648     }
4649   }
4650
4651   AmoebaNr[x][y] = group_nr;
4652   AmoebaCnt[group_nr]++;
4653   AmoebaCnt2[group_nr]++;
4654 }
4655
4656 static void LevelSolved(void)
4657 {
4658   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4659       game.players_still_needed > 0)
4660     return;
4661
4662   game.LevelSolved = TRUE;
4663   game.GameOver = TRUE;
4664
4665   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4666                       game_em.lev->score :
4667                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4668                       game_mm.score :
4669                       game.score);
4670   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4671                        MM_HEALTH(game_mm.laser_overload_value) :
4672                        game.health);
4673
4674   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4675   game.LevelSolved_CountingScore = game.score_final;
4676   game.LevelSolved_CountingHealth = game.health_final;
4677 }
4678
4679 void GameWon(void)
4680 {
4681   static int time_count_steps;
4682   static int time, time_final;
4683   static int score, score_final;
4684   static int health, health_final;
4685   static int game_over_delay_1 = 0;
4686   static int game_over_delay_2 = 0;
4687   static int game_over_delay_3 = 0;
4688   int game_over_delay_value_1 = 50;
4689   int game_over_delay_value_2 = 25;
4690   int game_over_delay_value_3 = 50;
4691
4692   if (!game.LevelSolved_GameWon)
4693   {
4694     int i;
4695
4696     // do not start end game actions before the player stops moving (to exit)
4697     if (local_player->active && local_player->MovPos)
4698       return;
4699
4700     game.LevelSolved_GameWon = TRUE;
4701     game.LevelSolved_SaveTape = tape.recording;
4702     game.LevelSolved_SaveScore = !tape.playing;
4703
4704     if (!tape.playing)
4705     {
4706       LevelStats_incSolved(level_nr);
4707
4708       SaveLevelSetup_SeriesInfo();
4709     }
4710
4711     if (tape.auto_play)         // tape might already be stopped here
4712       tape.auto_play_level_solved = TRUE;
4713
4714     TapeStop();
4715
4716     game_over_delay_1 = 0;
4717     game_over_delay_2 = 0;
4718     game_over_delay_3 = game_over_delay_value_3;
4719
4720     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4721     score = score_final = game.score_final;
4722     health = health_final = game.health_final;
4723
4724     if (level.score[SC_TIME_BONUS] > 0)
4725     {
4726       if (TimeLeft > 0)
4727       {
4728         time_final = 0;
4729         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4730       }
4731       else if (game.no_time_limit && TimePlayed < 999)
4732       {
4733         time_final = 999;
4734         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4735       }
4736
4737       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4738
4739       game_over_delay_1 = game_over_delay_value_1;
4740
4741       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4742       {
4743         health_final = 0;
4744         score_final += health * level.score[SC_TIME_BONUS];
4745
4746         game_over_delay_2 = game_over_delay_value_2;
4747       }
4748
4749       game.score_final = score_final;
4750       game.health_final = health_final;
4751     }
4752
4753     if (level_editor_test_game)
4754     {
4755       time = time_final;
4756       score = score_final;
4757
4758       game.LevelSolved_CountingTime = time;
4759       game.LevelSolved_CountingScore = score;
4760
4761       game_panel_controls[GAME_PANEL_TIME].value = time;
4762       game_panel_controls[GAME_PANEL_SCORE].value = score;
4763
4764       DisplayGameControlValues();
4765     }
4766
4767     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4768     {
4769       // check if last player has left the level
4770       if (game.exit_x >= 0 &&
4771           game.exit_y >= 0)
4772       {
4773         int x = game.exit_x;
4774         int y = game.exit_y;
4775         int element = Tile[x][y];
4776
4777         // close exit door after last player
4778         if ((game.all_players_gone &&
4779              (element == EL_EXIT_OPEN ||
4780               element == EL_SP_EXIT_OPEN ||
4781               element == EL_STEEL_EXIT_OPEN)) ||
4782             element == EL_EM_EXIT_OPEN ||
4783             element == EL_EM_STEEL_EXIT_OPEN)
4784         {
4785
4786           Tile[x][y] =
4787             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4788              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4789              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4790              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4791              EL_EM_STEEL_EXIT_CLOSING);
4792
4793           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4794         }
4795
4796         // player disappears
4797         DrawLevelField(x, y);
4798       }
4799
4800       for (i = 0; i < MAX_PLAYERS; i++)
4801       {
4802         struct PlayerInfo *player = &stored_player[i];
4803
4804         if (player->present)
4805         {
4806           RemovePlayer(player);
4807
4808           // player disappears
4809           DrawLevelField(player->jx, player->jy);
4810         }
4811       }
4812     }
4813
4814     PlaySound(SND_GAME_WINNING);
4815   }
4816
4817   if (game_over_delay_1 > 0)
4818   {
4819     game_over_delay_1--;
4820
4821     return;
4822   }
4823
4824   if (time != time_final)
4825   {
4826     int time_to_go = ABS(time_final - time);
4827     int time_count_dir = (time < time_final ? +1 : -1);
4828
4829     if (time_to_go < time_count_steps)
4830       time_count_steps = 1;
4831
4832     time  += time_count_steps * time_count_dir;
4833     score += time_count_steps * level.score[SC_TIME_BONUS];
4834
4835     game.LevelSolved_CountingTime = time;
4836     game.LevelSolved_CountingScore = score;
4837
4838     game_panel_controls[GAME_PANEL_TIME].value = time;
4839     game_panel_controls[GAME_PANEL_SCORE].value = score;
4840
4841     DisplayGameControlValues();
4842
4843     if (time == time_final)
4844       StopSound(SND_GAME_LEVELTIME_BONUS);
4845     else if (setup.sound_loops)
4846       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4847     else
4848       PlaySound(SND_GAME_LEVELTIME_BONUS);
4849
4850     return;
4851   }
4852
4853   if (game_over_delay_2 > 0)
4854   {
4855     game_over_delay_2--;
4856
4857     return;
4858   }
4859
4860   if (health != health_final)
4861   {
4862     int health_count_dir = (health < health_final ? +1 : -1);
4863
4864     health += health_count_dir;
4865     score  += level.score[SC_TIME_BONUS];
4866
4867     game.LevelSolved_CountingHealth = health;
4868     game.LevelSolved_CountingScore = score;
4869
4870     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4871     game_panel_controls[GAME_PANEL_SCORE].value = score;
4872
4873     DisplayGameControlValues();
4874
4875     if (health == health_final)
4876       StopSound(SND_GAME_LEVELTIME_BONUS);
4877     else if (setup.sound_loops)
4878       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4879     else
4880       PlaySound(SND_GAME_LEVELTIME_BONUS);
4881
4882     return;
4883   }
4884
4885   game.panel.active = FALSE;
4886
4887   if (game_over_delay_3 > 0)
4888   {
4889     game_over_delay_3--;
4890
4891     return;
4892   }
4893
4894   GameEnd();
4895 }
4896
4897 void GameEnd(void)
4898 {
4899   // used instead of "level_nr" (needed for network games)
4900   int last_level_nr = levelset.level_nr;
4901   int hi_pos;
4902
4903   game.LevelSolved_GameEnd = TRUE;
4904
4905   if (game.LevelSolved_SaveTape)
4906   {
4907     // make sure that request dialog to save tape does not open door again
4908     if (!global.use_envelope_request)
4909       CloseDoor(DOOR_CLOSE_1);
4910
4911     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4912   }
4913
4914   // if no tape is to be saved, close both doors simultaneously
4915   CloseDoor(DOOR_CLOSE_ALL);
4916
4917   if (level_editor_test_game)
4918   {
4919     SetGameStatus(GAME_MODE_MAIN);
4920
4921     DrawMainMenu();
4922
4923     return;
4924   }
4925
4926   if (!game.LevelSolved_SaveScore)
4927   {
4928     SetGameStatus(GAME_MODE_MAIN);
4929
4930     DrawMainMenu();
4931
4932     return;
4933   }
4934
4935   if (level_nr == leveldir_current->handicap_level)
4936   {
4937     leveldir_current->handicap_level++;
4938
4939     SaveLevelSetup_SeriesInfo();
4940   }
4941
4942   if (setup.increment_levels &&
4943       level_nr < leveldir_current->last_level &&
4944       !network_playing)
4945   {
4946     level_nr++;         // advance to next level
4947     TapeErase();        // start with empty tape
4948
4949     if (setup.auto_play_next_level)
4950     {
4951       LoadLevel(level_nr);
4952
4953       SaveLevelSetup_SeriesInfo();
4954     }
4955   }
4956
4957   hi_pos = NewHiScore(last_level_nr);
4958
4959   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4960   {
4961     SetGameStatus(GAME_MODE_SCORES);
4962
4963     DrawHallOfFame(last_level_nr, hi_pos);
4964   }
4965   else if (setup.auto_play_next_level && setup.increment_levels &&
4966            last_level_nr < leveldir_current->last_level &&
4967            !network_playing)
4968   {
4969     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4970   }
4971   else
4972   {
4973     SetGameStatus(GAME_MODE_MAIN);
4974
4975     DrawMainMenu();
4976   }
4977 }
4978
4979 int NewHiScore(int level_nr)
4980 {
4981   int k, l;
4982   int position = -1;
4983   boolean one_score_entry_per_name = !program.many_scores_per_name;
4984
4985   LoadScore(level_nr);
4986
4987   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4988       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4989     return -1;
4990
4991   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4992   {
4993     if (game.score_final > highscore[k].Score)
4994     {
4995       // player has made it to the hall of fame
4996
4997       if (k < MAX_SCORE_ENTRIES - 1)
4998       {
4999         int m = MAX_SCORE_ENTRIES - 1;
5000
5001         if (one_score_entry_per_name)
5002         {
5003           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5004             if (strEqual(setup.player_name, highscore[l].Name))
5005               m = l;
5006
5007           if (m == k)   // player's new highscore overwrites his old one
5008             goto put_into_list;
5009         }
5010
5011         for (l = m; l > k; l--)
5012         {
5013           strcpy(highscore[l].Name, highscore[l - 1].Name);
5014           highscore[l].Score = highscore[l - 1].Score;
5015         }
5016       }
5017
5018       put_into_list:
5019
5020       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5021       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5022       highscore[k].Score = game.score_final;
5023       position = k;
5024
5025       break;
5026     }
5027     else if (one_score_entry_per_name &&
5028              !strncmp(setup.player_name, highscore[k].Name,
5029                       MAX_PLAYER_NAME_LEN))
5030       break;    // player already there with a higher score
5031   }
5032
5033   if (position >= 0) 
5034     SaveScore(level_nr);
5035
5036   return position;
5037 }
5038
5039 static int getElementMoveStepsizeExt(int x, int y, int direction)
5040 {
5041   int element = Tile[x][y];
5042   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5043   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5044   int horiz_move = (dx != 0);
5045   int sign = (horiz_move ? dx : dy);
5046   int step = sign * element_info[element].move_stepsize;
5047
5048   // special values for move stepsize for spring and things on conveyor belt
5049   if (horiz_move)
5050   {
5051     if (CAN_FALL(element) &&
5052         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5053       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5054     else if (element == EL_SPRING)
5055       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5056   }
5057
5058   return step;
5059 }
5060
5061 static int getElementMoveStepsize(int x, int y)
5062 {
5063   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5064 }
5065
5066 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5067 {
5068   if (player->GfxAction != action || player->GfxDir != dir)
5069   {
5070     player->GfxAction = action;
5071     player->GfxDir = dir;
5072     player->Frame = 0;
5073     player->StepFrame = 0;
5074   }
5075 }
5076
5077 static void ResetGfxFrame(int x, int y)
5078 {
5079   // profiling showed that "autotest" spends 10~20% of its time in this function
5080   if (DrawingDeactivatedField())
5081     return;
5082
5083   int element = Tile[x][y];
5084   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5085
5086   if (graphic_info[graphic].anim_global_sync)
5087     GfxFrame[x][y] = FrameCounter;
5088   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5089     GfxFrame[x][y] = CustomValue[x][y];
5090   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5091     GfxFrame[x][y] = element_info[element].collect_score;
5092   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5093     GfxFrame[x][y] = ChangeDelay[x][y];
5094 }
5095
5096 static void ResetGfxAnimation(int x, int y)
5097 {
5098   GfxAction[x][y] = ACTION_DEFAULT;
5099   GfxDir[x][y] = MovDir[x][y];
5100   GfxFrame[x][y] = 0;
5101
5102   ResetGfxFrame(x, y);
5103 }
5104
5105 static void ResetRandomAnimationValue(int x, int y)
5106 {
5107   GfxRandom[x][y] = INIT_GFX_RANDOM();
5108 }
5109
5110 static void InitMovingField(int x, int y, int direction)
5111 {
5112   int element = Tile[x][y];
5113   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5114   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5115   int newx = x + dx;
5116   int newy = y + dy;
5117   boolean is_moving_before, is_moving_after;
5118
5119   // check if element was/is moving or being moved before/after mode change
5120   is_moving_before = (WasJustMoving[x][y] != 0);
5121   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5122
5123   // reset animation only for moving elements which change direction of moving
5124   // or which just started or stopped moving
5125   // (else CEs with property "can move" / "not moving" are reset each frame)
5126   if (is_moving_before != is_moving_after ||
5127       direction != MovDir[x][y])
5128     ResetGfxAnimation(x, y);
5129
5130   MovDir[x][y] = direction;
5131   GfxDir[x][y] = direction;
5132
5133   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5134                      direction == MV_DOWN && CAN_FALL(element) ?
5135                      ACTION_FALLING : ACTION_MOVING);
5136
5137   // this is needed for CEs with property "can move" / "not moving"
5138
5139   if (is_moving_after)
5140   {
5141     if (Tile[newx][newy] == EL_EMPTY)
5142       Tile[newx][newy] = EL_BLOCKED;
5143
5144     MovDir[newx][newy] = MovDir[x][y];
5145
5146     CustomValue[newx][newy] = CustomValue[x][y];
5147
5148     GfxFrame[newx][newy] = GfxFrame[x][y];
5149     GfxRandom[newx][newy] = GfxRandom[x][y];
5150     GfxAction[newx][newy] = GfxAction[x][y];
5151     GfxDir[newx][newy] = GfxDir[x][y];
5152   }
5153 }
5154
5155 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5156 {
5157   int direction = MovDir[x][y];
5158   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5159   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5160
5161   *goes_to_x = newx;
5162   *goes_to_y = newy;
5163 }
5164
5165 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5166 {
5167   int oldx = x, oldy = y;
5168   int direction = MovDir[x][y];
5169
5170   if (direction == MV_LEFT)
5171     oldx++;
5172   else if (direction == MV_RIGHT)
5173     oldx--;
5174   else if (direction == MV_UP)
5175     oldy++;
5176   else if (direction == MV_DOWN)
5177     oldy--;
5178
5179   *comes_from_x = oldx;
5180   *comes_from_y = oldy;
5181 }
5182
5183 static int MovingOrBlocked2Element(int x, int y)
5184 {
5185   int element = Tile[x][y];
5186
5187   if (element == EL_BLOCKED)
5188   {
5189     int oldx, oldy;
5190
5191     Blocked2Moving(x, y, &oldx, &oldy);
5192     return Tile[oldx][oldy];
5193   }
5194   else
5195     return element;
5196 }
5197
5198 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5199 {
5200   // like MovingOrBlocked2Element(), but if element is moving
5201   // and (x,y) is the field the moving element is just leaving,
5202   // return EL_BLOCKED instead of the element value
5203   int element = Tile[x][y];
5204
5205   if (IS_MOVING(x, y))
5206   {
5207     if (element == EL_BLOCKED)
5208     {
5209       int oldx, oldy;
5210
5211       Blocked2Moving(x, y, &oldx, &oldy);
5212       return Tile[oldx][oldy];
5213     }
5214     else
5215       return EL_BLOCKED;
5216   }
5217   else
5218     return element;
5219 }
5220
5221 static void RemoveField(int x, int y)
5222 {
5223   Tile[x][y] = EL_EMPTY;
5224
5225   MovPos[x][y] = 0;
5226   MovDir[x][y] = 0;
5227   MovDelay[x][y] = 0;
5228
5229   CustomValue[x][y] = 0;
5230
5231   AmoebaNr[x][y] = 0;
5232   ChangeDelay[x][y] = 0;
5233   ChangePage[x][y] = -1;
5234   Pushed[x][y] = FALSE;
5235
5236   GfxElement[x][y] = EL_UNDEFINED;
5237   GfxAction[x][y] = ACTION_DEFAULT;
5238   GfxDir[x][y] = MV_NONE;
5239 }
5240
5241 static void RemoveMovingField(int x, int y)
5242 {
5243   int oldx = x, oldy = y, newx = x, newy = y;
5244   int element = Tile[x][y];
5245   int next_element = EL_UNDEFINED;
5246
5247   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5248     return;
5249
5250   if (IS_MOVING(x, y))
5251   {
5252     Moving2Blocked(x, y, &newx, &newy);
5253
5254     if (Tile[newx][newy] != EL_BLOCKED)
5255     {
5256       // element is moving, but target field is not free (blocked), but
5257       // already occupied by something different (example: acid pool);
5258       // in this case, only remove the moving field, but not the target
5259
5260       RemoveField(oldx, oldy);
5261
5262       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5263
5264       TEST_DrawLevelField(oldx, oldy);
5265
5266       return;
5267     }
5268   }
5269   else if (element == EL_BLOCKED)
5270   {
5271     Blocked2Moving(x, y, &oldx, &oldy);
5272     if (!IS_MOVING(oldx, oldy))
5273       return;
5274   }
5275
5276   if (element == EL_BLOCKED &&
5277       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5278        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5279        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5280        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5281        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5282        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5283     next_element = get_next_element(Tile[oldx][oldy]);
5284
5285   RemoveField(oldx, oldy);
5286   RemoveField(newx, newy);
5287
5288   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5289
5290   if (next_element != EL_UNDEFINED)
5291     Tile[oldx][oldy] = next_element;
5292
5293   TEST_DrawLevelField(oldx, oldy);
5294   TEST_DrawLevelField(newx, newy);
5295 }
5296
5297 void DrawDynamite(int x, int y)
5298 {
5299   int sx = SCREENX(x), sy = SCREENY(y);
5300   int graphic = el2img(Tile[x][y]);
5301   int frame;
5302
5303   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5304     return;
5305
5306   if (IS_WALKABLE_INSIDE(Back[x][y]))
5307     return;
5308
5309   if (Back[x][y])
5310     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5311   else if (Store[x][y])
5312     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5313
5314   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5315
5316   if (Back[x][y] || Store[x][y])
5317     DrawGraphicThruMask(sx, sy, graphic, frame);
5318   else
5319     DrawGraphic(sx, sy, graphic, frame);
5320 }
5321
5322 static void CheckDynamite(int x, int y)
5323 {
5324   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5325   {
5326     MovDelay[x][y]--;
5327
5328     if (MovDelay[x][y] != 0)
5329     {
5330       DrawDynamite(x, y);
5331       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5332
5333       return;
5334     }
5335   }
5336
5337   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5338
5339   Bang(x, y);
5340 }
5341
5342 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5343 {
5344   boolean num_checked_players = 0;
5345   int i;
5346
5347   for (i = 0; i < MAX_PLAYERS; i++)
5348   {
5349     if (stored_player[i].active)
5350     {
5351       int sx = stored_player[i].jx;
5352       int sy = stored_player[i].jy;
5353
5354       if (num_checked_players == 0)
5355       {
5356         *sx1 = *sx2 = sx;
5357         *sy1 = *sy2 = sy;
5358       }
5359       else
5360       {
5361         *sx1 = MIN(*sx1, sx);
5362         *sy1 = MIN(*sy1, sy);
5363         *sx2 = MAX(*sx2, sx);
5364         *sy2 = MAX(*sy2, sy);
5365       }
5366
5367       num_checked_players++;
5368     }
5369   }
5370 }
5371
5372 static boolean checkIfAllPlayersFitToScreen_RND(void)
5373 {
5374   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5375
5376   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5377
5378   return (sx2 - sx1 < SCR_FIELDX &&
5379           sy2 - sy1 < SCR_FIELDY);
5380 }
5381
5382 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5383 {
5384   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5385
5386   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5387
5388   *sx = (sx1 + sx2) / 2;
5389   *sy = (sy1 + sy2) / 2;
5390 }
5391
5392 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5393                                boolean center_screen, boolean quick_relocation)
5394 {
5395   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5396   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5397   boolean no_delay = (tape.warp_forward);
5398   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5399   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5400   int new_scroll_x, new_scroll_y;
5401
5402   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5403   {
5404     // case 1: quick relocation inside visible screen (without scrolling)
5405
5406     RedrawPlayfield();
5407
5408     return;
5409   }
5410
5411   if (!level.shifted_relocation || center_screen)
5412   {
5413     // relocation _with_ centering of screen
5414
5415     new_scroll_x = SCROLL_POSITION_X(x);
5416     new_scroll_y = SCROLL_POSITION_Y(y);
5417   }
5418   else
5419   {
5420     // relocation _without_ centering of screen
5421
5422     int center_scroll_x = SCROLL_POSITION_X(old_x);
5423     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5424     int offset_x = x + (scroll_x - center_scroll_x);
5425     int offset_y = y + (scroll_y - center_scroll_y);
5426
5427     // for new screen position, apply previous offset to center position
5428     new_scroll_x = SCROLL_POSITION_X(offset_x);
5429     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5430   }
5431
5432   if (quick_relocation)
5433   {
5434     // case 2: quick relocation (redraw without visible scrolling)
5435
5436     scroll_x = new_scroll_x;
5437     scroll_y = new_scroll_y;
5438
5439     RedrawPlayfield();
5440
5441     return;
5442   }
5443
5444   // case 3: visible relocation (with scrolling to new position)
5445
5446   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5447
5448   SetVideoFrameDelay(wait_delay_value);
5449
5450   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5451   {
5452     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5453     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5454
5455     if (dx == 0 && dy == 0)             // no scrolling needed at all
5456       break;
5457
5458     scroll_x -= dx;
5459     scroll_y -= dy;
5460
5461     // set values for horizontal/vertical screen scrolling (half tile size)
5462     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5463     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5464     int pos_x = dx * TILEX / 2;
5465     int pos_y = dy * TILEY / 2;
5466     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5467     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5468
5469     ScrollLevel(dx, dy);
5470     DrawAllPlayers();
5471
5472     // scroll in two steps of half tile size to make things smoother
5473     BlitScreenToBitmapExt_RND(window, fx, fy);
5474
5475     // scroll second step to align at full tile size
5476     BlitScreenToBitmap(window);
5477   }
5478
5479   DrawAllPlayers();
5480   BackToFront();
5481
5482   SetVideoFrameDelay(frame_delay_value_old);
5483 }
5484
5485 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5486 {
5487   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5488   int player_nr = GET_PLAYER_NR(el_player);
5489   struct PlayerInfo *player = &stored_player[player_nr];
5490   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5491   boolean no_delay = (tape.warp_forward);
5492   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5493   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5494   int old_jx = player->jx;
5495   int old_jy = player->jy;
5496   int old_element = Tile[old_jx][old_jy];
5497   int element = Tile[jx][jy];
5498   boolean player_relocated = (old_jx != jx || old_jy != jy);
5499
5500   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5501   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5502   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5503   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5504   int leave_side_horiz = move_dir_horiz;
5505   int leave_side_vert  = move_dir_vert;
5506   int enter_side = enter_side_horiz | enter_side_vert;
5507   int leave_side = leave_side_horiz | leave_side_vert;
5508
5509   if (player->buried)           // do not reanimate dead player
5510     return;
5511
5512   if (!player_relocated)        // no need to relocate the player
5513     return;
5514
5515   if (IS_PLAYER(jx, jy))        // player already placed at new position
5516   {
5517     RemoveField(jx, jy);        // temporarily remove newly placed player
5518     DrawLevelField(jx, jy);
5519   }
5520
5521   if (player->present)
5522   {
5523     while (player->MovPos)
5524     {
5525       ScrollPlayer(player, SCROLL_GO_ON);
5526       ScrollScreen(NULL, SCROLL_GO_ON);
5527
5528       AdvanceFrameAndPlayerCounters(player->index_nr);
5529
5530       DrawPlayer(player);
5531
5532       BackToFront_WithFrameDelay(wait_delay_value);
5533     }
5534
5535     DrawPlayer(player);         // needed here only to cleanup last field
5536     DrawLevelField(player->jx, player->jy);     // remove player graphic
5537
5538     player->is_moving = FALSE;
5539   }
5540
5541   if (IS_CUSTOM_ELEMENT(old_element))
5542     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5543                                CE_LEFT_BY_PLAYER,
5544                                player->index_bit, leave_side);
5545
5546   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5547                                       CE_PLAYER_LEAVES_X,
5548                                       player->index_bit, leave_side);
5549
5550   Tile[jx][jy] = el_player;
5551   InitPlayerField(jx, jy, el_player, TRUE);
5552
5553   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5554      possible that the relocation target field did not contain a player element,
5555      but a walkable element, to which the new player was relocated -- in this
5556      case, restore that (already initialized!) element on the player field */
5557   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5558   {
5559     Tile[jx][jy] = element;     // restore previously existing element
5560   }
5561
5562   // only visually relocate centered player
5563   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5564                      FALSE, level.instant_relocation);
5565
5566   TestIfPlayerTouchesBadThing(jx, jy);
5567   TestIfPlayerTouchesCustomElement(jx, jy);
5568
5569   if (IS_CUSTOM_ELEMENT(element))
5570     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5571                                player->index_bit, enter_side);
5572
5573   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5574                                       player->index_bit, enter_side);
5575
5576   if (player->is_switching)
5577   {
5578     /* ensure that relocation while still switching an element does not cause
5579        a new element to be treated as also switched directly after relocation
5580        (this is important for teleporter switches that teleport the player to
5581        a place where another teleporter switch is in the same direction, which
5582        would then incorrectly be treated as immediately switched before the
5583        direction key that caused the switch was released) */
5584
5585     player->switch_x += jx - old_jx;
5586     player->switch_y += jy - old_jy;
5587   }
5588 }
5589
5590 static void Explode(int ex, int ey, int phase, int mode)
5591 {
5592   int x, y;
5593   int last_phase;
5594   int border_element;
5595
5596   // !!! eliminate this variable !!!
5597   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5598
5599   if (game.explosions_delayed)
5600   {
5601     ExplodeField[ex][ey] = mode;
5602     return;
5603   }
5604
5605   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5606   {
5607     int center_element = Tile[ex][ey];
5608     int artwork_element, explosion_element;     // set these values later
5609
5610     // remove things displayed in background while burning dynamite
5611     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5612       Back[ex][ey] = 0;
5613
5614     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5615     {
5616       // put moving element to center field (and let it explode there)
5617       center_element = MovingOrBlocked2Element(ex, ey);
5618       RemoveMovingField(ex, ey);
5619       Tile[ex][ey] = center_element;
5620     }
5621
5622     // now "center_element" is finally determined -- set related values now
5623     artwork_element = center_element;           // for custom player artwork
5624     explosion_element = center_element;         // for custom player artwork
5625
5626     if (IS_PLAYER(ex, ey))
5627     {
5628       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5629
5630       artwork_element = stored_player[player_nr].artwork_element;
5631
5632       if (level.use_explosion_element[player_nr])
5633       {
5634         explosion_element = level.explosion_element[player_nr];
5635         artwork_element = explosion_element;
5636       }
5637     }
5638
5639     if (mode == EX_TYPE_NORMAL ||
5640         mode == EX_TYPE_CENTER ||
5641         mode == EX_TYPE_CROSS)
5642       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5643
5644     last_phase = element_info[explosion_element].explosion_delay + 1;
5645
5646     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5647     {
5648       int xx = x - ex + 1;
5649       int yy = y - ey + 1;
5650       int element;
5651
5652       if (!IN_LEV_FIELD(x, y) ||
5653           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5654           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5655         continue;
5656
5657       element = Tile[x][y];
5658
5659       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5660       {
5661         element = MovingOrBlocked2Element(x, y);
5662
5663         if (!IS_EXPLOSION_PROOF(element))
5664           RemoveMovingField(x, y);
5665       }
5666
5667       // indestructible elements can only explode in center (but not flames)
5668       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5669                                            mode == EX_TYPE_BORDER)) ||
5670           element == EL_FLAMES)
5671         continue;
5672
5673       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5674          behaviour, for example when touching a yamyam that explodes to rocks
5675          with active deadly shield, a rock is created under the player !!! */
5676       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5677 #if 0
5678       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5679           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5680            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5681 #else
5682       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5683 #endif
5684       {
5685         if (IS_ACTIVE_BOMB(element))
5686         {
5687           // re-activate things under the bomb like gate or penguin
5688           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5689           Back[x][y] = 0;
5690         }
5691
5692         continue;
5693       }
5694
5695       // save walkable background elements while explosion on same tile
5696       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5697           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5698         Back[x][y] = element;
5699
5700       // ignite explodable elements reached by other explosion
5701       if (element == EL_EXPLOSION)
5702         element = Store2[x][y];
5703
5704       if (AmoebaNr[x][y] &&
5705           (element == EL_AMOEBA_FULL ||
5706            element == EL_BD_AMOEBA ||
5707            element == EL_AMOEBA_GROWING))
5708       {
5709         AmoebaCnt[AmoebaNr[x][y]]--;
5710         AmoebaCnt2[AmoebaNr[x][y]]--;
5711       }
5712
5713       RemoveField(x, y);
5714
5715       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5716       {
5717         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5718
5719         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5720
5721         if (PLAYERINFO(ex, ey)->use_murphy)
5722           Store[x][y] = EL_EMPTY;
5723       }
5724
5725       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5726       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5727       else if (ELEM_IS_PLAYER(center_element))
5728         Store[x][y] = EL_EMPTY;
5729       else if (center_element == EL_YAMYAM)
5730         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5731       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5732         Store[x][y] = element_info[center_element].content.e[xx][yy];
5733 #if 1
5734       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5735       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5736       // otherwise) -- FIX THIS !!!
5737       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5738         Store[x][y] = element_info[element].content.e[1][1];
5739 #else
5740       else if (!CAN_EXPLODE(element))
5741         Store[x][y] = element_info[element].content.e[1][1];
5742 #endif
5743       else
5744         Store[x][y] = EL_EMPTY;
5745
5746       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5747           center_element == EL_AMOEBA_TO_DIAMOND)
5748         Store2[x][y] = element;
5749
5750       Tile[x][y] = EL_EXPLOSION;
5751       GfxElement[x][y] = artwork_element;
5752
5753       ExplodePhase[x][y] = 1;
5754       ExplodeDelay[x][y] = last_phase;
5755
5756       Stop[x][y] = TRUE;
5757     }
5758
5759     if (center_element == EL_YAMYAM)
5760       game.yamyam_content_nr =
5761         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5762
5763     return;
5764   }
5765
5766   if (Stop[ex][ey])
5767     return;
5768
5769   x = ex;
5770   y = ey;
5771
5772   if (phase == 1)
5773     GfxFrame[x][y] = 0;         // restart explosion animation
5774
5775   last_phase = ExplodeDelay[x][y];
5776
5777   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5778
5779   // this can happen if the player leaves an explosion just in time
5780   if (GfxElement[x][y] == EL_UNDEFINED)
5781     GfxElement[x][y] = EL_EMPTY;
5782
5783   border_element = Store2[x][y];
5784   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5785     border_element = StorePlayer[x][y];
5786
5787   if (phase == element_info[border_element].ignition_delay ||
5788       phase == last_phase)
5789   {
5790     boolean border_explosion = FALSE;
5791
5792     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5793         !PLAYER_EXPLOSION_PROTECTED(x, y))
5794     {
5795       KillPlayerUnlessExplosionProtected(x, y);
5796       border_explosion = TRUE;
5797     }
5798     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5799     {
5800       Tile[x][y] = Store2[x][y];
5801       Store2[x][y] = 0;
5802       Bang(x, y);
5803       border_explosion = TRUE;
5804     }
5805     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5806     {
5807       AmoebaToDiamond(x, y);
5808       Store2[x][y] = 0;
5809       border_explosion = TRUE;
5810     }
5811
5812     // if an element just explodes due to another explosion (chain-reaction),
5813     // do not immediately end the new explosion when it was the last frame of
5814     // the explosion (as it would be done in the following "if"-statement!)
5815     if (border_explosion && phase == last_phase)
5816       return;
5817   }
5818
5819   if (phase == last_phase)
5820   {
5821     int element;
5822
5823     element = Tile[x][y] = Store[x][y];
5824     Store[x][y] = Store2[x][y] = 0;
5825     GfxElement[x][y] = EL_UNDEFINED;
5826
5827     // player can escape from explosions and might therefore be still alive
5828     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5829         element <= EL_PLAYER_IS_EXPLODING_4)
5830     {
5831       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5832       int explosion_element = EL_PLAYER_1 + player_nr;
5833       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5834       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5835
5836       if (level.use_explosion_element[player_nr])
5837         explosion_element = level.explosion_element[player_nr];
5838
5839       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5840                     element_info[explosion_element].content.e[xx][yy]);
5841     }
5842
5843     // restore probably existing indestructible background element
5844     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5845       element = Tile[x][y] = Back[x][y];
5846     Back[x][y] = 0;
5847
5848     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5849     GfxDir[x][y] = MV_NONE;
5850     ChangeDelay[x][y] = 0;
5851     ChangePage[x][y] = -1;
5852
5853     CustomValue[x][y] = 0;
5854
5855     InitField_WithBug2(x, y, FALSE);
5856
5857     TEST_DrawLevelField(x, y);
5858
5859     TestIfElementTouchesCustomElement(x, y);
5860
5861     if (GFX_CRUMBLED(element))
5862       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5863
5864     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5865       StorePlayer[x][y] = 0;
5866
5867     if (ELEM_IS_PLAYER(element))
5868       RelocatePlayer(x, y, element);
5869   }
5870   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5871   {
5872     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5873     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5874
5875     if (phase == delay)
5876       TEST_DrawLevelFieldCrumbled(x, y);
5877
5878     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5879     {
5880       DrawLevelElement(x, y, Back[x][y]);
5881       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5882     }
5883     else if (IS_WALKABLE_UNDER(Back[x][y]))
5884     {
5885       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5886       DrawLevelElementThruMask(x, y, Back[x][y]);
5887     }
5888     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5889       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5890   }
5891 }
5892
5893 static void DynaExplode(int ex, int ey)
5894 {
5895   int i, j;
5896   int dynabomb_element = Tile[ex][ey];
5897   int dynabomb_size = 1;
5898   boolean dynabomb_xl = FALSE;
5899   struct PlayerInfo *player;
5900   static int xy[4][2] =
5901   {
5902     { 0, -1 },
5903     { -1, 0 },
5904     { +1, 0 },
5905     { 0, +1 }
5906   };
5907
5908   if (IS_ACTIVE_BOMB(dynabomb_element))
5909   {
5910     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5911     dynabomb_size = player->dynabomb_size;
5912     dynabomb_xl = player->dynabomb_xl;
5913     player->dynabombs_left++;
5914   }
5915
5916   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5917
5918   for (i = 0; i < NUM_DIRECTIONS; i++)
5919   {
5920     for (j = 1; j <= dynabomb_size; j++)
5921     {
5922       int x = ex + j * xy[i][0];
5923       int y = ey + j * xy[i][1];
5924       int element;
5925
5926       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5927         break;
5928
5929       element = Tile[x][y];
5930
5931       // do not restart explosions of fields with active bombs
5932       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5933         continue;
5934
5935       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5936
5937       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5938           !IS_DIGGABLE(element) && !dynabomb_xl)
5939         break;
5940     }
5941   }
5942 }
5943
5944 void Bang(int x, int y)
5945 {
5946   int element = MovingOrBlocked2Element(x, y);
5947   int explosion_type = EX_TYPE_NORMAL;
5948
5949   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5950   {
5951     struct PlayerInfo *player = PLAYERINFO(x, y);
5952
5953     element = Tile[x][y] = player->initial_element;
5954
5955     if (level.use_explosion_element[player->index_nr])
5956     {
5957       int explosion_element = level.explosion_element[player->index_nr];
5958
5959       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5960         explosion_type = EX_TYPE_CROSS;
5961       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5962         explosion_type = EX_TYPE_CENTER;
5963     }
5964   }
5965
5966   switch (element)
5967   {
5968     case EL_BUG:
5969     case EL_SPACESHIP:
5970     case EL_BD_BUTTERFLY:
5971     case EL_BD_FIREFLY:
5972     case EL_YAMYAM:
5973     case EL_DARK_YAMYAM:
5974     case EL_ROBOT:
5975     case EL_PACMAN:
5976     case EL_MOLE:
5977       RaiseScoreElement(element);
5978       break;
5979
5980     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5981     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5982     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5983     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5984     case EL_DYNABOMB_INCREASE_NUMBER:
5985     case EL_DYNABOMB_INCREASE_SIZE:
5986     case EL_DYNABOMB_INCREASE_POWER:
5987       explosion_type = EX_TYPE_DYNA;
5988       break;
5989
5990     case EL_DC_LANDMINE:
5991       explosion_type = EX_TYPE_CENTER;
5992       break;
5993
5994     case EL_PENGUIN:
5995     case EL_LAMP:
5996     case EL_LAMP_ACTIVE:
5997     case EL_AMOEBA_TO_DIAMOND:
5998       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5999         explosion_type = EX_TYPE_CENTER;
6000       break;
6001
6002     default:
6003       if (element_info[element].explosion_type == EXPLODES_CROSS)
6004         explosion_type = EX_TYPE_CROSS;
6005       else if (element_info[element].explosion_type == EXPLODES_1X1)
6006         explosion_type = EX_TYPE_CENTER;
6007       break;
6008   }
6009
6010   if (explosion_type == EX_TYPE_DYNA)
6011     DynaExplode(x, y);
6012   else
6013     Explode(x, y, EX_PHASE_START, explosion_type);
6014
6015   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6016 }
6017
6018 static void SplashAcid(int x, int y)
6019 {
6020   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6021       (!IN_LEV_FIELD(x - 1, y - 2) ||
6022        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6023     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6024
6025   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6026       (!IN_LEV_FIELD(x + 1, y - 2) ||
6027        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6028     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6029
6030   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6031 }
6032
6033 static void InitBeltMovement(void)
6034 {
6035   static int belt_base_element[4] =
6036   {
6037     EL_CONVEYOR_BELT_1_LEFT,
6038     EL_CONVEYOR_BELT_2_LEFT,
6039     EL_CONVEYOR_BELT_3_LEFT,
6040     EL_CONVEYOR_BELT_4_LEFT
6041   };
6042   static int belt_base_active_element[4] =
6043   {
6044     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6045     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6046     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6047     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6048   };
6049
6050   int x, y, i, j;
6051
6052   // set frame order for belt animation graphic according to belt direction
6053   for (i = 0; i < NUM_BELTS; i++)
6054   {
6055     int belt_nr = i;
6056
6057     for (j = 0; j < NUM_BELT_PARTS; j++)
6058     {
6059       int element = belt_base_active_element[belt_nr] + j;
6060       int graphic_1 = el2img(element);
6061       int graphic_2 = el2panelimg(element);
6062
6063       if (game.belt_dir[i] == MV_LEFT)
6064       {
6065         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6066         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6067       }
6068       else
6069       {
6070         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6071         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6072       }
6073     }
6074   }
6075
6076   SCAN_PLAYFIELD(x, y)
6077   {
6078     int element = Tile[x][y];
6079
6080     for (i = 0; i < NUM_BELTS; i++)
6081     {
6082       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6083       {
6084         int e_belt_nr = getBeltNrFromBeltElement(element);
6085         int belt_nr = i;
6086
6087         if (e_belt_nr == belt_nr)
6088         {
6089           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6090
6091           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6092         }
6093       }
6094     }
6095   }
6096 }
6097
6098 static void ToggleBeltSwitch(int x, int y)
6099 {
6100   static int belt_base_element[4] =
6101   {
6102     EL_CONVEYOR_BELT_1_LEFT,
6103     EL_CONVEYOR_BELT_2_LEFT,
6104     EL_CONVEYOR_BELT_3_LEFT,
6105     EL_CONVEYOR_BELT_4_LEFT
6106   };
6107   static int belt_base_active_element[4] =
6108   {
6109     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6110     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6111     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6112     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6113   };
6114   static int belt_base_switch_element[4] =
6115   {
6116     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6117     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6118     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6119     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6120   };
6121   static int belt_move_dir[4] =
6122   {
6123     MV_LEFT,
6124     MV_NONE,
6125     MV_RIGHT,
6126     MV_NONE,
6127   };
6128
6129   int element = Tile[x][y];
6130   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6131   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6132   int belt_dir = belt_move_dir[belt_dir_nr];
6133   int xx, yy, i;
6134
6135   if (!IS_BELT_SWITCH(element))
6136     return;
6137
6138   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6139   game.belt_dir[belt_nr] = belt_dir;
6140
6141   if (belt_dir_nr == 3)
6142     belt_dir_nr = 1;
6143
6144   // set frame order for belt animation graphic according to belt direction
6145   for (i = 0; i < NUM_BELT_PARTS; i++)
6146   {
6147     int element = belt_base_active_element[belt_nr] + i;
6148     int graphic_1 = el2img(element);
6149     int graphic_2 = el2panelimg(element);
6150
6151     if (belt_dir == MV_LEFT)
6152     {
6153       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6154       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6155     }
6156     else
6157     {
6158       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6159       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6160     }
6161   }
6162
6163   SCAN_PLAYFIELD(xx, yy)
6164   {
6165     int element = Tile[xx][yy];
6166
6167     if (IS_BELT_SWITCH(element))
6168     {
6169       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6170
6171       if (e_belt_nr == belt_nr)
6172       {
6173         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6174         TEST_DrawLevelField(xx, yy);
6175       }
6176     }
6177     else if (IS_BELT(element) && belt_dir != MV_NONE)
6178     {
6179       int e_belt_nr = getBeltNrFromBeltElement(element);
6180
6181       if (e_belt_nr == belt_nr)
6182       {
6183         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6184
6185         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6186         TEST_DrawLevelField(xx, yy);
6187       }
6188     }
6189     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6190     {
6191       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6192
6193       if (e_belt_nr == belt_nr)
6194       {
6195         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6196
6197         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6198         TEST_DrawLevelField(xx, yy);
6199       }
6200     }
6201   }
6202 }
6203
6204 static void ToggleSwitchgateSwitch(int x, int y)
6205 {
6206   int xx, yy;
6207
6208   game.switchgate_pos = !game.switchgate_pos;
6209
6210   SCAN_PLAYFIELD(xx, yy)
6211   {
6212     int element = Tile[xx][yy];
6213
6214     if (element == EL_SWITCHGATE_SWITCH_UP)
6215     {
6216       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6217       TEST_DrawLevelField(xx, yy);
6218     }
6219     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6220     {
6221       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6222       TEST_DrawLevelField(xx, yy);
6223     }
6224     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6225     {
6226       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6227       TEST_DrawLevelField(xx, yy);
6228     }
6229     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6230     {
6231       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6232       TEST_DrawLevelField(xx, yy);
6233     }
6234     else if (element == EL_SWITCHGATE_OPEN ||
6235              element == EL_SWITCHGATE_OPENING)
6236     {
6237       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6238
6239       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6240     }
6241     else if (element == EL_SWITCHGATE_CLOSED ||
6242              element == EL_SWITCHGATE_CLOSING)
6243     {
6244       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6245
6246       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6247     }
6248   }
6249 }
6250
6251 static int getInvisibleActiveFromInvisibleElement(int element)
6252 {
6253   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6254           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6255           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6256           element);
6257 }
6258
6259 static int getInvisibleFromInvisibleActiveElement(int element)
6260 {
6261   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6262           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6263           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6264           element);
6265 }
6266
6267 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6268 {
6269   int x, y;
6270
6271   SCAN_PLAYFIELD(x, y)
6272   {
6273     int element = Tile[x][y];
6274
6275     if (element == EL_LIGHT_SWITCH &&
6276         game.light_time_left > 0)
6277     {
6278       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6279       TEST_DrawLevelField(x, y);
6280     }
6281     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6282              game.light_time_left == 0)
6283     {
6284       Tile[x][y] = EL_LIGHT_SWITCH;
6285       TEST_DrawLevelField(x, y);
6286     }
6287     else if (element == EL_EMC_DRIPPER &&
6288              game.light_time_left > 0)
6289     {
6290       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6291       TEST_DrawLevelField(x, y);
6292     }
6293     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6294              game.light_time_left == 0)
6295     {
6296       Tile[x][y] = EL_EMC_DRIPPER;
6297       TEST_DrawLevelField(x, y);
6298     }
6299     else if (element == EL_INVISIBLE_STEELWALL ||
6300              element == EL_INVISIBLE_WALL ||
6301              element == EL_INVISIBLE_SAND)
6302     {
6303       if (game.light_time_left > 0)
6304         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6305
6306       TEST_DrawLevelField(x, y);
6307
6308       // uncrumble neighbour fields, if needed
6309       if (element == EL_INVISIBLE_SAND)
6310         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6311     }
6312     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6313              element == EL_INVISIBLE_WALL_ACTIVE ||
6314              element == EL_INVISIBLE_SAND_ACTIVE)
6315     {
6316       if (game.light_time_left == 0)
6317         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6318
6319       TEST_DrawLevelField(x, y);
6320
6321       // re-crumble neighbour fields, if needed
6322       if (element == EL_INVISIBLE_SAND)
6323         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6324     }
6325   }
6326 }
6327
6328 static void RedrawAllInvisibleElementsForLenses(void)
6329 {
6330   int x, y;
6331
6332   SCAN_PLAYFIELD(x, y)
6333   {
6334     int element = Tile[x][y];
6335
6336     if (element == EL_EMC_DRIPPER &&
6337         game.lenses_time_left > 0)
6338     {
6339       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6340       TEST_DrawLevelField(x, y);
6341     }
6342     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6343              game.lenses_time_left == 0)
6344     {
6345       Tile[x][y] = EL_EMC_DRIPPER;
6346       TEST_DrawLevelField(x, y);
6347     }
6348     else if (element == EL_INVISIBLE_STEELWALL ||
6349              element == EL_INVISIBLE_WALL ||
6350              element == EL_INVISIBLE_SAND)
6351     {
6352       if (game.lenses_time_left > 0)
6353         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6354
6355       TEST_DrawLevelField(x, y);
6356
6357       // uncrumble neighbour fields, if needed
6358       if (element == EL_INVISIBLE_SAND)
6359         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6360     }
6361     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6362              element == EL_INVISIBLE_WALL_ACTIVE ||
6363              element == EL_INVISIBLE_SAND_ACTIVE)
6364     {
6365       if (game.lenses_time_left == 0)
6366         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6367
6368       TEST_DrawLevelField(x, y);
6369
6370       // re-crumble neighbour fields, if needed
6371       if (element == EL_INVISIBLE_SAND)
6372         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6373     }
6374   }
6375 }
6376
6377 static void RedrawAllInvisibleElementsForMagnifier(void)
6378 {
6379   int x, y;
6380
6381   SCAN_PLAYFIELD(x, y)
6382   {
6383     int element = Tile[x][y];
6384
6385     if (element == EL_EMC_FAKE_GRASS &&
6386         game.magnify_time_left > 0)
6387     {
6388       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6389       TEST_DrawLevelField(x, y);
6390     }
6391     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6392              game.magnify_time_left == 0)
6393     {
6394       Tile[x][y] = EL_EMC_FAKE_GRASS;
6395       TEST_DrawLevelField(x, y);
6396     }
6397     else if (IS_GATE_GRAY(element) &&
6398              game.magnify_time_left > 0)
6399     {
6400       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6401                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6402                     IS_EM_GATE_GRAY(element) ?
6403                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6404                     IS_EMC_GATE_GRAY(element) ?
6405                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6406                     IS_DC_GATE_GRAY(element) ?
6407                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6408                     element);
6409       TEST_DrawLevelField(x, y);
6410     }
6411     else if (IS_GATE_GRAY_ACTIVE(element) &&
6412              game.magnify_time_left == 0)
6413     {
6414       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6415                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6416                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6417                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6418                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6419                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6420                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6421                     EL_DC_GATE_WHITE_GRAY :
6422                     element);
6423       TEST_DrawLevelField(x, y);
6424     }
6425   }
6426 }
6427
6428 static void ToggleLightSwitch(int x, int y)
6429 {
6430   int element = Tile[x][y];
6431
6432   game.light_time_left =
6433     (element == EL_LIGHT_SWITCH ?
6434      level.time_light * FRAMES_PER_SECOND : 0);
6435
6436   RedrawAllLightSwitchesAndInvisibleElements();
6437 }
6438
6439 static void ActivateTimegateSwitch(int x, int y)
6440 {
6441   int xx, yy;
6442
6443   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6444
6445   SCAN_PLAYFIELD(xx, yy)
6446   {
6447     int element = Tile[xx][yy];
6448
6449     if (element == EL_TIMEGATE_CLOSED ||
6450         element == EL_TIMEGATE_CLOSING)
6451     {
6452       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6453       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6454     }
6455
6456     /*
6457     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6458     {
6459       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6460       TEST_DrawLevelField(xx, yy);
6461     }
6462     */
6463
6464   }
6465
6466   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6467                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6468 }
6469
6470 static void Impact(int x, int y)
6471 {
6472   boolean last_line = (y == lev_fieldy - 1);
6473   boolean object_hit = FALSE;
6474   boolean impact = (last_line || object_hit);
6475   int element = Tile[x][y];
6476   int smashed = EL_STEELWALL;
6477
6478   if (!last_line)       // check if element below was hit
6479   {
6480     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6481       return;
6482
6483     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6484                                          MovDir[x][y + 1] != MV_DOWN ||
6485                                          MovPos[x][y + 1] <= TILEY / 2));
6486
6487     // do not smash moving elements that left the smashed field in time
6488     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6489         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6490       object_hit = FALSE;
6491
6492 #if USE_QUICKSAND_IMPACT_BUGFIX
6493     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6494     {
6495       RemoveMovingField(x, y + 1);
6496       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6497       Tile[x][y + 2] = EL_ROCK;
6498       TEST_DrawLevelField(x, y + 2);
6499
6500       object_hit = TRUE;
6501     }
6502
6503     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6504     {
6505       RemoveMovingField(x, y + 1);
6506       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6507       Tile[x][y + 2] = EL_ROCK;
6508       TEST_DrawLevelField(x, y + 2);
6509
6510       object_hit = TRUE;
6511     }
6512 #endif
6513
6514     if (object_hit)
6515       smashed = MovingOrBlocked2Element(x, y + 1);
6516
6517     impact = (last_line || object_hit);
6518   }
6519
6520   if (!last_line && smashed == EL_ACID) // element falls into acid
6521   {
6522     SplashAcid(x, y + 1);
6523     return;
6524   }
6525
6526   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6527   // only reset graphic animation if graphic really changes after impact
6528   if (impact &&
6529       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6530   {
6531     ResetGfxAnimation(x, y);
6532     TEST_DrawLevelField(x, y);
6533   }
6534
6535   if (impact && CAN_EXPLODE_IMPACT(element))
6536   {
6537     Bang(x, y);
6538     return;
6539   }
6540   else if (impact && element == EL_PEARL &&
6541            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6542   {
6543     ResetGfxAnimation(x, y);
6544
6545     Tile[x][y] = EL_PEARL_BREAKING;
6546     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6547     return;
6548   }
6549   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6550   {
6551     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6552
6553     return;
6554   }
6555
6556   if (impact && element == EL_AMOEBA_DROP)
6557   {
6558     if (object_hit && IS_PLAYER(x, y + 1))
6559       KillPlayerUnlessEnemyProtected(x, y + 1);
6560     else if (object_hit && smashed == EL_PENGUIN)
6561       Bang(x, y + 1);
6562     else
6563     {
6564       Tile[x][y] = EL_AMOEBA_GROWING;
6565       Store[x][y] = EL_AMOEBA_WET;
6566
6567       ResetRandomAnimationValue(x, y);
6568     }
6569     return;
6570   }
6571
6572   if (object_hit)               // check which object was hit
6573   {
6574     if ((CAN_PASS_MAGIC_WALL(element) && 
6575          (smashed == EL_MAGIC_WALL ||
6576           smashed == EL_BD_MAGIC_WALL)) ||
6577         (CAN_PASS_DC_MAGIC_WALL(element) &&
6578          smashed == EL_DC_MAGIC_WALL))
6579     {
6580       int xx, yy;
6581       int activated_magic_wall =
6582         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6583          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6584          EL_DC_MAGIC_WALL_ACTIVE);
6585
6586       // activate magic wall / mill
6587       SCAN_PLAYFIELD(xx, yy)
6588       {
6589         if (Tile[xx][yy] == smashed)
6590           Tile[xx][yy] = activated_magic_wall;
6591       }
6592
6593       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6594       game.magic_wall_active = TRUE;
6595
6596       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6597                             SND_MAGIC_WALL_ACTIVATING :
6598                             smashed == EL_BD_MAGIC_WALL ?
6599                             SND_BD_MAGIC_WALL_ACTIVATING :
6600                             SND_DC_MAGIC_WALL_ACTIVATING));
6601     }
6602
6603     if (IS_PLAYER(x, y + 1))
6604     {
6605       if (CAN_SMASH_PLAYER(element))
6606       {
6607         KillPlayerUnlessEnemyProtected(x, y + 1);
6608         return;
6609       }
6610     }
6611     else if (smashed == EL_PENGUIN)
6612     {
6613       if (CAN_SMASH_PLAYER(element))
6614       {
6615         Bang(x, y + 1);
6616         return;
6617       }
6618     }
6619     else if (element == EL_BD_DIAMOND)
6620     {
6621       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6622       {
6623         Bang(x, y + 1);
6624         return;
6625       }
6626     }
6627     else if (((element == EL_SP_INFOTRON ||
6628                element == EL_SP_ZONK) &&
6629               (smashed == EL_SP_SNIKSNAK ||
6630                smashed == EL_SP_ELECTRON ||
6631                smashed == EL_SP_DISK_ORANGE)) ||
6632              (element == EL_SP_INFOTRON &&
6633               smashed == EL_SP_DISK_YELLOW))
6634     {
6635       Bang(x, y + 1);
6636       return;
6637     }
6638     else if (CAN_SMASH_EVERYTHING(element))
6639     {
6640       if (IS_CLASSIC_ENEMY(smashed) ||
6641           CAN_EXPLODE_SMASHED(smashed))
6642       {
6643         Bang(x, y + 1);
6644         return;
6645       }
6646       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6647       {
6648         if (smashed == EL_LAMP ||
6649             smashed == EL_LAMP_ACTIVE)
6650         {
6651           Bang(x, y + 1);
6652           return;
6653         }
6654         else if (smashed == EL_NUT)
6655         {
6656           Tile[x][y + 1] = EL_NUT_BREAKING;
6657           PlayLevelSound(x, y, SND_NUT_BREAKING);
6658           RaiseScoreElement(EL_NUT);
6659           return;
6660         }
6661         else if (smashed == EL_PEARL)
6662         {
6663           ResetGfxAnimation(x, y);
6664
6665           Tile[x][y + 1] = EL_PEARL_BREAKING;
6666           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6667           return;
6668         }
6669         else if (smashed == EL_DIAMOND)
6670         {
6671           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6672           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6673           return;
6674         }
6675         else if (IS_BELT_SWITCH(smashed))
6676         {
6677           ToggleBeltSwitch(x, y + 1);
6678         }
6679         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6680                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6681                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6682                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6683         {
6684           ToggleSwitchgateSwitch(x, y + 1);
6685         }
6686         else if (smashed == EL_LIGHT_SWITCH ||
6687                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6688         {
6689           ToggleLightSwitch(x, y + 1);
6690         }
6691         else
6692         {
6693           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6694
6695           CheckElementChangeBySide(x, y + 1, smashed, element,
6696                                    CE_SWITCHED, CH_SIDE_TOP);
6697           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6698                                             CH_SIDE_TOP);
6699         }
6700       }
6701       else
6702       {
6703         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6704       }
6705     }
6706   }
6707
6708   // play sound of magic wall / mill
6709   if (!last_line &&
6710       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6711        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6712        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6713   {
6714     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6715       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6716     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6717       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6718     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6719       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6720
6721     return;
6722   }
6723
6724   // play sound of object that hits the ground
6725   if (last_line || object_hit)
6726     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6727 }
6728
6729 static void TurnRoundExt(int x, int y)
6730 {
6731   static struct
6732   {
6733     int dx, dy;
6734   } move_xy[] =
6735   {
6736     {  0,  0 },
6737     { -1,  0 },
6738     { +1,  0 },
6739     {  0,  0 },
6740     {  0, -1 },
6741     {  0,  0 }, { 0, 0 }, { 0, 0 },
6742     {  0, +1 }
6743   };
6744   static struct
6745   {
6746     int left, right, back;
6747   } turn[] =
6748   {
6749     { 0,        0,              0        },
6750     { MV_DOWN,  MV_UP,          MV_RIGHT },
6751     { MV_UP,    MV_DOWN,        MV_LEFT  },
6752     { 0,        0,              0        },
6753     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6754     { 0,        0,              0        },
6755     { 0,        0,              0        },
6756     { 0,        0,              0        },
6757     { MV_RIGHT, MV_LEFT,        MV_UP    }
6758   };
6759
6760   int element = Tile[x][y];
6761   int move_pattern = element_info[element].move_pattern;
6762
6763   int old_move_dir = MovDir[x][y];
6764   int left_dir  = turn[old_move_dir].left;
6765   int right_dir = turn[old_move_dir].right;
6766   int back_dir  = turn[old_move_dir].back;
6767
6768   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6769   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6770   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6771   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6772
6773   int left_x  = x + left_dx,  left_y  = y + left_dy;
6774   int right_x = x + right_dx, right_y = y + right_dy;
6775   int move_x  = x + move_dx,  move_y  = y + move_dy;
6776
6777   int xx, yy;
6778
6779   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6780   {
6781     TestIfBadThingTouchesOtherBadThing(x, y);
6782
6783     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6784       MovDir[x][y] = right_dir;
6785     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6786       MovDir[x][y] = left_dir;
6787
6788     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6789       MovDelay[x][y] = 9;
6790     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6791       MovDelay[x][y] = 1;
6792   }
6793   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6794   {
6795     TestIfBadThingTouchesOtherBadThing(x, y);
6796
6797     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6798       MovDir[x][y] = left_dir;
6799     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6800       MovDir[x][y] = right_dir;
6801
6802     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6803       MovDelay[x][y] = 9;
6804     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6805       MovDelay[x][y] = 1;
6806   }
6807   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6808   {
6809     TestIfBadThingTouchesOtherBadThing(x, y);
6810
6811     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6812       MovDir[x][y] = left_dir;
6813     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6814       MovDir[x][y] = right_dir;
6815
6816     if (MovDir[x][y] != old_move_dir)
6817       MovDelay[x][y] = 9;
6818   }
6819   else if (element == EL_YAMYAM)
6820   {
6821     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6822     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6823
6824     if (can_turn_left && can_turn_right)
6825       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6826     else if (can_turn_left)
6827       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6828     else if (can_turn_right)
6829       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6830     else
6831       MovDir[x][y] = back_dir;
6832
6833     MovDelay[x][y] = 16 + 16 * RND(3);
6834   }
6835   else if (element == EL_DARK_YAMYAM)
6836   {
6837     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6838                                                          left_x, left_y);
6839     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6840                                                          right_x, right_y);
6841
6842     if (can_turn_left && can_turn_right)
6843       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6844     else if (can_turn_left)
6845       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6846     else if (can_turn_right)
6847       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6848     else
6849       MovDir[x][y] = back_dir;
6850
6851     MovDelay[x][y] = 16 + 16 * RND(3);
6852   }
6853   else if (element == EL_PACMAN)
6854   {
6855     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6856     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6857
6858     if (can_turn_left && can_turn_right)
6859       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6860     else if (can_turn_left)
6861       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6862     else if (can_turn_right)
6863       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6864     else
6865       MovDir[x][y] = back_dir;
6866
6867     MovDelay[x][y] = 6 + RND(40);
6868   }
6869   else if (element == EL_PIG)
6870   {
6871     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6872     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6873     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6874     boolean should_turn_left, should_turn_right, should_move_on;
6875     int rnd_value = 24;
6876     int rnd = RND(rnd_value);
6877
6878     should_turn_left = (can_turn_left &&
6879                         (!can_move_on ||
6880                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6881                                                    y + back_dy + left_dy)));
6882     should_turn_right = (can_turn_right &&
6883                          (!can_move_on ||
6884                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6885                                                     y + back_dy + right_dy)));
6886     should_move_on = (can_move_on &&
6887                       (!can_turn_left ||
6888                        !can_turn_right ||
6889                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6890                                                  y + move_dy + left_dy) ||
6891                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6892                                                  y + move_dy + right_dy)));
6893
6894     if (should_turn_left || should_turn_right || should_move_on)
6895     {
6896       if (should_turn_left && should_turn_right && should_move_on)
6897         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6898                         rnd < 2 * rnd_value / 3 ? right_dir :
6899                         old_move_dir);
6900       else if (should_turn_left && should_turn_right)
6901         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6902       else if (should_turn_left && should_move_on)
6903         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6904       else if (should_turn_right && should_move_on)
6905         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6906       else if (should_turn_left)
6907         MovDir[x][y] = left_dir;
6908       else if (should_turn_right)
6909         MovDir[x][y] = right_dir;
6910       else if (should_move_on)
6911         MovDir[x][y] = old_move_dir;
6912     }
6913     else if (can_move_on && rnd > rnd_value / 8)
6914       MovDir[x][y] = old_move_dir;
6915     else if (can_turn_left && can_turn_right)
6916       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6917     else if (can_turn_left && rnd > rnd_value / 8)
6918       MovDir[x][y] = left_dir;
6919     else if (can_turn_right && rnd > rnd_value/8)
6920       MovDir[x][y] = right_dir;
6921     else
6922       MovDir[x][y] = back_dir;
6923
6924     xx = x + move_xy[MovDir[x][y]].dx;
6925     yy = y + move_xy[MovDir[x][y]].dy;
6926
6927     if (!IN_LEV_FIELD(xx, yy) ||
6928         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6929       MovDir[x][y] = old_move_dir;
6930
6931     MovDelay[x][y] = 0;
6932   }
6933   else if (element == EL_DRAGON)
6934   {
6935     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6936     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6937     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6938     int rnd_value = 24;
6939     int rnd = RND(rnd_value);
6940
6941     if (can_move_on && rnd > rnd_value / 8)
6942       MovDir[x][y] = old_move_dir;
6943     else if (can_turn_left && can_turn_right)
6944       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6945     else if (can_turn_left && rnd > rnd_value / 8)
6946       MovDir[x][y] = left_dir;
6947     else if (can_turn_right && rnd > rnd_value / 8)
6948       MovDir[x][y] = right_dir;
6949     else
6950       MovDir[x][y] = back_dir;
6951
6952     xx = x + move_xy[MovDir[x][y]].dx;
6953     yy = y + move_xy[MovDir[x][y]].dy;
6954
6955     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6956       MovDir[x][y] = old_move_dir;
6957
6958     MovDelay[x][y] = 0;
6959   }
6960   else if (element == EL_MOLE)
6961   {
6962     boolean can_move_on =
6963       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6964                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6965                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6966     if (!can_move_on)
6967     {
6968       boolean can_turn_left =
6969         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6970                               IS_AMOEBOID(Tile[left_x][left_y])));
6971
6972       boolean can_turn_right =
6973         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6974                               IS_AMOEBOID(Tile[right_x][right_y])));
6975
6976       if (can_turn_left && can_turn_right)
6977         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6978       else if (can_turn_left)
6979         MovDir[x][y] = left_dir;
6980       else
6981         MovDir[x][y] = right_dir;
6982     }
6983
6984     if (MovDir[x][y] != old_move_dir)
6985       MovDelay[x][y] = 9;
6986   }
6987   else if (element == EL_BALLOON)
6988   {
6989     MovDir[x][y] = game.wind_direction;
6990     MovDelay[x][y] = 0;
6991   }
6992   else if (element == EL_SPRING)
6993   {
6994     if (MovDir[x][y] & MV_HORIZONTAL)
6995     {
6996       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6997           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6998       {
6999         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7000         ResetGfxAnimation(move_x, move_y);
7001         TEST_DrawLevelField(move_x, move_y);
7002
7003         MovDir[x][y] = back_dir;
7004       }
7005       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7006                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7007         MovDir[x][y] = MV_NONE;
7008     }
7009
7010     MovDelay[x][y] = 0;
7011   }
7012   else if (element == EL_ROBOT ||
7013            element == EL_SATELLITE ||
7014            element == EL_PENGUIN ||
7015            element == EL_EMC_ANDROID)
7016   {
7017     int attr_x = -1, attr_y = -1;
7018
7019     if (game.all_players_gone)
7020     {
7021       attr_x = game.exit_x;
7022       attr_y = game.exit_y;
7023     }
7024     else
7025     {
7026       int i;
7027
7028       for (i = 0; i < MAX_PLAYERS; i++)
7029       {
7030         struct PlayerInfo *player = &stored_player[i];
7031         int jx = player->jx, jy = player->jy;
7032
7033         if (!player->active)
7034           continue;
7035
7036         if (attr_x == -1 ||
7037             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7038         {
7039           attr_x = jx;
7040           attr_y = jy;
7041         }
7042       }
7043     }
7044
7045     if (element == EL_ROBOT &&
7046         game.robot_wheel_x >= 0 &&
7047         game.robot_wheel_y >= 0 &&
7048         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7049          game.engine_version < VERSION_IDENT(3,1,0,0)))
7050     {
7051       attr_x = game.robot_wheel_x;
7052       attr_y = game.robot_wheel_y;
7053     }
7054
7055     if (element == EL_PENGUIN)
7056     {
7057       int i;
7058       static int xy[4][2] =
7059       {
7060         { 0, -1 },
7061         { -1, 0 },
7062         { +1, 0 },
7063         { 0, +1 }
7064       };
7065
7066       for (i = 0; i < NUM_DIRECTIONS; i++)
7067       {
7068         int ex = x + xy[i][0];
7069         int ey = y + xy[i][1];
7070
7071         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7072                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7073                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7074                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7075         {
7076           attr_x = ex;
7077           attr_y = ey;
7078           break;
7079         }
7080       }
7081     }
7082
7083     MovDir[x][y] = MV_NONE;
7084     if (attr_x < x)
7085       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7086     else if (attr_x > x)
7087       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7088     if (attr_y < y)
7089       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7090     else if (attr_y > y)
7091       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7092
7093     if (element == EL_ROBOT)
7094     {
7095       int newx, newy;
7096
7097       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7098         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7099       Moving2Blocked(x, y, &newx, &newy);
7100
7101       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7102         MovDelay[x][y] = 8 + 8 * !RND(3);
7103       else
7104         MovDelay[x][y] = 16;
7105     }
7106     else if (element == EL_PENGUIN)
7107     {
7108       int newx, newy;
7109
7110       MovDelay[x][y] = 1;
7111
7112       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7113       {
7114         boolean first_horiz = RND(2);
7115         int new_move_dir = MovDir[x][y];
7116
7117         MovDir[x][y] =
7118           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7119         Moving2Blocked(x, y, &newx, &newy);
7120
7121         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7122           return;
7123
7124         MovDir[x][y] =
7125           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7126         Moving2Blocked(x, y, &newx, &newy);
7127
7128         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7129           return;
7130
7131         MovDir[x][y] = old_move_dir;
7132         return;
7133       }
7134     }
7135     else if (element == EL_SATELLITE)
7136     {
7137       int newx, newy;
7138
7139       MovDelay[x][y] = 1;
7140
7141       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7142       {
7143         boolean first_horiz = RND(2);
7144         int new_move_dir = MovDir[x][y];
7145
7146         MovDir[x][y] =
7147           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7148         Moving2Blocked(x, y, &newx, &newy);
7149
7150         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7151           return;
7152
7153         MovDir[x][y] =
7154           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7155         Moving2Blocked(x, y, &newx, &newy);
7156
7157         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7158           return;
7159
7160         MovDir[x][y] = old_move_dir;
7161         return;
7162       }
7163     }
7164     else if (element == EL_EMC_ANDROID)
7165     {
7166       static int check_pos[16] =
7167       {
7168         -1,             //  0 => (invalid)
7169         7,              //  1 => MV_LEFT
7170         3,              //  2 => MV_RIGHT
7171         -1,             //  3 => (invalid)
7172         1,              //  4 =>            MV_UP
7173         0,              //  5 => MV_LEFT  | MV_UP
7174         2,              //  6 => MV_RIGHT | MV_UP
7175         -1,             //  7 => (invalid)
7176         5,              //  8 =>            MV_DOWN
7177         6,              //  9 => MV_LEFT  | MV_DOWN
7178         4,              // 10 => MV_RIGHT | MV_DOWN
7179         -1,             // 11 => (invalid)
7180         -1,             // 12 => (invalid)
7181         -1,             // 13 => (invalid)
7182         -1,             // 14 => (invalid)
7183         -1,             // 15 => (invalid)
7184       };
7185       static struct
7186       {
7187         int dx, dy;
7188         int dir;
7189       } check_xy[8] =
7190       {
7191         { -1, -1,       MV_LEFT  | MV_UP   },
7192         {  0, -1,                  MV_UP   },
7193         { +1, -1,       MV_RIGHT | MV_UP   },
7194         { +1,  0,       MV_RIGHT           },
7195         { +1, +1,       MV_RIGHT | MV_DOWN },
7196         {  0, +1,                  MV_DOWN },
7197         { -1, +1,       MV_LEFT  | MV_DOWN },
7198         { -1,  0,       MV_LEFT            },
7199       };
7200       int start_pos, check_order;
7201       boolean can_clone = FALSE;
7202       int i;
7203
7204       // check if there is any free field around current position
7205       for (i = 0; i < 8; i++)
7206       {
7207         int newx = x + check_xy[i].dx;
7208         int newy = y + check_xy[i].dy;
7209
7210         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7211         {
7212           can_clone = TRUE;
7213
7214           break;
7215         }
7216       }
7217
7218       if (can_clone)            // randomly find an element to clone
7219       {
7220         can_clone = FALSE;
7221
7222         start_pos = check_pos[RND(8)];
7223         check_order = (RND(2) ? -1 : +1);
7224
7225         for (i = 0; i < 8; i++)
7226         {
7227           int pos_raw = start_pos + i * check_order;
7228           int pos = (pos_raw + 8) % 8;
7229           int newx = x + check_xy[pos].dx;
7230           int newy = y + check_xy[pos].dy;
7231
7232           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7233           {
7234             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7235             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7236
7237             Store[x][y] = Tile[newx][newy];
7238
7239             can_clone = TRUE;
7240
7241             break;
7242           }
7243         }
7244       }
7245
7246       if (can_clone)            // randomly find a direction to move
7247       {
7248         can_clone = FALSE;
7249
7250         start_pos = check_pos[RND(8)];
7251         check_order = (RND(2) ? -1 : +1);
7252
7253         for (i = 0; i < 8; i++)
7254         {
7255           int pos_raw = start_pos + i * check_order;
7256           int pos = (pos_raw + 8) % 8;
7257           int newx = x + check_xy[pos].dx;
7258           int newy = y + check_xy[pos].dy;
7259           int new_move_dir = check_xy[pos].dir;
7260
7261           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7262           {
7263             MovDir[x][y] = new_move_dir;
7264             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7265
7266             can_clone = TRUE;
7267
7268             break;
7269           }
7270         }
7271       }
7272
7273       if (can_clone)            // cloning and moving successful
7274         return;
7275
7276       // cannot clone -- try to move towards player
7277
7278       start_pos = check_pos[MovDir[x][y] & 0x0f];
7279       check_order = (RND(2) ? -1 : +1);
7280
7281       for (i = 0; i < 3; i++)
7282       {
7283         // first check start_pos, then previous/next or (next/previous) pos
7284         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7285         int pos = (pos_raw + 8) % 8;
7286         int newx = x + check_xy[pos].dx;
7287         int newy = y + check_xy[pos].dy;
7288         int new_move_dir = check_xy[pos].dir;
7289
7290         if (IS_PLAYER(newx, newy))
7291           break;
7292
7293         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7294         {
7295           MovDir[x][y] = new_move_dir;
7296           MovDelay[x][y] = level.android_move_time * 8 + 1;
7297
7298           break;
7299         }
7300       }
7301     }
7302   }
7303   else if (move_pattern == MV_TURNING_LEFT ||
7304            move_pattern == MV_TURNING_RIGHT ||
7305            move_pattern == MV_TURNING_LEFT_RIGHT ||
7306            move_pattern == MV_TURNING_RIGHT_LEFT ||
7307            move_pattern == MV_TURNING_RANDOM ||
7308            move_pattern == MV_ALL_DIRECTIONS)
7309   {
7310     boolean can_turn_left =
7311       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7312     boolean can_turn_right =
7313       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7314
7315     if (element_info[element].move_stepsize == 0)       // "not moving"
7316       return;
7317
7318     if (move_pattern == MV_TURNING_LEFT)
7319       MovDir[x][y] = left_dir;
7320     else if (move_pattern == MV_TURNING_RIGHT)
7321       MovDir[x][y] = right_dir;
7322     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7323       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7324     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7325       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7326     else if (move_pattern == MV_TURNING_RANDOM)
7327       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7328                       can_turn_right && !can_turn_left ? right_dir :
7329                       RND(2) ? left_dir : right_dir);
7330     else if (can_turn_left && can_turn_right)
7331       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7332     else if (can_turn_left)
7333       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7334     else if (can_turn_right)
7335       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7336     else
7337       MovDir[x][y] = back_dir;
7338
7339     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7340   }
7341   else if (move_pattern == MV_HORIZONTAL ||
7342            move_pattern == MV_VERTICAL)
7343   {
7344     if (move_pattern & old_move_dir)
7345       MovDir[x][y] = back_dir;
7346     else if (move_pattern == MV_HORIZONTAL)
7347       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7348     else if (move_pattern == MV_VERTICAL)
7349       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7350
7351     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7352   }
7353   else if (move_pattern & MV_ANY_DIRECTION)
7354   {
7355     MovDir[x][y] = move_pattern;
7356     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7357   }
7358   else if (move_pattern & MV_WIND_DIRECTION)
7359   {
7360     MovDir[x][y] = game.wind_direction;
7361     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7362   }
7363   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7364   {
7365     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7366       MovDir[x][y] = left_dir;
7367     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7368       MovDir[x][y] = right_dir;
7369
7370     if (MovDir[x][y] != old_move_dir)
7371       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7372   }
7373   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7374   {
7375     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7376       MovDir[x][y] = right_dir;
7377     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7378       MovDir[x][y] = left_dir;
7379
7380     if (MovDir[x][y] != old_move_dir)
7381       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7382   }
7383   else if (move_pattern == MV_TOWARDS_PLAYER ||
7384            move_pattern == MV_AWAY_FROM_PLAYER)
7385   {
7386     int attr_x = -1, attr_y = -1;
7387     int newx, newy;
7388     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7389
7390     if (game.all_players_gone)
7391     {
7392       attr_x = game.exit_x;
7393       attr_y = game.exit_y;
7394     }
7395     else
7396     {
7397       int i;
7398
7399       for (i = 0; i < MAX_PLAYERS; i++)
7400       {
7401         struct PlayerInfo *player = &stored_player[i];
7402         int jx = player->jx, jy = player->jy;
7403
7404         if (!player->active)
7405           continue;
7406
7407         if (attr_x == -1 ||
7408             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7409         {
7410           attr_x = jx;
7411           attr_y = jy;
7412         }
7413       }
7414     }
7415
7416     MovDir[x][y] = MV_NONE;
7417     if (attr_x < x)
7418       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7419     else if (attr_x > x)
7420       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7421     if (attr_y < y)
7422       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7423     else if (attr_y > y)
7424       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7425
7426     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7427
7428     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7429     {
7430       boolean first_horiz = RND(2);
7431       int new_move_dir = MovDir[x][y];
7432
7433       if (element_info[element].move_stepsize == 0)     // "not moving"
7434       {
7435         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7436         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7437
7438         return;
7439       }
7440
7441       MovDir[x][y] =
7442         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7443       Moving2Blocked(x, y, &newx, &newy);
7444
7445       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7446         return;
7447
7448       MovDir[x][y] =
7449         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7450       Moving2Blocked(x, y, &newx, &newy);
7451
7452       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7453         return;
7454
7455       MovDir[x][y] = old_move_dir;
7456     }
7457   }
7458   else if (move_pattern == MV_WHEN_PUSHED ||
7459            move_pattern == MV_WHEN_DROPPED)
7460   {
7461     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7462       MovDir[x][y] = MV_NONE;
7463
7464     MovDelay[x][y] = 0;
7465   }
7466   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7467   {
7468     static int test_xy[7][2] =
7469     {
7470       { 0, -1 },
7471       { -1, 0 },
7472       { +1, 0 },
7473       { 0, +1 },
7474       { 0, -1 },
7475       { -1, 0 },
7476       { +1, 0 },
7477     };
7478     static int test_dir[7] =
7479     {
7480       MV_UP,
7481       MV_LEFT,
7482       MV_RIGHT,
7483       MV_DOWN,
7484       MV_UP,
7485       MV_LEFT,
7486       MV_RIGHT,
7487     };
7488     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7489     int move_preference = -1000000;     // start with very low preference
7490     int new_move_dir = MV_NONE;
7491     int start_test = RND(4);
7492     int i;
7493
7494     for (i = 0; i < NUM_DIRECTIONS; i++)
7495     {
7496       int move_dir = test_dir[start_test + i];
7497       int move_dir_preference;
7498
7499       xx = x + test_xy[start_test + i][0];
7500       yy = y + test_xy[start_test + i][1];
7501
7502       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7503           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7504       {
7505         new_move_dir = move_dir;
7506
7507         break;
7508       }
7509
7510       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7511         continue;
7512
7513       move_dir_preference = -1 * RunnerVisit[xx][yy];
7514       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7515         move_dir_preference = PlayerVisit[xx][yy];
7516
7517       if (move_dir_preference > move_preference)
7518       {
7519         // prefer field that has not been visited for the longest time
7520         move_preference = move_dir_preference;
7521         new_move_dir = move_dir;
7522       }
7523       else if (move_dir_preference == move_preference &&
7524                move_dir == old_move_dir)
7525       {
7526         // prefer last direction when all directions are preferred equally
7527         move_preference = move_dir_preference;
7528         new_move_dir = move_dir;
7529       }
7530     }
7531
7532     MovDir[x][y] = new_move_dir;
7533     if (old_move_dir != new_move_dir)
7534       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7535   }
7536 }
7537
7538 static void TurnRound(int x, int y)
7539 {
7540   int direction = MovDir[x][y];
7541
7542   TurnRoundExt(x, y);
7543
7544   GfxDir[x][y] = MovDir[x][y];
7545
7546   if (direction != MovDir[x][y])
7547     GfxFrame[x][y] = 0;
7548
7549   if (MovDelay[x][y])
7550     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7551
7552   ResetGfxFrame(x, y);
7553 }
7554
7555 static boolean JustBeingPushed(int x, int y)
7556 {
7557   int i;
7558
7559   for (i = 0; i < MAX_PLAYERS; i++)
7560   {
7561     struct PlayerInfo *player = &stored_player[i];
7562
7563     if (player->active && player->is_pushing && player->MovPos)
7564     {
7565       int next_jx = player->jx + (player->jx - player->last_jx);
7566       int next_jy = player->jy + (player->jy - player->last_jy);
7567
7568       if (x == next_jx && y == next_jy)
7569         return TRUE;
7570     }
7571   }
7572
7573   return FALSE;
7574 }
7575
7576 static void StartMoving(int x, int y)
7577 {
7578   boolean started_moving = FALSE;       // some elements can fall _and_ move
7579   int element = Tile[x][y];
7580
7581   if (Stop[x][y])
7582     return;
7583
7584   if (MovDelay[x][y] == 0)
7585     GfxAction[x][y] = ACTION_DEFAULT;
7586
7587   if (CAN_FALL(element) && y < lev_fieldy - 1)
7588   {
7589     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7590         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7591       if (JustBeingPushed(x, y))
7592         return;
7593
7594     if (element == EL_QUICKSAND_FULL)
7595     {
7596       if (IS_FREE(x, y + 1))
7597       {
7598         InitMovingField(x, y, MV_DOWN);
7599         started_moving = TRUE;
7600
7601         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7602 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7603         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7604           Store[x][y] = EL_ROCK;
7605 #else
7606         Store[x][y] = EL_ROCK;
7607 #endif
7608
7609         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7610       }
7611       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7612       {
7613         if (!MovDelay[x][y])
7614         {
7615           MovDelay[x][y] = TILEY + 1;
7616
7617           ResetGfxAnimation(x, y);
7618           ResetGfxAnimation(x, y + 1);
7619         }
7620
7621         if (MovDelay[x][y])
7622         {
7623           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7624           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7625
7626           MovDelay[x][y]--;
7627           if (MovDelay[x][y])
7628             return;
7629         }
7630
7631         Tile[x][y] = EL_QUICKSAND_EMPTY;
7632         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7633         Store[x][y + 1] = Store[x][y];
7634         Store[x][y] = 0;
7635
7636         PlayLevelSoundAction(x, y, ACTION_FILLING);
7637       }
7638       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7639       {
7640         if (!MovDelay[x][y])
7641         {
7642           MovDelay[x][y] = TILEY + 1;
7643
7644           ResetGfxAnimation(x, y);
7645           ResetGfxAnimation(x, y + 1);
7646         }
7647
7648         if (MovDelay[x][y])
7649         {
7650           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7651           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7652
7653           MovDelay[x][y]--;
7654           if (MovDelay[x][y])
7655             return;
7656         }
7657
7658         Tile[x][y] = EL_QUICKSAND_EMPTY;
7659         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7660         Store[x][y + 1] = Store[x][y];
7661         Store[x][y] = 0;
7662
7663         PlayLevelSoundAction(x, y, ACTION_FILLING);
7664       }
7665     }
7666     else if (element == EL_QUICKSAND_FAST_FULL)
7667     {
7668       if (IS_FREE(x, y + 1))
7669       {
7670         InitMovingField(x, y, MV_DOWN);
7671         started_moving = TRUE;
7672
7673         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7674 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7675         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7676           Store[x][y] = EL_ROCK;
7677 #else
7678         Store[x][y] = EL_ROCK;
7679 #endif
7680
7681         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7682       }
7683       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7684       {
7685         if (!MovDelay[x][y])
7686         {
7687           MovDelay[x][y] = TILEY + 1;
7688
7689           ResetGfxAnimation(x, y);
7690           ResetGfxAnimation(x, y + 1);
7691         }
7692
7693         if (MovDelay[x][y])
7694         {
7695           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7696           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7697
7698           MovDelay[x][y]--;
7699           if (MovDelay[x][y])
7700             return;
7701         }
7702
7703         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7704         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7705         Store[x][y + 1] = Store[x][y];
7706         Store[x][y] = 0;
7707
7708         PlayLevelSoundAction(x, y, ACTION_FILLING);
7709       }
7710       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7711       {
7712         if (!MovDelay[x][y])
7713         {
7714           MovDelay[x][y] = TILEY + 1;
7715
7716           ResetGfxAnimation(x, y);
7717           ResetGfxAnimation(x, y + 1);
7718         }
7719
7720         if (MovDelay[x][y])
7721         {
7722           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7723           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7724
7725           MovDelay[x][y]--;
7726           if (MovDelay[x][y])
7727             return;
7728         }
7729
7730         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7731         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7732         Store[x][y + 1] = Store[x][y];
7733         Store[x][y] = 0;
7734
7735         PlayLevelSoundAction(x, y, ACTION_FILLING);
7736       }
7737     }
7738     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7739              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7740     {
7741       InitMovingField(x, y, MV_DOWN);
7742       started_moving = TRUE;
7743
7744       Tile[x][y] = EL_QUICKSAND_FILLING;
7745       Store[x][y] = element;
7746
7747       PlayLevelSoundAction(x, y, ACTION_FILLING);
7748     }
7749     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7750              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7751     {
7752       InitMovingField(x, y, MV_DOWN);
7753       started_moving = TRUE;
7754
7755       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7756       Store[x][y] = element;
7757
7758       PlayLevelSoundAction(x, y, ACTION_FILLING);
7759     }
7760     else if (element == EL_MAGIC_WALL_FULL)
7761     {
7762       if (IS_FREE(x, y + 1))
7763       {
7764         InitMovingField(x, y, MV_DOWN);
7765         started_moving = TRUE;
7766
7767         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7768         Store[x][y] = EL_CHANGED(Store[x][y]);
7769       }
7770       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7771       {
7772         if (!MovDelay[x][y])
7773           MovDelay[x][y] = TILEY / 4 + 1;
7774
7775         if (MovDelay[x][y])
7776         {
7777           MovDelay[x][y]--;
7778           if (MovDelay[x][y])
7779             return;
7780         }
7781
7782         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7783         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7784         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7785         Store[x][y] = 0;
7786       }
7787     }
7788     else if (element == EL_BD_MAGIC_WALL_FULL)
7789     {
7790       if (IS_FREE(x, y + 1))
7791       {
7792         InitMovingField(x, y, MV_DOWN);
7793         started_moving = TRUE;
7794
7795         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7796         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7797       }
7798       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7799       {
7800         if (!MovDelay[x][y])
7801           MovDelay[x][y] = TILEY / 4 + 1;
7802
7803         if (MovDelay[x][y])
7804         {
7805           MovDelay[x][y]--;
7806           if (MovDelay[x][y])
7807             return;
7808         }
7809
7810         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7811         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7812         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7813         Store[x][y] = 0;
7814       }
7815     }
7816     else if (element == EL_DC_MAGIC_WALL_FULL)
7817     {
7818       if (IS_FREE(x, y + 1))
7819       {
7820         InitMovingField(x, y, MV_DOWN);
7821         started_moving = TRUE;
7822
7823         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7824         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7825       }
7826       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7827       {
7828         if (!MovDelay[x][y])
7829           MovDelay[x][y] = TILEY / 4 + 1;
7830
7831         if (MovDelay[x][y])
7832         {
7833           MovDelay[x][y]--;
7834           if (MovDelay[x][y])
7835             return;
7836         }
7837
7838         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7839         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7840         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7841         Store[x][y] = 0;
7842       }
7843     }
7844     else if ((CAN_PASS_MAGIC_WALL(element) &&
7845               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7846                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7847              (CAN_PASS_DC_MAGIC_WALL(element) &&
7848               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7849
7850     {
7851       InitMovingField(x, y, MV_DOWN);
7852       started_moving = TRUE;
7853
7854       Tile[x][y] =
7855         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7856          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7857          EL_DC_MAGIC_WALL_FILLING);
7858       Store[x][y] = element;
7859     }
7860     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7861     {
7862       SplashAcid(x, y + 1);
7863
7864       InitMovingField(x, y, MV_DOWN);
7865       started_moving = TRUE;
7866
7867       Store[x][y] = EL_ACID;
7868     }
7869     else if (
7870              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7871               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7872              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7873               CAN_FALL(element) && WasJustFalling[x][y] &&
7874               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7875
7876              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7877               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7878               (Tile[x][y + 1] == EL_BLOCKED)))
7879     {
7880       /* this is needed for a special case not covered by calling "Impact()"
7881          from "ContinueMoving()": if an element moves to a tile directly below
7882          another element which was just falling on that tile (which was empty
7883          in the previous frame), the falling element above would just stop
7884          instead of smashing the element below (in previous version, the above
7885          element was just checked for "moving" instead of "falling", resulting
7886          in incorrect smashes caused by horizontal movement of the above
7887          element; also, the case of the player being the element to smash was
7888          simply not covered here... :-/ ) */
7889
7890       CheckCollision[x][y] = 0;
7891       CheckImpact[x][y] = 0;
7892
7893       Impact(x, y);
7894     }
7895     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7896     {
7897       if (MovDir[x][y] == MV_NONE)
7898       {
7899         InitMovingField(x, y, MV_DOWN);
7900         started_moving = TRUE;
7901       }
7902     }
7903     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7904     {
7905       if (WasJustFalling[x][y]) // prevent animation from being restarted
7906         MovDir[x][y] = MV_DOWN;
7907
7908       InitMovingField(x, y, MV_DOWN);
7909       started_moving = TRUE;
7910     }
7911     else if (element == EL_AMOEBA_DROP)
7912     {
7913       Tile[x][y] = EL_AMOEBA_GROWING;
7914       Store[x][y] = EL_AMOEBA_WET;
7915     }
7916     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7917               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7918              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7919              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7920     {
7921       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7922                                 (IS_FREE(x - 1, y + 1) ||
7923                                  Tile[x - 1][y + 1] == EL_ACID));
7924       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7925                                 (IS_FREE(x + 1, y + 1) ||
7926                                  Tile[x + 1][y + 1] == EL_ACID));
7927       boolean can_fall_any  = (can_fall_left || can_fall_right);
7928       boolean can_fall_both = (can_fall_left && can_fall_right);
7929       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7930
7931       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7932       {
7933         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7934           can_fall_right = FALSE;
7935         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7936           can_fall_left = FALSE;
7937         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7938           can_fall_right = FALSE;
7939         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7940           can_fall_left = FALSE;
7941
7942         can_fall_any  = (can_fall_left || can_fall_right);
7943         can_fall_both = FALSE;
7944       }
7945
7946       if (can_fall_both)
7947       {
7948         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7949           can_fall_right = FALSE;       // slip down on left side
7950         else
7951           can_fall_left = !(can_fall_right = RND(2));
7952
7953         can_fall_both = FALSE;
7954       }
7955
7956       if (can_fall_any)
7957       {
7958         // if not determined otherwise, prefer left side for slipping down
7959         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7960         started_moving = TRUE;
7961       }
7962     }
7963     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7964     {
7965       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7966       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7967       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7968       int belt_dir = game.belt_dir[belt_nr];
7969
7970       if ((belt_dir == MV_LEFT  && left_is_free) ||
7971           (belt_dir == MV_RIGHT && right_is_free))
7972       {
7973         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7974
7975         InitMovingField(x, y, belt_dir);
7976         started_moving = TRUE;
7977
7978         Pushed[x][y] = TRUE;
7979         Pushed[nextx][y] = TRUE;
7980
7981         GfxAction[x][y] = ACTION_DEFAULT;
7982       }
7983       else
7984       {
7985         MovDir[x][y] = 0;       // if element was moving, stop it
7986       }
7987     }
7988   }
7989
7990   // not "else if" because of elements that can fall and move (EL_SPRING)
7991   if (CAN_MOVE(element) && !started_moving)
7992   {
7993     int move_pattern = element_info[element].move_pattern;
7994     int newx, newy;
7995
7996     Moving2Blocked(x, y, &newx, &newy);
7997
7998     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7999       return;
8000
8001     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8002         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8003     {
8004       WasJustMoving[x][y] = 0;
8005       CheckCollision[x][y] = 0;
8006
8007       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8008
8009       if (Tile[x][y] != element)        // element has changed
8010         return;
8011     }
8012
8013     if (!MovDelay[x][y])        // start new movement phase
8014     {
8015       // all objects that can change their move direction after each step
8016       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8017
8018       if (element != EL_YAMYAM &&
8019           element != EL_DARK_YAMYAM &&
8020           element != EL_PACMAN &&
8021           !(move_pattern & MV_ANY_DIRECTION) &&
8022           move_pattern != MV_TURNING_LEFT &&
8023           move_pattern != MV_TURNING_RIGHT &&
8024           move_pattern != MV_TURNING_LEFT_RIGHT &&
8025           move_pattern != MV_TURNING_RIGHT_LEFT &&
8026           move_pattern != MV_TURNING_RANDOM)
8027       {
8028         TurnRound(x, y);
8029
8030         if (MovDelay[x][y] && (element == EL_BUG ||
8031                                element == EL_SPACESHIP ||
8032                                element == EL_SP_SNIKSNAK ||
8033                                element == EL_SP_ELECTRON ||
8034                                element == EL_MOLE))
8035           TEST_DrawLevelField(x, y);
8036       }
8037     }
8038
8039     if (MovDelay[x][y])         // wait some time before next movement
8040     {
8041       MovDelay[x][y]--;
8042
8043       if (element == EL_ROBOT ||
8044           element == EL_YAMYAM ||
8045           element == EL_DARK_YAMYAM)
8046       {
8047         DrawLevelElementAnimationIfNeeded(x, y, element);
8048         PlayLevelSoundAction(x, y, ACTION_WAITING);
8049       }
8050       else if (element == EL_SP_ELECTRON)
8051         DrawLevelElementAnimationIfNeeded(x, y, element);
8052       else if (element == EL_DRAGON)
8053       {
8054         int i;
8055         int dir = MovDir[x][y];
8056         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8057         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8058         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8059                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8060                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8061                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8062         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8063
8064         GfxAction[x][y] = ACTION_ATTACKING;
8065
8066         if (IS_PLAYER(x, y))
8067           DrawPlayerField(x, y);
8068         else
8069           TEST_DrawLevelField(x, y);
8070
8071         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8072
8073         for (i = 1; i <= 3; i++)
8074         {
8075           int xx = x + i * dx;
8076           int yy = y + i * dy;
8077           int sx = SCREENX(xx);
8078           int sy = SCREENY(yy);
8079           int flame_graphic = graphic + (i - 1);
8080
8081           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8082             break;
8083
8084           if (MovDelay[x][y])
8085           {
8086             int flamed = MovingOrBlocked2Element(xx, yy);
8087
8088             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8089               Bang(xx, yy);
8090             else
8091               RemoveMovingField(xx, yy);
8092
8093             ChangeDelay[xx][yy] = 0;
8094
8095             Tile[xx][yy] = EL_FLAMES;
8096
8097             if (IN_SCR_FIELD(sx, sy))
8098             {
8099               TEST_DrawLevelFieldCrumbled(xx, yy);
8100               DrawGraphic(sx, sy, flame_graphic, frame);
8101             }
8102           }
8103           else
8104           {
8105             if (Tile[xx][yy] == EL_FLAMES)
8106               Tile[xx][yy] = EL_EMPTY;
8107             TEST_DrawLevelField(xx, yy);
8108           }
8109         }
8110       }
8111
8112       if (MovDelay[x][y])       // element still has to wait some time
8113       {
8114         PlayLevelSoundAction(x, y, ACTION_WAITING);
8115
8116         return;
8117       }
8118     }
8119
8120     // now make next step
8121
8122     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8123
8124     if (DONT_COLLIDE_WITH(element) &&
8125         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8126         !PLAYER_ENEMY_PROTECTED(newx, newy))
8127     {
8128       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8129
8130       return;
8131     }
8132
8133     else if (CAN_MOVE_INTO_ACID(element) &&
8134              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8135              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8136              (MovDir[x][y] == MV_DOWN ||
8137               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8138     {
8139       SplashAcid(newx, newy);
8140       Store[x][y] = EL_ACID;
8141     }
8142     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8143     {
8144       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8145           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8146           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8147           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8148       {
8149         RemoveField(x, y);
8150         TEST_DrawLevelField(x, y);
8151
8152         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8153         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8154           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8155
8156         game.friends_still_needed--;
8157         if (!game.friends_still_needed &&
8158             !game.GameOver &&
8159             game.all_players_gone)
8160           LevelSolved();
8161
8162         return;
8163       }
8164       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8165       {
8166         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8167           TEST_DrawLevelField(newx, newy);
8168         else
8169           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8170       }
8171       else if (!IS_FREE(newx, newy))
8172       {
8173         GfxAction[x][y] = ACTION_WAITING;
8174
8175         if (IS_PLAYER(x, y))
8176           DrawPlayerField(x, y);
8177         else
8178           TEST_DrawLevelField(x, y);
8179
8180         return;
8181       }
8182     }
8183     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8184     {
8185       if (IS_FOOD_PIG(Tile[newx][newy]))
8186       {
8187         if (IS_MOVING(newx, newy))
8188           RemoveMovingField(newx, newy);
8189         else
8190         {
8191           Tile[newx][newy] = EL_EMPTY;
8192           TEST_DrawLevelField(newx, newy);
8193         }
8194
8195         PlayLevelSound(x, y, SND_PIG_DIGGING);
8196       }
8197       else if (!IS_FREE(newx, newy))
8198       {
8199         if (IS_PLAYER(x, y))
8200           DrawPlayerField(x, y);
8201         else
8202           TEST_DrawLevelField(x, y);
8203
8204         return;
8205       }
8206     }
8207     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8208     {
8209       if (Store[x][y] != EL_EMPTY)
8210       {
8211         boolean can_clone = FALSE;
8212         int xx, yy;
8213
8214         // check if element to clone is still there
8215         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8216         {
8217           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8218           {
8219             can_clone = TRUE;
8220
8221             break;
8222           }
8223         }
8224
8225         // cannot clone or target field not free anymore -- do not clone
8226         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8227           Store[x][y] = EL_EMPTY;
8228       }
8229
8230       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8231       {
8232         if (IS_MV_DIAGONAL(MovDir[x][y]))
8233         {
8234           int diagonal_move_dir = MovDir[x][y];
8235           int stored = Store[x][y];
8236           int change_delay = 8;
8237           int graphic;
8238
8239           // android is moving diagonally
8240
8241           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8242
8243           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8244           GfxElement[x][y] = EL_EMC_ANDROID;
8245           GfxAction[x][y] = ACTION_SHRINKING;
8246           GfxDir[x][y] = diagonal_move_dir;
8247           ChangeDelay[x][y] = change_delay;
8248
8249           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8250                                    GfxDir[x][y]);
8251
8252           DrawLevelGraphicAnimation(x, y, graphic);
8253           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8254
8255           if (Tile[newx][newy] == EL_ACID)
8256           {
8257             SplashAcid(newx, newy);
8258
8259             return;
8260           }
8261
8262           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8263
8264           Store[newx][newy] = EL_EMC_ANDROID;
8265           GfxElement[newx][newy] = EL_EMC_ANDROID;
8266           GfxAction[newx][newy] = ACTION_GROWING;
8267           GfxDir[newx][newy] = diagonal_move_dir;
8268           ChangeDelay[newx][newy] = change_delay;
8269
8270           graphic = el_act_dir2img(GfxElement[newx][newy],
8271                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8272
8273           DrawLevelGraphicAnimation(newx, newy, graphic);
8274           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8275
8276           return;
8277         }
8278         else
8279         {
8280           Tile[newx][newy] = EL_EMPTY;
8281           TEST_DrawLevelField(newx, newy);
8282
8283           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8284         }
8285       }
8286       else if (!IS_FREE(newx, newy))
8287       {
8288         return;
8289       }
8290     }
8291     else if (IS_CUSTOM_ELEMENT(element) &&
8292              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8293     {
8294       if (!DigFieldByCE(newx, newy, element))
8295         return;
8296
8297       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8298       {
8299         RunnerVisit[x][y] = FrameCounter;
8300         PlayerVisit[x][y] /= 8;         // expire player visit path
8301       }
8302     }
8303     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8304     {
8305       if (!IS_FREE(newx, newy))
8306       {
8307         if (IS_PLAYER(x, y))
8308           DrawPlayerField(x, y);
8309         else
8310           TEST_DrawLevelField(x, y);
8311
8312         return;
8313       }
8314       else
8315       {
8316         boolean wanna_flame = !RND(10);
8317         int dx = newx - x, dy = newy - y;
8318         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8319         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8320         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8321                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8322         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8323                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8324
8325         if ((wanna_flame ||
8326              IS_CLASSIC_ENEMY(element1) ||
8327              IS_CLASSIC_ENEMY(element2)) &&
8328             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8329             element1 != EL_FLAMES && element2 != EL_FLAMES)
8330         {
8331           ResetGfxAnimation(x, y);
8332           GfxAction[x][y] = ACTION_ATTACKING;
8333
8334           if (IS_PLAYER(x, y))
8335             DrawPlayerField(x, y);
8336           else
8337             TEST_DrawLevelField(x, y);
8338
8339           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8340
8341           MovDelay[x][y] = 50;
8342
8343           Tile[newx][newy] = EL_FLAMES;
8344           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8345             Tile[newx1][newy1] = EL_FLAMES;
8346           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8347             Tile[newx2][newy2] = EL_FLAMES;
8348
8349           return;
8350         }
8351       }
8352     }
8353     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8354              Tile[newx][newy] == EL_DIAMOND)
8355     {
8356       if (IS_MOVING(newx, newy))
8357         RemoveMovingField(newx, newy);
8358       else
8359       {
8360         Tile[newx][newy] = EL_EMPTY;
8361         TEST_DrawLevelField(newx, newy);
8362       }
8363
8364       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8365     }
8366     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8367              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8368     {
8369       if (AmoebaNr[newx][newy])
8370       {
8371         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8372         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8373             Tile[newx][newy] == EL_BD_AMOEBA)
8374           AmoebaCnt[AmoebaNr[newx][newy]]--;
8375       }
8376
8377       if (IS_MOVING(newx, newy))
8378       {
8379         RemoveMovingField(newx, newy);
8380       }
8381       else
8382       {
8383         Tile[newx][newy] = EL_EMPTY;
8384         TEST_DrawLevelField(newx, newy);
8385       }
8386
8387       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8388     }
8389     else if ((element == EL_PACMAN || element == EL_MOLE)
8390              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8391     {
8392       if (AmoebaNr[newx][newy])
8393       {
8394         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8395         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8396             Tile[newx][newy] == EL_BD_AMOEBA)
8397           AmoebaCnt[AmoebaNr[newx][newy]]--;
8398       }
8399
8400       if (element == EL_MOLE)
8401       {
8402         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8403         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8404
8405         ResetGfxAnimation(x, y);
8406         GfxAction[x][y] = ACTION_DIGGING;
8407         TEST_DrawLevelField(x, y);
8408
8409         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8410
8411         return;                         // wait for shrinking amoeba
8412       }
8413       else      // element == EL_PACMAN
8414       {
8415         Tile[newx][newy] = EL_EMPTY;
8416         TEST_DrawLevelField(newx, newy);
8417         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8418       }
8419     }
8420     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8421              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8422               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8423     {
8424       // wait for shrinking amoeba to completely disappear
8425       return;
8426     }
8427     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8428     {
8429       // object was running against a wall
8430
8431       TurnRound(x, y);
8432
8433       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8434         DrawLevelElementAnimation(x, y, element);
8435
8436       if (DONT_TOUCH(element))
8437         TestIfBadThingTouchesPlayer(x, y);
8438
8439       return;
8440     }
8441
8442     InitMovingField(x, y, MovDir[x][y]);
8443
8444     PlayLevelSoundAction(x, y, ACTION_MOVING);
8445   }
8446
8447   if (MovDir[x][y])
8448     ContinueMoving(x, y);
8449 }
8450
8451 void ContinueMoving(int x, int y)
8452 {
8453   int element = Tile[x][y];
8454   struct ElementInfo *ei = &element_info[element];
8455   int direction = MovDir[x][y];
8456   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8457   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8458   int newx = x + dx, newy = y + dy;
8459   int stored = Store[x][y];
8460   int stored_new = Store[newx][newy];
8461   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8462   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8463   boolean last_line = (newy == lev_fieldy - 1);
8464
8465   MovPos[x][y] += getElementMoveStepsize(x, y);
8466
8467   if (pushed_by_player) // special case: moving object pushed by player
8468     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8469
8470   if (ABS(MovPos[x][y]) < TILEX)
8471   {
8472     TEST_DrawLevelField(x, y);
8473
8474     return;     // element is still moving
8475   }
8476
8477   // element reached destination field
8478
8479   Tile[x][y] = EL_EMPTY;
8480   Tile[newx][newy] = element;
8481   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8482
8483   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8484   {
8485     element = Tile[newx][newy] = EL_ACID;
8486   }
8487   else if (element == EL_MOLE)
8488   {
8489     Tile[x][y] = EL_SAND;
8490
8491     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8492   }
8493   else if (element == EL_QUICKSAND_FILLING)
8494   {
8495     element = Tile[newx][newy] = get_next_element(element);
8496     Store[newx][newy] = Store[x][y];
8497   }
8498   else if (element == EL_QUICKSAND_EMPTYING)
8499   {
8500     Tile[x][y] = get_next_element(element);
8501     element = Tile[newx][newy] = Store[x][y];
8502   }
8503   else if (element == EL_QUICKSAND_FAST_FILLING)
8504   {
8505     element = Tile[newx][newy] = get_next_element(element);
8506     Store[newx][newy] = Store[x][y];
8507   }
8508   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8509   {
8510     Tile[x][y] = get_next_element(element);
8511     element = Tile[newx][newy] = Store[x][y];
8512   }
8513   else if (element == EL_MAGIC_WALL_FILLING)
8514   {
8515     element = Tile[newx][newy] = get_next_element(element);
8516     if (!game.magic_wall_active)
8517       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8518     Store[newx][newy] = Store[x][y];
8519   }
8520   else if (element == EL_MAGIC_WALL_EMPTYING)
8521   {
8522     Tile[x][y] = get_next_element(element);
8523     if (!game.magic_wall_active)
8524       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8525     element = Tile[newx][newy] = Store[x][y];
8526
8527     InitField(newx, newy, FALSE);
8528   }
8529   else if (element == EL_BD_MAGIC_WALL_FILLING)
8530   {
8531     element = Tile[newx][newy] = get_next_element(element);
8532     if (!game.magic_wall_active)
8533       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8534     Store[newx][newy] = Store[x][y];
8535   }
8536   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8537   {
8538     Tile[x][y] = get_next_element(element);
8539     if (!game.magic_wall_active)
8540       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8541     element = Tile[newx][newy] = Store[x][y];
8542
8543     InitField(newx, newy, FALSE);
8544   }
8545   else if (element == EL_DC_MAGIC_WALL_FILLING)
8546   {
8547     element = Tile[newx][newy] = get_next_element(element);
8548     if (!game.magic_wall_active)
8549       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8550     Store[newx][newy] = Store[x][y];
8551   }
8552   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8553   {
8554     Tile[x][y] = get_next_element(element);
8555     if (!game.magic_wall_active)
8556       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8557     element = Tile[newx][newy] = Store[x][y];
8558
8559     InitField(newx, newy, FALSE);
8560   }
8561   else if (element == EL_AMOEBA_DROPPING)
8562   {
8563     Tile[x][y] = get_next_element(element);
8564     element = Tile[newx][newy] = Store[x][y];
8565   }
8566   else if (element == EL_SOKOBAN_OBJECT)
8567   {
8568     if (Back[x][y])
8569       Tile[x][y] = Back[x][y];
8570
8571     if (Back[newx][newy])
8572       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8573
8574     Back[x][y] = Back[newx][newy] = 0;
8575   }
8576
8577   Store[x][y] = EL_EMPTY;
8578   MovPos[x][y] = 0;
8579   MovDir[x][y] = 0;
8580   MovDelay[x][y] = 0;
8581
8582   MovDelay[newx][newy] = 0;
8583
8584   if (CAN_CHANGE_OR_HAS_ACTION(element))
8585   {
8586     // copy element change control values to new field
8587     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8588     ChangePage[newx][newy]  = ChangePage[x][y];
8589     ChangeCount[newx][newy] = ChangeCount[x][y];
8590     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8591   }
8592
8593   CustomValue[newx][newy] = CustomValue[x][y];
8594
8595   ChangeDelay[x][y] = 0;
8596   ChangePage[x][y] = -1;
8597   ChangeCount[x][y] = 0;
8598   ChangeEvent[x][y] = -1;
8599
8600   CustomValue[x][y] = 0;
8601
8602   // copy animation control values to new field
8603   GfxFrame[newx][newy]  = GfxFrame[x][y];
8604   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8605   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8606   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8607
8608   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8609
8610   // some elements can leave other elements behind after moving
8611   if (ei->move_leave_element != EL_EMPTY &&
8612       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8613       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8614   {
8615     int move_leave_element = ei->move_leave_element;
8616
8617     // this makes it possible to leave the removed element again
8618     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8619       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8620
8621     Tile[x][y] = move_leave_element;
8622
8623     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8624       MovDir[x][y] = direction;
8625
8626     InitField(x, y, FALSE);
8627
8628     if (GFX_CRUMBLED(Tile[x][y]))
8629       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8630
8631     if (ELEM_IS_PLAYER(move_leave_element))
8632       RelocatePlayer(x, y, move_leave_element);
8633   }
8634
8635   // do this after checking for left-behind element
8636   ResetGfxAnimation(x, y);      // reset animation values for old field
8637
8638   if (!CAN_MOVE(element) ||
8639       (CAN_FALL(element) && direction == MV_DOWN &&
8640        (element == EL_SPRING ||
8641         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8642         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8643     GfxDir[x][y] = MovDir[newx][newy] = 0;
8644
8645   TEST_DrawLevelField(x, y);
8646   TEST_DrawLevelField(newx, newy);
8647
8648   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8649
8650   // prevent pushed element from moving on in pushed direction
8651   if (pushed_by_player && CAN_MOVE(element) &&
8652       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8653       !(element_info[element].move_pattern & direction))
8654     TurnRound(newx, newy);
8655
8656   // prevent elements on conveyor belt from moving on in last direction
8657   if (pushed_by_conveyor && CAN_FALL(element) &&
8658       direction & MV_HORIZONTAL)
8659     MovDir[newx][newy] = 0;
8660
8661   if (!pushed_by_player)
8662   {
8663     int nextx = newx + dx, nexty = newy + dy;
8664     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8665
8666     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8667
8668     if (CAN_FALL(element) && direction == MV_DOWN)
8669       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8670
8671     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8672       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8673
8674     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8675       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8676   }
8677
8678   if (DONT_TOUCH(element))      // object may be nasty to player or others
8679   {
8680     TestIfBadThingTouchesPlayer(newx, newy);
8681     TestIfBadThingTouchesFriend(newx, newy);
8682
8683     if (!IS_CUSTOM_ELEMENT(element))
8684       TestIfBadThingTouchesOtherBadThing(newx, newy);
8685   }
8686   else if (element == EL_PENGUIN)
8687     TestIfFriendTouchesBadThing(newx, newy);
8688
8689   if (DONT_GET_HIT_BY(element))
8690   {
8691     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8692   }
8693
8694   // give the player one last chance (one more frame) to move away
8695   if (CAN_FALL(element) && direction == MV_DOWN &&
8696       (last_line || (!IS_FREE(x, newy + 1) &&
8697                      (!IS_PLAYER(x, newy + 1) ||
8698                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8699     Impact(x, newy);
8700
8701   if (pushed_by_player && !game.use_change_when_pushing_bug)
8702   {
8703     int push_side = MV_DIR_OPPOSITE(direction);
8704     struct PlayerInfo *player = PLAYERINFO(x, y);
8705
8706     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8707                                player->index_bit, push_side);
8708     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8709                                         player->index_bit, push_side);
8710   }
8711
8712   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8713     MovDelay[newx][newy] = 1;
8714
8715   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8716
8717   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8718   TestIfElementHitsCustomElement(newx, newy, direction);
8719   TestIfPlayerTouchesCustomElement(newx, newy);
8720   TestIfElementTouchesCustomElement(newx, newy);
8721
8722   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8723       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8724     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8725                              MV_DIR_OPPOSITE(direction));
8726 }
8727
8728 int AmoebaNeighbourNr(int ax, int ay)
8729 {
8730   int i;
8731   int element = Tile[ax][ay];
8732   int group_nr = 0;
8733   static int xy[4][2] =
8734   {
8735     { 0, -1 },
8736     { -1, 0 },
8737     { +1, 0 },
8738     { 0, +1 }
8739   };
8740
8741   for (i = 0; i < NUM_DIRECTIONS; i++)
8742   {
8743     int x = ax + xy[i][0];
8744     int y = ay + xy[i][1];
8745
8746     if (!IN_LEV_FIELD(x, y))
8747       continue;
8748
8749     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8750       group_nr = AmoebaNr[x][y];
8751   }
8752
8753   return group_nr;
8754 }
8755
8756 static void AmoebaMerge(int ax, int ay)
8757 {
8758   int i, x, y, xx, yy;
8759   int new_group_nr = AmoebaNr[ax][ay];
8760   static int xy[4][2] =
8761   {
8762     { 0, -1 },
8763     { -1, 0 },
8764     { +1, 0 },
8765     { 0, +1 }
8766   };
8767
8768   if (new_group_nr == 0)
8769     return;
8770
8771   for (i = 0; i < NUM_DIRECTIONS; i++)
8772   {
8773     x = ax + xy[i][0];
8774     y = ay + xy[i][1];
8775
8776     if (!IN_LEV_FIELD(x, y))
8777       continue;
8778
8779     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8780          Tile[x][y] == EL_BD_AMOEBA ||
8781          Tile[x][y] == EL_AMOEBA_DEAD) &&
8782         AmoebaNr[x][y] != new_group_nr)
8783     {
8784       int old_group_nr = AmoebaNr[x][y];
8785
8786       if (old_group_nr == 0)
8787         return;
8788
8789       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8790       AmoebaCnt[old_group_nr] = 0;
8791       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8792       AmoebaCnt2[old_group_nr] = 0;
8793
8794       SCAN_PLAYFIELD(xx, yy)
8795       {
8796         if (AmoebaNr[xx][yy] == old_group_nr)
8797           AmoebaNr[xx][yy] = new_group_nr;
8798       }
8799     }
8800   }
8801 }
8802
8803 void AmoebaToDiamond(int ax, int ay)
8804 {
8805   int i, x, y;
8806
8807   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8808   {
8809     int group_nr = AmoebaNr[ax][ay];
8810
8811 #ifdef DEBUG
8812     if (group_nr == 0)
8813     {
8814       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8815       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8816
8817       return;
8818     }
8819 #endif
8820
8821     SCAN_PLAYFIELD(x, y)
8822     {
8823       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8824       {
8825         AmoebaNr[x][y] = 0;
8826         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8827       }
8828     }
8829
8830     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8831                             SND_AMOEBA_TURNING_TO_GEM :
8832                             SND_AMOEBA_TURNING_TO_ROCK));
8833     Bang(ax, ay);
8834   }
8835   else
8836   {
8837     static int xy[4][2] =
8838     {
8839       { 0, -1 },
8840       { -1, 0 },
8841       { +1, 0 },
8842       { 0, +1 }
8843     };
8844
8845     for (i = 0; i < NUM_DIRECTIONS; i++)
8846     {
8847       x = ax + xy[i][0];
8848       y = ay + xy[i][1];
8849
8850       if (!IN_LEV_FIELD(x, y))
8851         continue;
8852
8853       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8854       {
8855         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8856                               SND_AMOEBA_TURNING_TO_GEM :
8857                               SND_AMOEBA_TURNING_TO_ROCK));
8858         Bang(x, y);
8859       }
8860     }
8861   }
8862 }
8863
8864 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8865 {
8866   int x, y;
8867   int group_nr = AmoebaNr[ax][ay];
8868   boolean done = FALSE;
8869
8870 #ifdef DEBUG
8871   if (group_nr == 0)
8872   {
8873     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8874     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8875
8876     return;
8877   }
8878 #endif
8879
8880   SCAN_PLAYFIELD(x, y)
8881   {
8882     if (AmoebaNr[x][y] == group_nr &&
8883         (Tile[x][y] == EL_AMOEBA_DEAD ||
8884          Tile[x][y] == EL_BD_AMOEBA ||
8885          Tile[x][y] == EL_AMOEBA_GROWING))
8886     {
8887       AmoebaNr[x][y] = 0;
8888       Tile[x][y] = new_element;
8889       InitField(x, y, FALSE);
8890       TEST_DrawLevelField(x, y);
8891       done = TRUE;
8892     }
8893   }
8894
8895   if (done)
8896     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8897                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8898                             SND_BD_AMOEBA_TURNING_TO_GEM));
8899 }
8900
8901 static void AmoebaGrowing(int x, int y)
8902 {
8903   static unsigned int sound_delay = 0;
8904   static unsigned int sound_delay_value = 0;
8905
8906   if (!MovDelay[x][y])          // start new growing cycle
8907   {
8908     MovDelay[x][y] = 7;
8909
8910     if (DelayReached(&sound_delay, sound_delay_value))
8911     {
8912       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8913       sound_delay_value = 30;
8914     }
8915   }
8916
8917   if (MovDelay[x][y])           // wait some time before growing bigger
8918   {
8919     MovDelay[x][y]--;
8920     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8921     {
8922       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8923                                            6 - MovDelay[x][y]);
8924
8925       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8926     }
8927
8928     if (!MovDelay[x][y])
8929     {
8930       Tile[x][y] = Store[x][y];
8931       Store[x][y] = 0;
8932       TEST_DrawLevelField(x, y);
8933     }
8934   }
8935 }
8936
8937 static void AmoebaShrinking(int x, int y)
8938 {
8939   static unsigned int sound_delay = 0;
8940   static unsigned int sound_delay_value = 0;
8941
8942   if (!MovDelay[x][y])          // start new shrinking cycle
8943   {
8944     MovDelay[x][y] = 7;
8945
8946     if (DelayReached(&sound_delay, sound_delay_value))
8947       sound_delay_value = 30;
8948   }
8949
8950   if (MovDelay[x][y])           // wait some time before shrinking
8951   {
8952     MovDelay[x][y]--;
8953     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8954     {
8955       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8956                                            6 - MovDelay[x][y]);
8957
8958       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8959     }
8960
8961     if (!MovDelay[x][y])
8962     {
8963       Tile[x][y] = EL_EMPTY;
8964       TEST_DrawLevelField(x, y);
8965
8966       // don't let mole enter this field in this cycle;
8967       // (give priority to objects falling to this field from above)
8968       Stop[x][y] = TRUE;
8969     }
8970   }
8971 }
8972
8973 static void AmoebaReproduce(int ax, int ay)
8974 {
8975   int i;
8976   int element = Tile[ax][ay];
8977   int graphic = el2img(element);
8978   int newax = ax, neway = ay;
8979   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8980   static int xy[4][2] =
8981   {
8982     { 0, -1 },
8983     { -1, 0 },
8984     { +1, 0 },
8985     { 0, +1 }
8986   };
8987
8988   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8989   {
8990     Tile[ax][ay] = EL_AMOEBA_DEAD;
8991     TEST_DrawLevelField(ax, ay);
8992     return;
8993   }
8994
8995   if (IS_ANIMATED(graphic))
8996     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8997
8998   if (!MovDelay[ax][ay])        // start making new amoeba field
8999     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9000
9001   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9002   {
9003     MovDelay[ax][ay]--;
9004     if (MovDelay[ax][ay])
9005       return;
9006   }
9007
9008   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9009   {
9010     int start = RND(4);
9011     int x = ax + xy[start][0];
9012     int y = ay + xy[start][1];
9013
9014     if (!IN_LEV_FIELD(x, y))
9015       return;
9016
9017     if (IS_FREE(x, y) ||
9018         CAN_GROW_INTO(Tile[x][y]) ||
9019         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9020         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9021     {
9022       newax = x;
9023       neway = y;
9024     }
9025
9026     if (newax == ax && neway == ay)
9027       return;
9028   }
9029   else                          // normal or "filled" (BD style) amoeba
9030   {
9031     int start = RND(4);
9032     boolean waiting_for_player = FALSE;
9033
9034     for (i = 0; i < NUM_DIRECTIONS; i++)
9035     {
9036       int j = (start + i) % 4;
9037       int x = ax + xy[j][0];
9038       int y = ay + xy[j][1];
9039
9040       if (!IN_LEV_FIELD(x, y))
9041         continue;
9042
9043       if (IS_FREE(x, y) ||
9044           CAN_GROW_INTO(Tile[x][y]) ||
9045           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9046           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9047       {
9048         newax = x;
9049         neway = y;
9050         break;
9051       }
9052       else if (IS_PLAYER(x, y))
9053         waiting_for_player = TRUE;
9054     }
9055
9056     if (newax == ax && neway == ay)             // amoeba cannot grow
9057     {
9058       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9059       {
9060         Tile[ax][ay] = EL_AMOEBA_DEAD;
9061         TEST_DrawLevelField(ax, ay);
9062         AmoebaCnt[AmoebaNr[ax][ay]]--;
9063
9064         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9065         {
9066           if (element == EL_AMOEBA_FULL)
9067             AmoebaToDiamond(ax, ay);
9068           else if (element == EL_BD_AMOEBA)
9069             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9070         }
9071       }
9072       return;
9073     }
9074     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9075     {
9076       // amoeba gets larger by growing in some direction
9077
9078       int new_group_nr = AmoebaNr[ax][ay];
9079
9080 #ifdef DEBUG
9081   if (new_group_nr == 0)
9082   {
9083     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9084           newax, neway);
9085     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9086
9087     return;
9088   }
9089 #endif
9090
9091       AmoebaNr[newax][neway] = new_group_nr;
9092       AmoebaCnt[new_group_nr]++;
9093       AmoebaCnt2[new_group_nr]++;
9094
9095       // if amoeba touches other amoeba(s) after growing, unify them
9096       AmoebaMerge(newax, neway);
9097
9098       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9099       {
9100         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9101         return;
9102       }
9103     }
9104   }
9105
9106   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9107       (neway == lev_fieldy - 1 && newax != ax))
9108   {
9109     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9110     Store[newax][neway] = element;
9111   }
9112   else if (neway == ay || element == EL_EMC_DRIPPER)
9113   {
9114     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9115
9116     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9117   }
9118   else
9119   {
9120     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9121     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9122     Store[ax][ay] = EL_AMOEBA_DROP;
9123     ContinueMoving(ax, ay);
9124     return;
9125   }
9126
9127   TEST_DrawLevelField(newax, neway);
9128 }
9129
9130 static void Life(int ax, int ay)
9131 {
9132   int x1, y1, x2, y2;
9133   int life_time = 40;
9134   int element = Tile[ax][ay];
9135   int graphic = el2img(element);
9136   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9137                          level.biomaze);
9138   boolean changed = FALSE;
9139
9140   if (IS_ANIMATED(graphic))
9141     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9142
9143   if (Stop[ax][ay])
9144     return;
9145
9146   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9147     MovDelay[ax][ay] = life_time;
9148
9149   if (MovDelay[ax][ay])         // wait some time before next cycle
9150   {
9151     MovDelay[ax][ay]--;
9152     if (MovDelay[ax][ay])
9153       return;
9154   }
9155
9156   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9157   {
9158     int xx = ax+x1, yy = ay+y1;
9159     int old_element = Tile[xx][yy];
9160     int num_neighbours = 0;
9161
9162     if (!IN_LEV_FIELD(xx, yy))
9163       continue;
9164
9165     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9166     {
9167       int x = xx+x2, y = yy+y2;
9168
9169       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9170         continue;
9171
9172       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9173       boolean is_neighbour = FALSE;
9174
9175       if (level.use_life_bugs)
9176         is_neighbour =
9177           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9178            (IS_FREE(x, y)                             &&  Stop[x][y]));
9179       else
9180         is_neighbour =
9181           (Last[x][y] == element || is_player_cell);
9182
9183       if (is_neighbour)
9184         num_neighbours++;
9185     }
9186
9187     boolean is_free = FALSE;
9188
9189     if (level.use_life_bugs)
9190       is_free = (IS_FREE(xx, yy));
9191     else
9192       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9193
9194     if (xx == ax && yy == ay)           // field in the middle
9195     {
9196       if (num_neighbours < life_parameter[0] ||
9197           num_neighbours > life_parameter[1])
9198       {
9199         Tile[xx][yy] = EL_EMPTY;
9200         if (Tile[xx][yy] != old_element)
9201           TEST_DrawLevelField(xx, yy);
9202         Stop[xx][yy] = TRUE;
9203         changed = TRUE;
9204       }
9205     }
9206     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9207     {                                   // free border field
9208       if (num_neighbours >= life_parameter[2] &&
9209           num_neighbours <= life_parameter[3])
9210       {
9211         Tile[xx][yy] = element;
9212         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9213         if (Tile[xx][yy] != old_element)
9214           TEST_DrawLevelField(xx, yy);
9215         Stop[xx][yy] = TRUE;
9216         changed = TRUE;
9217       }
9218     }
9219   }
9220
9221   if (changed)
9222     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9223                    SND_GAME_OF_LIFE_GROWING);
9224 }
9225
9226 static void InitRobotWheel(int x, int y)
9227 {
9228   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9229 }
9230
9231 static void RunRobotWheel(int x, int y)
9232 {
9233   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9234 }
9235
9236 static void StopRobotWheel(int x, int y)
9237 {
9238   if (game.robot_wheel_x == x &&
9239       game.robot_wheel_y == y)
9240   {
9241     game.robot_wheel_x = -1;
9242     game.robot_wheel_y = -1;
9243     game.robot_wheel_active = FALSE;
9244   }
9245 }
9246
9247 static void InitTimegateWheel(int x, int y)
9248 {
9249   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9250 }
9251
9252 static void RunTimegateWheel(int x, int y)
9253 {
9254   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9255 }
9256
9257 static void InitMagicBallDelay(int x, int y)
9258 {
9259   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9260 }
9261
9262 static void ActivateMagicBall(int bx, int by)
9263 {
9264   int x, y;
9265
9266   if (level.ball_random)
9267   {
9268     int pos_border = RND(8);    // select one of the eight border elements
9269     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9270     int xx = pos_content % 3;
9271     int yy = pos_content / 3;
9272
9273     x = bx - 1 + xx;
9274     y = by - 1 + yy;
9275
9276     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9277       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9278   }
9279   else
9280   {
9281     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9282     {
9283       int xx = x - bx + 1;
9284       int yy = y - by + 1;
9285
9286       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9287         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9288     }
9289   }
9290
9291   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9292 }
9293
9294 static void CheckExit(int x, int y)
9295 {
9296   if (game.gems_still_needed > 0 ||
9297       game.sokoban_fields_still_needed > 0 ||
9298       game.sokoban_objects_still_needed > 0 ||
9299       game.lights_still_needed > 0)
9300   {
9301     int element = Tile[x][y];
9302     int graphic = el2img(element);
9303
9304     if (IS_ANIMATED(graphic))
9305       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9306
9307     return;
9308   }
9309
9310   // do not re-open exit door closed after last player
9311   if (game.all_players_gone)
9312     return;
9313
9314   Tile[x][y] = EL_EXIT_OPENING;
9315
9316   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9317 }
9318
9319 static void CheckExitEM(int x, int y)
9320 {
9321   if (game.gems_still_needed > 0 ||
9322       game.sokoban_fields_still_needed > 0 ||
9323       game.sokoban_objects_still_needed > 0 ||
9324       game.lights_still_needed > 0)
9325   {
9326     int element = Tile[x][y];
9327     int graphic = el2img(element);
9328
9329     if (IS_ANIMATED(graphic))
9330       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9331
9332     return;
9333   }
9334
9335   // do not re-open exit door closed after last player
9336   if (game.all_players_gone)
9337     return;
9338
9339   Tile[x][y] = EL_EM_EXIT_OPENING;
9340
9341   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9342 }
9343
9344 static void CheckExitSteel(int x, int y)
9345 {
9346   if (game.gems_still_needed > 0 ||
9347       game.sokoban_fields_still_needed > 0 ||
9348       game.sokoban_objects_still_needed > 0 ||
9349       game.lights_still_needed > 0)
9350   {
9351     int element = Tile[x][y];
9352     int graphic = el2img(element);
9353
9354     if (IS_ANIMATED(graphic))
9355       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9356
9357     return;
9358   }
9359
9360   // do not re-open exit door closed after last player
9361   if (game.all_players_gone)
9362     return;
9363
9364   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9365
9366   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9367 }
9368
9369 static void CheckExitSteelEM(int x, int y)
9370 {
9371   if (game.gems_still_needed > 0 ||
9372       game.sokoban_fields_still_needed > 0 ||
9373       game.sokoban_objects_still_needed > 0 ||
9374       game.lights_still_needed > 0)
9375   {
9376     int element = Tile[x][y];
9377     int graphic = el2img(element);
9378
9379     if (IS_ANIMATED(graphic))
9380       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9381
9382     return;
9383   }
9384
9385   // do not re-open exit door closed after last player
9386   if (game.all_players_gone)
9387     return;
9388
9389   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9390
9391   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9392 }
9393
9394 static void CheckExitSP(int x, int y)
9395 {
9396   if (game.gems_still_needed > 0)
9397   {
9398     int element = Tile[x][y];
9399     int graphic = el2img(element);
9400
9401     if (IS_ANIMATED(graphic))
9402       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9403
9404     return;
9405   }
9406
9407   // do not re-open exit door closed after last player
9408   if (game.all_players_gone)
9409     return;
9410
9411   Tile[x][y] = EL_SP_EXIT_OPENING;
9412
9413   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9414 }
9415
9416 static void CloseAllOpenTimegates(void)
9417 {
9418   int x, y;
9419
9420   SCAN_PLAYFIELD(x, y)
9421   {
9422     int element = Tile[x][y];
9423
9424     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9425     {
9426       Tile[x][y] = EL_TIMEGATE_CLOSING;
9427
9428       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9429     }
9430   }
9431 }
9432
9433 static void DrawTwinkleOnField(int x, int y)
9434 {
9435   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9436     return;
9437
9438   if (Tile[x][y] == EL_BD_DIAMOND)
9439     return;
9440
9441   if (MovDelay[x][y] == 0)      // next animation frame
9442     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9443
9444   if (MovDelay[x][y] != 0)      // wait some time before next frame
9445   {
9446     MovDelay[x][y]--;
9447
9448     DrawLevelElementAnimation(x, y, Tile[x][y]);
9449
9450     if (MovDelay[x][y] != 0)
9451     {
9452       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9453                                            10 - MovDelay[x][y]);
9454
9455       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9456     }
9457   }
9458 }
9459
9460 static void MauerWaechst(int x, int y)
9461 {
9462   int delay = 6;
9463
9464   if (!MovDelay[x][y])          // next animation frame
9465     MovDelay[x][y] = 3 * delay;
9466
9467   if (MovDelay[x][y])           // wait some time before next frame
9468   {
9469     MovDelay[x][y]--;
9470
9471     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9472     {
9473       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9474       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9475
9476       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9477     }
9478
9479     if (!MovDelay[x][y])
9480     {
9481       if (MovDir[x][y] == MV_LEFT)
9482       {
9483         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9484           TEST_DrawLevelField(x - 1, y);
9485       }
9486       else if (MovDir[x][y] == MV_RIGHT)
9487       {
9488         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9489           TEST_DrawLevelField(x + 1, y);
9490       }
9491       else if (MovDir[x][y] == MV_UP)
9492       {
9493         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9494           TEST_DrawLevelField(x, y - 1);
9495       }
9496       else
9497       {
9498         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9499           TEST_DrawLevelField(x, y + 1);
9500       }
9501
9502       Tile[x][y] = Store[x][y];
9503       Store[x][y] = 0;
9504       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9505       TEST_DrawLevelField(x, y);
9506     }
9507   }
9508 }
9509
9510 static void MauerAbleger(int ax, int ay)
9511 {
9512   int element = Tile[ax][ay];
9513   int graphic = el2img(element);
9514   boolean oben_frei = FALSE, unten_frei = FALSE;
9515   boolean links_frei = FALSE, rechts_frei = FALSE;
9516   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9517   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9518   boolean new_wall = FALSE;
9519
9520   if (IS_ANIMATED(graphic))
9521     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9522
9523   if (!MovDelay[ax][ay])        // start building new wall
9524     MovDelay[ax][ay] = 6;
9525
9526   if (MovDelay[ax][ay])         // wait some time before building new wall
9527   {
9528     MovDelay[ax][ay]--;
9529     if (MovDelay[ax][ay])
9530       return;
9531   }
9532
9533   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9534     oben_frei = TRUE;
9535   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9536     unten_frei = TRUE;
9537   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9538     links_frei = TRUE;
9539   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9540     rechts_frei = TRUE;
9541
9542   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9543       element == EL_EXPANDABLE_WALL_ANY)
9544   {
9545     if (oben_frei)
9546     {
9547       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9548       Store[ax][ay-1] = element;
9549       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9550       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9551         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9552                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9553       new_wall = TRUE;
9554     }
9555     if (unten_frei)
9556     {
9557       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9558       Store[ax][ay+1] = element;
9559       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9560       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9561         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9562                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9563       new_wall = TRUE;
9564     }
9565   }
9566
9567   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9568       element == EL_EXPANDABLE_WALL_ANY ||
9569       element == EL_EXPANDABLE_WALL ||
9570       element == EL_BD_EXPANDABLE_WALL)
9571   {
9572     if (links_frei)
9573     {
9574       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9575       Store[ax-1][ay] = element;
9576       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9577       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9578         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9579                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9580       new_wall = TRUE;
9581     }
9582
9583     if (rechts_frei)
9584     {
9585       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9586       Store[ax+1][ay] = element;
9587       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9588       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9589         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9590                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9591       new_wall = TRUE;
9592     }
9593   }
9594
9595   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9596     TEST_DrawLevelField(ax, ay);
9597
9598   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9599     oben_massiv = TRUE;
9600   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9601     unten_massiv = TRUE;
9602   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9603     links_massiv = TRUE;
9604   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9605     rechts_massiv = TRUE;
9606
9607   if (((oben_massiv && unten_massiv) ||
9608        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9609        element == EL_EXPANDABLE_WALL) &&
9610       ((links_massiv && rechts_massiv) ||
9611        element == EL_EXPANDABLE_WALL_VERTICAL))
9612     Tile[ax][ay] = EL_WALL;
9613
9614   if (new_wall)
9615     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9616 }
9617
9618 static void MauerAblegerStahl(int ax, int ay)
9619 {
9620   int element = Tile[ax][ay];
9621   int graphic = el2img(element);
9622   boolean oben_frei = FALSE, unten_frei = FALSE;
9623   boolean links_frei = FALSE, rechts_frei = FALSE;
9624   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9625   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9626   boolean new_wall = FALSE;
9627
9628   if (IS_ANIMATED(graphic))
9629     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9630
9631   if (!MovDelay[ax][ay])        // start building new wall
9632     MovDelay[ax][ay] = 6;
9633
9634   if (MovDelay[ax][ay])         // wait some time before building new wall
9635   {
9636     MovDelay[ax][ay]--;
9637     if (MovDelay[ax][ay])
9638       return;
9639   }
9640
9641   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9642     oben_frei = TRUE;
9643   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9644     unten_frei = TRUE;
9645   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9646     links_frei = TRUE;
9647   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9648     rechts_frei = TRUE;
9649
9650   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9651       element == EL_EXPANDABLE_STEELWALL_ANY)
9652   {
9653     if (oben_frei)
9654     {
9655       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9656       Store[ax][ay-1] = element;
9657       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9658       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9659         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9660                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9661       new_wall = TRUE;
9662     }
9663     if (unten_frei)
9664     {
9665       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9666       Store[ax][ay+1] = element;
9667       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9668       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9669         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9670                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9671       new_wall = TRUE;
9672     }
9673   }
9674
9675   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9676       element == EL_EXPANDABLE_STEELWALL_ANY)
9677   {
9678     if (links_frei)
9679     {
9680       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9681       Store[ax-1][ay] = element;
9682       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9683       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9684         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9685                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9686       new_wall = TRUE;
9687     }
9688
9689     if (rechts_frei)
9690     {
9691       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9692       Store[ax+1][ay] = element;
9693       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9694       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9695         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9696                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9697       new_wall = TRUE;
9698     }
9699   }
9700
9701   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9702     oben_massiv = TRUE;
9703   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9704     unten_massiv = TRUE;
9705   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9706     links_massiv = TRUE;
9707   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9708     rechts_massiv = TRUE;
9709
9710   if (((oben_massiv && unten_massiv) ||
9711        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9712       ((links_massiv && rechts_massiv) ||
9713        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9714     Tile[ax][ay] = EL_STEELWALL;
9715
9716   if (new_wall)
9717     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9718 }
9719
9720 static void CheckForDragon(int x, int y)
9721 {
9722   int i, j;
9723   boolean dragon_found = FALSE;
9724   static int xy[4][2] =
9725   {
9726     { 0, -1 },
9727     { -1, 0 },
9728     { +1, 0 },
9729     { 0, +1 }
9730   };
9731
9732   for (i = 0; i < NUM_DIRECTIONS; i++)
9733   {
9734     for (j = 0; j < 4; j++)
9735     {
9736       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9737
9738       if (IN_LEV_FIELD(xx, yy) &&
9739           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9740       {
9741         if (Tile[xx][yy] == EL_DRAGON)
9742           dragon_found = TRUE;
9743       }
9744       else
9745         break;
9746     }
9747   }
9748
9749   if (!dragon_found)
9750   {
9751     for (i = 0; i < NUM_DIRECTIONS; i++)
9752     {
9753       for (j = 0; j < 3; j++)
9754       {
9755         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9756   
9757         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9758         {
9759           Tile[xx][yy] = EL_EMPTY;
9760           TEST_DrawLevelField(xx, yy);
9761         }
9762         else
9763           break;
9764       }
9765     }
9766   }
9767 }
9768
9769 static void InitBuggyBase(int x, int y)
9770 {
9771   int element = Tile[x][y];
9772   int activating_delay = FRAMES_PER_SECOND / 4;
9773
9774   ChangeDelay[x][y] =
9775     (element == EL_SP_BUGGY_BASE ?
9776      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9777      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9778      activating_delay :
9779      element == EL_SP_BUGGY_BASE_ACTIVE ?
9780      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9781 }
9782
9783 static void WarnBuggyBase(int x, int y)
9784 {
9785   int i;
9786   static int xy[4][2] =
9787   {
9788     { 0, -1 },
9789     { -1, 0 },
9790     { +1, 0 },
9791     { 0, +1 }
9792   };
9793
9794   for (i = 0; i < NUM_DIRECTIONS; i++)
9795   {
9796     int xx = x + xy[i][0];
9797     int yy = y + xy[i][1];
9798
9799     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9800     {
9801       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9802
9803       break;
9804     }
9805   }
9806 }
9807
9808 static void InitTrap(int x, int y)
9809 {
9810   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9811 }
9812
9813 static void ActivateTrap(int x, int y)
9814 {
9815   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9816 }
9817
9818 static void ChangeActiveTrap(int x, int y)
9819 {
9820   int graphic = IMG_TRAP_ACTIVE;
9821
9822   // if new animation frame was drawn, correct crumbled sand border
9823   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9824     TEST_DrawLevelFieldCrumbled(x, y);
9825 }
9826
9827 static int getSpecialActionElement(int element, int number, int base_element)
9828 {
9829   return (element != EL_EMPTY ? element :
9830           number != -1 ? base_element + number - 1 :
9831           EL_EMPTY);
9832 }
9833
9834 static int getModifiedActionNumber(int value_old, int operator, int operand,
9835                                    int value_min, int value_max)
9836 {
9837   int value_new = (operator == CA_MODE_SET      ? operand :
9838                    operator == CA_MODE_ADD      ? value_old + operand :
9839                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9840                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9841                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9842                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9843                    value_old);
9844
9845   return (value_new < value_min ? value_min :
9846           value_new > value_max ? value_max :
9847           value_new);
9848 }
9849
9850 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9851 {
9852   struct ElementInfo *ei = &element_info[element];
9853   struct ElementChangeInfo *change = &ei->change_page[page];
9854   int target_element = change->target_element;
9855   int action_type = change->action_type;
9856   int action_mode = change->action_mode;
9857   int action_arg = change->action_arg;
9858   int action_element = change->action_element;
9859   int i;
9860
9861   if (!change->has_action)
9862     return;
9863
9864   // ---------- determine action paramater values -----------------------------
9865
9866   int level_time_value =
9867     (level.time > 0 ? TimeLeft :
9868      TimePlayed);
9869
9870   int action_arg_element_raw =
9871     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9872      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9873      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9874      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9875      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9876      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9877      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9878      EL_EMPTY);
9879   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9880
9881   int action_arg_direction =
9882     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9883      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9884      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9885      change->actual_trigger_side :
9886      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9887      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9888      MV_NONE);
9889
9890   int action_arg_number_min =
9891     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9892      CA_ARG_MIN);
9893
9894   int action_arg_number_max =
9895     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9896      action_type == CA_SET_LEVEL_GEMS ? 999 :
9897      action_type == CA_SET_LEVEL_TIME ? 9999 :
9898      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9899      action_type == CA_SET_CE_VALUE ? 9999 :
9900      action_type == CA_SET_CE_SCORE ? 9999 :
9901      CA_ARG_MAX);
9902
9903   int action_arg_number_reset =
9904     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9905      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9906      action_type == CA_SET_LEVEL_TIME ? level.time :
9907      action_type == CA_SET_LEVEL_SCORE ? 0 :
9908      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9909      action_type == CA_SET_CE_SCORE ? 0 :
9910      0);
9911
9912   int action_arg_number =
9913     (action_arg <= CA_ARG_MAX ? action_arg :
9914      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9915      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9916      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9917      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9918      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9919      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9920      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9921      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9922      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9923      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9924      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9925      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9926      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9927      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9928      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9929      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9930      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9931      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9932      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9933      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9934      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9935      -1);
9936
9937   int action_arg_number_old =
9938     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9939      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9940      action_type == CA_SET_LEVEL_SCORE ? game.score :
9941      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9942      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9943      0);
9944
9945   int action_arg_number_new =
9946     getModifiedActionNumber(action_arg_number_old,
9947                             action_mode, action_arg_number,
9948                             action_arg_number_min, action_arg_number_max);
9949
9950   int trigger_player_bits =
9951     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9952      change->actual_trigger_player_bits : change->trigger_player);
9953
9954   int action_arg_player_bits =
9955     (action_arg >= CA_ARG_PLAYER_1 &&
9956      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9957      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9958      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9959      PLAYER_BITS_ANY);
9960
9961   // ---------- execute action  -----------------------------------------------
9962
9963   switch (action_type)
9964   {
9965     case CA_NO_ACTION:
9966     {
9967       return;
9968     }
9969
9970     // ---------- level actions  ----------------------------------------------
9971
9972     case CA_RESTART_LEVEL:
9973     {
9974       game.restart_level = TRUE;
9975
9976       break;
9977     }
9978
9979     case CA_SHOW_ENVELOPE:
9980     {
9981       int element = getSpecialActionElement(action_arg_element,
9982                                             action_arg_number, EL_ENVELOPE_1);
9983
9984       if (IS_ENVELOPE(element))
9985         local_player->show_envelope = element;
9986
9987       break;
9988     }
9989
9990     case CA_SET_LEVEL_TIME:
9991     {
9992       if (level.time > 0)       // only modify limited time value
9993       {
9994         TimeLeft = action_arg_number_new;
9995
9996         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9997
9998         DisplayGameControlValues();
9999
10000         if (!TimeLeft && setup.time_limit)
10001           for (i = 0; i < MAX_PLAYERS; i++)
10002             KillPlayer(&stored_player[i]);
10003       }
10004
10005       break;
10006     }
10007
10008     case CA_SET_LEVEL_SCORE:
10009     {
10010       game.score = action_arg_number_new;
10011
10012       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10013
10014       DisplayGameControlValues();
10015
10016       break;
10017     }
10018
10019     case CA_SET_LEVEL_GEMS:
10020     {
10021       game.gems_still_needed = action_arg_number_new;
10022
10023       game.snapshot.collected_item = TRUE;
10024
10025       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10026
10027       DisplayGameControlValues();
10028
10029       break;
10030     }
10031
10032     case CA_SET_LEVEL_WIND:
10033     {
10034       game.wind_direction = action_arg_direction;
10035
10036       break;
10037     }
10038
10039     case CA_SET_LEVEL_RANDOM_SEED:
10040     {
10041       // ensure that setting a new random seed while playing is predictable
10042       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10043
10044       break;
10045     }
10046
10047     // ---------- player actions  ---------------------------------------------
10048
10049     case CA_MOVE_PLAYER:
10050     case CA_MOVE_PLAYER_NEW:
10051     {
10052       // automatically move to the next field in specified direction
10053       for (i = 0; i < MAX_PLAYERS; i++)
10054         if (trigger_player_bits & (1 << i))
10055           if (action_type == CA_MOVE_PLAYER ||
10056               stored_player[i].MovPos == 0)
10057             stored_player[i].programmed_action = action_arg_direction;
10058
10059       break;
10060     }
10061
10062     case CA_EXIT_PLAYER:
10063     {
10064       for (i = 0; i < MAX_PLAYERS; i++)
10065         if (action_arg_player_bits & (1 << i))
10066           ExitPlayer(&stored_player[i]);
10067
10068       if (game.players_still_needed == 0)
10069         LevelSolved();
10070
10071       break;
10072     }
10073
10074     case CA_KILL_PLAYER:
10075     {
10076       for (i = 0; i < MAX_PLAYERS; i++)
10077         if (action_arg_player_bits & (1 << i))
10078           KillPlayer(&stored_player[i]);
10079
10080       break;
10081     }
10082
10083     case CA_SET_PLAYER_KEYS:
10084     {
10085       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10086       int element = getSpecialActionElement(action_arg_element,
10087                                             action_arg_number, EL_KEY_1);
10088
10089       if (IS_KEY(element))
10090       {
10091         for (i = 0; i < MAX_PLAYERS; i++)
10092         {
10093           if (trigger_player_bits & (1 << i))
10094           {
10095             stored_player[i].key[KEY_NR(element)] = key_state;
10096
10097             DrawGameDoorValues();
10098           }
10099         }
10100       }
10101
10102       break;
10103     }
10104
10105     case CA_SET_PLAYER_SPEED:
10106     {
10107       for (i = 0; i < MAX_PLAYERS; i++)
10108       {
10109         if (trigger_player_bits & (1 << i))
10110         {
10111           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10112
10113           if (action_arg == CA_ARG_SPEED_FASTER &&
10114               stored_player[i].cannot_move)
10115           {
10116             action_arg_number = STEPSIZE_VERY_SLOW;
10117           }
10118           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10119                    action_arg == CA_ARG_SPEED_FASTER)
10120           {
10121             action_arg_number = 2;
10122             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10123                            CA_MODE_MULTIPLY);
10124           }
10125           else if (action_arg == CA_ARG_NUMBER_RESET)
10126           {
10127             action_arg_number = level.initial_player_stepsize[i];
10128           }
10129
10130           move_stepsize =
10131             getModifiedActionNumber(move_stepsize,
10132                                     action_mode,
10133                                     action_arg_number,
10134                                     action_arg_number_min,
10135                                     action_arg_number_max);
10136
10137           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10138         }
10139       }
10140
10141       break;
10142     }
10143
10144     case CA_SET_PLAYER_SHIELD:
10145     {
10146       for (i = 0; i < MAX_PLAYERS; i++)
10147       {
10148         if (trigger_player_bits & (1 << i))
10149         {
10150           if (action_arg == CA_ARG_SHIELD_OFF)
10151           {
10152             stored_player[i].shield_normal_time_left = 0;
10153             stored_player[i].shield_deadly_time_left = 0;
10154           }
10155           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10156           {
10157             stored_player[i].shield_normal_time_left = 999999;
10158           }
10159           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10160           {
10161             stored_player[i].shield_normal_time_left = 999999;
10162             stored_player[i].shield_deadly_time_left = 999999;
10163           }
10164         }
10165       }
10166
10167       break;
10168     }
10169
10170     case CA_SET_PLAYER_GRAVITY:
10171     {
10172       for (i = 0; i < MAX_PLAYERS; i++)
10173       {
10174         if (trigger_player_bits & (1 << i))
10175         {
10176           stored_player[i].gravity =
10177             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10178              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10179              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10180              stored_player[i].gravity);
10181         }
10182       }
10183
10184       break;
10185     }
10186
10187     case CA_SET_PLAYER_ARTWORK:
10188     {
10189       for (i = 0; i < MAX_PLAYERS; i++)
10190       {
10191         if (trigger_player_bits & (1 << i))
10192         {
10193           int artwork_element = action_arg_element;
10194
10195           if (action_arg == CA_ARG_ELEMENT_RESET)
10196             artwork_element =
10197               (level.use_artwork_element[i] ? level.artwork_element[i] :
10198                stored_player[i].element_nr);
10199
10200           if (stored_player[i].artwork_element != artwork_element)
10201             stored_player[i].Frame = 0;
10202
10203           stored_player[i].artwork_element = artwork_element;
10204
10205           SetPlayerWaiting(&stored_player[i], FALSE);
10206
10207           // set number of special actions for bored and sleeping animation
10208           stored_player[i].num_special_action_bored =
10209             get_num_special_action(artwork_element,
10210                                    ACTION_BORING_1, ACTION_BORING_LAST);
10211           stored_player[i].num_special_action_sleeping =
10212             get_num_special_action(artwork_element,
10213                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10214         }
10215       }
10216
10217       break;
10218     }
10219
10220     case CA_SET_PLAYER_INVENTORY:
10221     {
10222       for (i = 0; i < MAX_PLAYERS; i++)
10223       {
10224         struct PlayerInfo *player = &stored_player[i];
10225         int j, k;
10226
10227         if (trigger_player_bits & (1 << i))
10228         {
10229           int inventory_element = action_arg_element;
10230
10231           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10232               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10233               action_arg == CA_ARG_ELEMENT_ACTION)
10234           {
10235             int element = inventory_element;
10236             int collect_count = element_info[element].collect_count_initial;
10237
10238             if (!IS_CUSTOM_ELEMENT(element))
10239               collect_count = 1;
10240
10241             if (collect_count == 0)
10242               player->inventory_infinite_element = element;
10243             else
10244               for (k = 0; k < collect_count; k++)
10245                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10246                   player->inventory_element[player->inventory_size++] =
10247                     element;
10248           }
10249           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10250                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10251                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10252           {
10253             if (player->inventory_infinite_element != EL_UNDEFINED &&
10254                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10255                                      action_arg_element_raw))
10256               player->inventory_infinite_element = EL_UNDEFINED;
10257
10258             for (k = 0, j = 0; j < player->inventory_size; j++)
10259             {
10260               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10261                                         action_arg_element_raw))
10262                 player->inventory_element[k++] = player->inventory_element[j];
10263             }
10264
10265             player->inventory_size = k;
10266           }
10267           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10268           {
10269             if (player->inventory_size > 0)
10270             {
10271               for (j = 0; j < player->inventory_size - 1; j++)
10272                 player->inventory_element[j] = player->inventory_element[j + 1];
10273
10274               player->inventory_size--;
10275             }
10276           }
10277           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10278           {
10279             if (player->inventory_size > 0)
10280               player->inventory_size--;
10281           }
10282           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10283           {
10284             player->inventory_infinite_element = EL_UNDEFINED;
10285             player->inventory_size = 0;
10286           }
10287           else if (action_arg == CA_ARG_INVENTORY_RESET)
10288           {
10289             player->inventory_infinite_element = EL_UNDEFINED;
10290             player->inventory_size = 0;
10291
10292             if (level.use_initial_inventory[i])
10293             {
10294               for (j = 0; j < level.initial_inventory_size[i]; j++)
10295               {
10296                 int element = level.initial_inventory_content[i][j];
10297                 int collect_count = element_info[element].collect_count_initial;
10298
10299                 if (!IS_CUSTOM_ELEMENT(element))
10300                   collect_count = 1;
10301
10302                 if (collect_count == 0)
10303                   player->inventory_infinite_element = element;
10304                 else
10305                   for (k = 0; k < collect_count; k++)
10306                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10307                       player->inventory_element[player->inventory_size++] =
10308                         element;
10309               }
10310             }
10311           }
10312         }
10313       }
10314
10315       break;
10316     }
10317
10318     // ---------- CE actions  -------------------------------------------------
10319
10320     case CA_SET_CE_VALUE:
10321     {
10322       int last_ce_value = CustomValue[x][y];
10323
10324       CustomValue[x][y] = action_arg_number_new;
10325
10326       if (CustomValue[x][y] != last_ce_value)
10327       {
10328         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10329         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10330
10331         if (CustomValue[x][y] == 0)
10332         {
10333           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10334           ChangeCount[x][y] = 0;        // allow at least one more change
10335
10336           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10337           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10338         }
10339       }
10340
10341       break;
10342     }
10343
10344     case CA_SET_CE_SCORE:
10345     {
10346       int last_ce_score = ei->collect_score;
10347
10348       ei->collect_score = action_arg_number_new;
10349
10350       if (ei->collect_score != last_ce_score)
10351       {
10352         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10353         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10354
10355         if (ei->collect_score == 0)
10356         {
10357           int xx, yy;
10358
10359           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10360           ChangeCount[x][y] = 0;        // allow at least one more change
10361
10362           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10363           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10364
10365           /*
10366             This is a very special case that seems to be a mixture between
10367             CheckElementChange() and CheckTriggeredElementChange(): while
10368             the first one only affects single elements that are triggered
10369             directly, the second one affects multiple elements in the playfield
10370             that are triggered indirectly by another element. This is a third
10371             case: Changing the CE score always affects multiple identical CEs,
10372             so every affected CE must be checked, not only the single CE for
10373             which the CE score was changed in the first place (as every instance
10374             of that CE shares the same CE score, and therefore also can change)!
10375           */
10376           SCAN_PLAYFIELD(xx, yy)
10377           {
10378             if (Tile[xx][yy] == element)
10379               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10380                                  CE_SCORE_GETS_ZERO);
10381           }
10382         }
10383       }
10384
10385       break;
10386     }
10387
10388     case CA_SET_CE_ARTWORK:
10389     {
10390       int artwork_element = action_arg_element;
10391       boolean reset_frame = FALSE;
10392       int xx, yy;
10393
10394       if (action_arg == CA_ARG_ELEMENT_RESET)
10395         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10396                            element);
10397
10398       if (ei->gfx_element != artwork_element)
10399         reset_frame = TRUE;
10400
10401       ei->gfx_element = artwork_element;
10402
10403       SCAN_PLAYFIELD(xx, yy)
10404       {
10405         if (Tile[xx][yy] == element)
10406         {
10407           if (reset_frame)
10408           {
10409             ResetGfxAnimation(xx, yy);
10410             ResetRandomAnimationValue(xx, yy);
10411           }
10412
10413           TEST_DrawLevelField(xx, yy);
10414         }
10415       }
10416
10417       break;
10418     }
10419
10420     // ---------- engine actions  ---------------------------------------------
10421
10422     case CA_SET_ENGINE_SCAN_MODE:
10423     {
10424       InitPlayfieldScanMode(action_arg);
10425
10426       break;
10427     }
10428
10429     default:
10430       break;
10431   }
10432 }
10433
10434 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10435 {
10436   int old_element = Tile[x][y];
10437   int new_element = GetElementFromGroupElement(element);
10438   int previous_move_direction = MovDir[x][y];
10439   int last_ce_value = CustomValue[x][y];
10440   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10441   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10442   boolean add_player_onto_element = (new_element_is_player &&
10443                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10444                                      IS_WALKABLE(old_element));
10445
10446   if (!add_player_onto_element)
10447   {
10448     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10449       RemoveMovingField(x, y);
10450     else
10451       RemoveField(x, y);
10452
10453     Tile[x][y] = new_element;
10454
10455     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10456       MovDir[x][y] = previous_move_direction;
10457
10458     if (element_info[new_element].use_last_ce_value)
10459       CustomValue[x][y] = last_ce_value;
10460
10461     InitField_WithBug1(x, y, FALSE);
10462
10463     new_element = Tile[x][y];   // element may have changed
10464
10465     ResetGfxAnimation(x, y);
10466     ResetRandomAnimationValue(x, y);
10467
10468     TEST_DrawLevelField(x, y);
10469
10470     if (GFX_CRUMBLED(new_element))
10471       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10472   }
10473
10474   // check if element under the player changes from accessible to unaccessible
10475   // (needed for special case of dropping element which then changes)
10476   // (must be checked after creating new element for walkable group elements)
10477   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10478       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10479   {
10480     Bang(x, y);
10481
10482     return;
10483   }
10484
10485   // "ChangeCount" not set yet to allow "entered by player" change one time
10486   if (new_element_is_player)
10487     RelocatePlayer(x, y, new_element);
10488
10489   if (is_change)
10490     ChangeCount[x][y]++;        // count number of changes in the same frame
10491
10492   TestIfBadThingTouchesPlayer(x, y);
10493   TestIfPlayerTouchesCustomElement(x, y);
10494   TestIfElementTouchesCustomElement(x, y);
10495 }
10496
10497 static void CreateField(int x, int y, int element)
10498 {
10499   CreateFieldExt(x, y, element, FALSE);
10500 }
10501
10502 static void CreateElementFromChange(int x, int y, int element)
10503 {
10504   element = GET_VALID_RUNTIME_ELEMENT(element);
10505
10506   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10507   {
10508     int old_element = Tile[x][y];
10509
10510     // prevent changed element from moving in same engine frame
10511     // unless both old and new element can either fall or move
10512     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10513         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10514       Stop[x][y] = TRUE;
10515   }
10516
10517   CreateFieldExt(x, y, element, TRUE);
10518 }
10519
10520 static boolean ChangeElement(int x, int y, int element, int page)
10521 {
10522   struct ElementInfo *ei = &element_info[element];
10523   struct ElementChangeInfo *change = &ei->change_page[page];
10524   int ce_value = CustomValue[x][y];
10525   int ce_score = ei->collect_score;
10526   int target_element;
10527   int old_element = Tile[x][y];
10528
10529   // always use default change event to prevent running into a loop
10530   if (ChangeEvent[x][y] == -1)
10531     ChangeEvent[x][y] = CE_DELAY;
10532
10533   if (ChangeEvent[x][y] == CE_DELAY)
10534   {
10535     // reset actual trigger element, trigger player and action element
10536     change->actual_trigger_element = EL_EMPTY;
10537     change->actual_trigger_player = EL_EMPTY;
10538     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10539     change->actual_trigger_side = CH_SIDE_NONE;
10540     change->actual_trigger_ce_value = 0;
10541     change->actual_trigger_ce_score = 0;
10542   }
10543
10544   // do not change elements more than a specified maximum number of changes
10545   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10546     return FALSE;
10547
10548   ChangeCount[x][y]++;          // count number of changes in the same frame
10549
10550   if (change->explode)
10551   {
10552     Bang(x, y);
10553
10554     return TRUE;
10555   }
10556
10557   if (change->use_target_content)
10558   {
10559     boolean complete_replace = TRUE;
10560     boolean can_replace[3][3];
10561     int xx, yy;
10562
10563     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10564     {
10565       boolean is_empty;
10566       boolean is_walkable;
10567       boolean is_diggable;
10568       boolean is_collectible;
10569       boolean is_removable;
10570       boolean is_destructible;
10571       int ex = x + xx - 1;
10572       int ey = y + yy - 1;
10573       int content_element = change->target_content.e[xx][yy];
10574       int e;
10575
10576       can_replace[xx][yy] = TRUE;
10577
10578       if (ex == x && ey == y)   // do not check changing element itself
10579         continue;
10580
10581       if (content_element == EL_EMPTY_SPACE)
10582       {
10583         can_replace[xx][yy] = FALSE;    // do not replace border with space
10584
10585         continue;
10586       }
10587
10588       if (!IN_LEV_FIELD(ex, ey))
10589       {
10590         can_replace[xx][yy] = FALSE;
10591         complete_replace = FALSE;
10592
10593         continue;
10594       }
10595
10596       e = Tile[ex][ey];
10597
10598       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10599         e = MovingOrBlocked2Element(ex, ey);
10600
10601       is_empty = (IS_FREE(ex, ey) ||
10602                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10603
10604       is_walkable     = (is_empty || IS_WALKABLE(e));
10605       is_diggable     = (is_empty || IS_DIGGABLE(e));
10606       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10607       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10608       is_removable    = (is_diggable || is_collectible);
10609
10610       can_replace[xx][yy] =
10611         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10612           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10613           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10614           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10615           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10616           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10617          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10618
10619       if (!can_replace[xx][yy])
10620         complete_replace = FALSE;
10621     }
10622
10623     if (!change->only_if_complete || complete_replace)
10624     {
10625       boolean something_has_changed = FALSE;
10626
10627       if (change->only_if_complete && change->use_random_replace &&
10628           RND(100) < change->random_percentage)
10629         return FALSE;
10630
10631       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10632       {
10633         int ex = x + xx - 1;
10634         int ey = y + yy - 1;
10635         int content_element;
10636
10637         if (can_replace[xx][yy] && (!change->use_random_replace ||
10638                                     RND(100) < change->random_percentage))
10639         {
10640           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10641             RemoveMovingField(ex, ey);
10642
10643           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10644
10645           content_element = change->target_content.e[xx][yy];
10646           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10647                                               ce_value, ce_score);
10648
10649           CreateElementFromChange(ex, ey, target_element);
10650
10651           something_has_changed = TRUE;
10652
10653           // for symmetry reasons, freeze newly created border elements
10654           if (ex != x || ey != y)
10655             Stop[ex][ey] = TRUE;        // no more moving in this frame
10656         }
10657       }
10658
10659       if (something_has_changed)
10660       {
10661         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10662         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10663       }
10664     }
10665   }
10666   else
10667   {
10668     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10669                                         ce_value, ce_score);
10670
10671     if (element == EL_DIAGONAL_GROWING ||
10672         element == EL_DIAGONAL_SHRINKING)
10673     {
10674       target_element = Store[x][y];
10675
10676       Store[x][y] = EL_EMPTY;
10677     }
10678
10679     CreateElementFromChange(x, y, target_element);
10680
10681     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10682     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10683   }
10684
10685   // this uses direct change before indirect change
10686   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10687
10688   return TRUE;
10689 }
10690
10691 static void HandleElementChange(int x, int y, int page)
10692 {
10693   int element = MovingOrBlocked2Element(x, y);
10694   struct ElementInfo *ei = &element_info[element];
10695   struct ElementChangeInfo *change = &ei->change_page[page];
10696   boolean handle_action_before_change = FALSE;
10697
10698 #ifdef DEBUG
10699   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10700       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10701   {
10702     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10703           x, y, element, element_info[element].token_name);
10704     Debug("game:playing:HandleElementChange", "This should never happen!");
10705   }
10706 #endif
10707
10708   // this can happen with classic bombs on walkable, changing elements
10709   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10710   {
10711     return;
10712   }
10713
10714   if (ChangeDelay[x][y] == 0)           // initialize element change
10715   {
10716     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10717
10718     if (change->can_change)
10719     {
10720       // !!! not clear why graphic animation should be reset at all here !!!
10721       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10722       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10723
10724       /*
10725         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10726
10727         When using an animation frame delay of 1 (this only happens with
10728         "sp_zonk.moving.left/right" in the classic graphics), the default
10729         (non-moving) animation shows wrong animation frames (while the
10730         moving animation, like "sp_zonk.moving.left/right", is correct,
10731         so this graphical bug never shows up with the classic graphics).
10732         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10733         be drawn instead of the correct frames 0,1,2,3. This is caused by
10734         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10735         an element change: First when the change delay ("ChangeDelay[][]")
10736         counter has reached zero after decrementing, then a second time in
10737         the next frame (after "GfxFrame[][]" was already incremented) when
10738         "ChangeDelay[][]" is reset to the initial delay value again.
10739
10740         This causes frame 0 to be drawn twice, while the last frame won't
10741         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10742
10743         As some animations may already be cleverly designed around this bug
10744         (at least the "Snake Bite" snake tail animation does this), it cannot
10745         simply be fixed here without breaking such existing animations.
10746         Unfortunately, it cannot easily be detected if a graphics set was
10747         designed "before" or "after" the bug was fixed. As a workaround,
10748         a new graphics set option "game.graphics_engine_version" was added
10749         to be able to specify the game's major release version for which the
10750         graphics set was designed, which can then be used to decide if the
10751         bugfix should be used (version 4 and above) or not (version 3 or
10752         below, or if no version was specified at all, as with old sets).
10753
10754         (The wrong/fixed animation frames can be tested with the test level set
10755         "test_gfxframe" and level "000", which contains a specially prepared
10756         custom element at level position (x/y) == (11/9) which uses the zonk
10757         animation mentioned above. Using "game.graphics_engine_version: 4"
10758         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10759         This can also be seen from the debug output for this test element.)
10760       */
10761
10762       // when a custom element is about to change (for example by change delay),
10763       // do not reset graphic animation when the custom element is moving
10764       if (game.graphics_engine_version < 4 &&
10765           !IS_MOVING(x, y))
10766       {
10767         ResetGfxAnimation(x, y);
10768         ResetRandomAnimationValue(x, y);
10769       }
10770
10771       if (change->pre_change_function)
10772         change->pre_change_function(x, y);
10773     }
10774   }
10775
10776   ChangeDelay[x][y]--;
10777
10778   if (ChangeDelay[x][y] != 0)           // continue element change
10779   {
10780     if (change->can_change)
10781     {
10782       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10783
10784       if (IS_ANIMATED(graphic))
10785         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10786
10787       if (change->change_function)
10788         change->change_function(x, y);
10789     }
10790   }
10791   else                                  // finish element change
10792   {
10793     if (ChangePage[x][y] != -1)         // remember page from delayed change
10794     {
10795       page = ChangePage[x][y];
10796       ChangePage[x][y] = -1;
10797
10798       change = &ei->change_page[page];
10799     }
10800
10801     if (IS_MOVING(x, y))                // never change a running system ;-)
10802     {
10803       ChangeDelay[x][y] = 1;            // try change after next move step
10804       ChangePage[x][y] = page;          // remember page to use for change
10805
10806       return;
10807     }
10808
10809     // special case: set new level random seed before changing element
10810     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10811       handle_action_before_change = TRUE;
10812
10813     if (change->has_action && handle_action_before_change)
10814       ExecuteCustomElementAction(x, y, element, page);
10815
10816     if (change->can_change)
10817     {
10818       if (ChangeElement(x, y, element, page))
10819       {
10820         if (change->post_change_function)
10821           change->post_change_function(x, y);
10822       }
10823     }
10824
10825     if (change->has_action && !handle_action_before_change)
10826       ExecuteCustomElementAction(x, y, element, page);
10827   }
10828 }
10829
10830 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10831                                               int trigger_element,
10832                                               int trigger_event,
10833                                               int trigger_player,
10834                                               int trigger_side,
10835                                               int trigger_page)
10836 {
10837   boolean change_done_any = FALSE;
10838   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10839   int i;
10840
10841   if (!(trigger_events[trigger_element][trigger_event]))
10842     return FALSE;
10843
10844   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10845
10846   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10847   {
10848     int element = EL_CUSTOM_START + i;
10849     boolean change_done = FALSE;
10850     int p;
10851
10852     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10853         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10854       continue;
10855
10856     for (p = 0; p < element_info[element].num_change_pages; p++)
10857     {
10858       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10859
10860       if (change->can_change_or_has_action &&
10861           change->has_event[trigger_event] &&
10862           change->trigger_side & trigger_side &&
10863           change->trigger_player & trigger_player &&
10864           change->trigger_page & trigger_page_bits &&
10865           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10866       {
10867         change->actual_trigger_element = trigger_element;
10868         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10869         change->actual_trigger_player_bits = trigger_player;
10870         change->actual_trigger_side = trigger_side;
10871         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10872         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10873
10874         if ((change->can_change && !change_done) || change->has_action)
10875         {
10876           int x, y;
10877
10878           SCAN_PLAYFIELD(x, y)
10879           {
10880             if (Tile[x][y] == element)
10881             {
10882               if (change->can_change && !change_done)
10883               {
10884                 // if element already changed in this frame, not only prevent
10885                 // another element change (checked in ChangeElement()), but
10886                 // also prevent additional element actions for this element
10887
10888                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10889                     !level.use_action_after_change_bug)
10890                   continue;
10891
10892                 ChangeDelay[x][y] = 1;
10893                 ChangeEvent[x][y] = trigger_event;
10894
10895                 HandleElementChange(x, y, p);
10896               }
10897               else if (change->has_action)
10898               {
10899                 // if element already changed in this frame, not only prevent
10900                 // another element change (checked in ChangeElement()), but
10901                 // also prevent additional element actions for this element
10902
10903                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10904                     !level.use_action_after_change_bug)
10905                   continue;
10906
10907                 ExecuteCustomElementAction(x, y, element, p);
10908                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10909               }
10910             }
10911           }
10912
10913           if (change->can_change)
10914           {
10915             change_done = TRUE;
10916             change_done_any = TRUE;
10917           }
10918         }
10919       }
10920     }
10921   }
10922
10923   RECURSION_LOOP_DETECTION_END();
10924
10925   return change_done_any;
10926 }
10927
10928 static boolean CheckElementChangeExt(int x, int y,
10929                                      int element,
10930                                      int trigger_element,
10931                                      int trigger_event,
10932                                      int trigger_player,
10933                                      int trigger_side)
10934 {
10935   boolean change_done = FALSE;
10936   int p;
10937
10938   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10939       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10940     return FALSE;
10941
10942   if (Tile[x][y] == EL_BLOCKED)
10943   {
10944     Blocked2Moving(x, y, &x, &y);
10945     element = Tile[x][y];
10946   }
10947
10948   // check if element has already changed or is about to change after moving
10949   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10950        Tile[x][y] != element) ||
10951
10952       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10953        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10954         ChangePage[x][y] != -1)))
10955     return FALSE;
10956
10957   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10958
10959   for (p = 0; p < element_info[element].num_change_pages; p++)
10960   {
10961     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10962
10963     /* check trigger element for all events where the element that is checked
10964        for changing interacts with a directly adjacent element -- this is
10965        different to element changes that affect other elements to change on the
10966        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10967     boolean check_trigger_element =
10968       (trigger_event == CE_TOUCHING_X ||
10969        trigger_event == CE_HITTING_X ||
10970        trigger_event == CE_HIT_BY_X ||
10971        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10972
10973     if (change->can_change_or_has_action &&
10974         change->has_event[trigger_event] &&
10975         change->trigger_side & trigger_side &&
10976         change->trigger_player & trigger_player &&
10977         (!check_trigger_element ||
10978          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10979     {
10980       change->actual_trigger_element = trigger_element;
10981       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10982       change->actual_trigger_player_bits = trigger_player;
10983       change->actual_trigger_side = trigger_side;
10984       change->actual_trigger_ce_value = CustomValue[x][y];
10985       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10986
10987       // special case: trigger element not at (x,y) position for some events
10988       if (check_trigger_element)
10989       {
10990         static struct
10991         {
10992           int dx, dy;
10993         } move_xy[] =
10994           {
10995             {  0,  0 },
10996             { -1,  0 },
10997             { +1,  0 },
10998             {  0,  0 },
10999             {  0, -1 },
11000             {  0,  0 }, { 0, 0 }, { 0, 0 },
11001             {  0, +1 }
11002           };
11003
11004         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11005         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11006
11007         change->actual_trigger_ce_value = CustomValue[xx][yy];
11008         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11009       }
11010
11011       if (change->can_change && !change_done)
11012       {
11013         ChangeDelay[x][y] = 1;
11014         ChangeEvent[x][y] = trigger_event;
11015
11016         HandleElementChange(x, y, p);
11017
11018         change_done = TRUE;
11019       }
11020       else if (change->has_action)
11021       {
11022         ExecuteCustomElementAction(x, y, element, p);
11023         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11024       }
11025     }
11026   }
11027
11028   RECURSION_LOOP_DETECTION_END();
11029
11030   return change_done;
11031 }
11032
11033 static void PlayPlayerSound(struct PlayerInfo *player)
11034 {
11035   int jx = player->jx, jy = player->jy;
11036   int sound_element = player->artwork_element;
11037   int last_action = player->last_action_waiting;
11038   int action = player->action_waiting;
11039
11040   if (player->is_waiting)
11041   {
11042     if (action != last_action)
11043       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11044     else
11045       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11046   }
11047   else
11048   {
11049     if (action != last_action)
11050       StopSound(element_info[sound_element].sound[last_action]);
11051
11052     if (last_action == ACTION_SLEEPING)
11053       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11054   }
11055 }
11056
11057 static void PlayAllPlayersSound(void)
11058 {
11059   int i;
11060
11061   for (i = 0; i < MAX_PLAYERS; i++)
11062     if (stored_player[i].active)
11063       PlayPlayerSound(&stored_player[i]);
11064 }
11065
11066 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11067 {
11068   boolean last_waiting = player->is_waiting;
11069   int move_dir = player->MovDir;
11070
11071   player->dir_waiting = move_dir;
11072   player->last_action_waiting = player->action_waiting;
11073
11074   if (is_waiting)
11075   {
11076     if (!last_waiting)          // not waiting -> waiting
11077     {
11078       player->is_waiting = TRUE;
11079
11080       player->frame_counter_bored =
11081         FrameCounter +
11082         game.player_boring_delay_fixed +
11083         GetSimpleRandom(game.player_boring_delay_random);
11084       player->frame_counter_sleeping =
11085         FrameCounter +
11086         game.player_sleeping_delay_fixed +
11087         GetSimpleRandom(game.player_sleeping_delay_random);
11088
11089       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11090     }
11091
11092     if (game.player_sleeping_delay_fixed +
11093         game.player_sleeping_delay_random > 0 &&
11094         player->anim_delay_counter == 0 &&
11095         player->post_delay_counter == 0 &&
11096         FrameCounter >= player->frame_counter_sleeping)
11097       player->is_sleeping = TRUE;
11098     else if (game.player_boring_delay_fixed +
11099              game.player_boring_delay_random > 0 &&
11100              FrameCounter >= player->frame_counter_bored)
11101       player->is_bored = TRUE;
11102
11103     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11104                               player->is_bored ? ACTION_BORING :
11105                               ACTION_WAITING);
11106
11107     if (player->is_sleeping && player->use_murphy)
11108     {
11109       // special case for sleeping Murphy when leaning against non-free tile
11110
11111       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11112           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11113            !IS_MOVING(player->jx - 1, player->jy)))
11114         move_dir = MV_LEFT;
11115       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11116                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11117                 !IS_MOVING(player->jx + 1, player->jy)))
11118         move_dir = MV_RIGHT;
11119       else
11120         player->is_sleeping = FALSE;
11121
11122       player->dir_waiting = move_dir;
11123     }
11124
11125     if (player->is_sleeping)
11126     {
11127       if (player->num_special_action_sleeping > 0)
11128       {
11129         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11130         {
11131           int last_special_action = player->special_action_sleeping;
11132           int num_special_action = player->num_special_action_sleeping;
11133           int special_action =
11134             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11135              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11136              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11137              last_special_action + 1 : ACTION_SLEEPING);
11138           int special_graphic =
11139             el_act_dir2img(player->artwork_element, special_action, move_dir);
11140
11141           player->anim_delay_counter =
11142             graphic_info[special_graphic].anim_delay_fixed +
11143             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11144           player->post_delay_counter =
11145             graphic_info[special_graphic].post_delay_fixed +
11146             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11147
11148           player->special_action_sleeping = special_action;
11149         }
11150
11151         if (player->anim_delay_counter > 0)
11152         {
11153           player->action_waiting = player->special_action_sleeping;
11154           player->anim_delay_counter--;
11155         }
11156         else if (player->post_delay_counter > 0)
11157         {
11158           player->post_delay_counter--;
11159         }
11160       }
11161     }
11162     else if (player->is_bored)
11163     {
11164       if (player->num_special_action_bored > 0)
11165       {
11166         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11167         {
11168           int special_action =
11169             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11170           int special_graphic =
11171             el_act_dir2img(player->artwork_element, special_action, move_dir);
11172
11173           player->anim_delay_counter =
11174             graphic_info[special_graphic].anim_delay_fixed +
11175             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11176           player->post_delay_counter =
11177             graphic_info[special_graphic].post_delay_fixed +
11178             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11179
11180           player->special_action_bored = special_action;
11181         }
11182
11183         if (player->anim_delay_counter > 0)
11184         {
11185           player->action_waiting = player->special_action_bored;
11186           player->anim_delay_counter--;
11187         }
11188         else if (player->post_delay_counter > 0)
11189         {
11190           player->post_delay_counter--;
11191         }
11192       }
11193     }
11194   }
11195   else if (last_waiting)        // waiting -> not waiting
11196   {
11197     player->is_waiting = FALSE;
11198     player->is_bored = FALSE;
11199     player->is_sleeping = FALSE;
11200
11201     player->frame_counter_bored = -1;
11202     player->frame_counter_sleeping = -1;
11203
11204     player->anim_delay_counter = 0;
11205     player->post_delay_counter = 0;
11206
11207     player->dir_waiting = player->MovDir;
11208     player->action_waiting = ACTION_DEFAULT;
11209
11210     player->special_action_bored = ACTION_DEFAULT;
11211     player->special_action_sleeping = ACTION_DEFAULT;
11212   }
11213 }
11214
11215 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11216 {
11217   if ((!player->is_moving  && player->was_moving) ||
11218       (player->MovPos == 0 && player->was_moving) ||
11219       (player->is_snapping && !player->was_snapping) ||
11220       (player->is_dropping && !player->was_dropping))
11221   {
11222     if (!CheckSaveEngineSnapshotToList())
11223       return;
11224
11225     player->was_moving = FALSE;
11226     player->was_snapping = TRUE;
11227     player->was_dropping = TRUE;
11228   }
11229   else
11230   {
11231     if (player->is_moving)
11232       player->was_moving = TRUE;
11233
11234     if (!player->is_snapping)
11235       player->was_snapping = FALSE;
11236
11237     if (!player->is_dropping)
11238       player->was_dropping = FALSE;
11239   }
11240
11241   static struct MouseActionInfo mouse_action_last = { 0 };
11242   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11243   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11244
11245   if (new_released)
11246     CheckSaveEngineSnapshotToList();
11247
11248   mouse_action_last = mouse_action;
11249 }
11250
11251 static void CheckSingleStepMode(struct PlayerInfo *player)
11252 {
11253   if (tape.single_step && tape.recording && !tape.pausing)
11254   {
11255     /* as it is called "single step mode", just return to pause mode when the
11256        player stopped moving after one tile (or never starts moving at all) */
11257     if (!player->is_moving &&
11258         !player->is_pushing &&
11259         !player->is_dropping_pressed &&
11260         !player->effective_mouse_action.button)
11261       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11262   }
11263
11264   CheckSaveEngineSnapshot(player);
11265 }
11266
11267 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11268 {
11269   int left      = player_action & JOY_LEFT;
11270   int right     = player_action & JOY_RIGHT;
11271   int up        = player_action & JOY_UP;
11272   int down      = player_action & JOY_DOWN;
11273   int button1   = player_action & JOY_BUTTON_1;
11274   int button2   = player_action & JOY_BUTTON_2;
11275   int dx        = (left ? -1 : right ? 1 : 0);
11276   int dy        = (up   ? -1 : down  ? 1 : 0);
11277
11278   if (!player->active || tape.pausing)
11279     return 0;
11280
11281   if (player_action)
11282   {
11283     if (button1)
11284       SnapField(player, dx, dy);
11285     else
11286     {
11287       if (button2)
11288         DropElement(player);
11289
11290       MovePlayer(player, dx, dy);
11291     }
11292
11293     CheckSingleStepMode(player);
11294
11295     SetPlayerWaiting(player, FALSE);
11296
11297     return player_action;
11298   }
11299   else
11300   {
11301     // no actions for this player (no input at player's configured device)
11302
11303     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11304     SnapField(player, 0, 0);
11305     CheckGravityMovementWhenNotMoving(player);
11306
11307     if (player->MovPos == 0)
11308       SetPlayerWaiting(player, TRUE);
11309
11310     if (player->MovPos == 0)    // needed for tape.playing
11311       player->is_moving = FALSE;
11312
11313     player->is_dropping = FALSE;
11314     player->is_dropping_pressed = FALSE;
11315     player->drop_pressed_delay = 0;
11316
11317     CheckSingleStepMode(player);
11318
11319     return 0;
11320   }
11321 }
11322
11323 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11324                                          byte *tape_action)
11325 {
11326   if (!tape.use_mouse_actions)
11327     return;
11328
11329   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11330   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11331   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11332 }
11333
11334 static void SetTapeActionFromMouseAction(byte *tape_action,
11335                                          struct MouseActionInfo *mouse_action)
11336 {
11337   if (!tape.use_mouse_actions)
11338     return;
11339
11340   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11341   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11342   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11343 }
11344
11345 static void CheckLevelSolved(void)
11346 {
11347   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11348   {
11349     if (game_em.level_solved &&
11350         !game_em.game_over)                             // game won
11351     {
11352       LevelSolved();
11353
11354       game_em.game_over = TRUE;
11355
11356       game.all_players_gone = TRUE;
11357     }
11358
11359     if (game_em.game_over)                              // game lost
11360       game.all_players_gone = TRUE;
11361   }
11362   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11363   {
11364     if (game_sp.level_solved &&
11365         !game_sp.game_over)                             // game won
11366     {
11367       LevelSolved();
11368
11369       game_sp.game_over = TRUE;
11370
11371       game.all_players_gone = TRUE;
11372     }
11373
11374     if (game_sp.game_over)                              // game lost
11375       game.all_players_gone = TRUE;
11376   }
11377   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11378   {
11379     if (game_mm.level_solved &&
11380         !game_mm.game_over)                             // game won
11381     {
11382       LevelSolved();
11383
11384       game_mm.game_over = TRUE;
11385
11386       game.all_players_gone = TRUE;
11387     }
11388
11389     if (game_mm.game_over)                              // game lost
11390       game.all_players_gone = TRUE;
11391   }
11392 }
11393
11394 static void CheckLevelTime(void)
11395 {
11396   int i;
11397
11398   if (TimeFrames >= FRAMES_PER_SECOND)
11399   {
11400     TimeFrames = 0;
11401     TapeTime++;
11402
11403     for (i = 0; i < MAX_PLAYERS; i++)
11404     {
11405       struct PlayerInfo *player = &stored_player[i];
11406
11407       if (SHIELD_ON(player))
11408       {
11409         player->shield_normal_time_left--;
11410
11411         if (player->shield_deadly_time_left > 0)
11412           player->shield_deadly_time_left--;
11413       }
11414     }
11415
11416     if (!game.LevelSolved && !level.use_step_counter)
11417     {
11418       TimePlayed++;
11419
11420       if (TimeLeft > 0)
11421       {
11422         TimeLeft--;
11423
11424         if (TimeLeft <= 10 && setup.time_limit)
11425           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11426
11427         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11428            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11429
11430         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11431
11432         if (!TimeLeft && setup.time_limit)
11433         {
11434           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11435             game_em.lev->killed_out_of_time = TRUE;
11436           else
11437             for (i = 0; i < MAX_PLAYERS; i++)
11438               KillPlayer(&stored_player[i]);
11439         }
11440       }
11441       else if (game.no_time_limit && !game.all_players_gone)
11442       {
11443         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11444       }
11445
11446       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11447     }
11448
11449     if (tape.recording || tape.playing)
11450       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11451   }
11452
11453   if (tape.recording || tape.playing)
11454     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11455
11456   UpdateAndDisplayGameControlValues();
11457 }
11458
11459 void AdvanceFrameAndPlayerCounters(int player_nr)
11460 {
11461   int i;
11462
11463   // advance frame counters (global frame counter and time frame counter)
11464   FrameCounter++;
11465   TimeFrames++;
11466
11467   // advance player counters (counters for move delay, move animation etc.)
11468   for (i = 0; i < MAX_PLAYERS; i++)
11469   {
11470     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11471     int move_delay_value = stored_player[i].move_delay_value;
11472     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11473
11474     if (!advance_player_counters)       // not all players may be affected
11475       continue;
11476
11477     if (move_frames == 0)       // less than one move per game frame
11478     {
11479       int stepsize = TILEX / move_delay_value;
11480       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11481       int count = (stored_player[i].is_moving ?
11482                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11483
11484       if (count % delay == 0)
11485         move_frames = 1;
11486     }
11487
11488     stored_player[i].Frame += move_frames;
11489
11490     if (stored_player[i].MovPos != 0)
11491       stored_player[i].StepFrame += move_frames;
11492
11493     if (stored_player[i].move_delay > 0)
11494       stored_player[i].move_delay--;
11495
11496     // due to bugs in previous versions, counter must count up, not down
11497     if (stored_player[i].push_delay != -1)
11498       stored_player[i].push_delay++;
11499
11500     if (stored_player[i].drop_delay > 0)
11501       stored_player[i].drop_delay--;
11502
11503     if (stored_player[i].is_dropping_pressed)
11504       stored_player[i].drop_pressed_delay++;
11505   }
11506 }
11507
11508 void StartGameActions(boolean init_network_game, boolean record_tape,
11509                       int random_seed)
11510 {
11511   unsigned int new_random_seed = InitRND(random_seed);
11512
11513   if (record_tape)
11514     TapeStartRecording(new_random_seed);
11515
11516   if (init_network_game)
11517   {
11518     SendToServer_LevelFile();
11519     SendToServer_StartPlaying();
11520
11521     return;
11522   }
11523
11524   InitGame();
11525 }
11526
11527 static void GameActionsExt(void)
11528 {
11529 #if 0
11530   static unsigned int game_frame_delay = 0;
11531 #endif
11532   unsigned int game_frame_delay_value;
11533   byte *recorded_player_action;
11534   byte summarized_player_action = 0;
11535   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11536   int i;
11537
11538   // detect endless loops, caused by custom element programming
11539   if (recursion_loop_detected && recursion_loop_depth == 0)
11540   {
11541     char *message = getStringCat3("Internal Error! Element ",
11542                                   EL_NAME(recursion_loop_element),
11543                                   " caused endless loop! Quit the game?");
11544
11545     Warn("element '%s' caused endless loop in game engine",
11546          EL_NAME(recursion_loop_element));
11547
11548     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11549
11550     recursion_loop_detected = FALSE;    // if game should be continued
11551
11552     free(message);
11553
11554     return;
11555   }
11556
11557   if (game.restart_level)
11558     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11559
11560   CheckLevelSolved();
11561
11562   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11563     GameWon();
11564
11565   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11566     TapeStop();
11567
11568   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11569     return;
11570
11571   game_frame_delay_value =
11572     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11573
11574   if (tape.playing && tape.warp_forward && !tape.pausing)
11575     game_frame_delay_value = 0;
11576
11577   SetVideoFrameDelay(game_frame_delay_value);
11578
11579   // (de)activate virtual buttons depending on current game status
11580   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11581   {
11582     if (game.all_players_gone)  // if no players there to be controlled anymore
11583       SetOverlayActive(FALSE);
11584     else if (!tape.playing)     // if game continues after tape stopped playing
11585       SetOverlayActive(TRUE);
11586   }
11587
11588 #if 0
11589 #if 0
11590   // ---------- main game synchronization point ----------
11591
11592   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11593
11594   Debug("game:playing:skip", "skip == %d", skip);
11595
11596 #else
11597   // ---------- main game synchronization point ----------
11598
11599   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11600 #endif
11601 #endif
11602
11603   if (network_playing && !network_player_action_received)
11604   {
11605     // try to get network player actions in time
11606
11607     // last chance to get network player actions without main loop delay
11608     HandleNetworking();
11609
11610     // game was quit by network peer
11611     if (game_status != GAME_MODE_PLAYING)
11612       return;
11613
11614     // check if network player actions still missing and game still running
11615     if (!network_player_action_received && !checkGameEnded())
11616       return;           // failed to get network player actions in time
11617
11618     // do not yet reset "network_player_action_received" (for tape.pausing)
11619   }
11620
11621   if (tape.pausing)
11622     return;
11623
11624   // at this point we know that we really continue executing the game
11625
11626   network_player_action_received = FALSE;
11627
11628   // when playing tape, read previously recorded player input from tape data
11629   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11630
11631   local_player->effective_mouse_action = local_player->mouse_action;
11632
11633   if (recorded_player_action != NULL)
11634     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11635                                  recorded_player_action);
11636
11637   // TapePlayAction() may return NULL when toggling to "pause before death"
11638   if (tape.pausing)
11639     return;
11640
11641   if (tape.set_centered_player)
11642   {
11643     game.centered_player_nr_next = tape.centered_player_nr_next;
11644     game.set_centered_player = TRUE;
11645   }
11646
11647   for (i = 0; i < MAX_PLAYERS; i++)
11648   {
11649     summarized_player_action |= stored_player[i].action;
11650
11651     if (!network_playing && (game.team_mode || tape.playing))
11652       stored_player[i].effective_action = stored_player[i].action;
11653   }
11654
11655   if (network_playing && !checkGameEnded())
11656     SendToServer_MovePlayer(summarized_player_action);
11657
11658   // summarize all actions at local players mapped input device position
11659   // (this allows using different input devices in single player mode)
11660   if (!network.enabled && !game.team_mode)
11661     stored_player[map_player_action[local_player->index_nr]].effective_action =
11662       summarized_player_action;
11663
11664   // summarize all actions at centered player in local team mode
11665   if (tape.recording &&
11666       setup.team_mode && !network.enabled &&
11667       setup.input_on_focus &&
11668       game.centered_player_nr != -1)
11669   {
11670     for (i = 0; i < MAX_PLAYERS; i++)
11671       stored_player[map_player_action[i]].effective_action =
11672         (i == game.centered_player_nr ? summarized_player_action : 0);
11673   }
11674
11675   if (recorded_player_action != NULL)
11676     for (i = 0; i < MAX_PLAYERS; i++)
11677       stored_player[i].effective_action = recorded_player_action[i];
11678
11679   for (i = 0; i < MAX_PLAYERS; i++)
11680   {
11681     tape_action[i] = stored_player[i].effective_action;
11682
11683     /* (this may happen in the RND game engine if a player was not present on
11684        the playfield on level start, but appeared later from a custom element */
11685     if (setup.team_mode &&
11686         tape.recording &&
11687         tape_action[i] &&
11688         !tape.player_participates[i])
11689       tape.player_participates[i] = TRUE;
11690   }
11691
11692   SetTapeActionFromMouseAction(tape_action,
11693                                &local_player->effective_mouse_action);
11694
11695   // only record actions from input devices, but not programmed actions
11696   if (tape.recording)
11697     TapeRecordAction(tape_action);
11698
11699   // remember if game was played (especially after tape stopped playing)
11700   if (!tape.playing && summarized_player_action)
11701     game.GamePlayed = TRUE;
11702
11703 #if USE_NEW_PLAYER_ASSIGNMENTS
11704   // !!! also map player actions in single player mode !!!
11705   // if (game.team_mode)
11706   if (1)
11707   {
11708     byte mapped_action[MAX_PLAYERS];
11709
11710 #if DEBUG_PLAYER_ACTIONS
11711     for (i = 0; i < MAX_PLAYERS; i++)
11712       DebugContinued("", "%d, ", stored_player[i].effective_action);
11713 #endif
11714
11715     for (i = 0; i < MAX_PLAYERS; i++)
11716       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11717
11718     for (i = 0; i < MAX_PLAYERS; i++)
11719       stored_player[i].effective_action = mapped_action[i];
11720
11721 #if DEBUG_PLAYER_ACTIONS
11722     DebugContinued("", "=> ");
11723     for (i = 0; i < MAX_PLAYERS; i++)
11724       DebugContinued("", "%d, ", stored_player[i].effective_action);
11725     DebugContinued("game:playing:player", "\n");
11726 #endif
11727   }
11728 #if DEBUG_PLAYER_ACTIONS
11729   else
11730   {
11731     for (i = 0; i < MAX_PLAYERS; i++)
11732       DebugContinued("", "%d, ", stored_player[i].effective_action);
11733     DebugContinued("game:playing:player", "\n");
11734   }
11735 #endif
11736 #endif
11737
11738   for (i = 0; i < MAX_PLAYERS; i++)
11739   {
11740     // allow engine snapshot in case of changed movement attempt
11741     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11742         (stored_player[i].effective_action & KEY_MOTION))
11743       game.snapshot.changed_action = TRUE;
11744
11745     // allow engine snapshot in case of snapping/dropping attempt
11746     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11747         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11748       game.snapshot.changed_action = TRUE;
11749
11750     game.snapshot.last_action[i] = stored_player[i].effective_action;
11751   }
11752
11753   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11754   {
11755     GameActions_EM_Main();
11756   }
11757   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11758   {
11759     GameActions_SP_Main();
11760   }
11761   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11762   {
11763     GameActions_MM_Main();
11764   }
11765   else
11766   {
11767     GameActions_RND_Main();
11768   }
11769
11770   BlitScreenToBitmap(backbuffer);
11771
11772   CheckLevelSolved();
11773   CheckLevelTime();
11774
11775   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11776
11777   if (global.show_frames_per_second)
11778   {
11779     static unsigned int fps_counter = 0;
11780     static int fps_frames = 0;
11781     unsigned int fps_delay_ms = Counter() - fps_counter;
11782
11783     fps_frames++;
11784
11785     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11786     {
11787       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11788
11789       fps_frames = 0;
11790       fps_counter = Counter();
11791
11792       // always draw FPS to screen after FPS value was updated
11793       redraw_mask |= REDRAW_FPS;
11794     }
11795
11796     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11797     if (GetDrawDeactivationMask() == REDRAW_NONE)
11798       redraw_mask |= REDRAW_FPS;
11799   }
11800 }
11801
11802 static void GameActions_CheckSaveEngineSnapshot(void)
11803 {
11804   if (!game.snapshot.save_snapshot)
11805     return;
11806
11807   // clear flag for saving snapshot _before_ saving snapshot
11808   game.snapshot.save_snapshot = FALSE;
11809
11810   SaveEngineSnapshotToList();
11811 }
11812
11813 void GameActions(void)
11814 {
11815   GameActionsExt();
11816
11817   GameActions_CheckSaveEngineSnapshot();
11818 }
11819
11820 void GameActions_EM_Main(void)
11821 {
11822   byte effective_action[MAX_PLAYERS];
11823   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11824   int i;
11825
11826   for (i = 0; i < MAX_PLAYERS; i++)
11827     effective_action[i] = stored_player[i].effective_action;
11828
11829   GameActions_EM(effective_action, warp_mode);
11830 }
11831
11832 void GameActions_SP_Main(void)
11833 {
11834   byte effective_action[MAX_PLAYERS];
11835   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11836   int i;
11837
11838   for (i = 0; i < MAX_PLAYERS; i++)
11839     effective_action[i] = stored_player[i].effective_action;
11840
11841   GameActions_SP(effective_action, warp_mode);
11842
11843   for (i = 0; i < MAX_PLAYERS; i++)
11844   {
11845     if (stored_player[i].force_dropping)
11846       stored_player[i].action |= KEY_BUTTON_DROP;
11847
11848     stored_player[i].force_dropping = FALSE;
11849   }
11850 }
11851
11852 void GameActions_MM_Main(void)
11853 {
11854   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11855
11856   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11857 }
11858
11859 void GameActions_RND_Main(void)
11860 {
11861   GameActions_RND();
11862 }
11863
11864 void GameActions_RND(void)
11865 {
11866   static struct MouseActionInfo mouse_action_last = { 0 };
11867   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11868   int magic_wall_x = 0, magic_wall_y = 0;
11869   int i, x, y, element, graphic, last_gfx_frame;
11870
11871   InitPlayfieldScanModeVars();
11872
11873   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11874   {
11875     SCAN_PLAYFIELD(x, y)
11876     {
11877       ChangeCount[x][y] = 0;
11878       ChangeEvent[x][y] = -1;
11879     }
11880   }
11881
11882   if (game.set_centered_player)
11883   {
11884     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11885
11886     // switching to "all players" only possible if all players fit to screen
11887     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11888     {
11889       game.centered_player_nr_next = game.centered_player_nr;
11890       game.set_centered_player = FALSE;
11891     }
11892
11893     // do not switch focus to non-existing (or non-active) player
11894     if (game.centered_player_nr_next >= 0 &&
11895         !stored_player[game.centered_player_nr_next].active)
11896     {
11897       game.centered_player_nr_next = game.centered_player_nr;
11898       game.set_centered_player = FALSE;
11899     }
11900   }
11901
11902   if (game.set_centered_player &&
11903       ScreenMovPos == 0)        // screen currently aligned at tile position
11904   {
11905     int sx, sy;
11906
11907     if (game.centered_player_nr_next == -1)
11908     {
11909       setScreenCenteredToAllPlayers(&sx, &sy);
11910     }
11911     else
11912     {
11913       sx = stored_player[game.centered_player_nr_next].jx;
11914       sy = stored_player[game.centered_player_nr_next].jy;
11915     }
11916
11917     game.centered_player_nr = game.centered_player_nr_next;
11918     game.set_centered_player = FALSE;
11919
11920     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11921     DrawGameDoorValues();
11922   }
11923
11924   for (i = 0; i < MAX_PLAYERS; i++)
11925   {
11926     int actual_player_action = stored_player[i].effective_action;
11927
11928 #if 1
11929     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11930        - rnd_equinox_tetrachloride 048
11931        - rnd_equinox_tetrachloride_ii 096
11932        - rnd_emanuel_schmieg 002
11933        - doctor_sloan_ww 001, 020
11934     */
11935     if (stored_player[i].MovPos == 0)
11936       CheckGravityMovement(&stored_player[i]);
11937 #endif
11938
11939     // overwrite programmed action with tape action
11940     if (stored_player[i].programmed_action)
11941       actual_player_action = stored_player[i].programmed_action;
11942
11943     PlayerActions(&stored_player[i], actual_player_action);
11944
11945     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11946   }
11947
11948   ScrollScreen(NULL, SCROLL_GO_ON);
11949
11950   /* for backwards compatibility, the following code emulates a fixed bug that
11951      occured when pushing elements (causing elements that just made their last
11952      pushing step to already (if possible) make their first falling step in the
11953      same game frame, which is bad); this code is also needed to use the famous
11954      "spring push bug" which is used in older levels and might be wanted to be
11955      used also in newer levels, but in this case the buggy pushing code is only
11956      affecting the "spring" element and no other elements */
11957
11958   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11959   {
11960     for (i = 0; i < MAX_PLAYERS; i++)
11961     {
11962       struct PlayerInfo *player = &stored_player[i];
11963       int x = player->jx;
11964       int y = player->jy;
11965
11966       if (player->active && player->is_pushing && player->is_moving &&
11967           IS_MOVING(x, y) &&
11968           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11969            Tile[x][y] == EL_SPRING))
11970       {
11971         ContinueMoving(x, y);
11972
11973         // continue moving after pushing (this is actually a bug)
11974         if (!IS_MOVING(x, y))
11975           Stop[x][y] = FALSE;
11976       }
11977     }
11978   }
11979
11980   SCAN_PLAYFIELD(x, y)
11981   {
11982     Last[x][y] = Tile[x][y];
11983
11984     ChangeCount[x][y] = 0;
11985     ChangeEvent[x][y] = -1;
11986
11987     // this must be handled before main playfield loop
11988     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
11989     {
11990       MovDelay[x][y]--;
11991       if (MovDelay[x][y] <= 0)
11992         RemoveField(x, y);
11993     }
11994
11995     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
11996     {
11997       MovDelay[x][y]--;
11998       if (MovDelay[x][y] <= 0)
11999       {
12000         RemoveField(x, y);
12001         TEST_DrawLevelField(x, y);
12002
12003         TestIfElementTouchesCustomElement(x, y);        // for empty space
12004       }
12005     }
12006
12007 #if DEBUG
12008     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12009     {
12010       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12011             x, y);
12012       Debug("game:playing:GameActions_RND", "This should never happen!");
12013
12014       ChangePage[x][y] = -1;
12015     }
12016 #endif
12017
12018     Stop[x][y] = FALSE;
12019     if (WasJustMoving[x][y] > 0)
12020       WasJustMoving[x][y]--;
12021     if (WasJustFalling[x][y] > 0)
12022       WasJustFalling[x][y]--;
12023     if (CheckCollision[x][y] > 0)
12024       CheckCollision[x][y]--;
12025     if (CheckImpact[x][y] > 0)
12026       CheckImpact[x][y]--;
12027
12028     GfxFrame[x][y]++;
12029
12030     /* reset finished pushing action (not done in ContinueMoving() to allow
12031        continuous pushing animation for elements with zero push delay) */
12032     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12033     {
12034       ResetGfxAnimation(x, y);
12035       TEST_DrawLevelField(x, y);
12036     }
12037
12038 #if DEBUG
12039     if (IS_BLOCKED(x, y))
12040     {
12041       int oldx, oldy;
12042
12043       Blocked2Moving(x, y, &oldx, &oldy);
12044       if (!IS_MOVING(oldx, oldy))
12045       {
12046         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12047         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12048         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12049         Debug("game:playing:GameActions_RND", "This should never happen!");
12050       }
12051     }
12052 #endif
12053   }
12054
12055   if (mouse_action.button)
12056   {
12057     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12058
12059     x = mouse_action.lx;
12060     y = mouse_action.ly;
12061     element = Tile[x][y];
12062
12063     if (new_button)
12064     {
12065       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12066       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12067     }
12068
12069     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12070     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12071   }
12072
12073   SCAN_PLAYFIELD(x, y)
12074   {
12075     element = Tile[x][y];
12076     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12077     last_gfx_frame = GfxFrame[x][y];
12078
12079     ResetGfxFrame(x, y);
12080
12081     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12082       DrawLevelGraphicAnimation(x, y, graphic);
12083
12084     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12085         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12086       ResetRandomAnimationValue(x, y);
12087
12088     SetRandomAnimationValue(x, y);
12089
12090     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12091
12092     if (IS_INACTIVE(element))
12093     {
12094       if (IS_ANIMATED(graphic))
12095         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12096
12097       continue;
12098     }
12099
12100     // this may take place after moving, so 'element' may have changed
12101     if (IS_CHANGING(x, y) &&
12102         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12103     {
12104       int page = element_info[element].event_page_nr[CE_DELAY];
12105
12106       HandleElementChange(x, y, page);
12107
12108       element = Tile[x][y];
12109       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12110     }
12111
12112     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12113     {
12114       StartMoving(x, y);
12115
12116       element = Tile[x][y];
12117       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12118
12119       if (IS_ANIMATED(graphic) &&
12120           !IS_MOVING(x, y) &&
12121           !Stop[x][y])
12122         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12123
12124       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12125         TEST_DrawTwinkleOnField(x, y);
12126     }
12127     else if (element == EL_ACID)
12128     {
12129       if (!Stop[x][y])
12130         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12131     }
12132     else if ((element == EL_EXIT_OPEN ||
12133               element == EL_EM_EXIT_OPEN ||
12134               element == EL_SP_EXIT_OPEN ||
12135               element == EL_STEEL_EXIT_OPEN ||
12136               element == EL_EM_STEEL_EXIT_OPEN ||
12137               element == EL_SP_TERMINAL ||
12138               element == EL_SP_TERMINAL_ACTIVE ||
12139               element == EL_EXTRA_TIME ||
12140               element == EL_SHIELD_NORMAL ||
12141               element == EL_SHIELD_DEADLY) &&
12142              IS_ANIMATED(graphic))
12143       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12144     else if (IS_MOVING(x, y))
12145       ContinueMoving(x, y);
12146     else if (IS_ACTIVE_BOMB(element))
12147       CheckDynamite(x, y);
12148     else if (element == EL_AMOEBA_GROWING)
12149       AmoebaGrowing(x, y);
12150     else if (element == EL_AMOEBA_SHRINKING)
12151       AmoebaShrinking(x, y);
12152
12153 #if !USE_NEW_AMOEBA_CODE
12154     else if (IS_AMOEBALIVE(element))
12155       AmoebaReproduce(x, y);
12156 #endif
12157
12158     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12159       Life(x, y);
12160     else if (element == EL_EXIT_CLOSED)
12161       CheckExit(x, y);
12162     else if (element == EL_EM_EXIT_CLOSED)
12163       CheckExitEM(x, y);
12164     else if (element == EL_STEEL_EXIT_CLOSED)
12165       CheckExitSteel(x, y);
12166     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12167       CheckExitSteelEM(x, y);
12168     else if (element == EL_SP_EXIT_CLOSED)
12169       CheckExitSP(x, y);
12170     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12171              element == EL_EXPANDABLE_STEELWALL_GROWING)
12172       MauerWaechst(x, y);
12173     else if (element == EL_EXPANDABLE_WALL ||
12174              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12175              element == EL_EXPANDABLE_WALL_VERTICAL ||
12176              element == EL_EXPANDABLE_WALL_ANY ||
12177              element == EL_BD_EXPANDABLE_WALL)
12178       MauerAbleger(x, y);
12179     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12180              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12181              element == EL_EXPANDABLE_STEELWALL_ANY)
12182       MauerAblegerStahl(x, y);
12183     else if (element == EL_FLAMES)
12184       CheckForDragon(x, y);
12185     else if (element == EL_EXPLOSION)
12186       ; // drawing of correct explosion animation is handled separately
12187     else if (element == EL_ELEMENT_SNAPPING ||
12188              element == EL_DIAGONAL_SHRINKING ||
12189              element == EL_DIAGONAL_GROWING)
12190     {
12191       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12192
12193       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12194     }
12195     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12196       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12197
12198     if (IS_BELT_ACTIVE(element))
12199       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12200
12201     if (game.magic_wall_active)
12202     {
12203       int jx = local_player->jx, jy = local_player->jy;
12204
12205       // play the element sound at the position nearest to the player
12206       if ((element == EL_MAGIC_WALL_FULL ||
12207            element == EL_MAGIC_WALL_ACTIVE ||
12208            element == EL_MAGIC_WALL_EMPTYING ||
12209            element == EL_BD_MAGIC_WALL_FULL ||
12210            element == EL_BD_MAGIC_WALL_ACTIVE ||
12211            element == EL_BD_MAGIC_WALL_EMPTYING ||
12212            element == EL_DC_MAGIC_WALL_FULL ||
12213            element == EL_DC_MAGIC_WALL_ACTIVE ||
12214            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12215           ABS(x - jx) + ABS(y - jy) <
12216           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12217       {
12218         magic_wall_x = x;
12219         magic_wall_y = y;
12220       }
12221     }
12222   }
12223
12224 #if USE_NEW_AMOEBA_CODE
12225   // new experimental amoeba growth stuff
12226   if (!(FrameCounter % 8))
12227   {
12228     static unsigned int random = 1684108901;
12229
12230     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12231     {
12232       x = RND(lev_fieldx);
12233       y = RND(lev_fieldy);
12234       element = Tile[x][y];
12235
12236       if (!IS_PLAYER(x,y) &&
12237           (element == EL_EMPTY ||
12238            CAN_GROW_INTO(element) ||
12239            element == EL_QUICKSAND_EMPTY ||
12240            element == EL_QUICKSAND_FAST_EMPTY ||
12241            element == EL_ACID_SPLASH_LEFT ||
12242            element == EL_ACID_SPLASH_RIGHT))
12243       {
12244         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12245             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12246             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12247             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12248           Tile[x][y] = EL_AMOEBA_DROP;
12249       }
12250
12251       random = random * 129 + 1;
12252     }
12253   }
12254 #endif
12255
12256   game.explosions_delayed = FALSE;
12257
12258   SCAN_PLAYFIELD(x, y)
12259   {
12260     element = Tile[x][y];
12261
12262     if (ExplodeField[x][y])
12263       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12264     else if (element == EL_EXPLOSION)
12265       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12266
12267     ExplodeField[x][y] = EX_TYPE_NONE;
12268   }
12269
12270   game.explosions_delayed = TRUE;
12271
12272   if (game.magic_wall_active)
12273   {
12274     if (!(game.magic_wall_time_left % 4))
12275     {
12276       int element = Tile[magic_wall_x][magic_wall_y];
12277
12278       if (element == EL_BD_MAGIC_WALL_FULL ||
12279           element == EL_BD_MAGIC_WALL_ACTIVE ||
12280           element == EL_BD_MAGIC_WALL_EMPTYING)
12281         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12282       else if (element == EL_DC_MAGIC_WALL_FULL ||
12283                element == EL_DC_MAGIC_WALL_ACTIVE ||
12284                element == EL_DC_MAGIC_WALL_EMPTYING)
12285         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12286       else
12287         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12288     }
12289
12290     if (game.magic_wall_time_left > 0)
12291     {
12292       game.magic_wall_time_left--;
12293
12294       if (!game.magic_wall_time_left)
12295       {
12296         SCAN_PLAYFIELD(x, y)
12297         {
12298           element = Tile[x][y];
12299
12300           if (element == EL_MAGIC_WALL_ACTIVE ||
12301               element == EL_MAGIC_WALL_FULL)
12302           {
12303             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12304             TEST_DrawLevelField(x, y);
12305           }
12306           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12307                    element == EL_BD_MAGIC_WALL_FULL)
12308           {
12309             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12310             TEST_DrawLevelField(x, y);
12311           }
12312           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12313                    element == EL_DC_MAGIC_WALL_FULL)
12314           {
12315             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12316             TEST_DrawLevelField(x, y);
12317           }
12318         }
12319
12320         game.magic_wall_active = FALSE;
12321       }
12322     }
12323   }
12324
12325   if (game.light_time_left > 0)
12326   {
12327     game.light_time_left--;
12328
12329     if (game.light_time_left == 0)
12330       RedrawAllLightSwitchesAndInvisibleElements();
12331   }
12332
12333   if (game.timegate_time_left > 0)
12334   {
12335     game.timegate_time_left--;
12336
12337     if (game.timegate_time_left == 0)
12338       CloseAllOpenTimegates();
12339   }
12340
12341   if (game.lenses_time_left > 0)
12342   {
12343     game.lenses_time_left--;
12344
12345     if (game.lenses_time_left == 0)
12346       RedrawAllInvisibleElementsForLenses();
12347   }
12348
12349   if (game.magnify_time_left > 0)
12350   {
12351     game.magnify_time_left--;
12352
12353     if (game.magnify_time_left == 0)
12354       RedrawAllInvisibleElementsForMagnifier();
12355   }
12356
12357   for (i = 0; i < MAX_PLAYERS; i++)
12358   {
12359     struct PlayerInfo *player = &stored_player[i];
12360
12361     if (SHIELD_ON(player))
12362     {
12363       if (player->shield_deadly_time_left)
12364         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12365       else if (player->shield_normal_time_left)
12366         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12367     }
12368   }
12369
12370 #if USE_DELAYED_GFX_REDRAW
12371   SCAN_PLAYFIELD(x, y)
12372   {
12373     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12374     {
12375       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12376          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12377
12378       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12379         DrawLevelField(x, y);
12380
12381       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12382         DrawLevelFieldCrumbled(x, y);
12383
12384       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12385         DrawLevelFieldCrumbledNeighbours(x, y);
12386
12387       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12388         DrawTwinkleOnField(x, y);
12389     }
12390
12391     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12392   }
12393 #endif
12394
12395   DrawAllPlayers();
12396   PlayAllPlayersSound();
12397
12398   for (i = 0; i < MAX_PLAYERS; i++)
12399   {
12400     struct PlayerInfo *player = &stored_player[i];
12401
12402     if (player->show_envelope != 0 && (!player->active ||
12403                                        player->MovPos == 0))
12404     {
12405       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12406
12407       player->show_envelope = 0;
12408     }
12409   }
12410
12411   // use random number generator in every frame to make it less predictable
12412   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12413     RND(1);
12414
12415   mouse_action_last = mouse_action;
12416 }
12417
12418 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12419 {
12420   int min_x = x, min_y = y, max_x = x, max_y = y;
12421   int i;
12422
12423   for (i = 0; i < MAX_PLAYERS; i++)
12424   {
12425     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12426
12427     if (!stored_player[i].active || &stored_player[i] == player)
12428       continue;
12429
12430     min_x = MIN(min_x, jx);
12431     min_y = MIN(min_y, jy);
12432     max_x = MAX(max_x, jx);
12433     max_y = MAX(max_y, jy);
12434   }
12435
12436   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12437 }
12438
12439 static boolean AllPlayersInVisibleScreen(void)
12440 {
12441   int i;
12442
12443   for (i = 0; i < MAX_PLAYERS; i++)
12444   {
12445     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12446
12447     if (!stored_player[i].active)
12448       continue;
12449
12450     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12451       return FALSE;
12452   }
12453
12454   return TRUE;
12455 }
12456
12457 void ScrollLevel(int dx, int dy)
12458 {
12459   int scroll_offset = 2 * TILEX_VAR;
12460   int x, y;
12461
12462   BlitBitmap(drawto_field, drawto_field,
12463              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12464              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12465              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12466              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12467              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12468              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12469
12470   if (dx != 0)
12471   {
12472     x = (dx == 1 ? BX1 : BX2);
12473     for (y = BY1; y <= BY2; y++)
12474       DrawScreenField(x, y);
12475   }
12476
12477   if (dy != 0)
12478   {
12479     y = (dy == 1 ? BY1 : BY2);
12480     for (x = BX1; x <= BX2; x++)
12481       DrawScreenField(x, y);
12482   }
12483
12484   redraw_mask |= REDRAW_FIELD;
12485 }
12486
12487 static boolean canFallDown(struct PlayerInfo *player)
12488 {
12489   int jx = player->jx, jy = player->jy;
12490
12491   return (IN_LEV_FIELD(jx, jy + 1) &&
12492           (IS_FREE(jx, jy + 1) ||
12493            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12494           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12495           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12496 }
12497
12498 static boolean canPassField(int x, int y, int move_dir)
12499 {
12500   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12501   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12502   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12503   int nextx = x + dx;
12504   int nexty = y + dy;
12505   int element = Tile[x][y];
12506
12507   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12508           !CAN_MOVE(element) &&
12509           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12510           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12511           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12512 }
12513
12514 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12515 {
12516   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12517   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12518   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12519   int newx = x + dx;
12520   int newy = y + dy;
12521
12522   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12523           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12524           (IS_DIGGABLE(Tile[newx][newy]) ||
12525            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12526            canPassField(newx, newy, move_dir)));
12527 }
12528
12529 static void CheckGravityMovement(struct PlayerInfo *player)
12530 {
12531   if (player->gravity && !player->programmed_action)
12532   {
12533     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12534     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12535     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12536     int jx = player->jx, jy = player->jy;
12537     boolean player_is_moving_to_valid_field =
12538       (!player_is_snapping &&
12539        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12540         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12541     boolean player_can_fall_down = canFallDown(player);
12542
12543     if (player_can_fall_down &&
12544         !player_is_moving_to_valid_field)
12545       player->programmed_action = MV_DOWN;
12546   }
12547 }
12548
12549 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12550 {
12551   return CheckGravityMovement(player);
12552
12553   if (player->gravity && !player->programmed_action)
12554   {
12555     int jx = player->jx, jy = player->jy;
12556     boolean field_under_player_is_free =
12557       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12558     boolean player_is_standing_on_valid_field =
12559       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12560        (IS_WALKABLE(Tile[jx][jy]) &&
12561         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12562
12563     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12564       player->programmed_action = MV_DOWN;
12565   }
12566 }
12567
12568 /*
12569   MovePlayerOneStep()
12570   -----------------------------------------------------------------------------
12571   dx, dy:               direction (non-diagonal) to try to move the player to
12572   real_dx, real_dy:     direction as read from input device (can be diagonal)
12573 */
12574
12575 boolean MovePlayerOneStep(struct PlayerInfo *player,
12576                           int dx, int dy, int real_dx, int real_dy)
12577 {
12578   int jx = player->jx, jy = player->jy;
12579   int new_jx = jx + dx, new_jy = jy + dy;
12580   int can_move;
12581   boolean player_can_move = !player->cannot_move;
12582
12583   if (!player->active || (!dx && !dy))
12584     return MP_NO_ACTION;
12585
12586   player->MovDir = (dx < 0 ? MV_LEFT :
12587                     dx > 0 ? MV_RIGHT :
12588                     dy < 0 ? MV_UP :
12589                     dy > 0 ? MV_DOWN :  MV_NONE);
12590
12591   if (!IN_LEV_FIELD(new_jx, new_jy))
12592     return MP_NO_ACTION;
12593
12594   if (!player_can_move)
12595   {
12596     if (player->MovPos == 0)
12597     {
12598       player->is_moving = FALSE;
12599       player->is_digging = FALSE;
12600       player->is_collecting = FALSE;
12601       player->is_snapping = FALSE;
12602       player->is_pushing = FALSE;
12603     }
12604   }
12605
12606   if (!network.enabled && game.centered_player_nr == -1 &&
12607       !AllPlayersInSight(player, new_jx, new_jy))
12608     return MP_NO_ACTION;
12609
12610   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12611   if (can_move != MP_MOVING)
12612     return can_move;
12613
12614   // check if DigField() has caused relocation of the player
12615   if (player->jx != jx || player->jy != jy)
12616     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12617
12618   StorePlayer[jx][jy] = 0;
12619   player->last_jx = jx;
12620   player->last_jy = jy;
12621   player->jx = new_jx;
12622   player->jy = new_jy;
12623   StorePlayer[new_jx][new_jy] = player->element_nr;
12624
12625   if (player->move_delay_value_next != -1)
12626   {
12627     player->move_delay_value = player->move_delay_value_next;
12628     player->move_delay_value_next = -1;
12629   }
12630
12631   player->MovPos =
12632     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12633
12634   player->step_counter++;
12635
12636   PlayerVisit[jx][jy] = FrameCounter;
12637
12638   player->is_moving = TRUE;
12639
12640 #if 1
12641   // should better be called in MovePlayer(), but this breaks some tapes
12642   ScrollPlayer(player, SCROLL_INIT);
12643 #endif
12644
12645   return MP_MOVING;
12646 }
12647
12648 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12649 {
12650   int jx = player->jx, jy = player->jy;
12651   int old_jx = jx, old_jy = jy;
12652   int moved = MP_NO_ACTION;
12653
12654   if (!player->active)
12655     return FALSE;
12656
12657   if (!dx && !dy)
12658   {
12659     if (player->MovPos == 0)
12660     {
12661       player->is_moving = FALSE;
12662       player->is_digging = FALSE;
12663       player->is_collecting = FALSE;
12664       player->is_snapping = FALSE;
12665       player->is_pushing = FALSE;
12666     }
12667
12668     return FALSE;
12669   }
12670
12671   if (player->move_delay > 0)
12672     return FALSE;
12673
12674   player->move_delay = -1;              // set to "uninitialized" value
12675
12676   // store if player is automatically moved to next field
12677   player->is_auto_moving = (player->programmed_action != MV_NONE);
12678
12679   // remove the last programmed player action
12680   player->programmed_action = 0;
12681
12682   if (player->MovPos)
12683   {
12684     // should only happen if pre-1.2 tape recordings are played
12685     // this is only for backward compatibility
12686
12687     int original_move_delay_value = player->move_delay_value;
12688
12689 #if DEBUG
12690     Debug("game:playing:MovePlayer",
12691           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12692           tape.counter);
12693 #endif
12694
12695     // scroll remaining steps with finest movement resolution
12696     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12697
12698     while (player->MovPos)
12699     {
12700       ScrollPlayer(player, SCROLL_GO_ON);
12701       ScrollScreen(NULL, SCROLL_GO_ON);
12702
12703       AdvanceFrameAndPlayerCounters(player->index_nr);
12704
12705       DrawAllPlayers();
12706       BackToFront_WithFrameDelay(0);
12707     }
12708
12709     player->move_delay_value = original_move_delay_value;
12710   }
12711
12712   player->is_active = FALSE;
12713
12714   if (player->last_move_dir & MV_HORIZONTAL)
12715   {
12716     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12717       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12718   }
12719   else
12720   {
12721     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12722       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12723   }
12724
12725   if (!moved && !player->is_active)
12726   {
12727     player->is_moving = FALSE;
12728     player->is_digging = FALSE;
12729     player->is_collecting = FALSE;
12730     player->is_snapping = FALSE;
12731     player->is_pushing = FALSE;
12732   }
12733
12734   jx = player->jx;
12735   jy = player->jy;
12736
12737   if (moved & MP_MOVING && !ScreenMovPos &&
12738       (player->index_nr == game.centered_player_nr ||
12739        game.centered_player_nr == -1))
12740   {
12741     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12742
12743     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12744     {
12745       // actual player has left the screen -- scroll in that direction
12746       if (jx != old_jx)         // player has moved horizontally
12747         scroll_x += (jx - old_jx);
12748       else                      // player has moved vertically
12749         scroll_y += (jy - old_jy);
12750     }
12751     else
12752     {
12753       int offset_raw = game.scroll_delay_value;
12754
12755       if (jx != old_jx)         // player has moved horizontally
12756       {
12757         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12758         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12759         int new_scroll_x = jx - MIDPOSX + offset_x;
12760
12761         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12762             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12763           scroll_x = new_scroll_x;
12764
12765         // don't scroll over playfield boundaries
12766         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12767
12768         // don't scroll more than one field at a time
12769         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12770
12771         // don't scroll against the player's moving direction
12772         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12773             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12774           scroll_x = old_scroll_x;
12775       }
12776       else                      // player has moved vertically
12777       {
12778         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12779         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12780         int new_scroll_y = jy - MIDPOSY + offset_y;
12781
12782         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12783             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12784           scroll_y = new_scroll_y;
12785
12786         // don't scroll over playfield boundaries
12787         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12788
12789         // don't scroll more than one field at a time
12790         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12791
12792         // don't scroll against the player's moving direction
12793         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12794             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12795           scroll_y = old_scroll_y;
12796       }
12797     }
12798
12799     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12800     {
12801       if (!network.enabled && game.centered_player_nr == -1 &&
12802           !AllPlayersInVisibleScreen())
12803       {
12804         scroll_x = old_scroll_x;
12805         scroll_y = old_scroll_y;
12806       }
12807       else
12808       {
12809         ScrollScreen(player, SCROLL_INIT);
12810         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12811       }
12812     }
12813   }
12814
12815   player->StepFrame = 0;
12816
12817   if (moved & MP_MOVING)
12818   {
12819     if (old_jx != jx && old_jy == jy)
12820       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12821     else if (old_jx == jx && old_jy != jy)
12822       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12823
12824     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12825
12826     player->last_move_dir = player->MovDir;
12827     player->is_moving = TRUE;
12828     player->is_snapping = FALSE;
12829     player->is_switching = FALSE;
12830     player->is_dropping = FALSE;
12831     player->is_dropping_pressed = FALSE;
12832     player->drop_pressed_delay = 0;
12833
12834 #if 0
12835     // should better be called here than above, but this breaks some tapes
12836     ScrollPlayer(player, SCROLL_INIT);
12837 #endif
12838   }
12839   else
12840   {
12841     CheckGravityMovementWhenNotMoving(player);
12842
12843     player->is_moving = FALSE;
12844
12845     /* at this point, the player is allowed to move, but cannot move right now
12846        (e.g. because of something blocking the way) -- ensure that the player
12847        is also allowed to move in the next frame (in old versions before 3.1.1,
12848        the player was forced to wait again for eight frames before next try) */
12849
12850     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12851       player->move_delay = 0;   // allow direct movement in the next frame
12852   }
12853
12854   if (player->move_delay == -1)         // not yet initialized by DigField()
12855     player->move_delay = player->move_delay_value;
12856
12857   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12858   {
12859     TestIfPlayerTouchesBadThing(jx, jy);
12860     TestIfPlayerTouchesCustomElement(jx, jy);
12861   }
12862
12863   if (!player->active)
12864     RemovePlayer(player);
12865
12866   return moved;
12867 }
12868
12869 void ScrollPlayer(struct PlayerInfo *player, int mode)
12870 {
12871   int jx = player->jx, jy = player->jy;
12872   int last_jx = player->last_jx, last_jy = player->last_jy;
12873   int move_stepsize = TILEX / player->move_delay_value;
12874
12875   if (!player->active)
12876     return;
12877
12878   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12879     return;
12880
12881   if (mode == SCROLL_INIT)
12882   {
12883     player->actual_frame_counter = FrameCounter;
12884     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12885
12886     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12887         Tile[last_jx][last_jy] == EL_EMPTY)
12888     {
12889       int last_field_block_delay = 0;   // start with no blocking at all
12890       int block_delay_adjustment = player->block_delay_adjustment;
12891
12892       // if player blocks last field, add delay for exactly one move
12893       if (player->block_last_field)
12894       {
12895         last_field_block_delay += player->move_delay_value;
12896
12897         // when blocking enabled, prevent moving up despite gravity
12898         if (player->gravity && player->MovDir == MV_UP)
12899           block_delay_adjustment = -1;
12900       }
12901
12902       // add block delay adjustment (also possible when not blocking)
12903       last_field_block_delay += block_delay_adjustment;
12904
12905       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12906       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12907     }
12908
12909     if (player->MovPos != 0)    // player has not yet reached destination
12910       return;
12911   }
12912   else if (!FrameReached(&player->actual_frame_counter, 1))
12913     return;
12914
12915   if (player->MovPos != 0)
12916   {
12917     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12918     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12919
12920     // before DrawPlayer() to draw correct player graphic for this case
12921     if (player->MovPos == 0)
12922       CheckGravityMovement(player);
12923   }
12924
12925   if (player->MovPos == 0)      // player reached destination field
12926   {
12927     if (player->move_delay_reset_counter > 0)
12928     {
12929       player->move_delay_reset_counter--;
12930
12931       if (player->move_delay_reset_counter == 0)
12932       {
12933         // continue with normal speed after quickly moving through gate
12934         HALVE_PLAYER_SPEED(player);
12935
12936         // be able to make the next move without delay
12937         player->move_delay = 0;
12938       }
12939     }
12940
12941     player->last_jx = jx;
12942     player->last_jy = jy;
12943
12944     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12945         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12946         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12947         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12948         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12949         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12950         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12951         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12952     {
12953       ExitPlayer(player);
12954
12955       if (game.players_still_needed == 0 &&
12956           (game.friends_still_needed == 0 ||
12957            IS_SP_ELEMENT(Tile[jx][jy])))
12958         LevelSolved();
12959     }
12960
12961     // this breaks one level: "machine", level 000
12962     {
12963       int move_direction = player->MovDir;
12964       int enter_side = MV_DIR_OPPOSITE(move_direction);
12965       int leave_side = move_direction;
12966       int old_jx = last_jx;
12967       int old_jy = last_jy;
12968       int old_element = Tile[old_jx][old_jy];
12969       int new_element = Tile[jx][jy];
12970
12971       if (IS_CUSTOM_ELEMENT(old_element))
12972         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12973                                    CE_LEFT_BY_PLAYER,
12974                                    player->index_bit, leave_side);
12975
12976       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12977                                           CE_PLAYER_LEAVES_X,
12978                                           player->index_bit, leave_side);
12979
12980       if (IS_CUSTOM_ELEMENT(new_element))
12981         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12982                                    player->index_bit, enter_side);
12983
12984       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12985                                           CE_PLAYER_ENTERS_X,
12986                                           player->index_bit, enter_side);
12987
12988       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12989                                         CE_MOVE_OF_X, move_direction);
12990     }
12991
12992     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12993     {
12994       TestIfPlayerTouchesBadThing(jx, jy);
12995       TestIfPlayerTouchesCustomElement(jx, jy);
12996
12997       /* needed because pushed element has not yet reached its destination,
12998          so it would trigger a change event at its previous field location */
12999       if (!player->is_pushing)
13000         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13001
13002       if (!player->active)
13003         RemovePlayer(player);
13004     }
13005
13006     if (!game.LevelSolved && level.use_step_counter)
13007     {
13008       int i;
13009
13010       TimePlayed++;
13011
13012       if (TimeLeft > 0)
13013       {
13014         TimeLeft--;
13015
13016         if (TimeLeft <= 10 && setup.time_limit)
13017           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13018
13019         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13020
13021         DisplayGameControlValues();
13022
13023         if (!TimeLeft && setup.time_limit)
13024           for (i = 0; i < MAX_PLAYERS; i++)
13025             KillPlayer(&stored_player[i]);
13026       }
13027       else if (game.no_time_limit && !game.all_players_gone)
13028       {
13029         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13030
13031         DisplayGameControlValues();
13032       }
13033     }
13034
13035     if (tape.single_step && tape.recording && !tape.pausing &&
13036         !player->programmed_action)
13037       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13038
13039     if (!player->programmed_action)
13040       CheckSaveEngineSnapshot(player);
13041   }
13042 }
13043
13044 void ScrollScreen(struct PlayerInfo *player, int mode)
13045 {
13046   static unsigned int screen_frame_counter = 0;
13047
13048   if (mode == SCROLL_INIT)
13049   {
13050     // set scrolling step size according to actual player's moving speed
13051     ScrollStepSize = TILEX / player->move_delay_value;
13052
13053     screen_frame_counter = FrameCounter;
13054     ScreenMovDir = player->MovDir;
13055     ScreenMovPos = player->MovPos;
13056     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13057     return;
13058   }
13059   else if (!FrameReached(&screen_frame_counter, 1))
13060     return;
13061
13062   if (ScreenMovPos)
13063   {
13064     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13065     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13066     redraw_mask |= REDRAW_FIELD;
13067   }
13068   else
13069     ScreenMovDir = MV_NONE;
13070 }
13071
13072 void TestIfPlayerTouchesCustomElement(int x, int y)
13073 {
13074   static int xy[4][2] =
13075   {
13076     { 0, -1 },
13077     { -1, 0 },
13078     { +1, 0 },
13079     { 0, +1 }
13080   };
13081   static int trigger_sides[4][2] =
13082   {
13083     // center side       border side
13084     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13085     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13086     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13087     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13088   };
13089   static int touch_dir[4] =
13090   {
13091     MV_LEFT | MV_RIGHT,
13092     MV_UP   | MV_DOWN,
13093     MV_UP   | MV_DOWN,
13094     MV_LEFT | MV_RIGHT
13095   };
13096   int center_element = Tile[x][y];      // should always be non-moving!
13097   int i;
13098
13099   for (i = 0; i < NUM_DIRECTIONS; i++)
13100   {
13101     int xx = x + xy[i][0];
13102     int yy = y + xy[i][1];
13103     int center_side = trigger_sides[i][0];
13104     int border_side = trigger_sides[i][1];
13105     int border_element;
13106
13107     if (!IN_LEV_FIELD(xx, yy))
13108       continue;
13109
13110     if (IS_PLAYER(x, y))                // player found at center element
13111     {
13112       struct PlayerInfo *player = PLAYERINFO(x, y);
13113
13114       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13115         border_element = Tile[xx][yy];          // may be moving!
13116       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13117         border_element = Tile[xx][yy];
13118       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13119         border_element = MovingOrBlocked2Element(xx, yy);
13120       else
13121         continue;               // center and border element do not touch
13122
13123       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13124                                  player->index_bit, border_side);
13125       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13126                                           CE_PLAYER_TOUCHES_X,
13127                                           player->index_bit, border_side);
13128
13129       {
13130         /* use player element that is initially defined in the level playfield,
13131            not the player element that corresponds to the runtime player number
13132            (example: a level that contains EL_PLAYER_3 as the only player would
13133            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13134         int player_element = PLAYERINFO(x, y)->initial_element;
13135
13136         CheckElementChangeBySide(xx, yy, border_element, player_element,
13137                                  CE_TOUCHING_X, border_side);
13138       }
13139     }
13140     else if (IS_PLAYER(xx, yy))         // player found at border element
13141     {
13142       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13143
13144       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13145       {
13146         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13147           continue;             // center and border element do not touch
13148       }
13149
13150       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13151                                  player->index_bit, center_side);
13152       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13153                                           CE_PLAYER_TOUCHES_X,
13154                                           player->index_bit, center_side);
13155
13156       {
13157         /* use player element that is initially defined in the level playfield,
13158            not the player element that corresponds to the runtime player number
13159            (example: a level that contains EL_PLAYER_3 as the only player would
13160            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13161         int player_element = PLAYERINFO(xx, yy)->initial_element;
13162
13163         CheckElementChangeBySide(x, y, center_element, player_element,
13164                                  CE_TOUCHING_X, center_side);
13165       }
13166
13167       break;
13168     }
13169   }
13170 }
13171
13172 void TestIfElementTouchesCustomElement(int x, int y)
13173 {
13174   static int xy[4][2] =
13175   {
13176     { 0, -1 },
13177     { -1, 0 },
13178     { +1, 0 },
13179     { 0, +1 }
13180   };
13181   static int trigger_sides[4][2] =
13182   {
13183     // center side      border side
13184     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13185     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13186     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13187     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13188   };
13189   static int touch_dir[4] =
13190   {
13191     MV_LEFT | MV_RIGHT,
13192     MV_UP   | MV_DOWN,
13193     MV_UP   | MV_DOWN,
13194     MV_LEFT | MV_RIGHT
13195   };
13196   boolean change_center_element = FALSE;
13197   int center_element = Tile[x][y];      // should always be non-moving!
13198   int border_element_old[NUM_DIRECTIONS];
13199   int i;
13200
13201   for (i = 0; i < NUM_DIRECTIONS; i++)
13202   {
13203     int xx = x + xy[i][0];
13204     int yy = y + xy[i][1];
13205     int border_element;
13206
13207     border_element_old[i] = -1;
13208
13209     if (!IN_LEV_FIELD(xx, yy))
13210       continue;
13211
13212     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13213       border_element = Tile[xx][yy];    // may be moving!
13214     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13215       border_element = Tile[xx][yy];
13216     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13217       border_element = MovingOrBlocked2Element(xx, yy);
13218     else
13219       continue;                 // center and border element do not touch
13220
13221     border_element_old[i] = border_element;
13222   }
13223
13224   for (i = 0; i < NUM_DIRECTIONS; i++)
13225   {
13226     int xx = x + xy[i][0];
13227     int yy = y + xy[i][1];
13228     int center_side = trigger_sides[i][0];
13229     int border_element = border_element_old[i];
13230
13231     if (border_element == -1)
13232       continue;
13233
13234     // check for change of border element
13235     CheckElementChangeBySide(xx, yy, border_element, center_element,
13236                              CE_TOUCHING_X, center_side);
13237
13238     // (center element cannot be player, so we dont have to check this here)
13239   }
13240
13241   for (i = 0; i < NUM_DIRECTIONS; i++)
13242   {
13243     int xx = x + xy[i][0];
13244     int yy = y + xy[i][1];
13245     int border_side = trigger_sides[i][1];
13246     int border_element = border_element_old[i];
13247
13248     if (border_element == -1)
13249       continue;
13250
13251     // check for change of center element (but change it only once)
13252     if (!change_center_element)
13253       change_center_element =
13254         CheckElementChangeBySide(x, y, center_element, border_element,
13255                                  CE_TOUCHING_X, border_side);
13256
13257     if (IS_PLAYER(xx, yy))
13258     {
13259       /* use player element that is initially defined in the level playfield,
13260          not the player element that corresponds to the runtime player number
13261          (example: a level that contains EL_PLAYER_3 as the only player would
13262          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13263       int player_element = PLAYERINFO(xx, yy)->initial_element;
13264
13265       CheckElementChangeBySide(x, y, center_element, player_element,
13266                                CE_TOUCHING_X, border_side);
13267     }
13268   }
13269 }
13270
13271 void TestIfElementHitsCustomElement(int x, int y, int direction)
13272 {
13273   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13274   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13275   int hitx = x + dx, hity = y + dy;
13276   int hitting_element = Tile[x][y];
13277   int touched_element;
13278
13279   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13280     return;
13281
13282   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13283                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13284
13285   if (IN_LEV_FIELD(hitx, hity))
13286   {
13287     int opposite_direction = MV_DIR_OPPOSITE(direction);
13288     int hitting_side = direction;
13289     int touched_side = opposite_direction;
13290     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13291                           MovDir[hitx][hity] != direction ||
13292                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13293
13294     object_hit = TRUE;
13295
13296     if (object_hit)
13297     {
13298       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13299                                CE_HITTING_X, touched_side);
13300
13301       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13302                                CE_HIT_BY_X, hitting_side);
13303
13304       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13305                                CE_HIT_BY_SOMETHING, opposite_direction);
13306
13307       if (IS_PLAYER(hitx, hity))
13308       {
13309         /* use player element that is initially defined in the level playfield,
13310            not the player element that corresponds to the runtime player number
13311            (example: a level that contains EL_PLAYER_3 as the only player would
13312            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13313         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13314
13315         CheckElementChangeBySide(x, y, hitting_element, player_element,
13316                                  CE_HITTING_X, touched_side);
13317       }
13318     }
13319   }
13320
13321   // "hitting something" is also true when hitting the playfield border
13322   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13323                            CE_HITTING_SOMETHING, direction);
13324 }
13325
13326 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13327 {
13328   int i, kill_x = -1, kill_y = -1;
13329
13330   int bad_element = -1;
13331   static int test_xy[4][2] =
13332   {
13333     { 0, -1 },
13334     { -1, 0 },
13335     { +1, 0 },
13336     { 0, +1 }
13337   };
13338   static int test_dir[4] =
13339   {
13340     MV_UP,
13341     MV_LEFT,
13342     MV_RIGHT,
13343     MV_DOWN
13344   };
13345
13346   for (i = 0; i < NUM_DIRECTIONS; i++)
13347   {
13348     int test_x, test_y, test_move_dir, test_element;
13349
13350     test_x = good_x + test_xy[i][0];
13351     test_y = good_y + test_xy[i][1];
13352
13353     if (!IN_LEV_FIELD(test_x, test_y))
13354       continue;
13355
13356     test_move_dir =
13357       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13358
13359     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13360
13361     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13362        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13363     */
13364     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13365         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13366     {
13367       kill_x = test_x;
13368       kill_y = test_y;
13369       bad_element = test_element;
13370
13371       break;
13372     }
13373   }
13374
13375   if (kill_x != -1 || kill_y != -1)
13376   {
13377     if (IS_PLAYER(good_x, good_y))
13378     {
13379       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13380
13381       if (player->shield_deadly_time_left > 0 &&
13382           !IS_INDESTRUCTIBLE(bad_element))
13383         Bang(kill_x, kill_y);
13384       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13385         KillPlayer(player);
13386     }
13387     else
13388       Bang(good_x, good_y);
13389   }
13390 }
13391
13392 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13393 {
13394   int i, kill_x = -1, kill_y = -1;
13395   int bad_element = Tile[bad_x][bad_y];
13396   static int test_xy[4][2] =
13397   {
13398     { 0, -1 },
13399     { -1, 0 },
13400     { +1, 0 },
13401     { 0, +1 }
13402   };
13403   static int touch_dir[4] =
13404   {
13405     MV_LEFT | MV_RIGHT,
13406     MV_UP   | MV_DOWN,
13407     MV_UP   | MV_DOWN,
13408     MV_LEFT | MV_RIGHT
13409   };
13410   static int test_dir[4] =
13411   {
13412     MV_UP,
13413     MV_LEFT,
13414     MV_RIGHT,
13415     MV_DOWN
13416   };
13417
13418   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13419     return;
13420
13421   for (i = 0; i < NUM_DIRECTIONS; i++)
13422   {
13423     int test_x, test_y, test_move_dir, test_element;
13424
13425     test_x = bad_x + test_xy[i][0];
13426     test_y = bad_y + test_xy[i][1];
13427
13428     if (!IN_LEV_FIELD(test_x, test_y))
13429       continue;
13430
13431     test_move_dir =
13432       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13433
13434     test_element = Tile[test_x][test_y];
13435
13436     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13437        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13438     */
13439     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13440         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13441     {
13442       // good thing is player or penguin that does not move away
13443       if (IS_PLAYER(test_x, test_y))
13444       {
13445         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13446
13447         if (bad_element == EL_ROBOT && player->is_moving)
13448           continue;     // robot does not kill player if he is moving
13449
13450         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13451         {
13452           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13453             continue;           // center and border element do not touch
13454         }
13455
13456         kill_x = test_x;
13457         kill_y = test_y;
13458
13459         break;
13460       }
13461       else if (test_element == EL_PENGUIN)
13462       {
13463         kill_x = test_x;
13464         kill_y = test_y;
13465
13466         break;
13467       }
13468     }
13469   }
13470
13471   if (kill_x != -1 || kill_y != -1)
13472   {
13473     if (IS_PLAYER(kill_x, kill_y))
13474     {
13475       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13476
13477       if (player->shield_deadly_time_left > 0 &&
13478           !IS_INDESTRUCTIBLE(bad_element))
13479         Bang(bad_x, bad_y);
13480       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13481         KillPlayer(player);
13482     }
13483     else
13484       Bang(kill_x, kill_y);
13485   }
13486 }
13487
13488 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13489 {
13490   int bad_element = Tile[bad_x][bad_y];
13491   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13492   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13493   int test_x = bad_x + dx, test_y = bad_y + dy;
13494   int test_move_dir, test_element;
13495   int kill_x = -1, kill_y = -1;
13496
13497   if (!IN_LEV_FIELD(test_x, test_y))
13498     return;
13499
13500   test_move_dir =
13501     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13502
13503   test_element = Tile[test_x][test_y];
13504
13505   if (test_move_dir != bad_move_dir)
13506   {
13507     // good thing can be player or penguin that does not move away
13508     if (IS_PLAYER(test_x, test_y))
13509     {
13510       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13511
13512       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13513          player as being hit when he is moving towards the bad thing, because
13514          the "get hit by" condition would be lost after the player stops) */
13515       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13516         return;         // player moves away from bad thing
13517
13518       kill_x = test_x;
13519       kill_y = test_y;
13520     }
13521     else if (test_element == EL_PENGUIN)
13522     {
13523       kill_x = test_x;
13524       kill_y = test_y;
13525     }
13526   }
13527
13528   if (kill_x != -1 || kill_y != -1)
13529   {
13530     if (IS_PLAYER(kill_x, kill_y))
13531     {
13532       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13533
13534       if (player->shield_deadly_time_left > 0 &&
13535           !IS_INDESTRUCTIBLE(bad_element))
13536         Bang(bad_x, bad_y);
13537       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13538         KillPlayer(player);
13539     }
13540     else
13541       Bang(kill_x, kill_y);
13542   }
13543 }
13544
13545 void TestIfPlayerTouchesBadThing(int x, int y)
13546 {
13547   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13548 }
13549
13550 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13551 {
13552   TestIfGoodThingHitsBadThing(x, y, move_dir);
13553 }
13554
13555 void TestIfBadThingTouchesPlayer(int x, int y)
13556 {
13557   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13558 }
13559
13560 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13561 {
13562   TestIfBadThingHitsGoodThing(x, y, move_dir);
13563 }
13564
13565 void TestIfFriendTouchesBadThing(int x, int y)
13566 {
13567   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13568 }
13569
13570 void TestIfBadThingTouchesFriend(int x, int y)
13571 {
13572   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13573 }
13574
13575 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13576 {
13577   int i, kill_x = bad_x, kill_y = bad_y;
13578   static int xy[4][2] =
13579   {
13580     { 0, -1 },
13581     { -1, 0 },
13582     { +1, 0 },
13583     { 0, +1 }
13584   };
13585
13586   for (i = 0; i < NUM_DIRECTIONS; i++)
13587   {
13588     int x, y, element;
13589
13590     x = bad_x + xy[i][0];
13591     y = bad_y + xy[i][1];
13592     if (!IN_LEV_FIELD(x, y))
13593       continue;
13594
13595     element = Tile[x][y];
13596     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13597         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13598     {
13599       kill_x = x;
13600       kill_y = y;
13601       break;
13602     }
13603   }
13604
13605   if (kill_x != bad_x || kill_y != bad_y)
13606     Bang(bad_x, bad_y);
13607 }
13608
13609 void KillPlayer(struct PlayerInfo *player)
13610 {
13611   int jx = player->jx, jy = player->jy;
13612
13613   if (!player->active)
13614     return;
13615
13616 #if 0
13617   Debug("game:playing:KillPlayer",
13618         "0: killed == %d, active == %d, reanimated == %d",
13619         player->killed, player->active, player->reanimated);
13620 #endif
13621
13622   /* the following code was introduced to prevent an infinite loop when calling
13623      -> Bang()
13624      -> CheckTriggeredElementChangeExt()
13625      -> ExecuteCustomElementAction()
13626      -> KillPlayer()
13627      -> (infinitely repeating the above sequence of function calls)
13628      which occurs when killing the player while having a CE with the setting
13629      "kill player X when explosion of <player X>"; the solution using a new
13630      field "player->killed" was chosen for backwards compatibility, although
13631      clever use of the fields "player->active" etc. would probably also work */
13632 #if 1
13633   if (player->killed)
13634     return;
13635 #endif
13636
13637   player->killed = TRUE;
13638
13639   // remove accessible field at the player's position
13640   Tile[jx][jy] = EL_EMPTY;
13641
13642   // deactivate shield (else Bang()/Explode() would not work right)
13643   player->shield_normal_time_left = 0;
13644   player->shield_deadly_time_left = 0;
13645
13646 #if 0
13647   Debug("game:playing:KillPlayer",
13648         "1: killed == %d, active == %d, reanimated == %d",
13649         player->killed, player->active, player->reanimated);
13650 #endif
13651
13652   Bang(jx, jy);
13653
13654 #if 0
13655   Debug("game:playing:KillPlayer",
13656         "2: killed == %d, active == %d, reanimated == %d",
13657         player->killed, player->active, player->reanimated);
13658 #endif
13659
13660   if (player->reanimated)       // killed player may have been reanimated
13661     player->killed = player->reanimated = FALSE;
13662   else
13663     BuryPlayer(player);
13664 }
13665
13666 static void KillPlayerUnlessEnemyProtected(int x, int y)
13667 {
13668   if (!PLAYER_ENEMY_PROTECTED(x, y))
13669     KillPlayer(PLAYERINFO(x, y));
13670 }
13671
13672 static void KillPlayerUnlessExplosionProtected(int x, int y)
13673 {
13674   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13675     KillPlayer(PLAYERINFO(x, y));
13676 }
13677
13678 void BuryPlayer(struct PlayerInfo *player)
13679 {
13680   int jx = player->jx, jy = player->jy;
13681
13682   if (!player->active)
13683     return;
13684
13685   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13686   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13687
13688   RemovePlayer(player);
13689
13690   player->buried = TRUE;
13691
13692   if (game.all_players_gone)
13693     game.GameOver = TRUE;
13694 }
13695
13696 void RemovePlayer(struct PlayerInfo *player)
13697 {
13698   int jx = player->jx, jy = player->jy;
13699   int i, found = FALSE;
13700
13701   player->present = FALSE;
13702   player->active = FALSE;
13703
13704   // required for some CE actions (even if the player is not active anymore)
13705   player->MovPos = 0;
13706
13707   if (!ExplodeField[jx][jy])
13708     StorePlayer[jx][jy] = 0;
13709
13710   if (player->is_moving)
13711     TEST_DrawLevelField(player->last_jx, player->last_jy);
13712
13713   for (i = 0; i < MAX_PLAYERS; i++)
13714     if (stored_player[i].active)
13715       found = TRUE;
13716
13717   if (!found)
13718   {
13719     game.all_players_gone = TRUE;
13720     game.GameOver = TRUE;
13721   }
13722
13723   game.exit_x = game.robot_wheel_x = jx;
13724   game.exit_y = game.robot_wheel_y = jy;
13725 }
13726
13727 void ExitPlayer(struct PlayerInfo *player)
13728 {
13729   DrawPlayer(player);   // needed here only to cleanup last field
13730   RemovePlayer(player);
13731
13732   if (game.players_still_needed > 0)
13733     game.players_still_needed--;
13734 }
13735
13736 static void setFieldForSnapping(int x, int y, int element, int direction)
13737 {
13738   struct ElementInfo *ei = &element_info[element];
13739   int direction_bit = MV_DIR_TO_BIT(direction);
13740   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13741   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13742                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13743
13744   Tile[x][y] = EL_ELEMENT_SNAPPING;
13745   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13746
13747   ResetGfxAnimation(x, y);
13748
13749   GfxElement[x][y] = element;
13750   GfxAction[x][y] = action;
13751   GfxDir[x][y] = direction;
13752   GfxFrame[x][y] = -1;
13753 }
13754
13755 /*
13756   =============================================================================
13757   checkDiagonalPushing()
13758   -----------------------------------------------------------------------------
13759   check if diagonal input device direction results in pushing of object
13760   (by checking if the alternative direction is walkable, diggable, ...)
13761   =============================================================================
13762 */
13763
13764 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13765                                     int x, int y, int real_dx, int real_dy)
13766 {
13767   int jx, jy, dx, dy, xx, yy;
13768
13769   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13770     return TRUE;
13771
13772   // diagonal direction: check alternative direction
13773   jx = player->jx;
13774   jy = player->jy;
13775   dx = x - jx;
13776   dy = y - jy;
13777   xx = jx + (dx == 0 ? real_dx : 0);
13778   yy = jy + (dy == 0 ? real_dy : 0);
13779
13780   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13781 }
13782
13783 /*
13784   =============================================================================
13785   DigField()
13786   -----------------------------------------------------------------------------
13787   x, y:                 field next to player (non-diagonal) to try to dig to
13788   real_dx, real_dy:     direction as read from input device (can be diagonal)
13789   =============================================================================
13790 */
13791
13792 static int DigField(struct PlayerInfo *player,
13793                     int oldx, int oldy, int x, int y,
13794                     int real_dx, int real_dy, int mode)
13795 {
13796   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13797   boolean player_was_pushing = player->is_pushing;
13798   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13799   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13800   int jx = oldx, jy = oldy;
13801   int dx = x - jx, dy = y - jy;
13802   int nextx = x + dx, nexty = y + dy;
13803   int move_direction = (dx == -1 ? MV_LEFT  :
13804                         dx == +1 ? MV_RIGHT :
13805                         dy == -1 ? MV_UP    :
13806                         dy == +1 ? MV_DOWN  : MV_NONE);
13807   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13808   int dig_side = MV_DIR_OPPOSITE(move_direction);
13809   int old_element = Tile[jx][jy];
13810   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13811   int collect_count;
13812
13813   if (is_player)                // function can also be called by EL_PENGUIN
13814   {
13815     if (player->MovPos == 0)
13816     {
13817       player->is_digging = FALSE;
13818       player->is_collecting = FALSE;
13819     }
13820
13821     if (player->MovPos == 0)    // last pushing move finished
13822       player->is_pushing = FALSE;
13823
13824     if (mode == DF_NO_PUSH)     // player just stopped pushing
13825     {
13826       player->is_switching = FALSE;
13827       player->push_delay = -1;
13828
13829       return MP_NO_ACTION;
13830     }
13831   }
13832
13833   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13834     old_element = Back[jx][jy];
13835
13836   // in case of element dropped at player position, check background
13837   else if (Back[jx][jy] != EL_EMPTY &&
13838            game.engine_version >= VERSION_IDENT(2,2,0,0))
13839     old_element = Back[jx][jy];
13840
13841   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13842     return MP_NO_ACTION;        // field has no opening in this direction
13843
13844   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13845     return MP_NO_ACTION;        // field has no opening in this direction
13846
13847   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13848   {
13849     SplashAcid(x, y);
13850
13851     Tile[jx][jy] = player->artwork_element;
13852     InitMovingField(jx, jy, MV_DOWN);
13853     Store[jx][jy] = EL_ACID;
13854     ContinueMoving(jx, jy);
13855     BuryPlayer(player);
13856
13857     return MP_DONT_RUN_INTO;
13858   }
13859
13860   if (player_can_move && DONT_RUN_INTO(element))
13861   {
13862     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13863
13864     return MP_DONT_RUN_INTO;
13865   }
13866
13867   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13868     return MP_NO_ACTION;
13869
13870   collect_count = element_info[element].collect_count_initial;
13871
13872   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13873     return MP_NO_ACTION;
13874
13875   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13876     player_can_move = player_can_move_or_snap;
13877
13878   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13879       game.engine_version >= VERSION_IDENT(2,2,0,0))
13880   {
13881     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13882                                player->index_bit, dig_side);
13883     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13884                                         player->index_bit, dig_side);
13885
13886     if (element == EL_DC_LANDMINE)
13887       Bang(x, y);
13888
13889     if (Tile[x][y] != element)          // field changed by snapping
13890       return MP_ACTION;
13891
13892     return MP_NO_ACTION;
13893   }
13894
13895   if (player->gravity && is_player && !player->is_auto_moving &&
13896       canFallDown(player) && move_direction != MV_DOWN &&
13897       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13898     return MP_NO_ACTION;        // player cannot walk here due to gravity
13899
13900   if (player_can_move &&
13901       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13902   {
13903     int sound_element = SND_ELEMENT(element);
13904     int sound_action = ACTION_WALKING;
13905
13906     if (IS_RND_GATE(element))
13907     {
13908       if (!player->key[RND_GATE_NR(element)])
13909         return MP_NO_ACTION;
13910     }
13911     else if (IS_RND_GATE_GRAY(element))
13912     {
13913       if (!player->key[RND_GATE_GRAY_NR(element)])
13914         return MP_NO_ACTION;
13915     }
13916     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13917     {
13918       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13919         return MP_NO_ACTION;
13920     }
13921     else if (element == EL_EXIT_OPEN ||
13922              element == EL_EM_EXIT_OPEN ||
13923              element == EL_EM_EXIT_OPENING ||
13924              element == EL_STEEL_EXIT_OPEN ||
13925              element == EL_EM_STEEL_EXIT_OPEN ||
13926              element == EL_EM_STEEL_EXIT_OPENING ||
13927              element == EL_SP_EXIT_OPEN ||
13928              element == EL_SP_EXIT_OPENING)
13929     {
13930       sound_action = ACTION_PASSING;    // player is passing exit
13931     }
13932     else if (element == EL_EMPTY)
13933     {
13934       sound_action = ACTION_MOVING;             // nothing to walk on
13935     }
13936
13937     // play sound from background or player, whatever is available
13938     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13939       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13940     else
13941       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13942   }
13943   else if (player_can_move &&
13944            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13945   {
13946     if (!ACCESS_FROM(element, opposite_direction))
13947       return MP_NO_ACTION;      // field not accessible from this direction
13948
13949     if (CAN_MOVE(element))      // only fixed elements can be passed!
13950       return MP_NO_ACTION;
13951
13952     if (IS_EM_GATE(element))
13953     {
13954       if (!player->key[EM_GATE_NR(element)])
13955         return MP_NO_ACTION;
13956     }
13957     else if (IS_EM_GATE_GRAY(element))
13958     {
13959       if (!player->key[EM_GATE_GRAY_NR(element)])
13960         return MP_NO_ACTION;
13961     }
13962     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13963     {
13964       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13965         return MP_NO_ACTION;
13966     }
13967     else if (IS_EMC_GATE(element))
13968     {
13969       if (!player->key[EMC_GATE_NR(element)])
13970         return MP_NO_ACTION;
13971     }
13972     else if (IS_EMC_GATE_GRAY(element))
13973     {
13974       if (!player->key[EMC_GATE_GRAY_NR(element)])
13975         return MP_NO_ACTION;
13976     }
13977     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13978     {
13979       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13980         return MP_NO_ACTION;
13981     }
13982     else if (element == EL_DC_GATE_WHITE ||
13983              element == EL_DC_GATE_WHITE_GRAY ||
13984              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13985     {
13986       if (player->num_white_keys == 0)
13987         return MP_NO_ACTION;
13988
13989       player->num_white_keys--;
13990     }
13991     else if (IS_SP_PORT(element))
13992     {
13993       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13994           element == EL_SP_GRAVITY_PORT_RIGHT ||
13995           element == EL_SP_GRAVITY_PORT_UP ||
13996           element == EL_SP_GRAVITY_PORT_DOWN)
13997         player->gravity = !player->gravity;
13998       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13999                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14000                element == EL_SP_GRAVITY_ON_PORT_UP ||
14001                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14002         player->gravity = TRUE;
14003       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14004                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14005                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14006                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14007         player->gravity = FALSE;
14008     }
14009
14010     // automatically move to the next field with double speed
14011     player->programmed_action = move_direction;
14012
14013     if (player->move_delay_reset_counter == 0)
14014     {
14015       player->move_delay_reset_counter = 2;     // two double speed steps
14016
14017       DOUBLE_PLAYER_SPEED(player);
14018     }
14019
14020     PlayLevelSoundAction(x, y, ACTION_PASSING);
14021   }
14022   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14023   {
14024     RemoveField(x, y);
14025
14026     if (mode != DF_SNAP)
14027     {
14028       GfxElement[x][y] = GFX_ELEMENT(element);
14029       player->is_digging = TRUE;
14030     }
14031
14032     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14033
14034     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14035                                         player->index_bit, dig_side);
14036
14037     // if digging triggered player relocation, finish digging tile
14038     if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14039       setFieldForSnapping(x, y, element, move_direction);
14040
14041     if (mode == DF_SNAP)
14042     {
14043       if (level.block_snap_field)
14044         setFieldForSnapping(x, y, element, move_direction);
14045       else
14046         TestIfElementTouchesCustomElement(x, y);        // for empty space
14047
14048       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14049                                           player->index_bit, dig_side);
14050     }
14051   }
14052   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14053   {
14054     RemoveField(x, y);
14055
14056     if (is_player && mode != DF_SNAP)
14057     {
14058       GfxElement[x][y] = element;
14059       player->is_collecting = TRUE;
14060     }
14061
14062     if (element == EL_SPEED_PILL)
14063     {
14064       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14065     }
14066     else if (element == EL_EXTRA_TIME && level.time > 0)
14067     {
14068       TimeLeft += level.extra_time;
14069
14070       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14071
14072       DisplayGameControlValues();
14073     }
14074     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14075     {
14076       player->shield_normal_time_left += level.shield_normal_time;
14077       if (element == EL_SHIELD_DEADLY)
14078         player->shield_deadly_time_left += level.shield_deadly_time;
14079     }
14080     else if (element == EL_DYNAMITE ||
14081              element == EL_EM_DYNAMITE ||
14082              element == EL_SP_DISK_RED)
14083     {
14084       if (player->inventory_size < MAX_INVENTORY_SIZE)
14085         player->inventory_element[player->inventory_size++] = element;
14086
14087       DrawGameDoorValues();
14088     }
14089     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14090     {
14091       player->dynabomb_count++;
14092       player->dynabombs_left++;
14093     }
14094     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14095     {
14096       player->dynabomb_size++;
14097     }
14098     else if (element == EL_DYNABOMB_INCREASE_POWER)
14099     {
14100       player->dynabomb_xl = TRUE;
14101     }
14102     else if (IS_KEY(element))
14103     {
14104       player->key[KEY_NR(element)] = TRUE;
14105
14106       DrawGameDoorValues();
14107     }
14108     else if (element == EL_DC_KEY_WHITE)
14109     {
14110       player->num_white_keys++;
14111
14112       // display white keys?
14113       // DrawGameDoorValues();
14114     }
14115     else if (IS_ENVELOPE(element))
14116     {
14117       player->show_envelope = element;
14118     }
14119     else if (element == EL_EMC_LENSES)
14120     {
14121       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14122
14123       RedrawAllInvisibleElementsForLenses();
14124     }
14125     else if (element == EL_EMC_MAGNIFIER)
14126     {
14127       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14128
14129       RedrawAllInvisibleElementsForMagnifier();
14130     }
14131     else if (IS_DROPPABLE(element) ||
14132              IS_THROWABLE(element))     // can be collected and dropped
14133     {
14134       int i;
14135
14136       if (collect_count == 0)
14137         player->inventory_infinite_element = element;
14138       else
14139         for (i = 0; i < collect_count; i++)
14140           if (player->inventory_size < MAX_INVENTORY_SIZE)
14141             player->inventory_element[player->inventory_size++] = element;
14142
14143       DrawGameDoorValues();
14144     }
14145     else if (collect_count > 0)
14146     {
14147       game.gems_still_needed -= collect_count;
14148       if (game.gems_still_needed < 0)
14149         game.gems_still_needed = 0;
14150
14151       game.snapshot.collected_item = TRUE;
14152
14153       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14154
14155       DisplayGameControlValues();
14156     }
14157
14158     RaiseScoreElement(element);
14159     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14160
14161     if (is_player)
14162     {
14163       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14164                                           player->index_bit, dig_side);
14165
14166       // if collecting triggered player relocation, finish collecting tile
14167       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14168         setFieldForSnapping(x, y, element, move_direction);
14169     }
14170
14171     if (mode == DF_SNAP)
14172     {
14173       if (level.block_snap_field)
14174         setFieldForSnapping(x, y, element, move_direction);
14175       else
14176         TestIfElementTouchesCustomElement(x, y);        // for empty space
14177
14178       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14179                                           player->index_bit, dig_side);
14180     }
14181   }
14182   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14183   {
14184     if (mode == DF_SNAP && element != EL_BD_ROCK)
14185       return MP_NO_ACTION;
14186
14187     if (CAN_FALL(element) && dy)
14188       return MP_NO_ACTION;
14189
14190     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14191         !(element == EL_SPRING && level.use_spring_bug))
14192       return MP_NO_ACTION;
14193
14194     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14195         ((move_direction & MV_VERTICAL &&
14196           ((element_info[element].move_pattern & MV_LEFT &&
14197             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14198            (element_info[element].move_pattern & MV_RIGHT &&
14199             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14200          (move_direction & MV_HORIZONTAL &&
14201           ((element_info[element].move_pattern & MV_UP &&
14202             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14203            (element_info[element].move_pattern & MV_DOWN &&
14204             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14205       return MP_NO_ACTION;
14206
14207     // do not push elements already moving away faster than player
14208     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14209         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14210       return MP_NO_ACTION;
14211
14212     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14213     {
14214       if (player->push_delay_value == -1 || !player_was_pushing)
14215         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14216     }
14217     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14218     {
14219       if (player->push_delay_value == -1)
14220         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14221     }
14222     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14223     {
14224       if (!player->is_pushing)
14225         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14226     }
14227
14228     player->is_pushing = TRUE;
14229     player->is_active = TRUE;
14230
14231     if (!(IN_LEV_FIELD(nextx, nexty) &&
14232           (IS_FREE(nextx, nexty) ||
14233            (IS_SB_ELEMENT(element) &&
14234             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14235            (IS_CUSTOM_ELEMENT(element) &&
14236             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14237       return MP_NO_ACTION;
14238
14239     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14240       return MP_NO_ACTION;
14241
14242     if (player->push_delay == -1)       // new pushing; restart delay
14243       player->push_delay = 0;
14244
14245     if (player->push_delay < player->push_delay_value &&
14246         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14247         element != EL_SPRING && element != EL_BALLOON)
14248     {
14249       // make sure that there is no move delay before next try to push
14250       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14251         player->move_delay = 0;
14252
14253       return MP_NO_ACTION;
14254     }
14255
14256     if (IS_CUSTOM_ELEMENT(element) &&
14257         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14258     {
14259       if (!DigFieldByCE(nextx, nexty, element))
14260         return MP_NO_ACTION;
14261     }
14262
14263     if (IS_SB_ELEMENT(element))
14264     {
14265       boolean sokoban_task_solved = FALSE;
14266
14267       if (element == EL_SOKOBAN_FIELD_FULL)
14268       {
14269         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14270
14271         IncrementSokobanFieldsNeeded();
14272         IncrementSokobanObjectsNeeded();
14273       }
14274
14275       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14276       {
14277         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14278
14279         DecrementSokobanFieldsNeeded();
14280         DecrementSokobanObjectsNeeded();
14281
14282         // sokoban object was pushed from empty field to sokoban field
14283         if (Back[x][y] == EL_EMPTY)
14284           sokoban_task_solved = TRUE;
14285       }
14286
14287       Tile[x][y] = EL_SOKOBAN_OBJECT;
14288
14289       if (Back[x][y] == Back[nextx][nexty])
14290         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14291       else if (Back[x][y] != 0)
14292         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14293                                     ACTION_EMPTYING);
14294       else
14295         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14296                                     ACTION_FILLING);
14297
14298       if (sokoban_task_solved &&
14299           game.sokoban_fields_still_needed == 0 &&
14300           game.sokoban_objects_still_needed == 0 &&
14301           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14302       {
14303         game.players_still_needed = 0;
14304
14305         LevelSolved();
14306
14307         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14308       }
14309     }
14310     else
14311       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14312
14313     InitMovingField(x, y, move_direction);
14314     GfxAction[x][y] = ACTION_PUSHING;
14315
14316     if (mode == DF_SNAP)
14317       ContinueMoving(x, y);
14318     else
14319       MovPos[x][y] = (dx != 0 ? dx : dy);
14320
14321     Pushed[x][y] = TRUE;
14322     Pushed[nextx][nexty] = TRUE;
14323
14324     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14325       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14326     else
14327       player->push_delay_value = -1;    // get new value later
14328
14329     // check for element change _after_ element has been pushed
14330     if (game.use_change_when_pushing_bug)
14331     {
14332       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14333                                  player->index_bit, dig_side);
14334       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14335                                           player->index_bit, dig_side);
14336     }
14337   }
14338   else if (IS_SWITCHABLE(element))
14339   {
14340     if (PLAYER_SWITCHING(player, x, y))
14341     {
14342       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14343                                           player->index_bit, dig_side);
14344
14345       return MP_ACTION;
14346     }
14347
14348     player->is_switching = TRUE;
14349     player->switch_x = x;
14350     player->switch_y = y;
14351
14352     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14353
14354     if (element == EL_ROBOT_WHEEL)
14355     {
14356       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14357
14358       game.robot_wheel_x = x;
14359       game.robot_wheel_y = y;
14360       game.robot_wheel_active = TRUE;
14361
14362       TEST_DrawLevelField(x, y);
14363     }
14364     else if (element == EL_SP_TERMINAL)
14365     {
14366       int xx, yy;
14367
14368       SCAN_PLAYFIELD(xx, yy)
14369       {
14370         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14371         {
14372           Bang(xx, yy);
14373         }
14374         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14375         {
14376           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14377
14378           ResetGfxAnimation(xx, yy);
14379           TEST_DrawLevelField(xx, yy);
14380         }
14381       }
14382     }
14383     else if (IS_BELT_SWITCH(element))
14384     {
14385       ToggleBeltSwitch(x, y);
14386     }
14387     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14388              element == EL_SWITCHGATE_SWITCH_DOWN ||
14389              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14390              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14391     {
14392       ToggleSwitchgateSwitch(x, y);
14393     }
14394     else if (element == EL_LIGHT_SWITCH ||
14395              element == EL_LIGHT_SWITCH_ACTIVE)
14396     {
14397       ToggleLightSwitch(x, y);
14398     }
14399     else if (element == EL_TIMEGATE_SWITCH ||
14400              element == EL_DC_TIMEGATE_SWITCH)
14401     {
14402       ActivateTimegateSwitch(x, y);
14403     }
14404     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14405              element == EL_BALLOON_SWITCH_RIGHT ||
14406              element == EL_BALLOON_SWITCH_UP    ||
14407              element == EL_BALLOON_SWITCH_DOWN  ||
14408              element == EL_BALLOON_SWITCH_NONE  ||
14409              element == EL_BALLOON_SWITCH_ANY)
14410     {
14411       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14412                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14413                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14414                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14415                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14416                              move_direction);
14417     }
14418     else if (element == EL_LAMP)
14419     {
14420       Tile[x][y] = EL_LAMP_ACTIVE;
14421       game.lights_still_needed--;
14422
14423       ResetGfxAnimation(x, y);
14424       TEST_DrawLevelField(x, y);
14425     }
14426     else if (element == EL_TIME_ORB_FULL)
14427     {
14428       Tile[x][y] = EL_TIME_ORB_EMPTY;
14429
14430       if (level.time > 0 || level.use_time_orb_bug)
14431       {
14432         TimeLeft += level.time_orb_time;
14433         game.no_time_limit = FALSE;
14434
14435         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14436
14437         DisplayGameControlValues();
14438       }
14439
14440       ResetGfxAnimation(x, y);
14441       TEST_DrawLevelField(x, y);
14442     }
14443     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14444              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14445     {
14446       int xx, yy;
14447
14448       game.ball_active = !game.ball_active;
14449
14450       SCAN_PLAYFIELD(xx, yy)
14451       {
14452         int e = Tile[xx][yy];
14453
14454         if (game.ball_active)
14455         {
14456           if (e == EL_EMC_MAGIC_BALL)
14457             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14458           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14459             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14460         }
14461         else
14462         {
14463           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14464             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14465           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14466             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14467         }
14468       }
14469     }
14470
14471     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14472                                         player->index_bit, dig_side);
14473
14474     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14475                                         player->index_bit, dig_side);
14476
14477     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14478                                         player->index_bit, dig_side);
14479
14480     return MP_ACTION;
14481   }
14482   else
14483   {
14484     if (!PLAYER_SWITCHING(player, x, y))
14485     {
14486       player->is_switching = TRUE;
14487       player->switch_x = x;
14488       player->switch_y = y;
14489
14490       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14491                                  player->index_bit, dig_side);
14492       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14493                                           player->index_bit, dig_side);
14494
14495       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14496                                  player->index_bit, dig_side);
14497       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14498                                           player->index_bit, dig_side);
14499     }
14500
14501     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14502                                player->index_bit, dig_side);
14503     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14504                                         player->index_bit, dig_side);
14505
14506     return MP_NO_ACTION;
14507   }
14508
14509   player->push_delay = -1;
14510
14511   if (is_player)                // function can also be called by EL_PENGUIN
14512   {
14513     if (Tile[x][y] != element)          // really digged/collected something
14514     {
14515       player->is_collecting = !player->is_digging;
14516       player->is_active = TRUE;
14517     }
14518   }
14519
14520   return MP_MOVING;
14521 }
14522
14523 static boolean DigFieldByCE(int x, int y, int digging_element)
14524 {
14525   int element = Tile[x][y];
14526
14527   if (!IS_FREE(x, y))
14528   {
14529     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14530                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14531                   ACTION_BREAKING);
14532
14533     // no element can dig solid indestructible elements
14534     if (IS_INDESTRUCTIBLE(element) &&
14535         !IS_DIGGABLE(element) &&
14536         !IS_COLLECTIBLE(element))
14537       return FALSE;
14538
14539     if (AmoebaNr[x][y] &&
14540         (element == EL_AMOEBA_FULL ||
14541          element == EL_BD_AMOEBA ||
14542          element == EL_AMOEBA_GROWING))
14543     {
14544       AmoebaCnt[AmoebaNr[x][y]]--;
14545       AmoebaCnt2[AmoebaNr[x][y]]--;
14546     }
14547
14548     if (IS_MOVING(x, y))
14549       RemoveMovingField(x, y);
14550     else
14551     {
14552       RemoveField(x, y);
14553       TEST_DrawLevelField(x, y);
14554     }
14555
14556     // if digged element was about to explode, prevent the explosion
14557     ExplodeField[x][y] = EX_TYPE_NONE;
14558
14559     PlayLevelSoundAction(x, y, action);
14560   }
14561
14562   Store[x][y] = EL_EMPTY;
14563
14564   // this makes it possible to leave the removed element again
14565   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14566     Store[x][y] = element;
14567
14568   return TRUE;
14569 }
14570
14571 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14572 {
14573   int jx = player->jx, jy = player->jy;
14574   int x = jx + dx, y = jy + dy;
14575   int snap_direction = (dx == -1 ? MV_LEFT  :
14576                         dx == +1 ? MV_RIGHT :
14577                         dy == -1 ? MV_UP    :
14578                         dy == +1 ? MV_DOWN  : MV_NONE);
14579   boolean can_continue_snapping = (level.continuous_snapping &&
14580                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14581
14582   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14583     return FALSE;
14584
14585   if (!player->active || !IN_LEV_FIELD(x, y))
14586     return FALSE;
14587
14588   if (dx && dy)
14589     return FALSE;
14590
14591   if (!dx && !dy)
14592   {
14593     if (player->MovPos == 0)
14594       player->is_pushing = FALSE;
14595
14596     player->is_snapping = FALSE;
14597
14598     if (player->MovPos == 0)
14599     {
14600       player->is_moving = FALSE;
14601       player->is_digging = FALSE;
14602       player->is_collecting = FALSE;
14603     }
14604
14605     return FALSE;
14606   }
14607
14608   // prevent snapping with already pressed snap key when not allowed
14609   if (player->is_snapping && !can_continue_snapping)
14610     return FALSE;
14611
14612   player->MovDir = snap_direction;
14613
14614   if (player->MovPos == 0)
14615   {
14616     player->is_moving = FALSE;
14617     player->is_digging = FALSE;
14618     player->is_collecting = FALSE;
14619   }
14620
14621   player->is_dropping = FALSE;
14622   player->is_dropping_pressed = FALSE;
14623   player->drop_pressed_delay = 0;
14624
14625   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14626     return FALSE;
14627
14628   player->is_snapping = TRUE;
14629   player->is_active = TRUE;
14630
14631   if (player->MovPos == 0)
14632   {
14633     player->is_moving = FALSE;
14634     player->is_digging = FALSE;
14635     player->is_collecting = FALSE;
14636   }
14637
14638   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14639     TEST_DrawLevelField(player->last_jx, player->last_jy);
14640
14641   TEST_DrawLevelField(x, y);
14642
14643   return TRUE;
14644 }
14645
14646 static boolean DropElement(struct PlayerInfo *player)
14647 {
14648   int old_element, new_element;
14649   int dropx = player->jx, dropy = player->jy;
14650   int drop_direction = player->MovDir;
14651   int drop_side = drop_direction;
14652   int drop_element = get_next_dropped_element(player);
14653
14654   /* do not drop an element on top of another element; when holding drop key
14655      pressed without moving, dropped element must move away before the next
14656      element can be dropped (this is especially important if the next element
14657      is dynamite, which can be placed on background for historical reasons) */
14658   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14659     return MP_ACTION;
14660
14661   if (IS_THROWABLE(drop_element))
14662   {
14663     dropx += GET_DX_FROM_DIR(drop_direction);
14664     dropy += GET_DY_FROM_DIR(drop_direction);
14665
14666     if (!IN_LEV_FIELD(dropx, dropy))
14667       return FALSE;
14668   }
14669
14670   old_element = Tile[dropx][dropy];     // old element at dropping position
14671   new_element = drop_element;           // default: no change when dropping
14672
14673   // check if player is active, not moving and ready to drop
14674   if (!player->active || player->MovPos || player->drop_delay > 0)
14675     return FALSE;
14676
14677   // check if player has anything that can be dropped
14678   if (new_element == EL_UNDEFINED)
14679     return FALSE;
14680
14681   // only set if player has anything that can be dropped
14682   player->is_dropping_pressed = TRUE;
14683
14684   // check if drop key was pressed long enough for EM style dynamite
14685   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14686     return FALSE;
14687
14688   // check if anything can be dropped at the current position
14689   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14690     return FALSE;
14691
14692   // collected custom elements can only be dropped on empty fields
14693   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14694     return FALSE;
14695
14696   if (old_element != EL_EMPTY)
14697     Back[dropx][dropy] = old_element;   // store old element on this field
14698
14699   ResetGfxAnimation(dropx, dropy);
14700   ResetRandomAnimationValue(dropx, dropy);
14701
14702   if (player->inventory_size > 0 ||
14703       player->inventory_infinite_element != EL_UNDEFINED)
14704   {
14705     if (player->inventory_size > 0)
14706     {
14707       player->inventory_size--;
14708
14709       DrawGameDoorValues();
14710
14711       if (new_element == EL_DYNAMITE)
14712         new_element = EL_DYNAMITE_ACTIVE;
14713       else if (new_element == EL_EM_DYNAMITE)
14714         new_element = EL_EM_DYNAMITE_ACTIVE;
14715       else if (new_element == EL_SP_DISK_RED)
14716         new_element = EL_SP_DISK_RED_ACTIVE;
14717     }
14718
14719     Tile[dropx][dropy] = new_element;
14720
14721     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14722       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14723                           el2img(Tile[dropx][dropy]), 0);
14724
14725     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14726
14727     // needed if previous element just changed to "empty" in the last frame
14728     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14729
14730     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14731                                player->index_bit, drop_side);
14732     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14733                                         CE_PLAYER_DROPS_X,
14734                                         player->index_bit, drop_side);
14735
14736     TestIfElementTouchesCustomElement(dropx, dropy);
14737   }
14738   else          // player is dropping a dyna bomb
14739   {
14740     player->dynabombs_left--;
14741
14742     Tile[dropx][dropy] = new_element;
14743
14744     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14745       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14746                           el2img(Tile[dropx][dropy]), 0);
14747
14748     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14749   }
14750
14751   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14752     InitField_WithBug1(dropx, dropy, FALSE);
14753
14754   new_element = Tile[dropx][dropy];     // element might have changed
14755
14756   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14757       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14758   {
14759     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14760       MovDir[dropx][dropy] = drop_direction;
14761
14762     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14763
14764     // do not cause impact style collision by dropping elements that can fall
14765     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14766   }
14767
14768   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14769   player->is_dropping = TRUE;
14770
14771   player->drop_pressed_delay = 0;
14772   player->is_dropping_pressed = FALSE;
14773
14774   player->drop_x = dropx;
14775   player->drop_y = dropy;
14776
14777   return TRUE;
14778 }
14779
14780 // ----------------------------------------------------------------------------
14781 // game sound playing functions
14782 // ----------------------------------------------------------------------------
14783
14784 static int *loop_sound_frame = NULL;
14785 static int *loop_sound_volume = NULL;
14786
14787 void InitPlayLevelSound(void)
14788 {
14789   int num_sounds = getSoundListSize();
14790
14791   checked_free(loop_sound_frame);
14792   checked_free(loop_sound_volume);
14793
14794   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14795   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14796 }
14797
14798 static void PlayLevelSound(int x, int y, int nr)
14799 {
14800   int sx = SCREENX(x), sy = SCREENY(y);
14801   int volume, stereo_position;
14802   int max_distance = 8;
14803   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14804
14805   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14806       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14807     return;
14808
14809   if (!IN_LEV_FIELD(x, y) ||
14810       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14811       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14812     return;
14813
14814   volume = SOUND_MAX_VOLUME;
14815
14816   if (!IN_SCR_FIELD(sx, sy))
14817   {
14818     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14819     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14820
14821     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14822   }
14823
14824   stereo_position = (SOUND_MAX_LEFT +
14825                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14826                      (SCR_FIELDX + 2 * max_distance));
14827
14828   if (IS_LOOP_SOUND(nr))
14829   {
14830     /* This assures that quieter loop sounds do not overwrite louder ones,
14831        while restarting sound volume comparison with each new game frame. */
14832
14833     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14834       return;
14835
14836     loop_sound_volume[nr] = volume;
14837     loop_sound_frame[nr] = FrameCounter;
14838   }
14839
14840   PlaySoundExt(nr, volume, stereo_position, type);
14841 }
14842
14843 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14844 {
14845   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14846                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14847                  y < LEVELY(BY1) ? LEVELY(BY1) :
14848                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14849                  sound_action);
14850 }
14851
14852 static void PlayLevelSoundAction(int x, int y, int action)
14853 {
14854   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14855 }
14856
14857 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14858 {
14859   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14860
14861   if (sound_effect != SND_UNDEFINED)
14862     PlayLevelSound(x, y, sound_effect);
14863 }
14864
14865 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14866                                               int action)
14867 {
14868   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14869
14870   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14871     PlayLevelSound(x, y, sound_effect);
14872 }
14873
14874 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14875 {
14876   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14877
14878   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14879     PlayLevelSound(x, y, sound_effect);
14880 }
14881
14882 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14883 {
14884   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14885
14886   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14887     StopSound(sound_effect);
14888 }
14889
14890 static int getLevelMusicNr(void)
14891 {
14892   if (levelset.music[level_nr] != MUS_UNDEFINED)
14893     return levelset.music[level_nr];            // from config file
14894   else
14895     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14896 }
14897
14898 static void FadeLevelSounds(void)
14899 {
14900   FadeSounds();
14901 }
14902
14903 static void FadeLevelMusic(void)
14904 {
14905   int music_nr = getLevelMusicNr();
14906   char *curr_music = getCurrentlyPlayingMusicFilename();
14907   char *next_music = getMusicInfoEntryFilename(music_nr);
14908
14909   if (!strEqual(curr_music, next_music))
14910     FadeMusic();
14911 }
14912
14913 void FadeLevelSoundsAndMusic(void)
14914 {
14915   FadeLevelSounds();
14916   FadeLevelMusic();
14917 }
14918
14919 static void PlayLevelMusic(void)
14920 {
14921   int music_nr = getLevelMusicNr();
14922   char *curr_music = getCurrentlyPlayingMusicFilename();
14923   char *next_music = getMusicInfoEntryFilename(music_nr);
14924
14925   if (!strEqual(curr_music, next_music))
14926     PlayMusicLoop(music_nr);
14927 }
14928
14929 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14930 {
14931   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14932   int offset = 0;
14933   int x = xx - offset;
14934   int y = yy - offset;
14935
14936   switch (sample)
14937   {
14938     case SOUND_blank:
14939       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14940       break;
14941
14942     case SOUND_roll:
14943       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14944       break;
14945
14946     case SOUND_stone:
14947       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14948       break;
14949
14950     case SOUND_nut:
14951       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14952       break;
14953
14954     case SOUND_crack:
14955       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14956       break;
14957
14958     case SOUND_bug:
14959       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14960       break;
14961
14962     case SOUND_tank:
14963       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14964       break;
14965
14966     case SOUND_android_clone:
14967       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14968       break;
14969
14970     case SOUND_android_move:
14971       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14972       break;
14973
14974     case SOUND_spring:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14976       break;
14977
14978     case SOUND_slurp:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14980       break;
14981
14982     case SOUND_eater:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14984       break;
14985
14986     case SOUND_eater_eat:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14988       break;
14989
14990     case SOUND_alien:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14992       break;
14993
14994     case SOUND_collect:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14996       break;
14997
14998     case SOUND_diamond:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15000       break;
15001
15002     case SOUND_squash:
15003       // !!! CHECK THIS !!!
15004 #if 1
15005       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15006 #else
15007       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15008 #endif
15009       break;
15010
15011     case SOUND_wonderfall:
15012       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15013       break;
15014
15015     case SOUND_drip:
15016       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15017       break;
15018
15019     case SOUND_push:
15020       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15021       break;
15022
15023     case SOUND_dirt:
15024       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15025       break;
15026
15027     case SOUND_acid:
15028       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15029       break;
15030
15031     case SOUND_ball:
15032       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15033       break;
15034
15035     case SOUND_slide:
15036       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15037       break;
15038
15039     case SOUND_wonder:
15040       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15041       break;
15042
15043     case SOUND_door:
15044       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15045       break;
15046
15047     case SOUND_exit_open:
15048       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15049       break;
15050
15051     case SOUND_exit_leave:
15052       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15053       break;
15054
15055     case SOUND_dynamite:
15056       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15057       break;
15058
15059     case SOUND_tick:
15060       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15061       break;
15062
15063     case SOUND_press:
15064       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15065       break;
15066
15067     case SOUND_wheel:
15068       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15069       break;
15070
15071     case SOUND_boom:
15072       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15073       break;
15074
15075     case SOUND_die:
15076       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15077       break;
15078
15079     case SOUND_time:
15080       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15081       break;
15082
15083     default:
15084       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15085       break;
15086   }
15087 }
15088
15089 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15090 {
15091   int element = map_element_SP_to_RND(element_sp);
15092   int action = map_action_SP_to_RND(action_sp);
15093   int offset = (setup.sp_show_border_elements ? 0 : 1);
15094   int x = xx - offset;
15095   int y = yy - offset;
15096
15097   PlayLevelSoundElementAction(x, y, element, action);
15098 }
15099
15100 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15101 {
15102   int element = map_element_MM_to_RND(element_mm);
15103   int action = map_action_MM_to_RND(action_mm);
15104   int offset = 0;
15105   int x = xx - offset;
15106   int y = yy - offset;
15107
15108   if (!IS_MM_ELEMENT(element))
15109     element = EL_MM_DEFAULT;
15110
15111   PlayLevelSoundElementAction(x, y, element, action);
15112 }
15113
15114 void PlaySound_MM(int sound_mm)
15115 {
15116   int sound = map_sound_MM_to_RND(sound_mm);
15117
15118   if (sound == SND_UNDEFINED)
15119     return;
15120
15121   PlaySound(sound);
15122 }
15123
15124 void PlaySoundLoop_MM(int sound_mm)
15125 {
15126   int sound = map_sound_MM_to_RND(sound_mm);
15127
15128   if (sound == SND_UNDEFINED)
15129     return;
15130
15131   PlaySoundLoop(sound);
15132 }
15133
15134 void StopSound_MM(int sound_mm)
15135 {
15136   int sound = map_sound_MM_to_RND(sound_mm);
15137
15138   if (sound == SND_UNDEFINED)
15139     return;
15140
15141   StopSound(sound);
15142 }
15143
15144 void RaiseScore(int value)
15145 {
15146   game.score += value;
15147
15148   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15149
15150   DisplayGameControlValues();
15151 }
15152
15153 void RaiseScoreElement(int element)
15154 {
15155   switch (element)
15156   {
15157     case EL_EMERALD:
15158     case EL_BD_DIAMOND:
15159     case EL_EMERALD_YELLOW:
15160     case EL_EMERALD_RED:
15161     case EL_EMERALD_PURPLE:
15162     case EL_SP_INFOTRON:
15163       RaiseScore(level.score[SC_EMERALD]);
15164       break;
15165     case EL_DIAMOND:
15166       RaiseScore(level.score[SC_DIAMOND]);
15167       break;
15168     case EL_CRYSTAL:
15169       RaiseScore(level.score[SC_CRYSTAL]);
15170       break;
15171     case EL_PEARL:
15172       RaiseScore(level.score[SC_PEARL]);
15173       break;
15174     case EL_BUG:
15175     case EL_BD_BUTTERFLY:
15176     case EL_SP_ELECTRON:
15177       RaiseScore(level.score[SC_BUG]);
15178       break;
15179     case EL_SPACESHIP:
15180     case EL_BD_FIREFLY:
15181     case EL_SP_SNIKSNAK:
15182       RaiseScore(level.score[SC_SPACESHIP]);
15183       break;
15184     case EL_YAMYAM:
15185     case EL_DARK_YAMYAM:
15186       RaiseScore(level.score[SC_YAMYAM]);
15187       break;
15188     case EL_ROBOT:
15189       RaiseScore(level.score[SC_ROBOT]);
15190       break;
15191     case EL_PACMAN:
15192       RaiseScore(level.score[SC_PACMAN]);
15193       break;
15194     case EL_NUT:
15195       RaiseScore(level.score[SC_NUT]);
15196       break;
15197     case EL_DYNAMITE:
15198     case EL_EM_DYNAMITE:
15199     case EL_SP_DISK_RED:
15200     case EL_DYNABOMB_INCREASE_NUMBER:
15201     case EL_DYNABOMB_INCREASE_SIZE:
15202     case EL_DYNABOMB_INCREASE_POWER:
15203       RaiseScore(level.score[SC_DYNAMITE]);
15204       break;
15205     case EL_SHIELD_NORMAL:
15206     case EL_SHIELD_DEADLY:
15207       RaiseScore(level.score[SC_SHIELD]);
15208       break;
15209     case EL_EXTRA_TIME:
15210       RaiseScore(level.extra_time_score);
15211       break;
15212     case EL_KEY_1:
15213     case EL_KEY_2:
15214     case EL_KEY_3:
15215     case EL_KEY_4:
15216     case EL_EM_KEY_1:
15217     case EL_EM_KEY_2:
15218     case EL_EM_KEY_3:
15219     case EL_EM_KEY_4:
15220     case EL_EMC_KEY_5:
15221     case EL_EMC_KEY_6:
15222     case EL_EMC_KEY_7:
15223     case EL_EMC_KEY_8:
15224     case EL_DC_KEY_WHITE:
15225       RaiseScore(level.score[SC_KEY]);
15226       break;
15227     default:
15228       RaiseScore(element_info[element].collect_score);
15229       break;
15230   }
15231 }
15232
15233 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15234 {
15235   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15236   {
15237     // closing door required in case of envelope style request dialogs
15238     if (!skip_request)
15239     {
15240       // prevent short reactivation of overlay buttons while closing door
15241       SetOverlayActive(FALSE);
15242
15243       CloseDoor(DOOR_CLOSE_1);
15244     }
15245
15246     if (network.enabled)
15247       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15248     else
15249     {
15250       if (quick_quit)
15251         FadeSkipNextFadeIn();
15252
15253       SetGameStatus(GAME_MODE_MAIN);
15254
15255       DrawMainMenu();
15256     }
15257   }
15258   else          // continue playing the game
15259   {
15260     if (tape.playing && tape.deactivate_display)
15261       TapeDeactivateDisplayOff(TRUE);
15262
15263     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15264
15265     if (tape.playing && tape.deactivate_display)
15266       TapeDeactivateDisplayOn();
15267   }
15268 }
15269
15270 void RequestQuitGame(boolean ask_if_really_quit)
15271 {
15272   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15273   boolean skip_request = game.all_players_gone || quick_quit;
15274
15275   RequestQuitGameExt(skip_request, quick_quit,
15276                      "Do you really want to quit the game?");
15277 }
15278
15279 void RequestRestartGame(char *message)
15280 {
15281   game.restart_game_message = NULL;
15282
15283   boolean has_started_game = hasStartedNetworkGame();
15284   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15285
15286   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15287   {
15288     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15289   }
15290   else
15291   {
15292     // needed in case of envelope request to close game panel
15293     CloseDoor(DOOR_CLOSE_1);
15294
15295     SetGameStatus(GAME_MODE_MAIN);
15296
15297     DrawMainMenu();
15298   }
15299 }
15300
15301 void CheckGameOver(void)
15302 {
15303   static boolean last_game_over = FALSE;
15304   static int game_over_delay = 0;
15305   int game_over_delay_value = 50;
15306   boolean game_over = checkGameFailed();
15307
15308   // do not handle game over if request dialog is already active
15309   if (game.request_active)
15310     return;
15311
15312   // do not ask to play again if game was never actually played
15313   if (!game.GamePlayed)
15314     return;
15315
15316   if (!game_over)
15317   {
15318     last_game_over = FALSE;
15319     game_over_delay = game_over_delay_value;
15320
15321     return;
15322   }
15323
15324   if (game_over_delay > 0)
15325   {
15326     game_over_delay--;
15327
15328     return;
15329   }
15330
15331   if (last_game_over != game_over)
15332     game.restart_game_message = (hasStartedNetworkGame() ?
15333                                  "Game over! Play it again?" :
15334                                  "Game over!");
15335
15336   last_game_over = game_over;
15337 }
15338
15339 boolean checkGameSolved(void)
15340 {
15341   // set for all game engines if level was solved
15342   return game.LevelSolved_GameEnd;
15343 }
15344
15345 boolean checkGameFailed(void)
15346 {
15347   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15348     return (game_em.game_over && !game_em.level_solved);
15349   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15350     return (game_sp.game_over && !game_sp.level_solved);
15351   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15352     return (game_mm.game_over && !game_mm.level_solved);
15353   else                          // GAME_ENGINE_TYPE_RND
15354     return (game.GameOver && !game.LevelSolved);
15355 }
15356
15357 boolean checkGameEnded(void)
15358 {
15359   return (checkGameSolved() || checkGameFailed());
15360 }
15361
15362
15363 // ----------------------------------------------------------------------------
15364 // random generator functions
15365 // ----------------------------------------------------------------------------
15366
15367 unsigned int InitEngineRandom_RND(int seed)
15368 {
15369   game.num_random_calls = 0;
15370
15371   return InitEngineRandom(seed);
15372 }
15373
15374 unsigned int RND(int max)
15375 {
15376   if (max > 0)
15377   {
15378     game.num_random_calls++;
15379
15380     return GetEngineRandom(max);
15381   }
15382
15383   return 0;
15384 }
15385
15386
15387 // ----------------------------------------------------------------------------
15388 // game engine snapshot handling functions
15389 // ----------------------------------------------------------------------------
15390
15391 struct EngineSnapshotInfo
15392 {
15393   // runtime values for custom element collect score
15394   int collect_score[NUM_CUSTOM_ELEMENTS];
15395
15396   // runtime values for group element choice position
15397   int choice_pos[NUM_GROUP_ELEMENTS];
15398
15399   // runtime values for belt position animations
15400   int belt_graphic[4][NUM_BELT_PARTS];
15401   int belt_anim_mode[4][NUM_BELT_PARTS];
15402 };
15403
15404 static struct EngineSnapshotInfo engine_snapshot_rnd;
15405 static char *snapshot_level_identifier = NULL;
15406 static int snapshot_level_nr = -1;
15407
15408 static void SaveEngineSnapshotValues_RND(void)
15409 {
15410   static int belt_base_active_element[4] =
15411   {
15412     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15413     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15414     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15415     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15416   };
15417   int i, j;
15418
15419   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15420   {
15421     int element = EL_CUSTOM_START + i;
15422
15423     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15424   }
15425
15426   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15427   {
15428     int element = EL_GROUP_START + i;
15429
15430     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15431   }
15432
15433   for (i = 0; i < 4; i++)
15434   {
15435     for (j = 0; j < NUM_BELT_PARTS; j++)
15436     {
15437       int element = belt_base_active_element[i] + j;
15438       int graphic = el2img(element);
15439       int anim_mode = graphic_info[graphic].anim_mode;
15440
15441       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15442       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15443     }
15444   }
15445 }
15446
15447 static void LoadEngineSnapshotValues_RND(void)
15448 {
15449   unsigned int num_random_calls = game.num_random_calls;
15450   int i, j;
15451
15452   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15453   {
15454     int element = EL_CUSTOM_START + i;
15455
15456     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15457   }
15458
15459   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15460   {
15461     int element = EL_GROUP_START + i;
15462
15463     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15464   }
15465
15466   for (i = 0; i < 4; i++)
15467   {
15468     for (j = 0; j < NUM_BELT_PARTS; j++)
15469     {
15470       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15471       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15472
15473       graphic_info[graphic].anim_mode = anim_mode;
15474     }
15475   }
15476
15477   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15478   {
15479     InitRND(tape.random_seed);
15480     for (i = 0; i < num_random_calls; i++)
15481       RND(1);
15482   }
15483
15484   if (game.num_random_calls != num_random_calls)
15485   {
15486     Error("number of random calls out of sync");
15487     Error("number of random calls should be %d", num_random_calls);
15488     Error("number of random calls is %d", game.num_random_calls);
15489
15490     Fail("this should not happen -- please debug");
15491   }
15492 }
15493
15494 void FreeEngineSnapshotSingle(void)
15495 {
15496   FreeSnapshotSingle();
15497
15498   setString(&snapshot_level_identifier, NULL);
15499   snapshot_level_nr = -1;
15500 }
15501
15502 void FreeEngineSnapshotList(void)
15503 {
15504   FreeSnapshotList();
15505 }
15506
15507 static ListNode *SaveEngineSnapshotBuffers(void)
15508 {
15509   ListNode *buffers = NULL;
15510
15511   // copy some special values to a structure better suited for the snapshot
15512
15513   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15514     SaveEngineSnapshotValues_RND();
15515   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15516     SaveEngineSnapshotValues_EM();
15517   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15518     SaveEngineSnapshotValues_SP(&buffers);
15519   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15520     SaveEngineSnapshotValues_MM(&buffers);
15521
15522   // save values stored in special snapshot structure
15523
15524   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15525     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15526   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15527     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15528   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15529     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15530   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15531     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15532
15533   // save further RND engine values
15534
15535   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15536   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15537   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15538
15539   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15540   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15541   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15542   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15543   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15544
15545   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15546   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15547   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15548
15549   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15550
15551   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15552   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15553
15554   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15555   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15556   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15557   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15558   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15559   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15560   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15561   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15562   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15563   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15564   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15565   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15566   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15567   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15568   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15572
15573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15574   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15575
15576   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15578   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15579
15580   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15582
15583   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15586   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15588
15589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15591
15592 #if 0
15593   ListNode *node = engine_snapshot_list_rnd;
15594   int num_bytes = 0;
15595
15596   while (node != NULL)
15597   {
15598     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15599
15600     node = node->next;
15601   }
15602
15603   Debug("game:playing:SaveEngineSnapshotBuffers",
15604         "size of engine snapshot: %d bytes", num_bytes);
15605 #endif
15606
15607   return buffers;
15608 }
15609
15610 void SaveEngineSnapshotSingle(void)
15611 {
15612   ListNode *buffers = SaveEngineSnapshotBuffers();
15613
15614   // finally save all snapshot buffers to single snapshot
15615   SaveSnapshotSingle(buffers);
15616
15617   // save level identification information
15618   setString(&snapshot_level_identifier, leveldir_current->identifier);
15619   snapshot_level_nr = level_nr;
15620 }
15621
15622 boolean CheckSaveEngineSnapshotToList(void)
15623 {
15624   boolean save_snapshot =
15625     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15626      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15627       game.snapshot.changed_action) ||
15628      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15629       game.snapshot.collected_item));
15630
15631   game.snapshot.changed_action = FALSE;
15632   game.snapshot.collected_item = FALSE;
15633   game.snapshot.save_snapshot = save_snapshot;
15634
15635   return save_snapshot;
15636 }
15637
15638 void SaveEngineSnapshotToList(void)
15639 {
15640   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15641       tape.quick_resume)
15642     return;
15643
15644   ListNode *buffers = SaveEngineSnapshotBuffers();
15645
15646   // finally save all snapshot buffers to snapshot list
15647   SaveSnapshotToList(buffers);
15648 }
15649
15650 void SaveEngineSnapshotToListInitial(void)
15651 {
15652   FreeEngineSnapshotList();
15653
15654   SaveEngineSnapshotToList();
15655 }
15656
15657 static void LoadEngineSnapshotValues(void)
15658 {
15659   // restore special values from snapshot structure
15660
15661   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15662     LoadEngineSnapshotValues_RND();
15663   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15664     LoadEngineSnapshotValues_EM();
15665   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15666     LoadEngineSnapshotValues_SP();
15667   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15668     LoadEngineSnapshotValues_MM();
15669 }
15670
15671 void LoadEngineSnapshotSingle(void)
15672 {
15673   LoadSnapshotSingle();
15674
15675   LoadEngineSnapshotValues();
15676 }
15677
15678 static void LoadEngineSnapshot_Undo(int steps)
15679 {
15680   LoadSnapshotFromList_Older(steps);
15681
15682   LoadEngineSnapshotValues();
15683 }
15684
15685 static void LoadEngineSnapshot_Redo(int steps)
15686 {
15687   LoadSnapshotFromList_Newer(steps);
15688
15689   LoadEngineSnapshotValues();
15690 }
15691
15692 boolean CheckEngineSnapshotSingle(void)
15693 {
15694   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15695           snapshot_level_nr == level_nr);
15696 }
15697
15698 boolean CheckEngineSnapshotList(void)
15699 {
15700   return CheckSnapshotList();
15701 }
15702
15703
15704 // ---------- new game button stuff -------------------------------------------
15705
15706 static struct
15707 {
15708   int graphic;
15709   struct XY *pos;
15710   int gadget_id;
15711   boolean *setup_value;
15712   boolean allowed_on_tape;
15713   boolean is_touch_button;
15714   char *infotext;
15715 } gamebutton_info[NUM_GAME_BUTTONS] =
15716 {
15717   {
15718     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15719     GAME_CTRL_ID_STOP,                          NULL,
15720     TRUE, FALSE,                                "stop game"
15721   },
15722   {
15723     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15724     GAME_CTRL_ID_PAUSE,                         NULL,
15725     TRUE, FALSE,                                "pause game"
15726   },
15727   {
15728     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15729     GAME_CTRL_ID_PLAY,                          NULL,
15730     TRUE, FALSE,                                "play game"
15731   },
15732   {
15733     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15734     GAME_CTRL_ID_UNDO,                          NULL,
15735     TRUE, FALSE,                                "undo step"
15736   },
15737   {
15738     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15739     GAME_CTRL_ID_REDO,                          NULL,
15740     TRUE, FALSE,                                "redo step"
15741   },
15742   {
15743     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15744     GAME_CTRL_ID_SAVE,                          NULL,
15745     TRUE, FALSE,                                "save game"
15746   },
15747   {
15748     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15749     GAME_CTRL_ID_PAUSE2,                        NULL,
15750     TRUE, FALSE,                                "pause game"
15751   },
15752   {
15753     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15754     GAME_CTRL_ID_LOAD,                          NULL,
15755     TRUE, FALSE,                                "load game"
15756   },
15757   {
15758     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15759     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15760     FALSE, FALSE,                               "stop game"
15761   },
15762   {
15763     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15764     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15765     FALSE, FALSE,                               "pause game"
15766   },
15767   {
15768     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15769     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15770     FALSE, FALSE,                               "play game"
15771   },
15772   {
15773     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15774     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15775     FALSE, TRUE,                                "stop game"
15776   },
15777   {
15778     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15779     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15780     FALSE, TRUE,                                "pause game"
15781   },
15782   {
15783     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15784     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15785     TRUE, FALSE,                                "background music on/off"
15786   },
15787   {
15788     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15789     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15790     TRUE, FALSE,                                "sound loops on/off"
15791   },
15792   {
15793     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15794     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15795     TRUE, FALSE,                                "normal sounds on/off"
15796   },
15797   {
15798     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15799     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15800     FALSE, FALSE,                               "background music on/off"
15801   },
15802   {
15803     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15804     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15805     FALSE, FALSE,                               "sound loops on/off"
15806   },
15807   {
15808     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15809     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15810     FALSE, FALSE,                               "normal sounds on/off"
15811   }
15812 };
15813
15814 void CreateGameButtons(void)
15815 {
15816   int i;
15817
15818   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15819   {
15820     int graphic = gamebutton_info[i].graphic;
15821     struct GraphicInfo *gfx = &graphic_info[graphic];
15822     struct XY *pos = gamebutton_info[i].pos;
15823     struct GadgetInfo *gi;
15824     int button_type;
15825     boolean checked;
15826     unsigned int event_mask;
15827     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15828     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15829     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15830     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15831     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15832     int gd_x   = gfx->src_x;
15833     int gd_y   = gfx->src_y;
15834     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15835     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15836     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15837     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15838     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15839     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15840     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15841     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15842     int id = i;
15843
15844     if (gfx->bitmap == NULL)
15845     {
15846       game_gadget[id] = NULL;
15847
15848       continue;
15849     }
15850
15851     if (id == GAME_CTRL_ID_STOP ||
15852         id == GAME_CTRL_ID_PANEL_STOP ||
15853         id == GAME_CTRL_ID_TOUCH_STOP ||
15854         id == GAME_CTRL_ID_PLAY ||
15855         id == GAME_CTRL_ID_PANEL_PLAY ||
15856         id == GAME_CTRL_ID_SAVE ||
15857         id == GAME_CTRL_ID_LOAD)
15858     {
15859       button_type = GD_TYPE_NORMAL_BUTTON;
15860       checked = FALSE;
15861       event_mask = GD_EVENT_RELEASED;
15862     }
15863     else if (id == GAME_CTRL_ID_UNDO ||
15864              id == GAME_CTRL_ID_REDO)
15865     {
15866       button_type = GD_TYPE_NORMAL_BUTTON;
15867       checked = FALSE;
15868       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15869     }
15870     else
15871     {
15872       button_type = GD_TYPE_CHECK_BUTTON;
15873       checked = (gamebutton_info[i].setup_value != NULL ?
15874                  *gamebutton_info[i].setup_value : FALSE);
15875       event_mask = GD_EVENT_PRESSED;
15876     }
15877
15878     gi = CreateGadget(GDI_CUSTOM_ID, id,
15879                       GDI_IMAGE_ID, graphic,
15880                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15881                       GDI_X, base_x + x,
15882                       GDI_Y, base_y + y,
15883                       GDI_WIDTH, gfx->width,
15884                       GDI_HEIGHT, gfx->height,
15885                       GDI_TYPE, button_type,
15886                       GDI_STATE, GD_BUTTON_UNPRESSED,
15887                       GDI_CHECKED, checked,
15888                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15889                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15890                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15891                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15892                       GDI_DIRECT_DRAW, FALSE,
15893                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15894                       GDI_EVENT_MASK, event_mask,
15895                       GDI_CALLBACK_ACTION, HandleGameButtons,
15896                       GDI_END);
15897
15898     if (gi == NULL)
15899       Fail("cannot create gadget");
15900
15901     game_gadget[id] = gi;
15902   }
15903 }
15904
15905 void FreeGameButtons(void)
15906 {
15907   int i;
15908
15909   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15910     FreeGadget(game_gadget[i]);
15911 }
15912
15913 static void UnmapGameButtonsAtSamePosition(int id)
15914 {
15915   int i;
15916
15917   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15918     if (i != id &&
15919         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15920         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15921       UnmapGadget(game_gadget[i]);
15922 }
15923
15924 static void UnmapGameButtonsAtSamePosition_All(void)
15925 {
15926   if (setup.show_snapshot_buttons)
15927   {
15928     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15929     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15930     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15931   }
15932   else
15933   {
15934     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15935     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15936     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15937
15938     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15939     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15940     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15941   }
15942 }
15943
15944 static void MapGameButtonsAtSamePosition(int id)
15945 {
15946   int i;
15947
15948   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15949     if (i != id &&
15950         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15951         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15952       MapGadget(game_gadget[i]);
15953
15954   UnmapGameButtonsAtSamePosition_All();
15955 }
15956
15957 void MapUndoRedoButtons(void)
15958 {
15959   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15960   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15961
15962   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15963   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15964 }
15965
15966 void UnmapUndoRedoButtons(void)
15967 {
15968   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15969   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15970
15971   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15972   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15973 }
15974
15975 void ModifyPauseButtons(void)
15976 {
15977   static int ids[] =
15978   {
15979     GAME_CTRL_ID_PAUSE,
15980     GAME_CTRL_ID_PAUSE2,
15981     GAME_CTRL_ID_PANEL_PAUSE,
15982     GAME_CTRL_ID_TOUCH_PAUSE,
15983     -1
15984   };
15985   int i;
15986
15987   for (i = 0; ids[i] > -1; i++)
15988     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15989 }
15990
15991 static void MapGameButtonsExt(boolean on_tape)
15992 {
15993   int i;
15994
15995   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15996     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15997         i != GAME_CTRL_ID_UNDO &&
15998         i != GAME_CTRL_ID_REDO)
15999       MapGadget(game_gadget[i]);
16000
16001   UnmapGameButtonsAtSamePosition_All();
16002
16003   RedrawGameButtons();
16004 }
16005
16006 static void UnmapGameButtonsExt(boolean on_tape)
16007 {
16008   int i;
16009
16010   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16011     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16012       UnmapGadget(game_gadget[i]);
16013 }
16014
16015 static void RedrawGameButtonsExt(boolean on_tape)
16016 {
16017   int i;
16018
16019   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16020     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16021       RedrawGadget(game_gadget[i]);
16022 }
16023
16024 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16025 {
16026   if (gi == NULL)
16027     return;
16028
16029   gi->checked = state;
16030 }
16031
16032 static void RedrawSoundButtonGadget(int id)
16033 {
16034   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16035              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16036              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16037              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16038              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16039              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16040              id);
16041
16042   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16043   RedrawGadget(game_gadget[id2]);
16044 }
16045
16046 void MapGameButtons(void)
16047 {
16048   MapGameButtonsExt(FALSE);
16049 }
16050
16051 void UnmapGameButtons(void)
16052 {
16053   UnmapGameButtonsExt(FALSE);
16054 }
16055
16056 void RedrawGameButtons(void)
16057 {
16058   RedrawGameButtonsExt(FALSE);
16059 }
16060
16061 void MapGameButtonsOnTape(void)
16062 {
16063   MapGameButtonsExt(TRUE);
16064 }
16065
16066 void UnmapGameButtonsOnTape(void)
16067 {
16068   UnmapGameButtonsExt(TRUE);
16069 }
16070
16071 void RedrawGameButtonsOnTape(void)
16072 {
16073   RedrawGameButtonsExt(TRUE);
16074 }
16075
16076 static void GameUndoRedoExt(void)
16077 {
16078   ClearPlayerAction();
16079
16080   tape.pausing = TRUE;
16081
16082   RedrawPlayfield();
16083   UpdateAndDisplayGameControlValues();
16084
16085   DrawCompleteVideoDisplay();
16086   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16087   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16088   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16089
16090   BackToFront();
16091 }
16092
16093 static void GameUndo(int steps)
16094 {
16095   if (!CheckEngineSnapshotList())
16096     return;
16097
16098   LoadEngineSnapshot_Undo(steps);
16099
16100   GameUndoRedoExt();
16101 }
16102
16103 static void GameRedo(int steps)
16104 {
16105   if (!CheckEngineSnapshotList())
16106     return;
16107
16108   LoadEngineSnapshot_Redo(steps);
16109
16110   GameUndoRedoExt();
16111 }
16112
16113 static void HandleGameButtonsExt(int id, int button)
16114 {
16115   static boolean game_undo_executed = FALSE;
16116   int steps = BUTTON_STEPSIZE(button);
16117   boolean handle_game_buttons =
16118     (game_status == GAME_MODE_PLAYING ||
16119      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16120
16121   if (!handle_game_buttons)
16122     return;
16123
16124   switch (id)
16125   {
16126     case GAME_CTRL_ID_STOP:
16127     case GAME_CTRL_ID_PANEL_STOP:
16128     case GAME_CTRL_ID_TOUCH_STOP:
16129       if (game_status == GAME_MODE_MAIN)
16130         break;
16131
16132       if (tape.playing)
16133         TapeStop();
16134       else
16135         RequestQuitGame(TRUE);
16136
16137       break;
16138
16139     case GAME_CTRL_ID_PAUSE:
16140     case GAME_CTRL_ID_PAUSE2:
16141     case GAME_CTRL_ID_PANEL_PAUSE:
16142     case GAME_CTRL_ID_TOUCH_PAUSE:
16143       if (network.enabled && game_status == GAME_MODE_PLAYING)
16144       {
16145         if (tape.pausing)
16146           SendToServer_ContinuePlaying();
16147         else
16148           SendToServer_PausePlaying();
16149       }
16150       else
16151         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16152
16153       game_undo_executed = FALSE;
16154
16155       break;
16156
16157     case GAME_CTRL_ID_PLAY:
16158     case GAME_CTRL_ID_PANEL_PLAY:
16159       if (game_status == GAME_MODE_MAIN)
16160       {
16161         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16162       }
16163       else if (tape.pausing)
16164       {
16165         if (network.enabled)
16166           SendToServer_ContinuePlaying();
16167         else
16168           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16169       }
16170       break;
16171
16172     case GAME_CTRL_ID_UNDO:
16173       // Important: When using "save snapshot when collecting an item" mode,
16174       // load last (current) snapshot for first "undo" after pressing "pause"
16175       // (else the last-but-one snapshot would be loaded, because the snapshot
16176       // pointer already points to the last snapshot when pressing "pause",
16177       // which is fine for "every step/move" mode, but not for "every collect")
16178       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16179           !game_undo_executed)
16180         steps--;
16181
16182       game_undo_executed = TRUE;
16183
16184       GameUndo(steps);
16185       break;
16186
16187     case GAME_CTRL_ID_REDO:
16188       GameRedo(steps);
16189       break;
16190
16191     case GAME_CTRL_ID_SAVE:
16192       TapeQuickSave();
16193       break;
16194
16195     case GAME_CTRL_ID_LOAD:
16196       TapeQuickLoad();
16197       break;
16198
16199     case SOUND_CTRL_ID_MUSIC:
16200     case SOUND_CTRL_ID_PANEL_MUSIC:
16201       if (setup.sound_music)
16202       { 
16203         setup.sound_music = FALSE;
16204
16205         FadeMusic();
16206       }
16207       else if (audio.music_available)
16208       { 
16209         setup.sound = setup.sound_music = TRUE;
16210
16211         SetAudioMode(setup.sound);
16212
16213         if (game_status == GAME_MODE_PLAYING)
16214           PlayLevelMusic();
16215       }
16216
16217       RedrawSoundButtonGadget(id);
16218
16219       break;
16220
16221     case SOUND_CTRL_ID_LOOPS:
16222     case SOUND_CTRL_ID_PANEL_LOOPS:
16223       if (setup.sound_loops)
16224         setup.sound_loops = FALSE;
16225       else if (audio.loops_available)
16226       {
16227         setup.sound = setup.sound_loops = TRUE;
16228
16229         SetAudioMode(setup.sound);
16230       }
16231
16232       RedrawSoundButtonGadget(id);
16233
16234       break;
16235
16236     case SOUND_CTRL_ID_SIMPLE:
16237     case SOUND_CTRL_ID_PANEL_SIMPLE:
16238       if (setup.sound_simple)
16239         setup.sound_simple = FALSE;
16240       else if (audio.sound_available)
16241       {
16242         setup.sound = setup.sound_simple = TRUE;
16243
16244         SetAudioMode(setup.sound);
16245       }
16246
16247       RedrawSoundButtonGadget(id);
16248
16249       break;
16250
16251     default:
16252       break;
16253   }
16254 }
16255
16256 static void HandleGameButtons(struct GadgetInfo *gi)
16257 {
16258   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16259 }
16260
16261 void HandleSoundButtonKeys(Key key)
16262 {
16263   if (key == setup.shortcut.sound_simple)
16264     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16265   else if (key == setup.shortcut.sound_loops)
16266     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16267   else if (key == setup.shortcut.sound_music)
16268     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16269 }