minor code improvement
[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 void TestFieldAfterSnapping(int, int, int, int, int);
1123
1124 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1125
1126 // for detection of endless loops, caused by custom element programming
1127 // (using maximal playfield width x 10 is just a rough approximation)
1128 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1129
1130 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1131 {                                                                       \
1132   if (recursion_loop_detected)                                          \
1133     return (rc);                                                        \
1134                                                                         \
1135   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1136   {                                                                     \
1137     recursion_loop_detected = TRUE;                                     \
1138     recursion_loop_element = (e);                                       \
1139   }                                                                     \
1140                                                                         \
1141   recursion_loop_depth++;                                               \
1142 }
1143
1144 #define RECURSION_LOOP_DETECTION_END()                                  \
1145 {                                                                       \
1146   recursion_loop_depth--;                                               \
1147 }
1148
1149 static int recursion_loop_depth;
1150 static boolean recursion_loop_detected;
1151 static boolean recursion_loop_element;
1152
1153 static int map_player_action[MAX_PLAYERS];
1154
1155
1156 // ----------------------------------------------------------------------------
1157 // definition of elements that automatically change to other elements after
1158 // a specified time, eventually calling a function when changing
1159 // ----------------------------------------------------------------------------
1160
1161 // forward declaration for changer functions
1162 static void InitBuggyBase(int, int);
1163 static void WarnBuggyBase(int, int);
1164
1165 static void InitTrap(int, int);
1166 static void ActivateTrap(int, int);
1167 static void ChangeActiveTrap(int, int);
1168
1169 static void InitRobotWheel(int, int);
1170 static void RunRobotWheel(int, int);
1171 static void StopRobotWheel(int, int);
1172
1173 static void InitTimegateWheel(int, int);
1174 static void RunTimegateWheel(int, int);
1175
1176 static void InitMagicBallDelay(int, int);
1177 static void ActivateMagicBall(int, int);
1178
1179 struct ChangingElementInfo
1180 {
1181   int element;
1182   int target_element;
1183   int change_delay;
1184   void (*pre_change_function)(int x, int y);
1185   void (*change_function)(int x, int y);
1186   void (*post_change_function)(int x, int y);
1187 };
1188
1189 static struct ChangingElementInfo change_delay_list[] =
1190 {
1191   {
1192     EL_NUT_BREAKING,
1193     EL_EMERALD,
1194     6,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_PEARL_BREAKING,
1201     EL_EMPTY,
1202     8,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EXIT_OPENING,
1209     EL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_CLOSING,
1217     EL_EXIT_CLOSED,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_STEEL_EXIT_OPENING,
1225     EL_STEEL_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_CLOSING,
1233     EL_STEEL_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EM_EXIT_OPENING,
1241     EL_EM_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_CLOSING,
1249     EL_EMPTY,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_OPENING,
1257     EL_EM_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_CLOSING,
1265     EL_STEELWALL,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_SP_EXIT_OPENING,
1273     EL_SP_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_CLOSING,
1281     EL_SP_EXIT_CLOSED,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SWITCHGATE_OPENING,
1289     EL_SWITCHGATE_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_CLOSING,
1297     EL_SWITCHGATE_CLOSED,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_TIMEGATE_OPENING,
1305     EL_TIMEGATE_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_CLOSING,
1313     EL_TIMEGATE_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319
1320   {
1321     EL_ACID_SPLASH_LEFT,
1322     EL_EMPTY,
1323     8,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_ACID_SPLASH_RIGHT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_SP_BUGGY_BASE,
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     0,
1340     InitBuggyBase,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE_ACTIVATING,
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVE,
1354     EL_SP_BUGGY_BASE,
1355     0,
1356     InitBuggyBase,
1357     WarnBuggyBase,
1358     NULL
1359   },
1360   {
1361     EL_TRAP,
1362     EL_TRAP_ACTIVE,
1363     0,
1364     InitTrap,
1365     NULL,
1366     ActivateTrap
1367   },
1368   {
1369     EL_TRAP_ACTIVE,
1370     EL_TRAP,
1371     31,
1372     NULL,
1373     ChangeActiveTrap,
1374     NULL
1375   },
1376   {
1377     EL_ROBOT_WHEEL_ACTIVE,
1378     EL_ROBOT_WHEEL,
1379     0,
1380     InitRobotWheel,
1381     RunRobotWheel,
1382     StopRobotWheel
1383   },
1384   {
1385     EL_TIMEGATE_SWITCH_ACTIVE,
1386     EL_TIMEGATE_SWITCH,
1387     0,
1388     InitTimegateWheel,
1389     RunTimegateWheel,
1390     NULL
1391   },
1392   {
1393     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394     EL_DC_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     EL_EMC_MAGIC_BALL_ACTIVE,
1403     0,
1404     InitMagicBallDelay,
1405     NULL,
1406     ActivateMagicBall
1407   },
1408   {
1409     EL_EMC_SPRING_BUMPER_ACTIVE,
1410     EL_EMC_SPRING_BUMPER,
1411     8,
1412     NULL,
1413     NULL,
1414     NULL
1415   },
1416   {
1417     EL_DIAGONAL_SHRINKING,
1418     EL_UNDEFINED,
1419     0,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_GROWING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL,
1431   },
1432
1433   {
1434     EL_UNDEFINED,
1435     EL_UNDEFINED,
1436     -1,
1437     NULL,
1438     NULL,
1439     NULL
1440   }
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int push_delay_fixed, push_delay_random;
1447 }
1448 push_delay_list[] =
1449 {
1450   { EL_SPRING,                  0, 0 },
1451   { EL_BALLOON,                 0, 0 },
1452
1453   { EL_SOKOBAN_OBJECT,          2, 0 },
1454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1455   { EL_SATELLITE,               2, 0 },
1456   { EL_SP_DISK_YELLOW,          2, 0 },
1457
1458   { EL_UNDEFINED,               0, 0 },
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int move_stepsize;
1465 }
1466 move_stepsize_list[] =
1467 {
1468   { EL_AMOEBA_DROP,             2 },
1469   { EL_AMOEBA_DROPPING,         2 },
1470   { EL_QUICKSAND_FILLING,       1 },
1471   { EL_QUICKSAND_EMPTYING,      1 },
1472   { EL_QUICKSAND_FAST_FILLING,  2 },
1473   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474   { EL_MAGIC_WALL_FILLING,      2 },
1475   { EL_MAGIC_WALL_EMPTYING,     2 },
1476   { EL_BD_MAGIC_WALL_FILLING,   2 },
1477   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1478   { EL_DC_MAGIC_WALL_FILLING,   2 },
1479   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1480
1481   { EL_UNDEFINED,               0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int count;
1488 }
1489 collect_count_list[] =
1490 {
1491   { EL_EMERALD,                 1 },
1492   { EL_BD_DIAMOND,              1 },
1493   { EL_EMERALD_YELLOW,          1 },
1494   { EL_EMERALD_RED,             1 },
1495   { EL_EMERALD_PURPLE,          1 },
1496   { EL_DIAMOND,                 3 },
1497   { EL_SP_INFOTRON,             1 },
1498   { EL_PEARL,                   5 },
1499   { EL_CRYSTAL,                 8 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int direction;
1508 }
1509 access_direction_list[] =
1510 {
1511   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1514   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1515   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1517   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1518   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1519   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1520   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1521   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1522
1523   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1524   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1525   { EL_SP_PORT_UP,                                                   MV_DOWN },
1526   { EL_SP_PORT_DOWN,                                         MV_UP           },
1527   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1528   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1529   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1531   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1532   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1533   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1534   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1535   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1536   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1537   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1538   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1539   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1540   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1541   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1542
1543   { EL_UNDEFINED,                       MV_NONE                              }
1544 };
1545
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547
1548 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1551                                  IS_JUST_CHANGING(x, y))
1552
1553 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1554
1555 // static variables for playfield scan mode (scanning forward or backward)
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1560
1561 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1562                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1563                                      (y) += playfield_scan_delta_y)     \
1564                                 for ((x) = playfield_scan_start_x;      \
1565                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1566                                      (x) += playfield_scan_delta_x)
1567
1568 #ifdef DEBUG
1569 void DEBUG_SetMaximumDynamite(void)
1570 {
1571   int i;
1572
1573   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575       local_player->inventory_element[local_player->inventory_size++] =
1576         EL_DYNAMITE;
1577 }
1578 #endif
1579
1580 static void InitPlayfieldScanModeVars(void)
1581 {
1582   if (game.use_reverse_scan_direction)
1583   {
1584     playfield_scan_start_x = lev_fieldx - 1;
1585     playfield_scan_start_y = lev_fieldy - 1;
1586
1587     playfield_scan_delta_x = -1;
1588     playfield_scan_delta_y = -1;
1589   }
1590   else
1591   {
1592     playfield_scan_start_x = 0;
1593     playfield_scan_start_y = 0;
1594
1595     playfield_scan_delta_x = 1;
1596     playfield_scan_delta_y = 1;
1597   }
1598 }
1599
1600 static void InitPlayfieldScanMode(int mode)
1601 {
1602   game.use_reverse_scan_direction =
1603     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604
1605   InitPlayfieldScanModeVars();
1606 }
1607
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1609 {
1610   move_stepsize =
1611     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612
1613   // make sure that stepsize value is always a power of 2
1614   move_stepsize = (1 << log_2(move_stepsize));
1615
1616   return TILEX / move_stepsize;
1617 }
1618
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620                                boolean init_game)
1621 {
1622   int player_nr = player->index_nr;
1623   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625
1626   // do no immediately change move delay -- the player might just be moving
1627   player->move_delay_value_next = move_delay;
1628
1629   // information if player can move must be set separately
1630   player->cannot_move = cannot_move;
1631
1632   if (init_game)
1633   {
1634     player->move_delay       = game.initial_move_delay[player_nr];
1635     player->move_delay_value = game.initial_move_delay_value[player_nr];
1636
1637     player->move_delay_value_next = -1;
1638
1639     player->move_delay_reset_counter = 0;
1640   }
1641 }
1642
1643 void GetPlayerConfig(void)
1644 {
1645   GameFrameDelay = setup.game_frame_delay;
1646
1647   if (!audio.sound_available)
1648     setup.sound_simple = FALSE;
1649
1650   if (!audio.loops_available)
1651     setup.sound_loops = FALSE;
1652
1653   if (!audio.music_available)
1654     setup.sound_music = FALSE;
1655
1656   if (!video.fullscreen_available)
1657     setup.fullscreen = FALSE;
1658
1659   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660
1661   SetAudioMode(setup.sound);
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void IncrementSokobanFieldsNeeded(void)
1691 {
1692   if (level.sb_fields_needed)
1693     game.sokoban_fields_still_needed++;
1694 }
1695
1696 static void IncrementSokobanObjectsNeeded(void)
1697 {
1698   if (level.sb_objects_needed)
1699     game.sokoban_objects_still_needed++;
1700 }
1701
1702 static void DecrementSokobanFieldsNeeded(void)
1703 {
1704   if (game.sokoban_fields_still_needed > 0)
1705     game.sokoban_fields_still_needed--;
1706 }
1707
1708 static void DecrementSokobanObjectsNeeded(void)
1709 {
1710   if (game.sokoban_objects_still_needed > 0)
1711     game.sokoban_objects_still_needed--;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Tile[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Tile[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     // ---------- initialize player's last field block delay ------------------
1751
1752     // always start with reliable default value (no adjustment needed)
1753     player->block_delay_adjustment = 0;
1754
1755     // special case 1: in Supaplex, Murphy blocks last field one more frame
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     // special case 2: in game engines before 3.1.1, blocking was different
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!network.enabled || player->connected_network)
1764     {
1765       player->active = TRUE;
1766
1767       // remove potentially duplicate players
1768       if (StorePlayer[jx][jy] == Tile[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Tile[x][y];
1772
1773 #if DEBUG_INIT_PLAYER
1774       Debug("game:init:player", "- player element %d activated",
1775             player->element_nr);
1776       Debug("game:init:player", "  (local player is %d and currently %s)",
1777             local_player->element_nr,
1778             local_player->active ? "active" : "not active");
1779     }
1780 #endif
1781
1782     Tile[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   // always check if player was just killed and should be reanimated
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Tile[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Tile[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1832         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Tile[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Tile[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Tile[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Tile[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Tile[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error("'game_panel_controls' structure corrupted at %d", i);
2153
2154       Fail("this should not happen -- please debug");
2155     }
2156
2157     // force update of game controls after initialization
2158     gpc->value = gpc->last_value = -1;
2159     gpc->frame = gpc->last_frame = -1;
2160     gpc->gfx_frame = -1;
2161
2162     // determine panel value width for later calculation of alignment
2163     if (type == TYPE_INTEGER || type == TYPE_STRING)
2164     {
2165       pos->width = pos->size * getFontWidth(pos->font);
2166       pos->height = getFontHeight(pos->font);
2167     }
2168     else if (type == TYPE_ELEMENT)
2169     {
2170       pos->width = pos->size;
2171       pos->height = pos->size;
2172     }
2173
2174     // fill structure for game panel draw order
2175     gpo->nr = gpc->nr;
2176     gpo->sort_priority = pos->sort_priority;
2177   }
2178
2179   // sort game panel controls according to sort_priority and control number
2180   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2182 }
2183
2184 static void UpdatePlayfieldElementCount(void)
2185 {
2186   boolean use_element_count = FALSE;
2187   int i, j, x, y;
2188
2189   // first check if it is needed at all to calculate playfield element count
2190   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192       use_element_count = TRUE;
2193
2194   if (!use_element_count)
2195     return;
2196
2197   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198     element_info[i].element_count = 0;
2199
2200   SCAN_PLAYFIELD(x, y)
2201   {
2202     element_info[Tile[x][y]].element_count++;
2203   }
2204
2205   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207       if (IS_IN_GROUP(j, i))
2208         element_info[EL_GROUP_START + i].element_count +=
2209           element_info[j].element_count;
2210 }
2211
2212 static void UpdateGameControlValues(void)
2213 {
2214   int i, k;
2215   int time = (game.LevelSolved ?
2216               game.LevelSolved_CountingTime :
2217               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218               game_em.lev->time :
2219               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2220               game_sp.time_played :
2221               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2222               game_mm.energy_left :
2223               game.no_time_limit ? TimePlayed : TimeLeft);
2224   int score = (game.LevelSolved ?
2225                game.LevelSolved_CountingScore :
2226                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227                game_em.lev->score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2229                game_sp.score :
2230                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2231                game_mm.score :
2232                game.score);
2233   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234               game_em.lev->gems_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2236               game_sp.infotrons_still_needed :
2237               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2238               game_mm.kettles_still_needed :
2239               game.gems_still_needed);
2240   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241                      game_em.lev->gems_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243                      game_sp.infotrons_still_needed > 0 :
2244                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2245                      game_mm.kettles_still_needed > 0 ||
2246                      game_mm.lights_still_needed > 0 :
2247                      game.gems_still_needed > 0 ||
2248                      game.sokoban_fields_still_needed > 0 ||
2249                      game.sokoban_objects_still_needed > 0 ||
2250                      game.lights_still_needed > 0);
2251   int health = (game.LevelSolved ?
2252                 game.LevelSolved_CountingHealth :
2253                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2254                 MM_HEALTH(game_mm.laser_overload_value) :
2255                 game.health);
2256   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2257
2258   UpdatePlayfieldElementCount();
2259
2260   // update game panel control values
2261
2262   // used instead of "level_nr" (for network games)
2263   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2264   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2265
2266   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2267   for (i = 0; i < MAX_NUM_KEYS; i++)
2268     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2269   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2270   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2271
2272   if (game.centered_player_nr == -1)
2273   {
2274     for (i = 0; i < MAX_PLAYERS; i++)
2275     {
2276       // only one player in Supaplex game engine
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2278         break;
2279
2280       for (k = 0; k < MAX_NUM_KEYS; k++)
2281       {
2282         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283         {
2284           if (game_em.ply[i]->keys & (1 << k))
2285             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286               get_key_element_from_nr(k);
2287         }
2288         else if (stored_player[i].key[k])
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         getPlayerInventorySize(i);
2295
2296       if (stored_player[i].num_white_keys > 0)
2297         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2298           EL_DC_KEY_WHITE;
2299
2300       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301         stored_player[i].num_white_keys;
2302     }
2303   }
2304   else
2305   {
2306     int player_nr = game.centered_player_nr;
2307
2308     for (k = 0; k < MAX_NUM_KEYS; k++)
2309     {
2310       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2311       {
2312         if (game_em.ply[player_nr]->keys & (1 << k))
2313           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314             get_key_element_from_nr(k);
2315       }
2316       else if (stored_player[player_nr].key[k])
2317         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2318           get_key_element_from_nr(k);
2319     }
2320
2321     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2322       getPlayerInventorySize(player_nr);
2323
2324     if (stored_player[player_nr].num_white_keys > 0)
2325       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2326
2327     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328       stored_player[player_nr].num_white_keys;
2329   }
2330
2331   // re-arrange keys on game panel, if needed or if defined by style settings
2332   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2333   {
2334     int nr = GAME_PANEL_KEY_1 + i;
2335     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2336     struct TextPosInfo *pos = gpc->pos;
2337
2338     // skip check if key is not in the player's inventory
2339     if (gpc->value == EL_EMPTY)
2340       continue;
2341
2342     // check if keys should be arranged on panel from left to right
2343     if (pos->style == STYLE_LEFTMOST_POSITION)
2344     {
2345       // check previous key positions (left from current key)
2346       for (k = 0; k < i; k++)
2347       {
2348         int nr_new = GAME_PANEL_KEY_1 + k;
2349
2350         if (game_panel_controls[nr_new].value == EL_EMPTY)
2351         {
2352           game_panel_controls[nr_new].value = gpc->value;
2353           gpc->value = EL_EMPTY;
2354
2355           break;
2356         }
2357       }
2358     }
2359
2360     // check if "undefined" keys can be placed at some other position
2361     if (pos->x == -1 && pos->y == -1)
2362     {
2363       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2364
2365       // 1st try: display key at the same position as normal or EM keys
2366       if (game_panel_controls[nr_new].value == EL_EMPTY)
2367       {
2368         game_panel_controls[nr_new].value = gpc->value;
2369       }
2370       else
2371       {
2372         // 2nd try: display key at the next free position in the key panel
2373         for (k = 0; k < STD_NUM_KEYS; k++)
2374         {
2375           nr_new = GAME_PANEL_KEY_1 + k;
2376
2377           if (game_panel_controls[nr_new].value == EL_EMPTY)
2378           {
2379             game_panel_controls[nr_new].value = gpc->value;
2380
2381             break;
2382           }
2383         }
2384       }
2385     }
2386   }
2387
2388   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2389   {
2390     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, i);
2392     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2393       get_inventory_element_from_pos(local_player, -i - 1);
2394   }
2395
2396   game_panel_controls[GAME_PANEL_SCORE].value = score;
2397   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2398
2399   game_panel_controls[GAME_PANEL_TIME].value = time;
2400
2401   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2402   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2403   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2404
2405   if (level.time == 0)
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2407   else
2408     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2409
2410   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2411   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2412
2413   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2414
2415   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2416     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2417      EL_EMPTY);
2418   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2419     local_player->shield_normal_time_left;
2420   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2421     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2422      EL_EMPTY);
2423   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2424     local_player->shield_deadly_time_left;
2425
2426   game_panel_controls[GAME_PANEL_EXIT].value =
2427     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2428
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2431   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2432     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2433      EL_EMC_MAGIC_BALL_SWITCH);
2434
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2436     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2437   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2438     game.light_time_left;
2439
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2441     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2442   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2443     game.timegate_time_left;
2444
2445   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2446     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2447
2448   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2449     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2451     game.lenses_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2454     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2455   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2456     game.magnify_time_left;
2457
2458   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2459     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2460      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2461      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2462      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2463      EL_BALLOON_SWITCH_NONE);
2464
2465   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2466     local_player->dynabomb_count;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2468     local_player->dynabomb_size;
2469   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2470     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2471
2472   game_panel_controls[GAME_PANEL_PENGUINS].value =
2473     game.friends_still_needed;
2474
2475   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2476     game.sokoban_objects_still_needed;
2477   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2478     game.sokoban_fields_still_needed;
2479
2480   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2481     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2482
2483   for (i = 0; i < NUM_BELTS; i++)
2484   {
2485     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2486       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2487        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2488     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2489       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2490   }
2491
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2493     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2494   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2495     game.magic_wall_time_left;
2496
2497   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2498     local_player->gravity;
2499
2500   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2501     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2502
2503   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2504     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2505       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2506        game.panel.element[i].id : EL_UNDEFINED);
2507
2508   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2509     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2510       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2511        element_info[game.panel.element_count[i].id].element_count : 0);
2512
2513   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2514     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2515       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2516        element_info[game.panel.ce_score[i].id].collect_score : 0);
2517
2518   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2519     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2520       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2521        element_info[game.panel.ce_score_element[i].id].collect_score :
2522        EL_UNDEFINED);
2523
2524   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2525   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2526   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2527
2528   // update game panel control frames
2529
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531   {
2532     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533
2534     if (gpc->type == TYPE_ELEMENT)
2535     {
2536       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2537       {
2538         int last_anim_random_frame = gfx.anim_random_frame;
2539         int element = gpc->value;
2540         int graphic = el2panelimg(element);
2541         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2542                                sync_random_frame : INIT_GFX_RANDOM());
2543
2544         if (gpc->value != gpc->last_value)
2545         {
2546           gpc->gfx_frame = 0;
2547           gpc->gfx_random = init_gfx_random;
2548         }
2549         else
2550         {
2551           gpc->gfx_frame++;
2552
2553           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2554               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2555             gpc->gfx_random = init_gfx_random;
2556         }
2557
2558         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2559           gfx.anim_random_frame = gpc->gfx_random;
2560
2561         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2562           gpc->gfx_frame = element_info[element].collect_score;
2563
2564         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = last_anim_random_frame;
2568       }
2569     }
2570     else if (gpc->type == TYPE_GRAPHIC)
2571     {
2572       if (gpc->graphic != IMG_UNDEFINED)
2573       {
2574         int last_anim_random_frame = gfx.anim_random_frame;
2575         int graphic = gpc->graphic;
2576         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2577                                sync_random_frame : INIT_GFX_RANDOM());
2578
2579         if (gpc->value != gpc->last_value)
2580         {
2581           gpc->gfx_frame = 0;
2582           gpc->gfx_random = init_gfx_random;
2583         }
2584         else
2585         {
2586           gpc->gfx_frame++;
2587
2588           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2589               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2590             gpc->gfx_random = init_gfx_random;
2591         }
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = gpc->gfx_random;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602   }
2603 }
2604
2605 static void DisplayGameControlValues(void)
2606 {
2607   boolean redraw_panel = FALSE;
2608   int i;
2609
2610   for (i = 0; game_panel_controls[i].nr != -1; i++)
2611   {
2612     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2613
2614     if (PANEL_DEACTIVATED(gpc->pos))
2615       continue;
2616
2617     if (gpc->value == gpc->last_value &&
2618         gpc->frame == gpc->last_frame)
2619       continue;
2620
2621     redraw_panel = TRUE;
2622   }
2623
2624   if (!redraw_panel)
2625     return;
2626
2627   // copy default game door content to main double buffer
2628
2629   // !!! CHECK AGAIN !!!
2630   SetPanelBackground();
2631   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2632   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2633
2634   // redraw game control buttons
2635   RedrawGameButtons();
2636
2637   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2638
2639   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2640   {
2641     int nr = game_panel_order[i].nr;
2642     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2643     struct TextPosInfo *pos = gpc->pos;
2644     int type = gpc->type;
2645     int value = gpc->value;
2646     int frame = gpc->frame;
2647     int size = pos->size;
2648     int font = pos->font;
2649     boolean draw_masked = pos->draw_masked;
2650     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2651
2652     if (PANEL_DEACTIVATED(pos))
2653       continue;
2654
2655     if (pos->class == get_hash_from_key("extra_panel_items") &&
2656         !setup.prefer_extra_panel_items)
2657       continue;
2658
2659     gpc->last_value = value;
2660     gpc->last_frame = frame;
2661
2662     if (type == TYPE_INTEGER)
2663     {
2664       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2665           nr == GAME_PANEL_TIME)
2666       {
2667         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2668
2669         if (use_dynamic_size)           // use dynamic number of digits
2670         {
2671           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2672           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2673           int size2 = size1 + 1;
2674           int font1 = pos->font;
2675           int font2 = pos->font_alt;
2676
2677           size = (value < value_change ? size1 : size2);
2678           font = (value < value_change ? font1 : font2);
2679         }
2680       }
2681
2682       // correct text size if "digits" is zero or less
2683       if (size <= 0)
2684         size = strlen(int2str(value, size));
2685
2686       // dynamically correct text alignment
2687       pos->width = size * getFontWidth(font);
2688
2689       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2690                   int2str(value, size), font, mask_mode);
2691     }
2692     else if (type == TYPE_ELEMENT)
2693     {
2694       int element, graphic;
2695       Bitmap *src_bitmap;
2696       int src_x, src_y;
2697       int width, height;
2698       int dst_x = PANEL_XPOS(pos);
2699       int dst_y = PANEL_YPOS(pos);
2700
2701       if (value != EL_UNDEFINED && value != EL_EMPTY)
2702       {
2703         element = value;
2704         graphic = el2panelimg(value);
2705
2706 #if 0
2707         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2708               element, EL_NAME(element), size);
2709 #endif
2710
2711         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2712           size = TILESIZE;
2713
2714         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2715                               &src_x, &src_y);
2716
2717         width  = graphic_info[graphic].width  * size / TILESIZE;
2718         height = graphic_info[graphic].height * size / TILESIZE;
2719
2720         if (draw_masked)
2721           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2722                            dst_x, dst_y);
2723         else
2724           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2725                      dst_x, dst_y);
2726       }
2727     }
2728     else if (type == TYPE_GRAPHIC)
2729     {
2730       int graphic        = gpc->graphic;
2731       int graphic_active = gpc->graphic_active;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2738                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2739
2740       if (graphic != IMG_UNDEFINED && !skip)
2741       {
2742         if (pos->style == STYLE_REVERSE)
2743           value = 100 - value;
2744
2745         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2746
2747         if (pos->direction & MV_HORIZONTAL)
2748         {
2749           width  = graphic_info[graphic_active].width * value / 100;
2750           height = graphic_info[graphic_active].height;
2751
2752           if (pos->direction == MV_LEFT)
2753           {
2754             src_x += graphic_info[graphic_active].width - width;
2755             dst_x += graphic_info[graphic_active].width - width;
2756           }
2757         }
2758         else
2759         {
2760           width  = graphic_info[graphic_active].width;
2761           height = graphic_info[graphic_active].height * value / 100;
2762
2763           if (pos->direction == MV_UP)
2764           {
2765             src_y += graphic_info[graphic_active].height - height;
2766             dst_y += graphic_info[graphic_active].height - height;
2767           }
2768         }
2769
2770         if (draw_masked)
2771           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2772                            dst_x, dst_y);
2773         else
2774           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2775                      dst_x, dst_y);
2776
2777         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2778
2779         if (pos->direction & MV_HORIZONTAL)
2780         {
2781           if (pos->direction == MV_RIGHT)
2782           {
2783             src_x += width;
2784             dst_x += width;
2785           }
2786           else
2787           {
2788             dst_x = PANEL_XPOS(pos);
2789           }
2790
2791           width = graphic_info[graphic].width - width;
2792         }
2793         else
2794         {
2795           if (pos->direction == MV_DOWN)
2796           {
2797             src_y += height;
2798             dst_y += height;
2799           }
2800           else
2801           {
2802             dst_y = PANEL_YPOS(pos);
2803           }
2804
2805           height = graphic_info[graphic].height - height;
2806         }
2807
2808         if (draw_masked)
2809           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2810                            dst_x, dst_y);
2811         else
2812           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2813                      dst_x, dst_y);
2814       }
2815     }
2816     else if (type == TYPE_STRING)
2817     {
2818       boolean active = (value != 0);
2819       char *state_normal = "off";
2820       char *state_active = "on";
2821       char *state = (active ? state_active : state_normal);
2822       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2823                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2824                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2825                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2826
2827       if (nr == GAME_PANEL_GRAVITY_STATE)
2828       {
2829         int font1 = pos->font;          // (used for normal state)
2830         int font2 = pos->font_alt;      // (used for active state)
2831
2832         font = (active ? font2 : font1);
2833       }
2834
2835       if (s != NULL)
2836       {
2837         char *s_cut;
2838
2839         if (size <= 0)
2840         {
2841           // don't truncate output if "chars" is zero or less
2842           size = strlen(s);
2843
2844           // dynamically correct text alignment
2845           pos->width = size * getFontWidth(font);
2846         }
2847
2848         s_cut = getStringCopyN(s, size);
2849
2850         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2851                     s_cut, font, mask_mode);
2852
2853         free(s_cut);
2854       }
2855     }
2856
2857     redraw_mask |= REDRAW_DOOR_1;
2858   }
2859
2860   SetGameStatus(GAME_MODE_PLAYING);
2861 }
2862
2863 void UpdateAndDisplayGameControlValues(void)
2864 {
2865   if (tape.deactivate_display)
2866     return;
2867
2868   UpdateGameControlValues();
2869   DisplayGameControlValues();
2870 }
2871
2872 void UpdateGameDoorValues(void)
2873 {
2874   UpdateGameControlValues();
2875 }
2876
2877 void DrawGameDoorValues(void)
2878 {
2879   DisplayGameControlValues();
2880 }
2881
2882
2883 // ============================================================================
2884 // InitGameEngine()
2885 // ----------------------------------------------------------------------------
2886 // initialize game engine due to level / tape version number
2887 // ============================================================================
2888
2889 static void InitGameEngine(void)
2890 {
2891   int i, j, k, l, x, y;
2892
2893   // set game engine from tape file when re-playing, else from level file
2894   game.engine_version = (tape.playing ? tape.engine_version :
2895                          level.game_version);
2896
2897   // set single or multi-player game mode (needed for re-playing tapes)
2898   game.team_mode = setup.team_mode;
2899
2900   if (tape.playing)
2901   {
2902     int num_players = 0;
2903
2904     for (i = 0; i < MAX_PLAYERS; i++)
2905       if (tape.player_participates[i])
2906         num_players++;
2907
2908     // multi-player tapes contain input data for more than one player
2909     game.team_mode = (num_players > 1);
2910   }
2911
2912 #if 0
2913   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2914         level.game_version);
2915   Debug("game:init:level", "          tape.file_version   == %06d",
2916         tape.file_version);
2917   Debug("game:init:level", "          tape.game_version   == %06d",
2918         tape.game_version);
2919   Debug("game:init:level", "          tape.engine_version == %06d",
2920         tape.engine_version);
2921   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2922         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2923 #endif
2924
2925   // --------------------------------------------------------------------------
2926   // set flags for bugs and changes according to active game engine version
2927   // --------------------------------------------------------------------------
2928
2929   /*
2930     Summary of bugfix:
2931     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2932
2933     Bug was introduced in version:
2934     2.0.1
2935
2936     Bug was fixed in version:
2937     4.2.0.0
2938
2939     Description:
2940     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2941     but the property "can fall" was missing, which caused some levels to be
2942     unsolvable. This was fixed in version 4.2.0.0.
2943
2944     Affected levels/tapes:
2945     An example for a tape that was fixed by this bugfix is tape 029 from the
2946     level set "rnd_sam_bateman".
2947     The wrong behaviour will still be used for all levels or tapes that were
2948     created/recorded with it. An example for this is tape 023 from the level
2949     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2950   */
2951
2952   boolean use_amoeba_dropping_cannot_fall_bug =
2953     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2954       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2955      (tape.playing &&
2956       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2957       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2958
2959   /*
2960     Summary of bugfix/change:
2961     Fixed move speed of elements entering or leaving magic wall.
2962
2963     Fixed/changed in version:
2964     2.0.1
2965
2966     Description:
2967     Before 2.0.1, move speed of elements entering or leaving magic wall was
2968     twice as fast as it is now.
2969     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2970
2971     Affected levels/tapes:
2972     The first condition is generally needed for all levels/tapes before version
2973     2.0.1, which might use the old behaviour before it was changed; known tapes
2974     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2975     The second condition is an exception from the above case and is needed for
2976     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2977     above, but before it was known that this change would break tapes like the
2978     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2979     although the engine version while recording maybe was before 2.0.1. There
2980     are a lot of tapes that are affected by this exception, like tape 006 from
2981     the level set "rnd_conor_mancone".
2982   */
2983
2984   boolean use_old_move_stepsize_for_magic_wall =
2985     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2986      !(tape.playing &&
2987        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2988        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2989
2990   /*
2991     Summary of bugfix/change:
2992     Fixed handling for custom elements that change when pushed by the player.
2993
2994     Fixed/changed in version:
2995     3.1.0
2996
2997     Description:
2998     Before 3.1.0, custom elements that "change when pushing" changed directly
2999     after the player started pushing them (until then handled in "DigField()").
3000     Since 3.1.0, these custom elements are not changed until the "pushing"
3001     move of the element is finished (now handled in "ContinueMoving()").
3002
3003     Affected levels/tapes:
3004     The first condition is generally needed for all levels/tapes before version
3005     3.1.0, which might use the old behaviour before it was changed; known tapes
3006     that are affected are some tapes from the level set "Walpurgis Gardens" by
3007     Jamie Cullen.
3008     The second condition is an exception from the above case and is needed for
3009     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3010     above (including some development versions of 3.1.0), but before it was
3011     known that this change would break tapes like the above and was fixed in
3012     3.1.1, so that the changed behaviour was active although the engine version
3013     while recording maybe was before 3.1.0. There is at least one tape that is
3014     affected by this exception, which is the tape for the one-level set "Bug
3015     Machine" by Juergen Bonhagen.
3016   */
3017
3018   game.use_change_when_pushing_bug =
3019     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3020      !(tape.playing &&
3021        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3022        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3023
3024   /*
3025     Summary of bugfix/change:
3026     Fixed handling for blocking the field the player leaves when moving.
3027
3028     Fixed/changed in version:
3029     3.1.1
3030
3031     Description:
3032     Before 3.1.1, when "block last field when moving" was enabled, the field
3033     the player is leaving when moving was blocked for the time of the move,
3034     and was directly unblocked afterwards. This resulted in the last field
3035     being blocked for exactly one less than the number of frames of one player
3036     move. Additionally, even when blocking was disabled, the last field was
3037     blocked for exactly one frame.
3038     Since 3.1.1, due to changes in player movement handling, the last field
3039     is not blocked at all when blocking is disabled. When blocking is enabled,
3040     the last field is blocked for exactly the number of frames of one player
3041     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3042     last field is blocked for exactly one more than the number of frames of
3043     one player move.
3044
3045     Affected levels/tapes:
3046     (!!! yet to be determined -- probably many !!!)
3047   */
3048
3049   game.use_block_last_field_bug =
3050     (game.engine_version < VERSION_IDENT(3,1,1,0));
3051
3052   /* various special flags and settings for native Emerald Mine game engine */
3053
3054   game_em.use_single_button =
3055     (game.engine_version > VERSION_IDENT(4,0,0,2));
3056
3057   game_em.use_snap_key_bug =
3058     (game.engine_version < VERSION_IDENT(4,0,1,0));
3059
3060   game_em.use_random_bug =
3061     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3062
3063   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3064
3065   game_em.use_old_explosions            = use_old_em_engine;
3066   game_em.use_old_android               = use_old_em_engine;
3067   game_em.use_old_push_elements         = use_old_em_engine;
3068   game_em.use_old_push_into_acid        = use_old_em_engine;
3069
3070   game_em.use_wrap_around               = !use_old_em_engine;
3071
3072   // --------------------------------------------------------------------------
3073
3074   // set maximal allowed number of custom element changes per game frame
3075   game.max_num_changes_per_frame = 1;
3076
3077   // default scan direction: scan playfield from top/left to bottom/right
3078   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3079
3080   // dynamically adjust element properties according to game engine version
3081   InitElementPropertiesEngine(game.engine_version);
3082
3083   // ---------- initialize special element properties -------------------------
3084
3085   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3086   if (use_amoeba_dropping_cannot_fall_bug)
3087     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3088
3089   // ---------- initialize player's initial move delay ------------------------
3090
3091   // dynamically adjust player properties according to level information
3092   for (i = 0; i < MAX_PLAYERS; i++)
3093     game.initial_move_delay_value[i] =
3094       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3095
3096   // dynamically adjust player properties according to game engine version
3097   for (i = 0; i < MAX_PLAYERS; i++)
3098     game.initial_move_delay[i] =
3099       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3100        game.initial_move_delay_value[i] : 0);
3101
3102   // ---------- initialize player's initial push delay ------------------------
3103
3104   // dynamically adjust player properties according to game engine version
3105   game.initial_push_delay_value =
3106     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3107
3108   // ---------- initialize changing elements ----------------------------------
3109
3110   // initialize changing elements information
3111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3112   {
3113     struct ElementInfo *ei = &element_info[i];
3114
3115     // this pointer might have been changed in the level editor
3116     ei->change = &ei->change_page[0];
3117
3118     if (!IS_CUSTOM_ELEMENT(i))
3119     {
3120       ei->change->target_element = EL_EMPTY_SPACE;
3121       ei->change->delay_fixed = 0;
3122       ei->change->delay_random = 0;
3123       ei->change->delay_frames = 1;
3124     }
3125
3126     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3127     {
3128       ei->has_change_event[j] = FALSE;
3129
3130       ei->event_page_nr[j] = 0;
3131       ei->event_page[j] = &ei->change_page[0];
3132     }
3133   }
3134
3135   // add changing elements from pre-defined list
3136   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3137   {
3138     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3139     struct ElementInfo *ei = &element_info[ch_delay->element];
3140
3141     ei->change->target_element       = ch_delay->target_element;
3142     ei->change->delay_fixed          = ch_delay->change_delay;
3143
3144     ei->change->pre_change_function  = ch_delay->pre_change_function;
3145     ei->change->change_function      = ch_delay->change_function;
3146     ei->change->post_change_function = ch_delay->post_change_function;
3147
3148     ei->change->can_change = TRUE;
3149     ei->change->can_change_or_has_action = TRUE;
3150
3151     ei->has_change_event[CE_DELAY] = TRUE;
3152
3153     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3154     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3155   }
3156
3157   // ---------- initialize internal run-time variables ------------------------
3158
3159   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3160   {
3161     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3162
3163     for (j = 0; j < ei->num_change_pages; j++)
3164     {
3165       ei->change_page[j].can_change_or_has_action =
3166         (ei->change_page[j].can_change |
3167          ei->change_page[j].has_action);
3168     }
3169   }
3170
3171   // add change events from custom element configuration
3172   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3173   {
3174     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3175
3176     for (j = 0; j < ei->num_change_pages; j++)
3177     {
3178       if (!ei->change_page[j].can_change_or_has_action)
3179         continue;
3180
3181       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3182       {
3183         // only add event page for the first page found with this event
3184         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3185         {
3186           ei->has_change_event[k] = TRUE;
3187
3188           ei->event_page_nr[k] = j;
3189           ei->event_page[k] = &ei->change_page[j];
3190         }
3191       }
3192     }
3193   }
3194
3195   // ---------- initialize reference elements in change conditions ------------
3196
3197   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3198   {
3199     int element = EL_CUSTOM_START + i;
3200     struct ElementInfo *ei = &element_info[element];
3201
3202     for (j = 0; j < ei->num_change_pages; j++)
3203     {
3204       int trigger_element = ei->change_page[j].initial_trigger_element;
3205
3206       if (trigger_element >= EL_PREV_CE_8 &&
3207           trigger_element <= EL_NEXT_CE_8)
3208         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3209
3210       ei->change_page[j].trigger_element = trigger_element;
3211     }
3212   }
3213
3214   // ---------- initialize run-time trigger player and element ----------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3219
3220     for (j = 0; j < ei->num_change_pages; j++)
3221     {
3222       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3223       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3224       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3225       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3226       ei->change_page[j].actual_trigger_ce_value = 0;
3227       ei->change_page[j].actual_trigger_ce_score = 0;
3228     }
3229   }
3230
3231   // ---------- initialize trigger events -------------------------------------
3232
3233   // initialize trigger events information
3234   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3235     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3236       trigger_events[i][j] = FALSE;
3237
3238   // add trigger events from element change event properties
3239   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3240   {
3241     struct ElementInfo *ei = &element_info[i];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       if (!ei->change_page[j].can_change_or_has_action)
3246         continue;
3247
3248       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3249       {
3250         int trigger_element = ei->change_page[j].trigger_element;
3251
3252         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3253         {
3254           if (ei->change_page[j].has_event[k])
3255           {
3256             if (IS_GROUP_ELEMENT(trigger_element))
3257             {
3258               struct ElementGroupInfo *group =
3259                 element_info[trigger_element].group;
3260
3261               for (l = 0; l < group->num_elements_resolved; l++)
3262                 trigger_events[group->element_resolved[l]][k] = TRUE;
3263             }
3264             else if (trigger_element == EL_ANY_ELEMENT)
3265               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3266                 trigger_events[l][k] = TRUE;
3267             else
3268               trigger_events[trigger_element][k] = TRUE;
3269           }
3270         }
3271       }
3272     }
3273   }
3274
3275   // ---------- initialize push delay -----------------------------------------
3276
3277   // initialize push delay values to default
3278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3279   {
3280     if (!IS_CUSTOM_ELEMENT(i))
3281     {
3282       // set default push delay values (corrected since version 3.0.7-1)
3283       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3284       {
3285         element_info[i].push_delay_fixed = 2;
3286         element_info[i].push_delay_random = 8;
3287       }
3288       else
3289       {
3290         element_info[i].push_delay_fixed = 8;
3291         element_info[i].push_delay_random = 8;
3292       }
3293     }
3294   }
3295
3296   // set push delay value for certain elements from pre-defined list
3297   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3298   {
3299     int e = push_delay_list[i].element;
3300
3301     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3302     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3303   }
3304
3305   // set push delay value for Supaplex elements for newer engine versions
3306   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3307   {
3308     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309     {
3310       if (IS_SP_ELEMENT(i))
3311       {
3312         // set SP push delay to just enough to push under a falling zonk
3313         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3314
3315         element_info[i].push_delay_fixed  = delay;
3316         element_info[i].push_delay_random = 0;
3317       }
3318     }
3319   }
3320
3321   // ---------- initialize move stepsize --------------------------------------
3322
3323   // initialize move stepsize values to default
3324   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3325     if (!IS_CUSTOM_ELEMENT(i))
3326       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3327
3328   // set move stepsize value for certain elements from pre-defined list
3329   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3330   {
3331     int e = move_stepsize_list[i].element;
3332
3333     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3334
3335     // set move stepsize value for certain elements for older engine versions
3336     if (use_old_move_stepsize_for_magic_wall)
3337     {
3338       if (e == EL_MAGIC_WALL_FILLING ||
3339           e == EL_MAGIC_WALL_EMPTYING ||
3340           e == EL_BD_MAGIC_WALL_FILLING ||
3341           e == EL_BD_MAGIC_WALL_EMPTYING)
3342         element_info[e].move_stepsize *= 2;
3343     }
3344   }
3345
3346   // ---------- initialize collect score --------------------------------------
3347
3348   // initialize collect score values for custom elements from initial value
3349   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     if (IS_CUSTOM_ELEMENT(i))
3351       element_info[i].collect_score = element_info[i].collect_score_initial;
3352
3353   // ---------- initialize collect count --------------------------------------
3354
3355   // initialize collect count values for non-custom elements
3356   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3357     if (!IS_CUSTOM_ELEMENT(i))
3358       element_info[i].collect_count_initial = 0;
3359
3360   // add collect count values for all elements from pre-defined list
3361   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3362     element_info[collect_count_list[i].element].collect_count_initial =
3363       collect_count_list[i].count;
3364
3365   // ---------- initialize access direction -----------------------------------
3366
3367   // initialize access direction values to default (access from every side)
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (!IS_CUSTOM_ELEMENT(i))
3370       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3371
3372   // set access direction value for certain elements from pre-defined list
3373   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3374     element_info[access_direction_list[i].element].access_direction =
3375       access_direction_list[i].direction;
3376
3377   // ---------- initialize explosion content ----------------------------------
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379   {
3380     if (IS_CUSTOM_ELEMENT(i))
3381       continue;
3382
3383     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3384     {
3385       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3386
3387       element_info[i].content.e[x][y] =
3388         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3389          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3390          i == EL_PLAYER_3 ? EL_EMERALD :
3391          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3392          i == EL_MOLE ? EL_EMERALD_RED :
3393          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3394          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3395          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3396          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3397          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3398          i == EL_WALL_EMERALD ? EL_EMERALD :
3399          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3400          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3401          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3402          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3403          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3404          i == EL_WALL_PEARL ? EL_PEARL :
3405          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3406          EL_EMPTY);
3407     }
3408   }
3409
3410   // ---------- initialize recursion detection --------------------------------
3411   recursion_loop_depth = 0;
3412   recursion_loop_detected = FALSE;
3413   recursion_loop_element = EL_UNDEFINED;
3414
3415   // ---------- initialize graphics engine ------------------------------------
3416   game.scroll_delay_value =
3417     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3418      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3419      !setup.forced_scroll_delay           ? 0 :
3420      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3421   game.scroll_delay_value =
3422     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3423
3424   // ---------- initialize game engine snapshots ------------------------------
3425   for (i = 0; i < MAX_PLAYERS; i++)
3426     game.snapshot.last_action[i] = 0;
3427   game.snapshot.changed_action = FALSE;
3428   game.snapshot.collected_item = FALSE;
3429   game.snapshot.mode =
3430     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3431      SNAPSHOT_MODE_EVERY_STEP :
3432      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3433      SNAPSHOT_MODE_EVERY_MOVE :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3435      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3436   game.snapshot.save_snapshot = FALSE;
3437
3438   // ---------- initialize level time for Supaplex engine ---------------------
3439   // Supaplex levels with time limit currently unsupported -- should be added
3440   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3441     level.time = 0;
3442
3443   // ---------- initialize flags for handling game actions --------------------
3444
3445   // set flags for game actions to default values
3446   game.use_key_actions = TRUE;
3447   game.use_mouse_actions = FALSE;
3448
3449   // when using Mirror Magic game engine, handle mouse events only
3450   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3451   {
3452     game.use_key_actions = FALSE;
3453     game.use_mouse_actions = TRUE;
3454   }
3455
3456   // check for custom elements with mouse click events
3457   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3458   {
3459     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3460     {
3461       int element = EL_CUSTOM_START + i;
3462
3463       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3464           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3465           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3466           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3467         game.use_mouse_actions = TRUE;
3468     }
3469   }
3470 }
3471
3472 static int get_num_special_action(int element, int action_first,
3473                                   int action_last)
3474 {
3475   int num_special_action = 0;
3476   int i, j;
3477
3478   for (i = action_first; i <= action_last; i++)
3479   {
3480     boolean found = FALSE;
3481
3482     for (j = 0; j < NUM_DIRECTIONS; j++)
3483       if (el_act_dir2img(element, i, j) !=
3484           el_act_dir2img(element, ACTION_DEFAULT, j))
3485         found = TRUE;
3486
3487     if (found)
3488       num_special_action++;
3489     else
3490       break;
3491   }
3492
3493   return num_special_action;
3494 }
3495
3496
3497 // ============================================================================
3498 // InitGame()
3499 // ----------------------------------------------------------------------------
3500 // initialize and start new game
3501 // ============================================================================
3502
3503 #if DEBUG_INIT_PLAYER
3504 static void DebugPrintPlayerStatus(char *message)
3505 {
3506   int i;
3507
3508   if (!options.debug)
3509     return;
3510
3511   Debug("game:init:player", "%s:", message);
3512
3513   for (i = 0; i < MAX_PLAYERS; i++)
3514   {
3515     struct PlayerInfo *player = &stored_player[i];
3516
3517     Debug("game:init:player",
3518           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3519           i + 1,
3520           player->present,
3521           player->connected,
3522           player->connected_locally,
3523           player->connected_network,
3524           player->active,
3525           (local_player == player ? " (local player)" : ""));
3526   }
3527 }
3528 #endif
3529
3530 void InitGame(void)
3531 {
3532   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3533   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3534   int fade_mask = REDRAW_FIELD;
3535
3536   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3537   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3538   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3539   int initial_move_dir = MV_DOWN;
3540   int i, j, x, y;
3541
3542   // required here to update video display before fading (FIX THIS)
3543   DrawMaskedBorder(REDRAW_DOOR_2);
3544
3545   if (!game.restart_level)
3546     CloseDoor(DOOR_CLOSE_1);
3547
3548   SetGameStatus(GAME_MODE_PLAYING);
3549
3550   if (level_editor_test_game)
3551     FadeSkipNextFadeOut();
3552   else
3553     FadeSetEnterScreen();
3554
3555   if (CheckFadeAll())
3556     fade_mask = REDRAW_ALL;
3557
3558   FadeLevelSoundsAndMusic();
3559
3560   ExpireSoundLoops(TRUE);
3561
3562   FadeOut(fade_mask);
3563
3564   if (level_editor_test_game)
3565     FadeSkipNextFadeIn();
3566
3567   // needed if different viewport properties defined for playing
3568   ChangeViewportPropertiesIfNeeded();
3569
3570   ClearField();
3571
3572   DrawCompleteVideoDisplay();
3573
3574   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3575
3576   InitGameEngine();
3577   InitGameControlValues();
3578
3579   if (tape.recording)
3580   {
3581     // initialize tape actions from game when recording tape
3582     tape.use_key_actions   = game.use_key_actions;
3583     tape.use_mouse_actions = game.use_mouse_actions;
3584
3585     // initialize visible playfield size when recording tape (for team mode)
3586     tape.scr_fieldx = SCR_FIELDX;
3587     tape.scr_fieldy = SCR_FIELDY;
3588   }
3589
3590   // don't play tapes over network
3591   network_playing = (network.enabled && !tape.playing);
3592
3593   for (i = 0; i < MAX_PLAYERS; i++)
3594   {
3595     struct PlayerInfo *player = &stored_player[i];
3596
3597     player->index_nr = i;
3598     player->index_bit = (1 << i);
3599     player->element_nr = EL_PLAYER_1 + i;
3600
3601     player->present = FALSE;
3602     player->active = FALSE;
3603     player->mapped = FALSE;
3604
3605     player->killed = FALSE;
3606     player->reanimated = FALSE;
3607     player->buried = FALSE;
3608
3609     player->action = 0;
3610     player->effective_action = 0;
3611     player->programmed_action = 0;
3612     player->snap_action = 0;
3613
3614     player->mouse_action.lx = 0;
3615     player->mouse_action.ly = 0;
3616     player->mouse_action.button = 0;
3617     player->mouse_action.button_hint = 0;
3618
3619     player->effective_mouse_action.lx = 0;
3620     player->effective_mouse_action.ly = 0;
3621     player->effective_mouse_action.button = 0;
3622     player->effective_mouse_action.button_hint = 0;
3623
3624     for (j = 0; j < MAX_NUM_KEYS; j++)
3625       player->key[j] = FALSE;
3626
3627     player->num_white_keys = 0;
3628
3629     player->dynabomb_count = 0;
3630     player->dynabomb_size = 1;
3631     player->dynabombs_left = 0;
3632     player->dynabomb_xl = FALSE;
3633
3634     player->MovDir = initial_move_dir;
3635     player->MovPos = 0;
3636     player->GfxPos = 0;
3637     player->GfxDir = initial_move_dir;
3638     player->GfxAction = ACTION_DEFAULT;
3639     player->Frame = 0;
3640     player->StepFrame = 0;
3641
3642     player->initial_element = player->element_nr;
3643     player->artwork_element =
3644       (level.use_artwork_element[i] ? level.artwork_element[i] :
3645        player->element_nr);
3646     player->use_murphy = FALSE;
3647
3648     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3649     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3650
3651     player->gravity = level.initial_player_gravity[i];
3652
3653     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3654
3655     player->actual_frame_counter = 0;
3656
3657     player->step_counter = 0;
3658
3659     player->last_move_dir = initial_move_dir;
3660
3661     player->is_active = FALSE;
3662
3663     player->is_waiting = FALSE;
3664     player->is_moving = FALSE;
3665     player->is_auto_moving = FALSE;
3666     player->is_digging = FALSE;
3667     player->is_snapping = FALSE;
3668     player->is_collecting = FALSE;
3669     player->is_pushing = FALSE;
3670     player->is_switching = FALSE;
3671     player->is_dropping = FALSE;
3672     player->is_dropping_pressed = FALSE;
3673
3674     player->is_bored = FALSE;
3675     player->is_sleeping = FALSE;
3676
3677     player->was_waiting = TRUE;
3678     player->was_moving = FALSE;
3679     player->was_snapping = FALSE;
3680     player->was_dropping = FALSE;
3681
3682     player->force_dropping = FALSE;
3683
3684     player->frame_counter_bored = -1;
3685     player->frame_counter_sleeping = -1;
3686
3687     player->anim_delay_counter = 0;
3688     player->post_delay_counter = 0;
3689
3690     player->dir_waiting = initial_move_dir;
3691     player->action_waiting = ACTION_DEFAULT;
3692     player->last_action_waiting = ACTION_DEFAULT;
3693     player->special_action_bored = ACTION_DEFAULT;
3694     player->special_action_sleeping = ACTION_DEFAULT;
3695
3696     player->switch_x = -1;
3697     player->switch_y = -1;
3698
3699     player->drop_x = -1;
3700     player->drop_y = -1;
3701
3702     player->show_envelope = 0;
3703
3704     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3705
3706     player->push_delay       = -1;      // initialized when pushing starts
3707     player->push_delay_value = game.initial_push_delay_value;
3708
3709     player->drop_delay = 0;
3710     player->drop_pressed_delay = 0;
3711
3712     player->last_jx = -1;
3713     player->last_jy = -1;
3714     player->jx = -1;
3715     player->jy = -1;
3716
3717     player->shield_normal_time_left = 0;
3718     player->shield_deadly_time_left = 0;
3719
3720     player->last_removed_element = EL_UNDEFINED;
3721
3722     player->inventory_infinite_element = EL_UNDEFINED;
3723     player->inventory_size = 0;
3724
3725     if (level.use_initial_inventory[i])
3726     {
3727       for (j = 0; j < level.initial_inventory_size[i]; j++)
3728       {
3729         int element = level.initial_inventory_content[i][j];
3730         int collect_count = element_info[element].collect_count_initial;
3731         int k;
3732
3733         if (!IS_CUSTOM_ELEMENT(element))
3734           collect_count = 1;
3735
3736         if (collect_count == 0)
3737           player->inventory_infinite_element = element;
3738         else
3739           for (k = 0; k < collect_count; k++)
3740             if (player->inventory_size < MAX_INVENTORY_SIZE)
3741               player->inventory_element[player->inventory_size++] = element;
3742       }
3743     }
3744
3745     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3746     SnapField(player, 0, 0);
3747
3748     map_player_action[i] = i;
3749   }
3750
3751   network_player_action_received = FALSE;
3752
3753   // initial null action
3754   if (network_playing)
3755     SendToServer_MovePlayer(MV_NONE);
3756
3757   FrameCounter = 0;
3758   TimeFrames = 0;
3759   TimePlayed = 0;
3760   TimeLeft = level.time;
3761   TapeTime = 0;
3762
3763   ScreenMovDir = MV_NONE;
3764   ScreenMovPos = 0;
3765   ScreenGfxPos = 0;
3766
3767   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3768
3769   game.robot_wheel_x = -1;
3770   game.robot_wheel_y = -1;
3771
3772   game.exit_x = -1;
3773   game.exit_y = -1;
3774
3775   game.all_players_gone = FALSE;
3776
3777   game.LevelSolved = FALSE;
3778   game.GameOver = FALSE;
3779
3780   game.GamePlayed = !tape.playing;
3781
3782   game.LevelSolved_GameWon = FALSE;
3783   game.LevelSolved_GameEnd = FALSE;
3784   game.LevelSolved_SaveTape = FALSE;
3785   game.LevelSolved_SaveScore = FALSE;
3786
3787   game.LevelSolved_CountingTime = 0;
3788   game.LevelSolved_CountingScore = 0;
3789   game.LevelSolved_CountingHealth = 0;
3790
3791   game.panel.active = TRUE;
3792
3793   game.no_time_limit = (level.time == 0);
3794
3795   game.yamyam_content_nr = 0;
3796   game.robot_wheel_active = FALSE;
3797   game.magic_wall_active = FALSE;
3798   game.magic_wall_time_left = 0;
3799   game.light_time_left = 0;
3800   game.timegate_time_left = 0;
3801   game.switchgate_pos = 0;
3802   game.wind_direction = level.wind_direction_initial;
3803
3804   game.score = 0;
3805   game.score_final = 0;
3806
3807   game.health = MAX_HEALTH;
3808   game.health_final = MAX_HEALTH;
3809
3810   game.gems_still_needed = level.gems_needed;
3811   game.sokoban_fields_still_needed = 0;
3812   game.sokoban_objects_still_needed = 0;
3813   game.lights_still_needed = 0;
3814   game.players_still_needed = 0;
3815   game.friends_still_needed = 0;
3816
3817   game.lenses_time_left = 0;
3818   game.magnify_time_left = 0;
3819
3820   game.ball_active = level.ball_active_initial;
3821   game.ball_content_nr = 0;
3822
3823   game.explosions_delayed = TRUE;
3824
3825   game.envelope_active = FALSE;
3826
3827   for (i = 0; i < NUM_BELTS; i++)
3828   {
3829     game.belt_dir[i] = MV_NONE;
3830     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3831   }
3832
3833   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3834     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3835
3836 #if DEBUG_INIT_PLAYER
3837   DebugPrintPlayerStatus("Player status at level initialization");
3838 #endif
3839
3840   SCAN_PLAYFIELD(x, y)
3841   {
3842     Tile[x][y] = Last[x][y] = level.field[x][y];
3843     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3844     ChangeDelay[x][y] = 0;
3845     ChangePage[x][y] = -1;
3846     CustomValue[x][y] = 0;              // initialized in InitField()
3847     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3848     AmoebaNr[x][y] = 0;
3849     WasJustMoving[x][y] = 0;
3850     WasJustFalling[x][y] = 0;
3851     CheckCollision[x][y] = 0;
3852     CheckImpact[x][y] = 0;
3853     Stop[x][y] = FALSE;
3854     Pushed[x][y] = FALSE;
3855
3856     ChangeCount[x][y] = 0;
3857     ChangeEvent[x][y] = -1;
3858
3859     ExplodePhase[x][y] = 0;
3860     ExplodeDelay[x][y] = 0;
3861     ExplodeField[x][y] = EX_TYPE_NONE;
3862
3863     RunnerVisit[x][y] = 0;
3864     PlayerVisit[x][y] = 0;
3865
3866     GfxFrame[x][y] = 0;
3867     GfxRandom[x][y] = INIT_GFX_RANDOM();
3868     GfxElement[x][y] = EL_UNDEFINED;
3869     GfxAction[x][y] = ACTION_DEFAULT;
3870     GfxDir[x][y] = MV_NONE;
3871     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3872   }
3873
3874   SCAN_PLAYFIELD(x, y)
3875   {
3876     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3877       emulate_bd = FALSE;
3878     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3879       emulate_sb = FALSE;
3880     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3881       emulate_sp = FALSE;
3882
3883     InitField(x, y, TRUE);
3884
3885     ResetGfxAnimation(x, y);
3886   }
3887
3888   InitBeltMovement();
3889
3890   for (i = 0; i < MAX_PLAYERS; i++)
3891   {
3892     struct PlayerInfo *player = &stored_player[i];
3893
3894     // set number of special actions for bored and sleeping animation
3895     player->num_special_action_bored =
3896       get_num_special_action(player->artwork_element,
3897                              ACTION_BORING_1, ACTION_BORING_LAST);
3898     player->num_special_action_sleeping =
3899       get_num_special_action(player->artwork_element,
3900                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3901   }
3902
3903   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3904                     emulate_sb ? EMU_SOKOBAN :
3905                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3906
3907   // initialize type of slippery elements
3908   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3909   {
3910     if (!IS_CUSTOM_ELEMENT(i))
3911     {
3912       // default: elements slip down either to the left or right randomly
3913       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3914
3915       // SP style elements prefer to slip down on the left side
3916       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3917         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3918
3919       // BD style elements prefer to slip down on the left side
3920       if (game.emulation == EMU_BOULDERDASH)
3921         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3922     }
3923   }
3924
3925   // initialize explosion and ignition delay
3926   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3927   {
3928     if (!IS_CUSTOM_ELEMENT(i))
3929     {
3930       int num_phase = 8;
3931       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3932                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3933                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3934       int last_phase = (num_phase + 1) * delay;
3935       int half_phase = (num_phase / 2) * delay;
3936
3937       element_info[i].explosion_delay = last_phase - 1;
3938       element_info[i].ignition_delay = half_phase;
3939
3940       if (i == EL_BLACK_ORB)
3941         element_info[i].ignition_delay = 1;
3942     }
3943   }
3944
3945   // correct non-moving belts to start moving left
3946   for (i = 0; i < NUM_BELTS; i++)
3947     if (game.belt_dir[i] == MV_NONE)
3948       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3949
3950 #if USE_NEW_PLAYER_ASSIGNMENTS
3951   // use preferred player also in local single-player mode
3952   if (!network.enabled && !game.team_mode)
3953   {
3954     int new_index_nr = setup.network_player_nr;
3955
3956     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3957     {
3958       for (i = 0; i < MAX_PLAYERS; i++)
3959         stored_player[i].connected_locally = FALSE;
3960
3961       stored_player[new_index_nr].connected_locally = TRUE;
3962     }
3963   }
3964
3965   for (i = 0; i < MAX_PLAYERS; i++)
3966   {
3967     stored_player[i].connected = FALSE;
3968
3969     // in network game mode, the local player might not be the first player
3970     if (stored_player[i].connected_locally)
3971       local_player = &stored_player[i];
3972   }
3973
3974   if (!network.enabled)
3975     local_player->connected = TRUE;
3976
3977   if (tape.playing)
3978   {
3979     for (i = 0; i < MAX_PLAYERS; i++)
3980       stored_player[i].connected = tape.player_participates[i];
3981   }
3982   else if (network.enabled)
3983   {
3984     // add team mode players connected over the network (needed for correct
3985     // assignment of player figures from level to locally playing players)
3986
3987     for (i = 0; i < MAX_PLAYERS; i++)
3988       if (stored_player[i].connected_network)
3989         stored_player[i].connected = TRUE;
3990   }
3991   else if (game.team_mode)
3992   {
3993     // try to guess locally connected team mode players (needed for correct
3994     // assignment of player figures from level to locally playing players)
3995
3996     for (i = 0; i < MAX_PLAYERS; i++)
3997       if (setup.input[i].use_joystick ||
3998           setup.input[i].key.left != KSYM_UNDEFINED)
3999         stored_player[i].connected = TRUE;
4000   }
4001
4002 #if DEBUG_INIT_PLAYER
4003   DebugPrintPlayerStatus("Player status after level initialization");
4004 #endif
4005
4006 #if DEBUG_INIT_PLAYER
4007   Debug("game:init:player", "Reassigning players ...");
4008 #endif
4009
4010   // check if any connected player was not found in playfield
4011   for (i = 0; i < MAX_PLAYERS; i++)
4012   {
4013     struct PlayerInfo *player = &stored_player[i];
4014
4015     if (player->connected && !player->present)
4016     {
4017       struct PlayerInfo *field_player = NULL;
4018
4019 #if DEBUG_INIT_PLAYER
4020       Debug("game:init:player",
4021             "- looking for field player for player %d ...", i + 1);
4022 #endif
4023
4024       // assign first free player found that is present in the playfield
4025
4026       // first try: look for unmapped playfield player that is not connected
4027       for (j = 0; j < MAX_PLAYERS; j++)
4028         if (field_player == NULL &&
4029             stored_player[j].present &&
4030             !stored_player[j].mapped &&
4031             !stored_player[j].connected)
4032           field_player = &stored_player[j];
4033
4034       // second try: look for *any* unmapped playfield player
4035       for (j = 0; j < MAX_PLAYERS; j++)
4036         if (field_player == NULL &&
4037             stored_player[j].present &&
4038             !stored_player[j].mapped)
4039           field_player = &stored_player[j];
4040
4041       if (field_player != NULL)
4042       {
4043         int jx = field_player->jx, jy = field_player->jy;
4044
4045 #if DEBUG_INIT_PLAYER
4046         Debug("game:init:player", "- found player %d",
4047               field_player->index_nr + 1);
4048 #endif
4049
4050         player->present = FALSE;
4051         player->active = FALSE;
4052
4053         field_player->present = TRUE;
4054         field_player->active = TRUE;
4055
4056         /*
4057         player->initial_element = field_player->initial_element;
4058         player->artwork_element = field_player->artwork_element;
4059
4060         player->block_last_field       = field_player->block_last_field;
4061         player->block_delay_adjustment = field_player->block_delay_adjustment;
4062         */
4063
4064         StorePlayer[jx][jy] = field_player->element_nr;
4065
4066         field_player->jx = field_player->last_jx = jx;
4067         field_player->jy = field_player->last_jy = jy;
4068
4069         if (local_player == player)
4070           local_player = field_player;
4071
4072         map_player_action[field_player->index_nr] = i;
4073
4074         field_player->mapped = TRUE;
4075
4076 #if DEBUG_INIT_PLAYER
4077         Debug("game:init:player", "- map_player_action[%d] == %d",
4078               field_player->index_nr + 1, i + 1);
4079 #endif
4080       }
4081     }
4082
4083     if (player->connected && player->present)
4084       player->mapped = TRUE;
4085   }
4086
4087 #if DEBUG_INIT_PLAYER
4088   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4089 #endif
4090
4091 #else
4092
4093   // check if any connected player was not found in playfield
4094   for (i = 0; i < MAX_PLAYERS; i++)
4095   {
4096     struct PlayerInfo *player = &stored_player[i];
4097
4098     if (player->connected && !player->present)
4099     {
4100       for (j = 0; j < MAX_PLAYERS; j++)
4101       {
4102         struct PlayerInfo *field_player = &stored_player[j];
4103         int jx = field_player->jx, jy = field_player->jy;
4104
4105         // assign first free player found that is present in the playfield
4106         if (field_player->present && !field_player->connected)
4107         {
4108           player->present = TRUE;
4109           player->active = TRUE;
4110
4111           field_player->present = FALSE;
4112           field_player->active = FALSE;
4113
4114           player->initial_element = field_player->initial_element;
4115           player->artwork_element = field_player->artwork_element;
4116
4117           player->block_last_field       = field_player->block_last_field;
4118           player->block_delay_adjustment = field_player->block_delay_adjustment;
4119
4120           StorePlayer[jx][jy] = player->element_nr;
4121
4122           player->jx = player->last_jx = jx;
4123           player->jy = player->last_jy = jy;
4124
4125           break;
4126         }
4127       }
4128     }
4129   }
4130 #endif
4131
4132 #if 0
4133   Debug("game:init:player", "local_player->present == %d",
4134         local_player->present);
4135 #endif
4136
4137   // set focus to local player for network games, else to all players
4138   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4139   game.centered_player_nr_next = game.centered_player_nr;
4140   game.set_centered_player = FALSE;
4141   game.set_centered_player_wrap = FALSE;
4142
4143   if (network_playing && tape.recording)
4144   {
4145     // store client dependent player focus when recording network games
4146     tape.centered_player_nr_next = game.centered_player_nr_next;
4147     tape.set_centered_player = TRUE;
4148   }
4149
4150   if (tape.playing)
4151   {
4152     // when playing a tape, eliminate all players who do not participate
4153
4154 #if USE_NEW_PLAYER_ASSIGNMENTS
4155
4156     if (!game.team_mode)
4157     {
4158       for (i = 0; i < MAX_PLAYERS; i++)
4159       {
4160         if (stored_player[i].active &&
4161             !tape.player_participates[map_player_action[i]])
4162         {
4163           struct PlayerInfo *player = &stored_player[i];
4164           int jx = player->jx, jy = player->jy;
4165
4166 #if DEBUG_INIT_PLAYER
4167           Debug("game:init:player", "Removing player %d at (%d, %d)",
4168                 i + 1, jx, jy);
4169 #endif
4170
4171           player->active = FALSE;
4172           StorePlayer[jx][jy] = 0;
4173           Tile[jx][jy] = EL_EMPTY;
4174         }
4175       }
4176     }
4177
4178 #else
4179
4180     for (i = 0; i < MAX_PLAYERS; i++)
4181     {
4182       if (stored_player[i].active &&
4183           !tape.player_participates[i])
4184       {
4185         struct PlayerInfo *player = &stored_player[i];
4186         int jx = player->jx, jy = player->jy;
4187
4188         player->active = FALSE;
4189         StorePlayer[jx][jy] = 0;
4190         Tile[jx][jy] = EL_EMPTY;
4191       }
4192     }
4193 #endif
4194   }
4195   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4196   {
4197     // when in single player mode, eliminate all but the local player
4198
4199     for (i = 0; i < MAX_PLAYERS; i++)
4200     {
4201       struct PlayerInfo *player = &stored_player[i];
4202
4203       if (player->active && player != local_player)
4204       {
4205         int jx = player->jx, jy = player->jy;
4206
4207         player->active = FALSE;
4208         player->present = FALSE;
4209
4210         StorePlayer[jx][jy] = 0;
4211         Tile[jx][jy] = EL_EMPTY;
4212       }
4213     }
4214   }
4215
4216   for (i = 0; i < MAX_PLAYERS; i++)
4217     if (stored_player[i].active)
4218       game.players_still_needed++;
4219
4220   if (level.solved_by_one_player)
4221     game.players_still_needed = 1;
4222
4223   // when recording the game, store which players take part in the game
4224   if (tape.recording)
4225   {
4226 #if USE_NEW_PLAYER_ASSIGNMENTS
4227     for (i = 0; i < MAX_PLAYERS; i++)
4228       if (stored_player[i].connected)
4229         tape.player_participates[i] = TRUE;
4230 #else
4231     for (i = 0; i < MAX_PLAYERS; i++)
4232       if (stored_player[i].active)
4233         tape.player_participates[i] = TRUE;
4234 #endif
4235   }
4236
4237 #if DEBUG_INIT_PLAYER
4238   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4239 #endif
4240
4241   if (BorderElement == EL_EMPTY)
4242   {
4243     SBX_Left = 0;
4244     SBX_Right = lev_fieldx - SCR_FIELDX;
4245     SBY_Upper = 0;
4246     SBY_Lower = lev_fieldy - SCR_FIELDY;
4247   }
4248   else
4249   {
4250     SBX_Left = -1;
4251     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4252     SBY_Upper = -1;
4253     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4254   }
4255
4256   if (full_lev_fieldx <= SCR_FIELDX)
4257     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4258   if (full_lev_fieldy <= SCR_FIELDY)
4259     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4260
4261   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4262     SBX_Left--;
4263   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4264     SBY_Upper--;
4265
4266   // if local player not found, look for custom element that might create
4267   // the player (make some assumptions about the right custom element)
4268   if (!local_player->present)
4269   {
4270     int start_x = 0, start_y = 0;
4271     int found_rating = 0;
4272     int found_element = EL_UNDEFINED;
4273     int player_nr = local_player->index_nr;
4274
4275     SCAN_PLAYFIELD(x, y)
4276     {
4277       int element = Tile[x][y];
4278       int content;
4279       int xx, yy;
4280       boolean is_player;
4281
4282       if (level.use_start_element[player_nr] &&
4283           level.start_element[player_nr] == element &&
4284           found_rating < 4)
4285       {
4286         start_x = x;
4287         start_y = y;
4288
4289         found_rating = 4;
4290         found_element = element;
4291       }
4292
4293       if (!IS_CUSTOM_ELEMENT(element))
4294         continue;
4295
4296       if (CAN_CHANGE(element))
4297       {
4298         for (i = 0; i < element_info[element].num_change_pages; i++)
4299         {
4300           // check for player created from custom element as single target
4301           content = element_info[element].change_page[i].target_element;
4302           is_player = ELEM_IS_PLAYER(content);
4303
4304           if (is_player && (found_rating < 3 ||
4305                             (found_rating == 3 && element < found_element)))
4306           {
4307             start_x = x;
4308             start_y = y;
4309
4310             found_rating = 3;
4311             found_element = element;
4312           }
4313         }
4314       }
4315
4316       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4317       {
4318         // check for player created from custom element as explosion content
4319         content = element_info[element].content.e[xx][yy];
4320         is_player = ELEM_IS_PLAYER(content);
4321
4322         if (is_player && (found_rating < 2 ||
4323                           (found_rating == 2 && element < found_element)))
4324         {
4325           start_x = x + xx - 1;
4326           start_y = y + yy - 1;
4327
4328           found_rating = 2;
4329           found_element = element;
4330         }
4331
4332         if (!CAN_CHANGE(element))
4333           continue;
4334
4335         for (i = 0; i < element_info[element].num_change_pages; i++)
4336         {
4337           // check for player created from custom element as extended target
4338           content =
4339             element_info[element].change_page[i].target_content.e[xx][yy];
4340
4341           is_player = ELEM_IS_PLAYER(content);
4342
4343           if (is_player && (found_rating < 1 ||
4344                             (found_rating == 1 && element < found_element)))
4345           {
4346             start_x = x + xx - 1;
4347             start_y = y + yy - 1;
4348
4349             found_rating = 1;
4350             found_element = element;
4351           }
4352         }
4353       }
4354     }
4355
4356     scroll_x = SCROLL_POSITION_X(start_x);
4357     scroll_y = SCROLL_POSITION_Y(start_y);
4358   }
4359   else
4360   {
4361     scroll_x = SCROLL_POSITION_X(local_player->jx);
4362     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4363   }
4364
4365   // !!! FIX THIS (START) !!!
4366   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4367   {
4368     InitGameEngine_EM();
4369   }
4370   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4371   {
4372     InitGameEngine_SP();
4373   }
4374   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4375   {
4376     InitGameEngine_MM();
4377   }
4378   else
4379   {
4380     DrawLevel(REDRAW_FIELD);
4381     DrawAllPlayers();
4382
4383     // after drawing the level, correct some elements
4384     if (game.timegate_time_left == 0)
4385       CloseAllOpenTimegates();
4386   }
4387
4388   // blit playfield from scroll buffer to normal back buffer for fading in
4389   BlitScreenToBitmap(backbuffer);
4390   // !!! FIX THIS (END) !!!
4391
4392   DrawMaskedBorder(fade_mask);
4393
4394   FadeIn(fade_mask);
4395
4396 #if 1
4397   // full screen redraw is required at this point in the following cases:
4398   // - special editor door undrawn when game was started from level editor
4399   // - drawing area (playfield) was changed and has to be removed completely
4400   redraw_mask = REDRAW_ALL;
4401   BackToFront();
4402 #endif
4403
4404   if (!game.restart_level)
4405   {
4406     // copy default game door content to main double buffer
4407
4408     // !!! CHECK AGAIN !!!
4409     SetPanelBackground();
4410     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4411     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4412   }
4413
4414   SetPanelBackground();
4415   SetDrawBackgroundMask(REDRAW_DOOR_1);
4416
4417   UpdateAndDisplayGameControlValues();
4418
4419   if (!game.restart_level)
4420   {
4421     UnmapGameButtons();
4422     UnmapTapeButtons();
4423
4424     FreeGameButtons();
4425     CreateGameButtons();
4426
4427     MapGameButtons();
4428     MapTapeButtons();
4429
4430     // copy actual game door content to door double buffer for OpenDoor()
4431     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4432
4433     OpenDoor(DOOR_OPEN_ALL);
4434
4435     KeyboardAutoRepeatOffUnlessAutoplay();
4436
4437 #if DEBUG_INIT_PLAYER
4438     DebugPrintPlayerStatus("Player status (final)");
4439 #endif
4440   }
4441
4442   UnmapAllGadgets();
4443
4444   MapGameButtons();
4445   MapTapeButtons();
4446
4447   if (!game.restart_level && !tape.playing)
4448   {
4449     LevelStats_incPlayed(level_nr);
4450
4451     SaveLevelSetup_SeriesInfo();
4452   }
4453
4454   game.restart_level = FALSE;
4455   game.restart_game_message = NULL;
4456
4457   game.request_active = FALSE;
4458   game.request_active_or_moving = FALSE;
4459
4460   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4461     InitGameActions_MM();
4462
4463   SaveEngineSnapshotToListInitial();
4464
4465   if (!game.restart_level)
4466   {
4467     PlaySound(SND_GAME_STARTING);
4468
4469     if (setup.sound_music)
4470       PlayLevelMusic();
4471   }
4472 }
4473
4474 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4475                         int actual_player_x, int actual_player_y)
4476 {
4477   // this is used for non-R'n'D game engines to update certain engine values
4478
4479   // needed to determine if sounds are played within the visible screen area
4480   scroll_x = actual_scroll_x;
4481   scroll_y = actual_scroll_y;
4482
4483   // needed to get player position for "follow finger" playing input method
4484   local_player->jx = actual_player_x;
4485   local_player->jy = actual_player_y;
4486 }
4487
4488 void InitMovDir(int x, int y)
4489 {
4490   int i, element = Tile[x][y];
4491   static int xy[4][2] =
4492   {
4493     {  0, +1 },
4494     { +1,  0 },
4495     {  0, -1 },
4496     { -1,  0 }
4497   };
4498   static int direction[3][4] =
4499   {
4500     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4501     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4502     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4503   };
4504
4505   switch (element)
4506   {
4507     case EL_BUG_RIGHT:
4508     case EL_BUG_UP:
4509     case EL_BUG_LEFT:
4510     case EL_BUG_DOWN:
4511       Tile[x][y] = EL_BUG;
4512       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4513       break;
4514
4515     case EL_SPACESHIP_RIGHT:
4516     case EL_SPACESHIP_UP:
4517     case EL_SPACESHIP_LEFT:
4518     case EL_SPACESHIP_DOWN:
4519       Tile[x][y] = EL_SPACESHIP;
4520       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4521       break;
4522
4523     case EL_BD_BUTTERFLY_RIGHT:
4524     case EL_BD_BUTTERFLY_UP:
4525     case EL_BD_BUTTERFLY_LEFT:
4526     case EL_BD_BUTTERFLY_DOWN:
4527       Tile[x][y] = EL_BD_BUTTERFLY;
4528       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4529       break;
4530
4531     case EL_BD_FIREFLY_RIGHT:
4532     case EL_BD_FIREFLY_UP:
4533     case EL_BD_FIREFLY_LEFT:
4534     case EL_BD_FIREFLY_DOWN:
4535       Tile[x][y] = EL_BD_FIREFLY;
4536       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4537       break;
4538
4539     case EL_PACMAN_RIGHT:
4540     case EL_PACMAN_UP:
4541     case EL_PACMAN_LEFT:
4542     case EL_PACMAN_DOWN:
4543       Tile[x][y] = EL_PACMAN;
4544       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4545       break;
4546
4547     case EL_YAMYAM_LEFT:
4548     case EL_YAMYAM_RIGHT:
4549     case EL_YAMYAM_UP:
4550     case EL_YAMYAM_DOWN:
4551       Tile[x][y] = EL_YAMYAM;
4552       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4553       break;
4554
4555     case EL_SP_SNIKSNAK:
4556       MovDir[x][y] = MV_UP;
4557       break;
4558
4559     case EL_SP_ELECTRON:
4560       MovDir[x][y] = MV_LEFT;
4561       break;
4562
4563     case EL_MOLE_LEFT:
4564     case EL_MOLE_RIGHT:
4565     case EL_MOLE_UP:
4566     case EL_MOLE_DOWN:
4567       Tile[x][y] = EL_MOLE;
4568       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4569       break;
4570
4571     case EL_SPRING_LEFT:
4572     case EL_SPRING_RIGHT:
4573       Tile[x][y] = EL_SPRING;
4574       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4575       break;
4576
4577     default:
4578       if (IS_CUSTOM_ELEMENT(element))
4579       {
4580         struct ElementInfo *ei = &element_info[element];
4581         int move_direction_initial = ei->move_direction_initial;
4582         int move_pattern = ei->move_pattern;
4583
4584         if (move_direction_initial == MV_START_PREVIOUS)
4585         {
4586           if (MovDir[x][y] != MV_NONE)
4587             return;
4588
4589           move_direction_initial = MV_START_AUTOMATIC;
4590         }
4591
4592         if (move_direction_initial == MV_START_RANDOM)
4593           MovDir[x][y] = 1 << RND(4);
4594         else if (move_direction_initial & MV_ANY_DIRECTION)
4595           MovDir[x][y] = move_direction_initial;
4596         else if (move_pattern == MV_ALL_DIRECTIONS ||
4597                  move_pattern == MV_TURNING_LEFT ||
4598                  move_pattern == MV_TURNING_RIGHT ||
4599                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4600                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4601                  move_pattern == MV_TURNING_RANDOM)
4602           MovDir[x][y] = 1 << RND(4);
4603         else if (move_pattern == MV_HORIZONTAL)
4604           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4605         else if (move_pattern == MV_VERTICAL)
4606           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4607         else if (move_pattern & MV_ANY_DIRECTION)
4608           MovDir[x][y] = element_info[element].move_pattern;
4609         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4610                  move_pattern == MV_ALONG_RIGHT_SIDE)
4611         {
4612           // use random direction as default start direction
4613           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4614             MovDir[x][y] = 1 << RND(4);
4615
4616           for (i = 0; i < NUM_DIRECTIONS; i++)
4617           {
4618             int x1 = x + xy[i][0];
4619             int y1 = y + xy[i][1];
4620
4621             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4622             {
4623               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4624                 MovDir[x][y] = direction[0][i];
4625               else
4626                 MovDir[x][y] = direction[1][i];
4627
4628               break;
4629             }
4630           }
4631         }                
4632       }
4633       else
4634       {
4635         MovDir[x][y] = 1 << RND(4);
4636
4637         if (element != EL_BUG &&
4638             element != EL_SPACESHIP &&
4639             element != EL_BD_BUTTERFLY &&
4640             element != EL_BD_FIREFLY)
4641           break;
4642
4643         for (i = 0; i < NUM_DIRECTIONS; i++)
4644         {
4645           int x1 = x + xy[i][0];
4646           int y1 = y + xy[i][1];
4647
4648           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4649           {
4650             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4651             {
4652               MovDir[x][y] = direction[0][i];
4653               break;
4654             }
4655             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4656                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4657             {
4658               MovDir[x][y] = direction[1][i];
4659               break;
4660             }
4661           }
4662         }
4663       }
4664       break;
4665   }
4666
4667   GfxDir[x][y] = MovDir[x][y];
4668 }
4669
4670 void InitAmoebaNr(int x, int y)
4671 {
4672   int i;
4673   int group_nr = AmoebaNeighbourNr(x, y);
4674
4675   if (group_nr == 0)
4676   {
4677     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4678     {
4679       if (AmoebaCnt[i] == 0)
4680       {
4681         group_nr = i;
4682         break;
4683       }
4684     }
4685   }
4686
4687   AmoebaNr[x][y] = group_nr;
4688   AmoebaCnt[group_nr]++;
4689   AmoebaCnt2[group_nr]++;
4690 }
4691
4692 static void LevelSolved(void)
4693 {
4694   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4695       game.players_still_needed > 0)
4696     return;
4697
4698   game.LevelSolved = TRUE;
4699   game.GameOver = TRUE;
4700
4701   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4702                       game_em.lev->score :
4703                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4704                       game_mm.score :
4705                       game.score);
4706   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4707                        MM_HEALTH(game_mm.laser_overload_value) :
4708                        game.health);
4709
4710   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4711   game.LevelSolved_CountingScore = game.score_final;
4712   game.LevelSolved_CountingHealth = game.health_final;
4713 }
4714
4715 void GameWon(void)
4716 {
4717   static int time_count_steps;
4718   static int time, time_final;
4719   static float score, score_final; // needed for time score < 10 for 10 seconds
4720   static int health, health_final;
4721   static int game_over_delay_1 = 0;
4722   static int game_over_delay_2 = 0;
4723   static int game_over_delay_3 = 0;
4724   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4725   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4726
4727   if (!game.LevelSolved_GameWon)
4728   {
4729     int i;
4730
4731     // do not start end game actions before the player stops moving (to exit)
4732     if (local_player->active && local_player->MovPos)
4733       return;
4734
4735     game.LevelSolved_GameWon = TRUE;
4736     game.LevelSolved_SaveTape = tape.recording;
4737     game.LevelSolved_SaveScore = !tape.playing;
4738
4739     if (!tape.playing)
4740     {
4741       LevelStats_incSolved(level_nr);
4742
4743       SaveLevelSetup_SeriesInfo();
4744     }
4745
4746     if (tape.auto_play)         // tape might already be stopped here
4747       tape.auto_play_level_solved = TRUE;
4748
4749     TapeStop();
4750
4751     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4752     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4753     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4754
4755     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4756     score = score_final = game.score_final;
4757     health = health_final = game.health_final;
4758
4759     if (time_score > 0)
4760     {
4761       int time_frames = 0;
4762
4763       if (TimeLeft > 0)
4764       {
4765         time_final = 0;
4766         time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4767       }
4768       else if (game.no_time_limit && TimePlayed < 999)
4769       {
4770         time_final = 999;
4771         time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
4772       }
4773
4774       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4775
4776       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4777
4778       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4779       {
4780         health_final = 0;
4781         score_final += health * time_score;
4782       }
4783
4784       game.score_final = score_final;
4785       game.health_final = health_final;
4786     }
4787
4788     if (level_editor_test_game || !setup.count_score_after_game)
4789     {
4790       time = time_final;
4791       score = score_final;
4792
4793       game.LevelSolved_CountingTime = time;
4794       game.LevelSolved_CountingScore = score;
4795
4796       game_panel_controls[GAME_PANEL_TIME].value = time;
4797       game_panel_controls[GAME_PANEL_SCORE].value = score;
4798
4799       DisplayGameControlValues();
4800     }
4801
4802     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4803     {
4804       // check if last player has left the level
4805       if (game.exit_x >= 0 &&
4806           game.exit_y >= 0)
4807       {
4808         int x = game.exit_x;
4809         int y = game.exit_y;
4810         int element = Tile[x][y];
4811
4812         // close exit door after last player
4813         if ((game.all_players_gone &&
4814              (element == EL_EXIT_OPEN ||
4815               element == EL_SP_EXIT_OPEN ||
4816               element == EL_STEEL_EXIT_OPEN)) ||
4817             element == EL_EM_EXIT_OPEN ||
4818             element == EL_EM_STEEL_EXIT_OPEN)
4819         {
4820
4821           Tile[x][y] =
4822             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4823              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4824              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4825              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4826              EL_EM_STEEL_EXIT_CLOSING);
4827
4828           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4829         }
4830
4831         // player disappears
4832         DrawLevelField(x, y);
4833       }
4834
4835       for (i = 0; i < MAX_PLAYERS; i++)
4836       {
4837         struct PlayerInfo *player = &stored_player[i];
4838
4839         if (player->present)
4840         {
4841           RemovePlayer(player);
4842
4843           // player disappears
4844           DrawLevelField(player->jx, player->jy);
4845         }
4846       }
4847     }
4848
4849     PlaySound(SND_GAME_WINNING);
4850   }
4851
4852   if (setup.count_score_after_game)
4853   {
4854     if (time != time_final)
4855     {
4856       if (game_over_delay_1 > 0)
4857       {
4858         game_over_delay_1--;
4859
4860         return;
4861       }
4862
4863       int time_to_go = ABS(time_final - time);
4864       int time_count_dir = (time < time_final ? +1 : -1);
4865
4866       if (time_to_go < time_count_steps)
4867         time_count_steps = 1;
4868
4869       time  += time_count_steps * time_count_dir;
4870       score += time_count_steps * time_score;
4871
4872       // set final score to correct rounding differences after counting score
4873       if (time == time_final)
4874         score = score_final;
4875
4876       game.LevelSolved_CountingTime = time;
4877       game.LevelSolved_CountingScore = score;
4878
4879       game_panel_controls[GAME_PANEL_TIME].value = time;
4880       game_panel_controls[GAME_PANEL_SCORE].value = score;
4881
4882       DisplayGameControlValues();
4883
4884       if (time == time_final)
4885         StopSound(SND_GAME_LEVELTIME_BONUS);
4886       else if (setup.sound_loops)
4887         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4888       else
4889         PlaySound(SND_GAME_LEVELTIME_BONUS);
4890
4891       return;
4892     }
4893
4894     if (health != health_final)
4895     {
4896       if (game_over_delay_2 > 0)
4897       {
4898         game_over_delay_2--;
4899
4900         return;
4901       }
4902
4903       int health_count_dir = (health < health_final ? +1 : -1);
4904
4905       health += health_count_dir;
4906       score  += time_score;
4907
4908       game.LevelSolved_CountingHealth = health;
4909       game.LevelSolved_CountingScore = score;
4910
4911       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4912       game_panel_controls[GAME_PANEL_SCORE].value = score;
4913
4914       DisplayGameControlValues();
4915
4916       if (health == health_final)
4917         StopSound(SND_GAME_LEVELTIME_BONUS);
4918       else if (setup.sound_loops)
4919         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4920       else
4921         PlaySound(SND_GAME_LEVELTIME_BONUS);
4922
4923       return;
4924     }
4925   }
4926
4927   game.panel.active = FALSE;
4928
4929   if (game_over_delay_3 > 0)
4930   {
4931     game_over_delay_3--;
4932
4933     return;
4934   }
4935
4936   GameEnd();
4937 }
4938
4939 void GameEnd(void)
4940 {
4941   // used instead of "level_nr" (needed for network games)
4942   int last_level_nr = levelset.level_nr;
4943   int hi_pos;
4944
4945   game.LevelSolved_GameEnd = TRUE;
4946
4947   if (game.LevelSolved_SaveTape)
4948   {
4949     // make sure that request dialog to save tape does not open door again
4950     if (!global.use_envelope_request)
4951       CloseDoor(DOOR_CLOSE_1);
4952
4953     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4954   }
4955
4956   // if no tape is to be saved, close both doors simultaneously
4957   CloseDoor(DOOR_CLOSE_ALL);
4958
4959   if (level_editor_test_game)
4960   {
4961     SetGameStatus(GAME_MODE_MAIN);
4962
4963     DrawMainMenu();
4964
4965     return;
4966   }
4967
4968   if (!game.LevelSolved_SaveScore)
4969   {
4970     SetGameStatus(GAME_MODE_MAIN);
4971
4972     DrawMainMenu();
4973
4974     return;
4975   }
4976
4977   if (level_nr == leveldir_current->handicap_level)
4978   {
4979     leveldir_current->handicap_level++;
4980
4981     SaveLevelSetup_SeriesInfo();
4982   }
4983
4984   if (setup.increment_levels &&
4985       level_nr < leveldir_current->last_level &&
4986       !network_playing)
4987   {
4988     level_nr++;         // advance to next level
4989     TapeErase();        // start with empty tape
4990
4991     if (setup.auto_play_next_level)
4992     {
4993       LoadLevel(level_nr);
4994
4995       SaveLevelSetup_SeriesInfo();
4996     }
4997   }
4998
4999   hi_pos = NewHiScore(last_level_nr);
5000
5001   if (hi_pos >= 0 && setup.show_scores_after_game)
5002   {
5003     SetGameStatus(GAME_MODE_SCORES);
5004
5005     DrawHallOfFame(last_level_nr, hi_pos);
5006   }
5007   else if (setup.auto_play_next_level && setup.increment_levels &&
5008            last_level_nr < leveldir_current->last_level &&
5009            !network_playing)
5010   {
5011     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5012   }
5013   else
5014   {
5015     SetGameStatus(GAME_MODE_MAIN);
5016
5017     DrawMainMenu();
5018   }
5019 }
5020
5021 int NewHiScore(int level_nr)
5022 {
5023   int k, l;
5024   int position = -1;
5025   boolean one_score_entry_per_name = !program.many_scores_per_name;
5026
5027   LoadScore(level_nr);
5028
5029   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5030       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5031     return -1;
5032
5033   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5034   {
5035     if (game.score_final > highscore[k].Score)
5036     {
5037       // player has made it to the hall of fame
5038
5039       if (k < MAX_SCORE_ENTRIES - 1)
5040       {
5041         int m = MAX_SCORE_ENTRIES - 1;
5042
5043         if (one_score_entry_per_name)
5044         {
5045           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5046             if (strEqual(setup.player_name, highscore[l].Name))
5047               m = l;
5048
5049           if (m == k)   // player's new highscore overwrites his old one
5050             goto put_into_list;
5051         }
5052
5053         for (l = m; l > k; l--)
5054         {
5055           strcpy(highscore[l].Name, highscore[l - 1].Name);
5056           highscore[l].Score = highscore[l - 1].Score;
5057         }
5058       }
5059
5060       put_into_list:
5061
5062       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5063       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5064       highscore[k].Score = game.score_final;
5065       position = k;
5066
5067       break;
5068     }
5069     else if (one_score_entry_per_name &&
5070              !strncmp(setup.player_name, highscore[k].Name,
5071                       MAX_PLAYER_NAME_LEN))
5072       break;    // player already there with a higher score
5073   }
5074
5075   if (position >= 0) 
5076     SaveScore(level_nr);
5077
5078   return position;
5079 }
5080
5081 static int getElementMoveStepsizeExt(int x, int y, int direction)
5082 {
5083   int element = Tile[x][y];
5084   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5085   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5086   int horiz_move = (dx != 0);
5087   int sign = (horiz_move ? dx : dy);
5088   int step = sign * element_info[element].move_stepsize;
5089
5090   // special values for move stepsize for spring and things on conveyor belt
5091   if (horiz_move)
5092   {
5093     if (CAN_FALL(element) &&
5094         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5095       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5096     else if (element == EL_SPRING)
5097       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5098   }
5099
5100   return step;
5101 }
5102
5103 static int getElementMoveStepsize(int x, int y)
5104 {
5105   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5106 }
5107
5108 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5109 {
5110   if (player->GfxAction != action || player->GfxDir != dir)
5111   {
5112     player->GfxAction = action;
5113     player->GfxDir = dir;
5114     player->Frame = 0;
5115     player->StepFrame = 0;
5116   }
5117 }
5118
5119 static void ResetGfxFrame(int x, int y)
5120 {
5121   // profiling showed that "autotest" spends 10~20% of its time in this function
5122   if (DrawingDeactivatedField())
5123     return;
5124
5125   int element = Tile[x][y];
5126   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5127
5128   if (graphic_info[graphic].anim_global_sync)
5129     GfxFrame[x][y] = FrameCounter;
5130   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5131     GfxFrame[x][y] = CustomValue[x][y];
5132   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5133     GfxFrame[x][y] = element_info[element].collect_score;
5134   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5135     GfxFrame[x][y] = ChangeDelay[x][y];
5136 }
5137
5138 static void ResetGfxAnimation(int x, int y)
5139 {
5140   GfxAction[x][y] = ACTION_DEFAULT;
5141   GfxDir[x][y] = MovDir[x][y];
5142   GfxFrame[x][y] = 0;
5143
5144   ResetGfxFrame(x, y);
5145 }
5146
5147 static void ResetRandomAnimationValue(int x, int y)
5148 {
5149   GfxRandom[x][y] = INIT_GFX_RANDOM();
5150 }
5151
5152 static void InitMovingField(int x, int y, int direction)
5153 {
5154   int element = Tile[x][y];
5155   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5156   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5157   int newx = x + dx;
5158   int newy = y + dy;
5159   boolean is_moving_before, is_moving_after;
5160
5161   // check if element was/is moving or being moved before/after mode change
5162   is_moving_before = (WasJustMoving[x][y] != 0);
5163   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5164
5165   // reset animation only for moving elements which change direction of moving
5166   // or which just started or stopped moving
5167   // (else CEs with property "can move" / "not moving" are reset each frame)
5168   if (is_moving_before != is_moving_after ||
5169       direction != MovDir[x][y])
5170     ResetGfxAnimation(x, y);
5171
5172   MovDir[x][y] = direction;
5173   GfxDir[x][y] = direction;
5174
5175   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5176                      direction == MV_DOWN && CAN_FALL(element) ?
5177                      ACTION_FALLING : ACTION_MOVING);
5178
5179   // this is needed for CEs with property "can move" / "not moving"
5180
5181   if (is_moving_after)
5182   {
5183     if (Tile[newx][newy] == EL_EMPTY)
5184       Tile[newx][newy] = EL_BLOCKED;
5185
5186     MovDir[newx][newy] = MovDir[x][y];
5187
5188     CustomValue[newx][newy] = CustomValue[x][y];
5189
5190     GfxFrame[newx][newy] = GfxFrame[x][y];
5191     GfxRandom[newx][newy] = GfxRandom[x][y];
5192     GfxAction[newx][newy] = GfxAction[x][y];
5193     GfxDir[newx][newy] = GfxDir[x][y];
5194   }
5195 }
5196
5197 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5198 {
5199   int direction = MovDir[x][y];
5200   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5201   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5202
5203   *goes_to_x = newx;
5204   *goes_to_y = newy;
5205 }
5206
5207 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5208 {
5209   int oldx = x, oldy = y;
5210   int direction = MovDir[x][y];
5211
5212   if (direction == MV_LEFT)
5213     oldx++;
5214   else if (direction == MV_RIGHT)
5215     oldx--;
5216   else if (direction == MV_UP)
5217     oldy++;
5218   else if (direction == MV_DOWN)
5219     oldy--;
5220
5221   *comes_from_x = oldx;
5222   *comes_from_y = oldy;
5223 }
5224
5225 static int MovingOrBlocked2Element(int x, int y)
5226 {
5227   int element = Tile[x][y];
5228
5229   if (element == EL_BLOCKED)
5230   {
5231     int oldx, oldy;
5232
5233     Blocked2Moving(x, y, &oldx, &oldy);
5234     return Tile[oldx][oldy];
5235   }
5236   else
5237     return element;
5238 }
5239
5240 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5241 {
5242   // like MovingOrBlocked2Element(), but if element is moving
5243   // and (x,y) is the field the moving element is just leaving,
5244   // return EL_BLOCKED instead of the element value
5245   int element = Tile[x][y];
5246
5247   if (IS_MOVING(x, y))
5248   {
5249     if (element == EL_BLOCKED)
5250     {
5251       int oldx, oldy;
5252
5253       Blocked2Moving(x, y, &oldx, &oldy);
5254       return Tile[oldx][oldy];
5255     }
5256     else
5257       return EL_BLOCKED;
5258   }
5259   else
5260     return element;
5261 }
5262
5263 static void RemoveField(int x, int y)
5264 {
5265   Tile[x][y] = EL_EMPTY;
5266
5267   MovPos[x][y] = 0;
5268   MovDir[x][y] = 0;
5269   MovDelay[x][y] = 0;
5270
5271   CustomValue[x][y] = 0;
5272
5273   AmoebaNr[x][y] = 0;
5274   ChangeDelay[x][y] = 0;
5275   ChangePage[x][y] = -1;
5276   Pushed[x][y] = FALSE;
5277
5278   GfxElement[x][y] = EL_UNDEFINED;
5279   GfxAction[x][y] = ACTION_DEFAULT;
5280   GfxDir[x][y] = MV_NONE;
5281 }
5282
5283 static void RemoveMovingField(int x, int y)
5284 {
5285   int oldx = x, oldy = y, newx = x, newy = y;
5286   int element = Tile[x][y];
5287   int next_element = EL_UNDEFINED;
5288
5289   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5290     return;
5291
5292   if (IS_MOVING(x, y))
5293   {
5294     Moving2Blocked(x, y, &newx, &newy);
5295
5296     if (Tile[newx][newy] != EL_BLOCKED)
5297     {
5298       // element is moving, but target field is not free (blocked), but
5299       // already occupied by something different (example: acid pool);
5300       // in this case, only remove the moving field, but not the target
5301
5302       RemoveField(oldx, oldy);
5303
5304       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5305
5306       TEST_DrawLevelField(oldx, oldy);
5307
5308       return;
5309     }
5310   }
5311   else if (element == EL_BLOCKED)
5312   {
5313     Blocked2Moving(x, y, &oldx, &oldy);
5314     if (!IS_MOVING(oldx, oldy))
5315       return;
5316   }
5317
5318   if (element == EL_BLOCKED &&
5319       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5320        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5321        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5322        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5323        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5324        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5325     next_element = get_next_element(Tile[oldx][oldy]);
5326
5327   RemoveField(oldx, oldy);
5328   RemoveField(newx, newy);
5329
5330   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5331
5332   if (next_element != EL_UNDEFINED)
5333     Tile[oldx][oldy] = next_element;
5334
5335   TEST_DrawLevelField(oldx, oldy);
5336   TEST_DrawLevelField(newx, newy);
5337 }
5338
5339 void DrawDynamite(int x, int y)
5340 {
5341   int sx = SCREENX(x), sy = SCREENY(y);
5342   int graphic = el2img(Tile[x][y]);
5343   int frame;
5344
5345   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5346     return;
5347
5348   if (IS_WALKABLE_INSIDE(Back[x][y]))
5349     return;
5350
5351   if (Back[x][y])
5352     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5353   else if (Store[x][y])
5354     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5355
5356   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5357
5358   if (Back[x][y] || Store[x][y])
5359     DrawGraphicThruMask(sx, sy, graphic, frame);
5360   else
5361     DrawGraphic(sx, sy, graphic, frame);
5362 }
5363
5364 static void CheckDynamite(int x, int y)
5365 {
5366   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5367   {
5368     MovDelay[x][y]--;
5369
5370     if (MovDelay[x][y] != 0)
5371     {
5372       DrawDynamite(x, y);
5373       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5374
5375       return;
5376     }
5377   }
5378
5379   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5380
5381   Bang(x, y);
5382 }
5383
5384 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5385 {
5386   boolean num_checked_players = 0;
5387   int i;
5388
5389   for (i = 0; i < MAX_PLAYERS; i++)
5390   {
5391     if (stored_player[i].active)
5392     {
5393       int sx = stored_player[i].jx;
5394       int sy = stored_player[i].jy;
5395
5396       if (num_checked_players == 0)
5397       {
5398         *sx1 = *sx2 = sx;
5399         *sy1 = *sy2 = sy;
5400       }
5401       else
5402       {
5403         *sx1 = MIN(*sx1, sx);
5404         *sy1 = MIN(*sy1, sy);
5405         *sx2 = MAX(*sx2, sx);
5406         *sy2 = MAX(*sy2, sy);
5407       }
5408
5409       num_checked_players++;
5410     }
5411   }
5412 }
5413
5414 static boolean checkIfAllPlayersFitToScreen_RND(void)
5415 {
5416   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5417
5418   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5419
5420   return (sx2 - sx1 < SCR_FIELDX &&
5421           sy2 - sy1 < SCR_FIELDY);
5422 }
5423
5424 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5425 {
5426   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5427
5428   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5429
5430   *sx = (sx1 + sx2) / 2;
5431   *sy = (sy1 + sy2) / 2;
5432 }
5433
5434 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5435                                boolean center_screen, boolean quick_relocation)
5436 {
5437   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5438   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5439   boolean no_delay = (tape.warp_forward);
5440   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5441   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5442   int new_scroll_x, new_scroll_y;
5443
5444   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5445   {
5446     // case 1: quick relocation inside visible screen (without scrolling)
5447
5448     RedrawPlayfield();
5449
5450     return;
5451   }
5452
5453   if (!level.shifted_relocation || center_screen)
5454   {
5455     // relocation _with_ centering of screen
5456
5457     new_scroll_x = SCROLL_POSITION_X(x);
5458     new_scroll_y = SCROLL_POSITION_Y(y);
5459   }
5460   else
5461   {
5462     // relocation _without_ centering of screen
5463
5464     int center_scroll_x = SCROLL_POSITION_X(old_x);
5465     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5466     int offset_x = x + (scroll_x - center_scroll_x);
5467     int offset_y = y + (scroll_y - center_scroll_y);
5468
5469     // for new screen position, apply previous offset to center position
5470     new_scroll_x = SCROLL_POSITION_X(offset_x);
5471     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5472   }
5473
5474   if (quick_relocation)
5475   {
5476     // case 2: quick relocation (redraw without visible scrolling)
5477
5478     scroll_x = new_scroll_x;
5479     scroll_y = new_scroll_y;
5480
5481     RedrawPlayfield();
5482
5483     return;
5484   }
5485
5486   // case 3: visible relocation (with scrolling to new position)
5487
5488   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5489
5490   SetVideoFrameDelay(wait_delay_value);
5491
5492   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5493   {
5494     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5495     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5496
5497     if (dx == 0 && dy == 0)             // no scrolling needed at all
5498       break;
5499
5500     scroll_x -= dx;
5501     scroll_y -= dy;
5502
5503     // set values for horizontal/vertical screen scrolling (half tile size)
5504     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5505     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5506     int pos_x = dx * TILEX / 2;
5507     int pos_y = dy * TILEY / 2;
5508     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5509     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5510
5511     ScrollLevel(dx, dy);
5512     DrawAllPlayers();
5513
5514     // scroll in two steps of half tile size to make things smoother
5515     BlitScreenToBitmapExt_RND(window, fx, fy);
5516
5517     // scroll second step to align at full tile size
5518     BlitScreenToBitmap(window);
5519   }
5520
5521   DrawAllPlayers();
5522   BackToFront();
5523
5524   SetVideoFrameDelay(frame_delay_value_old);
5525 }
5526
5527 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5528 {
5529   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5530   int player_nr = GET_PLAYER_NR(el_player);
5531   struct PlayerInfo *player = &stored_player[player_nr];
5532   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5533   boolean no_delay = (tape.warp_forward);
5534   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5535   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5536   int old_jx = player->jx;
5537   int old_jy = player->jy;
5538   int old_element = Tile[old_jx][old_jy];
5539   int element = Tile[jx][jy];
5540   boolean player_relocated = (old_jx != jx || old_jy != jy);
5541
5542   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5543   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5544   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5545   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5546   int leave_side_horiz = move_dir_horiz;
5547   int leave_side_vert  = move_dir_vert;
5548   int enter_side = enter_side_horiz | enter_side_vert;
5549   int leave_side = leave_side_horiz | leave_side_vert;
5550
5551   if (player->buried)           // do not reanimate dead player
5552     return;
5553
5554   if (!player_relocated)        // no need to relocate the player
5555     return;
5556
5557   if (IS_PLAYER(jx, jy))        // player already placed at new position
5558   {
5559     RemoveField(jx, jy);        // temporarily remove newly placed player
5560     DrawLevelField(jx, jy);
5561   }
5562
5563   if (player->present)
5564   {
5565     while (player->MovPos)
5566     {
5567       ScrollPlayer(player, SCROLL_GO_ON);
5568       ScrollScreen(NULL, SCROLL_GO_ON);
5569
5570       AdvanceFrameAndPlayerCounters(player->index_nr);
5571
5572       DrawPlayer(player);
5573
5574       BackToFront_WithFrameDelay(wait_delay_value);
5575     }
5576
5577     DrawPlayer(player);         // needed here only to cleanup last field
5578     DrawLevelField(player->jx, player->jy);     // remove player graphic
5579
5580     player->is_moving = FALSE;
5581   }
5582
5583   if (IS_CUSTOM_ELEMENT(old_element))
5584     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5585                                CE_LEFT_BY_PLAYER,
5586                                player->index_bit, leave_side);
5587
5588   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5589                                       CE_PLAYER_LEAVES_X,
5590                                       player->index_bit, leave_side);
5591
5592   Tile[jx][jy] = el_player;
5593   InitPlayerField(jx, jy, el_player, TRUE);
5594
5595   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5596      possible that the relocation target field did not contain a player element,
5597      but a walkable element, to which the new player was relocated -- in this
5598      case, restore that (already initialized!) element on the player field */
5599   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5600   {
5601     Tile[jx][jy] = element;     // restore previously existing element
5602   }
5603
5604   // only visually relocate centered player
5605   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5606                      FALSE, level.instant_relocation);
5607
5608   TestIfPlayerTouchesBadThing(jx, jy);
5609   TestIfPlayerTouchesCustomElement(jx, jy);
5610
5611   if (IS_CUSTOM_ELEMENT(element))
5612     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5613                                player->index_bit, enter_side);
5614
5615   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5616                                       player->index_bit, enter_side);
5617
5618   if (player->is_switching)
5619   {
5620     /* ensure that relocation while still switching an element does not cause
5621        a new element to be treated as also switched directly after relocation
5622        (this is important for teleporter switches that teleport the player to
5623        a place where another teleporter switch is in the same direction, which
5624        would then incorrectly be treated as immediately switched before the
5625        direction key that caused the switch was released) */
5626
5627     player->switch_x += jx - old_jx;
5628     player->switch_y += jy - old_jy;
5629   }
5630 }
5631
5632 static void Explode(int ex, int ey, int phase, int mode)
5633 {
5634   int x, y;
5635   int last_phase;
5636   int border_element;
5637
5638   // !!! eliminate this variable !!!
5639   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5640
5641   if (game.explosions_delayed)
5642   {
5643     ExplodeField[ex][ey] = mode;
5644     return;
5645   }
5646
5647   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5648   {
5649     int center_element = Tile[ex][ey];
5650     int artwork_element, explosion_element;     // set these values later
5651
5652     // remove things displayed in background while burning dynamite
5653     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5654       Back[ex][ey] = 0;
5655
5656     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5657     {
5658       // put moving element to center field (and let it explode there)
5659       center_element = MovingOrBlocked2Element(ex, ey);
5660       RemoveMovingField(ex, ey);
5661       Tile[ex][ey] = center_element;
5662     }
5663
5664     // now "center_element" is finally determined -- set related values now
5665     artwork_element = center_element;           // for custom player artwork
5666     explosion_element = center_element;         // for custom player artwork
5667
5668     if (IS_PLAYER(ex, ey))
5669     {
5670       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5671
5672       artwork_element = stored_player[player_nr].artwork_element;
5673
5674       if (level.use_explosion_element[player_nr])
5675       {
5676         explosion_element = level.explosion_element[player_nr];
5677         artwork_element = explosion_element;
5678       }
5679     }
5680
5681     if (mode == EX_TYPE_NORMAL ||
5682         mode == EX_TYPE_CENTER ||
5683         mode == EX_TYPE_CROSS)
5684       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5685
5686     last_phase = element_info[explosion_element].explosion_delay + 1;
5687
5688     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5689     {
5690       int xx = x - ex + 1;
5691       int yy = y - ey + 1;
5692       int element;
5693
5694       if (!IN_LEV_FIELD(x, y) ||
5695           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5696           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5697         continue;
5698
5699       element = Tile[x][y];
5700
5701       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5702       {
5703         element = MovingOrBlocked2Element(x, y);
5704
5705         if (!IS_EXPLOSION_PROOF(element))
5706           RemoveMovingField(x, y);
5707       }
5708
5709       // indestructible elements can only explode in center (but not flames)
5710       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5711                                            mode == EX_TYPE_BORDER)) ||
5712           element == EL_FLAMES)
5713         continue;
5714
5715       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5716          behaviour, for example when touching a yamyam that explodes to rocks
5717          with active deadly shield, a rock is created under the player !!! */
5718       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5719 #if 0
5720       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5721           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5722            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5723 #else
5724       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5725 #endif
5726       {
5727         if (IS_ACTIVE_BOMB(element))
5728         {
5729           // re-activate things under the bomb like gate or penguin
5730           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5731           Back[x][y] = 0;
5732         }
5733
5734         continue;
5735       }
5736
5737       // save walkable background elements while explosion on same tile
5738       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5739           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5740         Back[x][y] = element;
5741
5742       // ignite explodable elements reached by other explosion
5743       if (element == EL_EXPLOSION)
5744         element = Store2[x][y];
5745
5746       if (AmoebaNr[x][y] &&
5747           (element == EL_AMOEBA_FULL ||
5748            element == EL_BD_AMOEBA ||
5749            element == EL_AMOEBA_GROWING))
5750       {
5751         AmoebaCnt[AmoebaNr[x][y]]--;
5752         AmoebaCnt2[AmoebaNr[x][y]]--;
5753       }
5754
5755       RemoveField(x, y);
5756
5757       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5758       {
5759         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5760
5761         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5762
5763         if (PLAYERINFO(ex, ey)->use_murphy)
5764           Store[x][y] = EL_EMPTY;
5765       }
5766
5767       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5768       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5769       else if (ELEM_IS_PLAYER(center_element))
5770         Store[x][y] = EL_EMPTY;
5771       else if (center_element == EL_YAMYAM)
5772         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5773       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5774         Store[x][y] = element_info[center_element].content.e[xx][yy];
5775 #if 1
5776       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5777       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5778       // otherwise) -- FIX THIS !!!
5779       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5780         Store[x][y] = element_info[element].content.e[1][1];
5781 #else
5782       else if (!CAN_EXPLODE(element))
5783         Store[x][y] = element_info[element].content.e[1][1];
5784 #endif
5785       else
5786         Store[x][y] = EL_EMPTY;
5787
5788       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5789           center_element == EL_AMOEBA_TO_DIAMOND)
5790         Store2[x][y] = element;
5791
5792       Tile[x][y] = EL_EXPLOSION;
5793       GfxElement[x][y] = artwork_element;
5794
5795       ExplodePhase[x][y] = 1;
5796       ExplodeDelay[x][y] = last_phase;
5797
5798       Stop[x][y] = TRUE;
5799     }
5800
5801     if (center_element == EL_YAMYAM)
5802       game.yamyam_content_nr =
5803         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5804
5805     return;
5806   }
5807
5808   if (Stop[ex][ey])
5809     return;
5810
5811   x = ex;
5812   y = ey;
5813
5814   if (phase == 1)
5815     GfxFrame[x][y] = 0;         // restart explosion animation
5816
5817   last_phase = ExplodeDelay[x][y];
5818
5819   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5820
5821   // this can happen if the player leaves an explosion just in time
5822   if (GfxElement[x][y] == EL_UNDEFINED)
5823     GfxElement[x][y] = EL_EMPTY;
5824
5825   border_element = Store2[x][y];
5826   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5827     border_element = StorePlayer[x][y];
5828
5829   if (phase == element_info[border_element].ignition_delay ||
5830       phase == last_phase)
5831   {
5832     boolean border_explosion = FALSE;
5833
5834     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5835         !PLAYER_EXPLOSION_PROTECTED(x, y))
5836     {
5837       KillPlayerUnlessExplosionProtected(x, y);
5838       border_explosion = TRUE;
5839     }
5840     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5841     {
5842       Tile[x][y] = Store2[x][y];
5843       Store2[x][y] = 0;
5844       Bang(x, y);
5845       border_explosion = TRUE;
5846     }
5847     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5848     {
5849       AmoebaToDiamond(x, y);
5850       Store2[x][y] = 0;
5851       border_explosion = TRUE;
5852     }
5853
5854     // if an element just explodes due to another explosion (chain-reaction),
5855     // do not immediately end the new explosion when it was the last frame of
5856     // the explosion (as it would be done in the following "if"-statement!)
5857     if (border_explosion && phase == last_phase)
5858       return;
5859   }
5860
5861   if (phase == last_phase)
5862   {
5863     int element;
5864
5865     element = Tile[x][y] = Store[x][y];
5866     Store[x][y] = Store2[x][y] = 0;
5867     GfxElement[x][y] = EL_UNDEFINED;
5868
5869     // player can escape from explosions and might therefore be still alive
5870     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5871         element <= EL_PLAYER_IS_EXPLODING_4)
5872     {
5873       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5874       int explosion_element = EL_PLAYER_1 + player_nr;
5875       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5876       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5877
5878       if (level.use_explosion_element[player_nr])
5879         explosion_element = level.explosion_element[player_nr];
5880
5881       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5882                     element_info[explosion_element].content.e[xx][yy]);
5883     }
5884
5885     // restore probably existing indestructible background element
5886     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5887       element = Tile[x][y] = Back[x][y];
5888     Back[x][y] = 0;
5889
5890     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5891     GfxDir[x][y] = MV_NONE;
5892     ChangeDelay[x][y] = 0;
5893     ChangePage[x][y] = -1;
5894
5895     CustomValue[x][y] = 0;
5896
5897     InitField_WithBug2(x, y, FALSE);
5898
5899     TEST_DrawLevelField(x, y);
5900
5901     TestIfElementTouchesCustomElement(x, y);
5902
5903     if (GFX_CRUMBLED(element))
5904       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5905
5906     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5907       StorePlayer[x][y] = 0;
5908
5909     if (ELEM_IS_PLAYER(element))
5910       RelocatePlayer(x, y, element);
5911   }
5912   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5913   {
5914     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5915     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5916
5917     if (phase == delay)
5918       TEST_DrawLevelFieldCrumbled(x, y);
5919
5920     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5921     {
5922       DrawLevelElement(x, y, Back[x][y]);
5923       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5924     }
5925     else if (IS_WALKABLE_UNDER(Back[x][y]))
5926     {
5927       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5928       DrawLevelElementThruMask(x, y, Back[x][y]);
5929     }
5930     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5931       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5932   }
5933 }
5934
5935 static void DynaExplode(int ex, int ey)
5936 {
5937   int i, j;
5938   int dynabomb_element = Tile[ex][ey];
5939   int dynabomb_size = 1;
5940   boolean dynabomb_xl = FALSE;
5941   struct PlayerInfo *player;
5942   static int xy[4][2] =
5943   {
5944     { 0, -1 },
5945     { -1, 0 },
5946     { +1, 0 },
5947     { 0, +1 }
5948   };
5949
5950   if (IS_ACTIVE_BOMB(dynabomb_element))
5951   {
5952     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5953     dynabomb_size = player->dynabomb_size;
5954     dynabomb_xl = player->dynabomb_xl;
5955     player->dynabombs_left++;
5956   }
5957
5958   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5959
5960   for (i = 0; i < NUM_DIRECTIONS; i++)
5961   {
5962     for (j = 1; j <= dynabomb_size; j++)
5963     {
5964       int x = ex + j * xy[i][0];
5965       int y = ey + j * xy[i][1];
5966       int element;
5967
5968       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5969         break;
5970
5971       element = Tile[x][y];
5972
5973       // do not restart explosions of fields with active bombs
5974       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5975         continue;
5976
5977       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5978
5979       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5980           !IS_DIGGABLE(element) && !dynabomb_xl)
5981         break;
5982     }
5983   }
5984 }
5985
5986 void Bang(int x, int y)
5987 {
5988   int element = MovingOrBlocked2Element(x, y);
5989   int explosion_type = EX_TYPE_NORMAL;
5990
5991   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5992   {
5993     struct PlayerInfo *player = PLAYERINFO(x, y);
5994
5995     element = Tile[x][y] = player->initial_element;
5996
5997     if (level.use_explosion_element[player->index_nr])
5998     {
5999       int explosion_element = level.explosion_element[player->index_nr];
6000
6001       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6002         explosion_type = EX_TYPE_CROSS;
6003       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6004         explosion_type = EX_TYPE_CENTER;
6005     }
6006   }
6007
6008   switch (element)
6009   {
6010     case EL_BUG:
6011     case EL_SPACESHIP:
6012     case EL_BD_BUTTERFLY:
6013     case EL_BD_FIREFLY:
6014     case EL_YAMYAM:
6015     case EL_DARK_YAMYAM:
6016     case EL_ROBOT:
6017     case EL_PACMAN:
6018     case EL_MOLE:
6019       RaiseScoreElement(element);
6020       break;
6021
6022     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6023     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6024     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6025     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6026     case EL_DYNABOMB_INCREASE_NUMBER:
6027     case EL_DYNABOMB_INCREASE_SIZE:
6028     case EL_DYNABOMB_INCREASE_POWER:
6029       explosion_type = EX_TYPE_DYNA;
6030       break;
6031
6032     case EL_DC_LANDMINE:
6033       explosion_type = EX_TYPE_CENTER;
6034       break;
6035
6036     case EL_PENGUIN:
6037     case EL_LAMP:
6038     case EL_LAMP_ACTIVE:
6039     case EL_AMOEBA_TO_DIAMOND:
6040       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6041         explosion_type = EX_TYPE_CENTER;
6042       break;
6043
6044     default:
6045       if (element_info[element].explosion_type == EXPLODES_CROSS)
6046         explosion_type = EX_TYPE_CROSS;
6047       else if (element_info[element].explosion_type == EXPLODES_1X1)
6048         explosion_type = EX_TYPE_CENTER;
6049       break;
6050   }
6051
6052   if (explosion_type == EX_TYPE_DYNA)
6053     DynaExplode(x, y);
6054   else
6055     Explode(x, y, EX_PHASE_START, explosion_type);
6056
6057   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6058 }
6059
6060 static void SplashAcid(int x, int y)
6061 {
6062   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6063       (!IN_LEV_FIELD(x - 1, y - 2) ||
6064        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6065     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6066
6067   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6068       (!IN_LEV_FIELD(x + 1, y - 2) ||
6069        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6070     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6071
6072   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6073 }
6074
6075 static void InitBeltMovement(void)
6076 {
6077   static int belt_base_element[4] =
6078   {
6079     EL_CONVEYOR_BELT_1_LEFT,
6080     EL_CONVEYOR_BELT_2_LEFT,
6081     EL_CONVEYOR_BELT_3_LEFT,
6082     EL_CONVEYOR_BELT_4_LEFT
6083   };
6084   static int belt_base_active_element[4] =
6085   {
6086     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6087     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6088     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6089     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6090   };
6091
6092   int x, y, i, j;
6093
6094   // set frame order for belt animation graphic according to belt direction
6095   for (i = 0; i < NUM_BELTS; i++)
6096   {
6097     int belt_nr = i;
6098
6099     for (j = 0; j < NUM_BELT_PARTS; j++)
6100     {
6101       int element = belt_base_active_element[belt_nr] + j;
6102       int graphic_1 = el2img(element);
6103       int graphic_2 = el2panelimg(element);
6104
6105       if (game.belt_dir[i] == MV_LEFT)
6106       {
6107         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6108         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6109       }
6110       else
6111       {
6112         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6113         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6114       }
6115     }
6116   }
6117
6118   SCAN_PLAYFIELD(x, y)
6119   {
6120     int element = Tile[x][y];
6121
6122     for (i = 0; i < NUM_BELTS; i++)
6123     {
6124       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6125       {
6126         int e_belt_nr = getBeltNrFromBeltElement(element);
6127         int belt_nr = i;
6128
6129         if (e_belt_nr == belt_nr)
6130         {
6131           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6132
6133           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6134         }
6135       }
6136     }
6137   }
6138 }
6139
6140 static void ToggleBeltSwitch(int x, int y)
6141 {
6142   static int belt_base_element[4] =
6143   {
6144     EL_CONVEYOR_BELT_1_LEFT,
6145     EL_CONVEYOR_BELT_2_LEFT,
6146     EL_CONVEYOR_BELT_3_LEFT,
6147     EL_CONVEYOR_BELT_4_LEFT
6148   };
6149   static int belt_base_active_element[4] =
6150   {
6151     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6152     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6153     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6154     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6155   };
6156   static int belt_base_switch_element[4] =
6157   {
6158     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6159     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6160     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6161     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6162   };
6163   static int belt_move_dir[4] =
6164   {
6165     MV_LEFT,
6166     MV_NONE,
6167     MV_RIGHT,
6168     MV_NONE,
6169   };
6170
6171   int element = Tile[x][y];
6172   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6173   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6174   int belt_dir = belt_move_dir[belt_dir_nr];
6175   int xx, yy, i;
6176
6177   if (!IS_BELT_SWITCH(element))
6178     return;
6179
6180   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6181   game.belt_dir[belt_nr] = belt_dir;
6182
6183   if (belt_dir_nr == 3)
6184     belt_dir_nr = 1;
6185
6186   // set frame order for belt animation graphic according to belt direction
6187   for (i = 0; i < NUM_BELT_PARTS; i++)
6188   {
6189     int element = belt_base_active_element[belt_nr] + i;
6190     int graphic_1 = el2img(element);
6191     int graphic_2 = el2panelimg(element);
6192
6193     if (belt_dir == MV_LEFT)
6194     {
6195       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6196       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6197     }
6198     else
6199     {
6200       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6201       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6202     }
6203   }
6204
6205   SCAN_PLAYFIELD(xx, yy)
6206   {
6207     int element = Tile[xx][yy];
6208
6209     if (IS_BELT_SWITCH(element))
6210     {
6211       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6212
6213       if (e_belt_nr == belt_nr)
6214       {
6215         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6216         TEST_DrawLevelField(xx, yy);
6217       }
6218     }
6219     else if (IS_BELT(element) && belt_dir != MV_NONE)
6220     {
6221       int e_belt_nr = getBeltNrFromBeltElement(element);
6222
6223       if (e_belt_nr == belt_nr)
6224       {
6225         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6226
6227         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6228         TEST_DrawLevelField(xx, yy);
6229       }
6230     }
6231     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6232     {
6233       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6234
6235       if (e_belt_nr == belt_nr)
6236       {
6237         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6238
6239         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6240         TEST_DrawLevelField(xx, yy);
6241       }
6242     }
6243   }
6244 }
6245
6246 static void ToggleSwitchgateSwitch(int x, int y)
6247 {
6248   int xx, yy;
6249
6250   game.switchgate_pos = !game.switchgate_pos;
6251
6252   SCAN_PLAYFIELD(xx, yy)
6253   {
6254     int element = Tile[xx][yy];
6255
6256     if (element == EL_SWITCHGATE_SWITCH_UP)
6257     {
6258       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6259       TEST_DrawLevelField(xx, yy);
6260     }
6261     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6262     {
6263       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6264       TEST_DrawLevelField(xx, yy);
6265     }
6266     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6267     {
6268       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6269       TEST_DrawLevelField(xx, yy);
6270     }
6271     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6272     {
6273       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6274       TEST_DrawLevelField(xx, yy);
6275     }
6276     else if (element == EL_SWITCHGATE_OPEN ||
6277              element == EL_SWITCHGATE_OPENING)
6278     {
6279       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6280
6281       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6282     }
6283     else if (element == EL_SWITCHGATE_CLOSED ||
6284              element == EL_SWITCHGATE_CLOSING)
6285     {
6286       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6287
6288       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6289     }
6290   }
6291 }
6292
6293 static int getInvisibleActiveFromInvisibleElement(int element)
6294 {
6295   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6296           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6297           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6298           element);
6299 }
6300
6301 static int getInvisibleFromInvisibleActiveElement(int element)
6302 {
6303   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6304           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6305           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6306           element);
6307 }
6308
6309 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6310 {
6311   int x, y;
6312
6313   SCAN_PLAYFIELD(x, y)
6314   {
6315     int element = Tile[x][y];
6316
6317     if (element == EL_LIGHT_SWITCH &&
6318         game.light_time_left > 0)
6319     {
6320       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6321       TEST_DrawLevelField(x, y);
6322     }
6323     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6324              game.light_time_left == 0)
6325     {
6326       Tile[x][y] = EL_LIGHT_SWITCH;
6327       TEST_DrawLevelField(x, y);
6328     }
6329     else if (element == EL_EMC_DRIPPER &&
6330              game.light_time_left > 0)
6331     {
6332       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6333       TEST_DrawLevelField(x, y);
6334     }
6335     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6336              game.light_time_left == 0)
6337     {
6338       Tile[x][y] = EL_EMC_DRIPPER;
6339       TEST_DrawLevelField(x, y);
6340     }
6341     else if (element == EL_INVISIBLE_STEELWALL ||
6342              element == EL_INVISIBLE_WALL ||
6343              element == EL_INVISIBLE_SAND)
6344     {
6345       if (game.light_time_left > 0)
6346         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6347
6348       TEST_DrawLevelField(x, y);
6349
6350       // uncrumble neighbour fields, if needed
6351       if (element == EL_INVISIBLE_SAND)
6352         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6353     }
6354     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6355              element == EL_INVISIBLE_WALL_ACTIVE ||
6356              element == EL_INVISIBLE_SAND_ACTIVE)
6357     {
6358       if (game.light_time_left == 0)
6359         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6360
6361       TEST_DrawLevelField(x, y);
6362
6363       // re-crumble neighbour fields, if needed
6364       if (element == EL_INVISIBLE_SAND)
6365         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6366     }
6367   }
6368 }
6369
6370 static void RedrawAllInvisibleElementsForLenses(void)
6371 {
6372   int x, y;
6373
6374   SCAN_PLAYFIELD(x, y)
6375   {
6376     int element = Tile[x][y];
6377
6378     if (element == EL_EMC_DRIPPER &&
6379         game.lenses_time_left > 0)
6380     {
6381       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6382       TEST_DrawLevelField(x, y);
6383     }
6384     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6385              game.lenses_time_left == 0)
6386     {
6387       Tile[x][y] = EL_EMC_DRIPPER;
6388       TEST_DrawLevelField(x, y);
6389     }
6390     else if (element == EL_INVISIBLE_STEELWALL ||
6391              element == EL_INVISIBLE_WALL ||
6392              element == EL_INVISIBLE_SAND)
6393     {
6394       if (game.lenses_time_left > 0)
6395         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6396
6397       TEST_DrawLevelField(x, y);
6398
6399       // uncrumble neighbour fields, if needed
6400       if (element == EL_INVISIBLE_SAND)
6401         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6402     }
6403     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6404              element == EL_INVISIBLE_WALL_ACTIVE ||
6405              element == EL_INVISIBLE_SAND_ACTIVE)
6406     {
6407       if (game.lenses_time_left == 0)
6408         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6409
6410       TEST_DrawLevelField(x, y);
6411
6412       // re-crumble neighbour fields, if needed
6413       if (element == EL_INVISIBLE_SAND)
6414         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6415     }
6416   }
6417 }
6418
6419 static void RedrawAllInvisibleElementsForMagnifier(void)
6420 {
6421   int x, y;
6422
6423   SCAN_PLAYFIELD(x, y)
6424   {
6425     int element = Tile[x][y];
6426
6427     if (element == EL_EMC_FAKE_GRASS &&
6428         game.magnify_time_left > 0)
6429     {
6430       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6431       TEST_DrawLevelField(x, y);
6432     }
6433     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6434              game.magnify_time_left == 0)
6435     {
6436       Tile[x][y] = EL_EMC_FAKE_GRASS;
6437       TEST_DrawLevelField(x, y);
6438     }
6439     else if (IS_GATE_GRAY(element) &&
6440              game.magnify_time_left > 0)
6441     {
6442       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6443                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6444                     IS_EM_GATE_GRAY(element) ?
6445                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6446                     IS_EMC_GATE_GRAY(element) ?
6447                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6448                     IS_DC_GATE_GRAY(element) ?
6449                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6450                     element);
6451       TEST_DrawLevelField(x, y);
6452     }
6453     else if (IS_GATE_GRAY_ACTIVE(element) &&
6454              game.magnify_time_left == 0)
6455     {
6456       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6457                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6458                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6459                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6460                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6461                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6462                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6463                     EL_DC_GATE_WHITE_GRAY :
6464                     element);
6465       TEST_DrawLevelField(x, y);
6466     }
6467   }
6468 }
6469
6470 static void ToggleLightSwitch(int x, int y)
6471 {
6472   int element = Tile[x][y];
6473
6474   game.light_time_left =
6475     (element == EL_LIGHT_SWITCH ?
6476      level.time_light * FRAMES_PER_SECOND : 0);
6477
6478   RedrawAllLightSwitchesAndInvisibleElements();
6479 }
6480
6481 static void ActivateTimegateSwitch(int x, int y)
6482 {
6483   int xx, yy;
6484
6485   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6486
6487   SCAN_PLAYFIELD(xx, yy)
6488   {
6489     int element = Tile[xx][yy];
6490
6491     if (element == EL_TIMEGATE_CLOSED ||
6492         element == EL_TIMEGATE_CLOSING)
6493     {
6494       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6495       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6496     }
6497
6498     /*
6499     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6500     {
6501       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6502       TEST_DrawLevelField(xx, yy);
6503     }
6504     */
6505
6506   }
6507
6508   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6509                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6510 }
6511
6512 static void Impact(int x, int y)
6513 {
6514   boolean last_line = (y == lev_fieldy - 1);
6515   boolean object_hit = FALSE;
6516   boolean impact = (last_line || object_hit);
6517   int element = Tile[x][y];
6518   int smashed = EL_STEELWALL;
6519
6520   if (!last_line)       // check if element below was hit
6521   {
6522     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6523       return;
6524
6525     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6526                                          MovDir[x][y + 1] != MV_DOWN ||
6527                                          MovPos[x][y + 1] <= TILEY / 2));
6528
6529     // do not smash moving elements that left the smashed field in time
6530     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6531         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6532       object_hit = FALSE;
6533
6534 #if USE_QUICKSAND_IMPACT_BUGFIX
6535     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6536     {
6537       RemoveMovingField(x, y + 1);
6538       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6539       Tile[x][y + 2] = EL_ROCK;
6540       TEST_DrawLevelField(x, y + 2);
6541
6542       object_hit = TRUE;
6543     }
6544
6545     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6546     {
6547       RemoveMovingField(x, y + 1);
6548       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6549       Tile[x][y + 2] = EL_ROCK;
6550       TEST_DrawLevelField(x, y + 2);
6551
6552       object_hit = TRUE;
6553     }
6554 #endif
6555
6556     if (object_hit)
6557       smashed = MovingOrBlocked2Element(x, y + 1);
6558
6559     impact = (last_line || object_hit);
6560   }
6561
6562   if (!last_line && smashed == EL_ACID) // element falls into acid
6563   {
6564     SplashAcid(x, y + 1);
6565     return;
6566   }
6567
6568   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6569   // only reset graphic animation if graphic really changes after impact
6570   if (impact &&
6571       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6572   {
6573     ResetGfxAnimation(x, y);
6574     TEST_DrawLevelField(x, y);
6575   }
6576
6577   if (impact && CAN_EXPLODE_IMPACT(element))
6578   {
6579     Bang(x, y);
6580     return;
6581   }
6582   else if (impact && element == EL_PEARL &&
6583            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6584   {
6585     ResetGfxAnimation(x, y);
6586
6587     Tile[x][y] = EL_PEARL_BREAKING;
6588     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6589     return;
6590   }
6591   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6592   {
6593     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6594
6595     return;
6596   }
6597
6598   if (impact && element == EL_AMOEBA_DROP)
6599   {
6600     if (object_hit && IS_PLAYER(x, y + 1))
6601       KillPlayerUnlessEnemyProtected(x, y + 1);
6602     else if (object_hit && smashed == EL_PENGUIN)
6603       Bang(x, y + 1);
6604     else
6605     {
6606       Tile[x][y] = EL_AMOEBA_GROWING;
6607       Store[x][y] = EL_AMOEBA_WET;
6608
6609       ResetRandomAnimationValue(x, y);
6610     }
6611     return;
6612   }
6613
6614   if (object_hit)               // check which object was hit
6615   {
6616     if ((CAN_PASS_MAGIC_WALL(element) && 
6617          (smashed == EL_MAGIC_WALL ||
6618           smashed == EL_BD_MAGIC_WALL)) ||
6619         (CAN_PASS_DC_MAGIC_WALL(element) &&
6620          smashed == EL_DC_MAGIC_WALL))
6621     {
6622       int xx, yy;
6623       int activated_magic_wall =
6624         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6625          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6626          EL_DC_MAGIC_WALL_ACTIVE);
6627
6628       // activate magic wall / mill
6629       SCAN_PLAYFIELD(xx, yy)
6630       {
6631         if (Tile[xx][yy] == smashed)
6632           Tile[xx][yy] = activated_magic_wall;
6633       }
6634
6635       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6636       game.magic_wall_active = TRUE;
6637
6638       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6639                             SND_MAGIC_WALL_ACTIVATING :
6640                             smashed == EL_BD_MAGIC_WALL ?
6641                             SND_BD_MAGIC_WALL_ACTIVATING :
6642                             SND_DC_MAGIC_WALL_ACTIVATING));
6643     }
6644
6645     if (IS_PLAYER(x, y + 1))
6646     {
6647       if (CAN_SMASH_PLAYER(element))
6648       {
6649         KillPlayerUnlessEnemyProtected(x, y + 1);
6650         return;
6651       }
6652     }
6653     else if (smashed == EL_PENGUIN)
6654     {
6655       if (CAN_SMASH_PLAYER(element))
6656       {
6657         Bang(x, y + 1);
6658         return;
6659       }
6660     }
6661     else if (element == EL_BD_DIAMOND)
6662     {
6663       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6664       {
6665         Bang(x, y + 1);
6666         return;
6667       }
6668     }
6669     else if (((element == EL_SP_INFOTRON ||
6670                element == EL_SP_ZONK) &&
6671               (smashed == EL_SP_SNIKSNAK ||
6672                smashed == EL_SP_ELECTRON ||
6673                smashed == EL_SP_DISK_ORANGE)) ||
6674              (element == EL_SP_INFOTRON &&
6675               smashed == EL_SP_DISK_YELLOW))
6676     {
6677       Bang(x, y + 1);
6678       return;
6679     }
6680     else if (CAN_SMASH_EVERYTHING(element))
6681     {
6682       if (IS_CLASSIC_ENEMY(smashed) ||
6683           CAN_EXPLODE_SMASHED(smashed))
6684       {
6685         Bang(x, y + 1);
6686         return;
6687       }
6688       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6689       {
6690         if (smashed == EL_LAMP ||
6691             smashed == EL_LAMP_ACTIVE)
6692         {
6693           Bang(x, y + 1);
6694           return;
6695         }
6696         else if (smashed == EL_NUT)
6697         {
6698           Tile[x][y + 1] = EL_NUT_BREAKING;
6699           PlayLevelSound(x, y, SND_NUT_BREAKING);
6700           RaiseScoreElement(EL_NUT);
6701           return;
6702         }
6703         else if (smashed == EL_PEARL)
6704         {
6705           ResetGfxAnimation(x, y);
6706
6707           Tile[x][y + 1] = EL_PEARL_BREAKING;
6708           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6709           return;
6710         }
6711         else if (smashed == EL_DIAMOND)
6712         {
6713           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6714           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6715           return;
6716         }
6717         else if (IS_BELT_SWITCH(smashed))
6718         {
6719           ToggleBeltSwitch(x, y + 1);
6720         }
6721         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6722                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6723                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6724                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6725         {
6726           ToggleSwitchgateSwitch(x, y + 1);
6727         }
6728         else if (smashed == EL_LIGHT_SWITCH ||
6729                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6730         {
6731           ToggleLightSwitch(x, y + 1);
6732         }
6733         else
6734         {
6735           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6736
6737           CheckElementChangeBySide(x, y + 1, smashed, element,
6738                                    CE_SWITCHED, CH_SIDE_TOP);
6739           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6740                                             CH_SIDE_TOP);
6741         }
6742       }
6743       else
6744       {
6745         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6746       }
6747     }
6748   }
6749
6750   // play sound of magic wall / mill
6751   if (!last_line &&
6752       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6753        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6754        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6755   {
6756     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6757       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6758     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6759       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6760     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6761       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6762
6763     return;
6764   }
6765
6766   // play sound of object that hits the ground
6767   if (last_line || object_hit)
6768     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6769 }
6770
6771 static void TurnRoundExt(int x, int y)
6772 {
6773   static struct
6774   {
6775     int dx, dy;
6776   } move_xy[] =
6777   {
6778     {  0,  0 },
6779     { -1,  0 },
6780     { +1,  0 },
6781     {  0,  0 },
6782     {  0, -1 },
6783     {  0,  0 }, { 0, 0 }, { 0, 0 },
6784     {  0, +1 }
6785   };
6786   static struct
6787   {
6788     int left, right, back;
6789   } turn[] =
6790   {
6791     { 0,        0,              0        },
6792     { MV_DOWN,  MV_UP,          MV_RIGHT },
6793     { MV_UP,    MV_DOWN,        MV_LEFT  },
6794     { 0,        0,              0        },
6795     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6796     { 0,        0,              0        },
6797     { 0,        0,              0        },
6798     { 0,        0,              0        },
6799     { MV_RIGHT, MV_LEFT,        MV_UP    }
6800   };
6801
6802   int element = Tile[x][y];
6803   int move_pattern = element_info[element].move_pattern;
6804
6805   int old_move_dir = MovDir[x][y];
6806   int left_dir  = turn[old_move_dir].left;
6807   int right_dir = turn[old_move_dir].right;
6808   int back_dir  = turn[old_move_dir].back;
6809
6810   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6811   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6812   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6813   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6814
6815   int left_x  = x + left_dx,  left_y  = y + left_dy;
6816   int right_x = x + right_dx, right_y = y + right_dy;
6817   int move_x  = x + move_dx,  move_y  = y + move_dy;
6818
6819   int xx, yy;
6820
6821   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6822   {
6823     TestIfBadThingTouchesOtherBadThing(x, y);
6824
6825     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6826       MovDir[x][y] = right_dir;
6827     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6828       MovDir[x][y] = left_dir;
6829
6830     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6831       MovDelay[x][y] = 9;
6832     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6833       MovDelay[x][y] = 1;
6834   }
6835   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6836   {
6837     TestIfBadThingTouchesOtherBadThing(x, y);
6838
6839     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6840       MovDir[x][y] = left_dir;
6841     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6842       MovDir[x][y] = right_dir;
6843
6844     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6845       MovDelay[x][y] = 9;
6846     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6847       MovDelay[x][y] = 1;
6848   }
6849   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6850   {
6851     TestIfBadThingTouchesOtherBadThing(x, y);
6852
6853     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6854       MovDir[x][y] = left_dir;
6855     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6856       MovDir[x][y] = right_dir;
6857
6858     if (MovDir[x][y] != old_move_dir)
6859       MovDelay[x][y] = 9;
6860   }
6861   else if (element == EL_YAMYAM)
6862   {
6863     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6864     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6865
6866     if (can_turn_left && can_turn_right)
6867       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6868     else if (can_turn_left)
6869       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6870     else if (can_turn_right)
6871       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6872     else
6873       MovDir[x][y] = back_dir;
6874
6875     MovDelay[x][y] = 16 + 16 * RND(3);
6876   }
6877   else if (element == EL_DARK_YAMYAM)
6878   {
6879     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6880                                                          left_x, left_y);
6881     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6882                                                          right_x, right_y);
6883
6884     if (can_turn_left && can_turn_right)
6885       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6886     else if (can_turn_left)
6887       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6888     else if (can_turn_right)
6889       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6890     else
6891       MovDir[x][y] = back_dir;
6892
6893     MovDelay[x][y] = 16 + 16 * RND(3);
6894   }
6895   else if (element == EL_PACMAN)
6896   {
6897     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6898     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6899
6900     if (can_turn_left && can_turn_right)
6901       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6902     else if (can_turn_left)
6903       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6904     else if (can_turn_right)
6905       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6906     else
6907       MovDir[x][y] = back_dir;
6908
6909     MovDelay[x][y] = 6 + RND(40);
6910   }
6911   else if (element == EL_PIG)
6912   {
6913     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6914     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6915     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6916     boolean should_turn_left, should_turn_right, should_move_on;
6917     int rnd_value = 24;
6918     int rnd = RND(rnd_value);
6919
6920     should_turn_left = (can_turn_left &&
6921                         (!can_move_on ||
6922                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6923                                                    y + back_dy + left_dy)));
6924     should_turn_right = (can_turn_right &&
6925                          (!can_move_on ||
6926                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6927                                                     y + back_dy + right_dy)));
6928     should_move_on = (can_move_on &&
6929                       (!can_turn_left ||
6930                        !can_turn_right ||
6931                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6932                                                  y + move_dy + left_dy) ||
6933                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6934                                                  y + move_dy + right_dy)));
6935
6936     if (should_turn_left || should_turn_right || should_move_on)
6937     {
6938       if (should_turn_left && should_turn_right && should_move_on)
6939         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6940                         rnd < 2 * rnd_value / 3 ? right_dir :
6941                         old_move_dir);
6942       else if (should_turn_left && should_turn_right)
6943         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6944       else if (should_turn_left && should_move_on)
6945         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6946       else if (should_turn_right && should_move_on)
6947         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6948       else if (should_turn_left)
6949         MovDir[x][y] = left_dir;
6950       else if (should_turn_right)
6951         MovDir[x][y] = right_dir;
6952       else if (should_move_on)
6953         MovDir[x][y] = old_move_dir;
6954     }
6955     else if (can_move_on && rnd > rnd_value / 8)
6956       MovDir[x][y] = old_move_dir;
6957     else if (can_turn_left && can_turn_right)
6958       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6959     else if (can_turn_left && rnd > rnd_value / 8)
6960       MovDir[x][y] = left_dir;
6961     else if (can_turn_right && rnd > rnd_value/8)
6962       MovDir[x][y] = right_dir;
6963     else
6964       MovDir[x][y] = back_dir;
6965
6966     xx = x + move_xy[MovDir[x][y]].dx;
6967     yy = y + move_xy[MovDir[x][y]].dy;
6968
6969     if (!IN_LEV_FIELD(xx, yy) ||
6970         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6971       MovDir[x][y] = old_move_dir;
6972
6973     MovDelay[x][y] = 0;
6974   }
6975   else if (element == EL_DRAGON)
6976   {
6977     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6978     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6979     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6980     int rnd_value = 24;
6981     int rnd = RND(rnd_value);
6982
6983     if (can_move_on && rnd > rnd_value / 8)
6984       MovDir[x][y] = old_move_dir;
6985     else if (can_turn_left && can_turn_right)
6986       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6987     else if (can_turn_left && rnd > rnd_value / 8)
6988       MovDir[x][y] = left_dir;
6989     else if (can_turn_right && rnd > rnd_value / 8)
6990       MovDir[x][y] = right_dir;
6991     else
6992       MovDir[x][y] = back_dir;
6993
6994     xx = x + move_xy[MovDir[x][y]].dx;
6995     yy = y + move_xy[MovDir[x][y]].dy;
6996
6997     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6998       MovDir[x][y] = old_move_dir;
6999
7000     MovDelay[x][y] = 0;
7001   }
7002   else if (element == EL_MOLE)
7003   {
7004     boolean can_move_on =
7005       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7006                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7007                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7008     if (!can_move_on)
7009     {
7010       boolean can_turn_left =
7011         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7012                               IS_AMOEBOID(Tile[left_x][left_y])));
7013
7014       boolean can_turn_right =
7015         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7016                               IS_AMOEBOID(Tile[right_x][right_y])));
7017
7018       if (can_turn_left && can_turn_right)
7019         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7020       else if (can_turn_left)
7021         MovDir[x][y] = left_dir;
7022       else
7023         MovDir[x][y] = right_dir;
7024     }
7025
7026     if (MovDir[x][y] != old_move_dir)
7027       MovDelay[x][y] = 9;
7028   }
7029   else if (element == EL_BALLOON)
7030   {
7031     MovDir[x][y] = game.wind_direction;
7032     MovDelay[x][y] = 0;
7033   }
7034   else if (element == EL_SPRING)
7035   {
7036     if (MovDir[x][y] & MV_HORIZONTAL)
7037     {
7038       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7039           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7040       {
7041         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7042         ResetGfxAnimation(move_x, move_y);
7043         TEST_DrawLevelField(move_x, move_y);
7044
7045         MovDir[x][y] = back_dir;
7046       }
7047       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7048                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7049         MovDir[x][y] = MV_NONE;
7050     }
7051
7052     MovDelay[x][y] = 0;
7053   }
7054   else if (element == EL_ROBOT ||
7055            element == EL_SATELLITE ||
7056            element == EL_PENGUIN ||
7057            element == EL_EMC_ANDROID)
7058   {
7059     int attr_x = -1, attr_y = -1;
7060
7061     if (game.all_players_gone)
7062     {
7063       attr_x = game.exit_x;
7064       attr_y = game.exit_y;
7065     }
7066     else
7067     {
7068       int i;
7069
7070       for (i = 0; i < MAX_PLAYERS; i++)
7071       {
7072         struct PlayerInfo *player = &stored_player[i];
7073         int jx = player->jx, jy = player->jy;
7074
7075         if (!player->active)
7076           continue;
7077
7078         if (attr_x == -1 ||
7079             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7080         {
7081           attr_x = jx;
7082           attr_y = jy;
7083         }
7084       }
7085     }
7086
7087     if (element == EL_ROBOT &&
7088         game.robot_wheel_x >= 0 &&
7089         game.robot_wheel_y >= 0 &&
7090         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7091          game.engine_version < VERSION_IDENT(3,1,0,0)))
7092     {
7093       attr_x = game.robot_wheel_x;
7094       attr_y = game.robot_wheel_y;
7095     }
7096
7097     if (element == EL_PENGUIN)
7098     {
7099       int i;
7100       static int xy[4][2] =
7101       {
7102         { 0, -1 },
7103         { -1, 0 },
7104         { +1, 0 },
7105         { 0, +1 }
7106       };
7107
7108       for (i = 0; i < NUM_DIRECTIONS; i++)
7109       {
7110         int ex = x + xy[i][0];
7111         int ey = y + xy[i][1];
7112
7113         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7114                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7115                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7116                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7117         {
7118           attr_x = ex;
7119           attr_y = ey;
7120           break;
7121         }
7122       }
7123     }
7124
7125     MovDir[x][y] = MV_NONE;
7126     if (attr_x < x)
7127       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7128     else if (attr_x > x)
7129       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7130     if (attr_y < y)
7131       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7132     else if (attr_y > y)
7133       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7134
7135     if (element == EL_ROBOT)
7136     {
7137       int newx, newy;
7138
7139       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7140         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7141       Moving2Blocked(x, y, &newx, &newy);
7142
7143       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7144         MovDelay[x][y] = 8 + 8 * !RND(3);
7145       else
7146         MovDelay[x][y] = 16;
7147     }
7148     else if (element == EL_PENGUIN)
7149     {
7150       int newx, newy;
7151
7152       MovDelay[x][y] = 1;
7153
7154       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7155       {
7156         boolean first_horiz = RND(2);
7157         int new_move_dir = MovDir[x][y];
7158
7159         MovDir[x][y] =
7160           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7161         Moving2Blocked(x, y, &newx, &newy);
7162
7163         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7164           return;
7165
7166         MovDir[x][y] =
7167           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7168         Moving2Blocked(x, y, &newx, &newy);
7169
7170         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7171           return;
7172
7173         MovDir[x][y] = old_move_dir;
7174         return;
7175       }
7176     }
7177     else if (element == EL_SATELLITE)
7178     {
7179       int newx, newy;
7180
7181       MovDelay[x][y] = 1;
7182
7183       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7184       {
7185         boolean first_horiz = RND(2);
7186         int new_move_dir = MovDir[x][y];
7187
7188         MovDir[x][y] =
7189           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7190         Moving2Blocked(x, y, &newx, &newy);
7191
7192         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7193           return;
7194
7195         MovDir[x][y] =
7196           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7197         Moving2Blocked(x, y, &newx, &newy);
7198
7199         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7200           return;
7201
7202         MovDir[x][y] = old_move_dir;
7203         return;
7204       }
7205     }
7206     else if (element == EL_EMC_ANDROID)
7207     {
7208       static int check_pos[16] =
7209       {
7210         -1,             //  0 => (invalid)
7211         7,              //  1 => MV_LEFT
7212         3,              //  2 => MV_RIGHT
7213         -1,             //  3 => (invalid)
7214         1,              //  4 =>            MV_UP
7215         0,              //  5 => MV_LEFT  | MV_UP
7216         2,              //  6 => MV_RIGHT | MV_UP
7217         -1,             //  7 => (invalid)
7218         5,              //  8 =>            MV_DOWN
7219         6,              //  9 => MV_LEFT  | MV_DOWN
7220         4,              // 10 => MV_RIGHT | MV_DOWN
7221         -1,             // 11 => (invalid)
7222         -1,             // 12 => (invalid)
7223         -1,             // 13 => (invalid)
7224         -1,             // 14 => (invalid)
7225         -1,             // 15 => (invalid)
7226       };
7227       static struct
7228       {
7229         int dx, dy;
7230         int dir;
7231       } check_xy[8] =
7232       {
7233         { -1, -1,       MV_LEFT  | MV_UP   },
7234         {  0, -1,                  MV_UP   },
7235         { +1, -1,       MV_RIGHT | MV_UP   },
7236         { +1,  0,       MV_RIGHT           },
7237         { +1, +1,       MV_RIGHT | MV_DOWN },
7238         {  0, +1,                  MV_DOWN },
7239         { -1, +1,       MV_LEFT  | MV_DOWN },
7240         { -1,  0,       MV_LEFT            },
7241       };
7242       int start_pos, check_order;
7243       boolean can_clone = FALSE;
7244       int i;
7245
7246       // check if there is any free field around current position
7247       for (i = 0; i < 8; i++)
7248       {
7249         int newx = x + check_xy[i].dx;
7250         int newy = y + check_xy[i].dy;
7251
7252         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7253         {
7254           can_clone = TRUE;
7255
7256           break;
7257         }
7258       }
7259
7260       if (can_clone)            // randomly find an element to clone
7261       {
7262         can_clone = FALSE;
7263
7264         start_pos = check_pos[RND(8)];
7265         check_order = (RND(2) ? -1 : +1);
7266
7267         for (i = 0; i < 8; i++)
7268         {
7269           int pos_raw = start_pos + i * check_order;
7270           int pos = (pos_raw + 8) % 8;
7271           int newx = x + check_xy[pos].dx;
7272           int newy = y + check_xy[pos].dy;
7273
7274           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7275           {
7276             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7277             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7278
7279             Store[x][y] = Tile[newx][newy];
7280
7281             can_clone = TRUE;
7282
7283             break;
7284           }
7285         }
7286       }
7287
7288       if (can_clone)            // randomly find a direction to move
7289       {
7290         can_clone = FALSE;
7291
7292         start_pos = check_pos[RND(8)];
7293         check_order = (RND(2) ? -1 : +1);
7294
7295         for (i = 0; i < 8; i++)
7296         {
7297           int pos_raw = start_pos + i * check_order;
7298           int pos = (pos_raw + 8) % 8;
7299           int newx = x + check_xy[pos].dx;
7300           int newy = y + check_xy[pos].dy;
7301           int new_move_dir = check_xy[pos].dir;
7302
7303           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7304           {
7305             MovDir[x][y] = new_move_dir;
7306             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7307
7308             can_clone = TRUE;
7309
7310             break;
7311           }
7312         }
7313       }
7314
7315       if (can_clone)            // cloning and moving successful
7316         return;
7317
7318       // cannot clone -- try to move towards player
7319
7320       start_pos = check_pos[MovDir[x][y] & 0x0f];
7321       check_order = (RND(2) ? -1 : +1);
7322
7323       for (i = 0; i < 3; i++)
7324       {
7325         // first check start_pos, then previous/next or (next/previous) pos
7326         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7327         int pos = (pos_raw + 8) % 8;
7328         int newx = x + check_xy[pos].dx;
7329         int newy = y + check_xy[pos].dy;
7330         int new_move_dir = check_xy[pos].dir;
7331
7332         if (IS_PLAYER(newx, newy))
7333           break;
7334
7335         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7336         {
7337           MovDir[x][y] = new_move_dir;
7338           MovDelay[x][y] = level.android_move_time * 8 + 1;
7339
7340           break;
7341         }
7342       }
7343     }
7344   }
7345   else if (move_pattern == MV_TURNING_LEFT ||
7346            move_pattern == MV_TURNING_RIGHT ||
7347            move_pattern == MV_TURNING_LEFT_RIGHT ||
7348            move_pattern == MV_TURNING_RIGHT_LEFT ||
7349            move_pattern == MV_TURNING_RANDOM ||
7350            move_pattern == MV_ALL_DIRECTIONS)
7351   {
7352     boolean can_turn_left =
7353       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7354     boolean can_turn_right =
7355       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7356
7357     if (element_info[element].move_stepsize == 0)       // "not moving"
7358       return;
7359
7360     if (move_pattern == MV_TURNING_LEFT)
7361       MovDir[x][y] = left_dir;
7362     else if (move_pattern == MV_TURNING_RIGHT)
7363       MovDir[x][y] = right_dir;
7364     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7365       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7366     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7367       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7368     else if (move_pattern == MV_TURNING_RANDOM)
7369       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7370                       can_turn_right && !can_turn_left ? right_dir :
7371                       RND(2) ? left_dir : right_dir);
7372     else if (can_turn_left && can_turn_right)
7373       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7374     else if (can_turn_left)
7375       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7376     else if (can_turn_right)
7377       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7378     else
7379       MovDir[x][y] = back_dir;
7380
7381     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7382   }
7383   else if (move_pattern == MV_HORIZONTAL ||
7384            move_pattern == MV_VERTICAL)
7385   {
7386     if (move_pattern & old_move_dir)
7387       MovDir[x][y] = back_dir;
7388     else if (move_pattern == MV_HORIZONTAL)
7389       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7390     else if (move_pattern == MV_VERTICAL)
7391       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7392
7393     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7394   }
7395   else if (move_pattern & MV_ANY_DIRECTION)
7396   {
7397     MovDir[x][y] = move_pattern;
7398     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7399   }
7400   else if (move_pattern & MV_WIND_DIRECTION)
7401   {
7402     MovDir[x][y] = game.wind_direction;
7403     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7404   }
7405   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7406   {
7407     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7408       MovDir[x][y] = left_dir;
7409     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7410       MovDir[x][y] = right_dir;
7411
7412     if (MovDir[x][y] != old_move_dir)
7413       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7414   }
7415   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7416   {
7417     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7418       MovDir[x][y] = right_dir;
7419     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7420       MovDir[x][y] = left_dir;
7421
7422     if (MovDir[x][y] != old_move_dir)
7423       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7424   }
7425   else if (move_pattern == MV_TOWARDS_PLAYER ||
7426            move_pattern == MV_AWAY_FROM_PLAYER)
7427   {
7428     int attr_x = -1, attr_y = -1;
7429     int newx, newy;
7430     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7431
7432     if (game.all_players_gone)
7433     {
7434       attr_x = game.exit_x;
7435       attr_y = game.exit_y;
7436     }
7437     else
7438     {
7439       int i;
7440
7441       for (i = 0; i < MAX_PLAYERS; i++)
7442       {
7443         struct PlayerInfo *player = &stored_player[i];
7444         int jx = player->jx, jy = player->jy;
7445
7446         if (!player->active)
7447           continue;
7448
7449         if (attr_x == -1 ||
7450             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7451         {
7452           attr_x = jx;
7453           attr_y = jy;
7454         }
7455       }
7456     }
7457
7458     MovDir[x][y] = MV_NONE;
7459     if (attr_x < x)
7460       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7461     else if (attr_x > x)
7462       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7463     if (attr_y < y)
7464       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7465     else if (attr_y > y)
7466       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7467
7468     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7469
7470     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7471     {
7472       boolean first_horiz = RND(2);
7473       int new_move_dir = MovDir[x][y];
7474
7475       if (element_info[element].move_stepsize == 0)     // "not moving"
7476       {
7477         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7478         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7479
7480         return;
7481       }
7482
7483       MovDir[x][y] =
7484         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7485       Moving2Blocked(x, y, &newx, &newy);
7486
7487       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7488         return;
7489
7490       MovDir[x][y] =
7491         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7492       Moving2Blocked(x, y, &newx, &newy);
7493
7494       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7495         return;
7496
7497       MovDir[x][y] = old_move_dir;
7498     }
7499   }
7500   else if (move_pattern == MV_WHEN_PUSHED ||
7501            move_pattern == MV_WHEN_DROPPED)
7502   {
7503     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7504       MovDir[x][y] = MV_NONE;
7505
7506     MovDelay[x][y] = 0;
7507   }
7508   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7509   {
7510     static int test_xy[7][2] =
7511     {
7512       { 0, -1 },
7513       { -1, 0 },
7514       { +1, 0 },
7515       { 0, +1 },
7516       { 0, -1 },
7517       { -1, 0 },
7518       { +1, 0 },
7519     };
7520     static int test_dir[7] =
7521     {
7522       MV_UP,
7523       MV_LEFT,
7524       MV_RIGHT,
7525       MV_DOWN,
7526       MV_UP,
7527       MV_LEFT,
7528       MV_RIGHT,
7529     };
7530     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7531     int move_preference = -1000000;     // start with very low preference
7532     int new_move_dir = MV_NONE;
7533     int start_test = RND(4);
7534     int i;
7535
7536     for (i = 0; i < NUM_DIRECTIONS; i++)
7537     {
7538       int move_dir = test_dir[start_test + i];
7539       int move_dir_preference;
7540
7541       xx = x + test_xy[start_test + i][0];
7542       yy = y + test_xy[start_test + i][1];
7543
7544       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7545           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7546       {
7547         new_move_dir = move_dir;
7548
7549         break;
7550       }
7551
7552       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7553         continue;
7554
7555       move_dir_preference = -1 * RunnerVisit[xx][yy];
7556       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7557         move_dir_preference = PlayerVisit[xx][yy];
7558
7559       if (move_dir_preference > move_preference)
7560       {
7561         // prefer field that has not been visited for the longest time
7562         move_preference = move_dir_preference;
7563         new_move_dir = move_dir;
7564       }
7565       else if (move_dir_preference == move_preference &&
7566                move_dir == old_move_dir)
7567       {
7568         // prefer last direction when all directions are preferred equally
7569         move_preference = move_dir_preference;
7570         new_move_dir = move_dir;
7571       }
7572     }
7573
7574     MovDir[x][y] = new_move_dir;
7575     if (old_move_dir != new_move_dir)
7576       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7577   }
7578 }
7579
7580 static void TurnRound(int x, int y)
7581 {
7582   int direction = MovDir[x][y];
7583
7584   TurnRoundExt(x, y);
7585
7586   GfxDir[x][y] = MovDir[x][y];
7587
7588   if (direction != MovDir[x][y])
7589     GfxFrame[x][y] = 0;
7590
7591   if (MovDelay[x][y])
7592     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7593
7594   ResetGfxFrame(x, y);
7595 }
7596
7597 static boolean JustBeingPushed(int x, int y)
7598 {
7599   int i;
7600
7601   for (i = 0; i < MAX_PLAYERS; i++)
7602   {
7603     struct PlayerInfo *player = &stored_player[i];
7604
7605     if (player->active && player->is_pushing && player->MovPos)
7606     {
7607       int next_jx = player->jx + (player->jx - player->last_jx);
7608       int next_jy = player->jy + (player->jy - player->last_jy);
7609
7610       if (x == next_jx && y == next_jy)
7611         return TRUE;
7612     }
7613   }
7614
7615   return FALSE;
7616 }
7617
7618 static void StartMoving(int x, int y)
7619 {
7620   boolean started_moving = FALSE;       // some elements can fall _and_ move
7621   int element = Tile[x][y];
7622
7623   if (Stop[x][y])
7624     return;
7625
7626   if (MovDelay[x][y] == 0)
7627     GfxAction[x][y] = ACTION_DEFAULT;
7628
7629   if (CAN_FALL(element) && y < lev_fieldy - 1)
7630   {
7631     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7632         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7633       if (JustBeingPushed(x, y))
7634         return;
7635
7636     if (element == EL_QUICKSAND_FULL)
7637     {
7638       if (IS_FREE(x, y + 1))
7639       {
7640         InitMovingField(x, y, MV_DOWN);
7641         started_moving = TRUE;
7642
7643         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7644 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7645         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7646           Store[x][y] = EL_ROCK;
7647 #else
7648         Store[x][y] = EL_ROCK;
7649 #endif
7650
7651         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7652       }
7653       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7654       {
7655         if (!MovDelay[x][y])
7656         {
7657           MovDelay[x][y] = TILEY + 1;
7658
7659           ResetGfxAnimation(x, y);
7660           ResetGfxAnimation(x, y + 1);
7661         }
7662
7663         if (MovDelay[x][y])
7664         {
7665           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7666           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7667
7668           MovDelay[x][y]--;
7669           if (MovDelay[x][y])
7670             return;
7671         }
7672
7673         Tile[x][y] = EL_QUICKSAND_EMPTY;
7674         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7675         Store[x][y + 1] = Store[x][y];
7676         Store[x][y] = 0;
7677
7678         PlayLevelSoundAction(x, y, ACTION_FILLING);
7679       }
7680       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7681       {
7682         if (!MovDelay[x][y])
7683         {
7684           MovDelay[x][y] = TILEY + 1;
7685
7686           ResetGfxAnimation(x, y);
7687           ResetGfxAnimation(x, y + 1);
7688         }
7689
7690         if (MovDelay[x][y])
7691         {
7692           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7693           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7694
7695           MovDelay[x][y]--;
7696           if (MovDelay[x][y])
7697             return;
7698         }
7699
7700         Tile[x][y] = EL_QUICKSAND_EMPTY;
7701         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7702         Store[x][y + 1] = Store[x][y];
7703         Store[x][y] = 0;
7704
7705         PlayLevelSoundAction(x, y, ACTION_FILLING);
7706       }
7707     }
7708     else if (element == EL_QUICKSAND_FAST_FULL)
7709     {
7710       if (IS_FREE(x, y + 1))
7711       {
7712         InitMovingField(x, y, MV_DOWN);
7713         started_moving = TRUE;
7714
7715         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7716 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7717         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7718           Store[x][y] = EL_ROCK;
7719 #else
7720         Store[x][y] = EL_ROCK;
7721 #endif
7722
7723         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7724       }
7725       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7726       {
7727         if (!MovDelay[x][y])
7728         {
7729           MovDelay[x][y] = TILEY + 1;
7730
7731           ResetGfxAnimation(x, y);
7732           ResetGfxAnimation(x, y + 1);
7733         }
7734
7735         if (MovDelay[x][y])
7736         {
7737           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7738           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7739
7740           MovDelay[x][y]--;
7741           if (MovDelay[x][y])
7742             return;
7743         }
7744
7745         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7746         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7747         Store[x][y + 1] = Store[x][y];
7748         Store[x][y] = 0;
7749
7750         PlayLevelSoundAction(x, y, ACTION_FILLING);
7751       }
7752       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7753       {
7754         if (!MovDelay[x][y])
7755         {
7756           MovDelay[x][y] = TILEY + 1;
7757
7758           ResetGfxAnimation(x, y);
7759           ResetGfxAnimation(x, y + 1);
7760         }
7761
7762         if (MovDelay[x][y])
7763         {
7764           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7765           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7766
7767           MovDelay[x][y]--;
7768           if (MovDelay[x][y])
7769             return;
7770         }
7771
7772         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7773         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7774         Store[x][y + 1] = Store[x][y];
7775         Store[x][y] = 0;
7776
7777         PlayLevelSoundAction(x, y, ACTION_FILLING);
7778       }
7779     }
7780     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7781              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7782     {
7783       InitMovingField(x, y, MV_DOWN);
7784       started_moving = TRUE;
7785
7786       Tile[x][y] = EL_QUICKSAND_FILLING;
7787       Store[x][y] = element;
7788
7789       PlayLevelSoundAction(x, y, ACTION_FILLING);
7790     }
7791     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7792              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7793     {
7794       InitMovingField(x, y, MV_DOWN);
7795       started_moving = TRUE;
7796
7797       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7798       Store[x][y] = element;
7799
7800       PlayLevelSoundAction(x, y, ACTION_FILLING);
7801     }
7802     else if (element == EL_MAGIC_WALL_FULL)
7803     {
7804       if (IS_FREE(x, y + 1))
7805       {
7806         InitMovingField(x, y, MV_DOWN);
7807         started_moving = TRUE;
7808
7809         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7810         Store[x][y] = EL_CHANGED(Store[x][y]);
7811       }
7812       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7813       {
7814         if (!MovDelay[x][y])
7815           MovDelay[x][y] = TILEY / 4 + 1;
7816
7817         if (MovDelay[x][y])
7818         {
7819           MovDelay[x][y]--;
7820           if (MovDelay[x][y])
7821             return;
7822         }
7823
7824         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7825         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7826         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7827         Store[x][y] = 0;
7828       }
7829     }
7830     else if (element == EL_BD_MAGIC_WALL_FULL)
7831     {
7832       if (IS_FREE(x, y + 1))
7833       {
7834         InitMovingField(x, y, MV_DOWN);
7835         started_moving = TRUE;
7836
7837         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7838         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7839       }
7840       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7841       {
7842         if (!MovDelay[x][y])
7843           MovDelay[x][y] = TILEY / 4 + 1;
7844
7845         if (MovDelay[x][y])
7846         {
7847           MovDelay[x][y]--;
7848           if (MovDelay[x][y])
7849             return;
7850         }
7851
7852         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7853         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7854         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7855         Store[x][y] = 0;
7856       }
7857     }
7858     else if (element == EL_DC_MAGIC_WALL_FULL)
7859     {
7860       if (IS_FREE(x, y + 1))
7861       {
7862         InitMovingField(x, y, MV_DOWN);
7863         started_moving = TRUE;
7864
7865         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7866         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7867       }
7868       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7869       {
7870         if (!MovDelay[x][y])
7871           MovDelay[x][y] = TILEY / 4 + 1;
7872
7873         if (MovDelay[x][y])
7874         {
7875           MovDelay[x][y]--;
7876           if (MovDelay[x][y])
7877             return;
7878         }
7879
7880         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7881         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7882         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7883         Store[x][y] = 0;
7884       }
7885     }
7886     else if ((CAN_PASS_MAGIC_WALL(element) &&
7887               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7888                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7889              (CAN_PASS_DC_MAGIC_WALL(element) &&
7890               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7891
7892     {
7893       InitMovingField(x, y, MV_DOWN);
7894       started_moving = TRUE;
7895
7896       Tile[x][y] =
7897         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7898          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7899          EL_DC_MAGIC_WALL_FILLING);
7900       Store[x][y] = element;
7901     }
7902     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7903     {
7904       SplashAcid(x, y + 1);
7905
7906       InitMovingField(x, y, MV_DOWN);
7907       started_moving = TRUE;
7908
7909       Store[x][y] = EL_ACID;
7910     }
7911     else if (
7912              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7913               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7914              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7915               CAN_FALL(element) && WasJustFalling[x][y] &&
7916               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7917
7918              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7919               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7920               (Tile[x][y + 1] == EL_BLOCKED)))
7921     {
7922       /* this is needed for a special case not covered by calling "Impact()"
7923          from "ContinueMoving()": if an element moves to a tile directly below
7924          another element which was just falling on that tile (which was empty
7925          in the previous frame), the falling element above would just stop
7926          instead of smashing the element below (in previous version, the above
7927          element was just checked for "moving" instead of "falling", resulting
7928          in incorrect smashes caused by horizontal movement of the above
7929          element; also, the case of the player being the element to smash was
7930          simply not covered here... :-/ ) */
7931
7932       CheckCollision[x][y] = 0;
7933       CheckImpact[x][y] = 0;
7934
7935       Impact(x, y);
7936     }
7937     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7938     {
7939       if (MovDir[x][y] == MV_NONE)
7940       {
7941         InitMovingField(x, y, MV_DOWN);
7942         started_moving = TRUE;
7943       }
7944     }
7945     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7946     {
7947       if (WasJustFalling[x][y]) // prevent animation from being restarted
7948         MovDir[x][y] = MV_DOWN;
7949
7950       InitMovingField(x, y, MV_DOWN);
7951       started_moving = TRUE;
7952     }
7953     else if (element == EL_AMOEBA_DROP)
7954     {
7955       Tile[x][y] = EL_AMOEBA_GROWING;
7956       Store[x][y] = EL_AMOEBA_WET;
7957     }
7958     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7959               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7960              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7961              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7962     {
7963       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7964                                 (IS_FREE(x - 1, y + 1) ||
7965                                  Tile[x - 1][y + 1] == EL_ACID));
7966       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7967                                 (IS_FREE(x + 1, y + 1) ||
7968                                  Tile[x + 1][y + 1] == EL_ACID));
7969       boolean can_fall_any  = (can_fall_left || can_fall_right);
7970       boolean can_fall_both = (can_fall_left && can_fall_right);
7971       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7972
7973       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7974       {
7975         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7976           can_fall_right = FALSE;
7977         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7978           can_fall_left = FALSE;
7979         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7980           can_fall_right = FALSE;
7981         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7982           can_fall_left = FALSE;
7983
7984         can_fall_any  = (can_fall_left || can_fall_right);
7985         can_fall_both = FALSE;
7986       }
7987
7988       if (can_fall_both)
7989       {
7990         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7991           can_fall_right = FALSE;       // slip down on left side
7992         else
7993           can_fall_left = !(can_fall_right = RND(2));
7994
7995         can_fall_both = FALSE;
7996       }
7997
7998       if (can_fall_any)
7999       {
8000         // if not determined otherwise, prefer left side for slipping down
8001         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8002         started_moving = TRUE;
8003       }
8004     }
8005     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8006     {
8007       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8008       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8009       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8010       int belt_dir = game.belt_dir[belt_nr];
8011
8012       if ((belt_dir == MV_LEFT  && left_is_free) ||
8013           (belt_dir == MV_RIGHT && right_is_free))
8014       {
8015         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8016
8017         InitMovingField(x, y, belt_dir);
8018         started_moving = TRUE;
8019
8020         Pushed[x][y] = TRUE;
8021         Pushed[nextx][y] = TRUE;
8022
8023         GfxAction[x][y] = ACTION_DEFAULT;
8024       }
8025       else
8026       {
8027         MovDir[x][y] = 0;       // if element was moving, stop it
8028       }
8029     }
8030   }
8031
8032   // not "else if" because of elements that can fall and move (EL_SPRING)
8033   if (CAN_MOVE(element) && !started_moving)
8034   {
8035     int move_pattern = element_info[element].move_pattern;
8036     int newx, newy;
8037
8038     Moving2Blocked(x, y, &newx, &newy);
8039
8040     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8041       return;
8042
8043     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8044         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8045     {
8046       WasJustMoving[x][y] = 0;
8047       CheckCollision[x][y] = 0;
8048
8049       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8050
8051       if (Tile[x][y] != element)        // element has changed
8052         return;
8053     }
8054
8055     if (!MovDelay[x][y])        // start new movement phase
8056     {
8057       // all objects that can change their move direction after each step
8058       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8059
8060       if (element != EL_YAMYAM &&
8061           element != EL_DARK_YAMYAM &&
8062           element != EL_PACMAN &&
8063           !(move_pattern & MV_ANY_DIRECTION) &&
8064           move_pattern != MV_TURNING_LEFT &&
8065           move_pattern != MV_TURNING_RIGHT &&
8066           move_pattern != MV_TURNING_LEFT_RIGHT &&
8067           move_pattern != MV_TURNING_RIGHT_LEFT &&
8068           move_pattern != MV_TURNING_RANDOM)
8069       {
8070         TurnRound(x, y);
8071
8072         if (MovDelay[x][y] && (element == EL_BUG ||
8073                                element == EL_SPACESHIP ||
8074                                element == EL_SP_SNIKSNAK ||
8075                                element == EL_SP_ELECTRON ||
8076                                element == EL_MOLE))
8077           TEST_DrawLevelField(x, y);
8078       }
8079     }
8080
8081     if (MovDelay[x][y])         // wait some time before next movement
8082     {
8083       MovDelay[x][y]--;
8084
8085       if (element == EL_ROBOT ||
8086           element == EL_YAMYAM ||
8087           element == EL_DARK_YAMYAM)
8088       {
8089         DrawLevelElementAnimationIfNeeded(x, y, element);
8090         PlayLevelSoundAction(x, y, ACTION_WAITING);
8091       }
8092       else if (element == EL_SP_ELECTRON)
8093         DrawLevelElementAnimationIfNeeded(x, y, element);
8094       else if (element == EL_DRAGON)
8095       {
8096         int i;
8097         int dir = MovDir[x][y];
8098         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8099         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8100         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8101                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8102                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8103                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8104         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8105
8106         GfxAction[x][y] = ACTION_ATTACKING;
8107
8108         if (IS_PLAYER(x, y))
8109           DrawPlayerField(x, y);
8110         else
8111           TEST_DrawLevelField(x, y);
8112
8113         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8114
8115         for (i = 1; i <= 3; i++)
8116         {
8117           int xx = x + i * dx;
8118           int yy = y + i * dy;
8119           int sx = SCREENX(xx);
8120           int sy = SCREENY(yy);
8121           int flame_graphic = graphic + (i - 1);
8122
8123           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8124             break;
8125
8126           if (MovDelay[x][y])
8127           {
8128             int flamed = MovingOrBlocked2Element(xx, yy);
8129
8130             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8131               Bang(xx, yy);
8132             else
8133               RemoveMovingField(xx, yy);
8134
8135             ChangeDelay[xx][yy] = 0;
8136
8137             Tile[xx][yy] = EL_FLAMES;
8138
8139             if (IN_SCR_FIELD(sx, sy))
8140             {
8141               TEST_DrawLevelFieldCrumbled(xx, yy);
8142               DrawGraphic(sx, sy, flame_graphic, frame);
8143             }
8144           }
8145           else
8146           {
8147             if (Tile[xx][yy] == EL_FLAMES)
8148               Tile[xx][yy] = EL_EMPTY;
8149             TEST_DrawLevelField(xx, yy);
8150           }
8151         }
8152       }
8153
8154       if (MovDelay[x][y])       // element still has to wait some time
8155       {
8156         PlayLevelSoundAction(x, y, ACTION_WAITING);
8157
8158         return;
8159       }
8160     }
8161
8162     // now make next step
8163
8164     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8165
8166     if (DONT_COLLIDE_WITH(element) &&
8167         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8168         !PLAYER_ENEMY_PROTECTED(newx, newy))
8169     {
8170       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8171
8172       return;
8173     }
8174
8175     else if (CAN_MOVE_INTO_ACID(element) &&
8176              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8177              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8178              (MovDir[x][y] == MV_DOWN ||
8179               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8180     {
8181       SplashAcid(newx, newy);
8182       Store[x][y] = EL_ACID;
8183     }
8184     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8185     {
8186       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8187           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8188           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8189           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8190       {
8191         RemoveField(x, y);
8192         TEST_DrawLevelField(x, y);
8193
8194         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8195         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8196           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8197
8198         game.friends_still_needed--;
8199         if (!game.friends_still_needed &&
8200             !game.GameOver &&
8201             game.all_players_gone)
8202           LevelSolved();
8203
8204         return;
8205       }
8206       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8207       {
8208         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8209           TEST_DrawLevelField(newx, newy);
8210         else
8211           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8212       }
8213       else if (!IS_FREE(newx, newy))
8214       {
8215         GfxAction[x][y] = ACTION_WAITING;
8216
8217         if (IS_PLAYER(x, y))
8218           DrawPlayerField(x, y);
8219         else
8220           TEST_DrawLevelField(x, y);
8221
8222         return;
8223       }
8224     }
8225     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8226     {
8227       if (IS_FOOD_PIG(Tile[newx][newy]))
8228       {
8229         if (IS_MOVING(newx, newy))
8230           RemoveMovingField(newx, newy);
8231         else
8232         {
8233           Tile[newx][newy] = EL_EMPTY;
8234           TEST_DrawLevelField(newx, newy);
8235         }
8236
8237         PlayLevelSound(x, y, SND_PIG_DIGGING);
8238       }
8239       else if (!IS_FREE(newx, newy))
8240       {
8241         if (IS_PLAYER(x, y))
8242           DrawPlayerField(x, y);
8243         else
8244           TEST_DrawLevelField(x, y);
8245
8246         return;
8247       }
8248     }
8249     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8250     {
8251       if (Store[x][y] != EL_EMPTY)
8252       {
8253         boolean can_clone = FALSE;
8254         int xx, yy;
8255
8256         // check if element to clone is still there
8257         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8258         {
8259           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8260           {
8261             can_clone = TRUE;
8262
8263             break;
8264           }
8265         }
8266
8267         // cannot clone or target field not free anymore -- do not clone
8268         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8269           Store[x][y] = EL_EMPTY;
8270       }
8271
8272       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8273       {
8274         if (IS_MV_DIAGONAL(MovDir[x][y]))
8275         {
8276           int diagonal_move_dir = MovDir[x][y];
8277           int stored = Store[x][y];
8278           int change_delay = 8;
8279           int graphic;
8280
8281           // android is moving diagonally
8282
8283           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8284
8285           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8286           GfxElement[x][y] = EL_EMC_ANDROID;
8287           GfxAction[x][y] = ACTION_SHRINKING;
8288           GfxDir[x][y] = diagonal_move_dir;
8289           ChangeDelay[x][y] = change_delay;
8290
8291           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8292                                    GfxDir[x][y]);
8293
8294           DrawLevelGraphicAnimation(x, y, graphic);
8295           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8296
8297           if (Tile[newx][newy] == EL_ACID)
8298           {
8299             SplashAcid(newx, newy);
8300
8301             return;
8302           }
8303
8304           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8305
8306           Store[newx][newy] = EL_EMC_ANDROID;
8307           GfxElement[newx][newy] = EL_EMC_ANDROID;
8308           GfxAction[newx][newy] = ACTION_GROWING;
8309           GfxDir[newx][newy] = diagonal_move_dir;
8310           ChangeDelay[newx][newy] = change_delay;
8311
8312           graphic = el_act_dir2img(GfxElement[newx][newy],
8313                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8314
8315           DrawLevelGraphicAnimation(newx, newy, graphic);
8316           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8317
8318           return;
8319         }
8320         else
8321         {
8322           Tile[newx][newy] = EL_EMPTY;
8323           TEST_DrawLevelField(newx, newy);
8324
8325           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8326         }
8327       }
8328       else if (!IS_FREE(newx, newy))
8329       {
8330         return;
8331       }
8332     }
8333     else if (IS_CUSTOM_ELEMENT(element) &&
8334              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8335     {
8336       if (!DigFieldByCE(newx, newy, element))
8337         return;
8338
8339       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8340       {
8341         RunnerVisit[x][y] = FrameCounter;
8342         PlayerVisit[x][y] /= 8;         // expire player visit path
8343       }
8344     }
8345     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8346     {
8347       if (!IS_FREE(newx, newy))
8348       {
8349         if (IS_PLAYER(x, y))
8350           DrawPlayerField(x, y);
8351         else
8352           TEST_DrawLevelField(x, y);
8353
8354         return;
8355       }
8356       else
8357       {
8358         boolean wanna_flame = !RND(10);
8359         int dx = newx - x, dy = newy - y;
8360         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8361         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8362         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8363                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8364         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8365                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8366
8367         if ((wanna_flame ||
8368              IS_CLASSIC_ENEMY(element1) ||
8369              IS_CLASSIC_ENEMY(element2)) &&
8370             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8371             element1 != EL_FLAMES && element2 != EL_FLAMES)
8372         {
8373           ResetGfxAnimation(x, y);
8374           GfxAction[x][y] = ACTION_ATTACKING;
8375
8376           if (IS_PLAYER(x, y))
8377             DrawPlayerField(x, y);
8378           else
8379             TEST_DrawLevelField(x, y);
8380
8381           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8382
8383           MovDelay[x][y] = 50;
8384
8385           Tile[newx][newy] = EL_FLAMES;
8386           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8387             Tile[newx1][newy1] = EL_FLAMES;
8388           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8389             Tile[newx2][newy2] = EL_FLAMES;
8390
8391           return;
8392         }
8393       }
8394     }
8395     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8396              Tile[newx][newy] == EL_DIAMOND)
8397     {
8398       if (IS_MOVING(newx, newy))
8399         RemoveMovingField(newx, newy);
8400       else
8401       {
8402         Tile[newx][newy] = EL_EMPTY;
8403         TEST_DrawLevelField(newx, newy);
8404       }
8405
8406       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8407     }
8408     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8409              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8410     {
8411       if (AmoebaNr[newx][newy])
8412       {
8413         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8414         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8415             Tile[newx][newy] == EL_BD_AMOEBA)
8416           AmoebaCnt[AmoebaNr[newx][newy]]--;
8417       }
8418
8419       if (IS_MOVING(newx, newy))
8420       {
8421         RemoveMovingField(newx, newy);
8422       }
8423       else
8424       {
8425         Tile[newx][newy] = EL_EMPTY;
8426         TEST_DrawLevelField(newx, newy);
8427       }
8428
8429       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8430     }
8431     else if ((element == EL_PACMAN || element == EL_MOLE)
8432              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8433     {
8434       if (AmoebaNr[newx][newy])
8435       {
8436         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8437         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8438             Tile[newx][newy] == EL_BD_AMOEBA)
8439           AmoebaCnt[AmoebaNr[newx][newy]]--;
8440       }
8441
8442       if (element == EL_MOLE)
8443       {
8444         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8445         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8446
8447         ResetGfxAnimation(x, y);
8448         GfxAction[x][y] = ACTION_DIGGING;
8449         TEST_DrawLevelField(x, y);
8450
8451         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8452
8453         return;                         // wait for shrinking amoeba
8454       }
8455       else      // element == EL_PACMAN
8456       {
8457         Tile[newx][newy] = EL_EMPTY;
8458         TEST_DrawLevelField(newx, newy);
8459         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8460       }
8461     }
8462     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8463              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8464               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8465     {
8466       // wait for shrinking amoeba to completely disappear
8467       return;
8468     }
8469     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8470     {
8471       // object was running against a wall
8472
8473       TurnRound(x, y);
8474
8475       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8476         DrawLevelElementAnimation(x, y, element);
8477
8478       if (DONT_TOUCH(element))
8479         TestIfBadThingTouchesPlayer(x, y);
8480
8481       return;
8482     }
8483
8484     InitMovingField(x, y, MovDir[x][y]);
8485
8486     PlayLevelSoundAction(x, y, ACTION_MOVING);
8487   }
8488
8489   if (MovDir[x][y])
8490     ContinueMoving(x, y);
8491 }
8492
8493 void ContinueMoving(int x, int y)
8494 {
8495   int element = Tile[x][y];
8496   struct ElementInfo *ei = &element_info[element];
8497   int direction = MovDir[x][y];
8498   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8499   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8500   int newx = x + dx, newy = y + dy;
8501   int stored = Store[x][y];
8502   int stored_new = Store[newx][newy];
8503   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8504   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8505   boolean last_line = (newy == lev_fieldy - 1);
8506
8507   MovPos[x][y] += getElementMoveStepsize(x, y);
8508
8509   if (pushed_by_player) // special case: moving object pushed by player
8510     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8511
8512   if (ABS(MovPos[x][y]) < TILEX)
8513   {
8514     TEST_DrawLevelField(x, y);
8515
8516     return;     // element is still moving
8517   }
8518
8519   // element reached destination field
8520
8521   Tile[x][y] = EL_EMPTY;
8522   Tile[newx][newy] = element;
8523   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8524
8525   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8526   {
8527     element = Tile[newx][newy] = EL_ACID;
8528   }
8529   else if (element == EL_MOLE)
8530   {
8531     Tile[x][y] = EL_SAND;
8532
8533     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8534   }
8535   else if (element == EL_QUICKSAND_FILLING)
8536   {
8537     element = Tile[newx][newy] = get_next_element(element);
8538     Store[newx][newy] = Store[x][y];
8539   }
8540   else if (element == EL_QUICKSAND_EMPTYING)
8541   {
8542     Tile[x][y] = get_next_element(element);
8543     element = Tile[newx][newy] = Store[x][y];
8544   }
8545   else if (element == EL_QUICKSAND_FAST_FILLING)
8546   {
8547     element = Tile[newx][newy] = get_next_element(element);
8548     Store[newx][newy] = Store[x][y];
8549   }
8550   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8551   {
8552     Tile[x][y] = get_next_element(element);
8553     element = Tile[newx][newy] = Store[x][y];
8554   }
8555   else if (element == EL_MAGIC_WALL_FILLING)
8556   {
8557     element = Tile[newx][newy] = get_next_element(element);
8558     if (!game.magic_wall_active)
8559       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8560     Store[newx][newy] = Store[x][y];
8561   }
8562   else if (element == EL_MAGIC_WALL_EMPTYING)
8563   {
8564     Tile[x][y] = get_next_element(element);
8565     if (!game.magic_wall_active)
8566       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8567     element = Tile[newx][newy] = Store[x][y];
8568
8569     InitField(newx, newy, FALSE);
8570   }
8571   else if (element == EL_BD_MAGIC_WALL_FILLING)
8572   {
8573     element = Tile[newx][newy] = get_next_element(element);
8574     if (!game.magic_wall_active)
8575       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8576     Store[newx][newy] = Store[x][y];
8577   }
8578   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8579   {
8580     Tile[x][y] = get_next_element(element);
8581     if (!game.magic_wall_active)
8582       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8583     element = Tile[newx][newy] = Store[x][y];
8584
8585     InitField(newx, newy, FALSE);
8586   }
8587   else if (element == EL_DC_MAGIC_WALL_FILLING)
8588   {
8589     element = Tile[newx][newy] = get_next_element(element);
8590     if (!game.magic_wall_active)
8591       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8592     Store[newx][newy] = Store[x][y];
8593   }
8594   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8595   {
8596     Tile[x][y] = get_next_element(element);
8597     if (!game.magic_wall_active)
8598       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8599     element = Tile[newx][newy] = Store[x][y];
8600
8601     InitField(newx, newy, FALSE);
8602   }
8603   else if (element == EL_AMOEBA_DROPPING)
8604   {
8605     Tile[x][y] = get_next_element(element);
8606     element = Tile[newx][newy] = Store[x][y];
8607   }
8608   else if (element == EL_SOKOBAN_OBJECT)
8609   {
8610     if (Back[x][y])
8611       Tile[x][y] = Back[x][y];
8612
8613     if (Back[newx][newy])
8614       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8615
8616     Back[x][y] = Back[newx][newy] = 0;
8617   }
8618
8619   Store[x][y] = EL_EMPTY;
8620   MovPos[x][y] = 0;
8621   MovDir[x][y] = 0;
8622   MovDelay[x][y] = 0;
8623
8624   MovDelay[newx][newy] = 0;
8625
8626   if (CAN_CHANGE_OR_HAS_ACTION(element))
8627   {
8628     // copy element change control values to new field
8629     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8630     ChangePage[newx][newy]  = ChangePage[x][y];
8631     ChangeCount[newx][newy] = ChangeCount[x][y];
8632     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8633   }
8634
8635   CustomValue[newx][newy] = CustomValue[x][y];
8636
8637   ChangeDelay[x][y] = 0;
8638   ChangePage[x][y] = -1;
8639   ChangeCount[x][y] = 0;
8640   ChangeEvent[x][y] = -1;
8641
8642   CustomValue[x][y] = 0;
8643
8644   // copy animation control values to new field
8645   GfxFrame[newx][newy]  = GfxFrame[x][y];
8646   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8647   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8648   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8649
8650   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8651
8652   // some elements can leave other elements behind after moving
8653   if (ei->move_leave_element != EL_EMPTY &&
8654       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8655       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8656   {
8657     int move_leave_element = ei->move_leave_element;
8658
8659     // this makes it possible to leave the removed element again
8660     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8661       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8662
8663     Tile[x][y] = move_leave_element;
8664
8665     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8666       MovDir[x][y] = direction;
8667
8668     InitField(x, y, FALSE);
8669
8670     if (GFX_CRUMBLED(Tile[x][y]))
8671       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8672
8673     if (ELEM_IS_PLAYER(move_leave_element))
8674       RelocatePlayer(x, y, move_leave_element);
8675   }
8676
8677   // do this after checking for left-behind element
8678   ResetGfxAnimation(x, y);      // reset animation values for old field
8679
8680   if (!CAN_MOVE(element) ||
8681       (CAN_FALL(element) && direction == MV_DOWN &&
8682        (element == EL_SPRING ||
8683         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8684         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8685     GfxDir[x][y] = MovDir[newx][newy] = 0;
8686
8687   TEST_DrawLevelField(x, y);
8688   TEST_DrawLevelField(newx, newy);
8689
8690   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8691
8692   // prevent pushed element from moving on in pushed direction
8693   if (pushed_by_player && CAN_MOVE(element) &&
8694       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8695       !(element_info[element].move_pattern & direction))
8696     TurnRound(newx, newy);
8697
8698   // prevent elements on conveyor belt from moving on in last direction
8699   if (pushed_by_conveyor && CAN_FALL(element) &&
8700       direction & MV_HORIZONTAL)
8701     MovDir[newx][newy] = 0;
8702
8703   if (!pushed_by_player)
8704   {
8705     int nextx = newx + dx, nexty = newy + dy;
8706     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8707
8708     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8709
8710     if (CAN_FALL(element) && direction == MV_DOWN)
8711       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8712
8713     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8714       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8715
8716     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8717       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8718   }
8719
8720   if (DONT_TOUCH(element))      // object may be nasty to player or others
8721   {
8722     TestIfBadThingTouchesPlayer(newx, newy);
8723     TestIfBadThingTouchesFriend(newx, newy);
8724
8725     if (!IS_CUSTOM_ELEMENT(element))
8726       TestIfBadThingTouchesOtherBadThing(newx, newy);
8727   }
8728   else if (element == EL_PENGUIN)
8729     TestIfFriendTouchesBadThing(newx, newy);
8730
8731   if (DONT_GET_HIT_BY(element))
8732   {
8733     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8734   }
8735
8736   // give the player one last chance (one more frame) to move away
8737   if (CAN_FALL(element) && direction == MV_DOWN &&
8738       (last_line || (!IS_FREE(x, newy + 1) &&
8739                      (!IS_PLAYER(x, newy + 1) ||
8740                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8741     Impact(x, newy);
8742
8743   if (pushed_by_player && !game.use_change_when_pushing_bug)
8744   {
8745     int push_side = MV_DIR_OPPOSITE(direction);
8746     struct PlayerInfo *player = PLAYERINFO(x, y);
8747
8748     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8749                                player->index_bit, push_side);
8750     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8751                                         player->index_bit, push_side);
8752   }
8753
8754   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8755     MovDelay[newx][newy] = 1;
8756
8757   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8758
8759   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8760   TestIfElementHitsCustomElement(newx, newy, direction);
8761   TestIfPlayerTouchesCustomElement(newx, newy);
8762   TestIfElementTouchesCustomElement(newx, newy);
8763
8764   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8765       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8766     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8767                              MV_DIR_OPPOSITE(direction));
8768 }
8769
8770 int AmoebaNeighbourNr(int ax, int ay)
8771 {
8772   int i;
8773   int element = Tile[ax][ay];
8774   int group_nr = 0;
8775   static int xy[4][2] =
8776   {
8777     { 0, -1 },
8778     { -1, 0 },
8779     { +1, 0 },
8780     { 0, +1 }
8781   };
8782
8783   for (i = 0; i < NUM_DIRECTIONS; i++)
8784   {
8785     int x = ax + xy[i][0];
8786     int y = ay + xy[i][1];
8787
8788     if (!IN_LEV_FIELD(x, y))
8789       continue;
8790
8791     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8792       group_nr = AmoebaNr[x][y];
8793   }
8794
8795   return group_nr;
8796 }
8797
8798 static void AmoebaMerge(int ax, int ay)
8799 {
8800   int i, x, y, xx, yy;
8801   int new_group_nr = AmoebaNr[ax][ay];
8802   static int xy[4][2] =
8803   {
8804     { 0, -1 },
8805     { -1, 0 },
8806     { +1, 0 },
8807     { 0, +1 }
8808   };
8809
8810   if (new_group_nr == 0)
8811     return;
8812
8813   for (i = 0; i < NUM_DIRECTIONS; i++)
8814   {
8815     x = ax + xy[i][0];
8816     y = ay + xy[i][1];
8817
8818     if (!IN_LEV_FIELD(x, y))
8819       continue;
8820
8821     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8822          Tile[x][y] == EL_BD_AMOEBA ||
8823          Tile[x][y] == EL_AMOEBA_DEAD) &&
8824         AmoebaNr[x][y] != new_group_nr)
8825     {
8826       int old_group_nr = AmoebaNr[x][y];
8827
8828       if (old_group_nr == 0)
8829         return;
8830
8831       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8832       AmoebaCnt[old_group_nr] = 0;
8833       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8834       AmoebaCnt2[old_group_nr] = 0;
8835
8836       SCAN_PLAYFIELD(xx, yy)
8837       {
8838         if (AmoebaNr[xx][yy] == old_group_nr)
8839           AmoebaNr[xx][yy] = new_group_nr;
8840       }
8841     }
8842   }
8843 }
8844
8845 void AmoebaToDiamond(int ax, int ay)
8846 {
8847   int i, x, y;
8848
8849   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8850   {
8851     int group_nr = AmoebaNr[ax][ay];
8852
8853 #ifdef DEBUG
8854     if (group_nr == 0)
8855     {
8856       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8857       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8858
8859       return;
8860     }
8861 #endif
8862
8863     SCAN_PLAYFIELD(x, y)
8864     {
8865       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8866       {
8867         AmoebaNr[x][y] = 0;
8868         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8869       }
8870     }
8871
8872     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8873                             SND_AMOEBA_TURNING_TO_GEM :
8874                             SND_AMOEBA_TURNING_TO_ROCK));
8875     Bang(ax, ay);
8876   }
8877   else
8878   {
8879     static int xy[4][2] =
8880     {
8881       { 0, -1 },
8882       { -1, 0 },
8883       { +1, 0 },
8884       { 0, +1 }
8885     };
8886
8887     for (i = 0; i < NUM_DIRECTIONS; i++)
8888     {
8889       x = ax + xy[i][0];
8890       y = ay + xy[i][1];
8891
8892       if (!IN_LEV_FIELD(x, y))
8893         continue;
8894
8895       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8896       {
8897         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8898                               SND_AMOEBA_TURNING_TO_GEM :
8899                               SND_AMOEBA_TURNING_TO_ROCK));
8900         Bang(x, y);
8901       }
8902     }
8903   }
8904 }
8905
8906 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8907 {
8908   int x, y;
8909   int group_nr = AmoebaNr[ax][ay];
8910   boolean done = FALSE;
8911
8912 #ifdef DEBUG
8913   if (group_nr == 0)
8914   {
8915     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8916     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8917
8918     return;
8919   }
8920 #endif
8921
8922   SCAN_PLAYFIELD(x, y)
8923   {
8924     if (AmoebaNr[x][y] == group_nr &&
8925         (Tile[x][y] == EL_AMOEBA_DEAD ||
8926          Tile[x][y] == EL_BD_AMOEBA ||
8927          Tile[x][y] == EL_AMOEBA_GROWING))
8928     {
8929       AmoebaNr[x][y] = 0;
8930       Tile[x][y] = new_element;
8931       InitField(x, y, FALSE);
8932       TEST_DrawLevelField(x, y);
8933       done = TRUE;
8934     }
8935   }
8936
8937   if (done)
8938     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8939                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8940                             SND_BD_AMOEBA_TURNING_TO_GEM));
8941 }
8942
8943 static void AmoebaGrowing(int x, int y)
8944 {
8945   static unsigned int sound_delay = 0;
8946   static unsigned int sound_delay_value = 0;
8947
8948   if (!MovDelay[x][y])          // start new growing cycle
8949   {
8950     MovDelay[x][y] = 7;
8951
8952     if (DelayReached(&sound_delay, sound_delay_value))
8953     {
8954       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8955       sound_delay_value = 30;
8956     }
8957   }
8958
8959   if (MovDelay[x][y])           // wait some time before growing bigger
8960   {
8961     MovDelay[x][y]--;
8962     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8963     {
8964       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8965                                            6 - MovDelay[x][y]);
8966
8967       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8968     }
8969
8970     if (!MovDelay[x][y])
8971     {
8972       Tile[x][y] = Store[x][y];
8973       Store[x][y] = 0;
8974       TEST_DrawLevelField(x, y);
8975     }
8976   }
8977 }
8978
8979 static void AmoebaShrinking(int x, int y)
8980 {
8981   static unsigned int sound_delay = 0;
8982   static unsigned int sound_delay_value = 0;
8983
8984   if (!MovDelay[x][y])          // start new shrinking cycle
8985   {
8986     MovDelay[x][y] = 7;
8987
8988     if (DelayReached(&sound_delay, sound_delay_value))
8989       sound_delay_value = 30;
8990   }
8991
8992   if (MovDelay[x][y])           // wait some time before shrinking
8993   {
8994     MovDelay[x][y]--;
8995     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8996     {
8997       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8998                                            6 - MovDelay[x][y]);
8999
9000       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9001     }
9002
9003     if (!MovDelay[x][y])
9004     {
9005       Tile[x][y] = EL_EMPTY;
9006       TEST_DrawLevelField(x, y);
9007
9008       // don't let mole enter this field in this cycle;
9009       // (give priority to objects falling to this field from above)
9010       Stop[x][y] = TRUE;
9011     }
9012   }
9013 }
9014
9015 static void AmoebaReproduce(int ax, int ay)
9016 {
9017   int i;
9018   int element = Tile[ax][ay];
9019   int graphic = el2img(element);
9020   int newax = ax, neway = ay;
9021   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9022   static int xy[4][2] =
9023   {
9024     { 0, -1 },
9025     { -1, 0 },
9026     { +1, 0 },
9027     { 0, +1 }
9028   };
9029
9030   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9031   {
9032     Tile[ax][ay] = EL_AMOEBA_DEAD;
9033     TEST_DrawLevelField(ax, ay);
9034     return;
9035   }
9036
9037   if (IS_ANIMATED(graphic))
9038     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9039
9040   if (!MovDelay[ax][ay])        // start making new amoeba field
9041     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9042
9043   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9044   {
9045     MovDelay[ax][ay]--;
9046     if (MovDelay[ax][ay])
9047       return;
9048   }
9049
9050   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9051   {
9052     int start = RND(4);
9053     int x = ax + xy[start][0];
9054     int y = ay + xy[start][1];
9055
9056     if (!IN_LEV_FIELD(x, y))
9057       return;
9058
9059     if (IS_FREE(x, y) ||
9060         CAN_GROW_INTO(Tile[x][y]) ||
9061         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9062         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9063     {
9064       newax = x;
9065       neway = y;
9066     }
9067
9068     if (newax == ax && neway == ay)
9069       return;
9070   }
9071   else                          // normal or "filled" (BD style) amoeba
9072   {
9073     int start = RND(4);
9074     boolean waiting_for_player = FALSE;
9075
9076     for (i = 0; i < NUM_DIRECTIONS; i++)
9077     {
9078       int j = (start + i) % 4;
9079       int x = ax + xy[j][0];
9080       int y = ay + xy[j][1];
9081
9082       if (!IN_LEV_FIELD(x, y))
9083         continue;
9084
9085       if (IS_FREE(x, y) ||
9086           CAN_GROW_INTO(Tile[x][y]) ||
9087           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9088           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9089       {
9090         newax = x;
9091         neway = y;
9092         break;
9093       }
9094       else if (IS_PLAYER(x, y))
9095         waiting_for_player = TRUE;
9096     }
9097
9098     if (newax == ax && neway == ay)             // amoeba cannot grow
9099     {
9100       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9101       {
9102         Tile[ax][ay] = EL_AMOEBA_DEAD;
9103         TEST_DrawLevelField(ax, ay);
9104         AmoebaCnt[AmoebaNr[ax][ay]]--;
9105
9106         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9107         {
9108           if (element == EL_AMOEBA_FULL)
9109             AmoebaToDiamond(ax, ay);
9110           else if (element == EL_BD_AMOEBA)
9111             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9112         }
9113       }
9114       return;
9115     }
9116     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9117     {
9118       // amoeba gets larger by growing in some direction
9119
9120       int new_group_nr = AmoebaNr[ax][ay];
9121
9122 #ifdef DEBUG
9123   if (new_group_nr == 0)
9124   {
9125     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9126           newax, neway);
9127     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9128
9129     return;
9130   }
9131 #endif
9132
9133       AmoebaNr[newax][neway] = new_group_nr;
9134       AmoebaCnt[new_group_nr]++;
9135       AmoebaCnt2[new_group_nr]++;
9136
9137       // if amoeba touches other amoeba(s) after growing, unify them
9138       AmoebaMerge(newax, neway);
9139
9140       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9141       {
9142         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9143         return;
9144       }
9145     }
9146   }
9147
9148   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9149       (neway == lev_fieldy - 1 && newax != ax))
9150   {
9151     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9152     Store[newax][neway] = element;
9153   }
9154   else if (neway == ay || element == EL_EMC_DRIPPER)
9155   {
9156     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9157
9158     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9159   }
9160   else
9161   {
9162     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9163     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9164     Store[ax][ay] = EL_AMOEBA_DROP;
9165     ContinueMoving(ax, ay);
9166     return;
9167   }
9168
9169   TEST_DrawLevelField(newax, neway);
9170 }
9171
9172 static void Life(int ax, int ay)
9173 {
9174   int x1, y1, x2, y2;
9175   int life_time = 40;
9176   int element = Tile[ax][ay];
9177   int graphic = el2img(element);
9178   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9179                          level.biomaze);
9180   boolean changed = FALSE;
9181
9182   if (IS_ANIMATED(graphic))
9183     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9184
9185   if (Stop[ax][ay])
9186     return;
9187
9188   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9189     MovDelay[ax][ay] = life_time;
9190
9191   if (MovDelay[ax][ay])         // wait some time before next cycle
9192   {
9193     MovDelay[ax][ay]--;
9194     if (MovDelay[ax][ay])
9195       return;
9196   }
9197
9198   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9199   {
9200     int xx = ax+x1, yy = ay+y1;
9201     int old_element = Tile[xx][yy];
9202     int num_neighbours = 0;
9203
9204     if (!IN_LEV_FIELD(xx, yy))
9205       continue;
9206
9207     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9208     {
9209       int x = xx+x2, y = yy+y2;
9210
9211       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9212         continue;
9213
9214       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9215       boolean is_neighbour = FALSE;
9216
9217       if (level.use_life_bugs)
9218         is_neighbour =
9219           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9220            (IS_FREE(x, y)                             &&  Stop[x][y]));
9221       else
9222         is_neighbour =
9223           (Last[x][y] == element || is_player_cell);
9224
9225       if (is_neighbour)
9226         num_neighbours++;
9227     }
9228
9229     boolean is_free = FALSE;
9230
9231     if (level.use_life_bugs)
9232       is_free = (IS_FREE(xx, yy));
9233     else
9234       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9235
9236     if (xx == ax && yy == ay)           // field in the middle
9237     {
9238       if (num_neighbours < life_parameter[0] ||
9239           num_neighbours > life_parameter[1])
9240       {
9241         Tile[xx][yy] = EL_EMPTY;
9242         if (Tile[xx][yy] != old_element)
9243           TEST_DrawLevelField(xx, yy);
9244         Stop[xx][yy] = TRUE;
9245         changed = TRUE;
9246       }
9247     }
9248     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9249     {                                   // free border field
9250       if (num_neighbours >= life_parameter[2] &&
9251           num_neighbours <= life_parameter[3])
9252       {
9253         Tile[xx][yy] = element;
9254         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9255         if (Tile[xx][yy] != old_element)
9256           TEST_DrawLevelField(xx, yy);
9257         Stop[xx][yy] = TRUE;
9258         changed = TRUE;
9259       }
9260     }
9261   }
9262
9263   if (changed)
9264     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9265                    SND_GAME_OF_LIFE_GROWING);
9266 }
9267
9268 static void InitRobotWheel(int x, int y)
9269 {
9270   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9271 }
9272
9273 static void RunRobotWheel(int x, int y)
9274 {
9275   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9276 }
9277
9278 static void StopRobotWheel(int x, int y)
9279 {
9280   if (game.robot_wheel_x == x &&
9281       game.robot_wheel_y == y)
9282   {
9283     game.robot_wheel_x = -1;
9284     game.robot_wheel_y = -1;
9285     game.robot_wheel_active = FALSE;
9286   }
9287 }
9288
9289 static void InitTimegateWheel(int x, int y)
9290 {
9291   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9292 }
9293
9294 static void RunTimegateWheel(int x, int y)
9295 {
9296   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9297 }
9298
9299 static void InitMagicBallDelay(int x, int y)
9300 {
9301   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9302 }
9303
9304 static void ActivateMagicBall(int bx, int by)
9305 {
9306   int x, y;
9307
9308   if (level.ball_random)
9309   {
9310     int pos_border = RND(8);    // select one of the eight border elements
9311     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9312     int xx = pos_content % 3;
9313     int yy = pos_content / 3;
9314
9315     x = bx - 1 + xx;
9316     y = by - 1 + yy;
9317
9318     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9319       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9320   }
9321   else
9322   {
9323     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9324     {
9325       int xx = x - bx + 1;
9326       int yy = y - by + 1;
9327
9328       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9329         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9330     }
9331   }
9332
9333   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9334 }
9335
9336 static void CheckExit(int x, int y)
9337 {
9338   if (game.gems_still_needed > 0 ||
9339       game.sokoban_fields_still_needed > 0 ||
9340       game.sokoban_objects_still_needed > 0 ||
9341       game.lights_still_needed > 0)
9342   {
9343     int element = Tile[x][y];
9344     int graphic = el2img(element);
9345
9346     if (IS_ANIMATED(graphic))
9347       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9348
9349     return;
9350   }
9351
9352   // do not re-open exit door closed after last player
9353   if (game.all_players_gone)
9354     return;
9355
9356   Tile[x][y] = EL_EXIT_OPENING;
9357
9358   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9359 }
9360
9361 static void CheckExitEM(int x, int y)
9362 {
9363   if (game.gems_still_needed > 0 ||
9364       game.sokoban_fields_still_needed > 0 ||
9365       game.sokoban_objects_still_needed > 0 ||
9366       game.lights_still_needed > 0)
9367   {
9368     int element = Tile[x][y];
9369     int graphic = el2img(element);
9370
9371     if (IS_ANIMATED(graphic))
9372       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9373
9374     return;
9375   }
9376
9377   // do not re-open exit door closed after last player
9378   if (game.all_players_gone)
9379     return;
9380
9381   Tile[x][y] = EL_EM_EXIT_OPENING;
9382
9383   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9384 }
9385
9386 static void CheckExitSteel(int x, int y)
9387 {
9388   if (game.gems_still_needed > 0 ||
9389       game.sokoban_fields_still_needed > 0 ||
9390       game.sokoban_objects_still_needed > 0 ||
9391       game.lights_still_needed > 0)
9392   {
9393     int element = Tile[x][y];
9394     int graphic = el2img(element);
9395
9396     if (IS_ANIMATED(graphic))
9397       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9398
9399     return;
9400   }
9401
9402   // do not re-open exit door closed after last player
9403   if (game.all_players_gone)
9404     return;
9405
9406   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9407
9408   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9409 }
9410
9411 static void CheckExitSteelEM(int x, int y)
9412 {
9413   if (game.gems_still_needed > 0 ||
9414       game.sokoban_fields_still_needed > 0 ||
9415       game.sokoban_objects_still_needed > 0 ||
9416       game.lights_still_needed > 0)
9417   {
9418     int element = Tile[x][y];
9419     int graphic = el2img(element);
9420
9421     if (IS_ANIMATED(graphic))
9422       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9423
9424     return;
9425   }
9426
9427   // do not re-open exit door closed after last player
9428   if (game.all_players_gone)
9429     return;
9430
9431   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9432
9433   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9434 }
9435
9436 static void CheckExitSP(int x, int y)
9437 {
9438   if (game.gems_still_needed > 0)
9439   {
9440     int element = Tile[x][y];
9441     int graphic = el2img(element);
9442
9443     if (IS_ANIMATED(graphic))
9444       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9445
9446     return;
9447   }
9448
9449   // do not re-open exit door closed after last player
9450   if (game.all_players_gone)
9451     return;
9452
9453   Tile[x][y] = EL_SP_EXIT_OPENING;
9454
9455   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9456 }
9457
9458 static void CloseAllOpenTimegates(void)
9459 {
9460   int x, y;
9461
9462   SCAN_PLAYFIELD(x, y)
9463   {
9464     int element = Tile[x][y];
9465
9466     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9467     {
9468       Tile[x][y] = EL_TIMEGATE_CLOSING;
9469
9470       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9471     }
9472   }
9473 }
9474
9475 static void DrawTwinkleOnField(int x, int y)
9476 {
9477   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9478     return;
9479
9480   if (Tile[x][y] == EL_BD_DIAMOND)
9481     return;
9482
9483   if (MovDelay[x][y] == 0)      // next animation frame
9484     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9485
9486   if (MovDelay[x][y] != 0)      // wait some time before next frame
9487   {
9488     MovDelay[x][y]--;
9489
9490     DrawLevelElementAnimation(x, y, Tile[x][y]);
9491
9492     if (MovDelay[x][y] != 0)
9493     {
9494       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9495                                            10 - MovDelay[x][y]);
9496
9497       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9498     }
9499   }
9500 }
9501
9502 static void MauerWaechst(int x, int y)
9503 {
9504   int delay = 6;
9505
9506   if (!MovDelay[x][y])          // next animation frame
9507     MovDelay[x][y] = 3 * delay;
9508
9509   if (MovDelay[x][y])           // wait some time before next frame
9510   {
9511     MovDelay[x][y]--;
9512
9513     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9514     {
9515       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9516       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9517
9518       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9519     }
9520
9521     if (!MovDelay[x][y])
9522     {
9523       if (MovDir[x][y] == MV_LEFT)
9524       {
9525         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9526           TEST_DrawLevelField(x - 1, y);
9527       }
9528       else if (MovDir[x][y] == MV_RIGHT)
9529       {
9530         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9531           TEST_DrawLevelField(x + 1, y);
9532       }
9533       else if (MovDir[x][y] == MV_UP)
9534       {
9535         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9536           TEST_DrawLevelField(x, y - 1);
9537       }
9538       else
9539       {
9540         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9541           TEST_DrawLevelField(x, y + 1);
9542       }
9543
9544       Tile[x][y] = Store[x][y];
9545       Store[x][y] = 0;
9546       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9547       TEST_DrawLevelField(x, y);
9548     }
9549   }
9550 }
9551
9552 static void MauerAbleger(int ax, int ay)
9553 {
9554   int element = Tile[ax][ay];
9555   int graphic = el2img(element);
9556   boolean oben_frei = FALSE, unten_frei = FALSE;
9557   boolean links_frei = FALSE, rechts_frei = FALSE;
9558   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9559   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9560   boolean new_wall = FALSE;
9561
9562   if (IS_ANIMATED(graphic))
9563     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9564
9565   if (!MovDelay[ax][ay])        // start building new wall
9566     MovDelay[ax][ay] = 6;
9567
9568   if (MovDelay[ax][ay])         // wait some time before building new wall
9569   {
9570     MovDelay[ax][ay]--;
9571     if (MovDelay[ax][ay])
9572       return;
9573   }
9574
9575   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9576     oben_frei = TRUE;
9577   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9578     unten_frei = TRUE;
9579   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9580     links_frei = TRUE;
9581   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9582     rechts_frei = TRUE;
9583
9584   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9585       element == EL_EXPANDABLE_WALL_ANY)
9586   {
9587     if (oben_frei)
9588     {
9589       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9590       Store[ax][ay-1] = element;
9591       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9592       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9593         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9594                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9595       new_wall = TRUE;
9596     }
9597     if (unten_frei)
9598     {
9599       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9600       Store[ax][ay+1] = element;
9601       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9602       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9603         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9604                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9605       new_wall = TRUE;
9606     }
9607   }
9608
9609   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9610       element == EL_EXPANDABLE_WALL_ANY ||
9611       element == EL_EXPANDABLE_WALL ||
9612       element == EL_BD_EXPANDABLE_WALL)
9613   {
9614     if (links_frei)
9615     {
9616       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9617       Store[ax-1][ay] = element;
9618       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9619       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9620         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9621                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9622       new_wall = TRUE;
9623     }
9624
9625     if (rechts_frei)
9626     {
9627       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9628       Store[ax+1][ay] = element;
9629       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9630       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9631         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9632                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9633       new_wall = TRUE;
9634     }
9635   }
9636
9637   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9638     TEST_DrawLevelField(ax, ay);
9639
9640   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9641     oben_massiv = TRUE;
9642   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9643     unten_massiv = TRUE;
9644   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9645     links_massiv = TRUE;
9646   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9647     rechts_massiv = TRUE;
9648
9649   if (((oben_massiv && unten_massiv) ||
9650        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9651        element == EL_EXPANDABLE_WALL) &&
9652       ((links_massiv && rechts_massiv) ||
9653        element == EL_EXPANDABLE_WALL_VERTICAL))
9654     Tile[ax][ay] = EL_WALL;
9655
9656   if (new_wall)
9657     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9658 }
9659
9660 static void MauerAblegerStahl(int ax, int ay)
9661 {
9662   int element = Tile[ax][ay];
9663   int graphic = el2img(element);
9664   boolean oben_frei = FALSE, unten_frei = FALSE;
9665   boolean links_frei = FALSE, rechts_frei = FALSE;
9666   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9667   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9668   boolean new_wall = FALSE;
9669
9670   if (IS_ANIMATED(graphic))
9671     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9672
9673   if (!MovDelay[ax][ay])        // start building new wall
9674     MovDelay[ax][ay] = 6;
9675
9676   if (MovDelay[ax][ay])         // wait some time before building new wall
9677   {
9678     MovDelay[ax][ay]--;
9679     if (MovDelay[ax][ay])
9680       return;
9681   }
9682
9683   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9684     oben_frei = TRUE;
9685   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9686     unten_frei = TRUE;
9687   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9688     links_frei = TRUE;
9689   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9690     rechts_frei = TRUE;
9691
9692   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9693       element == EL_EXPANDABLE_STEELWALL_ANY)
9694   {
9695     if (oben_frei)
9696     {
9697       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9698       Store[ax][ay-1] = element;
9699       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9700       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9701         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9702                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9703       new_wall = TRUE;
9704     }
9705     if (unten_frei)
9706     {
9707       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9708       Store[ax][ay+1] = element;
9709       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9710       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9711         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9712                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9713       new_wall = TRUE;
9714     }
9715   }
9716
9717   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9718       element == EL_EXPANDABLE_STEELWALL_ANY)
9719   {
9720     if (links_frei)
9721     {
9722       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9723       Store[ax-1][ay] = element;
9724       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9725       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9726         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9727                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9728       new_wall = TRUE;
9729     }
9730
9731     if (rechts_frei)
9732     {
9733       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9734       Store[ax+1][ay] = element;
9735       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9736       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9737         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9738                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9739       new_wall = TRUE;
9740     }
9741   }
9742
9743   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9744     oben_massiv = TRUE;
9745   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9746     unten_massiv = TRUE;
9747   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9748     links_massiv = TRUE;
9749   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9750     rechts_massiv = TRUE;
9751
9752   if (((oben_massiv && unten_massiv) ||
9753        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9754       ((links_massiv && rechts_massiv) ||
9755        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9756     Tile[ax][ay] = EL_STEELWALL;
9757
9758   if (new_wall)
9759     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9760 }
9761
9762 static void CheckForDragon(int x, int y)
9763 {
9764   int i, j;
9765   boolean dragon_found = FALSE;
9766   static int xy[4][2] =
9767   {
9768     { 0, -1 },
9769     { -1, 0 },
9770     { +1, 0 },
9771     { 0, +1 }
9772   };
9773
9774   for (i = 0; i < NUM_DIRECTIONS; i++)
9775   {
9776     for (j = 0; j < 4; j++)
9777     {
9778       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9779
9780       if (IN_LEV_FIELD(xx, yy) &&
9781           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9782       {
9783         if (Tile[xx][yy] == EL_DRAGON)
9784           dragon_found = TRUE;
9785       }
9786       else
9787         break;
9788     }
9789   }
9790
9791   if (!dragon_found)
9792   {
9793     for (i = 0; i < NUM_DIRECTIONS; i++)
9794     {
9795       for (j = 0; j < 3; j++)
9796       {
9797         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9798   
9799         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9800         {
9801           Tile[xx][yy] = EL_EMPTY;
9802           TEST_DrawLevelField(xx, yy);
9803         }
9804         else
9805           break;
9806       }
9807     }
9808   }
9809 }
9810
9811 static void InitBuggyBase(int x, int y)
9812 {
9813   int element = Tile[x][y];
9814   int activating_delay = FRAMES_PER_SECOND / 4;
9815
9816   ChangeDelay[x][y] =
9817     (element == EL_SP_BUGGY_BASE ?
9818      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9819      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9820      activating_delay :
9821      element == EL_SP_BUGGY_BASE_ACTIVE ?
9822      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9823 }
9824
9825 static void WarnBuggyBase(int x, int y)
9826 {
9827   int i;
9828   static int xy[4][2] =
9829   {
9830     { 0, -1 },
9831     { -1, 0 },
9832     { +1, 0 },
9833     { 0, +1 }
9834   };
9835
9836   for (i = 0; i < NUM_DIRECTIONS; i++)
9837   {
9838     int xx = x + xy[i][0];
9839     int yy = y + xy[i][1];
9840
9841     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9842     {
9843       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9844
9845       break;
9846     }
9847   }
9848 }
9849
9850 static void InitTrap(int x, int y)
9851 {
9852   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9853 }
9854
9855 static void ActivateTrap(int x, int y)
9856 {
9857   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9858 }
9859
9860 static void ChangeActiveTrap(int x, int y)
9861 {
9862   int graphic = IMG_TRAP_ACTIVE;
9863
9864   // if new animation frame was drawn, correct crumbled sand border
9865   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9866     TEST_DrawLevelFieldCrumbled(x, y);
9867 }
9868
9869 static int getSpecialActionElement(int element, int number, int base_element)
9870 {
9871   return (element != EL_EMPTY ? element :
9872           number != -1 ? base_element + number - 1 :
9873           EL_EMPTY);
9874 }
9875
9876 static int getModifiedActionNumber(int value_old, int operator, int operand,
9877                                    int value_min, int value_max)
9878 {
9879   int value_new = (operator == CA_MODE_SET      ? operand :
9880                    operator == CA_MODE_ADD      ? value_old + operand :
9881                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9882                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9883                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9884                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9885                    value_old);
9886
9887   return (value_new < value_min ? value_min :
9888           value_new > value_max ? value_max :
9889           value_new);
9890 }
9891
9892 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9893 {
9894   struct ElementInfo *ei = &element_info[element];
9895   struct ElementChangeInfo *change = &ei->change_page[page];
9896   int target_element = change->target_element;
9897   int action_type = change->action_type;
9898   int action_mode = change->action_mode;
9899   int action_arg = change->action_arg;
9900   int action_element = change->action_element;
9901   int i;
9902
9903   if (!change->has_action)
9904     return;
9905
9906   // ---------- determine action paramater values -----------------------------
9907
9908   int level_time_value =
9909     (level.time > 0 ? TimeLeft :
9910      TimePlayed);
9911
9912   int action_arg_element_raw =
9913     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9914      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9915      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9916      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9917      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9918      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9919      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9920      EL_EMPTY);
9921   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9922
9923   int action_arg_direction =
9924     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9925      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9926      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9927      change->actual_trigger_side :
9928      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9929      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9930      MV_NONE);
9931
9932   int action_arg_number_min =
9933     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9934      CA_ARG_MIN);
9935
9936   int action_arg_number_max =
9937     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9938      action_type == CA_SET_LEVEL_GEMS ? 999 :
9939      action_type == CA_SET_LEVEL_TIME ? 9999 :
9940      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9941      action_type == CA_SET_CE_VALUE ? 9999 :
9942      action_type == CA_SET_CE_SCORE ? 9999 :
9943      CA_ARG_MAX);
9944
9945   int action_arg_number_reset =
9946     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9947      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9948      action_type == CA_SET_LEVEL_TIME ? level.time :
9949      action_type == CA_SET_LEVEL_SCORE ? 0 :
9950      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9951      action_type == CA_SET_CE_SCORE ? 0 :
9952      0);
9953
9954   int action_arg_number =
9955     (action_arg <= CA_ARG_MAX ? action_arg :
9956      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9957      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9958      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9959      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9960      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9961      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9962      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9963      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9964      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9965      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9966      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9967      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9968      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9969      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9970      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9971      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9972      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9973      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9974      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9975      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9976      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9977      -1);
9978
9979   int action_arg_number_old =
9980     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9981      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9982      action_type == CA_SET_LEVEL_SCORE ? game.score :
9983      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9984      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9985      0);
9986
9987   int action_arg_number_new =
9988     getModifiedActionNumber(action_arg_number_old,
9989                             action_mode, action_arg_number,
9990                             action_arg_number_min, action_arg_number_max);
9991
9992   int trigger_player_bits =
9993     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9994      change->actual_trigger_player_bits : change->trigger_player);
9995
9996   int action_arg_player_bits =
9997     (action_arg >= CA_ARG_PLAYER_1 &&
9998      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9999      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10000      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10001      PLAYER_BITS_ANY);
10002
10003   // ---------- execute action  -----------------------------------------------
10004
10005   switch (action_type)
10006   {
10007     case CA_NO_ACTION:
10008     {
10009       return;
10010     }
10011
10012     // ---------- level actions  ----------------------------------------------
10013
10014     case CA_RESTART_LEVEL:
10015     {
10016       game.restart_level = TRUE;
10017
10018       break;
10019     }
10020
10021     case CA_SHOW_ENVELOPE:
10022     {
10023       int element = getSpecialActionElement(action_arg_element,
10024                                             action_arg_number, EL_ENVELOPE_1);
10025
10026       if (IS_ENVELOPE(element))
10027         local_player->show_envelope = element;
10028
10029       break;
10030     }
10031
10032     case CA_SET_LEVEL_TIME:
10033     {
10034       if (level.time > 0)       // only modify limited time value
10035       {
10036         TimeLeft = action_arg_number_new;
10037
10038         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10039
10040         DisplayGameControlValues();
10041
10042         if (!TimeLeft && setup.time_limit)
10043           for (i = 0; i < MAX_PLAYERS; i++)
10044             KillPlayer(&stored_player[i]);
10045       }
10046
10047       break;
10048     }
10049
10050     case CA_SET_LEVEL_SCORE:
10051     {
10052       game.score = action_arg_number_new;
10053
10054       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10055
10056       DisplayGameControlValues();
10057
10058       break;
10059     }
10060
10061     case CA_SET_LEVEL_GEMS:
10062     {
10063       game.gems_still_needed = action_arg_number_new;
10064
10065       game.snapshot.collected_item = TRUE;
10066
10067       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10068
10069       DisplayGameControlValues();
10070
10071       break;
10072     }
10073
10074     case CA_SET_LEVEL_WIND:
10075     {
10076       game.wind_direction = action_arg_direction;
10077
10078       break;
10079     }
10080
10081     case CA_SET_LEVEL_RANDOM_SEED:
10082     {
10083       // ensure that setting a new random seed while playing is predictable
10084       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10085
10086       break;
10087     }
10088
10089     // ---------- player actions  ---------------------------------------------
10090
10091     case CA_MOVE_PLAYER:
10092     case CA_MOVE_PLAYER_NEW:
10093     {
10094       // automatically move to the next field in specified direction
10095       for (i = 0; i < MAX_PLAYERS; i++)
10096         if (trigger_player_bits & (1 << i))
10097           if (action_type == CA_MOVE_PLAYER ||
10098               stored_player[i].MovPos == 0)
10099             stored_player[i].programmed_action = action_arg_direction;
10100
10101       break;
10102     }
10103
10104     case CA_EXIT_PLAYER:
10105     {
10106       for (i = 0; i < MAX_PLAYERS; i++)
10107         if (action_arg_player_bits & (1 << i))
10108           ExitPlayer(&stored_player[i]);
10109
10110       if (game.players_still_needed == 0)
10111         LevelSolved();
10112
10113       break;
10114     }
10115
10116     case CA_KILL_PLAYER:
10117     {
10118       for (i = 0; i < MAX_PLAYERS; i++)
10119         if (action_arg_player_bits & (1 << i))
10120           KillPlayer(&stored_player[i]);
10121
10122       break;
10123     }
10124
10125     case CA_SET_PLAYER_KEYS:
10126     {
10127       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10128       int element = getSpecialActionElement(action_arg_element,
10129                                             action_arg_number, EL_KEY_1);
10130
10131       if (IS_KEY(element))
10132       {
10133         for (i = 0; i < MAX_PLAYERS; i++)
10134         {
10135           if (trigger_player_bits & (1 << i))
10136           {
10137             stored_player[i].key[KEY_NR(element)] = key_state;
10138
10139             DrawGameDoorValues();
10140           }
10141         }
10142       }
10143
10144       break;
10145     }
10146
10147     case CA_SET_PLAYER_SPEED:
10148     {
10149       for (i = 0; i < MAX_PLAYERS; i++)
10150       {
10151         if (trigger_player_bits & (1 << i))
10152         {
10153           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10154
10155           if (action_arg == CA_ARG_SPEED_FASTER &&
10156               stored_player[i].cannot_move)
10157           {
10158             action_arg_number = STEPSIZE_VERY_SLOW;
10159           }
10160           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10161                    action_arg == CA_ARG_SPEED_FASTER)
10162           {
10163             action_arg_number = 2;
10164             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10165                            CA_MODE_MULTIPLY);
10166           }
10167           else if (action_arg == CA_ARG_NUMBER_RESET)
10168           {
10169             action_arg_number = level.initial_player_stepsize[i];
10170           }
10171
10172           move_stepsize =
10173             getModifiedActionNumber(move_stepsize,
10174                                     action_mode,
10175                                     action_arg_number,
10176                                     action_arg_number_min,
10177                                     action_arg_number_max);
10178
10179           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10180         }
10181       }
10182
10183       break;
10184     }
10185
10186     case CA_SET_PLAYER_SHIELD:
10187     {
10188       for (i = 0; i < MAX_PLAYERS; i++)
10189       {
10190         if (trigger_player_bits & (1 << i))
10191         {
10192           if (action_arg == CA_ARG_SHIELD_OFF)
10193           {
10194             stored_player[i].shield_normal_time_left = 0;
10195             stored_player[i].shield_deadly_time_left = 0;
10196           }
10197           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10198           {
10199             stored_player[i].shield_normal_time_left = 999999;
10200           }
10201           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10202           {
10203             stored_player[i].shield_normal_time_left = 999999;
10204             stored_player[i].shield_deadly_time_left = 999999;
10205           }
10206         }
10207       }
10208
10209       break;
10210     }
10211
10212     case CA_SET_PLAYER_GRAVITY:
10213     {
10214       for (i = 0; i < MAX_PLAYERS; i++)
10215       {
10216         if (trigger_player_bits & (1 << i))
10217         {
10218           stored_player[i].gravity =
10219             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10220              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10221              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10222              stored_player[i].gravity);
10223         }
10224       }
10225
10226       break;
10227     }
10228
10229     case CA_SET_PLAYER_ARTWORK:
10230     {
10231       for (i = 0; i < MAX_PLAYERS; i++)
10232       {
10233         if (trigger_player_bits & (1 << i))
10234         {
10235           int artwork_element = action_arg_element;
10236
10237           if (action_arg == CA_ARG_ELEMENT_RESET)
10238             artwork_element =
10239               (level.use_artwork_element[i] ? level.artwork_element[i] :
10240                stored_player[i].element_nr);
10241
10242           if (stored_player[i].artwork_element != artwork_element)
10243             stored_player[i].Frame = 0;
10244
10245           stored_player[i].artwork_element = artwork_element;
10246
10247           SetPlayerWaiting(&stored_player[i], FALSE);
10248
10249           // set number of special actions for bored and sleeping animation
10250           stored_player[i].num_special_action_bored =
10251             get_num_special_action(artwork_element,
10252                                    ACTION_BORING_1, ACTION_BORING_LAST);
10253           stored_player[i].num_special_action_sleeping =
10254             get_num_special_action(artwork_element,
10255                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10256         }
10257       }
10258
10259       break;
10260     }
10261
10262     case CA_SET_PLAYER_INVENTORY:
10263     {
10264       for (i = 0; i < MAX_PLAYERS; i++)
10265       {
10266         struct PlayerInfo *player = &stored_player[i];
10267         int j, k;
10268
10269         if (trigger_player_bits & (1 << i))
10270         {
10271           int inventory_element = action_arg_element;
10272
10273           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10274               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10275               action_arg == CA_ARG_ELEMENT_ACTION)
10276           {
10277             int element = inventory_element;
10278             int collect_count = element_info[element].collect_count_initial;
10279
10280             if (!IS_CUSTOM_ELEMENT(element))
10281               collect_count = 1;
10282
10283             if (collect_count == 0)
10284               player->inventory_infinite_element = element;
10285             else
10286               for (k = 0; k < collect_count; k++)
10287                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10288                   player->inventory_element[player->inventory_size++] =
10289                     element;
10290           }
10291           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10292                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10293                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10294           {
10295             if (player->inventory_infinite_element != EL_UNDEFINED &&
10296                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10297                                      action_arg_element_raw))
10298               player->inventory_infinite_element = EL_UNDEFINED;
10299
10300             for (k = 0, j = 0; j < player->inventory_size; j++)
10301             {
10302               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10303                                         action_arg_element_raw))
10304                 player->inventory_element[k++] = player->inventory_element[j];
10305             }
10306
10307             player->inventory_size = k;
10308           }
10309           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10310           {
10311             if (player->inventory_size > 0)
10312             {
10313               for (j = 0; j < player->inventory_size - 1; j++)
10314                 player->inventory_element[j] = player->inventory_element[j + 1];
10315
10316               player->inventory_size--;
10317             }
10318           }
10319           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10320           {
10321             if (player->inventory_size > 0)
10322               player->inventory_size--;
10323           }
10324           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10325           {
10326             player->inventory_infinite_element = EL_UNDEFINED;
10327             player->inventory_size = 0;
10328           }
10329           else if (action_arg == CA_ARG_INVENTORY_RESET)
10330           {
10331             player->inventory_infinite_element = EL_UNDEFINED;
10332             player->inventory_size = 0;
10333
10334             if (level.use_initial_inventory[i])
10335             {
10336               for (j = 0; j < level.initial_inventory_size[i]; j++)
10337               {
10338                 int element = level.initial_inventory_content[i][j];
10339                 int collect_count = element_info[element].collect_count_initial;
10340
10341                 if (!IS_CUSTOM_ELEMENT(element))
10342                   collect_count = 1;
10343
10344                 if (collect_count == 0)
10345                   player->inventory_infinite_element = element;
10346                 else
10347                   for (k = 0; k < collect_count; k++)
10348                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10349                       player->inventory_element[player->inventory_size++] =
10350                         element;
10351               }
10352             }
10353           }
10354         }
10355       }
10356
10357       break;
10358     }
10359
10360     // ---------- CE actions  -------------------------------------------------
10361
10362     case CA_SET_CE_VALUE:
10363     {
10364       int last_ce_value = CustomValue[x][y];
10365
10366       CustomValue[x][y] = action_arg_number_new;
10367
10368       if (CustomValue[x][y] != last_ce_value)
10369       {
10370         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10371         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10372
10373         if (CustomValue[x][y] == 0)
10374         {
10375           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10376           ChangeCount[x][y] = 0;        // allow at least one more change
10377
10378           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10379           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10380         }
10381       }
10382
10383       break;
10384     }
10385
10386     case CA_SET_CE_SCORE:
10387     {
10388       int last_ce_score = ei->collect_score;
10389
10390       ei->collect_score = action_arg_number_new;
10391
10392       if (ei->collect_score != last_ce_score)
10393       {
10394         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10395         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10396
10397         if (ei->collect_score == 0)
10398         {
10399           int xx, yy;
10400
10401           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10402           ChangeCount[x][y] = 0;        // allow at least one more change
10403
10404           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10405           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10406
10407           /*
10408             This is a very special case that seems to be a mixture between
10409             CheckElementChange() and CheckTriggeredElementChange(): while
10410             the first one only affects single elements that are triggered
10411             directly, the second one affects multiple elements in the playfield
10412             that are triggered indirectly by another element. This is a third
10413             case: Changing the CE score always affects multiple identical CEs,
10414             so every affected CE must be checked, not only the single CE for
10415             which the CE score was changed in the first place (as every instance
10416             of that CE shares the same CE score, and therefore also can change)!
10417           */
10418           SCAN_PLAYFIELD(xx, yy)
10419           {
10420             if (Tile[xx][yy] == element)
10421               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10422                                  CE_SCORE_GETS_ZERO);
10423           }
10424         }
10425       }
10426
10427       break;
10428     }
10429
10430     case CA_SET_CE_ARTWORK:
10431     {
10432       int artwork_element = action_arg_element;
10433       boolean reset_frame = FALSE;
10434       int xx, yy;
10435
10436       if (action_arg == CA_ARG_ELEMENT_RESET)
10437         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10438                            element);
10439
10440       if (ei->gfx_element != artwork_element)
10441         reset_frame = TRUE;
10442
10443       ei->gfx_element = artwork_element;
10444
10445       SCAN_PLAYFIELD(xx, yy)
10446       {
10447         if (Tile[xx][yy] == element)
10448         {
10449           if (reset_frame)
10450           {
10451             ResetGfxAnimation(xx, yy);
10452             ResetRandomAnimationValue(xx, yy);
10453           }
10454
10455           TEST_DrawLevelField(xx, yy);
10456         }
10457       }
10458
10459       break;
10460     }
10461
10462     // ---------- engine actions  ---------------------------------------------
10463
10464     case CA_SET_ENGINE_SCAN_MODE:
10465     {
10466       InitPlayfieldScanMode(action_arg);
10467
10468       break;
10469     }
10470
10471     default:
10472       break;
10473   }
10474 }
10475
10476 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10477 {
10478   int old_element = Tile[x][y];
10479   int new_element = GetElementFromGroupElement(element);
10480   int previous_move_direction = MovDir[x][y];
10481   int last_ce_value = CustomValue[x][y];
10482   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10483   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10484   boolean add_player_onto_element = (new_element_is_player &&
10485                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10486                                      IS_WALKABLE(old_element));
10487
10488   if (!add_player_onto_element)
10489   {
10490     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10491       RemoveMovingField(x, y);
10492     else
10493       RemoveField(x, y);
10494
10495     Tile[x][y] = new_element;
10496
10497     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10498       MovDir[x][y] = previous_move_direction;
10499
10500     if (element_info[new_element].use_last_ce_value)
10501       CustomValue[x][y] = last_ce_value;
10502
10503     InitField_WithBug1(x, y, FALSE);
10504
10505     new_element = Tile[x][y];   // element may have changed
10506
10507     ResetGfxAnimation(x, y);
10508     ResetRandomAnimationValue(x, y);
10509
10510     TEST_DrawLevelField(x, y);
10511
10512     if (GFX_CRUMBLED(new_element))
10513       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10514   }
10515
10516   // check if element under the player changes from accessible to unaccessible
10517   // (needed for special case of dropping element which then changes)
10518   // (must be checked after creating new element for walkable group elements)
10519   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10520       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10521   {
10522     Bang(x, y);
10523
10524     return;
10525   }
10526
10527   // "ChangeCount" not set yet to allow "entered by player" change one time
10528   if (new_element_is_player)
10529     RelocatePlayer(x, y, new_element);
10530
10531   if (is_change)
10532     ChangeCount[x][y]++;        // count number of changes in the same frame
10533
10534   TestIfBadThingTouchesPlayer(x, y);
10535   TestIfPlayerTouchesCustomElement(x, y);
10536   TestIfElementTouchesCustomElement(x, y);
10537 }
10538
10539 static void CreateField(int x, int y, int element)
10540 {
10541   CreateFieldExt(x, y, element, FALSE);
10542 }
10543
10544 static void CreateElementFromChange(int x, int y, int element)
10545 {
10546   element = GET_VALID_RUNTIME_ELEMENT(element);
10547
10548   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10549   {
10550     int old_element = Tile[x][y];
10551
10552     // prevent changed element from moving in same engine frame
10553     // unless both old and new element can either fall or move
10554     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10555         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10556       Stop[x][y] = TRUE;
10557   }
10558
10559   CreateFieldExt(x, y, element, TRUE);
10560 }
10561
10562 static boolean ChangeElement(int x, int y, int element, int page)
10563 {
10564   struct ElementInfo *ei = &element_info[element];
10565   struct ElementChangeInfo *change = &ei->change_page[page];
10566   int ce_value = CustomValue[x][y];
10567   int ce_score = ei->collect_score;
10568   int target_element;
10569   int old_element = Tile[x][y];
10570
10571   // always use default change event to prevent running into a loop
10572   if (ChangeEvent[x][y] == -1)
10573     ChangeEvent[x][y] = CE_DELAY;
10574
10575   if (ChangeEvent[x][y] == CE_DELAY)
10576   {
10577     // reset actual trigger element, trigger player and action element
10578     change->actual_trigger_element = EL_EMPTY;
10579     change->actual_trigger_player = EL_EMPTY;
10580     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10581     change->actual_trigger_side = CH_SIDE_NONE;
10582     change->actual_trigger_ce_value = 0;
10583     change->actual_trigger_ce_score = 0;
10584   }
10585
10586   // do not change elements more than a specified maximum number of changes
10587   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10588     return FALSE;
10589
10590   ChangeCount[x][y]++;          // count number of changes in the same frame
10591
10592   if (change->explode)
10593   {
10594     Bang(x, y);
10595
10596     return TRUE;
10597   }
10598
10599   if (change->use_target_content)
10600   {
10601     boolean complete_replace = TRUE;
10602     boolean can_replace[3][3];
10603     int xx, yy;
10604
10605     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10606     {
10607       boolean is_empty;
10608       boolean is_walkable;
10609       boolean is_diggable;
10610       boolean is_collectible;
10611       boolean is_removable;
10612       boolean is_destructible;
10613       int ex = x + xx - 1;
10614       int ey = y + yy - 1;
10615       int content_element = change->target_content.e[xx][yy];
10616       int e;
10617
10618       can_replace[xx][yy] = TRUE;
10619
10620       if (ex == x && ey == y)   // do not check changing element itself
10621         continue;
10622
10623       if (content_element == EL_EMPTY_SPACE)
10624       {
10625         can_replace[xx][yy] = FALSE;    // do not replace border with space
10626
10627         continue;
10628       }
10629
10630       if (!IN_LEV_FIELD(ex, ey))
10631       {
10632         can_replace[xx][yy] = FALSE;
10633         complete_replace = FALSE;
10634
10635         continue;
10636       }
10637
10638       e = Tile[ex][ey];
10639
10640       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10641         e = MovingOrBlocked2Element(ex, ey);
10642
10643       is_empty = (IS_FREE(ex, ey) ||
10644                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10645
10646       is_walkable     = (is_empty || IS_WALKABLE(e));
10647       is_diggable     = (is_empty || IS_DIGGABLE(e));
10648       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10649       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10650       is_removable    = (is_diggable || is_collectible);
10651
10652       can_replace[xx][yy] =
10653         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10654           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10655           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10656           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10657           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10658           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10659          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10660
10661       if (!can_replace[xx][yy])
10662         complete_replace = FALSE;
10663     }
10664
10665     if (!change->only_if_complete || complete_replace)
10666     {
10667       boolean something_has_changed = FALSE;
10668
10669       if (change->only_if_complete && change->use_random_replace &&
10670           RND(100) < change->random_percentage)
10671         return FALSE;
10672
10673       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10674       {
10675         int ex = x + xx - 1;
10676         int ey = y + yy - 1;
10677         int content_element;
10678
10679         if (can_replace[xx][yy] && (!change->use_random_replace ||
10680                                     RND(100) < change->random_percentage))
10681         {
10682           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10683             RemoveMovingField(ex, ey);
10684
10685           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10686
10687           content_element = change->target_content.e[xx][yy];
10688           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10689                                               ce_value, ce_score);
10690
10691           CreateElementFromChange(ex, ey, target_element);
10692
10693           something_has_changed = TRUE;
10694
10695           // for symmetry reasons, freeze newly created border elements
10696           if (ex != x || ey != y)
10697             Stop[ex][ey] = TRUE;        // no more moving in this frame
10698         }
10699       }
10700
10701       if (something_has_changed)
10702       {
10703         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10704         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10705       }
10706     }
10707   }
10708   else
10709   {
10710     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10711                                         ce_value, ce_score);
10712
10713     if (element == EL_DIAGONAL_GROWING ||
10714         element == EL_DIAGONAL_SHRINKING)
10715     {
10716       target_element = Store[x][y];
10717
10718       Store[x][y] = EL_EMPTY;
10719     }
10720
10721     CreateElementFromChange(x, y, target_element);
10722
10723     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10724     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10725   }
10726
10727   // this uses direct change before indirect change
10728   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10729
10730   return TRUE;
10731 }
10732
10733 static void HandleElementChange(int x, int y, int page)
10734 {
10735   int element = MovingOrBlocked2Element(x, y);
10736   struct ElementInfo *ei = &element_info[element];
10737   struct ElementChangeInfo *change = &ei->change_page[page];
10738   boolean handle_action_before_change = FALSE;
10739
10740 #ifdef DEBUG
10741   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10742       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10743   {
10744     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10745           x, y, element, element_info[element].token_name);
10746     Debug("game:playing:HandleElementChange", "This should never happen!");
10747   }
10748 #endif
10749
10750   // this can happen with classic bombs on walkable, changing elements
10751   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10752   {
10753     return;
10754   }
10755
10756   if (ChangeDelay[x][y] == 0)           // initialize element change
10757   {
10758     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10759
10760     if (change->can_change)
10761     {
10762       // !!! not clear why graphic animation should be reset at all here !!!
10763       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10764       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10765
10766       /*
10767         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10768
10769         When using an animation frame delay of 1 (this only happens with
10770         "sp_zonk.moving.left/right" in the classic graphics), the default
10771         (non-moving) animation shows wrong animation frames (while the
10772         moving animation, like "sp_zonk.moving.left/right", is correct,
10773         so this graphical bug never shows up with the classic graphics).
10774         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10775         be drawn instead of the correct frames 0,1,2,3. This is caused by
10776         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10777         an element change: First when the change delay ("ChangeDelay[][]")
10778         counter has reached zero after decrementing, then a second time in
10779         the next frame (after "GfxFrame[][]" was already incremented) when
10780         "ChangeDelay[][]" is reset to the initial delay value again.
10781
10782         This causes frame 0 to be drawn twice, while the last frame won't
10783         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10784
10785         As some animations may already be cleverly designed around this bug
10786         (at least the "Snake Bite" snake tail animation does this), it cannot
10787         simply be fixed here without breaking such existing animations.
10788         Unfortunately, it cannot easily be detected if a graphics set was
10789         designed "before" or "after" the bug was fixed. As a workaround,
10790         a new graphics set option "game.graphics_engine_version" was added
10791         to be able to specify the game's major release version for which the
10792         graphics set was designed, which can then be used to decide if the
10793         bugfix should be used (version 4 and above) or not (version 3 or
10794         below, or if no version was specified at all, as with old sets).
10795
10796         (The wrong/fixed animation frames can be tested with the test level set
10797         "test_gfxframe" and level "000", which contains a specially prepared
10798         custom element at level position (x/y) == (11/9) which uses the zonk
10799         animation mentioned above. Using "game.graphics_engine_version: 4"
10800         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10801         This can also be seen from the debug output for this test element.)
10802       */
10803
10804       // when a custom element is about to change (for example by change delay),
10805       // do not reset graphic animation when the custom element is moving
10806       if (game.graphics_engine_version < 4 &&
10807           !IS_MOVING(x, y))
10808       {
10809         ResetGfxAnimation(x, y);
10810         ResetRandomAnimationValue(x, y);
10811       }
10812
10813       if (change->pre_change_function)
10814         change->pre_change_function(x, y);
10815     }
10816   }
10817
10818   ChangeDelay[x][y]--;
10819
10820   if (ChangeDelay[x][y] != 0)           // continue element change
10821   {
10822     if (change->can_change)
10823     {
10824       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10825
10826       if (IS_ANIMATED(graphic))
10827         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10828
10829       if (change->change_function)
10830         change->change_function(x, y);
10831     }
10832   }
10833   else                                  // finish element change
10834   {
10835     if (ChangePage[x][y] != -1)         // remember page from delayed change
10836     {
10837       page = ChangePage[x][y];
10838       ChangePage[x][y] = -1;
10839
10840       change = &ei->change_page[page];
10841     }
10842
10843     if (IS_MOVING(x, y))                // never change a running system ;-)
10844     {
10845       ChangeDelay[x][y] = 1;            // try change after next move step
10846       ChangePage[x][y] = page;          // remember page to use for change
10847
10848       return;
10849     }
10850
10851     // special case: set new level random seed before changing element
10852     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10853       handle_action_before_change = TRUE;
10854
10855     if (change->has_action && handle_action_before_change)
10856       ExecuteCustomElementAction(x, y, element, page);
10857
10858     if (change->can_change)
10859     {
10860       if (ChangeElement(x, y, element, page))
10861       {
10862         if (change->post_change_function)
10863           change->post_change_function(x, y);
10864       }
10865     }
10866
10867     if (change->has_action && !handle_action_before_change)
10868       ExecuteCustomElementAction(x, y, element, page);
10869   }
10870 }
10871
10872 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10873                                               int trigger_element,
10874                                               int trigger_event,
10875                                               int trigger_player,
10876                                               int trigger_side,
10877                                               int trigger_page)
10878 {
10879   boolean change_done_any = FALSE;
10880   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10881   int i;
10882
10883   if (!(trigger_events[trigger_element][trigger_event]))
10884     return FALSE;
10885
10886   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10887
10888   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10889   {
10890     int element = EL_CUSTOM_START + i;
10891     boolean change_done = FALSE;
10892     int p;
10893
10894     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10895         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10896       continue;
10897
10898     for (p = 0; p < element_info[element].num_change_pages; p++)
10899     {
10900       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10901
10902       if (change->can_change_or_has_action &&
10903           change->has_event[trigger_event] &&
10904           change->trigger_side & trigger_side &&
10905           change->trigger_player & trigger_player &&
10906           change->trigger_page & trigger_page_bits &&
10907           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10908       {
10909         change->actual_trigger_element = trigger_element;
10910         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10911         change->actual_trigger_player_bits = trigger_player;
10912         change->actual_trigger_side = trigger_side;
10913         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10914         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10915
10916         if ((change->can_change && !change_done) || change->has_action)
10917         {
10918           int x, y;
10919
10920           SCAN_PLAYFIELD(x, y)
10921           {
10922             if (Tile[x][y] == element)
10923             {
10924               if (change->can_change && !change_done)
10925               {
10926                 // if element already changed in this frame, not only prevent
10927                 // another element change (checked in ChangeElement()), but
10928                 // also prevent additional element actions for this element
10929
10930                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10931                     !level.use_action_after_change_bug)
10932                   continue;
10933
10934                 ChangeDelay[x][y] = 1;
10935                 ChangeEvent[x][y] = trigger_event;
10936
10937                 HandleElementChange(x, y, p);
10938               }
10939               else if (change->has_action)
10940               {
10941                 // if element already changed in this frame, not only prevent
10942                 // another element change (checked in ChangeElement()), but
10943                 // also prevent additional element actions for this element
10944
10945                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10946                     !level.use_action_after_change_bug)
10947                   continue;
10948
10949                 ExecuteCustomElementAction(x, y, element, p);
10950                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10951               }
10952             }
10953           }
10954
10955           if (change->can_change)
10956           {
10957             change_done = TRUE;
10958             change_done_any = TRUE;
10959           }
10960         }
10961       }
10962     }
10963   }
10964
10965   RECURSION_LOOP_DETECTION_END();
10966
10967   return change_done_any;
10968 }
10969
10970 static boolean CheckElementChangeExt(int x, int y,
10971                                      int element,
10972                                      int trigger_element,
10973                                      int trigger_event,
10974                                      int trigger_player,
10975                                      int trigger_side)
10976 {
10977   boolean change_done = FALSE;
10978   int p;
10979
10980   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10981       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10982     return FALSE;
10983
10984   if (Tile[x][y] == EL_BLOCKED)
10985   {
10986     Blocked2Moving(x, y, &x, &y);
10987     element = Tile[x][y];
10988   }
10989
10990   // check if element has already changed or is about to change after moving
10991   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10992        Tile[x][y] != element) ||
10993
10994       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10995        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10996         ChangePage[x][y] != -1)))
10997     return FALSE;
10998
10999   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11000
11001   for (p = 0; p < element_info[element].num_change_pages; p++)
11002   {
11003     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11004
11005     /* check trigger element for all events where the element that is checked
11006        for changing interacts with a directly adjacent element -- this is
11007        different to element changes that affect other elements to change on the
11008        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11009     boolean check_trigger_element =
11010       (trigger_event == CE_TOUCHING_X ||
11011        trigger_event == CE_HITTING_X ||
11012        trigger_event == CE_HIT_BY_X ||
11013        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11014
11015     if (change->can_change_or_has_action &&
11016         change->has_event[trigger_event] &&
11017         change->trigger_side & trigger_side &&
11018         change->trigger_player & trigger_player &&
11019         (!check_trigger_element ||
11020          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11021     {
11022       change->actual_trigger_element = trigger_element;
11023       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11024       change->actual_trigger_player_bits = trigger_player;
11025       change->actual_trigger_side = trigger_side;
11026       change->actual_trigger_ce_value = CustomValue[x][y];
11027       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11028
11029       // special case: trigger element not at (x,y) position for some events
11030       if (check_trigger_element)
11031       {
11032         static struct
11033         {
11034           int dx, dy;
11035         } move_xy[] =
11036           {
11037             {  0,  0 },
11038             { -1,  0 },
11039             { +1,  0 },
11040             {  0,  0 },
11041             {  0, -1 },
11042             {  0,  0 }, { 0, 0 }, { 0, 0 },
11043             {  0, +1 }
11044           };
11045
11046         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11047         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11048
11049         change->actual_trigger_ce_value = CustomValue[xx][yy];
11050         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11051       }
11052
11053       if (change->can_change && !change_done)
11054       {
11055         ChangeDelay[x][y] = 1;
11056         ChangeEvent[x][y] = trigger_event;
11057
11058         HandleElementChange(x, y, p);
11059
11060         change_done = TRUE;
11061       }
11062       else if (change->has_action)
11063       {
11064         ExecuteCustomElementAction(x, y, element, p);
11065         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11066       }
11067     }
11068   }
11069
11070   RECURSION_LOOP_DETECTION_END();
11071
11072   return change_done;
11073 }
11074
11075 static void PlayPlayerSound(struct PlayerInfo *player)
11076 {
11077   int jx = player->jx, jy = player->jy;
11078   int sound_element = player->artwork_element;
11079   int last_action = player->last_action_waiting;
11080   int action = player->action_waiting;
11081
11082   if (player->is_waiting)
11083   {
11084     if (action != last_action)
11085       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11086     else
11087       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11088   }
11089   else
11090   {
11091     if (action != last_action)
11092       StopSound(element_info[sound_element].sound[last_action]);
11093
11094     if (last_action == ACTION_SLEEPING)
11095       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11096   }
11097 }
11098
11099 static void PlayAllPlayersSound(void)
11100 {
11101   int i;
11102
11103   for (i = 0; i < MAX_PLAYERS; i++)
11104     if (stored_player[i].active)
11105       PlayPlayerSound(&stored_player[i]);
11106 }
11107
11108 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11109 {
11110   boolean last_waiting = player->is_waiting;
11111   int move_dir = player->MovDir;
11112
11113   player->dir_waiting = move_dir;
11114   player->last_action_waiting = player->action_waiting;
11115
11116   if (is_waiting)
11117   {
11118     if (!last_waiting)          // not waiting -> waiting
11119     {
11120       player->is_waiting = TRUE;
11121
11122       player->frame_counter_bored =
11123         FrameCounter +
11124         game.player_boring_delay_fixed +
11125         GetSimpleRandom(game.player_boring_delay_random);
11126       player->frame_counter_sleeping =
11127         FrameCounter +
11128         game.player_sleeping_delay_fixed +
11129         GetSimpleRandom(game.player_sleeping_delay_random);
11130
11131       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11132     }
11133
11134     if (game.player_sleeping_delay_fixed +
11135         game.player_sleeping_delay_random > 0 &&
11136         player->anim_delay_counter == 0 &&
11137         player->post_delay_counter == 0 &&
11138         FrameCounter >= player->frame_counter_sleeping)
11139       player->is_sleeping = TRUE;
11140     else if (game.player_boring_delay_fixed +
11141              game.player_boring_delay_random > 0 &&
11142              FrameCounter >= player->frame_counter_bored)
11143       player->is_bored = TRUE;
11144
11145     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11146                               player->is_bored ? ACTION_BORING :
11147                               ACTION_WAITING);
11148
11149     if (player->is_sleeping && player->use_murphy)
11150     {
11151       // special case for sleeping Murphy when leaning against non-free tile
11152
11153       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11154           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11155            !IS_MOVING(player->jx - 1, player->jy)))
11156         move_dir = MV_LEFT;
11157       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11158                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11159                 !IS_MOVING(player->jx + 1, player->jy)))
11160         move_dir = MV_RIGHT;
11161       else
11162         player->is_sleeping = FALSE;
11163
11164       player->dir_waiting = move_dir;
11165     }
11166
11167     if (player->is_sleeping)
11168     {
11169       if (player->num_special_action_sleeping > 0)
11170       {
11171         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11172         {
11173           int last_special_action = player->special_action_sleeping;
11174           int num_special_action = player->num_special_action_sleeping;
11175           int special_action =
11176             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11177              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11178              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11179              last_special_action + 1 : ACTION_SLEEPING);
11180           int special_graphic =
11181             el_act_dir2img(player->artwork_element, special_action, move_dir);
11182
11183           player->anim_delay_counter =
11184             graphic_info[special_graphic].anim_delay_fixed +
11185             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11186           player->post_delay_counter =
11187             graphic_info[special_graphic].post_delay_fixed +
11188             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11189
11190           player->special_action_sleeping = special_action;
11191         }
11192
11193         if (player->anim_delay_counter > 0)
11194         {
11195           player->action_waiting = player->special_action_sleeping;
11196           player->anim_delay_counter--;
11197         }
11198         else if (player->post_delay_counter > 0)
11199         {
11200           player->post_delay_counter--;
11201         }
11202       }
11203     }
11204     else if (player->is_bored)
11205     {
11206       if (player->num_special_action_bored > 0)
11207       {
11208         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11209         {
11210           int special_action =
11211             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11212           int special_graphic =
11213             el_act_dir2img(player->artwork_element, special_action, move_dir);
11214
11215           player->anim_delay_counter =
11216             graphic_info[special_graphic].anim_delay_fixed +
11217             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11218           player->post_delay_counter =
11219             graphic_info[special_graphic].post_delay_fixed +
11220             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11221
11222           player->special_action_bored = special_action;
11223         }
11224
11225         if (player->anim_delay_counter > 0)
11226         {
11227           player->action_waiting = player->special_action_bored;
11228           player->anim_delay_counter--;
11229         }
11230         else if (player->post_delay_counter > 0)
11231         {
11232           player->post_delay_counter--;
11233         }
11234       }
11235     }
11236   }
11237   else if (last_waiting)        // waiting -> not waiting
11238   {
11239     player->is_waiting = FALSE;
11240     player->is_bored = FALSE;
11241     player->is_sleeping = FALSE;
11242
11243     player->frame_counter_bored = -1;
11244     player->frame_counter_sleeping = -1;
11245
11246     player->anim_delay_counter = 0;
11247     player->post_delay_counter = 0;
11248
11249     player->dir_waiting = player->MovDir;
11250     player->action_waiting = ACTION_DEFAULT;
11251
11252     player->special_action_bored = ACTION_DEFAULT;
11253     player->special_action_sleeping = ACTION_DEFAULT;
11254   }
11255 }
11256
11257 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11258 {
11259   if ((!player->is_moving  && player->was_moving) ||
11260       (player->MovPos == 0 && player->was_moving) ||
11261       (player->is_snapping && !player->was_snapping) ||
11262       (player->is_dropping && !player->was_dropping))
11263   {
11264     if (!CheckSaveEngineSnapshotToList())
11265       return;
11266
11267     player->was_moving = FALSE;
11268     player->was_snapping = TRUE;
11269     player->was_dropping = TRUE;
11270   }
11271   else
11272   {
11273     if (player->is_moving)
11274       player->was_moving = TRUE;
11275
11276     if (!player->is_snapping)
11277       player->was_snapping = FALSE;
11278
11279     if (!player->is_dropping)
11280       player->was_dropping = FALSE;
11281   }
11282
11283   static struct MouseActionInfo mouse_action_last = { 0 };
11284   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11285   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11286
11287   if (new_released)
11288     CheckSaveEngineSnapshotToList();
11289
11290   mouse_action_last = mouse_action;
11291 }
11292
11293 static void CheckSingleStepMode(struct PlayerInfo *player)
11294 {
11295   if (tape.single_step && tape.recording && !tape.pausing)
11296   {
11297     // as it is called "single step mode", just return to pause mode when the
11298     // player stopped moving after one tile (or never starts moving at all)
11299     // (reverse logic needed here in case single step mode used in team mode)
11300     if (player->is_moving ||
11301         player->is_pushing ||
11302         player->is_dropping_pressed ||
11303         player->effective_mouse_action.button)
11304       game.enter_single_step_mode = FALSE;
11305   }
11306
11307   CheckSaveEngineSnapshot(player);
11308 }
11309
11310 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11311 {
11312   int left      = player_action & JOY_LEFT;
11313   int right     = player_action & JOY_RIGHT;
11314   int up        = player_action & JOY_UP;
11315   int down      = player_action & JOY_DOWN;
11316   int button1   = player_action & JOY_BUTTON_1;
11317   int button2   = player_action & JOY_BUTTON_2;
11318   int dx        = (left ? -1 : right ? 1 : 0);
11319   int dy        = (up   ? -1 : down  ? 1 : 0);
11320
11321   if (!player->active || tape.pausing)
11322     return 0;
11323
11324   if (player_action)
11325   {
11326     if (button1)
11327       SnapField(player, dx, dy);
11328     else
11329     {
11330       if (button2)
11331         DropElement(player);
11332
11333       MovePlayer(player, dx, dy);
11334     }
11335
11336     CheckSingleStepMode(player);
11337
11338     SetPlayerWaiting(player, FALSE);
11339
11340     return player_action;
11341   }
11342   else
11343   {
11344     // no actions for this player (no input at player's configured device)
11345
11346     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11347     SnapField(player, 0, 0);
11348     CheckGravityMovementWhenNotMoving(player);
11349
11350     if (player->MovPos == 0)
11351       SetPlayerWaiting(player, TRUE);
11352
11353     if (player->MovPos == 0)    // needed for tape.playing
11354       player->is_moving = FALSE;
11355
11356     player->is_dropping = FALSE;
11357     player->is_dropping_pressed = FALSE;
11358     player->drop_pressed_delay = 0;
11359
11360     CheckSingleStepMode(player);
11361
11362     return 0;
11363   }
11364 }
11365
11366 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11367                                          byte *tape_action)
11368 {
11369   if (!tape.use_mouse_actions)
11370     return;
11371
11372   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11373   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11374   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11375 }
11376
11377 static void SetTapeActionFromMouseAction(byte *tape_action,
11378                                          struct MouseActionInfo *mouse_action)
11379 {
11380   if (!tape.use_mouse_actions)
11381     return;
11382
11383   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11384   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11385   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11386 }
11387
11388 static void CheckLevelSolved(void)
11389 {
11390   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11391   {
11392     if (game_em.level_solved &&
11393         !game_em.game_over)                             // game won
11394     {
11395       LevelSolved();
11396
11397       game_em.game_over = TRUE;
11398
11399       game.all_players_gone = TRUE;
11400     }
11401
11402     if (game_em.game_over)                              // game lost
11403       game.all_players_gone = TRUE;
11404   }
11405   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11406   {
11407     if (game_sp.level_solved &&
11408         !game_sp.game_over)                             // game won
11409     {
11410       LevelSolved();
11411
11412       game_sp.game_over = TRUE;
11413
11414       game.all_players_gone = TRUE;
11415     }
11416
11417     if (game_sp.game_over)                              // game lost
11418       game.all_players_gone = TRUE;
11419   }
11420   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11421   {
11422     if (game_mm.level_solved &&
11423         !game_mm.game_over)                             // game won
11424     {
11425       LevelSolved();
11426
11427       game_mm.game_over = TRUE;
11428
11429       game.all_players_gone = TRUE;
11430     }
11431
11432     if (game_mm.game_over)                              // game lost
11433       game.all_players_gone = TRUE;
11434   }
11435 }
11436
11437 static void CheckLevelTime(void)
11438 {
11439   int i;
11440
11441   if (TimeFrames >= FRAMES_PER_SECOND)
11442   {
11443     TimeFrames = 0;
11444     TapeTime++;
11445
11446     for (i = 0; i < MAX_PLAYERS; i++)
11447     {
11448       struct PlayerInfo *player = &stored_player[i];
11449
11450       if (SHIELD_ON(player))
11451       {
11452         player->shield_normal_time_left--;
11453
11454         if (player->shield_deadly_time_left > 0)
11455           player->shield_deadly_time_left--;
11456       }
11457     }
11458
11459     if (!game.LevelSolved && !level.use_step_counter)
11460     {
11461       TimePlayed++;
11462
11463       if (TimeLeft > 0)
11464       {
11465         TimeLeft--;
11466
11467         if (TimeLeft <= 10 && setup.time_limit)
11468           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11469
11470         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11471            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11472
11473         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11474
11475         if (!TimeLeft && setup.time_limit)
11476         {
11477           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11478             game_em.lev->killed_out_of_time = TRUE;
11479           else
11480             for (i = 0; i < MAX_PLAYERS; i++)
11481               KillPlayer(&stored_player[i]);
11482         }
11483       }
11484       else if (game.no_time_limit && !game.all_players_gone)
11485       {
11486         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11487       }
11488
11489       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11490     }
11491
11492     if (tape.recording || tape.playing)
11493       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11494   }
11495
11496   if (tape.recording || tape.playing)
11497     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11498
11499   UpdateAndDisplayGameControlValues();
11500 }
11501
11502 void AdvanceFrameAndPlayerCounters(int player_nr)
11503 {
11504   int i;
11505
11506   // advance frame counters (global frame counter and time frame counter)
11507   FrameCounter++;
11508   TimeFrames++;
11509
11510   // advance player counters (counters for move delay, move animation etc.)
11511   for (i = 0; i < MAX_PLAYERS; i++)
11512   {
11513     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11514     int move_delay_value = stored_player[i].move_delay_value;
11515     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11516
11517     if (!advance_player_counters)       // not all players may be affected
11518       continue;
11519
11520     if (move_frames == 0)       // less than one move per game frame
11521     {
11522       int stepsize = TILEX / move_delay_value;
11523       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11524       int count = (stored_player[i].is_moving ?
11525                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11526
11527       if (count % delay == 0)
11528         move_frames = 1;
11529     }
11530
11531     stored_player[i].Frame += move_frames;
11532
11533     if (stored_player[i].MovPos != 0)
11534       stored_player[i].StepFrame += move_frames;
11535
11536     if (stored_player[i].move_delay > 0)
11537       stored_player[i].move_delay--;
11538
11539     // due to bugs in previous versions, counter must count up, not down
11540     if (stored_player[i].push_delay != -1)
11541       stored_player[i].push_delay++;
11542
11543     if (stored_player[i].drop_delay > 0)
11544       stored_player[i].drop_delay--;
11545
11546     if (stored_player[i].is_dropping_pressed)
11547       stored_player[i].drop_pressed_delay++;
11548   }
11549 }
11550
11551 void StartGameActions(boolean init_network_game, boolean record_tape,
11552                       int random_seed)
11553 {
11554   unsigned int new_random_seed = InitRND(random_seed);
11555
11556   if (record_tape)
11557     TapeStartRecording(new_random_seed);
11558
11559   if (init_network_game)
11560   {
11561     SendToServer_LevelFile();
11562     SendToServer_StartPlaying();
11563
11564     return;
11565   }
11566
11567   InitGame();
11568 }
11569
11570 static void GameActionsExt(void)
11571 {
11572 #if 0
11573   static unsigned int game_frame_delay = 0;
11574 #endif
11575   unsigned int game_frame_delay_value;
11576   byte *recorded_player_action;
11577   byte summarized_player_action = 0;
11578   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11579   int i;
11580
11581   // detect endless loops, caused by custom element programming
11582   if (recursion_loop_detected && recursion_loop_depth == 0)
11583   {
11584     char *message = getStringCat3("Internal Error! Element ",
11585                                   EL_NAME(recursion_loop_element),
11586                                   " caused endless loop! Quit the game?");
11587
11588     Warn("element '%s' caused endless loop in game engine",
11589          EL_NAME(recursion_loop_element));
11590
11591     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11592
11593     recursion_loop_detected = FALSE;    // if game should be continued
11594
11595     free(message);
11596
11597     return;
11598   }
11599
11600   if (game.restart_level)
11601     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11602
11603   CheckLevelSolved();
11604
11605   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11606     GameWon();
11607
11608   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11609     TapeStop();
11610
11611   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11612     return;
11613
11614   game_frame_delay_value =
11615     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11616
11617   if (tape.playing && tape.warp_forward && !tape.pausing)
11618     game_frame_delay_value = 0;
11619
11620   SetVideoFrameDelay(game_frame_delay_value);
11621
11622   // (de)activate virtual buttons depending on current game status
11623   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11624   {
11625     if (game.all_players_gone)  // if no players there to be controlled anymore
11626       SetOverlayActive(FALSE);
11627     else if (!tape.playing)     // if game continues after tape stopped playing
11628       SetOverlayActive(TRUE);
11629   }
11630
11631 #if 0
11632 #if 0
11633   // ---------- main game synchronization point ----------
11634
11635   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11636
11637   Debug("game:playing:skip", "skip == %d", skip);
11638
11639 #else
11640   // ---------- main game synchronization point ----------
11641
11642   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11643 #endif
11644 #endif
11645
11646   if (network_playing && !network_player_action_received)
11647   {
11648     // try to get network player actions in time
11649
11650     // last chance to get network player actions without main loop delay
11651     HandleNetworking();
11652
11653     // game was quit by network peer
11654     if (game_status != GAME_MODE_PLAYING)
11655       return;
11656
11657     // check if network player actions still missing and game still running
11658     if (!network_player_action_received && !checkGameEnded())
11659       return;           // failed to get network player actions in time
11660
11661     // do not yet reset "network_player_action_received" (for tape.pausing)
11662   }
11663
11664   if (tape.pausing)
11665     return;
11666
11667   // at this point we know that we really continue executing the game
11668
11669   network_player_action_received = FALSE;
11670
11671   // when playing tape, read previously recorded player input from tape data
11672   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11673
11674   local_player->effective_mouse_action = local_player->mouse_action;
11675
11676   if (recorded_player_action != NULL)
11677     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11678                                  recorded_player_action);
11679
11680   // TapePlayAction() may return NULL when toggling to "pause before death"
11681   if (tape.pausing)
11682     return;
11683
11684   if (tape.set_centered_player)
11685   {
11686     game.centered_player_nr_next = tape.centered_player_nr_next;
11687     game.set_centered_player = TRUE;
11688   }
11689
11690   for (i = 0; i < MAX_PLAYERS; i++)
11691   {
11692     summarized_player_action |= stored_player[i].action;
11693
11694     if (!network_playing && (game.team_mode || tape.playing))
11695       stored_player[i].effective_action = stored_player[i].action;
11696   }
11697
11698   if (network_playing && !checkGameEnded())
11699     SendToServer_MovePlayer(summarized_player_action);
11700
11701   // summarize all actions at local players mapped input device position
11702   // (this allows using different input devices in single player mode)
11703   if (!network.enabled && !game.team_mode)
11704     stored_player[map_player_action[local_player->index_nr]].effective_action =
11705       summarized_player_action;
11706
11707   // summarize all actions at centered player in local team mode
11708   if (tape.recording &&
11709       setup.team_mode && !network.enabled &&
11710       setup.input_on_focus &&
11711       game.centered_player_nr != -1)
11712   {
11713     for (i = 0; i < MAX_PLAYERS; i++)
11714       stored_player[map_player_action[i]].effective_action =
11715         (i == game.centered_player_nr ? summarized_player_action : 0);
11716   }
11717
11718   if (recorded_player_action != NULL)
11719     for (i = 0; i < MAX_PLAYERS; i++)
11720       stored_player[i].effective_action = recorded_player_action[i];
11721
11722   for (i = 0; i < MAX_PLAYERS; i++)
11723   {
11724     tape_action[i] = stored_player[i].effective_action;
11725
11726     /* (this may happen in the RND game engine if a player was not present on
11727        the playfield on level start, but appeared later from a custom element */
11728     if (setup.team_mode &&
11729         tape.recording &&
11730         tape_action[i] &&
11731         !tape.player_participates[i])
11732       tape.player_participates[i] = TRUE;
11733   }
11734
11735   SetTapeActionFromMouseAction(tape_action,
11736                                &local_player->effective_mouse_action);
11737
11738   // only record actions from input devices, but not programmed actions
11739   if (tape.recording)
11740     TapeRecordAction(tape_action);
11741
11742   // remember if game was played (especially after tape stopped playing)
11743   if (!tape.playing && summarized_player_action)
11744     game.GamePlayed = TRUE;
11745
11746 #if USE_NEW_PLAYER_ASSIGNMENTS
11747   // !!! also map player actions in single player mode !!!
11748   // if (game.team_mode)
11749   if (1)
11750   {
11751     byte mapped_action[MAX_PLAYERS];
11752
11753 #if DEBUG_PLAYER_ACTIONS
11754     for (i = 0; i < MAX_PLAYERS; i++)
11755       DebugContinued("", "%d, ", stored_player[i].effective_action);
11756 #endif
11757
11758     for (i = 0; i < MAX_PLAYERS; i++)
11759       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11760
11761     for (i = 0; i < MAX_PLAYERS; i++)
11762       stored_player[i].effective_action = mapped_action[i];
11763
11764 #if DEBUG_PLAYER_ACTIONS
11765     DebugContinued("", "=> ");
11766     for (i = 0; i < MAX_PLAYERS; i++)
11767       DebugContinued("", "%d, ", stored_player[i].effective_action);
11768     DebugContinued("game:playing:player", "\n");
11769 #endif
11770   }
11771 #if DEBUG_PLAYER_ACTIONS
11772   else
11773   {
11774     for (i = 0; i < MAX_PLAYERS; i++)
11775       DebugContinued("", "%d, ", stored_player[i].effective_action);
11776     DebugContinued("game:playing:player", "\n");
11777   }
11778 #endif
11779 #endif
11780
11781   for (i = 0; i < MAX_PLAYERS; i++)
11782   {
11783     // allow engine snapshot in case of changed movement attempt
11784     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11785         (stored_player[i].effective_action & KEY_MOTION))
11786       game.snapshot.changed_action = TRUE;
11787
11788     // allow engine snapshot in case of snapping/dropping attempt
11789     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11790         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11791       game.snapshot.changed_action = TRUE;
11792
11793     game.snapshot.last_action[i] = stored_player[i].effective_action;
11794   }
11795
11796   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11797   {
11798     GameActions_EM_Main();
11799   }
11800   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11801   {
11802     GameActions_SP_Main();
11803   }
11804   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11805   {
11806     GameActions_MM_Main();
11807   }
11808   else
11809   {
11810     GameActions_RND_Main();
11811   }
11812
11813   BlitScreenToBitmap(backbuffer);
11814
11815   CheckLevelSolved();
11816   CheckLevelTime();
11817
11818   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11819
11820   if (global.show_frames_per_second)
11821   {
11822     static unsigned int fps_counter = 0;
11823     static int fps_frames = 0;
11824     unsigned int fps_delay_ms = Counter() - fps_counter;
11825
11826     fps_frames++;
11827
11828     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11829     {
11830       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11831
11832       fps_frames = 0;
11833       fps_counter = Counter();
11834
11835       // always draw FPS to screen after FPS value was updated
11836       redraw_mask |= REDRAW_FPS;
11837     }
11838
11839     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11840     if (GetDrawDeactivationMask() == REDRAW_NONE)
11841       redraw_mask |= REDRAW_FPS;
11842   }
11843 }
11844
11845 static void GameActions_CheckSaveEngineSnapshot(void)
11846 {
11847   if (!game.snapshot.save_snapshot)
11848     return;
11849
11850   // clear flag for saving snapshot _before_ saving snapshot
11851   game.snapshot.save_snapshot = FALSE;
11852
11853   SaveEngineSnapshotToList();
11854 }
11855
11856 void GameActions(void)
11857 {
11858   GameActionsExt();
11859
11860   GameActions_CheckSaveEngineSnapshot();
11861 }
11862
11863 void GameActions_EM_Main(void)
11864 {
11865   byte effective_action[MAX_PLAYERS];
11866   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11867   int i;
11868
11869   for (i = 0; i < MAX_PLAYERS; i++)
11870     effective_action[i] = stored_player[i].effective_action;
11871
11872   GameActions_EM(effective_action, warp_mode);
11873 }
11874
11875 void GameActions_SP_Main(void)
11876 {
11877   byte effective_action[MAX_PLAYERS];
11878   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11879   int i;
11880
11881   for (i = 0; i < MAX_PLAYERS; i++)
11882     effective_action[i] = stored_player[i].effective_action;
11883
11884   GameActions_SP(effective_action, warp_mode);
11885
11886   for (i = 0; i < MAX_PLAYERS; i++)
11887   {
11888     if (stored_player[i].force_dropping)
11889       stored_player[i].action |= KEY_BUTTON_DROP;
11890
11891     stored_player[i].force_dropping = FALSE;
11892   }
11893 }
11894
11895 void GameActions_MM_Main(void)
11896 {
11897   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11898
11899   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11900 }
11901
11902 void GameActions_RND_Main(void)
11903 {
11904   GameActions_RND();
11905 }
11906
11907 void GameActions_RND(void)
11908 {
11909   static struct MouseActionInfo mouse_action_last = { 0 };
11910   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11911   int magic_wall_x = 0, magic_wall_y = 0;
11912   int i, x, y, element, graphic, last_gfx_frame;
11913
11914   InitPlayfieldScanModeVars();
11915
11916   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11917   {
11918     SCAN_PLAYFIELD(x, y)
11919     {
11920       ChangeCount[x][y] = 0;
11921       ChangeEvent[x][y] = -1;
11922     }
11923   }
11924
11925   if (game.set_centered_player)
11926   {
11927     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11928
11929     // switching to "all players" only possible if all players fit to screen
11930     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11931     {
11932       game.centered_player_nr_next = game.centered_player_nr;
11933       game.set_centered_player = FALSE;
11934     }
11935
11936     // do not switch focus to non-existing (or non-active) player
11937     if (game.centered_player_nr_next >= 0 &&
11938         !stored_player[game.centered_player_nr_next].active)
11939     {
11940       game.centered_player_nr_next = game.centered_player_nr;
11941       game.set_centered_player = FALSE;
11942     }
11943   }
11944
11945   if (game.set_centered_player &&
11946       ScreenMovPos == 0)        // screen currently aligned at tile position
11947   {
11948     int sx, sy;
11949
11950     if (game.centered_player_nr_next == -1)
11951     {
11952       setScreenCenteredToAllPlayers(&sx, &sy);
11953     }
11954     else
11955     {
11956       sx = stored_player[game.centered_player_nr_next].jx;
11957       sy = stored_player[game.centered_player_nr_next].jy;
11958     }
11959
11960     game.centered_player_nr = game.centered_player_nr_next;
11961     game.set_centered_player = FALSE;
11962
11963     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11964     DrawGameDoorValues();
11965   }
11966
11967   // check single step mode (set flag and clear again if any player is active)
11968   game.enter_single_step_mode =
11969     (tape.single_step && tape.recording && !tape.pausing);
11970
11971   for (i = 0; i < MAX_PLAYERS; i++)
11972   {
11973     int actual_player_action = stored_player[i].effective_action;
11974
11975 #if 1
11976     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11977        - rnd_equinox_tetrachloride 048
11978        - rnd_equinox_tetrachloride_ii 096
11979        - rnd_emanuel_schmieg 002
11980        - doctor_sloan_ww 001, 020
11981     */
11982     if (stored_player[i].MovPos == 0)
11983       CheckGravityMovement(&stored_player[i]);
11984 #endif
11985
11986     // overwrite programmed action with tape action
11987     if (stored_player[i].programmed_action)
11988       actual_player_action = stored_player[i].programmed_action;
11989
11990     PlayerActions(&stored_player[i], actual_player_action);
11991
11992     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11993   }
11994
11995   // single step pause mode may already have been toggled by "ScrollPlayer()"
11996   if (game.enter_single_step_mode && !tape.pausing)
11997     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11998
11999   ScrollScreen(NULL, SCROLL_GO_ON);
12000
12001   /* for backwards compatibility, the following code emulates a fixed bug that
12002      occured when pushing elements (causing elements that just made their last
12003      pushing step to already (if possible) make their first falling step in the
12004      same game frame, which is bad); this code is also needed to use the famous
12005      "spring push bug" which is used in older levels and might be wanted to be
12006      used also in newer levels, but in this case the buggy pushing code is only
12007      affecting the "spring" element and no other elements */
12008
12009   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12010   {
12011     for (i = 0; i < MAX_PLAYERS; i++)
12012     {
12013       struct PlayerInfo *player = &stored_player[i];
12014       int x = player->jx;
12015       int y = player->jy;
12016
12017       if (player->active && player->is_pushing && player->is_moving &&
12018           IS_MOVING(x, y) &&
12019           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12020            Tile[x][y] == EL_SPRING))
12021       {
12022         ContinueMoving(x, y);
12023
12024         // continue moving after pushing (this is actually a bug)
12025         if (!IS_MOVING(x, y))
12026           Stop[x][y] = FALSE;
12027       }
12028     }
12029   }
12030
12031   SCAN_PLAYFIELD(x, y)
12032   {
12033     Last[x][y] = Tile[x][y];
12034
12035     ChangeCount[x][y] = 0;
12036     ChangeEvent[x][y] = -1;
12037
12038     // this must be handled before main playfield loop
12039     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12040     {
12041       MovDelay[x][y]--;
12042       if (MovDelay[x][y] <= 0)
12043         RemoveField(x, y);
12044     }
12045
12046     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12047     {
12048       MovDelay[x][y]--;
12049       if (MovDelay[x][y] <= 0)
12050       {
12051         int element = Store[x][y];
12052         int move_direction = MovDir[x][y];
12053         int player_index_bit = Store2[x][y];
12054
12055         Store[x][y] = 0;
12056         Store2[x][y] = 0;
12057
12058         RemoveField(x, y);
12059         TEST_DrawLevelField(x, y);
12060
12061         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12062       }
12063     }
12064
12065 #if DEBUG
12066     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12067     {
12068       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12069             x, y);
12070       Debug("game:playing:GameActions_RND", "This should never happen!");
12071
12072       ChangePage[x][y] = -1;
12073     }
12074 #endif
12075
12076     Stop[x][y] = FALSE;
12077     if (WasJustMoving[x][y] > 0)
12078       WasJustMoving[x][y]--;
12079     if (WasJustFalling[x][y] > 0)
12080       WasJustFalling[x][y]--;
12081     if (CheckCollision[x][y] > 0)
12082       CheckCollision[x][y]--;
12083     if (CheckImpact[x][y] > 0)
12084       CheckImpact[x][y]--;
12085
12086     GfxFrame[x][y]++;
12087
12088     /* reset finished pushing action (not done in ContinueMoving() to allow
12089        continuous pushing animation for elements with zero push delay) */
12090     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12091     {
12092       ResetGfxAnimation(x, y);
12093       TEST_DrawLevelField(x, y);
12094     }
12095
12096 #if DEBUG
12097     if (IS_BLOCKED(x, y))
12098     {
12099       int oldx, oldy;
12100
12101       Blocked2Moving(x, y, &oldx, &oldy);
12102       if (!IS_MOVING(oldx, oldy))
12103       {
12104         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12105         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12106         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12107         Debug("game:playing:GameActions_RND", "This should never happen!");
12108       }
12109     }
12110 #endif
12111   }
12112
12113   if (mouse_action.button)
12114   {
12115     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12116
12117     x = mouse_action.lx;
12118     y = mouse_action.ly;
12119     element = Tile[x][y];
12120
12121     if (new_button)
12122     {
12123       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12124       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12125     }
12126
12127     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12128     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12129   }
12130
12131   SCAN_PLAYFIELD(x, y)
12132   {
12133     element = Tile[x][y];
12134     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12135     last_gfx_frame = GfxFrame[x][y];
12136
12137     ResetGfxFrame(x, y);
12138
12139     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12140       DrawLevelGraphicAnimation(x, y, graphic);
12141
12142     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12143         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12144       ResetRandomAnimationValue(x, y);
12145
12146     SetRandomAnimationValue(x, y);
12147
12148     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12149
12150     if (IS_INACTIVE(element))
12151     {
12152       if (IS_ANIMATED(graphic))
12153         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12154
12155       continue;
12156     }
12157
12158     // this may take place after moving, so 'element' may have changed
12159     if (IS_CHANGING(x, y) &&
12160         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12161     {
12162       int page = element_info[element].event_page_nr[CE_DELAY];
12163
12164       HandleElementChange(x, y, page);
12165
12166       element = Tile[x][y];
12167       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12168     }
12169
12170     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12171     {
12172       StartMoving(x, y);
12173
12174       element = Tile[x][y];
12175       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12176
12177       if (IS_ANIMATED(graphic) &&
12178           !IS_MOVING(x, y) &&
12179           !Stop[x][y])
12180         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12181
12182       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12183         TEST_DrawTwinkleOnField(x, y);
12184     }
12185     else if (element == EL_ACID)
12186     {
12187       if (!Stop[x][y])
12188         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12189     }
12190     else if ((element == EL_EXIT_OPEN ||
12191               element == EL_EM_EXIT_OPEN ||
12192               element == EL_SP_EXIT_OPEN ||
12193               element == EL_STEEL_EXIT_OPEN ||
12194               element == EL_EM_STEEL_EXIT_OPEN ||
12195               element == EL_SP_TERMINAL ||
12196               element == EL_SP_TERMINAL_ACTIVE ||
12197               element == EL_EXTRA_TIME ||
12198               element == EL_SHIELD_NORMAL ||
12199               element == EL_SHIELD_DEADLY) &&
12200              IS_ANIMATED(graphic))
12201       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12202     else if (IS_MOVING(x, y))
12203       ContinueMoving(x, y);
12204     else if (IS_ACTIVE_BOMB(element))
12205       CheckDynamite(x, y);
12206     else if (element == EL_AMOEBA_GROWING)
12207       AmoebaGrowing(x, y);
12208     else if (element == EL_AMOEBA_SHRINKING)
12209       AmoebaShrinking(x, y);
12210
12211 #if !USE_NEW_AMOEBA_CODE
12212     else if (IS_AMOEBALIVE(element))
12213       AmoebaReproduce(x, y);
12214 #endif
12215
12216     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12217       Life(x, y);
12218     else if (element == EL_EXIT_CLOSED)
12219       CheckExit(x, y);
12220     else if (element == EL_EM_EXIT_CLOSED)
12221       CheckExitEM(x, y);
12222     else if (element == EL_STEEL_EXIT_CLOSED)
12223       CheckExitSteel(x, y);
12224     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12225       CheckExitSteelEM(x, y);
12226     else if (element == EL_SP_EXIT_CLOSED)
12227       CheckExitSP(x, y);
12228     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12229              element == EL_EXPANDABLE_STEELWALL_GROWING)
12230       MauerWaechst(x, y);
12231     else if (element == EL_EXPANDABLE_WALL ||
12232              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12233              element == EL_EXPANDABLE_WALL_VERTICAL ||
12234              element == EL_EXPANDABLE_WALL_ANY ||
12235              element == EL_BD_EXPANDABLE_WALL)
12236       MauerAbleger(x, y);
12237     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12238              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12239              element == EL_EXPANDABLE_STEELWALL_ANY)
12240       MauerAblegerStahl(x, y);
12241     else if (element == EL_FLAMES)
12242       CheckForDragon(x, y);
12243     else if (element == EL_EXPLOSION)
12244       ; // drawing of correct explosion animation is handled separately
12245     else if (element == EL_ELEMENT_SNAPPING ||
12246              element == EL_DIAGONAL_SHRINKING ||
12247              element == EL_DIAGONAL_GROWING)
12248     {
12249       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12250
12251       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12252     }
12253     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12254       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12255
12256     if (IS_BELT_ACTIVE(element))
12257       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12258
12259     if (game.magic_wall_active)
12260     {
12261       int jx = local_player->jx, jy = local_player->jy;
12262
12263       // play the element sound at the position nearest to the player
12264       if ((element == EL_MAGIC_WALL_FULL ||
12265            element == EL_MAGIC_WALL_ACTIVE ||
12266            element == EL_MAGIC_WALL_EMPTYING ||
12267            element == EL_BD_MAGIC_WALL_FULL ||
12268            element == EL_BD_MAGIC_WALL_ACTIVE ||
12269            element == EL_BD_MAGIC_WALL_EMPTYING ||
12270            element == EL_DC_MAGIC_WALL_FULL ||
12271            element == EL_DC_MAGIC_WALL_ACTIVE ||
12272            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12273           ABS(x - jx) + ABS(y - jy) <
12274           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12275       {
12276         magic_wall_x = x;
12277         magic_wall_y = y;
12278       }
12279     }
12280   }
12281
12282 #if USE_NEW_AMOEBA_CODE
12283   // new experimental amoeba growth stuff
12284   if (!(FrameCounter % 8))
12285   {
12286     static unsigned int random = 1684108901;
12287
12288     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12289     {
12290       x = RND(lev_fieldx);
12291       y = RND(lev_fieldy);
12292       element = Tile[x][y];
12293
12294       if (!IS_PLAYER(x,y) &&
12295           (element == EL_EMPTY ||
12296            CAN_GROW_INTO(element) ||
12297            element == EL_QUICKSAND_EMPTY ||
12298            element == EL_QUICKSAND_FAST_EMPTY ||
12299            element == EL_ACID_SPLASH_LEFT ||
12300            element == EL_ACID_SPLASH_RIGHT))
12301       {
12302         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12303             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12304             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12305             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12306           Tile[x][y] = EL_AMOEBA_DROP;
12307       }
12308
12309       random = random * 129 + 1;
12310     }
12311   }
12312 #endif
12313
12314   game.explosions_delayed = FALSE;
12315
12316   SCAN_PLAYFIELD(x, y)
12317   {
12318     element = Tile[x][y];
12319
12320     if (ExplodeField[x][y])
12321       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12322     else if (element == EL_EXPLOSION)
12323       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12324
12325     ExplodeField[x][y] = EX_TYPE_NONE;
12326   }
12327
12328   game.explosions_delayed = TRUE;
12329
12330   if (game.magic_wall_active)
12331   {
12332     if (!(game.magic_wall_time_left % 4))
12333     {
12334       int element = Tile[magic_wall_x][magic_wall_y];
12335
12336       if (element == EL_BD_MAGIC_WALL_FULL ||
12337           element == EL_BD_MAGIC_WALL_ACTIVE ||
12338           element == EL_BD_MAGIC_WALL_EMPTYING)
12339         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12340       else if (element == EL_DC_MAGIC_WALL_FULL ||
12341                element == EL_DC_MAGIC_WALL_ACTIVE ||
12342                element == EL_DC_MAGIC_WALL_EMPTYING)
12343         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12344       else
12345         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12346     }
12347
12348     if (game.magic_wall_time_left > 0)
12349     {
12350       game.magic_wall_time_left--;
12351
12352       if (!game.magic_wall_time_left)
12353       {
12354         SCAN_PLAYFIELD(x, y)
12355         {
12356           element = Tile[x][y];
12357
12358           if (element == EL_MAGIC_WALL_ACTIVE ||
12359               element == EL_MAGIC_WALL_FULL)
12360           {
12361             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12362             TEST_DrawLevelField(x, y);
12363           }
12364           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12365                    element == EL_BD_MAGIC_WALL_FULL)
12366           {
12367             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12368             TEST_DrawLevelField(x, y);
12369           }
12370           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12371                    element == EL_DC_MAGIC_WALL_FULL)
12372           {
12373             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12374             TEST_DrawLevelField(x, y);
12375           }
12376         }
12377
12378         game.magic_wall_active = FALSE;
12379       }
12380     }
12381   }
12382
12383   if (game.light_time_left > 0)
12384   {
12385     game.light_time_left--;
12386
12387     if (game.light_time_left == 0)
12388       RedrawAllLightSwitchesAndInvisibleElements();
12389   }
12390
12391   if (game.timegate_time_left > 0)
12392   {
12393     game.timegate_time_left--;
12394
12395     if (game.timegate_time_left == 0)
12396       CloseAllOpenTimegates();
12397   }
12398
12399   if (game.lenses_time_left > 0)
12400   {
12401     game.lenses_time_left--;
12402
12403     if (game.lenses_time_left == 0)
12404       RedrawAllInvisibleElementsForLenses();
12405   }
12406
12407   if (game.magnify_time_left > 0)
12408   {
12409     game.magnify_time_left--;
12410
12411     if (game.magnify_time_left == 0)
12412       RedrawAllInvisibleElementsForMagnifier();
12413   }
12414
12415   for (i = 0; i < MAX_PLAYERS; i++)
12416   {
12417     struct PlayerInfo *player = &stored_player[i];
12418
12419     if (SHIELD_ON(player))
12420     {
12421       if (player->shield_deadly_time_left)
12422         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12423       else if (player->shield_normal_time_left)
12424         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12425     }
12426   }
12427
12428 #if USE_DELAYED_GFX_REDRAW
12429   SCAN_PLAYFIELD(x, y)
12430   {
12431     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12432     {
12433       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12434          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12435
12436       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12437         DrawLevelField(x, y);
12438
12439       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12440         DrawLevelFieldCrumbled(x, y);
12441
12442       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12443         DrawLevelFieldCrumbledNeighbours(x, y);
12444
12445       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12446         DrawTwinkleOnField(x, y);
12447     }
12448
12449     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12450   }
12451 #endif
12452
12453   DrawAllPlayers();
12454   PlayAllPlayersSound();
12455
12456   for (i = 0; i < MAX_PLAYERS; i++)
12457   {
12458     struct PlayerInfo *player = &stored_player[i];
12459
12460     if (player->show_envelope != 0 && (!player->active ||
12461                                        player->MovPos == 0))
12462     {
12463       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12464
12465       player->show_envelope = 0;
12466     }
12467   }
12468
12469   // use random number generator in every frame to make it less predictable
12470   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12471     RND(1);
12472
12473   mouse_action_last = mouse_action;
12474 }
12475
12476 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12477 {
12478   int min_x = x, min_y = y, max_x = x, max_y = y;
12479   int scr_fieldx = getScreenFieldSizeX();
12480   int scr_fieldy = getScreenFieldSizeY();
12481   int i;
12482
12483   for (i = 0; i < MAX_PLAYERS; i++)
12484   {
12485     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12486
12487     if (!stored_player[i].active || &stored_player[i] == player)
12488       continue;
12489
12490     min_x = MIN(min_x, jx);
12491     min_y = MIN(min_y, jy);
12492     max_x = MAX(max_x, jx);
12493     max_y = MAX(max_y, jy);
12494   }
12495
12496   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12497 }
12498
12499 static boolean AllPlayersInVisibleScreen(void)
12500 {
12501   int i;
12502
12503   for (i = 0; i < MAX_PLAYERS; i++)
12504   {
12505     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12506
12507     if (!stored_player[i].active)
12508       continue;
12509
12510     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12511       return FALSE;
12512   }
12513
12514   return TRUE;
12515 }
12516
12517 void ScrollLevel(int dx, int dy)
12518 {
12519   int scroll_offset = 2 * TILEX_VAR;
12520   int x, y;
12521
12522   BlitBitmap(drawto_field, drawto_field,
12523              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12524              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12525              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12526              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12527              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12528              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12529
12530   if (dx != 0)
12531   {
12532     x = (dx == 1 ? BX1 : BX2);
12533     for (y = BY1; y <= BY2; y++)
12534       DrawScreenField(x, y);
12535   }
12536
12537   if (dy != 0)
12538   {
12539     y = (dy == 1 ? BY1 : BY2);
12540     for (x = BX1; x <= BX2; x++)
12541       DrawScreenField(x, y);
12542   }
12543
12544   redraw_mask |= REDRAW_FIELD;
12545 }
12546
12547 static boolean canFallDown(struct PlayerInfo *player)
12548 {
12549   int jx = player->jx, jy = player->jy;
12550
12551   return (IN_LEV_FIELD(jx, jy + 1) &&
12552           (IS_FREE(jx, jy + 1) ||
12553            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12554           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12555           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12556 }
12557
12558 static boolean canPassField(int x, int y, int move_dir)
12559 {
12560   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12561   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12562   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12563   int nextx = x + dx;
12564   int nexty = y + dy;
12565   int element = Tile[x][y];
12566
12567   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12568           !CAN_MOVE(element) &&
12569           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12570           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12571           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12572 }
12573
12574 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12575 {
12576   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12577   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12578   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12579   int newx = x + dx;
12580   int newy = y + dy;
12581
12582   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12583           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12584           (IS_DIGGABLE(Tile[newx][newy]) ||
12585            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12586            canPassField(newx, newy, move_dir)));
12587 }
12588
12589 static void CheckGravityMovement(struct PlayerInfo *player)
12590 {
12591   if (player->gravity && !player->programmed_action)
12592   {
12593     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12594     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12595     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12596     int jx = player->jx, jy = player->jy;
12597     boolean player_is_moving_to_valid_field =
12598       (!player_is_snapping &&
12599        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12600         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12601     boolean player_can_fall_down = canFallDown(player);
12602
12603     if (player_can_fall_down &&
12604         !player_is_moving_to_valid_field)
12605       player->programmed_action = MV_DOWN;
12606   }
12607 }
12608
12609 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12610 {
12611   return CheckGravityMovement(player);
12612
12613   if (player->gravity && !player->programmed_action)
12614   {
12615     int jx = player->jx, jy = player->jy;
12616     boolean field_under_player_is_free =
12617       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12618     boolean player_is_standing_on_valid_field =
12619       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12620        (IS_WALKABLE(Tile[jx][jy]) &&
12621         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12622
12623     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12624       player->programmed_action = MV_DOWN;
12625   }
12626 }
12627
12628 /*
12629   MovePlayerOneStep()
12630   -----------------------------------------------------------------------------
12631   dx, dy:               direction (non-diagonal) to try to move the player to
12632   real_dx, real_dy:     direction as read from input device (can be diagonal)
12633 */
12634
12635 boolean MovePlayerOneStep(struct PlayerInfo *player,
12636                           int dx, int dy, int real_dx, int real_dy)
12637 {
12638   int jx = player->jx, jy = player->jy;
12639   int new_jx = jx + dx, new_jy = jy + dy;
12640   int can_move;
12641   boolean player_can_move = !player->cannot_move;
12642
12643   if (!player->active || (!dx && !dy))
12644     return MP_NO_ACTION;
12645
12646   player->MovDir = (dx < 0 ? MV_LEFT :
12647                     dx > 0 ? MV_RIGHT :
12648                     dy < 0 ? MV_UP :
12649                     dy > 0 ? MV_DOWN :  MV_NONE);
12650
12651   if (!IN_LEV_FIELD(new_jx, new_jy))
12652     return MP_NO_ACTION;
12653
12654   if (!player_can_move)
12655   {
12656     if (player->MovPos == 0)
12657     {
12658       player->is_moving = FALSE;
12659       player->is_digging = FALSE;
12660       player->is_collecting = FALSE;
12661       player->is_snapping = FALSE;
12662       player->is_pushing = FALSE;
12663     }
12664   }
12665
12666   if (!network.enabled && game.centered_player_nr == -1 &&
12667       !AllPlayersInSight(player, new_jx, new_jy))
12668     return MP_NO_ACTION;
12669
12670   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12671   if (can_move != MP_MOVING)
12672     return can_move;
12673
12674   // check if DigField() has caused relocation of the player
12675   if (player->jx != jx || player->jy != jy)
12676     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12677
12678   StorePlayer[jx][jy] = 0;
12679   player->last_jx = jx;
12680   player->last_jy = jy;
12681   player->jx = new_jx;
12682   player->jy = new_jy;
12683   StorePlayer[new_jx][new_jy] = player->element_nr;
12684
12685   if (player->move_delay_value_next != -1)
12686   {
12687     player->move_delay_value = player->move_delay_value_next;
12688     player->move_delay_value_next = -1;
12689   }
12690
12691   player->MovPos =
12692     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12693
12694   player->step_counter++;
12695
12696   PlayerVisit[jx][jy] = FrameCounter;
12697
12698   player->is_moving = TRUE;
12699
12700 #if 1
12701   // should better be called in MovePlayer(), but this breaks some tapes
12702   ScrollPlayer(player, SCROLL_INIT);
12703 #endif
12704
12705   return MP_MOVING;
12706 }
12707
12708 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12709 {
12710   int jx = player->jx, jy = player->jy;
12711   int old_jx = jx, old_jy = jy;
12712   int moved = MP_NO_ACTION;
12713
12714   if (!player->active)
12715     return FALSE;
12716
12717   if (!dx && !dy)
12718   {
12719     if (player->MovPos == 0)
12720     {
12721       player->is_moving = FALSE;
12722       player->is_digging = FALSE;
12723       player->is_collecting = FALSE;
12724       player->is_snapping = FALSE;
12725       player->is_pushing = FALSE;
12726     }
12727
12728     return FALSE;
12729   }
12730
12731   if (player->move_delay > 0)
12732     return FALSE;
12733
12734   player->move_delay = -1;              // set to "uninitialized" value
12735
12736   // store if player is automatically moved to next field
12737   player->is_auto_moving = (player->programmed_action != MV_NONE);
12738
12739   // remove the last programmed player action
12740   player->programmed_action = 0;
12741
12742   if (player->MovPos)
12743   {
12744     // should only happen if pre-1.2 tape recordings are played
12745     // this is only for backward compatibility
12746
12747     int original_move_delay_value = player->move_delay_value;
12748
12749 #if DEBUG
12750     Debug("game:playing:MovePlayer",
12751           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12752           tape.counter);
12753 #endif
12754
12755     // scroll remaining steps with finest movement resolution
12756     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12757
12758     while (player->MovPos)
12759     {
12760       ScrollPlayer(player, SCROLL_GO_ON);
12761       ScrollScreen(NULL, SCROLL_GO_ON);
12762
12763       AdvanceFrameAndPlayerCounters(player->index_nr);
12764
12765       DrawAllPlayers();
12766       BackToFront_WithFrameDelay(0);
12767     }
12768
12769     player->move_delay_value = original_move_delay_value;
12770   }
12771
12772   player->is_active = FALSE;
12773
12774   if (player->last_move_dir & MV_HORIZONTAL)
12775   {
12776     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12777       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12778   }
12779   else
12780   {
12781     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12782       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12783   }
12784
12785   if (!moved && !player->is_active)
12786   {
12787     player->is_moving = FALSE;
12788     player->is_digging = FALSE;
12789     player->is_collecting = FALSE;
12790     player->is_snapping = FALSE;
12791     player->is_pushing = FALSE;
12792   }
12793
12794   jx = player->jx;
12795   jy = player->jy;
12796
12797   if (moved & MP_MOVING && !ScreenMovPos &&
12798       (player->index_nr == game.centered_player_nr ||
12799        game.centered_player_nr == -1))
12800   {
12801     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12802
12803     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12804     {
12805       // actual player has left the screen -- scroll in that direction
12806       if (jx != old_jx)         // player has moved horizontally
12807         scroll_x += (jx - old_jx);
12808       else                      // player has moved vertically
12809         scroll_y += (jy - old_jy);
12810     }
12811     else
12812     {
12813       int offset_raw = game.scroll_delay_value;
12814
12815       if (jx != old_jx)         // player has moved horizontally
12816       {
12817         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12818         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12819         int new_scroll_x = jx - MIDPOSX + offset_x;
12820
12821         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12822             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12823           scroll_x = new_scroll_x;
12824
12825         // don't scroll over playfield boundaries
12826         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12827
12828         // don't scroll more than one field at a time
12829         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12830
12831         // don't scroll against the player's moving direction
12832         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12833             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12834           scroll_x = old_scroll_x;
12835       }
12836       else                      // player has moved vertically
12837       {
12838         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12839         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12840         int new_scroll_y = jy - MIDPOSY + offset_y;
12841
12842         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12843             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12844           scroll_y = new_scroll_y;
12845
12846         // don't scroll over playfield boundaries
12847         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12848
12849         // don't scroll more than one field at a time
12850         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12851
12852         // don't scroll against the player's moving direction
12853         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12854             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12855           scroll_y = old_scroll_y;
12856       }
12857     }
12858
12859     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12860     {
12861       if (!network.enabled && game.centered_player_nr == -1 &&
12862           !AllPlayersInVisibleScreen())
12863       {
12864         scroll_x = old_scroll_x;
12865         scroll_y = old_scroll_y;
12866       }
12867       else
12868       {
12869         ScrollScreen(player, SCROLL_INIT);
12870         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12871       }
12872     }
12873   }
12874
12875   player->StepFrame = 0;
12876
12877   if (moved & MP_MOVING)
12878   {
12879     if (old_jx != jx && old_jy == jy)
12880       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12881     else if (old_jx == jx && old_jy != jy)
12882       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12883
12884     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12885
12886     player->last_move_dir = player->MovDir;
12887     player->is_moving = TRUE;
12888     player->is_snapping = FALSE;
12889     player->is_switching = FALSE;
12890     player->is_dropping = FALSE;
12891     player->is_dropping_pressed = FALSE;
12892     player->drop_pressed_delay = 0;
12893
12894 #if 0
12895     // should better be called here than above, but this breaks some tapes
12896     ScrollPlayer(player, SCROLL_INIT);
12897 #endif
12898   }
12899   else
12900   {
12901     CheckGravityMovementWhenNotMoving(player);
12902
12903     player->is_moving = FALSE;
12904
12905     /* at this point, the player is allowed to move, but cannot move right now
12906        (e.g. because of something blocking the way) -- ensure that the player
12907        is also allowed to move in the next frame (in old versions before 3.1.1,
12908        the player was forced to wait again for eight frames before next try) */
12909
12910     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12911       player->move_delay = 0;   // allow direct movement in the next frame
12912   }
12913
12914   if (player->move_delay == -1)         // not yet initialized by DigField()
12915     player->move_delay = player->move_delay_value;
12916
12917   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12918   {
12919     TestIfPlayerTouchesBadThing(jx, jy);
12920     TestIfPlayerTouchesCustomElement(jx, jy);
12921   }
12922
12923   if (!player->active)
12924     RemovePlayer(player);
12925
12926   return moved;
12927 }
12928
12929 void ScrollPlayer(struct PlayerInfo *player, int mode)
12930 {
12931   int jx = player->jx, jy = player->jy;
12932   int last_jx = player->last_jx, last_jy = player->last_jy;
12933   int move_stepsize = TILEX / player->move_delay_value;
12934
12935   if (!player->active)
12936     return;
12937
12938   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12939     return;
12940
12941   if (mode == SCROLL_INIT)
12942   {
12943     player->actual_frame_counter = FrameCounter;
12944     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12945
12946     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12947         Tile[last_jx][last_jy] == EL_EMPTY)
12948     {
12949       int last_field_block_delay = 0;   // start with no blocking at all
12950       int block_delay_adjustment = player->block_delay_adjustment;
12951
12952       // if player blocks last field, add delay for exactly one move
12953       if (player->block_last_field)
12954       {
12955         last_field_block_delay += player->move_delay_value;
12956
12957         // when blocking enabled, prevent moving up despite gravity
12958         if (player->gravity && player->MovDir == MV_UP)
12959           block_delay_adjustment = -1;
12960       }
12961
12962       // add block delay adjustment (also possible when not blocking)
12963       last_field_block_delay += block_delay_adjustment;
12964
12965       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12966       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12967     }
12968
12969     if (player->MovPos != 0)    // player has not yet reached destination
12970       return;
12971   }
12972   else if (!FrameReached(&player->actual_frame_counter, 1))
12973     return;
12974
12975   if (player->MovPos != 0)
12976   {
12977     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12978     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12979
12980     // before DrawPlayer() to draw correct player graphic for this case
12981     if (player->MovPos == 0)
12982       CheckGravityMovement(player);
12983   }
12984
12985   if (player->MovPos == 0)      // player reached destination field
12986   {
12987     if (player->move_delay_reset_counter > 0)
12988     {
12989       player->move_delay_reset_counter--;
12990
12991       if (player->move_delay_reset_counter == 0)
12992       {
12993         // continue with normal speed after quickly moving through gate
12994         HALVE_PLAYER_SPEED(player);
12995
12996         // be able to make the next move without delay
12997         player->move_delay = 0;
12998       }
12999     }
13000
13001     player->last_jx = jx;
13002     player->last_jy = jy;
13003
13004     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13005         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13006         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13007         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13008         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13009         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13010         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13011         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13012     {
13013       ExitPlayer(player);
13014
13015       if (game.players_still_needed == 0 &&
13016           (game.friends_still_needed == 0 ||
13017            IS_SP_ELEMENT(Tile[jx][jy])))
13018         LevelSolved();
13019     }
13020
13021     // this breaks one level: "machine", level 000
13022     {
13023       int move_direction = player->MovDir;
13024       int enter_side = MV_DIR_OPPOSITE(move_direction);
13025       int leave_side = move_direction;
13026       int old_jx = last_jx;
13027       int old_jy = last_jy;
13028       int old_element = Tile[old_jx][old_jy];
13029       int new_element = Tile[jx][jy];
13030
13031       if (IS_CUSTOM_ELEMENT(old_element))
13032         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13033                                    CE_LEFT_BY_PLAYER,
13034                                    player->index_bit, leave_side);
13035
13036       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13037                                           CE_PLAYER_LEAVES_X,
13038                                           player->index_bit, leave_side);
13039
13040       if (IS_CUSTOM_ELEMENT(new_element))
13041         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13042                                    player->index_bit, enter_side);
13043
13044       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13045                                           CE_PLAYER_ENTERS_X,
13046                                           player->index_bit, enter_side);
13047
13048       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13049                                         CE_MOVE_OF_X, move_direction);
13050     }
13051
13052     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13053     {
13054       TestIfPlayerTouchesBadThing(jx, jy);
13055       TestIfPlayerTouchesCustomElement(jx, jy);
13056
13057       /* needed because pushed element has not yet reached its destination,
13058          so it would trigger a change event at its previous field location */
13059       if (!player->is_pushing)
13060         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13061
13062       if (level.finish_dig_collect &&
13063           (player->is_digging || player->is_collecting))
13064       {
13065         int last_element = player->last_removed_element;
13066         int move_direction = player->MovDir;
13067         int enter_side = MV_DIR_OPPOSITE(move_direction);
13068         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13069                             CE_PLAYER_COLLECTS_X);
13070
13071         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13072                                             player->index_bit, enter_side);
13073
13074         player->last_removed_element = EL_UNDEFINED;
13075       }
13076
13077       if (!player->active)
13078         RemovePlayer(player);
13079     }
13080
13081     if (!game.LevelSolved && level.use_step_counter)
13082     {
13083       int i;
13084
13085       TimePlayed++;
13086
13087       if (TimeLeft > 0)
13088       {
13089         TimeLeft--;
13090
13091         if (TimeLeft <= 10 && setup.time_limit)
13092           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13093
13094         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13095
13096         DisplayGameControlValues();
13097
13098         if (!TimeLeft && setup.time_limit)
13099           for (i = 0; i < MAX_PLAYERS; i++)
13100             KillPlayer(&stored_player[i]);
13101       }
13102       else if (game.no_time_limit && !game.all_players_gone)
13103       {
13104         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13105
13106         DisplayGameControlValues();
13107       }
13108     }
13109
13110     if (tape.single_step && tape.recording && !tape.pausing &&
13111         !player->programmed_action)
13112       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13113
13114     if (!player->programmed_action)
13115       CheckSaveEngineSnapshot(player);
13116   }
13117 }
13118
13119 void ScrollScreen(struct PlayerInfo *player, int mode)
13120 {
13121   static unsigned int screen_frame_counter = 0;
13122
13123   if (mode == SCROLL_INIT)
13124   {
13125     // set scrolling step size according to actual player's moving speed
13126     ScrollStepSize = TILEX / player->move_delay_value;
13127
13128     screen_frame_counter = FrameCounter;
13129     ScreenMovDir = player->MovDir;
13130     ScreenMovPos = player->MovPos;
13131     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13132     return;
13133   }
13134   else if (!FrameReached(&screen_frame_counter, 1))
13135     return;
13136
13137   if (ScreenMovPos)
13138   {
13139     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13140     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13141     redraw_mask |= REDRAW_FIELD;
13142   }
13143   else
13144     ScreenMovDir = MV_NONE;
13145 }
13146
13147 void TestIfPlayerTouchesCustomElement(int x, int y)
13148 {
13149   static int xy[4][2] =
13150   {
13151     { 0, -1 },
13152     { -1, 0 },
13153     { +1, 0 },
13154     { 0, +1 }
13155   };
13156   static int trigger_sides[4][2] =
13157   {
13158     // center side       border side
13159     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13160     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13161     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13162     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13163   };
13164   static int touch_dir[4] =
13165   {
13166     MV_LEFT | MV_RIGHT,
13167     MV_UP   | MV_DOWN,
13168     MV_UP   | MV_DOWN,
13169     MV_LEFT | MV_RIGHT
13170   };
13171   int center_element = Tile[x][y];      // should always be non-moving!
13172   int i;
13173
13174   for (i = 0; i < NUM_DIRECTIONS; i++)
13175   {
13176     int xx = x + xy[i][0];
13177     int yy = y + xy[i][1];
13178     int center_side = trigger_sides[i][0];
13179     int border_side = trigger_sides[i][1];
13180     int border_element;
13181
13182     if (!IN_LEV_FIELD(xx, yy))
13183       continue;
13184
13185     if (IS_PLAYER(x, y))                // player found at center element
13186     {
13187       struct PlayerInfo *player = PLAYERINFO(x, y);
13188
13189       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13190         border_element = Tile[xx][yy];          // may be moving!
13191       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13192         border_element = Tile[xx][yy];
13193       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13194         border_element = MovingOrBlocked2Element(xx, yy);
13195       else
13196         continue;               // center and border element do not touch
13197
13198       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13199                                  player->index_bit, border_side);
13200       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13201                                           CE_PLAYER_TOUCHES_X,
13202                                           player->index_bit, border_side);
13203
13204       {
13205         /* use player element that is initially defined in the level playfield,
13206            not the player element that corresponds to the runtime player number
13207            (example: a level that contains EL_PLAYER_3 as the only player would
13208            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13209         int player_element = PLAYERINFO(x, y)->initial_element;
13210
13211         CheckElementChangeBySide(xx, yy, border_element, player_element,
13212                                  CE_TOUCHING_X, border_side);
13213       }
13214     }
13215     else if (IS_PLAYER(xx, yy))         // player found at border element
13216     {
13217       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13218
13219       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13220       {
13221         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13222           continue;             // center and border element do not touch
13223       }
13224
13225       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13226                                  player->index_bit, center_side);
13227       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13228                                           CE_PLAYER_TOUCHES_X,
13229                                           player->index_bit, center_side);
13230
13231       {
13232         /* use player element that is initially defined in the level playfield,
13233            not the player element that corresponds to the runtime player number
13234            (example: a level that contains EL_PLAYER_3 as the only player would
13235            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13236         int player_element = PLAYERINFO(xx, yy)->initial_element;
13237
13238         CheckElementChangeBySide(x, y, center_element, player_element,
13239                                  CE_TOUCHING_X, center_side);
13240       }
13241
13242       break;
13243     }
13244   }
13245 }
13246
13247 void TestIfElementTouchesCustomElement(int x, int y)
13248 {
13249   static int xy[4][2] =
13250   {
13251     { 0, -1 },
13252     { -1, 0 },
13253     { +1, 0 },
13254     { 0, +1 }
13255   };
13256   static int trigger_sides[4][2] =
13257   {
13258     // center side      border side
13259     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13260     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13261     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13262     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13263   };
13264   static int touch_dir[4] =
13265   {
13266     MV_LEFT | MV_RIGHT,
13267     MV_UP   | MV_DOWN,
13268     MV_UP   | MV_DOWN,
13269     MV_LEFT | MV_RIGHT
13270   };
13271   boolean change_center_element = FALSE;
13272   int center_element = Tile[x][y];      // should always be non-moving!
13273   int border_element_old[NUM_DIRECTIONS];
13274   int i;
13275
13276   for (i = 0; i < NUM_DIRECTIONS; i++)
13277   {
13278     int xx = x + xy[i][0];
13279     int yy = y + xy[i][1];
13280     int border_element;
13281
13282     border_element_old[i] = -1;
13283
13284     if (!IN_LEV_FIELD(xx, yy))
13285       continue;
13286
13287     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13288       border_element = Tile[xx][yy];    // may be moving!
13289     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13290       border_element = Tile[xx][yy];
13291     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13292       border_element = MovingOrBlocked2Element(xx, yy);
13293     else
13294       continue;                 // center and border element do not touch
13295
13296     border_element_old[i] = border_element;
13297   }
13298
13299   for (i = 0; i < NUM_DIRECTIONS; i++)
13300   {
13301     int xx = x + xy[i][0];
13302     int yy = y + xy[i][1];
13303     int center_side = trigger_sides[i][0];
13304     int border_element = border_element_old[i];
13305
13306     if (border_element == -1)
13307       continue;
13308
13309     // check for change of border element
13310     CheckElementChangeBySide(xx, yy, border_element, center_element,
13311                              CE_TOUCHING_X, center_side);
13312
13313     // (center element cannot be player, so we dont have to check this here)
13314   }
13315
13316   for (i = 0; i < NUM_DIRECTIONS; i++)
13317   {
13318     int xx = x + xy[i][0];
13319     int yy = y + xy[i][1];
13320     int border_side = trigger_sides[i][1];
13321     int border_element = border_element_old[i];
13322
13323     if (border_element == -1)
13324       continue;
13325
13326     // check for change of center element (but change it only once)
13327     if (!change_center_element)
13328       change_center_element =
13329         CheckElementChangeBySide(x, y, center_element, border_element,
13330                                  CE_TOUCHING_X, border_side);
13331
13332     if (IS_PLAYER(xx, yy))
13333     {
13334       /* use player element that is initially defined in the level playfield,
13335          not the player element that corresponds to the runtime player number
13336          (example: a level that contains EL_PLAYER_3 as the only player would
13337          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13338       int player_element = PLAYERINFO(xx, yy)->initial_element;
13339
13340       CheckElementChangeBySide(x, y, center_element, player_element,
13341                                CE_TOUCHING_X, border_side);
13342     }
13343   }
13344 }
13345
13346 void TestIfElementHitsCustomElement(int x, int y, int direction)
13347 {
13348   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13349   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13350   int hitx = x + dx, hity = y + dy;
13351   int hitting_element = Tile[x][y];
13352   int touched_element;
13353
13354   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13355     return;
13356
13357   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13358                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13359
13360   if (IN_LEV_FIELD(hitx, hity))
13361   {
13362     int opposite_direction = MV_DIR_OPPOSITE(direction);
13363     int hitting_side = direction;
13364     int touched_side = opposite_direction;
13365     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13366                           MovDir[hitx][hity] != direction ||
13367                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13368
13369     object_hit = TRUE;
13370
13371     if (object_hit)
13372     {
13373       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13374                                CE_HITTING_X, touched_side);
13375
13376       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13377                                CE_HIT_BY_X, hitting_side);
13378
13379       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13380                                CE_HIT_BY_SOMETHING, opposite_direction);
13381
13382       if (IS_PLAYER(hitx, hity))
13383       {
13384         /* use player element that is initially defined in the level playfield,
13385            not the player element that corresponds to the runtime player number
13386            (example: a level that contains EL_PLAYER_3 as the only player would
13387            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13388         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13389
13390         CheckElementChangeBySide(x, y, hitting_element, player_element,
13391                                  CE_HITTING_X, touched_side);
13392       }
13393     }
13394   }
13395
13396   // "hitting something" is also true when hitting the playfield border
13397   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13398                            CE_HITTING_SOMETHING, direction);
13399 }
13400
13401 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13402 {
13403   int i, kill_x = -1, kill_y = -1;
13404
13405   int bad_element = -1;
13406   static int test_xy[4][2] =
13407   {
13408     { 0, -1 },
13409     { -1, 0 },
13410     { +1, 0 },
13411     { 0, +1 }
13412   };
13413   static int test_dir[4] =
13414   {
13415     MV_UP,
13416     MV_LEFT,
13417     MV_RIGHT,
13418     MV_DOWN
13419   };
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 = good_x + test_xy[i][0];
13426     test_y = good_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 = MovingOrBlocked2ElementIfNotLeaving(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(test_element) && good_move_dir == test_dir[i]) ||
13440         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13441     {
13442       kill_x = test_x;
13443       kill_y = test_y;
13444       bad_element = test_element;
13445
13446       break;
13447     }
13448   }
13449
13450   if (kill_x != -1 || kill_y != -1)
13451   {
13452     if (IS_PLAYER(good_x, good_y))
13453     {
13454       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13455
13456       if (player->shield_deadly_time_left > 0 &&
13457           !IS_INDESTRUCTIBLE(bad_element))
13458         Bang(kill_x, kill_y);
13459       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13460         KillPlayer(player);
13461     }
13462     else
13463       Bang(good_x, good_y);
13464   }
13465 }
13466
13467 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13468 {
13469   int i, kill_x = -1, kill_y = -1;
13470   int bad_element = Tile[bad_x][bad_y];
13471   static int test_xy[4][2] =
13472   {
13473     { 0, -1 },
13474     { -1, 0 },
13475     { +1, 0 },
13476     { 0, +1 }
13477   };
13478   static int touch_dir[4] =
13479   {
13480     MV_LEFT | MV_RIGHT,
13481     MV_UP   | MV_DOWN,
13482     MV_UP   | MV_DOWN,
13483     MV_LEFT | MV_RIGHT
13484   };
13485   static int test_dir[4] =
13486   {
13487     MV_UP,
13488     MV_LEFT,
13489     MV_RIGHT,
13490     MV_DOWN
13491   };
13492
13493   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13494     return;
13495
13496   for (i = 0; i < NUM_DIRECTIONS; i++)
13497   {
13498     int test_x, test_y, test_move_dir, test_element;
13499
13500     test_x = bad_x + test_xy[i][0];
13501     test_y = bad_y + test_xy[i][1];
13502
13503     if (!IN_LEV_FIELD(test_x, test_y))
13504       continue;
13505
13506     test_move_dir =
13507       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13508
13509     test_element = Tile[test_x][test_y];
13510
13511     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13512        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13513     */
13514     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13515         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13516     {
13517       // good thing is player or penguin that does not move away
13518       if (IS_PLAYER(test_x, test_y))
13519       {
13520         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13521
13522         if (bad_element == EL_ROBOT && player->is_moving)
13523           continue;     // robot does not kill player if he is moving
13524
13525         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13526         {
13527           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13528             continue;           // center and border element do not touch
13529         }
13530
13531         kill_x = test_x;
13532         kill_y = test_y;
13533
13534         break;
13535       }
13536       else if (test_element == EL_PENGUIN)
13537       {
13538         kill_x = test_x;
13539         kill_y = test_y;
13540
13541         break;
13542       }
13543     }
13544   }
13545
13546   if (kill_x != -1 || kill_y != -1)
13547   {
13548     if (IS_PLAYER(kill_x, kill_y))
13549     {
13550       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13551
13552       if (player->shield_deadly_time_left > 0 &&
13553           !IS_INDESTRUCTIBLE(bad_element))
13554         Bang(bad_x, bad_y);
13555       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13556         KillPlayer(player);
13557     }
13558     else
13559       Bang(kill_x, kill_y);
13560   }
13561 }
13562
13563 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13564 {
13565   int bad_element = Tile[bad_x][bad_y];
13566   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13567   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13568   int test_x = bad_x + dx, test_y = bad_y + dy;
13569   int test_move_dir, test_element;
13570   int kill_x = -1, kill_y = -1;
13571
13572   if (!IN_LEV_FIELD(test_x, test_y))
13573     return;
13574
13575   test_move_dir =
13576     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13577
13578   test_element = Tile[test_x][test_y];
13579
13580   if (test_move_dir != bad_move_dir)
13581   {
13582     // good thing can be player or penguin that does not move away
13583     if (IS_PLAYER(test_x, test_y))
13584     {
13585       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13586
13587       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13588          player as being hit when he is moving towards the bad thing, because
13589          the "get hit by" condition would be lost after the player stops) */
13590       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13591         return;         // player moves away from bad thing
13592
13593       kill_x = test_x;
13594       kill_y = test_y;
13595     }
13596     else if (test_element == EL_PENGUIN)
13597     {
13598       kill_x = test_x;
13599       kill_y = test_y;
13600     }
13601   }
13602
13603   if (kill_x != -1 || kill_y != -1)
13604   {
13605     if (IS_PLAYER(kill_x, kill_y))
13606     {
13607       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13608
13609       if (player->shield_deadly_time_left > 0 &&
13610           !IS_INDESTRUCTIBLE(bad_element))
13611         Bang(bad_x, bad_y);
13612       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13613         KillPlayer(player);
13614     }
13615     else
13616       Bang(kill_x, kill_y);
13617   }
13618 }
13619
13620 void TestIfPlayerTouchesBadThing(int x, int y)
13621 {
13622   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13623 }
13624
13625 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13626 {
13627   TestIfGoodThingHitsBadThing(x, y, move_dir);
13628 }
13629
13630 void TestIfBadThingTouchesPlayer(int x, int y)
13631 {
13632   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13633 }
13634
13635 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13636 {
13637   TestIfBadThingHitsGoodThing(x, y, move_dir);
13638 }
13639
13640 void TestIfFriendTouchesBadThing(int x, int y)
13641 {
13642   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13643 }
13644
13645 void TestIfBadThingTouchesFriend(int x, int y)
13646 {
13647   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13648 }
13649
13650 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13651 {
13652   int i, kill_x = bad_x, kill_y = bad_y;
13653   static int xy[4][2] =
13654   {
13655     { 0, -1 },
13656     { -1, 0 },
13657     { +1, 0 },
13658     { 0, +1 }
13659   };
13660
13661   for (i = 0; i < NUM_DIRECTIONS; i++)
13662   {
13663     int x, y, element;
13664
13665     x = bad_x + xy[i][0];
13666     y = bad_y + xy[i][1];
13667     if (!IN_LEV_FIELD(x, y))
13668       continue;
13669
13670     element = Tile[x][y];
13671     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13672         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13673     {
13674       kill_x = x;
13675       kill_y = y;
13676       break;
13677     }
13678   }
13679
13680   if (kill_x != bad_x || kill_y != bad_y)
13681     Bang(bad_x, bad_y);
13682 }
13683
13684 void KillPlayer(struct PlayerInfo *player)
13685 {
13686   int jx = player->jx, jy = player->jy;
13687
13688   if (!player->active)
13689     return;
13690
13691 #if 0
13692   Debug("game:playing:KillPlayer",
13693         "0: killed == %d, active == %d, reanimated == %d",
13694         player->killed, player->active, player->reanimated);
13695 #endif
13696
13697   /* the following code was introduced to prevent an infinite loop when calling
13698      -> Bang()
13699      -> CheckTriggeredElementChangeExt()
13700      -> ExecuteCustomElementAction()
13701      -> KillPlayer()
13702      -> (infinitely repeating the above sequence of function calls)
13703      which occurs when killing the player while having a CE with the setting
13704      "kill player X when explosion of <player X>"; the solution using a new
13705      field "player->killed" was chosen for backwards compatibility, although
13706      clever use of the fields "player->active" etc. would probably also work */
13707 #if 1
13708   if (player->killed)
13709     return;
13710 #endif
13711
13712   player->killed = TRUE;
13713
13714   // remove accessible field at the player's position
13715   Tile[jx][jy] = EL_EMPTY;
13716
13717   // deactivate shield (else Bang()/Explode() would not work right)
13718   player->shield_normal_time_left = 0;
13719   player->shield_deadly_time_left = 0;
13720
13721 #if 0
13722   Debug("game:playing:KillPlayer",
13723         "1: killed == %d, active == %d, reanimated == %d",
13724         player->killed, player->active, player->reanimated);
13725 #endif
13726
13727   Bang(jx, jy);
13728
13729 #if 0
13730   Debug("game:playing:KillPlayer",
13731         "2: killed == %d, active == %d, reanimated == %d",
13732         player->killed, player->active, player->reanimated);
13733 #endif
13734
13735   if (player->reanimated)       // killed player may have been reanimated
13736     player->killed = player->reanimated = FALSE;
13737   else
13738     BuryPlayer(player);
13739 }
13740
13741 static void KillPlayerUnlessEnemyProtected(int x, int y)
13742 {
13743   if (!PLAYER_ENEMY_PROTECTED(x, y))
13744     KillPlayer(PLAYERINFO(x, y));
13745 }
13746
13747 static void KillPlayerUnlessExplosionProtected(int x, int y)
13748 {
13749   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13750     KillPlayer(PLAYERINFO(x, y));
13751 }
13752
13753 void BuryPlayer(struct PlayerInfo *player)
13754 {
13755   int jx = player->jx, jy = player->jy;
13756
13757   if (!player->active)
13758     return;
13759
13760   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13761   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13762
13763   RemovePlayer(player);
13764
13765   player->buried = TRUE;
13766
13767   if (game.all_players_gone)
13768     game.GameOver = TRUE;
13769 }
13770
13771 void RemovePlayer(struct PlayerInfo *player)
13772 {
13773   int jx = player->jx, jy = player->jy;
13774   int i, found = FALSE;
13775
13776   player->present = FALSE;
13777   player->active = FALSE;
13778
13779   // required for some CE actions (even if the player is not active anymore)
13780   player->MovPos = 0;
13781
13782   if (!ExplodeField[jx][jy])
13783     StorePlayer[jx][jy] = 0;
13784
13785   if (player->is_moving)
13786     TEST_DrawLevelField(player->last_jx, player->last_jy);
13787
13788   for (i = 0; i < MAX_PLAYERS; i++)
13789     if (stored_player[i].active)
13790       found = TRUE;
13791
13792   if (!found)
13793   {
13794     game.all_players_gone = TRUE;
13795     game.GameOver = TRUE;
13796   }
13797
13798   game.exit_x = game.robot_wheel_x = jx;
13799   game.exit_y = game.robot_wheel_y = jy;
13800 }
13801
13802 void ExitPlayer(struct PlayerInfo *player)
13803 {
13804   DrawPlayer(player);   // needed here only to cleanup last field
13805   RemovePlayer(player);
13806
13807   if (game.players_still_needed > 0)
13808     game.players_still_needed--;
13809 }
13810
13811 static void SetFieldForSnapping(int x, int y, int element, int direction,
13812                                 int player_index_bit)
13813 {
13814   struct ElementInfo *ei = &element_info[element];
13815   int direction_bit = MV_DIR_TO_BIT(direction);
13816   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13817   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13818                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13819
13820   Tile[x][y] = EL_ELEMENT_SNAPPING;
13821   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13822   MovDir[x][y] = direction;
13823   Store[x][y] = element;
13824   Store2[x][y] = player_index_bit;
13825
13826   ResetGfxAnimation(x, y);
13827
13828   GfxElement[x][y] = element;
13829   GfxAction[x][y] = action;
13830   GfxDir[x][y] = direction;
13831   GfxFrame[x][y] = -1;
13832 }
13833
13834 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13835                                    int player_index_bit)
13836 {
13837   TestIfElementTouchesCustomElement(x, y);      // for empty space
13838
13839   if (level.finish_dig_collect)
13840   {
13841     int dig_side = MV_DIR_OPPOSITE(direction);
13842
13843     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13844                                         player_index_bit, dig_side);
13845   }
13846 }
13847
13848 /*
13849   =============================================================================
13850   checkDiagonalPushing()
13851   -----------------------------------------------------------------------------
13852   check if diagonal input device direction results in pushing of object
13853   (by checking if the alternative direction is walkable, diggable, ...)
13854   =============================================================================
13855 */
13856
13857 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13858                                     int x, int y, int real_dx, int real_dy)
13859 {
13860   int jx, jy, dx, dy, xx, yy;
13861
13862   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13863     return TRUE;
13864
13865   // diagonal direction: check alternative direction
13866   jx = player->jx;
13867   jy = player->jy;
13868   dx = x - jx;
13869   dy = y - jy;
13870   xx = jx + (dx == 0 ? real_dx : 0);
13871   yy = jy + (dy == 0 ? real_dy : 0);
13872
13873   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13874 }
13875
13876 /*
13877   =============================================================================
13878   DigField()
13879   -----------------------------------------------------------------------------
13880   x, y:                 field next to player (non-diagonal) to try to dig to
13881   real_dx, real_dy:     direction as read from input device (can be diagonal)
13882   =============================================================================
13883 */
13884
13885 static int DigField(struct PlayerInfo *player,
13886                     int oldx, int oldy, int x, int y,
13887                     int real_dx, int real_dy, int mode)
13888 {
13889   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13890   boolean player_was_pushing = player->is_pushing;
13891   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13892   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13893   int jx = oldx, jy = oldy;
13894   int dx = x - jx, dy = y - jy;
13895   int nextx = x + dx, nexty = y + dy;
13896   int move_direction = (dx == -1 ? MV_LEFT  :
13897                         dx == +1 ? MV_RIGHT :
13898                         dy == -1 ? MV_UP    :
13899                         dy == +1 ? MV_DOWN  : MV_NONE);
13900   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13901   int dig_side = MV_DIR_OPPOSITE(move_direction);
13902   int old_element = Tile[jx][jy];
13903   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13904   int collect_count;
13905
13906   if (is_player)                // function can also be called by EL_PENGUIN
13907   {
13908     if (player->MovPos == 0)
13909     {
13910       player->is_digging = FALSE;
13911       player->is_collecting = FALSE;
13912     }
13913
13914     if (player->MovPos == 0)    // last pushing move finished
13915       player->is_pushing = FALSE;
13916
13917     if (mode == DF_NO_PUSH)     // player just stopped pushing
13918     {
13919       player->is_switching = FALSE;
13920       player->push_delay = -1;
13921
13922       return MP_NO_ACTION;
13923     }
13924   }
13925
13926   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13927     old_element = Back[jx][jy];
13928
13929   // in case of element dropped at player position, check background
13930   else if (Back[jx][jy] != EL_EMPTY &&
13931            game.engine_version >= VERSION_IDENT(2,2,0,0))
13932     old_element = Back[jx][jy];
13933
13934   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13935     return MP_NO_ACTION;        // field has no opening in this direction
13936
13937   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13938     return MP_NO_ACTION;        // field has no opening in this direction
13939
13940   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13941   {
13942     SplashAcid(x, y);
13943
13944     Tile[jx][jy] = player->artwork_element;
13945     InitMovingField(jx, jy, MV_DOWN);
13946     Store[jx][jy] = EL_ACID;
13947     ContinueMoving(jx, jy);
13948     BuryPlayer(player);
13949
13950     return MP_DONT_RUN_INTO;
13951   }
13952
13953   if (player_can_move && DONT_RUN_INTO(element))
13954   {
13955     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13956
13957     return MP_DONT_RUN_INTO;
13958   }
13959
13960   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13961     return MP_NO_ACTION;
13962
13963   collect_count = element_info[element].collect_count_initial;
13964
13965   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13966     return MP_NO_ACTION;
13967
13968   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13969     player_can_move = player_can_move_or_snap;
13970
13971   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13972       game.engine_version >= VERSION_IDENT(2,2,0,0))
13973   {
13974     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13975                                player->index_bit, dig_side);
13976     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13977                                         player->index_bit, dig_side);
13978
13979     if (element == EL_DC_LANDMINE)
13980       Bang(x, y);
13981
13982     if (Tile[x][y] != element)          // field changed by snapping
13983       return MP_ACTION;
13984
13985     return MP_NO_ACTION;
13986   }
13987
13988   if (player->gravity && is_player && !player->is_auto_moving &&
13989       canFallDown(player) && move_direction != MV_DOWN &&
13990       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13991     return MP_NO_ACTION;        // player cannot walk here due to gravity
13992
13993   if (player_can_move &&
13994       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13995   {
13996     int sound_element = SND_ELEMENT(element);
13997     int sound_action = ACTION_WALKING;
13998
13999     if (IS_RND_GATE(element))
14000     {
14001       if (!player->key[RND_GATE_NR(element)])
14002         return MP_NO_ACTION;
14003     }
14004     else if (IS_RND_GATE_GRAY(element))
14005     {
14006       if (!player->key[RND_GATE_GRAY_NR(element)])
14007         return MP_NO_ACTION;
14008     }
14009     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14010     {
14011       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14012         return MP_NO_ACTION;
14013     }
14014     else if (element == EL_EXIT_OPEN ||
14015              element == EL_EM_EXIT_OPEN ||
14016              element == EL_EM_EXIT_OPENING ||
14017              element == EL_STEEL_EXIT_OPEN ||
14018              element == EL_EM_STEEL_EXIT_OPEN ||
14019              element == EL_EM_STEEL_EXIT_OPENING ||
14020              element == EL_SP_EXIT_OPEN ||
14021              element == EL_SP_EXIT_OPENING)
14022     {
14023       sound_action = ACTION_PASSING;    // player is passing exit
14024     }
14025     else if (element == EL_EMPTY)
14026     {
14027       sound_action = ACTION_MOVING;             // nothing to walk on
14028     }
14029
14030     // play sound from background or player, whatever is available
14031     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14032       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14033     else
14034       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14035   }
14036   else if (player_can_move &&
14037            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14038   {
14039     if (!ACCESS_FROM(element, opposite_direction))
14040       return MP_NO_ACTION;      // field not accessible from this direction
14041
14042     if (CAN_MOVE(element))      // only fixed elements can be passed!
14043       return MP_NO_ACTION;
14044
14045     if (IS_EM_GATE(element))
14046     {
14047       if (!player->key[EM_GATE_NR(element)])
14048         return MP_NO_ACTION;
14049     }
14050     else if (IS_EM_GATE_GRAY(element))
14051     {
14052       if (!player->key[EM_GATE_GRAY_NR(element)])
14053         return MP_NO_ACTION;
14054     }
14055     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14056     {
14057       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14058         return MP_NO_ACTION;
14059     }
14060     else if (IS_EMC_GATE(element))
14061     {
14062       if (!player->key[EMC_GATE_NR(element)])
14063         return MP_NO_ACTION;
14064     }
14065     else if (IS_EMC_GATE_GRAY(element))
14066     {
14067       if (!player->key[EMC_GATE_GRAY_NR(element)])
14068         return MP_NO_ACTION;
14069     }
14070     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14071     {
14072       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14073         return MP_NO_ACTION;
14074     }
14075     else if (element == EL_DC_GATE_WHITE ||
14076              element == EL_DC_GATE_WHITE_GRAY ||
14077              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14078     {
14079       if (player->num_white_keys == 0)
14080         return MP_NO_ACTION;
14081
14082       player->num_white_keys--;
14083     }
14084     else if (IS_SP_PORT(element))
14085     {
14086       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14087           element == EL_SP_GRAVITY_PORT_RIGHT ||
14088           element == EL_SP_GRAVITY_PORT_UP ||
14089           element == EL_SP_GRAVITY_PORT_DOWN)
14090         player->gravity = !player->gravity;
14091       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14092                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14093                element == EL_SP_GRAVITY_ON_PORT_UP ||
14094                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14095         player->gravity = TRUE;
14096       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14097                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14098                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14099                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14100         player->gravity = FALSE;
14101     }
14102
14103     // automatically move to the next field with double speed
14104     player->programmed_action = move_direction;
14105
14106     if (player->move_delay_reset_counter == 0)
14107     {
14108       player->move_delay_reset_counter = 2;     // two double speed steps
14109
14110       DOUBLE_PLAYER_SPEED(player);
14111     }
14112
14113     PlayLevelSoundAction(x, y, ACTION_PASSING);
14114   }
14115   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14116   {
14117     RemoveField(x, y);
14118
14119     if (mode != DF_SNAP)
14120     {
14121       GfxElement[x][y] = GFX_ELEMENT(element);
14122       player->is_digging = TRUE;
14123     }
14124
14125     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14126
14127     // use old behaviour for old levels (digging)
14128     if (!level.finish_dig_collect)
14129     {
14130       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14131                                           player->index_bit, dig_side);
14132
14133       // if digging triggered player relocation, finish digging tile
14134       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14135         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14136     }
14137
14138     if (mode == DF_SNAP)
14139     {
14140       if (level.block_snap_field)
14141         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14142       else
14143         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14144
14145       // use old behaviour for old levels (snapping)
14146       if (!level.finish_dig_collect)
14147         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14148                                             player->index_bit, dig_side);
14149     }
14150   }
14151   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14152   {
14153     RemoveField(x, y);
14154
14155     if (is_player && mode != DF_SNAP)
14156     {
14157       GfxElement[x][y] = element;
14158       player->is_collecting = TRUE;
14159     }
14160
14161     if (element == EL_SPEED_PILL)
14162     {
14163       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14164     }
14165     else if (element == EL_EXTRA_TIME && level.time > 0)
14166     {
14167       TimeLeft += level.extra_time;
14168
14169       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14170
14171       DisplayGameControlValues();
14172     }
14173     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14174     {
14175       player->shield_normal_time_left += level.shield_normal_time;
14176       if (element == EL_SHIELD_DEADLY)
14177         player->shield_deadly_time_left += level.shield_deadly_time;
14178     }
14179     else if (element == EL_DYNAMITE ||
14180              element == EL_EM_DYNAMITE ||
14181              element == EL_SP_DISK_RED)
14182     {
14183       if (player->inventory_size < MAX_INVENTORY_SIZE)
14184         player->inventory_element[player->inventory_size++] = element;
14185
14186       DrawGameDoorValues();
14187     }
14188     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14189     {
14190       player->dynabomb_count++;
14191       player->dynabombs_left++;
14192     }
14193     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14194     {
14195       player->dynabomb_size++;
14196     }
14197     else if (element == EL_DYNABOMB_INCREASE_POWER)
14198     {
14199       player->dynabomb_xl = TRUE;
14200     }
14201     else if (IS_KEY(element))
14202     {
14203       player->key[KEY_NR(element)] = TRUE;
14204
14205       DrawGameDoorValues();
14206     }
14207     else if (element == EL_DC_KEY_WHITE)
14208     {
14209       player->num_white_keys++;
14210
14211       // display white keys?
14212       // DrawGameDoorValues();
14213     }
14214     else if (IS_ENVELOPE(element))
14215     {
14216       player->show_envelope = element;
14217     }
14218     else if (element == EL_EMC_LENSES)
14219     {
14220       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14221
14222       RedrawAllInvisibleElementsForLenses();
14223     }
14224     else if (element == EL_EMC_MAGNIFIER)
14225     {
14226       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14227
14228       RedrawAllInvisibleElementsForMagnifier();
14229     }
14230     else if (IS_DROPPABLE(element) ||
14231              IS_THROWABLE(element))     // can be collected and dropped
14232     {
14233       int i;
14234
14235       if (collect_count == 0)
14236         player->inventory_infinite_element = element;
14237       else
14238         for (i = 0; i < collect_count; i++)
14239           if (player->inventory_size < MAX_INVENTORY_SIZE)
14240             player->inventory_element[player->inventory_size++] = element;
14241
14242       DrawGameDoorValues();
14243     }
14244     else if (collect_count > 0)
14245     {
14246       game.gems_still_needed -= collect_count;
14247       if (game.gems_still_needed < 0)
14248         game.gems_still_needed = 0;
14249
14250       game.snapshot.collected_item = TRUE;
14251
14252       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14253
14254       DisplayGameControlValues();
14255     }
14256
14257     RaiseScoreElement(element);
14258     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14259
14260     // use old behaviour for old levels (collecting)
14261     if (!level.finish_dig_collect && is_player)
14262     {
14263       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14264                                           player->index_bit, dig_side);
14265
14266       // if collecting triggered player relocation, finish collecting tile
14267       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14268         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14269     }
14270
14271     if (mode == DF_SNAP)
14272     {
14273       if (level.block_snap_field)
14274         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14275       else
14276         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14277
14278       // use old behaviour for old levels (snapping)
14279       if (!level.finish_dig_collect)
14280         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14281                                             player->index_bit, dig_side);
14282     }
14283   }
14284   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14285   {
14286     if (mode == DF_SNAP && element != EL_BD_ROCK)
14287       return MP_NO_ACTION;
14288
14289     if (CAN_FALL(element) && dy)
14290       return MP_NO_ACTION;
14291
14292     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14293         !(element == EL_SPRING && level.use_spring_bug))
14294       return MP_NO_ACTION;
14295
14296     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14297         ((move_direction & MV_VERTICAL &&
14298           ((element_info[element].move_pattern & MV_LEFT &&
14299             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14300            (element_info[element].move_pattern & MV_RIGHT &&
14301             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14302          (move_direction & MV_HORIZONTAL &&
14303           ((element_info[element].move_pattern & MV_UP &&
14304             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14305            (element_info[element].move_pattern & MV_DOWN &&
14306             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14307       return MP_NO_ACTION;
14308
14309     // do not push elements already moving away faster than player
14310     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14311         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14312       return MP_NO_ACTION;
14313
14314     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14315     {
14316       if (player->push_delay_value == -1 || !player_was_pushing)
14317         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14318     }
14319     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14320     {
14321       if (player->push_delay_value == -1)
14322         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14323     }
14324     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14325     {
14326       if (!player->is_pushing)
14327         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14328     }
14329
14330     player->is_pushing = TRUE;
14331     player->is_active = TRUE;
14332
14333     if (!(IN_LEV_FIELD(nextx, nexty) &&
14334           (IS_FREE(nextx, nexty) ||
14335            (IS_SB_ELEMENT(element) &&
14336             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14337            (IS_CUSTOM_ELEMENT(element) &&
14338             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14339       return MP_NO_ACTION;
14340
14341     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14342       return MP_NO_ACTION;
14343
14344     if (player->push_delay == -1)       // new pushing; restart delay
14345       player->push_delay = 0;
14346
14347     if (player->push_delay < player->push_delay_value &&
14348         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14349         element != EL_SPRING && element != EL_BALLOON)
14350     {
14351       // make sure that there is no move delay before next try to push
14352       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14353         player->move_delay = 0;
14354
14355       return MP_NO_ACTION;
14356     }
14357
14358     if (IS_CUSTOM_ELEMENT(element) &&
14359         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14360     {
14361       if (!DigFieldByCE(nextx, nexty, element))
14362         return MP_NO_ACTION;
14363     }
14364
14365     if (IS_SB_ELEMENT(element))
14366     {
14367       boolean sokoban_task_solved = FALSE;
14368
14369       if (element == EL_SOKOBAN_FIELD_FULL)
14370       {
14371         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14372
14373         IncrementSokobanFieldsNeeded();
14374         IncrementSokobanObjectsNeeded();
14375       }
14376
14377       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14378       {
14379         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14380
14381         DecrementSokobanFieldsNeeded();
14382         DecrementSokobanObjectsNeeded();
14383
14384         // sokoban object was pushed from empty field to sokoban field
14385         if (Back[x][y] == EL_EMPTY)
14386           sokoban_task_solved = TRUE;
14387       }
14388
14389       Tile[x][y] = EL_SOKOBAN_OBJECT;
14390
14391       if (Back[x][y] == Back[nextx][nexty])
14392         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14393       else if (Back[x][y] != 0)
14394         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14395                                     ACTION_EMPTYING);
14396       else
14397         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14398                                     ACTION_FILLING);
14399
14400       if (sokoban_task_solved &&
14401           game.sokoban_fields_still_needed == 0 &&
14402           game.sokoban_objects_still_needed == 0 &&
14403           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14404       {
14405         game.players_still_needed = 0;
14406
14407         LevelSolved();
14408
14409         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14410       }
14411     }
14412     else
14413       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14414
14415     InitMovingField(x, y, move_direction);
14416     GfxAction[x][y] = ACTION_PUSHING;
14417
14418     if (mode == DF_SNAP)
14419       ContinueMoving(x, y);
14420     else
14421       MovPos[x][y] = (dx != 0 ? dx : dy);
14422
14423     Pushed[x][y] = TRUE;
14424     Pushed[nextx][nexty] = TRUE;
14425
14426     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14427       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14428     else
14429       player->push_delay_value = -1;    // get new value later
14430
14431     // check for element change _after_ element has been pushed
14432     if (game.use_change_when_pushing_bug)
14433     {
14434       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14435                                  player->index_bit, dig_side);
14436       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14437                                           player->index_bit, dig_side);
14438     }
14439   }
14440   else if (IS_SWITCHABLE(element))
14441   {
14442     if (PLAYER_SWITCHING(player, x, y))
14443     {
14444       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14445                                           player->index_bit, dig_side);
14446
14447       return MP_ACTION;
14448     }
14449
14450     player->is_switching = TRUE;
14451     player->switch_x = x;
14452     player->switch_y = y;
14453
14454     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14455
14456     if (element == EL_ROBOT_WHEEL)
14457     {
14458       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14459
14460       game.robot_wheel_x = x;
14461       game.robot_wheel_y = y;
14462       game.robot_wheel_active = TRUE;
14463
14464       TEST_DrawLevelField(x, y);
14465     }
14466     else if (element == EL_SP_TERMINAL)
14467     {
14468       int xx, yy;
14469
14470       SCAN_PLAYFIELD(xx, yy)
14471       {
14472         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14473         {
14474           Bang(xx, yy);
14475         }
14476         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14477         {
14478           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14479
14480           ResetGfxAnimation(xx, yy);
14481           TEST_DrawLevelField(xx, yy);
14482         }
14483       }
14484     }
14485     else if (IS_BELT_SWITCH(element))
14486     {
14487       ToggleBeltSwitch(x, y);
14488     }
14489     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14490              element == EL_SWITCHGATE_SWITCH_DOWN ||
14491              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14492              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14493     {
14494       ToggleSwitchgateSwitch(x, y);
14495     }
14496     else if (element == EL_LIGHT_SWITCH ||
14497              element == EL_LIGHT_SWITCH_ACTIVE)
14498     {
14499       ToggleLightSwitch(x, y);
14500     }
14501     else if (element == EL_TIMEGATE_SWITCH ||
14502              element == EL_DC_TIMEGATE_SWITCH)
14503     {
14504       ActivateTimegateSwitch(x, y);
14505     }
14506     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14507              element == EL_BALLOON_SWITCH_RIGHT ||
14508              element == EL_BALLOON_SWITCH_UP    ||
14509              element == EL_BALLOON_SWITCH_DOWN  ||
14510              element == EL_BALLOON_SWITCH_NONE  ||
14511              element == EL_BALLOON_SWITCH_ANY)
14512     {
14513       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14514                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14515                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14516                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14517                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14518                              move_direction);
14519     }
14520     else if (element == EL_LAMP)
14521     {
14522       Tile[x][y] = EL_LAMP_ACTIVE;
14523       game.lights_still_needed--;
14524
14525       ResetGfxAnimation(x, y);
14526       TEST_DrawLevelField(x, y);
14527     }
14528     else if (element == EL_TIME_ORB_FULL)
14529     {
14530       Tile[x][y] = EL_TIME_ORB_EMPTY;
14531
14532       if (level.time > 0 || level.use_time_orb_bug)
14533       {
14534         TimeLeft += level.time_orb_time;
14535         game.no_time_limit = FALSE;
14536
14537         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14538
14539         DisplayGameControlValues();
14540       }
14541
14542       ResetGfxAnimation(x, y);
14543       TEST_DrawLevelField(x, y);
14544     }
14545     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14546              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14547     {
14548       int xx, yy;
14549
14550       game.ball_active = !game.ball_active;
14551
14552       SCAN_PLAYFIELD(xx, yy)
14553       {
14554         int e = Tile[xx][yy];
14555
14556         if (game.ball_active)
14557         {
14558           if (e == EL_EMC_MAGIC_BALL)
14559             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14560           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14561             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14562         }
14563         else
14564         {
14565           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14566             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14567           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14568             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14569         }
14570       }
14571     }
14572
14573     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14574                                         player->index_bit, dig_side);
14575
14576     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14577                                         player->index_bit, dig_side);
14578
14579     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14580                                         player->index_bit, dig_side);
14581
14582     return MP_ACTION;
14583   }
14584   else
14585   {
14586     if (!PLAYER_SWITCHING(player, x, y))
14587     {
14588       player->is_switching = TRUE;
14589       player->switch_x = x;
14590       player->switch_y = y;
14591
14592       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14593                                  player->index_bit, dig_side);
14594       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14595                                           player->index_bit, dig_side);
14596
14597       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14598                                  player->index_bit, dig_side);
14599       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14600                                           player->index_bit, dig_side);
14601     }
14602
14603     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14604                                player->index_bit, dig_side);
14605     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14606                                         player->index_bit, dig_side);
14607
14608     return MP_NO_ACTION;
14609   }
14610
14611   player->push_delay = -1;
14612
14613   if (is_player)                // function can also be called by EL_PENGUIN
14614   {
14615     if (Tile[x][y] != element)          // really digged/collected something
14616     {
14617       player->is_collecting = !player->is_digging;
14618       player->is_active = TRUE;
14619
14620       player->last_removed_element = element;
14621     }
14622   }
14623
14624   return MP_MOVING;
14625 }
14626
14627 static boolean DigFieldByCE(int x, int y, int digging_element)
14628 {
14629   int element = Tile[x][y];
14630
14631   if (!IS_FREE(x, y))
14632   {
14633     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14634                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14635                   ACTION_BREAKING);
14636
14637     // no element can dig solid indestructible elements
14638     if (IS_INDESTRUCTIBLE(element) &&
14639         !IS_DIGGABLE(element) &&
14640         !IS_COLLECTIBLE(element))
14641       return FALSE;
14642
14643     if (AmoebaNr[x][y] &&
14644         (element == EL_AMOEBA_FULL ||
14645          element == EL_BD_AMOEBA ||
14646          element == EL_AMOEBA_GROWING))
14647     {
14648       AmoebaCnt[AmoebaNr[x][y]]--;
14649       AmoebaCnt2[AmoebaNr[x][y]]--;
14650     }
14651
14652     if (IS_MOVING(x, y))
14653       RemoveMovingField(x, y);
14654     else
14655     {
14656       RemoveField(x, y);
14657       TEST_DrawLevelField(x, y);
14658     }
14659
14660     // if digged element was about to explode, prevent the explosion
14661     ExplodeField[x][y] = EX_TYPE_NONE;
14662
14663     PlayLevelSoundAction(x, y, action);
14664   }
14665
14666   Store[x][y] = EL_EMPTY;
14667
14668   // this makes it possible to leave the removed element again
14669   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14670     Store[x][y] = element;
14671
14672   return TRUE;
14673 }
14674
14675 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14676 {
14677   int jx = player->jx, jy = player->jy;
14678   int x = jx + dx, y = jy + dy;
14679   int snap_direction = (dx == -1 ? MV_LEFT  :
14680                         dx == +1 ? MV_RIGHT :
14681                         dy == -1 ? MV_UP    :
14682                         dy == +1 ? MV_DOWN  : MV_NONE);
14683   boolean can_continue_snapping = (level.continuous_snapping &&
14684                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14685
14686   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14687     return FALSE;
14688
14689   if (!player->active || !IN_LEV_FIELD(x, y))
14690     return FALSE;
14691
14692   if (dx && dy)
14693     return FALSE;
14694
14695   if (!dx && !dy)
14696   {
14697     if (player->MovPos == 0)
14698       player->is_pushing = FALSE;
14699
14700     player->is_snapping = FALSE;
14701
14702     if (player->MovPos == 0)
14703     {
14704       player->is_moving = FALSE;
14705       player->is_digging = FALSE;
14706       player->is_collecting = FALSE;
14707     }
14708
14709     return FALSE;
14710   }
14711
14712   // prevent snapping with already pressed snap key when not allowed
14713   if (player->is_snapping && !can_continue_snapping)
14714     return FALSE;
14715
14716   player->MovDir = snap_direction;
14717
14718   if (player->MovPos == 0)
14719   {
14720     player->is_moving = FALSE;
14721     player->is_digging = FALSE;
14722     player->is_collecting = FALSE;
14723   }
14724
14725   player->is_dropping = FALSE;
14726   player->is_dropping_pressed = FALSE;
14727   player->drop_pressed_delay = 0;
14728
14729   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14730     return FALSE;
14731
14732   player->is_snapping = TRUE;
14733   player->is_active = TRUE;
14734
14735   if (player->MovPos == 0)
14736   {
14737     player->is_moving = FALSE;
14738     player->is_digging = FALSE;
14739     player->is_collecting = FALSE;
14740   }
14741
14742   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14743     TEST_DrawLevelField(player->last_jx, player->last_jy);
14744
14745   TEST_DrawLevelField(x, y);
14746
14747   return TRUE;
14748 }
14749
14750 static boolean DropElement(struct PlayerInfo *player)
14751 {
14752   int old_element, new_element;
14753   int dropx = player->jx, dropy = player->jy;
14754   int drop_direction = player->MovDir;
14755   int drop_side = drop_direction;
14756   int drop_element = get_next_dropped_element(player);
14757
14758   /* do not drop an element on top of another element; when holding drop key
14759      pressed without moving, dropped element must move away before the next
14760      element can be dropped (this is especially important if the next element
14761      is dynamite, which can be placed on background for historical reasons) */
14762   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14763     return MP_ACTION;
14764
14765   if (IS_THROWABLE(drop_element))
14766   {
14767     dropx += GET_DX_FROM_DIR(drop_direction);
14768     dropy += GET_DY_FROM_DIR(drop_direction);
14769
14770     if (!IN_LEV_FIELD(dropx, dropy))
14771       return FALSE;
14772   }
14773
14774   old_element = Tile[dropx][dropy];     // old element at dropping position
14775   new_element = drop_element;           // default: no change when dropping
14776
14777   // check if player is active, not moving and ready to drop
14778   if (!player->active || player->MovPos || player->drop_delay > 0)
14779     return FALSE;
14780
14781   // check if player has anything that can be dropped
14782   if (new_element == EL_UNDEFINED)
14783     return FALSE;
14784
14785   // only set if player has anything that can be dropped
14786   player->is_dropping_pressed = TRUE;
14787
14788   // check if drop key was pressed long enough for EM style dynamite
14789   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14790     return FALSE;
14791
14792   // check if anything can be dropped at the current position
14793   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14794     return FALSE;
14795
14796   // collected custom elements can only be dropped on empty fields
14797   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14798     return FALSE;
14799
14800   if (old_element != EL_EMPTY)
14801     Back[dropx][dropy] = old_element;   // store old element on this field
14802
14803   ResetGfxAnimation(dropx, dropy);
14804   ResetRandomAnimationValue(dropx, dropy);
14805
14806   if (player->inventory_size > 0 ||
14807       player->inventory_infinite_element != EL_UNDEFINED)
14808   {
14809     if (player->inventory_size > 0)
14810     {
14811       player->inventory_size--;
14812
14813       DrawGameDoorValues();
14814
14815       if (new_element == EL_DYNAMITE)
14816         new_element = EL_DYNAMITE_ACTIVE;
14817       else if (new_element == EL_EM_DYNAMITE)
14818         new_element = EL_EM_DYNAMITE_ACTIVE;
14819       else if (new_element == EL_SP_DISK_RED)
14820         new_element = EL_SP_DISK_RED_ACTIVE;
14821     }
14822
14823     Tile[dropx][dropy] = new_element;
14824
14825     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14826       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14827                           el2img(Tile[dropx][dropy]), 0);
14828
14829     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14830
14831     // needed if previous element just changed to "empty" in the last frame
14832     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14833
14834     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14835                                player->index_bit, drop_side);
14836     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14837                                         CE_PLAYER_DROPS_X,
14838                                         player->index_bit, drop_side);
14839
14840     TestIfElementTouchesCustomElement(dropx, dropy);
14841   }
14842   else          // player is dropping a dyna bomb
14843   {
14844     player->dynabombs_left--;
14845
14846     Tile[dropx][dropy] = new_element;
14847
14848     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14849       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14850                           el2img(Tile[dropx][dropy]), 0);
14851
14852     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14853   }
14854
14855   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14856     InitField_WithBug1(dropx, dropy, FALSE);
14857
14858   new_element = Tile[dropx][dropy];     // element might have changed
14859
14860   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14861       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14862   {
14863     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14864       MovDir[dropx][dropy] = drop_direction;
14865
14866     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14867
14868     // do not cause impact style collision by dropping elements that can fall
14869     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14870   }
14871
14872   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14873   player->is_dropping = TRUE;
14874
14875   player->drop_pressed_delay = 0;
14876   player->is_dropping_pressed = FALSE;
14877
14878   player->drop_x = dropx;
14879   player->drop_y = dropy;
14880
14881   return TRUE;
14882 }
14883
14884 // ----------------------------------------------------------------------------
14885 // game sound playing functions
14886 // ----------------------------------------------------------------------------
14887
14888 static int *loop_sound_frame = NULL;
14889 static int *loop_sound_volume = NULL;
14890
14891 void InitPlayLevelSound(void)
14892 {
14893   int num_sounds = getSoundListSize();
14894
14895   checked_free(loop_sound_frame);
14896   checked_free(loop_sound_volume);
14897
14898   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14899   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14900 }
14901
14902 static void PlayLevelSound(int x, int y, int nr)
14903 {
14904   int sx = SCREENX(x), sy = SCREENY(y);
14905   int volume, stereo_position;
14906   int max_distance = 8;
14907   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14908
14909   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14910       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14911     return;
14912
14913   if (!IN_LEV_FIELD(x, y) ||
14914       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14915       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14916     return;
14917
14918   volume = SOUND_MAX_VOLUME;
14919
14920   if (!IN_SCR_FIELD(sx, sy))
14921   {
14922     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14923     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14924
14925     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14926   }
14927
14928   stereo_position = (SOUND_MAX_LEFT +
14929                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14930                      (SCR_FIELDX + 2 * max_distance));
14931
14932   if (IS_LOOP_SOUND(nr))
14933   {
14934     /* This assures that quieter loop sounds do not overwrite louder ones,
14935        while restarting sound volume comparison with each new game frame. */
14936
14937     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14938       return;
14939
14940     loop_sound_volume[nr] = volume;
14941     loop_sound_frame[nr] = FrameCounter;
14942   }
14943
14944   PlaySoundExt(nr, volume, stereo_position, type);
14945 }
14946
14947 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14948 {
14949   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14950                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14951                  y < LEVELY(BY1) ? LEVELY(BY1) :
14952                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14953                  sound_action);
14954 }
14955
14956 static void PlayLevelSoundAction(int x, int y, int action)
14957 {
14958   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14959 }
14960
14961 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14962 {
14963   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14964
14965   if (sound_effect != SND_UNDEFINED)
14966     PlayLevelSound(x, y, sound_effect);
14967 }
14968
14969 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14970                                               int action)
14971 {
14972   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14973
14974   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14975     PlayLevelSound(x, y, sound_effect);
14976 }
14977
14978 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14979 {
14980   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14981
14982   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14983     PlayLevelSound(x, y, sound_effect);
14984 }
14985
14986 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14987 {
14988   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14989
14990   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14991     StopSound(sound_effect);
14992 }
14993
14994 static int getLevelMusicNr(void)
14995 {
14996   if (levelset.music[level_nr] != MUS_UNDEFINED)
14997     return levelset.music[level_nr];            // from config file
14998   else
14999     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15000 }
15001
15002 static void FadeLevelSounds(void)
15003 {
15004   FadeSounds();
15005 }
15006
15007 static void FadeLevelMusic(void)
15008 {
15009   int music_nr = getLevelMusicNr();
15010   char *curr_music = getCurrentlyPlayingMusicFilename();
15011   char *next_music = getMusicInfoEntryFilename(music_nr);
15012
15013   if (!strEqual(curr_music, next_music))
15014     FadeMusic();
15015 }
15016
15017 void FadeLevelSoundsAndMusic(void)
15018 {
15019   FadeLevelSounds();
15020   FadeLevelMusic();
15021 }
15022
15023 static void PlayLevelMusic(void)
15024 {
15025   int music_nr = getLevelMusicNr();
15026   char *curr_music = getCurrentlyPlayingMusicFilename();
15027   char *next_music = getMusicInfoEntryFilename(music_nr);
15028
15029   if (!strEqual(curr_music, next_music))
15030     PlayMusicLoop(music_nr);
15031 }
15032
15033 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15034 {
15035   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15036   int offset = 0;
15037   int x = xx - offset;
15038   int y = yy - offset;
15039
15040   switch (sample)
15041   {
15042     case SOUND_blank:
15043       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15044       break;
15045
15046     case SOUND_roll:
15047       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15048       break;
15049
15050     case SOUND_stone:
15051       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15052       break;
15053
15054     case SOUND_nut:
15055       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15056       break;
15057
15058     case SOUND_crack:
15059       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15060       break;
15061
15062     case SOUND_bug:
15063       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15064       break;
15065
15066     case SOUND_tank:
15067       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15068       break;
15069
15070     case SOUND_android_clone:
15071       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15072       break;
15073
15074     case SOUND_android_move:
15075       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15076       break;
15077
15078     case SOUND_spring:
15079       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15080       break;
15081
15082     case SOUND_slurp:
15083       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15084       break;
15085
15086     case SOUND_eater:
15087       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15088       break;
15089
15090     case SOUND_eater_eat:
15091       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15092       break;
15093
15094     case SOUND_alien:
15095       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15096       break;
15097
15098     case SOUND_collect:
15099       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15100       break;
15101
15102     case SOUND_diamond:
15103       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15104       break;
15105
15106     case SOUND_squash:
15107       // !!! CHECK THIS !!!
15108 #if 1
15109       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15110 #else
15111       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15112 #endif
15113       break;
15114
15115     case SOUND_wonderfall:
15116       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15117       break;
15118
15119     case SOUND_drip:
15120       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15121       break;
15122
15123     case SOUND_push:
15124       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15125       break;
15126
15127     case SOUND_dirt:
15128       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15129       break;
15130
15131     case SOUND_acid:
15132       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15133       break;
15134
15135     case SOUND_ball:
15136       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15137       break;
15138
15139     case SOUND_slide:
15140       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15141       break;
15142
15143     case SOUND_wonder:
15144       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15145       break;
15146
15147     case SOUND_door:
15148       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15149       break;
15150
15151     case SOUND_exit_open:
15152       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15153       break;
15154
15155     case SOUND_exit_leave:
15156       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15157       break;
15158
15159     case SOUND_dynamite:
15160       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15161       break;
15162
15163     case SOUND_tick:
15164       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15165       break;
15166
15167     case SOUND_press:
15168       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15169       break;
15170
15171     case SOUND_wheel:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15173       break;
15174
15175     case SOUND_boom:
15176       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15177       break;
15178
15179     case SOUND_die:
15180       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15181       break;
15182
15183     case SOUND_time:
15184       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15185       break;
15186
15187     default:
15188       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15189       break;
15190   }
15191 }
15192
15193 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15194 {
15195   int element = map_element_SP_to_RND(element_sp);
15196   int action = map_action_SP_to_RND(action_sp);
15197   int offset = (setup.sp_show_border_elements ? 0 : 1);
15198   int x = xx - offset;
15199   int y = yy - offset;
15200
15201   PlayLevelSoundElementAction(x, y, element, action);
15202 }
15203
15204 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15205 {
15206   int element = map_element_MM_to_RND(element_mm);
15207   int action = map_action_MM_to_RND(action_mm);
15208   int offset = 0;
15209   int x = xx - offset;
15210   int y = yy - offset;
15211
15212   if (!IS_MM_ELEMENT(element))
15213     element = EL_MM_DEFAULT;
15214
15215   PlayLevelSoundElementAction(x, y, element, action);
15216 }
15217
15218 void PlaySound_MM(int sound_mm)
15219 {
15220   int sound = map_sound_MM_to_RND(sound_mm);
15221
15222   if (sound == SND_UNDEFINED)
15223     return;
15224
15225   PlaySound(sound);
15226 }
15227
15228 void PlaySoundLoop_MM(int sound_mm)
15229 {
15230   int sound = map_sound_MM_to_RND(sound_mm);
15231
15232   if (sound == SND_UNDEFINED)
15233     return;
15234
15235   PlaySoundLoop(sound);
15236 }
15237
15238 void StopSound_MM(int sound_mm)
15239 {
15240   int sound = map_sound_MM_to_RND(sound_mm);
15241
15242   if (sound == SND_UNDEFINED)
15243     return;
15244
15245   StopSound(sound);
15246 }
15247
15248 void RaiseScore(int value)
15249 {
15250   game.score += value;
15251
15252   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15253
15254   DisplayGameControlValues();
15255 }
15256
15257 void RaiseScoreElement(int element)
15258 {
15259   switch (element)
15260   {
15261     case EL_EMERALD:
15262     case EL_BD_DIAMOND:
15263     case EL_EMERALD_YELLOW:
15264     case EL_EMERALD_RED:
15265     case EL_EMERALD_PURPLE:
15266     case EL_SP_INFOTRON:
15267       RaiseScore(level.score[SC_EMERALD]);
15268       break;
15269     case EL_DIAMOND:
15270       RaiseScore(level.score[SC_DIAMOND]);
15271       break;
15272     case EL_CRYSTAL:
15273       RaiseScore(level.score[SC_CRYSTAL]);
15274       break;
15275     case EL_PEARL:
15276       RaiseScore(level.score[SC_PEARL]);
15277       break;
15278     case EL_BUG:
15279     case EL_BD_BUTTERFLY:
15280     case EL_SP_ELECTRON:
15281       RaiseScore(level.score[SC_BUG]);
15282       break;
15283     case EL_SPACESHIP:
15284     case EL_BD_FIREFLY:
15285     case EL_SP_SNIKSNAK:
15286       RaiseScore(level.score[SC_SPACESHIP]);
15287       break;
15288     case EL_YAMYAM:
15289     case EL_DARK_YAMYAM:
15290       RaiseScore(level.score[SC_YAMYAM]);
15291       break;
15292     case EL_ROBOT:
15293       RaiseScore(level.score[SC_ROBOT]);
15294       break;
15295     case EL_PACMAN:
15296       RaiseScore(level.score[SC_PACMAN]);
15297       break;
15298     case EL_NUT:
15299       RaiseScore(level.score[SC_NUT]);
15300       break;
15301     case EL_DYNAMITE:
15302     case EL_EM_DYNAMITE:
15303     case EL_SP_DISK_RED:
15304     case EL_DYNABOMB_INCREASE_NUMBER:
15305     case EL_DYNABOMB_INCREASE_SIZE:
15306     case EL_DYNABOMB_INCREASE_POWER:
15307       RaiseScore(level.score[SC_DYNAMITE]);
15308       break;
15309     case EL_SHIELD_NORMAL:
15310     case EL_SHIELD_DEADLY:
15311       RaiseScore(level.score[SC_SHIELD]);
15312       break;
15313     case EL_EXTRA_TIME:
15314       RaiseScore(level.extra_time_score);
15315       break;
15316     case EL_KEY_1:
15317     case EL_KEY_2:
15318     case EL_KEY_3:
15319     case EL_KEY_4:
15320     case EL_EM_KEY_1:
15321     case EL_EM_KEY_2:
15322     case EL_EM_KEY_3:
15323     case EL_EM_KEY_4:
15324     case EL_EMC_KEY_5:
15325     case EL_EMC_KEY_6:
15326     case EL_EMC_KEY_7:
15327     case EL_EMC_KEY_8:
15328     case EL_DC_KEY_WHITE:
15329       RaiseScore(level.score[SC_KEY]);
15330       break;
15331     default:
15332       RaiseScore(element_info[element].collect_score);
15333       break;
15334   }
15335 }
15336
15337 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15338 {
15339   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15340   {
15341     // closing door required in case of envelope style request dialogs
15342     if (!skip_request)
15343     {
15344       // prevent short reactivation of overlay buttons while closing door
15345       SetOverlayActive(FALSE);
15346
15347       CloseDoor(DOOR_CLOSE_1);
15348     }
15349
15350     if (network.enabled)
15351       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15352     else
15353     {
15354       if (quick_quit)
15355         FadeSkipNextFadeIn();
15356
15357       SetGameStatus(GAME_MODE_MAIN);
15358
15359       DrawMainMenu();
15360     }
15361   }
15362   else          // continue playing the game
15363   {
15364     if (tape.playing && tape.deactivate_display)
15365       TapeDeactivateDisplayOff(TRUE);
15366
15367     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15368
15369     if (tape.playing && tape.deactivate_display)
15370       TapeDeactivateDisplayOn();
15371   }
15372 }
15373
15374 void RequestQuitGame(boolean ask_if_really_quit)
15375 {
15376   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15377   boolean skip_request = game.all_players_gone || quick_quit;
15378
15379   RequestQuitGameExt(skip_request, quick_quit,
15380                      "Do you really want to quit the game?");
15381 }
15382
15383 void RequestRestartGame(char *message)
15384 {
15385   game.restart_game_message = NULL;
15386
15387   boolean has_started_game = hasStartedNetworkGame();
15388   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15389
15390   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15391   {
15392     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15393   }
15394   else
15395   {
15396     // needed in case of envelope request to close game panel
15397     CloseDoor(DOOR_CLOSE_1);
15398
15399     SetGameStatus(GAME_MODE_MAIN);
15400
15401     DrawMainMenu();
15402   }
15403 }
15404
15405 void CheckGameOver(void)
15406 {
15407   static boolean last_game_over = FALSE;
15408   static int game_over_delay = 0;
15409   int game_over_delay_value = 50;
15410   boolean game_over = checkGameFailed();
15411
15412   // do not handle game over if request dialog is already active
15413   if (game.request_active)
15414     return;
15415
15416   // do not ask to play again if game was never actually played
15417   if (!game.GamePlayed)
15418     return;
15419
15420   if (!game_over)
15421   {
15422     last_game_over = FALSE;
15423     game_over_delay = game_over_delay_value;
15424
15425     return;
15426   }
15427
15428   if (game_over_delay > 0)
15429   {
15430     game_over_delay--;
15431
15432     return;
15433   }
15434
15435   if (last_game_over != game_over)
15436     game.restart_game_message = (hasStartedNetworkGame() ?
15437                                  "Game over! Play it again?" :
15438                                  "Game over!");
15439
15440   last_game_over = game_over;
15441 }
15442
15443 boolean checkGameSolved(void)
15444 {
15445   // set for all game engines if level was solved
15446   return game.LevelSolved_GameEnd;
15447 }
15448
15449 boolean checkGameFailed(void)
15450 {
15451   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15452     return (game_em.game_over && !game_em.level_solved);
15453   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15454     return (game_sp.game_over && !game_sp.level_solved);
15455   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15456     return (game_mm.game_over && !game_mm.level_solved);
15457   else                          // GAME_ENGINE_TYPE_RND
15458     return (game.GameOver && !game.LevelSolved);
15459 }
15460
15461 boolean checkGameEnded(void)
15462 {
15463   return (checkGameSolved() || checkGameFailed());
15464 }
15465
15466
15467 // ----------------------------------------------------------------------------
15468 // random generator functions
15469 // ----------------------------------------------------------------------------
15470
15471 unsigned int InitEngineRandom_RND(int seed)
15472 {
15473   game.num_random_calls = 0;
15474
15475   return InitEngineRandom(seed);
15476 }
15477
15478 unsigned int RND(int max)
15479 {
15480   if (max > 0)
15481   {
15482     game.num_random_calls++;
15483
15484     return GetEngineRandom(max);
15485   }
15486
15487   return 0;
15488 }
15489
15490
15491 // ----------------------------------------------------------------------------
15492 // game engine snapshot handling functions
15493 // ----------------------------------------------------------------------------
15494
15495 struct EngineSnapshotInfo
15496 {
15497   // runtime values for custom element collect score
15498   int collect_score[NUM_CUSTOM_ELEMENTS];
15499
15500   // runtime values for group element choice position
15501   int choice_pos[NUM_GROUP_ELEMENTS];
15502
15503   // runtime values for belt position animations
15504   int belt_graphic[4][NUM_BELT_PARTS];
15505   int belt_anim_mode[4][NUM_BELT_PARTS];
15506 };
15507
15508 static struct EngineSnapshotInfo engine_snapshot_rnd;
15509 static char *snapshot_level_identifier = NULL;
15510 static int snapshot_level_nr = -1;
15511
15512 static void SaveEngineSnapshotValues_RND(void)
15513 {
15514   static int belt_base_active_element[4] =
15515   {
15516     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15517     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15518     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15519     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15520   };
15521   int i, j;
15522
15523   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15524   {
15525     int element = EL_CUSTOM_START + i;
15526
15527     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15528   }
15529
15530   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15531   {
15532     int element = EL_GROUP_START + i;
15533
15534     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15535   }
15536
15537   for (i = 0; i < 4; i++)
15538   {
15539     for (j = 0; j < NUM_BELT_PARTS; j++)
15540     {
15541       int element = belt_base_active_element[i] + j;
15542       int graphic = el2img(element);
15543       int anim_mode = graphic_info[graphic].anim_mode;
15544
15545       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15546       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15547     }
15548   }
15549 }
15550
15551 static void LoadEngineSnapshotValues_RND(void)
15552 {
15553   unsigned int num_random_calls = game.num_random_calls;
15554   int i, j;
15555
15556   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15557   {
15558     int element = EL_CUSTOM_START + i;
15559
15560     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15561   }
15562
15563   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15564   {
15565     int element = EL_GROUP_START + i;
15566
15567     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15568   }
15569
15570   for (i = 0; i < 4; i++)
15571   {
15572     for (j = 0; j < NUM_BELT_PARTS; j++)
15573     {
15574       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15575       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15576
15577       graphic_info[graphic].anim_mode = anim_mode;
15578     }
15579   }
15580
15581   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15582   {
15583     InitRND(tape.random_seed);
15584     for (i = 0; i < num_random_calls; i++)
15585       RND(1);
15586   }
15587
15588   if (game.num_random_calls != num_random_calls)
15589   {
15590     Error("number of random calls out of sync");
15591     Error("number of random calls should be %d", num_random_calls);
15592     Error("number of random calls is %d", game.num_random_calls);
15593
15594     Fail("this should not happen -- please debug");
15595   }
15596 }
15597
15598 void FreeEngineSnapshotSingle(void)
15599 {
15600   FreeSnapshotSingle();
15601
15602   setString(&snapshot_level_identifier, NULL);
15603   snapshot_level_nr = -1;
15604 }
15605
15606 void FreeEngineSnapshotList(void)
15607 {
15608   FreeSnapshotList();
15609 }
15610
15611 static ListNode *SaveEngineSnapshotBuffers(void)
15612 {
15613   ListNode *buffers = NULL;
15614
15615   // copy some special values to a structure better suited for the snapshot
15616
15617   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15618     SaveEngineSnapshotValues_RND();
15619   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15620     SaveEngineSnapshotValues_EM();
15621   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15622     SaveEngineSnapshotValues_SP(&buffers);
15623   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15624     SaveEngineSnapshotValues_MM(&buffers);
15625
15626   // save values stored in special snapshot structure
15627
15628   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15629     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15630   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15631     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15632   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15633     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15634   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15635     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15636
15637   // save further RND engine values
15638
15639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15640   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15642
15643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15648
15649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15650   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15652
15653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15654
15655   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15657
15658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15676
15677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15679
15680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15683
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15686
15687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15692
15693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15695
15696 #if 0
15697   ListNode *node = engine_snapshot_list_rnd;
15698   int num_bytes = 0;
15699
15700   while (node != NULL)
15701   {
15702     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15703
15704     node = node->next;
15705   }
15706
15707   Debug("game:playing:SaveEngineSnapshotBuffers",
15708         "size of engine snapshot: %d bytes", num_bytes);
15709 #endif
15710
15711   return buffers;
15712 }
15713
15714 void SaveEngineSnapshotSingle(void)
15715 {
15716   ListNode *buffers = SaveEngineSnapshotBuffers();
15717
15718   // finally save all snapshot buffers to single snapshot
15719   SaveSnapshotSingle(buffers);
15720
15721   // save level identification information
15722   setString(&snapshot_level_identifier, leveldir_current->identifier);
15723   snapshot_level_nr = level_nr;
15724 }
15725
15726 boolean CheckSaveEngineSnapshotToList(void)
15727 {
15728   boolean save_snapshot =
15729     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15730      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15731       game.snapshot.changed_action) ||
15732      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15733       game.snapshot.collected_item));
15734
15735   game.snapshot.changed_action = FALSE;
15736   game.snapshot.collected_item = FALSE;
15737   game.snapshot.save_snapshot = save_snapshot;
15738
15739   return save_snapshot;
15740 }
15741
15742 void SaveEngineSnapshotToList(void)
15743 {
15744   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15745       tape.quick_resume)
15746     return;
15747
15748   ListNode *buffers = SaveEngineSnapshotBuffers();
15749
15750   // finally save all snapshot buffers to snapshot list
15751   SaveSnapshotToList(buffers);
15752 }
15753
15754 void SaveEngineSnapshotToListInitial(void)
15755 {
15756   FreeEngineSnapshotList();
15757
15758   SaveEngineSnapshotToList();
15759 }
15760
15761 static void LoadEngineSnapshotValues(void)
15762 {
15763   // restore special values from snapshot structure
15764
15765   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15766     LoadEngineSnapshotValues_RND();
15767   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15768     LoadEngineSnapshotValues_EM();
15769   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15770     LoadEngineSnapshotValues_SP();
15771   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15772     LoadEngineSnapshotValues_MM();
15773 }
15774
15775 void LoadEngineSnapshotSingle(void)
15776 {
15777   LoadSnapshotSingle();
15778
15779   LoadEngineSnapshotValues();
15780 }
15781
15782 static void LoadEngineSnapshot_Undo(int steps)
15783 {
15784   LoadSnapshotFromList_Older(steps);
15785
15786   LoadEngineSnapshotValues();
15787 }
15788
15789 static void LoadEngineSnapshot_Redo(int steps)
15790 {
15791   LoadSnapshotFromList_Newer(steps);
15792
15793   LoadEngineSnapshotValues();
15794 }
15795
15796 boolean CheckEngineSnapshotSingle(void)
15797 {
15798   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15799           snapshot_level_nr == level_nr);
15800 }
15801
15802 boolean CheckEngineSnapshotList(void)
15803 {
15804   return CheckSnapshotList();
15805 }
15806
15807
15808 // ---------- new game button stuff -------------------------------------------
15809
15810 static struct
15811 {
15812   int graphic;
15813   struct XY *pos;
15814   int gadget_id;
15815   boolean *setup_value;
15816   boolean allowed_on_tape;
15817   boolean is_touch_button;
15818   char *infotext;
15819 } gamebutton_info[NUM_GAME_BUTTONS] =
15820 {
15821   {
15822     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15823     GAME_CTRL_ID_STOP,                          NULL,
15824     TRUE, FALSE,                                "stop game"
15825   },
15826   {
15827     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15828     GAME_CTRL_ID_PAUSE,                         NULL,
15829     TRUE, FALSE,                                "pause game"
15830   },
15831   {
15832     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15833     GAME_CTRL_ID_PLAY,                          NULL,
15834     TRUE, FALSE,                                "play game"
15835   },
15836   {
15837     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15838     GAME_CTRL_ID_UNDO,                          NULL,
15839     TRUE, FALSE,                                "undo step"
15840   },
15841   {
15842     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15843     GAME_CTRL_ID_REDO,                          NULL,
15844     TRUE, FALSE,                                "redo step"
15845   },
15846   {
15847     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15848     GAME_CTRL_ID_SAVE,                          NULL,
15849     TRUE, FALSE,                                "save game"
15850   },
15851   {
15852     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15853     GAME_CTRL_ID_PAUSE2,                        NULL,
15854     TRUE, FALSE,                                "pause game"
15855   },
15856   {
15857     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15858     GAME_CTRL_ID_LOAD,                          NULL,
15859     TRUE, FALSE,                                "load game"
15860   },
15861   {
15862     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15863     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15864     FALSE, FALSE,                               "stop game"
15865   },
15866   {
15867     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15868     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15869     FALSE, FALSE,                               "pause game"
15870   },
15871   {
15872     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15873     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15874     FALSE, FALSE,                               "play game"
15875   },
15876   {
15877     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15878     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15879     FALSE, TRUE,                                "stop game"
15880   },
15881   {
15882     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15883     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15884     FALSE, TRUE,                                "pause game"
15885   },
15886   {
15887     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15888     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15889     TRUE, FALSE,                                "background music on/off"
15890   },
15891   {
15892     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15893     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15894     TRUE, FALSE,                                "sound loops on/off"
15895   },
15896   {
15897     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15898     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15899     TRUE, FALSE,                                "normal sounds on/off"
15900   },
15901   {
15902     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15903     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15904     FALSE, FALSE,                               "background music on/off"
15905   },
15906   {
15907     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15908     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15909     FALSE, FALSE,                               "sound loops on/off"
15910   },
15911   {
15912     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15913     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15914     FALSE, FALSE,                               "normal sounds on/off"
15915   }
15916 };
15917
15918 void CreateGameButtons(void)
15919 {
15920   int i;
15921
15922   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15923   {
15924     int graphic = gamebutton_info[i].graphic;
15925     struct GraphicInfo *gfx = &graphic_info[graphic];
15926     struct XY *pos = gamebutton_info[i].pos;
15927     struct GadgetInfo *gi;
15928     int button_type;
15929     boolean checked;
15930     unsigned int event_mask;
15931     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15932     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15933     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15934     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15935     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15936     int gd_x   = gfx->src_x;
15937     int gd_y   = gfx->src_y;
15938     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15939     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15940     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15941     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15942     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15943     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15944     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15945     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15946     int id = i;
15947
15948     if (gfx->bitmap == NULL)
15949     {
15950       game_gadget[id] = NULL;
15951
15952       continue;
15953     }
15954
15955     if (id == GAME_CTRL_ID_STOP ||
15956         id == GAME_CTRL_ID_PANEL_STOP ||
15957         id == GAME_CTRL_ID_TOUCH_STOP ||
15958         id == GAME_CTRL_ID_PLAY ||
15959         id == GAME_CTRL_ID_PANEL_PLAY ||
15960         id == GAME_CTRL_ID_SAVE ||
15961         id == GAME_CTRL_ID_LOAD)
15962     {
15963       button_type = GD_TYPE_NORMAL_BUTTON;
15964       checked = FALSE;
15965       event_mask = GD_EVENT_RELEASED;
15966     }
15967     else if (id == GAME_CTRL_ID_UNDO ||
15968              id == GAME_CTRL_ID_REDO)
15969     {
15970       button_type = GD_TYPE_NORMAL_BUTTON;
15971       checked = FALSE;
15972       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15973     }
15974     else
15975     {
15976       button_type = GD_TYPE_CHECK_BUTTON;
15977       checked = (gamebutton_info[i].setup_value != NULL ?
15978                  *gamebutton_info[i].setup_value : FALSE);
15979       event_mask = GD_EVENT_PRESSED;
15980     }
15981
15982     gi = CreateGadget(GDI_CUSTOM_ID, id,
15983                       GDI_IMAGE_ID, graphic,
15984                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15985                       GDI_X, base_x + x,
15986                       GDI_Y, base_y + y,
15987                       GDI_WIDTH, gfx->width,
15988                       GDI_HEIGHT, gfx->height,
15989                       GDI_TYPE, button_type,
15990                       GDI_STATE, GD_BUTTON_UNPRESSED,
15991                       GDI_CHECKED, checked,
15992                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15993                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15994                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15995                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15996                       GDI_DIRECT_DRAW, FALSE,
15997                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15998                       GDI_EVENT_MASK, event_mask,
15999                       GDI_CALLBACK_ACTION, HandleGameButtons,
16000                       GDI_END);
16001
16002     if (gi == NULL)
16003       Fail("cannot create gadget");
16004
16005     game_gadget[id] = gi;
16006   }
16007 }
16008
16009 void FreeGameButtons(void)
16010 {
16011   int i;
16012
16013   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16014     FreeGadget(game_gadget[i]);
16015 }
16016
16017 static void UnmapGameButtonsAtSamePosition(int id)
16018 {
16019   int i;
16020
16021   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16022     if (i != id &&
16023         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16024         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16025       UnmapGadget(game_gadget[i]);
16026 }
16027
16028 static void UnmapGameButtonsAtSamePosition_All(void)
16029 {
16030   if (setup.show_snapshot_buttons)
16031   {
16032     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16033     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16034     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16035   }
16036   else
16037   {
16038     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16039     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16041
16042     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16043     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16044     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16045   }
16046 }
16047
16048 static void MapGameButtonsAtSamePosition(int id)
16049 {
16050   int i;
16051
16052   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16053     if (i != id &&
16054         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16055         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16056       MapGadget(game_gadget[i]);
16057
16058   UnmapGameButtonsAtSamePosition_All();
16059 }
16060
16061 void MapUndoRedoButtons(void)
16062 {
16063   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16064   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16065
16066   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16067   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16068 }
16069
16070 void UnmapUndoRedoButtons(void)
16071 {
16072   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16073   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16074
16075   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16076   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16077 }
16078
16079 void ModifyPauseButtons(void)
16080 {
16081   static int ids[] =
16082   {
16083     GAME_CTRL_ID_PAUSE,
16084     GAME_CTRL_ID_PAUSE2,
16085     GAME_CTRL_ID_PANEL_PAUSE,
16086     GAME_CTRL_ID_TOUCH_PAUSE,
16087     -1
16088   };
16089   int i;
16090
16091   for (i = 0; ids[i] > -1; i++)
16092     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16093 }
16094
16095 static void MapGameButtonsExt(boolean on_tape)
16096 {
16097   int i;
16098
16099   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16100     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16101         i != GAME_CTRL_ID_UNDO &&
16102         i != GAME_CTRL_ID_REDO)
16103       MapGadget(game_gadget[i]);
16104
16105   UnmapGameButtonsAtSamePosition_All();
16106
16107   RedrawGameButtons();
16108 }
16109
16110 static void UnmapGameButtonsExt(boolean on_tape)
16111 {
16112   int i;
16113
16114   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16115     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16116       UnmapGadget(game_gadget[i]);
16117 }
16118
16119 static void RedrawGameButtonsExt(boolean on_tape)
16120 {
16121   int i;
16122
16123   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16124     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16125       RedrawGadget(game_gadget[i]);
16126 }
16127
16128 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16129 {
16130   if (gi == NULL)
16131     return;
16132
16133   gi->checked = state;
16134 }
16135
16136 static void RedrawSoundButtonGadget(int id)
16137 {
16138   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16139              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16140              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16141              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16142              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16143              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16144              id);
16145
16146   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16147   RedrawGadget(game_gadget[id2]);
16148 }
16149
16150 void MapGameButtons(void)
16151 {
16152   MapGameButtonsExt(FALSE);
16153 }
16154
16155 void UnmapGameButtons(void)
16156 {
16157   UnmapGameButtonsExt(FALSE);
16158 }
16159
16160 void RedrawGameButtons(void)
16161 {
16162   RedrawGameButtonsExt(FALSE);
16163 }
16164
16165 void MapGameButtonsOnTape(void)
16166 {
16167   MapGameButtonsExt(TRUE);
16168 }
16169
16170 void UnmapGameButtonsOnTape(void)
16171 {
16172   UnmapGameButtonsExt(TRUE);
16173 }
16174
16175 void RedrawGameButtonsOnTape(void)
16176 {
16177   RedrawGameButtonsExt(TRUE);
16178 }
16179
16180 static void GameUndoRedoExt(void)
16181 {
16182   ClearPlayerAction();
16183
16184   tape.pausing = TRUE;
16185
16186   RedrawPlayfield();
16187   UpdateAndDisplayGameControlValues();
16188
16189   DrawCompleteVideoDisplay();
16190   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16191   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16192   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16193
16194   BackToFront();
16195 }
16196
16197 static void GameUndo(int steps)
16198 {
16199   if (!CheckEngineSnapshotList())
16200     return;
16201
16202   LoadEngineSnapshot_Undo(steps);
16203
16204   GameUndoRedoExt();
16205 }
16206
16207 static void GameRedo(int steps)
16208 {
16209   if (!CheckEngineSnapshotList())
16210     return;
16211
16212   LoadEngineSnapshot_Redo(steps);
16213
16214   GameUndoRedoExt();
16215 }
16216
16217 static void HandleGameButtonsExt(int id, int button)
16218 {
16219   static boolean game_undo_executed = FALSE;
16220   int steps = BUTTON_STEPSIZE(button);
16221   boolean handle_game_buttons =
16222     (game_status == GAME_MODE_PLAYING ||
16223      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16224
16225   if (!handle_game_buttons)
16226     return;
16227
16228   switch (id)
16229   {
16230     case GAME_CTRL_ID_STOP:
16231     case GAME_CTRL_ID_PANEL_STOP:
16232     case GAME_CTRL_ID_TOUCH_STOP:
16233       if (game_status == GAME_MODE_MAIN)
16234         break;
16235
16236       if (tape.playing)
16237         TapeStop();
16238       else
16239         RequestQuitGame(TRUE);
16240
16241       break;
16242
16243     case GAME_CTRL_ID_PAUSE:
16244     case GAME_CTRL_ID_PAUSE2:
16245     case GAME_CTRL_ID_PANEL_PAUSE:
16246     case GAME_CTRL_ID_TOUCH_PAUSE:
16247       if (network.enabled && game_status == GAME_MODE_PLAYING)
16248       {
16249         if (tape.pausing)
16250           SendToServer_ContinuePlaying();
16251         else
16252           SendToServer_PausePlaying();
16253       }
16254       else
16255         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16256
16257       game_undo_executed = FALSE;
16258
16259       break;
16260
16261     case GAME_CTRL_ID_PLAY:
16262     case GAME_CTRL_ID_PANEL_PLAY:
16263       if (game_status == GAME_MODE_MAIN)
16264       {
16265         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16266       }
16267       else if (tape.pausing)
16268       {
16269         if (network.enabled)
16270           SendToServer_ContinuePlaying();
16271         else
16272           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16273       }
16274       break;
16275
16276     case GAME_CTRL_ID_UNDO:
16277       // Important: When using "save snapshot when collecting an item" mode,
16278       // load last (current) snapshot for first "undo" after pressing "pause"
16279       // (else the last-but-one snapshot would be loaded, because the snapshot
16280       // pointer already points to the last snapshot when pressing "pause",
16281       // which is fine for "every step/move" mode, but not for "every collect")
16282       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16283           !game_undo_executed)
16284         steps--;
16285
16286       game_undo_executed = TRUE;
16287
16288       GameUndo(steps);
16289       break;
16290
16291     case GAME_CTRL_ID_REDO:
16292       GameRedo(steps);
16293       break;
16294
16295     case GAME_CTRL_ID_SAVE:
16296       TapeQuickSave();
16297       break;
16298
16299     case GAME_CTRL_ID_LOAD:
16300       TapeQuickLoad();
16301       break;
16302
16303     case SOUND_CTRL_ID_MUSIC:
16304     case SOUND_CTRL_ID_PANEL_MUSIC:
16305       if (setup.sound_music)
16306       { 
16307         setup.sound_music = FALSE;
16308
16309         FadeMusic();
16310       }
16311       else if (audio.music_available)
16312       { 
16313         setup.sound = setup.sound_music = TRUE;
16314
16315         SetAudioMode(setup.sound);
16316
16317         if (game_status == GAME_MODE_PLAYING)
16318           PlayLevelMusic();
16319       }
16320
16321       RedrawSoundButtonGadget(id);
16322
16323       break;
16324
16325     case SOUND_CTRL_ID_LOOPS:
16326     case SOUND_CTRL_ID_PANEL_LOOPS:
16327       if (setup.sound_loops)
16328         setup.sound_loops = FALSE;
16329       else if (audio.loops_available)
16330       {
16331         setup.sound = setup.sound_loops = TRUE;
16332
16333         SetAudioMode(setup.sound);
16334       }
16335
16336       RedrawSoundButtonGadget(id);
16337
16338       break;
16339
16340     case SOUND_CTRL_ID_SIMPLE:
16341     case SOUND_CTRL_ID_PANEL_SIMPLE:
16342       if (setup.sound_simple)
16343         setup.sound_simple = FALSE;
16344       else if (audio.sound_available)
16345       {
16346         setup.sound = setup.sound_simple = TRUE;
16347
16348         SetAudioMode(setup.sound);
16349       }
16350
16351       RedrawSoundButtonGadget(id);
16352
16353       break;
16354
16355     default:
16356       break;
16357   }
16358 }
16359
16360 static void HandleGameButtons(struct GadgetInfo *gi)
16361 {
16362   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16363 }
16364
16365 void HandleSoundButtonKeys(Key key)
16366 {
16367   if (key == setup.shortcut.sound_simple)
16368     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16369   else if (key == setup.shortcut.sound_loops)
16370     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16371   else if (key == setup.shortcut.sound_music)
16372     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16373 }