added support for style value for key arrangement on game panel
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Tile[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Tile[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Tile[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Tile[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       Debug("game:init:player", "- player element %d activated",
1773             player->element_nr);
1774       Debug("game:init:player", "  (local player is %d and currently %s)",
1775             local_player->element_nr,
1776             local_player->active ? "active" : "not active");
1777     }
1778 #endif
1779
1780     Tile[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   // always check if player was just killed and should be reanimated
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Tile[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Tile[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1828         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878     case EL_SPRING_LEFT:
1879     case EL_SPRING_RIGHT:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Tile[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Tile[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Tile[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Tile[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Tile[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error("'game_panel_controls' structure corrupted at %d", i);
2151
2152       Fail("this should not happen -- please debug");
2153     }
2154
2155     // force update of game controls after initialization
2156     gpc->value = gpc->last_value = -1;
2157     gpc->frame = gpc->last_frame = -1;
2158     gpc->gfx_frame = -1;
2159
2160     // determine panel value width for later calculation of alignment
2161     if (type == TYPE_INTEGER || type == TYPE_STRING)
2162     {
2163       pos->width = pos->size * getFontWidth(pos->font);
2164       pos->height = getFontHeight(pos->font);
2165     }
2166     else if (type == TYPE_ELEMENT)
2167     {
2168       pos->width = pos->size;
2169       pos->height = pos->size;
2170     }
2171
2172     // fill structure for game panel draw order
2173     gpo->nr = gpc->nr;
2174     gpo->sort_priority = pos->sort_priority;
2175   }
2176
2177   // sort game panel controls according to sort_priority and control number
2178   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2179         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2180 }
2181
2182 static void UpdatePlayfieldElementCount(void)
2183 {
2184   boolean use_element_count = FALSE;
2185   int i, j, x, y;
2186
2187   // first check if it is needed at all to calculate playfield element count
2188   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2189     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2190       use_element_count = TRUE;
2191
2192   if (!use_element_count)
2193     return;
2194
2195   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2196     element_info[i].element_count = 0;
2197
2198   SCAN_PLAYFIELD(x, y)
2199   {
2200     element_info[Tile[x][y]].element_count++;
2201   }
2202
2203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2204     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2205       if (IS_IN_GROUP(j, i))
2206         element_info[EL_GROUP_START + i].element_count +=
2207           element_info[j].element_count;
2208 }
2209
2210 static void UpdateGameControlValues(void)
2211 {
2212   int i, k;
2213   int time = (game.LevelSolved ?
2214               game.LevelSolved_CountingTime :
2215               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216               game_em.lev->time :
2217               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2218               game_sp.time_played :
2219               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220               game_mm.energy_left :
2221               game.no_time_limit ? TimePlayed : TimeLeft);
2222   int score = (game.LevelSolved ?
2223                game.LevelSolved_CountingScore :
2224                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2225                game_em.lev->score :
2226                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2227                game_sp.score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2229                game_mm.score :
2230                game.score);
2231   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232               game_em.lev->gems_needed :
2233               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2234               game_sp.infotrons_still_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2236               game_mm.kettles_still_needed :
2237               game.gems_still_needed);
2238   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2239                      game_em.lev->gems_needed > 0 :
2240                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2241                      game_sp.infotrons_still_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2243                      game_mm.kettles_still_needed > 0 ||
2244                      game_mm.lights_still_needed > 0 :
2245                      game.gems_still_needed > 0 ||
2246                      game.sokoban_fields_still_needed > 0 ||
2247                      game.sokoban_objects_still_needed > 0 ||
2248                      game.lights_still_needed > 0);
2249   int health = (game.LevelSolved ?
2250                 game.LevelSolved_CountingHealth :
2251                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252                 MM_HEALTH(game_mm.laser_overload_value) :
2253                 game.health);
2254   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2255
2256   UpdatePlayfieldElementCount();
2257
2258   // update game panel control values
2259
2260   // used instead of "level_nr" (for network games)
2261   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2262   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2263
2264   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2265   for (i = 0; i < MAX_NUM_KEYS; i++)
2266     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2268   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2269
2270   if (game.centered_player_nr == -1)
2271   {
2272     for (i = 0; i < MAX_PLAYERS; i++)
2273     {
2274       // only one player in Supaplex game engine
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2276         break;
2277
2278       for (k = 0; k < MAX_NUM_KEYS; k++)
2279       {
2280         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281         {
2282           if (game_em.ply[i]->keys & (1 << k))
2283             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284               get_key_element_from_nr(k);
2285         }
2286         else if (stored_player[i].key[k])
2287           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2288             get_key_element_from_nr(k);
2289       }
2290
2291       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292         getPlayerInventorySize(i);
2293
2294       if (stored_player[i].num_white_keys > 0)
2295         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2296           EL_DC_KEY_WHITE;
2297
2298       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2299         stored_player[i].num_white_keys;
2300     }
2301   }
2302   else
2303   {
2304     int player_nr = game.centered_player_nr;
2305
2306     for (k = 0; k < MAX_NUM_KEYS; k++)
2307     {
2308       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2309       {
2310         if (game_em.ply[player_nr]->keys & (1 << k))
2311           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312             get_key_element_from_nr(k);
2313       }
2314       else if (stored_player[player_nr].key[k])
2315         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316           get_key_element_from_nr(k);
2317     }
2318
2319     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2320       getPlayerInventorySize(player_nr);
2321
2322     if (stored_player[player_nr].num_white_keys > 0)
2323       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2324
2325     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2326       stored_player[player_nr].num_white_keys;
2327   }
2328
2329   // move keys to leftmost position in game panel, if defined by style settings
2330   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2331   {
2332     int nr = GAME_PANEL_KEY_1 + i;
2333     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2334     struct TextPosInfo *pos = gpc->pos;
2335
2336     if (gpc->value == EL_EMPTY)
2337       continue;
2338
2339     if (pos->style != STYLE_LEFTMOST_POSITION)
2340       continue;
2341
2342     // check previous key positions (left from current key)
2343     for (k = 0; k < i; k++)
2344     {
2345       int nr_new = GAME_PANEL_KEY_1 + k;
2346
2347       if (game_panel_controls[nr_new].value == EL_EMPTY)
2348       {
2349         game_panel_controls[nr_new].value = gpc->value;
2350         gpc->value = EL_EMPTY;
2351
2352         break;
2353       }
2354     }
2355   }
2356
2357   // try to display as many collected keys as possible in the default game panel
2358   for (i = STD_NUM_KEYS; i < MAX_NUM_KEYS + 1; i++)     // EMC keys + white key
2359   {
2360     int nr = GAME_PANEL_KEY_1 + i;
2361     int emc_key = get_key_element_from_nr(i);
2362     int element = (i < MAX_NUM_KEYS ? emc_key : EL_DC_KEY_WHITE);
2363     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2364     struct TextPosInfo *pos = gpc->pos;
2365
2366     // check if panel position is undefined for a certain EMC key or white key
2367     if (gpc->value != EL_EMPTY && pos->x == -1 && pos->y == -1)
2368     {
2369       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2370
2371       // 1st try: display key at the same position as normal or EM keys
2372       if (game_panel_controls[nr_new].value == EL_EMPTY)
2373       {
2374         game_panel_controls[nr_new].value = element;
2375       }
2376       else
2377       {
2378         // 2nd try: display key at the next free position in the key panel
2379         for (k = 0; k < STD_NUM_KEYS; k++)
2380         {
2381           nr_new = GAME_PANEL_KEY_1 + k;
2382
2383           if (game_panel_controls[nr_new].value == EL_EMPTY)
2384           {
2385             game_panel_controls[nr_new].value = element;
2386
2387             break;
2388           }
2389         }
2390       }
2391     }
2392   }
2393
2394   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2395   {
2396     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2397       get_inventory_element_from_pos(local_player, i);
2398     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, -i - 1);
2400   }
2401
2402   game_panel_controls[GAME_PANEL_SCORE].value = score;
2403   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2404
2405   game_panel_controls[GAME_PANEL_TIME].value = time;
2406
2407   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2408   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2409   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2410
2411   if (level.time == 0)
2412     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2413   else
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2415
2416   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2417   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2418
2419   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2420
2421   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2422     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2423      EL_EMPTY);
2424   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2425     local_player->shield_normal_time_left;
2426   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2427     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2428      EL_EMPTY);
2429   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2430     local_player->shield_deadly_time_left;
2431
2432   game_panel_controls[GAME_PANEL_EXIT].value =
2433     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2434
2435   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2436     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2439      EL_EMC_MAGIC_BALL_SWITCH);
2440
2441   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2442     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2444     game.light_time_left;
2445
2446   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2447     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2449     game.timegate_time_left;
2450
2451   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2452     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2453
2454   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2455     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2456   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2457     game.lenses_time_left;
2458
2459   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2460     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2462     game.magnify_time_left;
2463
2464   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2465     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2466      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2467      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2468      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2469      EL_BALLOON_SWITCH_NONE);
2470
2471   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2472     local_player->dynabomb_count;
2473   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2474     local_player->dynabomb_size;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2476     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2477
2478   game_panel_controls[GAME_PANEL_PENGUINS].value =
2479     game.friends_still_needed;
2480
2481   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2482     game.sokoban_objects_still_needed;
2483   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2484     game.sokoban_fields_still_needed;
2485
2486   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2487     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2488
2489   for (i = 0; i < NUM_BELTS; i++)
2490   {
2491     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2492       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2493        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2494     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2495       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2496   }
2497
2498   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2499     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2501     game.magic_wall_time_left;
2502
2503   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2504     local_player->gravity;
2505
2506   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2507     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2508
2509   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2510     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2511       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2512        game.panel.element[i].id : EL_UNDEFINED);
2513
2514   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2515     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2516       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2517        element_info[game.panel.element_count[i].id].element_count : 0);
2518
2519   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2520     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2521       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2522        element_info[game.panel.ce_score[i].id].collect_score : 0);
2523
2524   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2525     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2526       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2527        element_info[game.panel.ce_score_element[i].id].collect_score :
2528        EL_UNDEFINED);
2529
2530   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2531   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2532   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2533
2534   // update game panel control frames
2535
2536   for (i = 0; game_panel_controls[i].nr != -1; i++)
2537   {
2538     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2539
2540     if (gpc->type == TYPE_ELEMENT)
2541     {
2542       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2543       {
2544         int last_anim_random_frame = gfx.anim_random_frame;
2545         int element = gpc->value;
2546         int graphic = el2panelimg(element);
2547         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2548                                sync_random_frame : INIT_GFX_RANDOM());
2549
2550         if (gpc->value != gpc->last_value)
2551         {
2552           gpc->gfx_frame = 0;
2553           gpc->gfx_random = init_gfx_random;
2554         }
2555         else
2556         {
2557           gpc->gfx_frame++;
2558
2559           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2560               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2561             gpc->gfx_random = init_gfx_random;
2562         }
2563
2564         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2565           gfx.anim_random_frame = gpc->gfx_random;
2566
2567         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2568           gpc->gfx_frame = element_info[element].collect_score;
2569
2570         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2571
2572         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2573           gfx.anim_random_frame = last_anim_random_frame;
2574       }
2575     }
2576     else if (gpc->type == TYPE_GRAPHIC)
2577     {
2578       if (gpc->graphic != IMG_UNDEFINED)
2579       {
2580         int last_anim_random_frame = gfx.anim_random_frame;
2581         int graphic = gpc->graphic;
2582         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2583                                sync_random_frame : INIT_GFX_RANDOM());
2584
2585         if (gpc->value != gpc->last_value)
2586         {
2587           gpc->gfx_frame = 0;
2588           gpc->gfx_random = init_gfx_random;
2589         }
2590         else
2591         {
2592           gpc->gfx_frame++;
2593
2594           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2595               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2596             gpc->gfx_random = init_gfx_random;
2597         }
2598
2599         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2600           gfx.anim_random_frame = gpc->gfx_random;
2601
2602         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2603
2604         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2605           gfx.anim_random_frame = last_anim_random_frame;
2606       }
2607     }
2608   }
2609 }
2610
2611 static void DisplayGameControlValues(void)
2612 {
2613   boolean redraw_panel = FALSE;
2614   int i;
2615
2616   for (i = 0; game_panel_controls[i].nr != -1; i++)
2617   {
2618     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2619
2620     if (PANEL_DEACTIVATED(gpc->pos))
2621       continue;
2622
2623     if (gpc->value == gpc->last_value &&
2624         gpc->frame == gpc->last_frame)
2625       continue;
2626
2627     redraw_panel = TRUE;
2628   }
2629
2630   if (!redraw_panel)
2631     return;
2632
2633   // copy default game door content to main double buffer
2634
2635   // !!! CHECK AGAIN !!!
2636   SetPanelBackground();
2637   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2638   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2639
2640   // redraw game control buttons
2641   RedrawGameButtons();
2642
2643   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2644
2645   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2646   {
2647     int nr = game_panel_order[i].nr;
2648     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2649     struct TextPosInfo *pos = gpc->pos;
2650     int type = gpc->type;
2651     int value = gpc->value;
2652     int frame = gpc->frame;
2653     int size = pos->size;
2654     int font = pos->font;
2655     boolean draw_masked = pos->draw_masked;
2656     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2657
2658     if (PANEL_DEACTIVATED(pos))
2659       continue;
2660
2661     if (pos->class == get_hash_from_key("extra_panel_items") &&
2662         !setup.prefer_extra_panel_items)
2663       continue;
2664
2665     gpc->last_value = value;
2666     gpc->last_frame = frame;
2667
2668     if (type == TYPE_INTEGER)
2669     {
2670       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2671           nr == GAME_PANEL_TIME)
2672       {
2673         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2674
2675         if (use_dynamic_size)           // use dynamic number of digits
2676         {
2677           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2678           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2679           int size2 = size1 + 1;
2680           int font1 = pos->font;
2681           int font2 = pos->font_alt;
2682
2683           size = (value < value_change ? size1 : size2);
2684           font = (value < value_change ? font1 : font2);
2685         }
2686       }
2687
2688       // correct text size if "digits" is zero or less
2689       if (size <= 0)
2690         size = strlen(int2str(value, size));
2691
2692       // dynamically correct text alignment
2693       pos->width = size * getFontWidth(font);
2694
2695       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2696                   int2str(value, size), font, mask_mode);
2697     }
2698     else if (type == TYPE_ELEMENT)
2699     {
2700       int element, graphic;
2701       Bitmap *src_bitmap;
2702       int src_x, src_y;
2703       int width, height;
2704       int dst_x = PANEL_XPOS(pos);
2705       int dst_y = PANEL_YPOS(pos);
2706
2707       if (value != EL_UNDEFINED && value != EL_EMPTY)
2708       {
2709         element = value;
2710         graphic = el2panelimg(value);
2711
2712 #if 0
2713         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2714               element, EL_NAME(element), size);
2715 #endif
2716
2717         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2718           size = TILESIZE;
2719
2720         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2721                               &src_x, &src_y);
2722
2723         width  = graphic_info[graphic].width  * size / TILESIZE;
2724         height = graphic_info[graphic].height * size / TILESIZE;
2725
2726         if (draw_masked)
2727           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2728                            dst_x, dst_y);
2729         else
2730           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2731                      dst_x, dst_y);
2732       }
2733     }
2734     else if (type == TYPE_GRAPHIC)
2735     {
2736       int graphic        = gpc->graphic;
2737       int graphic_active = gpc->graphic_active;
2738       Bitmap *src_bitmap;
2739       int src_x, src_y;
2740       int width, height;
2741       int dst_x = PANEL_XPOS(pos);
2742       int dst_y = PANEL_YPOS(pos);
2743       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2744                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2745
2746       if (graphic != IMG_UNDEFINED && !skip)
2747       {
2748         if (pos->style == STYLE_REVERSE)
2749           value = 100 - value;
2750
2751         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2752
2753         if (pos->direction & MV_HORIZONTAL)
2754         {
2755           width  = graphic_info[graphic_active].width * value / 100;
2756           height = graphic_info[graphic_active].height;
2757
2758           if (pos->direction == MV_LEFT)
2759           {
2760             src_x += graphic_info[graphic_active].width - width;
2761             dst_x += graphic_info[graphic_active].width - width;
2762           }
2763         }
2764         else
2765         {
2766           width  = graphic_info[graphic_active].width;
2767           height = graphic_info[graphic_active].height * value / 100;
2768
2769           if (pos->direction == MV_UP)
2770           {
2771             src_y += graphic_info[graphic_active].height - height;
2772             dst_y += graphic_info[graphic_active].height - height;
2773           }
2774         }
2775
2776         if (draw_masked)
2777           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2778                            dst_x, dst_y);
2779         else
2780           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2781                      dst_x, dst_y);
2782
2783         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2784
2785         if (pos->direction & MV_HORIZONTAL)
2786         {
2787           if (pos->direction == MV_RIGHT)
2788           {
2789             src_x += width;
2790             dst_x += width;
2791           }
2792           else
2793           {
2794             dst_x = PANEL_XPOS(pos);
2795           }
2796
2797           width = graphic_info[graphic].width - width;
2798         }
2799         else
2800         {
2801           if (pos->direction == MV_DOWN)
2802           {
2803             src_y += height;
2804             dst_y += height;
2805           }
2806           else
2807           {
2808             dst_y = PANEL_YPOS(pos);
2809           }
2810
2811           height = graphic_info[graphic].height - height;
2812         }
2813
2814         if (draw_masked)
2815           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2816                            dst_x, dst_y);
2817         else
2818           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2819                      dst_x, dst_y);
2820       }
2821     }
2822     else if (type == TYPE_STRING)
2823     {
2824       boolean active = (value != 0);
2825       char *state_normal = "off";
2826       char *state_active = "on";
2827       char *state = (active ? state_active : state_normal);
2828       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2829                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2830                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2831                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2832
2833       if (nr == GAME_PANEL_GRAVITY_STATE)
2834       {
2835         int font1 = pos->font;          // (used for normal state)
2836         int font2 = pos->font_alt;      // (used for active state)
2837
2838         font = (active ? font2 : font1);
2839       }
2840
2841       if (s != NULL)
2842       {
2843         char *s_cut;
2844
2845         if (size <= 0)
2846         {
2847           // don't truncate output if "chars" is zero or less
2848           size = strlen(s);
2849
2850           // dynamically correct text alignment
2851           pos->width = size * getFontWidth(font);
2852         }
2853
2854         s_cut = getStringCopyN(s, size);
2855
2856         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2857                     s_cut, font, mask_mode);
2858
2859         free(s_cut);
2860       }
2861     }
2862
2863     redraw_mask |= REDRAW_DOOR_1;
2864   }
2865
2866   SetGameStatus(GAME_MODE_PLAYING);
2867 }
2868
2869 void UpdateAndDisplayGameControlValues(void)
2870 {
2871   if (tape.deactivate_display)
2872     return;
2873
2874   UpdateGameControlValues();
2875   DisplayGameControlValues();
2876 }
2877
2878 #if 0
2879 static void UpdateGameDoorValues(void)
2880 {
2881   UpdateGameControlValues();
2882 }
2883 #endif
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   // initialize tape actions from game when recording tape
3588   if (tape.recording)
3589   {
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592   }
3593
3594   // don't play tapes over network
3595   network_playing = (network.enabled && !tape.playing);
3596
3597   for (i = 0; i < MAX_PLAYERS; i++)
3598   {
3599     struct PlayerInfo *player = &stored_player[i];
3600
3601     player->index_nr = i;
3602     player->index_bit = (1 << i);
3603     player->element_nr = EL_PLAYER_1 + i;
3604
3605     player->present = FALSE;
3606     player->active = FALSE;
3607     player->mapped = FALSE;
3608
3609     player->killed = FALSE;
3610     player->reanimated = FALSE;
3611     player->buried = FALSE;
3612
3613     player->action = 0;
3614     player->effective_action = 0;
3615     player->programmed_action = 0;
3616     player->snap_action = 0;
3617
3618     player->mouse_action.lx = 0;
3619     player->mouse_action.ly = 0;
3620     player->mouse_action.button = 0;
3621     player->mouse_action.button_hint = 0;
3622
3623     player->effective_mouse_action.lx = 0;
3624     player->effective_mouse_action.ly = 0;
3625     player->effective_mouse_action.button = 0;
3626     player->effective_mouse_action.button_hint = 0;
3627
3628     for (j = 0; j < MAX_NUM_KEYS; j++)
3629       player->key[j] = FALSE;
3630
3631     player->num_white_keys = 0;
3632
3633     player->dynabomb_count = 0;
3634     player->dynabomb_size = 1;
3635     player->dynabombs_left = 0;
3636     player->dynabomb_xl = FALSE;
3637
3638     player->MovDir = initial_move_dir;
3639     player->MovPos = 0;
3640     player->GfxPos = 0;
3641     player->GfxDir = initial_move_dir;
3642     player->GfxAction = ACTION_DEFAULT;
3643     player->Frame = 0;
3644     player->StepFrame = 0;
3645
3646     player->initial_element = player->element_nr;
3647     player->artwork_element =
3648       (level.use_artwork_element[i] ? level.artwork_element[i] :
3649        player->element_nr);
3650     player->use_murphy = FALSE;
3651
3652     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3653     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3654
3655     player->gravity = level.initial_player_gravity[i];
3656
3657     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3658
3659     player->actual_frame_counter = 0;
3660
3661     player->step_counter = 0;
3662
3663     player->last_move_dir = initial_move_dir;
3664
3665     player->is_active = FALSE;
3666
3667     player->is_waiting = FALSE;
3668     player->is_moving = FALSE;
3669     player->is_auto_moving = FALSE;
3670     player->is_digging = FALSE;
3671     player->is_snapping = FALSE;
3672     player->is_collecting = FALSE;
3673     player->is_pushing = FALSE;
3674     player->is_switching = FALSE;
3675     player->is_dropping = FALSE;
3676     player->is_dropping_pressed = FALSE;
3677
3678     player->is_bored = FALSE;
3679     player->is_sleeping = FALSE;
3680
3681     player->was_waiting = TRUE;
3682     player->was_moving = FALSE;
3683     player->was_snapping = FALSE;
3684     player->was_dropping = FALSE;
3685
3686     player->force_dropping = FALSE;
3687
3688     player->frame_counter_bored = -1;
3689     player->frame_counter_sleeping = -1;
3690
3691     player->anim_delay_counter = 0;
3692     player->post_delay_counter = 0;
3693
3694     player->dir_waiting = initial_move_dir;
3695     player->action_waiting = ACTION_DEFAULT;
3696     player->last_action_waiting = ACTION_DEFAULT;
3697     player->special_action_bored = ACTION_DEFAULT;
3698     player->special_action_sleeping = ACTION_DEFAULT;
3699
3700     player->switch_x = -1;
3701     player->switch_y = -1;
3702
3703     player->drop_x = -1;
3704     player->drop_y = -1;
3705
3706     player->show_envelope = 0;
3707
3708     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3709
3710     player->push_delay       = -1;      // initialized when pushing starts
3711     player->push_delay_value = game.initial_push_delay_value;
3712
3713     player->drop_delay = 0;
3714     player->drop_pressed_delay = 0;
3715
3716     player->last_jx = -1;
3717     player->last_jy = -1;
3718     player->jx = -1;
3719     player->jy = -1;
3720
3721     player->shield_normal_time_left = 0;
3722     player->shield_deadly_time_left = 0;
3723
3724     player->inventory_infinite_element = EL_UNDEFINED;
3725     player->inventory_size = 0;
3726
3727     if (level.use_initial_inventory[i])
3728     {
3729       for (j = 0; j < level.initial_inventory_size[i]; j++)
3730       {
3731         int element = level.initial_inventory_content[i][j];
3732         int collect_count = element_info[element].collect_count_initial;
3733         int k;
3734
3735         if (!IS_CUSTOM_ELEMENT(element))
3736           collect_count = 1;
3737
3738         if (collect_count == 0)
3739           player->inventory_infinite_element = element;
3740         else
3741           for (k = 0; k < collect_count; k++)
3742             if (player->inventory_size < MAX_INVENTORY_SIZE)
3743               player->inventory_element[player->inventory_size++] = element;
3744       }
3745     }
3746
3747     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3748     SnapField(player, 0, 0);
3749
3750     map_player_action[i] = i;
3751   }
3752
3753   network_player_action_received = FALSE;
3754
3755   // initial null action
3756   if (network_playing)
3757     SendToServer_MovePlayer(MV_NONE);
3758
3759   FrameCounter = 0;
3760   TimeFrames = 0;
3761   TimePlayed = 0;
3762   TimeLeft = level.time;
3763   TapeTime = 0;
3764
3765   ScreenMovDir = MV_NONE;
3766   ScreenMovPos = 0;
3767   ScreenGfxPos = 0;
3768
3769   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3770
3771   game.robot_wheel_x = -1;
3772   game.robot_wheel_y = -1;
3773
3774   game.exit_x = -1;
3775   game.exit_y = -1;
3776
3777   game.all_players_gone = FALSE;
3778
3779   game.LevelSolved = FALSE;
3780   game.GameOver = FALSE;
3781
3782   game.GamePlayed = !tape.playing;
3783
3784   game.LevelSolved_GameWon = FALSE;
3785   game.LevelSolved_GameEnd = FALSE;
3786   game.LevelSolved_SaveTape = FALSE;
3787   game.LevelSolved_SaveScore = FALSE;
3788
3789   game.LevelSolved_CountingTime = 0;
3790   game.LevelSolved_CountingScore = 0;
3791   game.LevelSolved_CountingHealth = 0;
3792
3793   game.panel.active = TRUE;
3794
3795   game.no_time_limit = (level.time == 0);
3796
3797   game.yamyam_content_nr = 0;
3798   game.robot_wheel_active = FALSE;
3799   game.magic_wall_active = FALSE;
3800   game.magic_wall_time_left = 0;
3801   game.light_time_left = 0;
3802   game.timegate_time_left = 0;
3803   game.switchgate_pos = 0;
3804   game.wind_direction = level.wind_direction_initial;
3805
3806   game.score = 0;
3807   game.score_final = 0;
3808
3809   game.health = MAX_HEALTH;
3810   game.health_final = MAX_HEALTH;
3811
3812   game.gems_still_needed = level.gems_needed;
3813   game.sokoban_fields_still_needed = 0;
3814   game.sokoban_objects_still_needed = 0;
3815   game.lights_still_needed = 0;
3816   game.players_still_needed = 0;
3817   game.friends_still_needed = 0;
3818
3819   game.lenses_time_left = 0;
3820   game.magnify_time_left = 0;
3821
3822   game.ball_active = level.ball_active_initial;
3823   game.ball_content_nr = 0;
3824
3825   game.explosions_delayed = TRUE;
3826
3827   game.envelope_active = FALSE;
3828
3829   for (i = 0; i < NUM_BELTS; i++)
3830   {
3831     game.belt_dir[i] = MV_NONE;
3832     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3833   }
3834
3835   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3836     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3837
3838 #if DEBUG_INIT_PLAYER
3839   DebugPrintPlayerStatus("Player status at level initialization");
3840 #endif
3841
3842   SCAN_PLAYFIELD(x, y)
3843   {
3844     Tile[x][y] = Last[x][y] = level.field[x][y];
3845     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3846     ChangeDelay[x][y] = 0;
3847     ChangePage[x][y] = -1;
3848     CustomValue[x][y] = 0;              // initialized in InitField()
3849     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3850     AmoebaNr[x][y] = 0;
3851     WasJustMoving[x][y] = 0;
3852     WasJustFalling[x][y] = 0;
3853     CheckCollision[x][y] = 0;
3854     CheckImpact[x][y] = 0;
3855     Stop[x][y] = FALSE;
3856     Pushed[x][y] = FALSE;
3857
3858     ChangeCount[x][y] = 0;
3859     ChangeEvent[x][y] = -1;
3860
3861     ExplodePhase[x][y] = 0;
3862     ExplodeDelay[x][y] = 0;
3863     ExplodeField[x][y] = EX_TYPE_NONE;
3864
3865     RunnerVisit[x][y] = 0;
3866     PlayerVisit[x][y] = 0;
3867
3868     GfxFrame[x][y] = 0;
3869     GfxRandom[x][y] = INIT_GFX_RANDOM();
3870     GfxElement[x][y] = EL_UNDEFINED;
3871     GfxAction[x][y] = ACTION_DEFAULT;
3872     GfxDir[x][y] = MV_NONE;
3873     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3874   }
3875
3876   SCAN_PLAYFIELD(x, y)
3877   {
3878     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3879       emulate_bd = FALSE;
3880     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3881       emulate_sb = FALSE;
3882     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3883       emulate_sp = FALSE;
3884
3885     InitField(x, y, TRUE);
3886
3887     ResetGfxAnimation(x, y);
3888   }
3889
3890   InitBeltMovement();
3891
3892   for (i = 0; i < MAX_PLAYERS; i++)
3893   {
3894     struct PlayerInfo *player = &stored_player[i];
3895
3896     // set number of special actions for bored and sleeping animation
3897     player->num_special_action_bored =
3898       get_num_special_action(player->artwork_element,
3899                              ACTION_BORING_1, ACTION_BORING_LAST);
3900     player->num_special_action_sleeping =
3901       get_num_special_action(player->artwork_element,
3902                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3903   }
3904
3905   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3906                     emulate_sb ? EMU_SOKOBAN :
3907                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3908
3909   // initialize type of slippery elements
3910   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3911   {
3912     if (!IS_CUSTOM_ELEMENT(i))
3913     {
3914       // default: elements slip down either to the left or right randomly
3915       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3916
3917       // SP style elements prefer to slip down on the left side
3918       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3919         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3920
3921       // BD style elements prefer to slip down on the left side
3922       if (game.emulation == EMU_BOULDERDASH)
3923         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3924     }
3925   }
3926
3927   // initialize explosion and ignition delay
3928   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3929   {
3930     if (!IS_CUSTOM_ELEMENT(i))
3931     {
3932       int num_phase = 8;
3933       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3934                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3935                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3936       int last_phase = (num_phase + 1) * delay;
3937       int half_phase = (num_phase / 2) * delay;
3938
3939       element_info[i].explosion_delay = last_phase - 1;
3940       element_info[i].ignition_delay = half_phase;
3941
3942       if (i == EL_BLACK_ORB)
3943         element_info[i].ignition_delay = 1;
3944     }
3945   }
3946
3947   // correct non-moving belts to start moving left
3948   for (i = 0; i < NUM_BELTS; i++)
3949     if (game.belt_dir[i] == MV_NONE)
3950       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3951
3952 #if USE_NEW_PLAYER_ASSIGNMENTS
3953   // use preferred player also in local single-player mode
3954   if (!network.enabled && !game.team_mode)
3955   {
3956     int new_index_nr = setup.network_player_nr;
3957
3958     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3959     {
3960       for (i = 0; i < MAX_PLAYERS; i++)
3961         stored_player[i].connected_locally = FALSE;
3962
3963       stored_player[new_index_nr].connected_locally = TRUE;
3964     }
3965   }
3966
3967   for (i = 0; i < MAX_PLAYERS; i++)
3968   {
3969     stored_player[i].connected = FALSE;
3970
3971     // in network game mode, the local player might not be the first player
3972     if (stored_player[i].connected_locally)
3973       local_player = &stored_player[i];
3974   }
3975
3976   if (!network.enabled)
3977     local_player->connected = TRUE;
3978
3979   if (tape.playing)
3980   {
3981     for (i = 0; i < MAX_PLAYERS; i++)
3982       stored_player[i].connected = tape.player_participates[i];
3983   }
3984   else if (network.enabled)
3985   {
3986     // add team mode players connected over the network (needed for correct
3987     // assignment of player figures from level to locally playing players)
3988
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990       if (stored_player[i].connected_network)
3991         stored_player[i].connected = TRUE;
3992   }
3993   else if (game.team_mode)
3994   {
3995     // try to guess locally connected team mode players (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (setup.input[i].use_joystick ||
4000           setup.input[i].key.left != KSYM_UNDEFINED)
4001         stored_player[i].connected = TRUE;
4002   }
4003
4004 #if DEBUG_INIT_PLAYER
4005   DebugPrintPlayerStatus("Player status after level initialization");
4006 #endif
4007
4008 #if DEBUG_INIT_PLAYER
4009   Debug("game:init:player", "Reassigning players ...");
4010 #endif
4011
4012   // check if any connected player was not found in playfield
4013   for (i = 0; i < MAX_PLAYERS; i++)
4014   {
4015     struct PlayerInfo *player = &stored_player[i];
4016
4017     if (player->connected && !player->present)
4018     {
4019       struct PlayerInfo *field_player = NULL;
4020
4021 #if DEBUG_INIT_PLAYER
4022       Debug("game:init:player",
4023             "- looking for field player for player %d ...", i + 1);
4024 #endif
4025
4026       // assign first free player found that is present in the playfield
4027
4028       // first try: look for unmapped playfield player that is not connected
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030         if (field_player == NULL &&
4031             stored_player[j].present &&
4032             !stored_player[j].mapped &&
4033             !stored_player[j].connected)
4034           field_player = &stored_player[j];
4035
4036       // second try: look for *any* unmapped playfield player
4037       for (j = 0; j < MAX_PLAYERS; j++)
4038         if (field_player == NULL &&
4039             stored_player[j].present &&
4040             !stored_player[j].mapped)
4041           field_player = &stored_player[j];
4042
4043       if (field_player != NULL)
4044       {
4045         int jx = field_player->jx, jy = field_player->jy;
4046
4047 #if DEBUG_INIT_PLAYER
4048         Debug("game:init:player", "- found player %d",
4049               field_player->index_nr + 1);
4050 #endif
4051
4052         player->present = FALSE;
4053         player->active = FALSE;
4054
4055         field_player->present = TRUE;
4056         field_player->active = TRUE;
4057
4058         /*
4059         player->initial_element = field_player->initial_element;
4060         player->artwork_element = field_player->artwork_element;
4061
4062         player->block_last_field       = field_player->block_last_field;
4063         player->block_delay_adjustment = field_player->block_delay_adjustment;
4064         */
4065
4066         StorePlayer[jx][jy] = field_player->element_nr;
4067
4068         field_player->jx = field_player->last_jx = jx;
4069         field_player->jy = field_player->last_jy = jy;
4070
4071         if (local_player == player)
4072           local_player = field_player;
4073
4074         map_player_action[field_player->index_nr] = i;
4075
4076         field_player->mapped = TRUE;
4077
4078 #if DEBUG_INIT_PLAYER
4079         Debug("game:init:player", "- map_player_action[%d] == %d",
4080               field_player->index_nr + 1, i + 1);
4081 #endif
4082       }
4083     }
4084
4085     if (player->connected && player->present)
4086       player->mapped = TRUE;
4087   }
4088
4089 #if DEBUG_INIT_PLAYER
4090   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4091 #endif
4092
4093 #else
4094
4095   // check if any connected player was not found in playfield
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097   {
4098     struct PlayerInfo *player = &stored_player[i];
4099
4100     if (player->connected && !player->present)
4101     {
4102       for (j = 0; j < MAX_PLAYERS; j++)
4103       {
4104         struct PlayerInfo *field_player = &stored_player[j];
4105         int jx = field_player->jx, jy = field_player->jy;
4106
4107         // assign first free player found that is present in the playfield
4108         if (field_player->present && !field_player->connected)
4109         {
4110           player->present = TRUE;
4111           player->active = TRUE;
4112
4113           field_player->present = FALSE;
4114           field_player->active = FALSE;
4115
4116           player->initial_element = field_player->initial_element;
4117           player->artwork_element = field_player->artwork_element;
4118
4119           player->block_last_field       = field_player->block_last_field;
4120           player->block_delay_adjustment = field_player->block_delay_adjustment;
4121
4122           StorePlayer[jx][jy] = player->element_nr;
4123
4124           player->jx = player->last_jx = jx;
4125           player->jy = player->last_jy = jy;
4126
4127           break;
4128         }
4129       }
4130     }
4131   }
4132 #endif
4133
4134 #if 0
4135   Debug("game:init:player", "local_player->present == %d",
4136         local_player->present);
4137 #endif
4138
4139   // set focus to local player for network games, else to all players
4140   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4141   game.centered_player_nr_next = game.centered_player_nr;
4142   game.set_centered_player = FALSE;
4143   game.set_centered_player_wrap = FALSE;
4144
4145   if (network_playing && tape.recording)
4146   {
4147     // store client dependent player focus when recording network games
4148     tape.centered_player_nr_next = game.centered_player_nr_next;
4149     tape.set_centered_player = TRUE;
4150   }
4151
4152   if (tape.playing)
4153   {
4154     // when playing a tape, eliminate all players who do not participate
4155
4156 #if USE_NEW_PLAYER_ASSIGNMENTS
4157
4158     if (!game.team_mode)
4159     {
4160       for (i = 0; i < MAX_PLAYERS; i++)
4161       {
4162         if (stored_player[i].active &&
4163             !tape.player_participates[map_player_action[i]])
4164         {
4165           struct PlayerInfo *player = &stored_player[i];
4166           int jx = player->jx, jy = player->jy;
4167
4168 #if DEBUG_INIT_PLAYER
4169           Debug("game:init:player", "Removing player %d at (%d, %d)",
4170                 i + 1, jx, jy);
4171 #endif
4172
4173           player->active = FALSE;
4174           StorePlayer[jx][jy] = 0;
4175           Tile[jx][jy] = EL_EMPTY;
4176         }
4177       }
4178     }
4179
4180 #else
4181
4182     for (i = 0; i < MAX_PLAYERS; i++)
4183     {
4184       if (stored_player[i].active &&
4185           !tape.player_participates[i])
4186       {
4187         struct PlayerInfo *player = &stored_player[i];
4188         int jx = player->jx, jy = player->jy;
4189
4190         player->active = FALSE;
4191         StorePlayer[jx][jy] = 0;
4192         Tile[jx][jy] = EL_EMPTY;
4193       }
4194     }
4195 #endif
4196   }
4197   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4198   {
4199     // when in single player mode, eliminate all but the local player
4200
4201     for (i = 0; i < MAX_PLAYERS; i++)
4202     {
4203       struct PlayerInfo *player = &stored_player[i];
4204
4205       if (player->active && player != local_player)
4206       {
4207         int jx = player->jx, jy = player->jy;
4208
4209         player->active = FALSE;
4210         player->present = FALSE;
4211
4212         StorePlayer[jx][jy] = 0;
4213         Tile[jx][jy] = EL_EMPTY;
4214       }
4215     }
4216   }
4217
4218   for (i = 0; i < MAX_PLAYERS; i++)
4219     if (stored_player[i].active)
4220       game.players_still_needed++;
4221
4222   if (level.solved_by_one_player)
4223     game.players_still_needed = 1;
4224
4225   // when recording the game, store which players take part in the game
4226   if (tape.recording)
4227   {
4228 #if USE_NEW_PLAYER_ASSIGNMENTS
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230       if (stored_player[i].connected)
4231         tape.player_participates[i] = TRUE;
4232 #else
4233     for (i = 0; i < MAX_PLAYERS; i++)
4234       if (stored_player[i].active)
4235         tape.player_participates[i] = TRUE;
4236 #endif
4237   }
4238
4239 #if DEBUG_INIT_PLAYER
4240   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4241 #endif
4242
4243   if (BorderElement == EL_EMPTY)
4244   {
4245     SBX_Left = 0;
4246     SBX_Right = lev_fieldx - SCR_FIELDX;
4247     SBY_Upper = 0;
4248     SBY_Lower = lev_fieldy - SCR_FIELDY;
4249   }
4250   else
4251   {
4252     SBX_Left = -1;
4253     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4254     SBY_Upper = -1;
4255     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4256   }
4257
4258   if (full_lev_fieldx <= SCR_FIELDX)
4259     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4260   if (full_lev_fieldy <= SCR_FIELDY)
4261     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4262
4263   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4264     SBX_Left--;
4265   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4266     SBY_Upper--;
4267
4268   // if local player not found, look for custom element that might create
4269   // the player (make some assumptions about the right custom element)
4270   if (!local_player->present)
4271   {
4272     int start_x = 0, start_y = 0;
4273     int found_rating = 0;
4274     int found_element = EL_UNDEFINED;
4275     int player_nr = local_player->index_nr;
4276
4277     SCAN_PLAYFIELD(x, y)
4278     {
4279       int element = Tile[x][y];
4280       int content;
4281       int xx, yy;
4282       boolean is_player;
4283
4284       if (level.use_start_element[player_nr] &&
4285           level.start_element[player_nr] == element &&
4286           found_rating < 4)
4287       {
4288         start_x = x;
4289         start_y = y;
4290
4291         found_rating = 4;
4292         found_element = element;
4293       }
4294
4295       if (!IS_CUSTOM_ELEMENT(element))
4296         continue;
4297
4298       if (CAN_CHANGE(element))
4299       {
4300         for (i = 0; i < element_info[element].num_change_pages; i++)
4301         {
4302           // check for player created from custom element as single target
4303           content = element_info[element].change_page[i].target_element;
4304           is_player = ELEM_IS_PLAYER(content);
4305
4306           if (is_player && (found_rating < 3 ||
4307                             (found_rating == 3 && element < found_element)))
4308           {
4309             start_x = x;
4310             start_y = y;
4311
4312             found_rating = 3;
4313             found_element = element;
4314           }
4315         }
4316       }
4317
4318       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4319       {
4320         // check for player created from custom element as explosion content
4321         content = element_info[element].content.e[xx][yy];
4322         is_player = ELEM_IS_PLAYER(content);
4323
4324         if (is_player && (found_rating < 2 ||
4325                           (found_rating == 2 && element < found_element)))
4326         {
4327           start_x = x + xx - 1;
4328           start_y = y + yy - 1;
4329
4330           found_rating = 2;
4331           found_element = element;
4332         }
4333
4334         if (!CAN_CHANGE(element))
4335           continue;
4336
4337         for (i = 0; i < element_info[element].num_change_pages; i++)
4338         {
4339           // check for player created from custom element as extended target
4340           content =
4341             element_info[element].change_page[i].target_content.e[xx][yy];
4342
4343           is_player = ELEM_IS_PLAYER(content);
4344
4345           if (is_player && (found_rating < 1 ||
4346                             (found_rating == 1 && element < found_element)))
4347           {
4348             start_x = x + xx - 1;
4349             start_y = y + yy - 1;
4350
4351             found_rating = 1;
4352             found_element = element;
4353           }
4354         }
4355       }
4356     }
4357
4358     scroll_x = SCROLL_POSITION_X(start_x);
4359     scroll_y = SCROLL_POSITION_Y(start_y);
4360   }
4361   else
4362   {
4363     scroll_x = SCROLL_POSITION_X(local_player->jx);
4364     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4365   }
4366
4367   // !!! FIX THIS (START) !!!
4368   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4369   {
4370     InitGameEngine_EM();
4371   }
4372   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4373   {
4374     InitGameEngine_SP();
4375   }
4376   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4377   {
4378     InitGameEngine_MM();
4379   }
4380   else
4381   {
4382     DrawLevel(REDRAW_FIELD);
4383     DrawAllPlayers();
4384
4385     // after drawing the level, correct some elements
4386     if (game.timegate_time_left == 0)
4387       CloseAllOpenTimegates();
4388   }
4389
4390   // blit playfield from scroll buffer to normal back buffer for fading in
4391   BlitScreenToBitmap(backbuffer);
4392   // !!! FIX THIS (END) !!!
4393
4394   DrawMaskedBorder(fade_mask);
4395
4396   FadeIn(fade_mask);
4397
4398 #if 1
4399   // full screen redraw is required at this point in the following cases:
4400   // - special editor door undrawn when game was started from level editor
4401   // - drawing area (playfield) was changed and has to be removed completely
4402   redraw_mask = REDRAW_ALL;
4403   BackToFront();
4404 #endif
4405
4406   if (!game.restart_level)
4407   {
4408     // copy default game door content to main double buffer
4409
4410     // !!! CHECK AGAIN !!!
4411     SetPanelBackground();
4412     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4413     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4414   }
4415
4416   SetPanelBackground();
4417   SetDrawBackgroundMask(REDRAW_DOOR_1);
4418
4419   UpdateAndDisplayGameControlValues();
4420
4421   if (!game.restart_level)
4422   {
4423     UnmapGameButtons();
4424     UnmapTapeButtons();
4425
4426     FreeGameButtons();
4427     CreateGameButtons();
4428
4429     MapGameButtons();
4430     MapTapeButtons();
4431
4432     // copy actual game door content to door double buffer for OpenDoor()
4433     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4434
4435     OpenDoor(DOOR_OPEN_ALL);
4436
4437     KeyboardAutoRepeatOffUnlessAutoplay();
4438
4439 #if DEBUG_INIT_PLAYER
4440     DebugPrintPlayerStatus("Player status (final)");
4441 #endif
4442   }
4443
4444   UnmapAllGadgets();
4445
4446   MapGameButtons();
4447   MapTapeButtons();
4448
4449   if (!game.restart_level && !tape.playing)
4450   {
4451     LevelStats_incPlayed(level_nr);
4452
4453     SaveLevelSetup_SeriesInfo();
4454   }
4455
4456   game.restart_level = FALSE;
4457   game.restart_game_message = NULL;
4458   game.request_active = 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 int score, score_final;
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 game_over_delay_value_1 = 50;
4725   int game_over_delay_value_2 = 25;
4726   int game_over_delay_value_3 = 50;
4727
4728   if (!game.LevelSolved_GameWon)
4729   {
4730     int i;
4731
4732     // do not start end game actions before the player stops moving (to exit)
4733     if (local_player->active && local_player->MovPos)
4734       return;
4735
4736     game.LevelSolved_GameWon = TRUE;
4737     game.LevelSolved_SaveTape = tape.recording;
4738     game.LevelSolved_SaveScore = !tape.playing;
4739
4740     if (!tape.playing)
4741     {
4742       LevelStats_incSolved(level_nr);
4743
4744       SaveLevelSetup_SeriesInfo();
4745     }
4746
4747     if (tape.auto_play)         // tape might already be stopped here
4748       tape.auto_play_level_solved = TRUE;
4749
4750     TapeStop();
4751
4752     game_over_delay_1 = 0;
4753     game_over_delay_2 = 0;
4754     game_over_delay_3 = game_over_delay_value_3;
4755
4756     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4757     score = score_final = game.score_final;
4758     health = health_final = game.health_final;
4759
4760     if (level.score[SC_TIME_BONUS] > 0)
4761     {
4762       if (TimeLeft > 0)
4763       {
4764         time_final = 0;
4765         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4766       }
4767       else if (game.no_time_limit && TimePlayed < 999)
4768       {
4769         time_final = 999;
4770         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4771       }
4772
4773       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4774
4775       game_over_delay_1 = game_over_delay_value_1;
4776
4777       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4778       {
4779         health_final = 0;
4780         score_final += health * level.score[SC_TIME_BONUS];
4781
4782         game_over_delay_2 = game_over_delay_value_2;
4783       }
4784
4785       game.score_final = score_final;
4786       game.health_final = health_final;
4787     }
4788
4789     if (level_editor_test_game)
4790     {
4791       time = time_final;
4792       score = score_final;
4793
4794       game.LevelSolved_CountingTime = time;
4795       game.LevelSolved_CountingScore = score;
4796
4797       game_panel_controls[GAME_PANEL_TIME].value = time;
4798       game_panel_controls[GAME_PANEL_SCORE].value = score;
4799
4800       DisplayGameControlValues();
4801     }
4802
4803     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4804     {
4805       // check if last player has left the level
4806       if (game.exit_x >= 0 &&
4807           game.exit_y >= 0)
4808       {
4809         int x = game.exit_x;
4810         int y = game.exit_y;
4811         int element = Tile[x][y];
4812
4813         // close exit door after last player
4814         if ((game.all_players_gone &&
4815              (element == EL_EXIT_OPEN ||
4816               element == EL_SP_EXIT_OPEN ||
4817               element == EL_STEEL_EXIT_OPEN)) ||
4818             element == EL_EM_EXIT_OPEN ||
4819             element == EL_EM_STEEL_EXIT_OPEN)
4820         {
4821
4822           Tile[x][y] =
4823             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4824              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4825              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4826              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4827              EL_EM_STEEL_EXIT_CLOSING);
4828
4829           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4830         }
4831
4832         // player disappears
4833         DrawLevelField(x, y);
4834       }
4835
4836       for (i = 0; i < MAX_PLAYERS; i++)
4837       {
4838         struct PlayerInfo *player = &stored_player[i];
4839
4840         if (player->present)
4841         {
4842           RemovePlayer(player);
4843
4844           // player disappears
4845           DrawLevelField(player->jx, player->jy);
4846         }
4847       }
4848     }
4849
4850     PlaySound(SND_GAME_WINNING);
4851   }
4852
4853   if (game_over_delay_1 > 0)
4854   {
4855     game_over_delay_1--;
4856
4857     return;
4858   }
4859
4860   if (time != time_final)
4861   {
4862     int time_to_go = ABS(time_final - time);
4863     int time_count_dir = (time < time_final ? +1 : -1);
4864
4865     if (time_to_go < time_count_steps)
4866       time_count_steps = 1;
4867
4868     time  += time_count_steps * time_count_dir;
4869     score += time_count_steps * level.score[SC_TIME_BONUS];
4870
4871     game.LevelSolved_CountingTime = time;
4872     game.LevelSolved_CountingScore = score;
4873
4874     game_panel_controls[GAME_PANEL_TIME].value = time;
4875     game_panel_controls[GAME_PANEL_SCORE].value = score;
4876
4877     DisplayGameControlValues();
4878
4879     if (time == time_final)
4880       StopSound(SND_GAME_LEVELTIME_BONUS);
4881     else if (setup.sound_loops)
4882       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4883     else
4884       PlaySound(SND_GAME_LEVELTIME_BONUS);
4885
4886     return;
4887   }
4888
4889   if (game_over_delay_2 > 0)
4890   {
4891     game_over_delay_2--;
4892
4893     return;
4894   }
4895
4896   if (health != health_final)
4897   {
4898     int health_count_dir = (health < health_final ? +1 : -1);
4899
4900     health += health_count_dir;
4901     score  += level.score[SC_TIME_BONUS];
4902
4903     game.LevelSolved_CountingHealth = health;
4904     game.LevelSolved_CountingScore = score;
4905
4906     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4907     game_panel_controls[GAME_PANEL_SCORE].value = score;
4908
4909     DisplayGameControlValues();
4910
4911     if (health == health_final)
4912       StopSound(SND_GAME_LEVELTIME_BONUS);
4913     else if (setup.sound_loops)
4914       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4915     else
4916       PlaySound(SND_GAME_LEVELTIME_BONUS);
4917
4918     return;
4919   }
4920
4921   game.panel.active = FALSE;
4922
4923   if (game_over_delay_3 > 0)
4924   {
4925     game_over_delay_3--;
4926
4927     return;
4928   }
4929
4930   GameEnd();
4931 }
4932
4933 void GameEnd(void)
4934 {
4935   // used instead of "level_nr" (needed for network games)
4936   int last_level_nr = levelset.level_nr;
4937   int hi_pos;
4938
4939   game.LevelSolved_GameEnd = TRUE;
4940
4941   if (game.LevelSolved_SaveTape)
4942   {
4943     // make sure that request dialog to save tape does not open door again
4944     if (!global.use_envelope_request)
4945       CloseDoor(DOOR_CLOSE_1);
4946
4947     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4948   }
4949
4950   // if no tape is to be saved, close both doors simultaneously
4951   CloseDoor(DOOR_CLOSE_ALL);
4952
4953   if (level_editor_test_game)
4954   {
4955     SetGameStatus(GAME_MODE_MAIN);
4956
4957     DrawMainMenu();
4958
4959     return;
4960   }
4961
4962   if (!game.LevelSolved_SaveScore)
4963   {
4964     SetGameStatus(GAME_MODE_MAIN);
4965
4966     DrawMainMenu();
4967
4968     return;
4969   }
4970
4971   if (level_nr == leveldir_current->handicap_level)
4972   {
4973     leveldir_current->handicap_level++;
4974
4975     SaveLevelSetup_SeriesInfo();
4976   }
4977
4978   if (setup.increment_levels &&
4979       level_nr < leveldir_current->last_level &&
4980       !network_playing)
4981   {
4982     level_nr++;         // advance to next level
4983     TapeErase();        // start with empty tape
4984
4985     if (setup.auto_play_next_level)
4986     {
4987       LoadLevel(level_nr);
4988
4989       SaveLevelSetup_SeriesInfo();
4990     }
4991   }
4992
4993   hi_pos = NewHiScore(last_level_nr);
4994
4995   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4996   {
4997     SetGameStatus(GAME_MODE_SCORES);
4998
4999     DrawHallOfFame(last_level_nr, hi_pos);
5000   }
5001   else if (setup.auto_play_next_level && setup.increment_levels &&
5002            last_level_nr < leveldir_current->last_level &&
5003            !network_playing)
5004   {
5005     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5006   }
5007   else
5008   {
5009     SetGameStatus(GAME_MODE_MAIN);
5010
5011     DrawMainMenu();
5012   }
5013 }
5014
5015 int NewHiScore(int level_nr)
5016 {
5017   int k, l;
5018   int position = -1;
5019   boolean one_score_entry_per_name = !program.many_scores_per_name;
5020
5021   LoadScore(level_nr);
5022
5023   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5024       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5025     return -1;
5026
5027   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5028   {
5029     if (game.score_final > highscore[k].Score)
5030     {
5031       // player has made it to the hall of fame
5032
5033       if (k < MAX_SCORE_ENTRIES - 1)
5034       {
5035         int m = MAX_SCORE_ENTRIES - 1;
5036
5037         if (one_score_entry_per_name)
5038         {
5039           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5040             if (strEqual(setup.player_name, highscore[l].Name))
5041               m = l;
5042
5043           if (m == k)   // player's new highscore overwrites his old one
5044             goto put_into_list;
5045         }
5046
5047         for (l = m; l > k; l--)
5048         {
5049           strcpy(highscore[l].Name, highscore[l - 1].Name);
5050           highscore[l].Score = highscore[l - 1].Score;
5051         }
5052       }
5053
5054       put_into_list:
5055
5056       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5057       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5058       highscore[k].Score = game.score_final;
5059       position = k;
5060
5061       break;
5062     }
5063     else if (one_score_entry_per_name &&
5064              !strncmp(setup.player_name, highscore[k].Name,
5065                       MAX_PLAYER_NAME_LEN))
5066       break;    // player already there with a higher score
5067   }
5068
5069   if (position >= 0) 
5070     SaveScore(level_nr);
5071
5072   return position;
5073 }
5074
5075 static int getElementMoveStepsizeExt(int x, int y, int direction)
5076 {
5077   int element = Tile[x][y];
5078   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5079   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5080   int horiz_move = (dx != 0);
5081   int sign = (horiz_move ? dx : dy);
5082   int step = sign * element_info[element].move_stepsize;
5083
5084   // special values for move stepsize for spring and things on conveyor belt
5085   if (horiz_move)
5086   {
5087     if (CAN_FALL(element) &&
5088         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5089       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5090     else if (element == EL_SPRING)
5091       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5092   }
5093
5094   return step;
5095 }
5096
5097 static int getElementMoveStepsize(int x, int y)
5098 {
5099   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5100 }
5101
5102 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5103 {
5104   if (player->GfxAction != action || player->GfxDir != dir)
5105   {
5106     player->GfxAction = action;
5107     player->GfxDir = dir;
5108     player->Frame = 0;
5109     player->StepFrame = 0;
5110   }
5111 }
5112
5113 static void ResetGfxFrame(int x, int y)
5114 {
5115   // profiling showed that "autotest" spends 10~20% of its time in this function
5116   if (DrawingDeactivatedField())
5117     return;
5118
5119   int element = Tile[x][y];
5120   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5121
5122   if (graphic_info[graphic].anim_global_sync)
5123     GfxFrame[x][y] = FrameCounter;
5124   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5125     GfxFrame[x][y] = CustomValue[x][y];
5126   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5127     GfxFrame[x][y] = element_info[element].collect_score;
5128   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5129     GfxFrame[x][y] = ChangeDelay[x][y];
5130 }
5131
5132 static void ResetGfxAnimation(int x, int y)
5133 {
5134   GfxAction[x][y] = ACTION_DEFAULT;
5135   GfxDir[x][y] = MovDir[x][y];
5136   GfxFrame[x][y] = 0;
5137
5138   ResetGfxFrame(x, y);
5139 }
5140
5141 static void ResetRandomAnimationValue(int x, int y)
5142 {
5143   GfxRandom[x][y] = INIT_GFX_RANDOM();
5144 }
5145
5146 static void InitMovingField(int x, int y, int direction)
5147 {
5148   int element = Tile[x][y];
5149   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5150   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5151   int newx = x + dx;
5152   int newy = y + dy;
5153   boolean is_moving_before, is_moving_after;
5154
5155   // check if element was/is moving or being moved before/after mode change
5156   is_moving_before = (WasJustMoving[x][y] != 0);
5157   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5158
5159   // reset animation only for moving elements which change direction of moving
5160   // or which just started or stopped moving
5161   // (else CEs with property "can move" / "not moving" are reset each frame)
5162   if (is_moving_before != is_moving_after ||
5163       direction != MovDir[x][y])
5164     ResetGfxAnimation(x, y);
5165
5166   MovDir[x][y] = direction;
5167   GfxDir[x][y] = direction;
5168
5169   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5170                      direction == MV_DOWN && CAN_FALL(element) ?
5171                      ACTION_FALLING : ACTION_MOVING);
5172
5173   // this is needed for CEs with property "can move" / "not moving"
5174
5175   if (is_moving_after)
5176   {
5177     if (Tile[newx][newy] == EL_EMPTY)
5178       Tile[newx][newy] = EL_BLOCKED;
5179
5180     MovDir[newx][newy] = MovDir[x][y];
5181
5182     CustomValue[newx][newy] = CustomValue[x][y];
5183
5184     GfxFrame[newx][newy] = GfxFrame[x][y];
5185     GfxRandom[newx][newy] = GfxRandom[x][y];
5186     GfxAction[newx][newy] = GfxAction[x][y];
5187     GfxDir[newx][newy] = GfxDir[x][y];
5188   }
5189 }
5190
5191 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5192 {
5193   int direction = MovDir[x][y];
5194   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5195   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5196
5197   *goes_to_x = newx;
5198   *goes_to_y = newy;
5199 }
5200
5201 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5202 {
5203   int oldx = x, oldy = y;
5204   int direction = MovDir[x][y];
5205
5206   if (direction == MV_LEFT)
5207     oldx++;
5208   else if (direction == MV_RIGHT)
5209     oldx--;
5210   else if (direction == MV_UP)
5211     oldy++;
5212   else if (direction == MV_DOWN)
5213     oldy--;
5214
5215   *comes_from_x = oldx;
5216   *comes_from_y = oldy;
5217 }
5218
5219 static int MovingOrBlocked2Element(int x, int y)
5220 {
5221   int element = Tile[x][y];
5222
5223   if (element == EL_BLOCKED)
5224   {
5225     int oldx, oldy;
5226
5227     Blocked2Moving(x, y, &oldx, &oldy);
5228     return Tile[oldx][oldy];
5229   }
5230   else
5231     return element;
5232 }
5233
5234 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5235 {
5236   // like MovingOrBlocked2Element(), but if element is moving
5237   // and (x,y) is the field the moving element is just leaving,
5238   // return EL_BLOCKED instead of the element value
5239   int element = Tile[x][y];
5240
5241   if (IS_MOVING(x, y))
5242   {
5243     if (element == EL_BLOCKED)
5244     {
5245       int oldx, oldy;
5246
5247       Blocked2Moving(x, y, &oldx, &oldy);
5248       return Tile[oldx][oldy];
5249     }
5250     else
5251       return EL_BLOCKED;
5252   }
5253   else
5254     return element;
5255 }
5256
5257 static void RemoveField(int x, int y)
5258 {
5259   Tile[x][y] = EL_EMPTY;
5260
5261   MovPos[x][y] = 0;
5262   MovDir[x][y] = 0;
5263   MovDelay[x][y] = 0;
5264
5265   CustomValue[x][y] = 0;
5266
5267   AmoebaNr[x][y] = 0;
5268   ChangeDelay[x][y] = 0;
5269   ChangePage[x][y] = -1;
5270   Pushed[x][y] = FALSE;
5271
5272   GfxElement[x][y] = EL_UNDEFINED;
5273   GfxAction[x][y] = ACTION_DEFAULT;
5274   GfxDir[x][y] = MV_NONE;
5275 }
5276
5277 static void RemoveMovingField(int x, int y)
5278 {
5279   int oldx = x, oldy = y, newx = x, newy = y;
5280   int element = Tile[x][y];
5281   int next_element = EL_UNDEFINED;
5282
5283   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5284     return;
5285
5286   if (IS_MOVING(x, y))
5287   {
5288     Moving2Blocked(x, y, &newx, &newy);
5289
5290     if (Tile[newx][newy] != EL_BLOCKED)
5291     {
5292       // element is moving, but target field is not free (blocked), but
5293       // already occupied by something different (example: acid pool);
5294       // in this case, only remove the moving field, but not the target
5295
5296       RemoveField(oldx, oldy);
5297
5298       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5299
5300       TEST_DrawLevelField(oldx, oldy);
5301
5302       return;
5303     }
5304   }
5305   else if (element == EL_BLOCKED)
5306   {
5307     Blocked2Moving(x, y, &oldx, &oldy);
5308     if (!IS_MOVING(oldx, oldy))
5309       return;
5310   }
5311
5312   if (element == EL_BLOCKED &&
5313       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5314        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5315        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5316        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5317        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5318        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5319     next_element = get_next_element(Tile[oldx][oldy]);
5320
5321   RemoveField(oldx, oldy);
5322   RemoveField(newx, newy);
5323
5324   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5325
5326   if (next_element != EL_UNDEFINED)
5327     Tile[oldx][oldy] = next_element;
5328
5329   TEST_DrawLevelField(oldx, oldy);
5330   TEST_DrawLevelField(newx, newy);
5331 }
5332
5333 void DrawDynamite(int x, int y)
5334 {
5335   int sx = SCREENX(x), sy = SCREENY(y);
5336   int graphic = el2img(Tile[x][y]);
5337   int frame;
5338
5339   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5340     return;
5341
5342   if (IS_WALKABLE_INSIDE(Back[x][y]))
5343     return;
5344
5345   if (Back[x][y])
5346     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5347   else if (Store[x][y])
5348     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5349
5350   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5351
5352   if (Back[x][y] || Store[x][y])
5353     DrawGraphicThruMask(sx, sy, graphic, frame);
5354   else
5355     DrawGraphic(sx, sy, graphic, frame);
5356 }
5357
5358 static void CheckDynamite(int x, int y)
5359 {
5360   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5361   {
5362     MovDelay[x][y]--;
5363
5364     if (MovDelay[x][y] != 0)
5365     {
5366       DrawDynamite(x, y);
5367       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5368
5369       return;
5370     }
5371   }
5372
5373   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5374
5375   Bang(x, y);
5376 }
5377
5378 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5379 {
5380   boolean num_checked_players = 0;
5381   int i;
5382
5383   for (i = 0; i < MAX_PLAYERS; i++)
5384   {
5385     if (stored_player[i].active)
5386     {
5387       int sx = stored_player[i].jx;
5388       int sy = stored_player[i].jy;
5389
5390       if (num_checked_players == 0)
5391       {
5392         *sx1 = *sx2 = sx;
5393         *sy1 = *sy2 = sy;
5394       }
5395       else
5396       {
5397         *sx1 = MIN(*sx1, sx);
5398         *sy1 = MIN(*sy1, sy);
5399         *sx2 = MAX(*sx2, sx);
5400         *sy2 = MAX(*sy2, sy);
5401       }
5402
5403       num_checked_players++;
5404     }
5405   }
5406 }
5407
5408 static boolean checkIfAllPlayersFitToScreen_RND(void)
5409 {
5410   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5411
5412   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5413
5414   return (sx2 - sx1 < SCR_FIELDX &&
5415           sy2 - sy1 < SCR_FIELDY);
5416 }
5417
5418 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5419 {
5420   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5421
5422   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5423
5424   *sx = (sx1 + sx2) / 2;
5425   *sy = (sy1 + sy2) / 2;
5426 }
5427
5428 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5429                                boolean center_screen, boolean quick_relocation)
5430 {
5431   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5432   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5433   boolean no_delay = (tape.warp_forward);
5434   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5435   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5436   int new_scroll_x, new_scroll_y;
5437
5438   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5439   {
5440     // case 1: quick relocation inside visible screen (without scrolling)
5441
5442     RedrawPlayfield();
5443
5444     return;
5445   }
5446
5447   if (!level.shifted_relocation || center_screen)
5448   {
5449     // relocation _with_ centering of screen
5450
5451     new_scroll_x = SCROLL_POSITION_X(x);
5452     new_scroll_y = SCROLL_POSITION_Y(y);
5453   }
5454   else
5455   {
5456     // relocation _without_ centering of screen
5457
5458     int center_scroll_x = SCROLL_POSITION_X(old_x);
5459     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5460     int offset_x = x + (scroll_x - center_scroll_x);
5461     int offset_y = y + (scroll_y - center_scroll_y);
5462
5463     // for new screen position, apply previous offset to center position
5464     new_scroll_x = SCROLL_POSITION_X(offset_x);
5465     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5466   }
5467
5468   if (quick_relocation)
5469   {
5470     // case 2: quick relocation (redraw without visible scrolling)
5471
5472     scroll_x = new_scroll_x;
5473     scroll_y = new_scroll_y;
5474
5475     RedrawPlayfield();
5476
5477     return;
5478   }
5479
5480   // case 3: visible relocation (with scrolling to new position)
5481
5482   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5483
5484   SetVideoFrameDelay(wait_delay_value);
5485
5486   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5487   {
5488     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5489     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5490
5491     if (dx == 0 && dy == 0)             // no scrolling needed at all
5492       break;
5493
5494     scroll_x -= dx;
5495     scroll_y -= dy;
5496
5497     // set values for horizontal/vertical screen scrolling (half tile size)
5498     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5499     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5500     int pos_x = dx * TILEX / 2;
5501     int pos_y = dy * TILEY / 2;
5502     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5503     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5504
5505     ScrollLevel(dx, dy);
5506     DrawAllPlayers();
5507
5508     // scroll in two steps of half tile size to make things smoother
5509     BlitScreenToBitmapExt_RND(window, fx, fy);
5510
5511     // scroll second step to align at full tile size
5512     BlitScreenToBitmap(window);
5513   }
5514
5515   DrawAllPlayers();
5516   BackToFront();
5517
5518   SetVideoFrameDelay(frame_delay_value_old);
5519 }
5520
5521 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5522 {
5523   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5524   int player_nr = GET_PLAYER_NR(el_player);
5525   struct PlayerInfo *player = &stored_player[player_nr];
5526   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5527   boolean no_delay = (tape.warp_forward);
5528   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5529   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5530   int old_jx = player->jx;
5531   int old_jy = player->jy;
5532   int old_element = Tile[old_jx][old_jy];
5533   int element = Tile[jx][jy];
5534   boolean player_relocated = (old_jx != jx || old_jy != jy);
5535
5536   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5537   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5538   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5539   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5540   int leave_side_horiz = move_dir_horiz;
5541   int leave_side_vert  = move_dir_vert;
5542   int enter_side = enter_side_horiz | enter_side_vert;
5543   int leave_side = leave_side_horiz | leave_side_vert;
5544
5545   if (player->buried)           // do not reanimate dead player
5546     return;
5547
5548   if (!player_relocated)        // no need to relocate the player
5549     return;
5550
5551   if (IS_PLAYER(jx, jy))        // player already placed at new position
5552   {
5553     RemoveField(jx, jy);        // temporarily remove newly placed player
5554     DrawLevelField(jx, jy);
5555   }
5556
5557   if (player->present)
5558   {
5559     while (player->MovPos)
5560     {
5561       ScrollPlayer(player, SCROLL_GO_ON);
5562       ScrollScreen(NULL, SCROLL_GO_ON);
5563
5564       AdvanceFrameAndPlayerCounters(player->index_nr);
5565
5566       DrawPlayer(player);
5567
5568       BackToFront_WithFrameDelay(wait_delay_value);
5569     }
5570
5571     DrawPlayer(player);         // needed here only to cleanup last field
5572     DrawLevelField(player->jx, player->jy);     // remove player graphic
5573
5574     player->is_moving = FALSE;
5575   }
5576
5577   if (IS_CUSTOM_ELEMENT(old_element))
5578     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5579                                CE_LEFT_BY_PLAYER,
5580                                player->index_bit, leave_side);
5581
5582   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5583                                       CE_PLAYER_LEAVES_X,
5584                                       player->index_bit, leave_side);
5585
5586   Tile[jx][jy] = el_player;
5587   InitPlayerField(jx, jy, el_player, TRUE);
5588
5589   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5590      possible that the relocation target field did not contain a player element,
5591      but a walkable element, to which the new player was relocated -- in this
5592      case, restore that (already initialized!) element on the player field */
5593   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5594   {
5595     Tile[jx][jy] = element;     // restore previously existing element
5596   }
5597
5598   // only visually relocate centered player
5599   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5600                      FALSE, level.instant_relocation);
5601
5602   TestIfPlayerTouchesBadThing(jx, jy);
5603   TestIfPlayerTouchesCustomElement(jx, jy);
5604
5605   if (IS_CUSTOM_ELEMENT(element))
5606     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5607                                player->index_bit, enter_side);
5608
5609   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5610                                       player->index_bit, enter_side);
5611
5612   if (player->is_switching)
5613   {
5614     /* ensure that relocation while still switching an element does not cause
5615        a new element to be treated as also switched directly after relocation
5616        (this is important for teleporter switches that teleport the player to
5617        a place where another teleporter switch is in the same direction, which
5618        would then incorrectly be treated as immediately switched before the
5619        direction key that caused the switch was released) */
5620
5621     player->switch_x += jx - old_jx;
5622     player->switch_y += jy - old_jy;
5623   }
5624 }
5625
5626 static void Explode(int ex, int ey, int phase, int mode)
5627 {
5628   int x, y;
5629   int last_phase;
5630   int border_element;
5631
5632   // !!! eliminate this variable !!!
5633   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5634
5635   if (game.explosions_delayed)
5636   {
5637     ExplodeField[ex][ey] = mode;
5638     return;
5639   }
5640
5641   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5642   {
5643     int center_element = Tile[ex][ey];
5644     int artwork_element, explosion_element;     // set these values later
5645
5646     // remove things displayed in background while burning dynamite
5647     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5648       Back[ex][ey] = 0;
5649
5650     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5651     {
5652       // put moving element to center field (and let it explode there)
5653       center_element = MovingOrBlocked2Element(ex, ey);
5654       RemoveMovingField(ex, ey);
5655       Tile[ex][ey] = center_element;
5656     }
5657
5658     // now "center_element" is finally determined -- set related values now
5659     artwork_element = center_element;           // for custom player artwork
5660     explosion_element = center_element;         // for custom player artwork
5661
5662     if (IS_PLAYER(ex, ey))
5663     {
5664       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5665
5666       artwork_element = stored_player[player_nr].artwork_element;
5667
5668       if (level.use_explosion_element[player_nr])
5669       {
5670         explosion_element = level.explosion_element[player_nr];
5671         artwork_element = explosion_element;
5672       }
5673     }
5674
5675     if (mode == EX_TYPE_NORMAL ||
5676         mode == EX_TYPE_CENTER ||
5677         mode == EX_TYPE_CROSS)
5678       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5679
5680     last_phase = element_info[explosion_element].explosion_delay + 1;
5681
5682     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5683     {
5684       int xx = x - ex + 1;
5685       int yy = y - ey + 1;
5686       int element;
5687
5688       if (!IN_LEV_FIELD(x, y) ||
5689           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5690           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5691         continue;
5692
5693       element = Tile[x][y];
5694
5695       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5696       {
5697         element = MovingOrBlocked2Element(x, y);
5698
5699         if (!IS_EXPLOSION_PROOF(element))
5700           RemoveMovingField(x, y);
5701       }
5702
5703       // indestructible elements can only explode in center (but not flames)
5704       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5705                                            mode == EX_TYPE_BORDER)) ||
5706           element == EL_FLAMES)
5707         continue;
5708
5709       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5710          behaviour, for example when touching a yamyam that explodes to rocks
5711          with active deadly shield, a rock is created under the player !!! */
5712       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5713 #if 0
5714       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5715           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5716            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5717 #else
5718       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5719 #endif
5720       {
5721         if (IS_ACTIVE_BOMB(element))
5722         {
5723           // re-activate things under the bomb like gate or penguin
5724           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5725           Back[x][y] = 0;
5726         }
5727
5728         continue;
5729       }
5730
5731       // save walkable background elements while explosion on same tile
5732       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5733           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5734         Back[x][y] = element;
5735
5736       // ignite explodable elements reached by other explosion
5737       if (element == EL_EXPLOSION)
5738         element = Store2[x][y];
5739
5740       if (AmoebaNr[x][y] &&
5741           (element == EL_AMOEBA_FULL ||
5742            element == EL_BD_AMOEBA ||
5743            element == EL_AMOEBA_GROWING))
5744       {
5745         AmoebaCnt[AmoebaNr[x][y]]--;
5746         AmoebaCnt2[AmoebaNr[x][y]]--;
5747       }
5748
5749       RemoveField(x, y);
5750
5751       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5752       {
5753         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5754
5755         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5756
5757         if (PLAYERINFO(ex, ey)->use_murphy)
5758           Store[x][y] = EL_EMPTY;
5759       }
5760
5761       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5762       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5763       else if (ELEM_IS_PLAYER(center_element))
5764         Store[x][y] = EL_EMPTY;
5765       else if (center_element == EL_YAMYAM)
5766         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5767       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5768         Store[x][y] = element_info[center_element].content.e[xx][yy];
5769 #if 1
5770       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5771       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5772       // otherwise) -- FIX THIS !!!
5773       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5774         Store[x][y] = element_info[element].content.e[1][1];
5775 #else
5776       else if (!CAN_EXPLODE(element))
5777         Store[x][y] = element_info[element].content.e[1][1];
5778 #endif
5779       else
5780         Store[x][y] = EL_EMPTY;
5781
5782       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5783           center_element == EL_AMOEBA_TO_DIAMOND)
5784         Store2[x][y] = element;
5785
5786       Tile[x][y] = EL_EXPLOSION;
5787       GfxElement[x][y] = artwork_element;
5788
5789       ExplodePhase[x][y] = 1;
5790       ExplodeDelay[x][y] = last_phase;
5791
5792       Stop[x][y] = TRUE;
5793     }
5794
5795     if (center_element == EL_YAMYAM)
5796       game.yamyam_content_nr =
5797         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5798
5799     return;
5800   }
5801
5802   if (Stop[ex][ey])
5803     return;
5804
5805   x = ex;
5806   y = ey;
5807
5808   if (phase == 1)
5809     GfxFrame[x][y] = 0;         // restart explosion animation
5810
5811   last_phase = ExplodeDelay[x][y];
5812
5813   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5814
5815   // this can happen if the player leaves an explosion just in time
5816   if (GfxElement[x][y] == EL_UNDEFINED)
5817     GfxElement[x][y] = EL_EMPTY;
5818
5819   border_element = Store2[x][y];
5820   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5821     border_element = StorePlayer[x][y];
5822
5823   if (phase == element_info[border_element].ignition_delay ||
5824       phase == last_phase)
5825   {
5826     boolean border_explosion = FALSE;
5827
5828     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5829         !PLAYER_EXPLOSION_PROTECTED(x, y))
5830     {
5831       KillPlayerUnlessExplosionProtected(x, y);
5832       border_explosion = TRUE;
5833     }
5834     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5835     {
5836       Tile[x][y] = Store2[x][y];
5837       Store2[x][y] = 0;
5838       Bang(x, y);
5839       border_explosion = TRUE;
5840     }
5841     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5842     {
5843       AmoebaToDiamond(x, y);
5844       Store2[x][y] = 0;
5845       border_explosion = TRUE;
5846     }
5847
5848     // if an element just explodes due to another explosion (chain-reaction),
5849     // do not immediately end the new explosion when it was the last frame of
5850     // the explosion (as it would be done in the following "if"-statement!)
5851     if (border_explosion && phase == last_phase)
5852       return;
5853   }
5854
5855   if (phase == last_phase)
5856   {
5857     int element;
5858
5859     element = Tile[x][y] = Store[x][y];
5860     Store[x][y] = Store2[x][y] = 0;
5861     GfxElement[x][y] = EL_UNDEFINED;
5862
5863     // player can escape from explosions and might therefore be still alive
5864     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5865         element <= EL_PLAYER_IS_EXPLODING_4)
5866     {
5867       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5868       int explosion_element = EL_PLAYER_1 + player_nr;
5869       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5870       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5871
5872       if (level.use_explosion_element[player_nr])
5873         explosion_element = level.explosion_element[player_nr];
5874
5875       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5876                     element_info[explosion_element].content.e[xx][yy]);
5877     }
5878
5879     // restore probably existing indestructible background element
5880     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5881       element = Tile[x][y] = Back[x][y];
5882     Back[x][y] = 0;
5883
5884     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5885     GfxDir[x][y] = MV_NONE;
5886     ChangeDelay[x][y] = 0;
5887     ChangePage[x][y] = -1;
5888
5889     CustomValue[x][y] = 0;
5890
5891     InitField_WithBug2(x, y, FALSE);
5892
5893     TEST_DrawLevelField(x, y);
5894
5895     TestIfElementTouchesCustomElement(x, y);
5896
5897     if (GFX_CRUMBLED(element))
5898       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5899
5900     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5901       StorePlayer[x][y] = 0;
5902
5903     if (ELEM_IS_PLAYER(element))
5904       RelocatePlayer(x, y, element);
5905   }
5906   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5907   {
5908     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5909     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5910
5911     if (phase == delay)
5912       TEST_DrawLevelFieldCrumbled(x, y);
5913
5914     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5915     {
5916       DrawLevelElement(x, y, Back[x][y]);
5917       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5918     }
5919     else if (IS_WALKABLE_UNDER(Back[x][y]))
5920     {
5921       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5922       DrawLevelElementThruMask(x, y, Back[x][y]);
5923     }
5924     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5925       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5926   }
5927 }
5928
5929 static void DynaExplode(int ex, int ey)
5930 {
5931   int i, j;
5932   int dynabomb_element = Tile[ex][ey];
5933   int dynabomb_size = 1;
5934   boolean dynabomb_xl = FALSE;
5935   struct PlayerInfo *player;
5936   static int xy[4][2] =
5937   {
5938     { 0, -1 },
5939     { -1, 0 },
5940     { +1, 0 },
5941     { 0, +1 }
5942   };
5943
5944   if (IS_ACTIVE_BOMB(dynabomb_element))
5945   {
5946     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5947     dynabomb_size = player->dynabomb_size;
5948     dynabomb_xl = player->dynabomb_xl;
5949     player->dynabombs_left++;
5950   }
5951
5952   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5953
5954   for (i = 0; i < NUM_DIRECTIONS; i++)
5955   {
5956     for (j = 1; j <= dynabomb_size; j++)
5957     {
5958       int x = ex + j * xy[i][0];
5959       int y = ey + j * xy[i][1];
5960       int element;
5961
5962       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5963         break;
5964
5965       element = Tile[x][y];
5966
5967       // do not restart explosions of fields with active bombs
5968       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5969         continue;
5970
5971       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5972
5973       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5974           !IS_DIGGABLE(element) && !dynabomb_xl)
5975         break;
5976     }
5977   }
5978 }
5979
5980 void Bang(int x, int y)
5981 {
5982   int element = MovingOrBlocked2Element(x, y);
5983   int explosion_type = EX_TYPE_NORMAL;
5984
5985   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5986   {
5987     struct PlayerInfo *player = PLAYERINFO(x, y);
5988
5989     element = Tile[x][y] = player->initial_element;
5990
5991     if (level.use_explosion_element[player->index_nr])
5992     {
5993       int explosion_element = level.explosion_element[player->index_nr];
5994
5995       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5996         explosion_type = EX_TYPE_CROSS;
5997       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5998         explosion_type = EX_TYPE_CENTER;
5999     }
6000   }
6001
6002   switch (element)
6003   {
6004     case EL_BUG:
6005     case EL_SPACESHIP:
6006     case EL_BD_BUTTERFLY:
6007     case EL_BD_FIREFLY:
6008     case EL_YAMYAM:
6009     case EL_DARK_YAMYAM:
6010     case EL_ROBOT:
6011     case EL_PACMAN:
6012     case EL_MOLE:
6013       RaiseScoreElement(element);
6014       break;
6015
6016     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6017     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6018     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6019     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6020     case EL_DYNABOMB_INCREASE_NUMBER:
6021     case EL_DYNABOMB_INCREASE_SIZE:
6022     case EL_DYNABOMB_INCREASE_POWER:
6023       explosion_type = EX_TYPE_DYNA;
6024       break;
6025
6026     case EL_DC_LANDMINE:
6027       explosion_type = EX_TYPE_CENTER;
6028       break;
6029
6030     case EL_PENGUIN:
6031     case EL_LAMP:
6032     case EL_LAMP_ACTIVE:
6033     case EL_AMOEBA_TO_DIAMOND:
6034       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6035         explosion_type = EX_TYPE_CENTER;
6036       break;
6037
6038     default:
6039       if (element_info[element].explosion_type == EXPLODES_CROSS)
6040         explosion_type = EX_TYPE_CROSS;
6041       else if (element_info[element].explosion_type == EXPLODES_1X1)
6042         explosion_type = EX_TYPE_CENTER;
6043       break;
6044   }
6045
6046   if (explosion_type == EX_TYPE_DYNA)
6047     DynaExplode(x, y);
6048   else
6049     Explode(x, y, EX_PHASE_START, explosion_type);
6050
6051   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6052 }
6053
6054 static void SplashAcid(int x, int y)
6055 {
6056   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6057       (!IN_LEV_FIELD(x - 1, y - 2) ||
6058        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6059     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6060
6061   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6062       (!IN_LEV_FIELD(x + 1, y - 2) ||
6063        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6064     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6065
6066   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6067 }
6068
6069 static void InitBeltMovement(void)
6070 {
6071   static int belt_base_element[4] =
6072   {
6073     EL_CONVEYOR_BELT_1_LEFT,
6074     EL_CONVEYOR_BELT_2_LEFT,
6075     EL_CONVEYOR_BELT_3_LEFT,
6076     EL_CONVEYOR_BELT_4_LEFT
6077   };
6078   static int belt_base_active_element[4] =
6079   {
6080     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6081     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6082     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6083     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6084   };
6085
6086   int x, y, i, j;
6087
6088   // set frame order for belt animation graphic according to belt direction
6089   for (i = 0; i < NUM_BELTS; i++)
6090   {
6091     int belt_nr = i;
6092
6093     for (j = 0; j < NUM_BELT_PARTS; j++)
6094     {
6095       int element = belt_base_active_element[belt_nr] + j;
6096       int graphic_1 = el2img(element);
6097       int graphic_2 = el2panelimg(element);
6098
6099       if (game.belt_dir[i] == MV_LEFT)
6100       {
6101         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6102         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6103       }
6104       else
6105       {
6106         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6107         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6108       }
6109     }
6110   }
6111
6112   SCAN_PLAYFIELD(x, y)
6113   {
6114     int element = Tile[x][y];
6115
6116     for (i = 0; i < NUM_BELTS; i++)
6117     {
6118       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6119       {
6120         int e_belt_nr = getBeltNrFromBeltElement(element);
6121         int belt_nr = i;
6122
6123         if (e_belt_nr == belt_nr)
6124         {
6125           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6126
6127           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6128         }
6129       }
6130     }
6131   }
6132 }
6133
6134 static void ToggleBeltSwitch(int x, int y)
6135 {
6136   static int belt_base_element[4] =
6137   {
6138     EL_CONVEYOR_BELT_1_LEFT,
6139     EL_CONVEYOR_BELT_2_LEFT,
6140     EL_CONVEYOR_BELT_3_LEFT,
6141     EL_CONVEYOR_BELT_4_LEFT
6142   };
6143   static int belt_base_active_element[4] =
6144   {
6145     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6146     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6147     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6148     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6149   };
6150   static int belt_base_switch_element[4] =
6151   {
6152     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6153     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6154     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6155     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6156   };
6157   static int belt_move_dir[4] =
6158   {
6159     MV_LEFT,
6160     MV_NONE,
6161     MV_RIGHT,
6162     MV_NONE,
6163   };
6164
6165   int element = Tile[x][y];
6166   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6167   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6168   int belt_dir = belt_move_dir[belt_dir_nr];
6169   int xx, yy, i;
6170
6171   if (!IS_BELT_SWITCH(element))
6172     return;
6173
6174   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6175   game.belt_dir[belt_nr] = belt_dir;
6176
6177   if (belt_dir_nr == 3)
6178     belt_dir_nr = 1;
6179
6180   // set frame order for belt animation graphic according to belt direction
6181   for (i = 0; i < NUM_BELT_PARTS; i++)
6182   {
6183     int element = belt_base_active_element[belt_nr] + i;
6184     int graphic_1 = el2img(element);
6185     int graphic_2 = el2panelimg(element);
6186
6187     if (belt_dir == MV_LEFT)
6188     {
6189       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6190       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6191     }
6192     else
6193     {
6194       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6195       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6196     }
6197   }
6198
6199   SCAN_PLAYFIELD(xx, yy)
6200   {
6201     int element = Tile[xx][yy];
6202
6203     if (IS_BELT_SWITCH(element))
6204     {
6205       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6206
6207       if (e_belt_nr == belt_nr)
6208       {
6209         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6210         TEST_DrawLevelField(xx, yy);
6211       }
6212     }
6213     else if (IS_BELT(element) && belt_dir != MV_NONE)
6214     {
6215       int e_belt_nr = getBeltNrFromBeltElement(element);
6216
6217       if (e_belt_nr == belt_nr)
6218       {
6219         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6220
6221         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6222         TEST_DrawLevelField(xx, yy);
6223       }
6224     }
6225     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6226     {
6227       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6228
6229       if (e_belt_nr == belt_nr)
6230       {
6231         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6232
6233         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6234         TEST_DrawLevelField(xx, yy);
6235       }
6236     }
6237   }
6238 }
6239
6240 static void ToggleSwitchgateSwitch(int x, int y)
6241 {
6242   int xx, yy;
6243
6244   game.switchgate_pos = !game.switchgate_pos;
6245
6246   SCAN_PLAYFIELD(xx, yy)
6247   {
6248     int element = Tile[xx][yy];
6249
6250     if (element == EL_SWITCHGATE_SWITCH_UP)
6251     {
6252       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6253       TEST_DrawLevelField(xx, yy);
6254     }
6255     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6256     {
6257       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6258       TEST_DrawLevelField(xx, yy);
6259     }
6260     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6261     {
6262       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6263       TEST_DrawLevelField(xx, yy);
6264     }
6265     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6266     {
6267       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6268       TEST_DrawLevelField(xx, yy);
6269     }
6270     else if (element == EL_SWITCHGATE_OPEN ||
6271              element == EL_SWITCHGATE_OPENING)
6272     {
6273       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6274
6275       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6276     }
6277     else if (element == EL_SWITCHGATE_CLOSED ||
6278              element == EL_SWITCHGATE_CLOSING)
6279     {
6280       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6281
6282       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6283     }
6284   }
6285 }
6286
6287 static int getInvisibleActiveFromInvisibleElement(int element)
6288 {
6289   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6290           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6291           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6292           element);
6293 }
6294
6295 static int getInvisibleFromInvisibleActiveElement(int element)
6296 {
6297   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6298           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6299           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6300           element);
6301 }
6302
6303 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6304 {
6305   int x, y;
6306
6307   SCAN_PLAYFIELD(x, y)
6308   {
6309     int element = Tile[x][y];
6310
6311     if (element == EL_LIGHT_SWITCH &&
6312         game.light_time_left > 0)
6313     {
6314       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6315       TEST_DrawLevelField(x, y);
6316     }
6317     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6318              game.light_time_left == 0)
6319     {
6320       Tile[x][y] = EL_LIGHT_SWITCH;
6321       TEST_DrawLevelField(x, y);
6322     }
6323     else if (element == EL_EMC_DRIPPER &&
6324              game.light_time_left > 0)
6325     {
6326       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6327       TEST_DrawLevelField(x, y);
6328     }
6329     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6330              game.light_time_left == 0)
6331     {
6332       Tile[x][y] = EL_EMC_DRIPPER;
6333       TEST_DrawLevelField(x, y);
6334     }
6335     else if (element == EL_INVISIBLE_STEELWALL ||
6336              element == EL_INVISIBLE_WALL ||
6337              element == EL_INVISIBLE_SAND)
6338     {
6339       if (game.light_time_left > 0)
6340         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6341
6342       TEST_DrawLevelField(x, y);
6343
6344       // uncrumble neighbour fields, if needed
6345       if (element == EL_INVISIBLE_SAND)
6346         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6347     }
6348     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6349              element == EL_INVISIBLE_WALL_ACTIVE ||
6350              element == EL_INVISIBLE_SAND_ACTIVE)
6351     {
6352       if (game.light_time_left == 0)
6353         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6354
6355       TEST_DrawLevelField(x, y);
6356
6357       // re-crumble neighbour fields, if needed
6358       if (element == EL_INVISIBLE_SAND)
6359         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6360     }
6361   }
6362 }
6363
6364 static void RedrawAllInvisibleElementsForLenses(void)
6365 {
6366   int x, y;
6367
6368   SCAN_PLAYFIELD(x, y)
6369   {
6370     int element = Tile[x][y];
6371
6372     if (element == EL_EMC_DRIPPER &&
6373         game.lenses_time_left > 0)
6374     {
6375       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6376       TEST_DrawLevelField(x, y);
6377     }
6378     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6379              game.lenses_time_left == 0)
6380     {
6381       Tile[x][y] = EL_EMC_DRIPPER;
6382       TEST_DrawLevelField(x, y);
6383     }
6384     else if (element == EL_INVISIBLE_STEELWALL ||
6385              element == EL_INVISIBLE_WALL ||
6386              element == EL_INVISIBLE_SAND)
6387     {
6388       if (game.lenses_time_left > 0)
6389         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6390
6391       TEST_DrawLevelField(x, y);
6392
6393       // uncrumble neighbour fields, if needed
6394       if (element == EL_INVISIBLE_SAND)
6395         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6396     }
6397     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6398              element == EL_INVISIBLE_WALL_ACTIVE ||
6399              element == EL_INVISIBLE_SAND_ACTIVE)
6400     {
6401       if (game.lenses_time_left == 0)
6402         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6403
6404       TEST_DrawLevelField(x, y);
6405
6406       // re-crumble neighbour fields, if needed
6407       if (element == EL_INVISIBLE_SAND)
6408         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6409     }
6410   }
6411 }
6412
6413 static void RedrawAllInvisibleElementsForMagnifier(void)
6414 {
6415   int x, y;
6416
6417   SCAN_PLAYFIELD(x, y)
6418   {
6419     int element = Tile[x][y];
6420
6421     if (element == EL_EMC_FAKE_GRASS &&
6422         game.magnify_time_left > 0)
6423     {
6424       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6425       TEST_DrawLevelField(x, y);
6426     }
6427     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6428              game.magnify_time_left == 0)
6429     {
6430       Tile[x][y] = EL_EMC_FAKE_GRASS;
6431       TEST_DrawLevelField(x, y);
6432     }
6433     else if (IS_GATE_GRAY(element) &&
6434              game.magnify_time_left > 0)
6435     {
6436       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6437                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6438                     IS_EM_GATE_GRAY(element) ?
6439                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6440                     IS_EMC_GATE_GRAY(element) ?
6441                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6442                     IS_DC_GATE_GRAY(element) ?
6443                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6444                     element);
6445       TEST_DrawLevelField(x, y);
6446     }
6447     else if (IS_GATE_GRAY_ACTIVE(element) &&
6448              game.magnify_time_left == 0)
6449     {
6450       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6451                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6452                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6453                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6454                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6455                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6456                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6457                     EL_DC_GATE_WHITE_GRAY :
6458                     element);
6459       TEST_DrawLevelField(x, y);
6460     }
6461   }
6462 }
6463
6464 static void ToggleLightSwitch(int x, int y)
6465 {
6466   int element = Tile[x][y];
6467
6468   game.light_time_left =
6469     (element == EL_LIGHT_SWITCH ?
6470      level.time_light * FRAMES_PER_SECOND : 0);
6471
6472   RedrawAllLightSwitchesAndInvisibleElements();
6473 }
6474
6475 static void ActivateTimegateSwitch(int x, int y)
6476 {
6477   int xx, yy;
6478
6479   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6480
6481   SCAN_PLAYFIELD(xx, yy)
6482   {
6483     int element = Tile[xx][yy];
6484
6485     if (element == EL_TIMEGATE_CLOSED ||
6486         element == EL_TIMEGATE_CLOSING)
6487     {
6488       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6489       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6490     }
6491
6492     /*
6493     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6494     {
6495       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6496       TEST_DrawLevelField(xx, yy);
6497     }
6498     */
6499
6500   }
6501
6502   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6503                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6504 }
6505
6506 static void Impact(int x, int y)
6507 {
6508   boolean last_line = (y == lev_fieldy - 1);
6509   boolean object_hit = FALSE;
6510   boolean impact = (last_line || object_hit);
6511   int element = Tile[x][y];
6512   int smashed = EL_STEELWALL;
6513
6514   if (!last_line)       // check if element below was hit
6515   {
6516     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6517       return;
6518
6519     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6520                                          MovDir[x][y + 1] != MV_DOWN ||
6521                                          MovPos[x][y + 1] <= TILEY / 2));
6522
6523     // do not smash moving elements that left the smashed field in time
6524     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6525         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6526       object_hit = FALSE;
6527
6528 #if USE_QUICKSAND_IMPACT_BUGFIX
6529     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6530     {
6531       RemoveMovingField(x, y + 1);
6532       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6533       Tile[x][y + 2] = EL_ROCK;
6534       TEST_DrawLevelField(x, y + 2);
6535
6536       object_hit = TRUE;
6537     }
6538
6539     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6540     {
6541       RemoveMovingField(x, y + 1);
6542       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6543       Tile[x][y + 2] = EL_ROCK;
6544       TEST_DrawLevelField(x, y + 2);
6545
6546       object_hit = TRUE;
6547     }
6548 #endif
6549
6550     if (object_hit)
6551       smashed = MovingOrBlocked2Element(x, y + 1);
6552
6553     impact = (last_line || object_hit);
6554   }
6555
6556   if (!last_line && smashed == EL_ACID) // element falls into acid
6557   {
6558     SplashAcid(x, y + 1);
6559     return;
6560   }
6561
6562   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6563   // only reset graphic animation if graphic really changes after impact
6564   if (impact &&
6565       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6566   {
6567     ResetGfxAnimation(x, y);
6568     TEST_DrawLevelField(x, y);
6569   }
6570
6571   if (impact && CAN_EXPLODE_IMPACT(element))
6572   {
6573     Bang(x, y);
6574     return;
6575   }
6576   else if (impact && element == EL_PEARL &&
6577            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6578   {
6579     ResetGfxAnimation(x, y);
6580
6581     Tile[x][y] = EL_PEARL_BREAKING;
6582     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6583     return;
6584   }
6585   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6586   {
6587     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6588
6589     return;
6590   }
6591
6592   if (impact && element == EL_AMOEBA_DROP)
6593   {
6594     if (object_hit && IS_PLAYER(x, y + 1))
6595       KillPlayerUnlessEnemyProtected(x, y + 1);
6596     else if (object_hit && smashed == EL_PENGUIN)
6597       Bang(x, y + 1);
6598     else
6599     {
6600       Tile[x][y] = EL_AMOEBA_GROWING;
6601       Store[x][y] = EL_AMOEBA_WET;
6602
6603       ResetRandomAnimationValue(x, y);
6604     }
6605     return;
6606   }
6607
6608   if (object_hit)               // check which object was hit
6609   {
6610     if ((CAN_PASS_MAGIC_WALL(element) && 
6611          (smashed == EL_MAGIC_WALL ||
6612           smashed == EL_BD_MAGIC_WALL)) ||
6613         (CAN_PASS_DC_MAGIC_WALL(element) &&
6614          smashed == EL_DC_MAGIC_WALL))
6615     {
6616       int xx, yy;
6617       int activated_magic_wall =
6618         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6619          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6620          EL_DC_MAGIC_WALL_ACTIVE);
6621
6622       // activate magic wall / mill
6623       SCAN_PLAYFIELD(xx, yy)
6624       {
6625         if (Tile[xx][yy] == smashed)
6626           Tile[xx][yy] = activated_magic_wall;
6627       }
6628
6629       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6630       game.magic_wall_active = TRUE;
6631
6632       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6633                             SND_MAGIC_WALL_ACTIVATING :
6634                             smashed == EL_BD_MAGIC_WALL ?
6635                             SND_BD_MAGIC_WALL_ACTIVATING :
6636                             SND_DC_MAGIC_WALL_ACTIVATING));
6637     }
6638
6639     if (IS_PLAYER(x, y + 1))
6640     {
6641       if (CAN_SMASH_PLAYER(element))
6642       {
6643         KillPlayerUnlessEnemyProtected(x, y + 1);
6644         return;
6645       }
6646     }
6647     else if (smashed == EL_PENGUIN)
6648     {
6649       if (CAN_SMASH_PLAYER(element))
6650       {
6651         Bang(x, y + 1);
6652         return;
6653       }
6654     }
6655     else if (element == EL_BD_DIAMOND)
6656     {
6657       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6658       {
6659         Bang(x, y + 1);
6660         return;
6661       }
6662     }
6663     else if (((element == EL_SP_INFOTRON ||
6664                element == EL_SP_ZONK) &&
6665               (smashed == EL_SP_SNIKSNAK ||
6666                smashed == EL_SP_ELECTRON ||
6667                smashed == EL_SP_DISK_ORANGE)) ||
6668              (element == EL_SP_INFOTRON &&
6669               smashed == EL_SP_DISK_YELLOW))
6670     {
6671       Bang(x, y + 1);
6672       return;
6673     }
6674     else if (CAN_SMASH_EVERYTHING(element))
6675     {
6676       if (IS_CLASSIC_ENEMY(smashed) ||
6677           CAN_EXPLODE_SMASHED(smashed))
6678       {
6679         Bang(x, y + 1);
6680         return;
6681       }
6682       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6683       {
6684         if (smashed == EL_LAMP ||
6685             smashed == EL_LAMP_ACTIVE)
6686         {
6687           Bang(x, y + 1);
6688           return;
6689         }
6690         else if (smashed == EL_NUT)
6691         {
6692           Tile[x][y + 1] = EL_NUT_BREAKING;
6693           PlayLevelSound(x, y, SND_NUT_BREAKING);
6694           RaiseScoreElement(EL_NUT);
6695           return;
6696         }
6697         else if (smashed == EL_PEARL)
6698         {
6699           ResetGfxAnimation(x, y);
6700
6701           Tile[x][y + 1] = EL_PEARL_BREAKING;
6702           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6703           return;
6704         }
6705         else if (smashed == EL_DIAMOND)
6706         {
6707           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6708           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6709           return;
6710         }
6711         else if (IS_BELT_SWITCH(smashed))
6712         {
6713           ToggleBeltSwitch(x, y + 1);
6714         }
6715         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6716                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6717                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6718                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6719         {
6720           ToggleSwitchgateSwitch(x, y + 1);
6721         }
6722         else if (smashed == EL_LIGHT_SWITCH ||
6723                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6724         {
6725           ToggleLightSwitch(x, y + 1);
6726         }
6727         else
6728         {
6729           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6730
6731           CheckElementChangeBySide(x, y + 1, smashed, element,
6732                                    CE_SWITCHED, CH_SIDE_TOP);
6733           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6734                                             CH_SIDE_TOP);
6735         }
6736       }
6737       else
6738       {
6739         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6740       }
6741     }
6742   }
6743
6744   // play sound of magic wall / mill
6745   if (!last_line &&
6746       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6747        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6748        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6749   {
6750     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6751       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6752     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6753       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6754     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6755       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6756
6757     return;
6758   }
6759
6760   // play sound of object that hits the ground
6761   if (last_line || object_hit)
6762     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6763 }
6764
6765 static void TurnRoundExt(int x, int y)
6766 {
6767   static struct
6768   {
6769     int dx, dy;
6770   } move_xy[] =
6771   {
6772     {  0,  0 },
6773     { -1,  0 },
6774     { +1,  0 },
6775     {  0,  0 },
6776     {  0, -1 },
6777     {  0,  0 }, { 0, 0 }, { 0, 0 },
6778     {  0, +1 }
6779   };
6780   static struct
6781   {
6782     int left, right, back;
6783   } turn[] =
6784   {
6785     { 0,        0,              0        },
6786     { MV_DOWN,  MV_UP,          MV_RIGHT },
6787     { MV_UP,    MV_DOWN,        MV_LEFT  },
6788     { 0,        0,              0        },
6789     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6790     { 0,        0,              0        },
6791     { 0,        0,              0        },
6792     { 0,        0,              0        },
6793     { MV_RIGHT, MV_LEFT,        MV_UP    }
6794   };
6795
6796   int element = Tile[x][y];
6797   int move_pattern = element_info[element].move_pattern;
6798
6799   int old_move_dir = MovDir[x][y];
6800   int left_dir  = turn[old_move_dir].left;
6801   int right_dir = turn[old_move_dir].right;
6802   int back_dir  = turn[old_move_dir].back;
6803
6804   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6805   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6806   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6807   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6808
6809   int left_x  = x + left_dx,  left_y  = y + left_dy;
6810   int right_x = x + right_dx, right_y = y + right_dy;
6811   int move_x  = x + move_dx,  move_y  = y + move_dy;
6812
6813   int xx, yy;
6814
6815   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6816   {
6817     TestIfBadThingTouchesOtherBadThing(x, y);
6818
6819     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6820       MovDir[x][y] = right_dir;
6821     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6822       MovDir[x][y] = left_dir;
6823
6824     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6825       MovDelay[x][y] = 9;
6826     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6827       MovDelay[x][y] = 1;
6828   }
6829   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6830   {
6831     TestIfBadThingTouchesOtherBadThing(x, y);
6832
6833     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6834       MovDir[x][y] = left_dir;
6835     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6836       MovDir[x][y] = right_dir;
6837
6838     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6839       MovDelay[x][y] = 9;
6840     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6841       MovDelay[x][y] = 1;
6842   }
6843   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6844   {
6845     TestIfBadThingTouchesOtherBadThing(x, y);
6846
6847     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6848       MovDir[x][y] = left_dir;
6849     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6850       MovDir[x][y] = right_dir;
6851
6852     if (MovDir[x][y] != old_move_dir)
6853       MovDelay[x][y] = 9;
6854   }
6855   else if (element == EL_YAMYAM)
6856   {
6857     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6858     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6859
6860     if (can_turn_left && can_turn_right)
6861       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6862     else if (can_turn_left)
6863       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6864     else if (can_turn_right)
6865       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6866     else
6867       MovDir[x][y] = back_dir;
6868
6869     MovDelay[x][y] = 16 + 16 * RND(3);
6870   }
6871   else if (element == EL_DARK_YAMYAM)
6872   {
6873     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6874                                                          left_x, left_y);
6875     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6876                                                          right_x, right_y);
6877
6878     if (can_turn_left && can_turn_right)
6879       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6880     else if (can_turn_left)
6881       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6882     else if (can_turn_right)
6883       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6884     else
6885       MovDir[x][y] = back_dir;
6886
6887     MovDelay[x][y] = 16 + 16 * RND(3);
6888   }
6889   else if (element == EL_PACMAN)
6890   {
6891     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6892     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6893
6894     if (can_turn_left && can_turn_right)
6895       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6896     else if (can_turn_left)
6897       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6898     else if (can_turn_right)
6899       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6900     else
6901       MovDir[x][y] = back_dir;
6902
6903     MovDelay[x][y] = 6 + RND(40);
6904   }
6905   else if (element == EL_PIG)
6906   {
6907     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6908     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6909     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6910     boolean should_turn_left, should_turn_right, should_move_on;
6911     int rnd_value = 24;
6912     int rnd = RND(rnd_value);
6913
6914     should_turn_left = (can_turn_left &&
6915                         (!can_move_on ||
6916                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6917                                                    y + back_dy + left_dy)));
6918     should_turn_right = (can_turn_right &&
6919                          (!can_move_on ||
6920                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6921                                                     y + back_dy + right_dy)));
6922     should_move_on = (can_move_on &&
6923                       (!can_turn_left ||
6924                        !can_turn_right ||
6925                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6926                                                  y + move_dy + left_dy) ||
6927                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6928                                                  y + move_dy + right_dy)));
6929
6930     if (should_turn_left || should_turn_right || should_move_on)
6931     {
6932       if (should_turn_left && should_turn_right && should_move_on)
6933         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6934                         rnd < 2 * rnd_value / 3 ? right_dir :
6935                         old_move_dir);
6936       else if (should_turn_left && should_turn_right)
6937         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6938       else if (should_turn_left && should_move_on)
6939         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6940       else if (should_turn_right && should_move_on)
6941         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6942       else if (should_turn_left)
6943         MovDir[x][y] = left_dir;
6944       else if (should_turn_right)
6945         MovDir[x][y] = right_dir;
6946       else if (should_move_on)
6947         MovDir[x][y] = old_move_dir;
6948     }
6949     else if (can_move_on && rnd > rnd_value / 8)
6950       MovDir[x][y] = old_move_dir;
6951     else if (can_turn_left && can_turn_right)
6952       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6953     else if (can_turn_left && rnd > rnd_value / 8)
6954       MovDir[x][y] = left_dir;
6955     else if (can_turn_right && rnd > rnd_value/8)
6956       MovDir[x][y] = right_dir;
6957     else
6958       MovDir[x][y] = back_dir;
6959
6960     xx = x + move_xy[MovDir[x][y]].dx;
6961     yy = y + move_xy[MovDir[x][y]].dy;
6962
6963     if (!IN_LEV_FIELD(xx, yy) ||
6964         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6965       MovDir[x][y] = old_move_dir;
6966
6967     MovDelay[x][y] = 0;
6968   }
6969   else if (element == EL_DRAGON)
6970   {
6971     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6972     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6973     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6974     int rnd_value = 24;
6975     int rnd = RND(rnd_value);
6976
6977     if (can_move_on && rnd > rnd_value / 8)
6978       MovDir[x][y] = old_move_dir;
6979     else if (can_turn_left && can_turn_right)
6980       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6981     else if (can_turn_left && rnd > rnd_value / 8)
6982       MovDir[x][y] = left_dir;
6983     else if (can_turn_right && rnd > rnd_value / 8)
6984       MovDir[x][y] = right_dir;
6985     else
6986       MovDir[x][y] = back_dir;
6987
6988     xx = x + move_xy[MovDir[x][y]].dx;
6989     yy = y + move_xy[MovDir[x][y]].dy;
6990
6991     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6992       MovDir[x][y] = old_move_dir;
6993
6994     MovDelay[x][y] = 0;
6995   }
6996   else if (element == EL_MOLE)
6997   {
6998     boolean can_move_on =
6999       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7000                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7001                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7002     if (!can_move_on)
7003     {
7004       boolean can_turn_left =
7005         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7006                               IS_AMOEBOID(Tile[left_x][left_y])));
7007
7008       boolean can_turn_right =
7009         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7010                               IS_AMOEBOID(Tile[right_x][right_y])));
7011
7012       if (can_turn_left && can_turn_right)
7013         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7014       else if (can_turn_left)
7015         MovDir[x][y] = left_dir;
7016       else
7017         MovDir[x][y] = right_dir;
7018     }
7019
7020     if (MovDir[x][y] != old_move_dir)
7021       MovDelay[x][y] = 9;
7022   }
7023   else if (element == EL_BALLOON)
7024   {
7025     MovDir[x][y] = game.wind_direction;
7026     MovDelay[x][y] = 0;
7027   }
7028   else if (element == EL_SPRING)
7029   {
7030     if (MovDir[x][y] & MV_HORIZONTAL)
7031     {
7032       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7033           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7034       {
7035         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7036         ResetGfxAnimation(move_x, move_y);
7037         TEST_DrawLevelField(move_x, move_y);
7038
7039         MovDir[x][y] = back_dir;
7040       }
7041       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7042                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7043         MovDir[x][y] = MV_NONE;
7044     }
7045
7046     MovDelay[x][y] = 0;
7047   }
7048   else if (element == EL_ROBOT ||
7049            element == EL_SATELLITE ||
7050            element == EL_PENGUIN ||
7051            element == EL_EMC_ANDROID)
7052   {
7053     int attr_x = -1, attr_y = -1;
7054
7055     if (game.all_players_gone)
7056     {
7057       attr_x = game.exit_x;
7058       attr_y = game.exit_y;
7059     }
7060     else
7061     {
7062       int i;
7063
7064       for (i = 0; i < MAX_PLAYERS; i++)
7065       {
7066         struct PlayerInfo *player = &stored_player[i];
7067         int jx = player->jx, jy = player->jy;
7068
7069         if (!player->active)
7070           continue;
7071
7072         if (attr_x == -1 ||
7073             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7074         {
7075           attr_x = jx;
7076           attr_y = jy;
7077         }
7078       }
7079     }
7080
7081     if (element == EL_ROBOT &&
7082         game.robot_wheel_x >= 0 &&
7083         game.robot_wheel_y >= 0 &&
7084         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7085          game.engine_version < VERSION_IDENT(3,1,0,0)))
7086     {
7087       attr_x = game.robot_wheel_x;
7088       attr_y = game.robot_wheel_y;
7089     }
7090
7091     if (element == EL_PENGUIN)
7092     {
7093       int i;
7094       static int xy[4][2] =
7095       {
7096         { 0, -1 },
7097         { -1, 0 },
7098         { +1, 0 },
7099         { 0, +1 }
7100       };
7101
7102       for (i = 0; i < NUM_DIRECTIONS; i++)
7103       {
7104         int ex = x + xy[i][0];
7105         int ey = y + xy[i][1];
7106
7107         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7108                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7109                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7110                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7111         {
7112           attr_x = ex;
7113           attr_y = ey;
7114           break;
7115         }
7116       }
7117     }
7118
7119     MovDir[x][y] = MV_NONE;
7120     if (attr_x < x)
7121       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7122     else if (attr_x > x)
7123       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7124     if (attr_y < y)
7125       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7126     else if (attr_y > y)
7127       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7128
7129     if (element == EL_ROBOT)
7130     {
7131       int newx, newy;
7132
7133       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7134         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7135       Moving2Blocked(x, y, &newx, &newy);
7136
7137       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7138         MovDelay[x][y] = 8 + 8 * !RND(3);
7139       else
7140         MovDelay[x][y] = 16;
7141     }
7142     else if (element == EL_PENGUIN)
7143     {
7144       int newx, newy;
7145
7146       MovDelay[x][y] = 1;
7147
7148       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7149       {
7150         boolean first_horiz = RND(2);
7151         int new_move_dir = MovDir[x][y];
7152
7153         MovDir[x][y] =
7154           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7155         Moving2Blocked(x, y, &newx, &newy);
7156
7157         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7158           return;
7159
7160         MovDir[x][y] =
7161           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7162         Moving2Blocked(x, y, &newx, &newy);
7163
7164         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7165           return;
7166
7167         MovDir[x][y] = old_move_dir;
7168         return;
7169       }
7170     }
7171     else if (element == EL_SATELLITE)
7172     {
7173       int newx, newy;
7174
7175       MovDelay[x][y] = 1;
7176
7177       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7178       {
7179         boolean first_horiz = RND(2);
7180         int new_move_dir = MovDir[x][y];
7181
7182         MovDir[x][y] =
7183           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7184         Moving2Blocked(x, y, &newx, &newy);
7185
7186         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7187           return;
7188
7189         MovDir[x][y] =
7190           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7191         Moving2Blocked(x, y, &newx, &newy);
7192
7193         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7194           return;
7195
7196         MovDir[x][y] = old_move_dir;
7197         return;
7198       }
7199     }
7200     else if (element == EL_EMC_ANDROID)
7201     {
7202       static int check_pos[16] =
7203       {
7204         -1,             //  0 => (invalid)
7205         7,              //  1 => MV_LEFT
7206         3,              //  2 => MV_RIGHT
7207         -1,             //  3 => (invalid)
7208         1,              //  4 =>            MV_UP
7209         0,              //  5 => MV_LEFT  | MV_UP
7210         2,              //  6 => MV_RIGHT | MV_UP
7211         -1,             //  7 => (invalid)
7212         5,              //  8 =>            MV_DOWN
7213         6,              //  9 => MV_LEFT  | MV_DOWN
7214         4,              // 10 => MV_RIGHT | MV_DOWN
7215         -1,             // 11 => (invalid)
7216         -1,             // 12 => (invalid)
7217         -1,             // 13 => (invalid)
7218         -1,             // 14 => (invalid)
7219         -1,             // 15 => (invalid)
7220       };
7221       static struct
7222       {
7223         int dx, dy;
7224         int dir;
7225       } check_xy[8] =
7226       {
7227         { -1, -1,       MV_LEFT  | MV_UP   },
7228         {  0, -1,                  MV_UP   },
7229         { +1, -1,       MV_RIGHT | MV_UP   },
7230         { +1,  0,       MV_RIGHT           },
7231         { +1, +1,       MV_RIGHT | MV_DOWN },
7232         {  0, +1,                  MV_DOWN },
7233         { -1, +1,       MV_LEFT  | MV_DOWN },
7234         { -1,  0,       MV_LEFT            },
7235       };
7236       int start_pos, check_order;
7237       boolean can_clone = FALSE;
7238       int i;
7239
7240       // check if there is any free field around current position
7241       for (i = 0; i < 8; i++)
7242       {
7243         int newx = x + check_xy[i].dx;
7244         int newy = y + check_xy[i].dy;
7245
7246         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7247         {
7248           can_clone = TRUE;
7249
7250           break;
7251         }
7252       }
7253
7254       if (can_clone)            // randomly find an element to clone
7255       {
7256         can_clone = FALSE;
7257
7258         start_pos = check_pos[RND(8)];
7259         check_order = (RND(2) ? -1 : +1);
7260
7261         for (i = 0; i < 8; i++)
7262         {
7263           int pos_raw = start_pos + i * check_order;
7264           int pos = (pos_raw + 8) % 8;
7265           int newx = x + check_xy[pos].dx;
7266           int newy = y + check_xy[pos].dy;
7267
7268           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7269           {
7270             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7271             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7272
7273             Store[x][y] = Tile[newx][newy];
7274
7275             can_clone = TRUE;
7276
7277             break;
7278           }
7279         }
7280       }
7281
7282       if (can_clone)            // randomly find a direction to move
7283       {
7284         can_clone = FALSE;
7285
7286         start_pos = check_pos[RND(8)];
7287         check_order = (RND(2) ? -1 : +1);
7288
7289         for (i = 0; i < 8; i++)
7290         {
7291           int pos_raw = start_pos + i * check_order;
7292           int pos = (pos_raw + 8) % 8;
7293           int newx = x + check_xy[pos].dx;
7294           int newy = y + check_xy[pos].dy;
7295           int new_move_dir = check_xy[pos].dir;
7296
7297           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7298           {
7299             MovDir[x][y] = new_move_dir;
7300             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7301
7302             can_clone = TRUE;
7303
7304             break;
7305           }
7306         }
7307       }
7308
7309       if (can_clone)            // cloning and moving successful
7310         return;
7311
7312       // cannot clone -- try to move towards player
7313
7314       start_pos = check_pos[MovDir[x][y] & 0x0f];
7315       check_order = (RND(2) ? -1 : +1);
7316
7317       for (i = 0; i < 3; i++)
7318       {
7319         // first check start_pos, then previous/next or (next/previous) pos
7320         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7321         int pos = (pos_raw + 8) % 8;
7322         int newx = x + check_xy[pos].dx;
7323         int newy = y + check_xy[pos].dy;
7324         int new_move_dir = check_xy[pos].dir;
7325
7326         if (IS_PLAYER(newx, newy))
7327           break;
7328
7329         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7330         {
7331           MovDir[x][y] = new_move_dir;
7332           MovDelay[x][y] = level.android_move_time * 8 + 1;
7333
7334           break;
7335         }
7336       }
7337     }
7338   }
7339   else if (move_pattern == MV_TURNING_LEFT ||
7340            move_pattern == MV_TURNING_RIGHT ||
7341            move_pattern == MV_TURNING_LEFT_RIGHT ||
7342            move_pattern == MV_TURNING_RIGHT_LEFT ||
7343            move_pattern == MV_TURNING_RANDOM ||
7344            move_pattern == MV_ALL_DIRECTIONS)
7345   {
7346     boolean can_turn_left =
7347       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7348     boolean can_turn_right =
7349       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7350
7351     if (element_info[element].move_stepsize == 0)       // "not moving"
7352       return;
7353
7354     if (move_pattern == MV_TURNING_LEFT)
7355       MovDir[x][y] = left_dir;
7356     else if (move_pattern == MV_TURNING_RIGHT)
7357       MovDir[x][y] = right_dir;
7358     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7359       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7360     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7361       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7362     else if (move_pattern == MV_TURNING_RANDOM)
7363       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7364                       can_turn_right && !can_turn_left ? right_dir :
7365                       RND(2) ? left_dir : right_dir);
7366     else if (can_turn_left && can_turn_right)
7367       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7368     else if (can_turn_left)
7369       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7370     else if (can_turn_right)
7371       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7372     else
7373       MovDir[x][y] = back_dir;
7374
7375     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7376   }
7377   else if (move_pattern == MV_HORIZONTAL ||
7378            move_pattern == MV_VERTICAL)
7379   {
7380     if (move_pattern & old_move_dir)
7381       MovDir[x][y] = back_dir;
7382     else if (move_pattern == MV_HORIZONTAL)
7383       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7384     else if (move_pattern == MV_VERTICAL)
7385       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7386
7387     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7388   }
7389   else if (move_pattern & MV_ANY_DIRECTION)
7390   {
7391     MovDir[x][y] = move_pattern;
7392     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7393   }
7394   else if (move_pattern & MV_WIND_DIRECTION)
7395   {
7396     MovDir[x][y] = game.wind_direction;
7397     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7398   }
7399   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7400   {
7401     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7402       MovDir[x][y] = left_dir;
7403     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7404       MovDir[x][y] = right_dir;
7405
7406     if (MovDir[x][y] != old_move_dir)
7407       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7408   }
7409   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7410   {
7411     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7412       MovDir[x][y] = right_dir;
7413     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7414       MovDir[x][y] = left_dir;
7415
7416     if (MovDir[x][y] != old_move_dir)
7417       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7418   }
7419   else if (move_pattern == MV_TOWARDS_PLAYER ||
7420            move_pattern == MV_AWAY_FROM_PLAYER)
7421   {
7422     int attr_x = -1, attr_y = -1;
7423     int newx, newy;
7424     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7425
7426     if (game.all_players_gone)
7427     {
7428       attr_x = game.exit_x;
7429       attr_y = game.exit_y;
7430     }
7431     else
7432     {
7433       int i;
7434
7435       for (i = 0; i < MAX_PLAYERS; i++)
7436       {
7437         struct PlayerInfo *player = &stored_player[i];
7438         int jx = player->jx, jy = player->jy;
7439
7440         if (!player->active)
7441           continue;
7442
7443         if (attr_x == -1 ||
7444             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7445         {
7446           attr_x = jx;
7447           attr_y = jy;
7448         }
7449       }
7450     }
7451
7452     MovDir[x][y] = MV_NONE;
7453     if (attr_x < x)
7454       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7455     else if (attr_x > x)
7456       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7457     if (attr_y < y)
7458       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7459     else if (attr_y > y)
7460       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7461
7462     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7463
7464     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7465     {
7466       boolean first_horiz = RND(2);
7467       int new_move_dir = MovDir[x][y];
7468
7469       if (element_info[element].move_stepsize == 0)     // "not moving"
7470       {
7471         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7472         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7473
7474         return;
7475       }
7476
7477       MovDir[x][y] =
7478         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7479       Moving2Blocked(x, y, &newx, &newy);
7480
7481       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7482         return;
7483
7484       MovDir[x][y] =
7485         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7486       Moving2Blocked(x, y, &newx, &newy);
7487
7488       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7489         return;
7490
7491       MovDir[x][y] = old_move_dir;
7492     }
7493   }
7494   else if (move_pattern == MV_WHEN_PUSHED ||
7495            move_pattern == MV_WHEN_DROPPED)
7496   {
7497     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7498       MovDir[x][y] = MV_NONE;
7499
7500     MovDelay[x][y] = 0;
7501   }
7502   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7503   {
7504     static int test_xy[7][2] =
7505     {
7506       { 0, -1 },
7507       { -1, 0 },
7508       { +1, 0 },
7509       { 0, +1 },
7510       { 0, -1 },
7511       { -1, 0 },
7512       { +1, 0 },
7513     };
7514     static int test_dir[7] =
7515     {
7516       MV_UP,
7517       MV_LEFT,
7518       MV_RIGHT,
7519       MV_DOWN,
7520       MV_UP,
7521       MV_LEFT,
7522       MV_RIGHT,
7523     };
7524     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7525     int move_preference = -1000000;     // start with very low preference
7526     int new_move_dir = MV_NONE;
7527     int start_test = RND(4);
7528     int i;
7529
7530     for (i = 0; i < NUM_DIRECTIONS; i++)
7531     {
7532       int move_dir = test_dir[start_test + i];
7533       int move_dir_preference;
7534
7535       xx = x + test_xy[start_test + i][0];
7536       yy = y + test_xy[start_test + i][1];
7537
7538       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7539           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7540       {
7541         new_move_dir = move_dir;
7542
7543         break;
7544       }
7545
7546       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7547         continue;
7548
7549       move_dir_preference = -1 * RunnerVisit[xx][yy];
7550       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7551         move_dir_preference = PlayerVisit[xx][yy];
7552
7553       if (move_dir_preference > move_preference)
7554       {
7555         // prefer field that has not been visited for the longest time
7556         move_preference = move_dir_preference;
7557         new_move_dir = move_dir;
7558       }
7559       else if (move_dir_preference == move_preference &&
7560                move_dir == old_move_dir)
7561       {
7562         // prefer last direction when all directions are preferred equally
7563         move_preference = move_dir_preference;
7564         new_move_dir = move_dir;
7565       }
7566     }
7567
7568     MovDir[x][y] = new_move_dir;
7569     if (old_move_dir != new_move_dir)
7570       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7571   }
7572 }
7573
7574 static void TurnRound(int x, int y)
7575 {
7576   int direction = MovDir[x][y];
7577
7578   TurnRoundExt(x, y);
7579
7580   GfxDir[x][y] = MovDir[x][y];
7581
7582   if (direction != MovDir[x][y])
7583     GfxFrame[x][y] = 0;
7584
7585   if (MovDelay[x][y])
7586     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7587
7588   ResetGfxFrame(x, y);
7589 }
7590
7591 static boolean JustBeingPushed(int x, int y)
7592 {
7593   int i;
7594
7595   for (i = 0; i < MAX_PLAYERS; i++)
7596   {
7597     struct PlayerInfo *player = &stored_player[i];
7598
7599     if (player->active && player->is_pushing && player->MovPos)
7600     {
7601       int next_jx = player->jx + (player->jx - player->last_jx);
7602       int next_jy = player->jy + (player->jy - player->last_jy);
7603
7604       if (x == next_jx && y == next_jy)
7605         return TRUE;
7606     }
7607   }
7608
7609   return FALSE;
7610 }
7611
7612 static void StartMoving(int x, int y)
7613 {
7614   boolean started_moving = FALSE;       // some elements can fall _and_ move
7615   int element = Tile[x][y];
7616
7617   if (Stop[x][y])
7618     return;
7619
7620   if (MovDelay[x][y] == 0)
7621     GfxAction[x][y] = ACTION_DEFAULT;
7622
7623   if (CAN_FALL(element) && y < lev_fieldy - 1)
7624   {
7625     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7626         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7627       if (JustBeingPushed(x, y))
7628         return;
7629
7630     if (element == EL_QUICKSAND_FULL)
7631     {
7632       if (IS_FREE(x, y + 1))
7633       {
7634         InitMovingField(x, y, MV_DOWN);
7635         started_moving = TRUE;
7636
7637         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7638 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7639         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7640           Store[x][y] = EL_ROCK;
7641 #else
7642         Store[x][y] = EL_ROCK;
7643 #endif
7644
7645         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7646       }
7647       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7648       {
7649         if (!MovDelay[x][y])
7650         {
7651           MovDelay[x][y] = TILEY + 1;
7652
7653           ResetGfxAnimation(x, y);
7654           ResetGfxAnimation(x, y + 1);
7655         }
7656
7657         if (MovDelay[x][y])
7658         {
7659           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7660           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7661
7662           MovDelay[x][y]--;
7663           if (MovDelay[x][y])
7664             return;
7665         }
7666
7667         Tile[x][y] = EL_QUICKSAND_EMPTY;
7668         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7669         Store[x][y + 1] = Store[x][y];
7670         Store[x][y] = 0;
7671
7672         PlayLevelSoundAction(x, y, ACTION_FILLING);
7673       }
7674       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7675       {
7676         if (!MovDelay[x][y])
7677         {
7678           MovDelay[x][y] = TILEY + 1;
7679
7680           ResetGfxAnimation(x, y);
7681           ResetGfxAnimation(x, y + 1);
7682         }
7683
7684         if (MovDelay[x][y])
7685         {
7686           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7687           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7688
7689           MovDelay[x][y]--;
7690           if (MovDelay[x][y])
7691             return;
7692         }
7693
7694         Tile[x][y] = EL_QUICKSAND_EMPTY;
7695         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7696         Store[x][y + 1] = Store[x][y];
7697         Store[x][y] = 0;
7698
7699         PlayLevelSoundAction(x, y, ACTION_FILLING);
7700       }
7701     }
7702     else if (element == EL_QUICKSAND_FAST_FULL)
7703     {
7704       if (IS_FREE(x, y + 1))
7705       {
7706         InitMovingField(x, y, MV_DOWN);
7707         started_moving = TRUE;
7708
7709         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7710 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7711         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7712           Store[x][y] = EL_ROCK;
7713 #else
7714         Store[x][y] = EL_ROCK;
7715 #endif
7716
7717         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7718       }
7719       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7720       {
7721         if (!MovDelay[x][y])
7722         {
7723           MovDelay[x][y] = TILEY + 1;
7724
7725           ResetGfxAnimation(x, y);
7726           ResetGfxAnimation(x, y + 1);
7727         }
7728
7729         if (MovDelay[x][y])
7730         {
7731           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7732           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7733
7734           MovDelay[x][y]--;
7735           if (MovDelay[x][y])
7736             return;
7737         }
7738
7739         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7740         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7741         Store[x][y + 1] = Store[x][y];
7742         Store[x][y] = 0;
7743
7744         PlayLevelSoundAction(x, y, ACTION_FILLING);
7745       }
7746       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7747       {
7748         if (!MovDelay[x][y])
7749         {
7750           MovDelay[x][y] = TILEY + 1;
7751
7752           ResetGfxAnimation(x, y);
7753           ResetGfxAnimation(x, y + 1);
7754         }
7755
7756         if (MovDelay[x][y])
7757         {
7758           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7759           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7760
7761           MovDelay[x][y]--;
7762           if (MovDelay[x][y])
7763             return;
7764         }
7765
7766         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7767         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7768         Store[x][y + 1] = Store[x][y];
7769         Store[x][y] = 0;
7770
7771         PlayLevelSoundAction(x, y, ACTION_FILLING);
7772       }
7773     }
7774     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7775              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7776     {
7777       InitMovingField(x, y, MV_DOWN);
7778       started_moving = TRUE;
7779
7780       Tile[x][y] = EL_QUICKSAND_FILLING;
7781       Store[x][y] = element;
7782
7783       PlayLevelSoundAction(x, y, ACTION_FILLING);
7784     }
7785     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7786              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7787     {
7788       InitMovingField(x, y, MV_DOWN);
7789       started_moving = TRUE;
7790
7791       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7792       Store[x][y] = element;
7793
7794       PlayLevelSoundAction(x, y, ACTION_FILLING);
7795     }
7796     else if (element == EL_MAGIC_WALL_FULL)
7797     {
7798       if (IS_FREE(x, y + 1))
7799       {
7800         InitMovingField(x, y, MV_DOWN);
7801         started_moving = TRUE;
7802
7803         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7804         Store[x][y] = EL_CHANGED(Store[x][y]);
7805       }
7806       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7807       {
7808         if (!MovDelay[x][y])
7809           MovDelay[x][y] = TILEY / 4 + 1;
7810
7811         if (MovDelay[x][y])
7812         {
7813           MovDelay[x][y]--;
7814           if (MovDelay[x][y])
7815             return;
7816         }
7817
7818         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7819         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7820         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7821         Store[x][y] = 0;
7822       }
7823     }
7824     else if (element == EL_BD_MAGIC_WALL_FULL)
7825     {
7826       if (IS_FREE(x, y + 1))
7827       {
7828         InitMovingField(x, y, MV_DOWN);
7829         started_moving = TRUE;
7830
7831         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7832         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7833       }
7834       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7835       {
7836         if (!MovDelay[x][y])
7837           MovDelay[x][y] = TILEY / 4 + 1;
7838
7839         if (MovDelay[x][y])
7840         {
7841           MovDelay[x][y]--;
7842           if (MovDelay[x][y])
7843             return;
7844         }
7845
7846         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7847         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7848         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7849         Store[x][y] = 0;
7850       }
7851     }
7852     else if (element == EL_DC_MAGIC_WALL_FULL)
7853     {
7854       if (IS_FREE(x, y + 1))
7855       {
7856         InitMovingField(x, y, MV_DOWN);
7857         started_moving = TRUE;
7858
7859         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7860         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7861       }
7862       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7863       {
7864         if (!MovDelay[x][y])
7865           MovDelay[x][y] = TILEY / 4 + 1;
7866
7867         if (MovDelay[x][y])
7868         {
7869           MovDelay[x][y]--;
7870           if (MovDelay[x][y])
7871             return;
7872         }
7873
7874         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7875         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7876         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7877         Store[x][y] = 0;
7878       }
7879     }
7880     else if ((CAN_PASS_MAGIC_WALL(element) &&
7881               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7882                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7883              (CAN_PASS_DC_MAGIC_WALL(element) &&
7884               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7885
7886     {
7887       InitMovingField(x, y, MV_DOWN);
7888       started_moving = TRUE;
7889
7890       Tile[x][y] =
7891         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7892          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7893          EL_DC_MAGIC_WALL_FILLING);
7894       Store[x][y] = element;
7895     }
7896     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7897     {
7898       SplashAcid(x, y + 1);
7899
7900       InitMovingField(x, y, MV_DOWN);
7901       started_moving = TRUE;
7902
7903       Store[x][y] = EL_ACID;
7904     }
7905     else if (
7906              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7907               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7908              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7909               CAN_FALL(element) && WasJustFalling[x][y] &&
7910               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7911
7912              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7913               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7914               (Tile[x][y + 1] == EL_BLOCKED)))
7915     {
7916       /* this is needed for a special case not covered by calling "Impact()"
7917          from "ContinueMoving()": if an element moves to a tile directly below
7918          another element which was just falling on that tile (which was empty
7919          in the previous frame), the falling element above would just stop
7920          instead of smashing the element below (in previous version, the above
7921          element was just checked for "moving" instead of "falling", resulting
7922          in incorrect smashes caused by horizontal movement of the above
7923          element; also, the case of the player being the element to smash was
7924          simply not covered here... :-/ ) */
7925
7926       CheckCollision[x][y] = 0;
7927       CheckImpact[x][y] = 0;
7928
7929       Impact(x, y);
7930     }
7931     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7932     {
7933       if (MovDir[x][y] == MV_NONE)
7934       {
7935         InitMovingField(x, y, MV_DOWN);
7936         started_moving = TRUE;
7937       }
7938     }
7939     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7940     {
7941       if (WasJustFalling[x][y]) // prevent animation from being restarted
7942         MovDir[x][y] = MV_DOWN;
7943
7944       InitMovingField(x, y, MV_DOWN);
7945       started_moving = TRUE;
7946     }
7947     else if (element == EL_AMOEBA_DROP)
7948     {
7949       Tile[x][y] = EL_AMOEBA_GROWING;
7950       Store[x][y] = EL_AMOEBA_WET;
7951     }
7952     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7953               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7954              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7955              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7956     {
7957       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7958                                 (IS_FREE(x - 1, y + 1) ||
7959                                  Tile[x - 1][y + 1] == EL_ACID));
7960       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7961                                 (IS_FREE(x + 1, y + 1) ||
7962                                  Tile[x + 1][y + 1] == EL_ACID));
7963       boolean can_fall_any  = (can_fall_left || can_fall_right);
7964       boolean can_fall_both = (can_fall_left && can_fall_right);
7965       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7966
7967       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7968       {
7969         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7970           can_fall_right = FALSE;
7971         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7972           can_fall_left = FALSE;
7973         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7974           can_fall_right = FALSE;
7975         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7976           can_fall_left = FALSE;
7977
7978         can_fall_any  = (can_fall_left || can_fall_right);
7979         can_fall_both = FALSE;
7980       }
7981
7982       if (can_fall_both)
7983       {
7984         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7985           can_fall_right = FALSE;       // slip down on left side
7986         else
7987           can_fall_left = !(can_fall_right = RND(2));
7988
7989         can_fall_both = FALSE;
7990       }
7991
7992       if (can_fall_any)
7993       {
7994         // if not determined otherwise, prefer left side for slipping down
7995         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7996         started_moving = TRUE;
7997       }
7998     }
7999     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8000     {
8001       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8002       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8003       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8004       int belt_dir = game.belt_dir[belt_nr];
8005
8006       if ((belt_dir == MV_LEFT  && left_is_free) ||
8007           (belt_dir == MV_RIGHT && right_is_free))
8008       {
8009         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8010
8011         InitMovingField(x, y, belt_dir);
8012         started_moving = TRUE;
8013
8014         Pushed[x][y] = TRUE;
8015         Pushed[nextx][y] = TRUE;
8016
8017         GfxAction[x][y] = ACTION_DEFAULT;
8018       }
8019       else
8020       {
8021         MovDir[x][y] = 0;       // if element was moving, stop it
8022       }
8023     }
8024   }
8025
8026   // not "else if" because of elements that can fall and move (EL_SPRING)
8027   if (CAN_MOVE(element) && !started_moving)
8028   {
8029     int move_pattern = element_info[element].move_pattern;
8030     int newx, newy;
8031
8032     Moving2Blocked(x, y, &newx, &newy);
8033
8034     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8035       return;
8036
8037     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8038         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8039     {
8040       WasJustMoving[x][y] = 0;
8041       CheckCollision[x][y] = 0;
8042
8043       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8044
8045       if (Tile[x][y] != element)        // element has changed
8046         return;
8047     }
8048
8049     if (!MovDelay[x][y])        // start new movement phase
8050     {
8051       // all objects that can change their move direction after each step
8052       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8053
8054       if (element != EL_YAMYAM &&
8055           element != EL_DARK_YAMYAM &&
8056           element != EL_PACMAN &&
8057           !(move_pattern & MV_ANY_DIRECTION) &&
8058           move_pattern != MV_TURNING_LEFT &&
8059           move_pattern != MV_TURNING_RIGHT &&
8060           move_pattern != MV_TURNING_LEFT_RIGHT &&
8061           move_pattern != MV_TURNING_RIGHT_LEFT &&
8062           move_pattern != MV_TURNING_RANDOM)
8063       {
8064         TurnRound(x, y);
8065
8066         if (MovDelay[x][y] && (element == EL_BUG ||
8067                                element == EL_SPACESHIP ||
8068                                element == EL_SP_SNIKSNAK ||
8069                                element == EL_SP_ELECTRON ||
8070                                element == EL_MOLE))
8071           TEST_DrawLevelField(x, y);
8072       }
8073     }
8074
8075     if (MovDelay[x][y])         // wait some time before next movement
8076     {
8077       MovDelay[x][y]--;
8078
8079       if (element == EL_ROBOT ||
8080           element == EL_YAMYAM ||
8081           element == EL_DARK_YAMYAM)
8082       {
8083         DrawLevelElementAnimationIfNeeded(x, y, element);
8084         PlayLevelSoundAction(x, y, ACTION_WAITING);
8085       }
8086       else if (element == EL_SP_ELECTRON)
8087         DrawLevelElementAnimationIfNeeded(x, y, element);
8088       else if (element == EL_DRAGON)
8089       {
8090         int i;
8091         int dir = MovDir[x][y];
8092         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8093         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8094         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8095                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8096                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8097                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8098         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8099
8100         GfxAction[x][y] = ACTION_ATTACKING;
8101
8102         if (IS_PLAYER(x, y))
8103           DrawPlayerField(x, y);
8104         else
8105           TEST_DrawLevelField(x, y);
8106
8107         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8108
8109         for (i = 1; i <= 3; i++)
8110         {
8111           int xx = x + i * dx;
8112           int yy = y + i * dy;
8113           int sx = SCREENX(xx);
8114           int sy = SCREENY(yy);
8115           int flame_graphic = graphic + (i - 1);
8116
8117           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8118             break;
8119
8120           if (MovDelay[x][y])
8121           {
8122             int flamed = MovingOrBlocked2Element(xx, yy);
8123
8124             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8125               Bang(xx, yy);
8126             else
8127               RemoveMovingField(xx, yy);
8128
8129             ChangeDelay[xx][yy] = 0;
8130
8131             Tile[xx][yy] = EL_FLAMES;
8132
8133             if (IN_SCR_FIELD(sx, sy))
8134             {
8135               TEST_DrawLevelFieldCrumbled(xx, yy);
8136               DrawGraphic(sx, sy, flame_graphic, frame);
8137             }
8138           }
8139           else
8140           {
8141             if (Tile[xx][yy] == EL_FLAMES)
8142               Tile[xx][yy] = EL_EMPTY;
8143             TEST_DrawLevelField(xx, yy);
8144           }
8145         }
8146       }
8147
8148       if (MovDelay[x][y])       // element still has to wait some time
8149       {
8150         PlayLevelSoundAction(x, y, ACTION_WAITING);
8151
8152         return;
8153       }
8154     }
8155
8156     // now make next step
8157
8158     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8159
8160     if (DONT_COLLIDE_WITH(element) &&
8161         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8162         !PLAYER_ENEMY_PROTECTED(newx, newy))
8163     {
8164       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8165
8166       return;
8167     }
8168
8169     else if (CAN_MOVE_INTO_ACID(element) &&
8170              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8171              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8172              (MovDir[x][y] == MV_DOWN ||
8173               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8174     {
8175       SplashAcid(newx, newy);
8176       Store[x][y] = EL_ACID;
8177     }
8178     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8179     {
8180       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8181           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8182           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8183           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8184       {
8185         RemoveField(x, y);
8186         TEST_DrawLevelField(x, y);
8187
8188         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8189         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8190           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8191
8192         game.friends_still_needed--;
8193         if (!game.friends_still_needed &&
8194             !game.GameOver &&
8195             game.all_players_gone)
8196           LevelSolved();
8197
8198         return;
8199       }
8200       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8201       {
8202         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8203           TEST_DrawLevelField(newx, newy);
8204         else
8205           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8206       }
8207       else if (!IS_FREE(newx, newy))
8208       {
8209         GfxAction[x][y] = ACTION_WAITING;
8210
8211         if (IS_PLAYER(x, y))
8212           DrawPlayerField(x, y);
8213         else
8214           TEST_DrawLevelField(x, y);
8215
8216         return;
8217       }
8218     }
8219     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8220     {
8221       if (IS_FOOD_PIG(Tile[newx][newy]))
8222       {
8223         if (IS_MOVING(newx, newy))
8224           RemoveMovingField(newx, newy);
8225         else
8226         {
8227           Tile[newx][newy] = EL_EMPTY;
8228           TEST_DrawLevelField(newx, newy);
8229         }
8230
8231         PlayLevelSound(x, y, SND_PIG_DIGGING);
8232       }
8233       else if (!IS_FREE(newx, newy))
8234       {
8235         if (IS_PLAYER(x, y))
8236           DrawPlayerField(x, y);
8237         else
8238           TEST_DrawLevelField(x, y);
8239
8240         return;
8241       }
8242     }
8243     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8244     {
8245       if (Store[x][y] != EL_EMPTY)
8246       {
8247         boolean can_clone = FALSE;
8248         int xx, yy;
8249
8250         // check if element to clone is still there
8251         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8252         {
8253           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8254           {
8255             can_clone = TRUE;
8256
8257             break;
8258           }
8259         }
8260
8261         // cannot clone or target field not free anymore -- do not clone
8262         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8263           Store[x][y] = EL_EMPTY;
8264       }
8265
8266       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8267       {
8268         if (IS_MV_DIAGONAL(MovDir[x][y]))
8269         {
8270           int diagonal_move_dir = MovDir[x][y];
8271           int stored = Store[x][y];
8272           int change_delay = 8;
8273           int graphic;
8274
8275           // android is moving diagonally
8276
8277           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8278
8279           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8280           GfxElement[x][y] = EL_EMC_ANDROID;
8281           GfxAction[x][y] = ACTION_SHRINKING;
8282           GfxDir[x][y] = diagonal_move_dir;
8283           ChangeDelay[x][y] = change_delay;
8284
8285           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8286                                    GfxDir[x][y]);
8287
8288           DrawLevelGraphicAnimation(x, y, graphic);
8289           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8290
8291           if (Tile[newx][newy] == EL_ACID)
8292           {
8293             SplashAcid(newx, newy);
8294
8295             return;
8296           }
8297
8298           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8299
8300           Store[newx][newy] = EL_EMC_ANDROID;
8301           GfxElement[newx][newy] = EL_EMC_ANDROID;
8302           GfxAction[newx][newy] = ACTION_GROWING;
8303           GfxDir[newx][newy] = diagonal_move_dir;
8304           ChangeDelay[newx][newy] = change_delay;
8305
8306           graphic = el_act_dir2img(GfxElement[newx][newy],
8307                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8308
8309           DrawLevelGraphicAnimation(newx, newy, graphic);
8310           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8311
8312           return;
8313         }
8314         else
8315         {
8316           Tile[newx][newy] = EL_EMPTY;
8317           TEST_DrawLevelField(newx, newy);
8318
8319           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8320         }
8321       }
8322       else if (!IS_FREE(newx, newy))
8323       {
8324         return;
8325       }
8326     }
8327     else if (IS_CUSTOM_ELEMENT(element) &&
8328              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8329     {
8330       if (!DigFieldByCE(newx, newy, element))
8331         return;
8332
8333       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8334       {
8335         RunnerVisit[x][y] = FrameCounter;
8336         PlayerVisit[x][y] /= 8;         // expire player visit path
8337       }
8338     }
8339     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8340     {
8341       if (!IS_FREE(newx, newy))
8342       {
8343         if (IS_PLAYER(x, y))
8344           DrawPlayerField(x, y);
8345         else
8346           TEST_DrawLevelField(x, y);
8347
8348         return;
8349       }
8350       else
8351       {
8352         boolean wanna_flame = !RND(10);
8353         int dx = newx - x, dy = newy - y;
8354         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8355         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8356         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8357                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8358         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8359                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8360
8361         if ((wanna_flame ||
8362              IS_CLASSIC_ENEMY(element1) ||
8363              IS_CLASSIC_ENEMY(element2)) &&
8364             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8365             element1 != EL_FLAMES && element2 != EL_FLAMES)
8366         {
8367           ResetGfxAnimation(x, y);
8368           GfxAction[x][y] = ACTION_ATTACKING;
8369
8370           if (IS_PLAYER(x, y))
8371             DrawPlayerField(x, y);
8372           else
8373             TEST_DrawLevelField(x, y);
8374
8375           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8376
8377           MovDelay[x][y] = 50;
8378
8379           Tile[newx][newy] = EL_FLAMES;
8380           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8381             Tile[newx1][newy1] = EL_FLAMES;
8382           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8383             Tile[newx2][newy2] = EL_FLAMES;
8384
8385           return;
8386         }
8387       }
8388     }
8389     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8390              Tile[newx][newy] == EL_DIAMOND)
8391     {
8392       if (IS_MOVING(newx, newy))
8393         RemoveMovingField(newx, newy);
8394       else
8395       {
8396         Tile[newx][newy] = EL_EMPTY;
8397         TEST_DrawLevelField(newx, newy);
8398       }
8399
8400       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8401     }
8402     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8403              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8404     {
8405       if (AmoebaNr[newx][newy])
8406       {
8407         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8408         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8409             Tile[newx][newy] == EL_BD_AMOEBA)
8410           AmoebaCnt[AmoebaNr[newx][newy]]--;
8411       }
8412
8413       if (IS_MOVING(newx, newy))
8414       {
8415         RemoveMovingField(newx, newy);
8416       }
8417       else
8418       {
8419         Tile[newx][newy] = EL_EMPTY;
8420         TEST_DrawLevelField(newx, newy);
8421       }
8422
8423       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8424     }
8425     else if ((element == EL_PACMAN || element == EL_MOLE)
8426              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8427     {
8428       if (AmoebaNr[newx][newy])
8429       {
8430         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8431         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8432             Tile[newx][newy] == EL_BD_AMOEBA)
8433           AmoebaCnt[AmoebaNr[newx][newy]]--;
8434       }
8435
8436       if (element == EL_MOLE)
8437       {
8438         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8439         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8440
8441         ResetGfxAnimation(x, y);
8442         GfxAction[x][y] = ACTION_DIGGING;
8443         TEST_DrawLevelField(x, y);
8444
8445         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8446
8447         return;                         // wait for shrinking amoeba
8448       }
8449       else      // element == EL_PACMAN
8450       {
8451         Tile[newx][newy] = EL_EMPTY;
8452         TEST_DrawLevelField(newx, newy);
8453         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8454       }
8455     }
8456     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8457              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8458               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8459     {
8460       // wait for shrinking amoeba to completely disappear
8461       return;
8462     }
8463     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8464     {
8465       // object was running against a wall
8466
8467       TurnRound(x, y);
8468
8469       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8470         DrawLevelElementAnimation(x, y, element);
8471
8472       if (DONT_TOUCH(element))
8473         TestIfBadThingTouchesPlayer(x, y);
8474
8475       return;
8476     }
8477
8478     InitMovingField(x, y, MovDir[x][y]);
8479
8480     PlayLevelSoundAction(x, y, ACTION_MOVING);
8481   }
8482
8483   if (MovDir[x][y])
8484     ContinueMoving(x, y);
8485 }
8486
8487 void ContinueMoving(int x, int y)
8488 {
8489   int element = Tile[x][y];
8490   struct ElementInfo *ei = &element_info[element];
8491   int direction = MovDir[x][y];
8492   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8493   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8494   int newx = x + dx, newy = y + dy;
8495   int stored = Store[x][y];
8496   int stored_new = Store[newx][newy];
8497   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8498   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8499   boolean last_line = (newy == lev_fieldy - 1);
8500
8501   MovPos[x][y] += getElementMoveStepsize(x, y);
8502
8503   if (pushed_by_player) // special case: moving object pushed by player
8504     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8505
8506   if (ABS(MovPos[x][y]) < TILEX)
8507   {
8508     TEST_DrawLevelField(x, y);
8509
8510     return;     // element is still moving
8511   }
8512
8513   // element reached destination field
8514
8515   Tile[x][y] = EL_EMPTY;
8516   Tile[newx][newy] = element;
8517   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8518
8519   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8520   {
8521     element = Tile[newx][newy] = EL_ACID;
8522   }
8523   else if (element == EL_MOLE)
8524   {
8525     Tile[x][y] = EL_SAND;
8526
8527     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8528   }
8529   else if (element == EL_QUICKSAND_FILLING)
8530   {
8531     element = Tile[newx][newy] = get_next_element(element);
8532     Store[newx][newy] = Store[x][y];
8533   }
8534   else if (element == EL_QUICKSAND_EMPTYING)
8535   {
8536     Tile[x][y] = get_next_element(element);
8537     element = Tile[newx][newy] = Store[x][y];
8538   }
8539   else if (element == EL_QUICKSAND_FAST_FILLING)
8540   {
8541     element = Tile[newx][newy] = get_next_element(element);
8542     Store[newx][newy] = Store[x][y];
8543   }
8544   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8545   {
8546     Tile[x][y] = get_next_element(element);
8547     element = Tile[newx][newy] = Store[x][y];
8548   }
8549   else if (element == EL_MAGIC_WALL_FILLING)
8550   {
8551     element = Tile[newx][newy] = get_next_element(element);
8552     if (!game.magic_wall_active)
8553       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8554     Store[newx][newy] = Store[x][y];
8555   }
8556   else if (element == EL_MAGIC_WALL_EMPTYING)
8557   {
8558     Tile[x][y] = get_next_element(element);
8559     if (!game.magic_wall_active)
8560       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8561     element = Tile[newx][newy] = Store[x][y];
8562
8563     InitField(newx, newy, FALSE);
8564   }
8565   else if (element == EL_BD_MAGIC_WALL_FILLING)
8566   {
8567     element = Tile[newx][newy] = get_next_element(element);
8568     if (!game.magic_wall_active)
8569       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8570     Store[newx][newy] = Store[x][y];
8571   }
8572   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8573   {
8574     Tile[x][y] = get_next_element(element);
8575     if (!game.magic_wall_active)
8576       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8577     element = Tile[newx][newy] = Store[x][y];
8578
8579     InitField(newx, newy, FALSE);
8580   }
8581   else if (element == EL_DC_MAGIC_WALL_FILLING)
8582   {
8583     element = Tile[newx][newy] = get_next_element(element);
8584     if (!game.magic_wall_active)
8585       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8586     Store[newx][newy] = Store[x][y];
8587   }
8588   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8589   {
8590     Tile[x][y] = get_next_element(element);
8591     if (!game.magic_wall_active)
8592       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8593     element = Tile[newx][newy] = Store[x][y];
8594
8595     InitField(newx, newy, FALSE);
8596   }
8597   else if (element == EL_AMOEBA_DROPPING)
8598   {
8599     Tile[x][y] = get_next_element(element);
8600     element = Tile[newx][newy] = Store[x][y];
8601   }
8602   else if (element == EL_SOKOBAN_OBJECT)
8603   {
8604     if (Back[x][y])
8605       Tile[x][y] = Back[x][y];
8606
8607     if (Back[newx][newy])
8608       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8609
8610     Back[x][y] = Back[newx][newy] = 0;
8611   }
8612
8613   Store[x][y] = EL_EMPTY;
8614   MovPos[x][y] = 0;
8615   MovDir[x][y] = 0;
8616   MovDelay[x][y] = 0;
8617
8618   MovDelay[newx][newy] = 0;
8619
8620   if (CAN_CHANGE_OR_HAS_ACTION(element))
8621   {
8622     // copy element change control values to new field
8623     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8624     ChangePage[newx][newy]  = ChangePage[x][y];
8625     ChangeCount[newx][newy] = ChangeCount[x][y];
8626     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8627   }
8628
8629   CustomValue[newx][newy] = CustomValue[x][y];
8630
8631   ChangeDelay[x][y] = 0;
8632   ChangePage[x][y] = -1;
8633   ChangeCount[x][y] = 0;
8634   ChangeEvent[x][y] = -1;
8635
8636   CustomValue[x][y] = 0;
8637
8638   // copy animation control values to new field
8639   GfxFrame[newx][newy]  = GfxFrame[x][y];
8640   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8641   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8642   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8643
8644   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8645
8646   // some elements can leave other elements behind after moving
8647   if (ei->move_leave_element != EL_EMPTY &&
8648       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8649       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8650   {
8651     int move_leave_element = ei->move_leave_element;
8652
8653     // this makes it possible to leave the removed element again
8654     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8655       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8656
8657     Tile[x][y] = move_leave_element;
8658
8659     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8660       MovDir[x][y] = direction;
8661
8662     InitField(x, y, FALSE);
8663
8664     if (GFX_CRUMBLED(Tile[x][y]))
8665       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8666
8667     if (ELEM_IS_PLAYER(move_leave_element))
8668       RelocatePlayer(x, y, move_leave_element);
8669   }
8670
8671   // do this after checking for left-behind element
8672   ResetGfxAnimation(x, y);      // reset animation values for old field
8673
8674   if (!CAN_MOVE(element) ||
8675       (CAN_FALL(element) && direction == MV_DOWN &&
8676        (element == EL_SPRING ||
8677         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8678         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8679     GfxDir[x][y] = MovDir[newx][newy] = 0;
8680
8681   TEST_DrawLevelField(x, y);
8682   TEST_DrawLevelField(newx, newy);
8683
8684   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8685
8686   // prevent pushed element from moving on in pushed direction
8687   if (pushed_by_player && CAN_MOVE(element) &&
8688       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8689       !(element_info[element].move_pattern & direction))
8690     TurnRound(newx, newy);
8691
8692   // prevent elements on conveyor belt from moving on in last direction
8693   if (pushed_by_conveyor && CAN_FALL(element) &&
8694       direction & MV_HORIZONTAL)
8695     MovDir[newx][newy] = 0;
8696
8697   if (!pushed_by_player)
8698   {
8699     int nextx = newx + dx, nexty = newy + dy;
8700     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8701
8702     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8703
8704     if (CAN_FALL(element) && direction == MV_DOWN)
8705       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8706
8707     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8708       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8709
8710     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8711       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8712   }
8713
8714   if (DONT_TOUCH(element))      // object may be nasty to player or others
8715   {
8716     TestIfBadThingTouchesPlayer(newx, newy);
8717     TestIfBadThingTouchesFriend(newx, newy);
8718
8719     if (!IS_CUSTOM_ELEMENT(element))
8720       TestIfBadThingTouchesOtherBadThing(newx, newy);
8721   }
8722   else if (element == EL_PENGUIN)
8723     TestIfFriendTouchesBadThing(newx, newy);
8724
8725   if (DONT_GET_HIT_BY(element))
8726   {
8727     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8728   }
8729
8730   // give the player one last chance (one more frame) to move away
8731   if (CAN_FALL(element) && direction == MV_DOWN &&
8732       (last_line || (!IS_FREE(x, newy + 1) &&
8733                      (!IS_PLAYER(x, newy + 1) ||
8734                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8735     Impact(x, newy);
8736
8737   if (pushed_by_player && !game.use_change_when_pushing_bug)
8738   {
8739     int push_side = MV_DIR_OPPOSITE(direction);
8740     struct PlayerInfo *player = PLAYERINFO(x, y);
8741
8742     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8743                                player->index_bit, push_side);
8744     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8745                                         player->index_bit, push_side);
8746   }
8747
8748   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8749     MovDelay[newx][newy] = 1;
8750
8751   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8752
8753   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8754   TestIfElementHitsCustomElement(newx, newy, direction);
8755   TestIfPlayerTouchesCustomElement(newx, newy);
8756   TestIfElementTouchesCustomElement(newx, newy);
8757
8758   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8759       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8760     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8761                              MV_DIR_OPPOSITE(direction));
8762 }
8763
8764 int AmoebaNeighbourNr(int ax, int ay)
8765 {
8766   int i;
8767   int element = Tile[ax][ay];
8768   int group_nr = 0;
8769   static int xy[4][2] =
8770   {
8771     { 0, -1 },
8772     { -1, 0 },
8773     { +1, 0 },
8774     { 0, +1 }
8775   };
8776
8777   for (i = 0; i < NUM_DIRECTIONS; i++)
8778   {
8779     int x = ax + xy[i][0];
8780     int y = ay + xy[i][1];
8781
8782     if (!IN_LEV_FIELD(x, y))
8783       continue;
8784
8785     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8786       group_nr = AmoebaNr[x][y];
8787   }
8788
8789   return group_nr;
8790 }
8791
8792 static void AmoebaMerge(int ax, int ay)
8793 {
8794   int i, x, y, xx, yy;
8795   int new_group_nr = AmoebaNr[ax][ay];
8796   static int xy[4][2] =
8797   {
8798     { 0, -1 },
8799     { -1, 0 },
8800     { +1, 0 },
8801     { 0, +1 }
8802   };
8803
8804   if (new_group_nr == 0)
8805     return;
8806
8807   for (i = 0; i < NUM_DIRECTIONS; i++)
8808   {
8809     x = ax + xy[i][0];
8810     y = ay + xy[i][1];
8811
8812     if (!IN_LEV_FIELD(x, y))
8813       continue;
8814
8815     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8816          Tile[x][y] == EL_BD_AMOEBA ||
8817          Tile[x][y] == EL_AMOEBA_DEAD) &&
8818         AmoebaNr[x][y] != new_group_nr)
8819     {
8820       int old_group_nr = AmoebaNr[x][y];
8821
8822       if (old_group_nr == 0)
8823         return;
8824
8825       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8826       AmoebaCnt[old_group_nr] = 0;
8827       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8828       AmoebaCnt2[old_group_nr] = 0;
8829
8830       SCAN_PLAYFIELD(xx, yy)
8831       {
8832         if (AmoebaNr[xx][yy] == old_group_nr)
8833           AmoebaNr[xx][yy] = new_group_nr;
8834       }
8835     }
8836   }
8837 }
8838
8839 void AmoebaToDiamond(int ax, int ay)
8840 {
8841   int i, x, y;
8842
8843   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8844   {
8845     int group_nr = AmoebaNr[ax][ay];
8846
8847 #ifdef DEBUG
8848     if (group_nr == 0)
8849     {
8850       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8851       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8852
8853       return;
8854     }
8855 #endif
8856
8857     SCAN_PLAYFIELD(x, y)
8858     {
8859       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8860       {
8861         AmoebaNr[x][y] = 0;
8862         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8863       }
8864     }
8865
8866     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8867                             SND_AMOEBA_TURNING_TO_GEM :
8868                             SND_AMOEBA_TURNING_TO_ROCK));
8869     Bang(ax, ay);
8870   }
8871   else
8872   {
8873     static int xy[4][2] =
8874     {
8875       { 0, -1 },
8876       { -1, 0 },
8877       { +1, 0 },
8878       { 0, +1 }
8879     };
8880
8881     for (i = 0; i < NUM_DIRECTIONS; i++)
8882     {
8883       x = ax + xy[i][0];
8884       y = ay + xy[i][1];
8885
8886       if (!IN_LEV_FIELD(x, y))
8887         continue;
8888
8889       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8890       {
8891         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8892                               SND_AMOEBA_TURNING_TO_GEM :
8893                               SND_AMOEBA_TURNING_TO_ROCK));
8894         Bang(x, y);
8895       }
8896     }
8897   }
8898 }
8899
8900 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8901 {
8902   int x, y;
8903   int group_nr = AmoebaNr[ax][ay];
8904   boolean done = FALSE;
8905
8906 #ifdef DEBUG
8907   if (group_nr == 0)
8908   {
8909     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8910     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8911
8912     return;
8913   }
8914 #endif
8915
8916   SCAN_PLAYFIELD(x, y)
8917   {
8918     if (AmoebaNr[x][y] == group_nr &&
8919         (Tile[x][y] == EL_AMOEBA_DEAD ||
8920          Tile[x][y] == EL_BD_AMOEBA ||
8921          Tile[x][y] == EL_AMOEBA_GROWING))
8922     {
8923       AmoebaNr[x][y] = 0;
8924       Tile[x][y] = new_element;
8925       InitField(x, y, FALSE);
8926       TEST_DrawLevelField(x, y);
8927       done = TRUE;
8928     }
8929   }
8930
8931   if (done)
8932     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8933                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8934                             SND_BD_AMOEBA_TURNING_TO_GEM));
8935 }
8936
8937 static void AmoebaGrowing(int x, int y)
8938 {
8939   static unsigned int sound_delay = 0;
8940   static unsigned int sound_delay_value = 0;
8941
8942   if (!MovDelay[x][y])          // start new growing cycle
8943   {
8944     MovDelay[x][y] = 7;
8945
8946     if (DelayReached(&sound_delay, sound_delay_value))
8947     {
8948       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8949       sound_delay_value = 30;
8950     }
8951   }
8952
8953   if (MovDelay[x][y])           // wait some time before growing bigger
8954   {
8955     MovDelay[x][y]--;
8956     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8957     {
8958       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8959                                            6 - MovDelay[x][y]);
8960
8961       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8962     }
8963
8964     if (!MovDelay[x][y])
8965     {
8966       Tile[x][y] = Store[x][y];
8967       Store[x][y] = 0;
8968       TEST_DrawLevelField(x, y);
8969     }
8970   }
8971 }
8972
8973 static void AmoebaShrinking(int x, int y)
8974 {
8975   static unsigned int sound_delay = 0;
8976   static unsigned int sound_delay_value = 0;
8977
8978   if (!MovDelay[x][y])          // start new shrinking cycle
8979   {
8980     MovDelay[x][y] = 7;
8981
8982     if (DelayReached(&sound_delay, sound_delay_value))
8983       sound_delay_value = 30;
8984   }
8985
8986   if (MovDelay[x][y])           // wait some time before shrinking
8987   {
8988     MovDelay[x][y]--;
8989     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8990     {
8991       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8992                                            6 - MovDelay[x][y]);
8993
8994       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8995     }
8996
8997     if (!MovDelay[x][y])
8998     {
8999       Tile[x][y] = EL_EMPTY;
9000       TEST_DrawLevelField(x, y);
9001
9002       // don't let mole enter this field in this cycle;
9003       // (give priority to objects falling to this field from above)
9004       Stop[x][y] = TRUE;
9005     }
9006   }
9007 }
9008
9009 static void AmoebaReproduce(int ax, int ay)
9010 {
9011   int i;
9012   int element = Tile[ax][ay];
9013   int graphic = el2img(element);
9014   int newax = ax, neway = ay;
9015   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9016   static int xy[4][2] =
9017   {
9018     { 0, -1 },
9019     { -1, 0 },
9020     { +1, 0 },
9021     { 0, +1 }
9022   };
9023
9024   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9025   {
9026     Tile[ax][ay] = EL_AMOEBA_DEAD;
9027     TEST_DrawLevelField(ax, ay);
9028     return;
9029   }
9030
9031   if (IS_ANIMATED(graphic))
9032     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9033
9034   if (!MovDelay[ax][ay])        // start making new amoeba field
9035     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9036
9037   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9038   {
9039     MovDelay[ax][ay]--;
9040     if (MovDelay[ax][ay])
9041       return;
9042   }
9043
9044   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9045   {
9046     int start = RND(4);
9047     int x = ax + xy[start][0];
9048     int y = ay + xy[start][1];
9049
9050     if (!IN_LEV_FIELD(x, y))
9051       return;
9052
9053     if (IS_FREE(x, y) ||
9054         CAN_GROW_INTO(Tile[x][y]) ||
9055         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9056         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9057     {
9058       newax = x;
9059       neway = y;
9060     }
9061
9062     if (newax == ax && neway == ay)
9063       return;
9064   }
9065   else                          // normal or "filled" (BD style) amoeba
9066   {
9067     int start = RND(4);
9068     boolean waiting_for_player = FALSE;
9069
9070     for (i = 0; i < NUM_DIRECTIONS; i++)
9071     {
9072       int j = (start + i) % 4;
9073       int x = ax + xy[j][0];
9074       int y = ay + xy[j][1];
9075
9076       if (!IN_LEV_FIELD(x, y))
9077         continue;
9078
9079       if (IS_FREE(x, y) ||
9080           CAN_GROW_INTO(Tile[x][y]) ||
9081           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9082           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9083       {
9084         newax = x;
9085         neway = y;
9086         break;
9087       }
9088       else if (IS_PLAYER(x, y))
9089         waiting_for_player = TRUE;
9090     }
9091
9092     if (newax == ax && neway == ay)             // amoeba cannot grow
9093     {
9094       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9095       {
9096         Tile[ax][ay] = EL_AMOEBA_DEAD;
9097         TEST_DrawLevelField(ax, ay);
9098         AmoebaCnt[AmoebaNr[ax][ay]]--;
9099
9100         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9101         {
9102           if (element == EL_AMOEBA_FULL)
9103             AmoebaToDiamond(ax, ay);
9104           else if (element == EL_BD_AMOEBA)
9105             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9106         }
9107       }
9108       return;
9109     }
9110     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9111     {
9112       // amoeba gets larger by growing in some direction
9113
9114       int new_group_nr = AmoebaNr[ax][ay];
9115
9116 #ifdef DEBUG
9117   if (new_group_nr == 0)
9118   {
9119     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9120           newax, neway);
9121     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9122
9123     return;
9124   }
9125 #endif
9126
9127       AmoebaNr[newax][neway] = new_group_nr;
9128       AmoebaCnt[new_group_nr]++;
9129       AmoebaCnt2[new_group_nr]++;
9130
9131       // if amoeba touches other amoeba(s) after growing, unify them
9132       AmoebaMerge(newax, neway);
9133
9134       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9135       {
9136         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9137         return;
9138       }
9139     }
9140   }
9141
9142   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9143       (neway == lev_fieldy - 1 && newax != ax))
9144   {
9145     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9146     Store[newax][neway] = element;
9147   }
9148   else if (neway == ay || element == EL_EMC_DRIPPER)
9149   {
9150     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9151
9152     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9153   }
9154   else
9155   {
9156     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9157     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9158     Store[ax][ay] = EL_AMOEBA_DROP;
9159     ContinueMoving(ax, ay);
9160     return;
9161   }
9162
9163   TEST_DrawLevelField(newax, neway);
9164 }
9165
9166 static void Life(int ax, int ay)
9167 {
9168   int x1, y1, x2, y2;
9169   int life_time = 40;
9170   int element = Tile[ax][ay];
9171   int graphic = el2img(element);
9172   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9173                          level.biomaze);
9174   boolean changed = FALSE;
9175
9176   if (IS_ANIMATED(graphic))
9177     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9178
9179   if (Stop[ax][ay])
9180     return;
9181
9182   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9183     MovDelay[ax][ay] = life_time;
9184
9185   if (MovDelay[ax][ay])         // wait some time before next cycle
9186   {
9187     MovDelay[ax][ay]--;
9188     if (MovDelay[ax][ay])
9189       return;
9190   }
9191
9192   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9193   {
9194     int xx = ax+x1, yy = ay+y1;
9195     int old_element = Tile[xx][yy];
9196     int num_neighbours = 0;
9197
9198     if (!IN_LEV_FIELD(xx, yy))
9199       continue;
9200
9201     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9202     {
9203       int x = xx+x2, y = yy+y2;
9204
9205       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9206         continue;
9207
9208       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9209       boolean is_neighbour = FALSE;
9210
9211       if (level.use_life_bugs)
9212         is_neighbour =
9213           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9214            (IS_FREE(x, y)                             &&  Stop[x][y]));
9215       else
9216         is_neighbour =
9217           (Last[x][y] == element || is_player_cell);
9218
9219       if (is_neighbour)
9220         num_neighbours++;
9221     }
9222
9223     boolean is_free = FALSE;
9224
9225     if (level.use_life_bugs)
9226       is_free = (IS_FREE(xx, yy));
9227     else
9228       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9229
9230     if (xx == ax && yy == ay)           // field in the middle
9231     {
9232       if (num_neighbours < life_parameter[0] ||
9233           num_neighbours > life_parameter[1])
9234       {
9235         Tile[xx][yy] = EL_EMPTY;
9236         if (Tile[xx][yy] != old_element)
9237           TEST_DrawLevelField(xx, yy);
9238         Stop[xx][yy] = TRUE;
9239         changed = TRUE;
9240       }
9241     }
9242     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9243     {                                   // free border field
9244       if (num_neighbours >= life_parameter[2] &&
9245           num_neighbours <= life_parameter[3])
9246       {
9247         Tile[xx][yy] = element;
9248         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9249         if (Tile[xx][yy] != old_element)
9250           TEST_DrawLevelField(xx, yy);
9251         Stop[xx][yy] = TRUE;
9252         changed = TRUE;
9253       }
9254     }
9255   }
9256
9257   if (changed)
9258     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9259                    SND_GAME_OF_LIFE_GROWING);
9260 }
9261
9262 static void InitRobotWheel(int x, int y)
9263 {
9264   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9265 }
9266
9267 static void RunRobotWheel(int x, int y)
9268 {
9269   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9270 }
9271
9272 static void StopRobotWheel(int x, int y)
9273 {
9274   if (game.robot_wheel_x == x &&
9275       game.robot_wheel_y == y)
9276   {
9277     game.robot_wheel_x = -1;
9278     game.robot_wheel_y = -1;
9279     game.robot_wheel_active = FALSE;
9280   }
9281 }
9282
9283 static void InitTimegateWheel(int x, int y)
9284 {
9285   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9286 }
9287
9288 static void RunTimegateWheel(int x, int y)
9289 {
9290   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9291 }
9292
9293 static void InitMagicBallDelay(int x, int y)
9294 {
9295   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9296 }
9297
9298 static void ActivateMagicBall(int bx, int by)
9299 {
9300   int x, y;
9301
9302   if (level.ball_random)
9303   {
9304     int pos_border = RND(8);    // select one of the eight border elements
9305     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9306     int xx = pos_content % 3;
9307     int yy = pos_content / 3;
9308
9309     x = bx - 1 + xx;
9310     y = by - 1 + yy;
9311
9312     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9313       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9314   }
9315   else
9316   {
9317     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9318     {
9319       int xx = x - bx + 1;
9320       int yy = y - by + 1;
9321
9322       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9323         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9324     }
9325   }
9326
9327   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9328 }
9329
9330 static void CheckExit(int x, int y)
9331 {
9332   if (game.gems_still_needed > 0 ||
9333       game.sokoban_fields_still_needed > 0 ||
9334       game.sokoban_objects_still_needed > 0 ||
9335       game.lights_still_needed > 0)
9336   {
9337     int element = Tile[x][y];
9338     int graphic = el2img(element);
9339
9340     if (IS_ANIMATED(graphic))
9341       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9342
9343     return;
9344   }
9345
9346   // do not re-open exit door closed after last player
9347   if (game.all_players_gone)
9348     return;
9349
9350   Tile[x][y] = EL_EXIT_OPENING;
9351
9352   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9353 }
9354
9355 static void CheckExitEM(int x, int y)
9356 {
9357   if (game.gems_still_needed > 0 ||
9358       game.sokoban_fields_still_needed > 0 ||
9359       game.sokoban_objects_still_needed > 0 ||
9360       game.lights_still_needed > 0)
9361   {
9362     int element = Tile[x][y];
9363     int graphic = el2img(element);
9364
9365     if (IS_ANIMATED(graphic))
9366       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9367
9368     return;
9369   }
9370
9371   // do not re-open exit door closed after last player
9372   if (game.all_players_gone)
9373     return;
9374
9375   Tile[x][y] = EL_EM_EXIT_OPENING;
9376
9377   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9378 }
9379
9380 static void CheckExitSteel(int x, int y)
9381 {
9382   if (game.gems_still_needed > 0 ||
9383       game.sokoban_fields_still_needed > 0 ||
9384       game.sokoban_objects_still_needed > 0 ||
9385       game.lights_still_needed > 0)
9386   {
9387     int element = Tile[x][y];
9388     int graphic = el2img(element);
9389
9390     if (IS_ANIMATED(graphic))
9391       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9392
9393     return;
9394   }
9395
9396   // do not re-open exit door closed after last player
9397   if (game.all_players_gone)
9398     return;
9399
9400   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9401
9402   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9403 }
9404
9405 static void CheckExitSteelEM(int x, int y)
9406 {
9407   if (game.gems_still_needed > 0 ||
9408       game.sokoban_fields_still_needed > 0 ||
9409       game.sokoban_objects_still_needed > 0 ||
9410       game.lights_still_needed > 0)
9411   {
9412     int element = Tile[x][y];
9413     int graphic = el2img(element);
9414
9415     if (IS_ANIMATED(graphic))
9416       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9417
9418     return;
9419   }
9420
9421   // do not re-open exit door closed after last player
9422   if (game.all_players_gone)
9423     return;
9424
9425   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9426
9427   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9428 }
9429
9430 static void CheckExitSP(int x, int y)
9431 {
9432   if (game.gems_still_needed > 0)
9433   {
9434     int element = Tile[x][y];
9435     int graphic = el2img(element);
9436
9437     if (IS_ANIMATED(graphic))
9438       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9439
9440     return;
9441   }
9442
9443   // do not re-open exit door closed after last player
9444   if (game.all_players_gone)
9445     return;
9446
9447   Tile[x][y] = EL_SP_EXIT_OPENING;
9448
9449   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9450 }
9451
9452 static void CloseAllOpenTimegates(void)
9453 {
9454   int x, y;
9455
9456   SCAN_PLAYFIELD(x, y)
9457   {
9458     int element = Tile[x][y];
9459
9460     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9461     {
9462       Tile[x][y] = EL_TIMEGATE_CLOSING;
9463
9464       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9465     }
9466   }
9467 }
9468
9469 static void DrawTwinkleOnField(int x, int y)
9470 {
9471   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9472     return;
9473
9474   if (Tile[x][y] == EL_BD_DIAMOND)
9475     return;
9476
9477   if (MovDelay[x][y] == 0)      // next animation frame
9478     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9479
9480   if (MovDelay[x][y] != 0)      // wait some time before next frame
9481   {
9482     MovDelay[x][y]--;
9483
9484     DrawLevelElementAnimation(x, y, Tile[x][y]);
9485
9486     if (MovDelay[x][y] != 0)
9487     {
9488       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9489                                            10 - MovDelay[x][y]);
9490
9491       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9492     }
9493   }
9494 }
9495
9496 static void MauerWaechst(int x, int y)
9497 {
9498   int delay = 6;
9499
9500   if (!MovDelay[x][y])          // next animation frame
9501     MovDelay[x][y] = 3 * delay;
9502
9503   if (MovDelay[x][y])           // wait some time before next frame
9504   {
9505     MovDelay[x][y]--;
9506
9507     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9508     {
9509       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9510       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9511
9512       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9513     }
9514
9515     if (!MovDelay[x][y])
9516     {
9517       if (MovDir[x][y] == MV_LEFT)
9518       {
9519         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9520           TEST_DrawLevelField(x - 1, y);
9521       }
9522       else if (MovDir[x][y] == MV_RIGHT)
9523       {
9524         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9525           TEST_DrawLevelField(x + 1, y);
9526       }
9527       else if (MovDir[x][y] == MV_UP)
9528       {
9529         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9530           TEST_DrawLevelField(x, y - 1);
9531       }
9532       else
9533       {
9534         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9535           TEST_DrawLevelField(x, y + 1);
9536       }
9537
9538       Tile[x][y] = Store[x][y];
9539       Store[x][y] = 0;
9540       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9541       TEST_DrawLevelField(x, y);
9542     }
9543   }
9544 }
9545
9546 static void MauerAbleger(int ax, int ay)
9547 {
9548   int element = Tile[ax][ay];
9549   int graphic = el2img(element);
9550   boolean oben_frei = FALSE, unten_frei = FALSE;
9551   boolean links_frei = FALSE, rechts_frei = FALSE;
9552   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9553   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9554   boolean new_wall = FALSE;
9555
9556   if (IS_ANIMATED(graphic))
9557     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9558
9559   if (!MovDelay[ax][ay])        // start building new wall
9560     MovDelay[ax][ay] = 6;
9561
9562   if (MovDelay[ax][ay])         // wait some time before building new wall
9563   {
9564     MovDelay[ax][ay]--;
9565     if (MovDelay[ax][ay])
9566       return;
9567   }
9568
9569   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9570     oben_frei = TRUE;
9571   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9572     unten_frei = TRUE;
9573   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9574     links_frei = TRUE;
9575   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9576     rechts_frei = TRUE;
9577
9578   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9579       element == EL_EXPANDABLE_WALL_ANY)
9580   {
9581     if (oben_frei)
9582     {
9583       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9584       Store[ax][ay-1] = element;
9585       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9586       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9587         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9588                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9589       new_wall = TRUE;
9590     }
9591     if (unten_frei)
9592     {
9593       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9594       Store[ax][ay+1] = element;
9595       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9596       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9597         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9598                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9599       new_wall = TRUE;
9600     }
9601   }
9602
9603   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9604       element == EL_EXPANDABLE_WALL_ANY ||
9605       element == EL_EXPANDABLE_WALL ||
9606       element == EL_BD_EXPANDABLE_WALL)
9607   {
9608     if (links_frei)
9609     {
9610       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9611       Store[ax-1][ay] = element;
9612       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9613       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9614         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9615                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9616       new_wall = TRUE;
9617     }
9618
9619     if (rechts_frei)
9620     {
9621       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9622       Store[ax+1][ay] = element;
9623       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9624       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9625         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9626                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9627       new_wall = TRUE;
9628     }
9629   }
9630
9631   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9632     TEST_DrawLevelField(ax, ay);
9633
9634   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9635     oben_massiv = TRUE;
9636   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9637     unten_massiv = TRUE;
9638   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9639     links_massiv = TRUE;
9640   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9641     rechts_massiv = TRUE;
9642
9643   if (((oben_massiv && unten_massiv) ||
9644        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9645        element == EL_EXPANDABLE_WALL) &&
9646       ((links_massiv && rechts_massiv) ||
9647        element == EL_EXPANDABLE_WALL_VERTICAL))
9648     Tile[ax][ay] = EL_WALL;
9649
9650   if (new_wall)
9651     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9652 }
9653
9654 static void MauerAblegerStahl(int ax, int ay)
9655 {
9656   int element = Tile[ax][ay];
9657   int graphic = el2img(element);
9658   boolean oben_frei = FALSE, unten_frei = FALSE;
9659   boolean links_frei = FALSE, rechts_frei = FALSE;
9660   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9661   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9662   boolean new_wall = FALSE;
9663
9664   if (IS_ANIMATED(graphic))
9665     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9666
9667   if (!MovDelay[ax][ay])        // start building new wall
9668     MovDelay[ax][ay] = 6;
9669
9670   if (MovDelay[ax][ay])         // wait some time before building new wall
9671   {
9672     MovDelay[ax][ay]--;
9673     if (MovDelay[ax][ay])
9674       return;
9675   }
9676
9677   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9678     oben_frei = TRUE;
9679   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9680     unten_frei = TRUE;
9681   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9682     links_frei = TRUE;
9683   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9684     rechts_frei = TRUE;
9685
9686   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9687       element == EL_EXPANDABLE_STEELWALL_ANY)
9688   {
9689     if (oben_frei)
9690     {
9691       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9692       Store[ax][ay-1] = element;
9693       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9694       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9695         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9696                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9697       new_wall = TRUE;
9698     }
9699     if (unten_frei)
9700     {
9701       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9702       Store[ax][ay+1] = element;
9703       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9704       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9705         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9706                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9707       new_wall = TRUE;
9708     }
9709   }
9710
9711   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9712       element == EL_EXPANDABLE_STEELWALL_ANY)
9713   {
9714     if (links_frei)
9715     {
9716       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9717       Store[ax-1][ay] = element;
9718       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9719       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9720         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9721                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9722       new_wall = TRUE;
9723     }
9724
9725     if (rechts_frei)
9726     {
9727       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9728       Store[ax+1][ay] = element;
9729       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9730       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9731         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9732                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9733       new_wall = TRUE;
9734     }
9735   }
9736
9737   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9738     oben_massiv = TRUE;
9739   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9740     unten_massiv = TRUE;
9741   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9742     links_massiv = TRUE;
9743   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9744     rechts_massiv = TRUE;
9745
9746   if (((oben_massiv && unten_massiv) ||
9747        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9748       ((links_massiv && rechts_massiv) ||
9749        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9750     Tile[ax][ay] = EL_STEELWALL;
9751
9752   if (new_wall)
9753     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9754 }
9755
9756 static void CheckForDragon(int x, int y)
9757 {
9758   int i, j;
9759   boolean dragon_found = FALSE;
9760   static int xy[4][2] =
9761   {
9762     { 0, -1 },
9763     { -1, 0 },
9764     { +1, 0 },
9765     { 0, +1 }
9766   };
9767
9768   for (i = 0; i < NUM_DIRECTIONS; i++)
9769   {
9770     for (j = 0; j < 4; j++)
9771     {
9772       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9773
9774       if (IN_LEV_FIELD(xx, yy) &&
9775           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9776       {
9777         if (Tile[xx][yy] == EL_DRAGON)
9778           dragon_found = TRUE;
9779       }
9780       else
9781         break;
9782     }
9783   }
9784
9785   if (!dragon_found)
9786   {
9787     for (i = 0; i < NUM_DIRECTIONS; i++)
9788     {
9789       for (j = 0; j < 3; j++)
9790       {
9791         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9792   
9793         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9794         {
9795           Tile[xx][yy] = EL_EMPTY;
9796           TEST_DrawLevelField(xx, yy);
9797         }
9798         else
9799           break;
9800       }
9801     }
9802   }
9803 }
9804
9805 static void InitBuggyBase(int x, int y)
9806 {
9807   int element = Tile[x][y];
9808   int activating_delay = FRAMES_PER_SECOND / 4;
9809
9810   ChangeDelay[x][y] =
9811     (element == EL_SP_BUGGY_BASE ?
9812      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9813      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9814      activating_delay :
9815      element == EL_SP_BUGGY_BASE_ACTIVE ?
9816      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9817 }
9818
9819 static void WarnBuggyBase(int x, int y)
9820 {
9821   int i;
9822   static int xy[4][2] =
9823   {
9824     { 0, -1 },
9825     { -1, 0 },
9826     { +1, 0 },
9827     { 0, +1 }
9828   };
9829
9830   for (i = 0; i < NUM_DIRECTIONS; i++)
9831   {
9832     int xx = x + xy[i][0];
9833     int yy = y + xy[i][1];
9834
9835     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9836     {
9837       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9838
9839       break;
9840     }
9841   }
9842 }
9843
9844 static void InitTrap(int x, int y)
9845 {
9846   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9847 }
9848
9849 static void ActivateTrap(int x, int y)
9850 {
9851   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9852 }
9853
9854 static void ChangeActiveTrap(int x, int y)
9855 {
9856   int graphic = IMG_TRAP_ACTIVE;
9857
9858   // if new animation frame was drawn, correct crumbled sand border
9859   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9860     TEST_DrawLevelFieldCrumbled(x, y);
9861 }
9862
9863 static int getSpecialActionElement(int element, int number, int base_element)
9864 {
9865   return (element != EL_EMPTY ? element :
9866           number != -1 ? base_element + number - 1 :
9867           EL_EMPTY);
9868 }
9869
9870 static int getModifiedActionNumber(int value_old, int operator, int operand,
9871                                    int value_min, int value_max)
9872 {
9873   int value_new = (operator == CA_MODE_SET      ? operand :
9874                    operator == CA_MODE_ADD      ? value_old + operand :
9875                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9876                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9877                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9878                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9879                    value_old);
9880
9881   return (value_new < value_min ? value_min :
9882           value_new > value_max ? value_max :
9883           value_new);
9884 }
9885
9886 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9887 {
9888   struct ElementInfo *ei = &element_info[element];
9889   struct ElementChangeInfo *change = &ei->change_page[page];
9890   int target_element = change->target_element;
9891   int action_type = change->action_type;
9892   int action_mode = change->action_mode;
9893   int action_arg = change->action_arg;
9894   int action_element = change->action_element;
9895   int i;
9896
9897   if (!change->has_action)
9898     return;
9899
9900   // ---------- determine action paramater values -----------------------------
9901
9902   int level_time_value =
9903     (level.time > 0 ? TimeLeft :
9904      TimePlayed);
9905
9906   int action_arg_element_raw =
9907     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9908      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9909      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9910      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9911      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9912      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9913      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9914      EL_EMPTY);
9915   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9916
9917   int action_arg_direction =
9918     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9919      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9920      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9921      change->actual_trigger_side :
9922      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9923      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9924      MV_NONE);
9925
9926   int action_arg_number_min =
9927     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9928      CA_ARG_MIN);
9929
9930   int action_arg_number_max =
9931     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9932      action_type == CA_SET_LEVEL_GEMS ? 999 :
9933      action_type == CA_SET_LEVEL_TIME ? 9999 :
9934      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9935      action_type == CA_SET_CE_VALUE ? 9999 :
9936      action_type == CA_SET_CE_SCORE ? 9999 :
9937      CA_ARG_MAX);
9938
9939   int action_arg_number_reset =
9940     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9941      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9942      action_type == CA_SET_LEVEL_TIME ? level.time :
9943      action_type == CA_SET_LEVEL_SCORE ? 0 :
9944      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9945      action_type == CA_SET_CE_SCORE ? 0 :
9946      0);
9947
9948   int action_arg_number =
9949     (action_arg <= CA_ARG_MAX ? action_arg :
9950      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9951      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9952      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9953      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9954      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9955      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9956      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9957      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9958      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9959      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9960      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9961      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9962      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9963      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9964      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9965      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9966      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9967      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9968      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9969      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9970      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9971      -1);
9972
9973   int action_arg_number_old =
9974     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9975      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9976      action_type == CA_SET_LEVEL_SCORE ? game.score :
9977      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9978      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9979      0);
9980
9981   int action_arg_number_new =
9982     getModifiedActionNumber(action_arg_number_old,
9983                             action_mode, action_arg_number,
9984                             action_arg_number_min, action_arg_number_max);
9985
9986   int trigger_player_bits =
9987     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9988      change->actual_trigger_player_bits : change->trigger_player);
9989
9990   int action_arg_player_bits =
9991     (action_arg >= CA_ARG_PLAYER_1 &&
9992      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9993      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9994      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9995      PLAYER_BITS_ANY);
9996
9997   // ---------- execute action  -----------------------------------------------
9998
9999   switch (action_type)
10000   {
10001     case CA_NO_ACTION:
10002     {
10003       return;
10004     }
10005
10006     // ---------- level actions  ----------------------------------------------
10007
10008     case CA_RESTART_LEVEL:
10009     {
10010       game.restart_level = TRUE;
10011
10012       break;
10013     }
10014
10015     case CA_SHOW_ENVELOPE:
10016     {
10017       int element = getSpecialActionElement(action_arg_element,
10018                                             action_arg_number, EL_ENVELOPE_1);
10019
10020       if (IS_ENVELOPE(element))
10021         local_player->show_envelope = element;
10022
10023       break;
10024     }
10025
10026     case CA_SET_LEVEL_TIME:
10027     {
10028       if (level.time > 0)       // only modify limited time value
10029       {
10030         TimeLeft = action_arg_number_new;
10031
10032         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10033
10034         DisplayGameControlValues();
10035
10036         if (!TimeLeft && setup.time_limit)
10037           for (i = 0; i < MAX_PLAYERS; i++)
10038             KillPlayer(&stored_player[i]);
10039       }
10040
10041       break;
10042     }
10043
10044     case CA_SET_LEVEL_SCORE:
10045     {
10046       game.score = action_arg_number_new;
10047
10048       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10049
10050       DisplayGameControlValues();
10051
10052       break;
10053     }
10054
10055     case CA_SET_LEVEL_GEMS:
10056     {
10057       game.gems_still_needed = action_arg_number_new;
10058
10059       game.snapshot.collected_item = TRUE;
10060
10061       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10062
10063       DisplayGameControlValues();
10064
10065       break;
10066     }
10067
10068     case CA_SET_LEVEL_WIND:
10069     {
10070       game.wind_direction = action_arg_direction;
10071
10072       break;
10073     }
10074
10075     case CA_SET_LEVEL_RANDOM_SEED:
10076     {
10077       // ensure that setting a new random seed while playing is predictable
10078       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10079
10080       break;
10081     }
10082
10083     // ---------- player actions  ---------------------------------------------
10084
10085     case CA_MOVE_PLAYER:
10086     case CA_MOVE_PLAYER_NEW:
10087     {
10088       // automatically move to the next field in specified direction
10089       for (i = 0; i < MAX_PLAYERS; i++)
10090         if (trigger_player_bits & (1 << i))
10091           if (action_type == CA_MOVE_PLAYER ||
10092               stored_player[i].MovPos == 0)
10093             stored_player[i].programmed_action = action_arg_direction;
10094
10095       break;
10096     }
10097
10098     case CA_EXIT_PLAYER:
10099     {
10100       for (i = 0; i < MAX_PLAYERS; i++)
10101         if (action_arg_player_bits & (1 << i))
10102           ExitPlayer(&stored_player[i]);
10103
10104       if (game.players_still_needed == 0)
10105         LevelSolved();
10106
10107       break;
10108     }
10109
10110     case CA_KILL_PLAYER:
10111     {
10112       for (i = 0; i < MAX_PLAYERS; i++)
10113         if (action_arg_player_bits & (1 << i))
10114           KillPlayer(&stored_player[i]);
10115
10116       break;
10117     }
10118
10119     case CA_SET_PLAYER_KEYS:
10120     {
10121       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10122       int element = getSpecialActionElement(action_arg_element,
10123                                             action_arg_number, EL_KEY_1);
10124
10125       if (IS_KEY(element))
10126       {
10127         for (i = 0; i < MAX_PLAYERS; i++)
10128         {
10129           if (trigger_player_bits & (1 << i))
10130           {
10131             stored_player[i].key[KEY_NR(element)] = key_state;
10132
10133             DrawGameDoorValues();
10134           }
10135         }
10136       }
10137
10138       break;
10139     }
10140
10141     case CA_SET_PLAYER_SPEED:
10142     {
10143       for (i = 0; i < MAX_PLAYERS; i++)
10144       {
10145         if (trigger_player_bits & (1 << i))
10146         {
10147           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10148
10149           if (action_arg == CA_ARG_SPEED_FASTER &&
10150               stored_player[i].cannot_move)
10151           {
10152             action_arg_number = STEPSIZE_VERY_SLOW;
10153           }
10154           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10155                    action_arg == CA_ARG_SPEED_FASTER)
10156           {
10157             action_arg_number = 2;
10158             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10159                            CA_MODE_MULTIPLY);
10160           }
10161           else if (action_arg == CA_ARG_NUMBER_RESET)
10162           {
10163             action_arg_number = level.initial_player_stepsize[i];
10164           }
10165
10166           move_stepsize =
10167             getModifiedActionNumber(move_stepsize,
10168                                     action_mode,
10169                                     action_arg_number,
10170                                     action_arg_number_min,
10171                                     action_arg_number_max);
10172
10173           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10174         }
10175       }
10176
10177       break;
10178     }
10179
10180     case CA_SET_PLAYER_SHIELD:
10181     {
10182       for (i = 0; i < MAX_PLAYERS; i++)
10183       {
10184         if (trigger_player_bits & (1 << i))
10185         {
10186           if (action_arg == CA_ARG_SHIELD_OFF)
10187           {
10188             stored_player[i].shield_normal_time_left = 0;
10189             stored_player[i].shield_deadly_time_left = 0;
10190           }
10191           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10192           {
10193             stored_player[i].shield_normal_time_left = 999999;
10194           }
10195           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10196           {
10197             stored_player[i].shield_normal_time_left = 999999;
10198             stored_player[i].shield_deadly_time_left = 999999;
10199           }
10200         }
10201       }
10202
10203       break;
10204     }
10205
10206     case CA_SET_PLAYER_GRAVITY:
10207     {
10208       for (i = 0; i < MAX_PLAYERS; i++)
10209       {
10210         if (trigger_player_bits & (1 << i))
10211         {
10212           stored_player[i].gravity =
10213             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10214              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10215              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10216              stored_player[i].gravity);
10217         }
10218       }
10219
10220       break;
10221     }
10222
10223     case CA_SET_PLAYER_ARTWORK:
10224     {
10225       for (i = 0; i < MAX_PLAYERS; i++)
10226       {
10227         if (trigger_player_bits & (1 << i))
10228         {
10229           int artwork_element = action_arg_element;
10230
10231           if (action_arg == CA_ARG_ELEMENT_RESET)
10232             artwork_element =
10233               (level.use_artwork_element[i] ? level.artwork_element[i] :
10234                stored_player[i].element_nr);
10235
10236           if (stored_player[i].artwork_element != artwork_element)
10237             stored_player[i].Frame = 0;
10238
10239           stored_player[i].artwork_element = artwork_element;
10240
10241           SetPlayerWaiting(&stored_player[i], FALSE);
10242
10243           // set number of special actions for bored and sleeping animation
10244           stored_player[i].num_special_action_bored =
10245             get_num_special_action(artwork_element,
10246                                    ACTION_BORING_1, ACTION_BORING_LAST);
10247           stored_player[i].num_special_action_sleeping =
10248             get_num_special_action(artwork_element,
10249                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10250         }
10251       }
10252
10253       break;
10254     }
10255
10256     case CA_SET_PLAYER_INVENTORY:
10257     {
10258       for (i = 0; i < MAX_PLAYERS; i++)
10259       {
10260         struct PlayerInfo *player = &stored_player[i];
10261         int j, k;
10262
10263         if (trigger_player_bits & (1 << i))
10264         {
10265           int inventory_element = action_arg_element;
10266
10267           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10268               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10269               action_arg == CA_ARG_ELEMENT_ACTION)
10270           {
10271             int element = inventory_element;
10272             int collect_count = element_info[element].collect_count_initial;
10273
10274             if (!IS_CUSTOM_ELEMENT(element))
10275               collect_count = 1;
10276
10277             if (collect_count == 0)
10278               player->inventory_infinite_element = element;
10279             else
10280               for (k = 0; k < collect_count; k++)
10281                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10282                   player->inventory_element[player->inventory_size++] =
10283                     element;
10284           }
10285           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10286                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10287                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10288           {
10289             if (player->inventory_infinite_element != EL_UNDEFINED &&
10290                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10291                                      action_arg_element_raw))
10292               player->inventory_infinite_element = EL_UNDEFINED;
10293
10294             for (k = 0, j = 0; j < player->inventory_size; j++)
10295             {
10296               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10297                                         action_arg_element_raw))
10298                 player->inventory_element[k++] = player->inventory_element[j];
10299             }
10300
10301             player->inventory_size = k;
10302           }
10303           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10304           {
10305             if (player->inventory_size > 0)
10306             {
10307               for (j = 0; j < player->inventory_size - 1; j++)
10308                 player->inventory_element[j] = player->inventory_element[j + 1];
10309
10310               player->inventory_size--;
10311             }
10312           }
10313           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10314           {
10315             if (player->inventory_size > 0)
10316               player->inventory_size--;
10317           }
10318           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10319           {
10320             player->inventory_infinite_element = EL_UNDEFINED;
10321             player->inventory_size = 0;
10322           }
10323           else if (action_arg == CA_ARG_INVENTORY_RESET)
10324           {
10325             player->inventory_infinite_element = EL_UNDEFINED;
10326             player->inventory_size = 0;
10327
10328             if (level.use_initial_inventory[i])
10329             {
10330               for (j = 0; j < level.initial_inventory_size[i]; j++)
10331               {
10332                 int element = level.initial_inventory_content[i][j];
10333                 int collect_count = element_info[element].collect_count_initial;
10334
10335                 if (!IS_CUSTOM_ELEMENT(element))
10336                   collect_count = 1;
10337
10338                 if (collect_count == 0)
10339                   player->inventory_infinite_element = element;
10340                 else
10341                   for (k = 0; k < collect_count; k++)
10342                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10343                       player->inventory_element[player->inventory_size++] =
10344                         element;
10345               }
10346             }
10347           }
10348         }
10349       }
10350
10351       break;
10352     }
10353
10354     // ---------- CE actions  -------------------------------------------------
10355
10356     case CA_SET_CE_VALUE:
10357     {
10358       int last_ce_value = CustomValue[x][y];
10359
10360       CustomValue[x][y] = action_arg_number_new;
10361
10362       if (CustomValue[x][y] != last_ce_value)
10363       {
10364         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10365         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10366
10367         if (CustomValue[x][y] == 0)
10368         {
10369           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10370           ChangeCount[x][y] = 0;        // allow at least one more change
10371
10372           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10373           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10374         }
10375       }
10376
10377       break;
10378     }
10379
10380     case CA_SET_CE_SCORE:
10381     {
10382       int last_ce_score = ei->collect_score;
10383
10384       ei->collect_score = action_arg_number_new;
10385
10386       if (ei->collect_score != last_ce_score)
10387       {
10388         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10389         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10390
10391         if (ei->collect_score == 0)
10392         {
10393           int xx, yy;
10394
10395           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10396           ChangeCount[x][y] = 0;        // allow at least one more change
10397
10398           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10399           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10400
10401           /*
10402             This is a very special case that seems to be a mixture between
10403             CheckElementChange() and CheckTriggeredElementChange(): while
10404             the first one only affects single elements that are triggered
10405             directly, the second one affects multiple elements in the playfield
10406             that are triggered indirectly by another element. This is a third
10407             case: Changing the CE score always affects multiple identical CEs,
10408             so every affected CE must be checked, not only the single CE for
10409             which the CE score was changed in the first place (as every instance
10410             of that CE shares the same CE score, and therefore also can change)!
10411           */
10412           SCAN_PLAYFIELD(xx, yy)
10413           {
10414             if (Tile[xx][yy] == element)
10415               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10416                                  CE_SCORE_GETS_ZERO);
10417           }
10418         }
10419       }
10420
10421       break;
10422     }
10423
10424     case CA_SET_CE_ARTWORK:
10425     {
10426       int artwork_element = action_arg_element;
10427       boolean reset_frame = FALSE;
10428       int xx, yy;
10429
10430       if (action_arg == CA_ARG_ELEMENT_RESET)
10431         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10432                            element);
10433
10434       if (ei->gfx_element != artwork_element)
10435         reset_frame = TRUE;
10436
10437       ei->gfx_element = artwork_element;
10438
10439       SCAN_PLAYFIELD(xx, yy)
10440       {
10441         if (Tile[xx][yy] == element)
10442         {
10443           if (reset_frame)
10444           {
10445             ResetGfxAnimation(xx, yy);
10446             ResetRandomAnimationValue(xx, yy);
10447           }
10448
10449           TEST_DrawLevelField(xx, yy);
10450         }
10451       }
10452
10453       break;
10454     }
10455
10456     // ---------- engine actions  ---------------------------------------------
10457
10458     case CA_SET_ENGINE_SCAN_MODE:
10459     {
10460       InitPlayfieldScanMode(action_arg);
10461
10462       break;
10463     }
10464
10465     default:
10466       break;
10467   }
10468 }
10469
10470 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10471 {
10472   int old_element = Tile[x][y];
10473   int new_element = GetElementFromGroupElement(element);
10474   int previous_move_direction = MovDir[x][y];
10475   int last_ce_value = CustomValue[x][y];
10476   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10477   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10478   boolean add_player_onto_element = (new_element_is_player &&
10479                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10480                                      IS_WALKABLE(old_element));
10481
10482   if (!add_player_onto_element)
10483   {
10484     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10485       RemoveMovingField(x, y);
10486     else
10487       RemoveField(x, y);
10488
10489     Tile[x][y] = new_element;
10490
10491     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10492       MovDir[x][y] = previous_move_direction;
10493
10494     if (element_info[new_element].use_last_ce_value)
10495       CustomValue[x][y] = last_ce_value;
10496
10497     InitField_WithBug1(x, y, FALSE);
10498
10499     new_element = Tile[x][y];   // element may have changed
10500
10501     ResetGfxAnimation(x, y);
10502     ResetRandomAnimationValue(x, y);
10503
10504     TEST_DrawLevelField(x, y);
10505
10506     if (GFX_CRUMBLED(new_element))
10507       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10508   }
10509
10510   // check if element under the player changes from accessible to unaccessible
10511   // (needed for special case of dropping element which then changes)
10512   // (must be checked after creating new element for walkable group elements)
10513   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10514       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10515   {
10516     Bang(x, y);
10517
10518     return;
10519   }
10520
10521   // "ChangeCount" not set yet to allow "entered by player" change one time
10522   if (new_element_is_player)
10523     RelocatePlayer(x, y, new_element);
10524
10525   if (is_change)
10526     ChangeCount[x][y]++;        // count number of changes in the same frame
10527
10528   TestIfBadThingTouchesPlayer(x, y);
10529   TestIfPlayerTouchesCustomElement(x, y);
10530   TestIfElementTouchesCustomElement(x, y);
10531 }
10532
10533 static void CreateField(int x, int y, int element)
10534 {
10535   CreateFieldExt(x, y, element, FALSE);
10536 }
10537
10538 static void CreateElementFromChange(int x, int y, int element)
10539 {
10540   element = GET_VALID_RUNTIME_ELEMENT(element);
10541
10542   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10543   {
10544     int old_element = Tile[x][y];
10545
10546     // prevent changed element from moving in same engine frame
10547     // unless both old and new element can either fall or move
10548     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10549         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10550       Stop[x][y] = TRUE;
10551   }
10552
10553   CreateFieldExt(x, y, element, TRUE);
10554 }
10555
10556 static boolean ChangeElement(int x, int y, int element, int page)
10557 {
10558   struct ElementInfo *ei = &element_info[element];
10559   struct ElementChangeInfo *change = &ei->change_page[page];
10560   int ce_value = CustomValue[x][y];
10561   int ce_score = ei->collect_score;
10562   int target_element;
10563   int old_element = Tile[x][y];
10564
10565   // always use default change event to prevent running into a loop
10566   if (ChangeEvent[x][y] == -1)
10567     ChangeEvent[x][y] = CE_DELAY;
10568
10569   if (ChangeEvent[x][y] == CE_DELAY)
10570   {
10571     // reset actual trigger element, trigger player and action element
10572     change->actual_trigger_element = EL_EMPTY;
10573     change->actual_trigger_player = EL_EMPTY;
10574     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10575     change->actual_trigger_side = CH_SIDE_NONE;
10576     change->actual_trigger_ce_value = 0;
10577     change->actual_trigger_ce_score = 0;
10578   }
10579
10580   // do not change elements more than a specified maximum number of changes
10581   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10582     return FALSE;
10583
10584   ChangeCount[x][y]++;          // count number of changes in the same frame
10585
10586   if (change->explode)
10587   {
10588     Bang(x, y);
10589
10590     return TRUE;
10591   }
10592
10593   if (change->use_target_content)
10594   {
10595     boolean complete_replace = TRUE;
10596     boolean can_replace[3][3];
10597     int xx, yy;
10598
10599     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10600     {
10601       boolean is_empty;
10602       boolean is_walkable;
10603       boolean is_diggable;
10604       boolean is_collectible;
10605       boolean is_removable;
10606       boolean is_destructible;
10607       int ex = x + xx - 1;
10608       int ey = y + yy - 1;
10609       int content_element = change->target_content.e[xx][yy];
10610       int e;
10611
10612       can_replace[xx][yy] = TRUE;
10613
10614       if (ex == x && ey == y)   // do not check changing element itself
10615         continue;
10616
10617       if (content_element == EL_EMPTY_SPACE)
10618       {
10619         can_replace[xx][yy] = FALSE;    // do not replace border with space
10620
10621         continue;
10622       }
10623
10624       if (!IN_LEV_FIELD(ex, ey))
10625       {
10626         can_replace[xx][yy] = FALSE;
10627         complete_replace = FALSE;
10628
10629         continue;
10630       }
10631
10632       e = Tile[ex][ey];
10633
10634       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10635         e = MovingOrBlocked2Element(ex, ey);
10636
10637       is_empty = (IS_FREE(ex, ey) ||
10638                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10639
10640       is_walkable     = (is_empty || IS_WALKABLE(e));
10641       is_diggable     = (is_empty || IS_DIGGABLE(e));
10642       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10643       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10644       is_removable    = (is_diggable || is_collectible);
10645
10646       can_replace[xx][yy] =
10647         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10648           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10649           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10650           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10651           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10652           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10653          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10654
10655       if (!can_replace[xx][yy])
10656         complete_replace = FALSE;
10657     }
10658
10659     if (!change->only_if_complete || complete_replace)
10660     {
10661       boolean something_has_changed = FALSE;
10662
10663       if (change->only_if_complete && change->use_random_replace &&
10664           RND(100) < change->random_percentage)
10665         return FALSE;
10666
10667       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10668       {
10669         int ex = x + xx - 1;
10670         int ey = y + yy - 1;
10671         int content_element;
10672
10673         if (can_replace[xx][yy] && (!change->use_random_replace ||
10674                                     RND(100) < change->random_percentage))
10675         {
10676           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10677             RemoveMovingField(ex, ey);
10678
10679           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10680
10681           content_element = change->target_content.e[xx][yy];
10682           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10683                                               ce_value, ce_score);
10684
10685           CreateElementFromChange(ex, ey, target_element);
10686
10687           something_has_changed = TRUE;
10688
10689           // for symmetry reasons, freeze newly created border elements
10690           if (ex != x || ey != y)
10691             Stop[ex][ey] = TRUE;        // no more moving in this frame
10692         }
10693       }
10694
10695       if (something_has_changed)
10696       {
10697         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10698         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10699       }
10700     }
10701   }
10702   else
10703   {
10704     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10705                                         ce_value, ce_score);
10706
10707     if (element == EL_DIAGONAL_GROWING ||
10708         element == EL_DIAGONAL_SHRINKING)
10709     {
10710       target_element = Store[x][y];
10711
10712       Store[x][y] = EL_EMPTY;
10713     }
10714
10715     CreateElementFromChange(x, y, target_element);
10716
10717     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10718     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10719   }
10720
10721   // this uses direct change before indirect change
10722   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10723
10724   return TRUE;
10725 }
10726
10727 static void HandleElementChange(int x, int y, int page)
10728 {
10729   int element = MovingOrBlocked2Element(x, y);
10730   struct ElementInfo *ei = &element_info[element];
10731   struct ElementChangeInfo *change = &ei->change_page[page];
10732   boolean handle_action_before_change = FALSE;
10733
10734 #ifdef DEBUG
10735   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10736       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10737   {
10738     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10739           x, y, element, element_info[element].token_name);
10740     Debug("game:playing:HandleElementChange", "This should never happen!");
10741   }
10742 #endif
10743
10744   // this can happen with classic bombs on walkable, changing elements
10745   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10746   {
10747     return;
10748   }
10749
10750   if (ChangeDelay[x][y] == 0)           // initialize element change
10751   {
10752     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10753
10754     if (change->can_change)
10755     {
10756       // !!! not clear why graphic animation should be reset at all here !!!
10757       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10758       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10759
10760       /*
10761         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10762
10763         When using an animation frame delay of 1 (this only happens with
10764         "sp_zonk.moving.left/right" in the classic graphics), the default
10765         (non-moving) animation shows wrong animation frames (while the
10766         moving animation, like "sp_zonk.moving.left/right", is correct,
10767         so this graphical bug never shows up with the classic graphics).
10768         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10769         be drawn instead of the correct frames 0,1,2,3. This is caused by
10770         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10771         an element change: First when the change delay ("ChangeDelay[][]")
10772         counter has reached zero after decrementing, then a second time in
10773         the next frame (after "GfxFrame[][]" was already incremented) when
10774         "ChangeDelay[][]" is reset to the initial delay value again.
10775
10776         This causes frame 0 to be drawn twice, while the last frame won't
10777         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10778
10779         As some animations may already be cleverly designed around this bug
10780         (at least the "Snake Bite" snake tail animation does this), it cannot
10781         simply be fixed here without breaking such existing animations.
10782         Unfortunately, it cannot easily be detected if a graphics set was
10783         designed "before" or "after" the bug was fixed. As a workaround,
10784         a new graphics set option "game.graphics_engine_version" was added
10785         to be able to specify the game's major release version for which the
10786         graphics set was designed, which can then be used to decide if the
10787         bugfix should be used (version 4 and above) or not (version 3 or
10788         below, or if no version was specified at all, as with old sets).
10789
10790         (The wrong/fixed animation frames can be tested with the test level set
10791         "test_gfxframe" and level "000", which contains a specially prepared
10792         custom element at level position (x/y) == (11/9) which uses the zonk
10793         animation mentioned above. Using "game.graphics_engine_version: 4"
10794         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10795         This can also be seen from the debug output for this test element.)
10796       */
10797
10798       // when a custom element is about to change (for example by change delay),
10799       // do not reset graphic animation when the custom element is moving
10800       if (game.graphics_engine_version < 4 &&
10801           !IS_MOVING(x, y))
10802       {
10803         ResetGfxAnimation(x, y);
10804         ResetRandomAnimationValue(x, y);
10805       }
10806
10807       if (change->pre_change_function)
10808         change->pre_change_function(x, y);
10809     }
10810   }
10811
10812   ChangeDelay[x][y]--;
10813
10814   if (ChangeDelay[x][y] != 0)           // continue element change
10815   {
10816     if (change->can_change)
10817     {
10818       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10819
10820       if (IS_ANIMATED(graphic))
10821         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10822
10823       if (change->change_function)
10824         change->change_function(x, y);
10825     }
10826   }
10827   else                                  // finish element change
10828   {
10829     if (ChangePage[x][y] != -1)         // remember page from delayed change
10830     {
10831       page = ChangePage[x][y];
10832       ChangePage[x][y] = -1;
10833
10834       change = &ei->change_page[page];
10835     }
10836
10837     if (IS_MOVING(x, y))                // never change a running system ;-)
10838     {
10839       ChangeDelay[x][y] = 1;            // try change after next move step
10840       ChangePage[x][y] = page;          // remember page to use for change
10841
10842       return;
10843     }
10844
10845     // special case: set new level random seed before changing element
10846     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10847       handle_action_before_change = TRUE;
10848
10849     if (change->has_action && handle_action_before_change)
10850       ExecuteCustomElementAction(x, y, element, page);
10851
10852     if (change->can_change)
10853     {
10854       if (ChangeElement(x, y, element, page))
10855       {
10856         if (change->post_change_function)
10857           change->post_change_function(x, y);
10858       }
10859     }
10860
10861     if (change->has_action && !handle_action_before_change)
10862       ExecuteCustomElementAction(x, y, element, page);
10863   }
10864 }
10865
10866 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10867                                               int trigger_element,
10868                                               int trigger_event,
10869                                               int trigger_player,
10870                                               int trigger_side,
10871                                               int trigger_page)
10872 {
10873   boolean change_done_any = FALSE;
10874   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10875   int i;
10876
10877   if (!(trigger_events[trigger_element][trigger_event]))
10878     return FALSE;
10879
10880   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10881
10882   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10883   {
10884     int element = EL_CUSTOM_START + i;
10885     boolean change_done = FALSE;
10886     int p;
10887
10888     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10889         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10890       continue;
10891
10892     for (p = 0; p < element_info[element].num_change_pages; p++)
10893     {
10894       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10895
10896       if (change->can_change_or_has_action &&
10897           change->has_event[trigger_event] &&
10898           change->trigger_side & trigger_side &&
10899           change->trigger_player & trigger_player &&
10900           change->trigger_page & trigger_page_bits &&
10901           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10902       {
10903         change->actual_trigger_element = trigger_element;
10904         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10905         change->actual_trigger_player_bits = trigger_player;
10906         change->actual_trigger_side = trigger_side;
10907         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10908         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10909
10910         if ((change->can_change && !change_done) || change->has_action)
10911         {
10912           int x, y;
10913
10914           SCAN_PLAYFIELD(x, y)
10915           {
10916             if (Tile[x][y] == element)
10917             {
10918               if (change->can_change && !change_done)
10919               {
10920                 // if element already changed in this frame, not only prevent
10921                 // another element change (checked in ChangeElement()), but
10922                 // also prevent additional element actions for this element
10923
10924                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10925                     !level.use_action_after_change_bug)
10926                   continue;
10927
10928                 ChangeDelay[x][y] = 1;
10929                 ChangeEvent[x][y] = trigger_event;
10930
10931                 HandleElementChange(x, y, p);
10932               }
10933               else if (change->has_action)
10934               {
10935                 // if element already changed in this frame, not only prevent
10936                 // another element change (checked in ChangeElement()), but
10937                 // also prevent additional element actions for this element
10938
10939                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10940                     !level.use_action_after_change_bug)
10941                   continue;
10942
10943                 ExecuteCustomElementAction(x, y, element, p);
10944                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10945               }
10946             }
10947           }
10948
10949           if (change->can_change)
10950           {
10951             change_done = TRUE;
10952             change_done_any = TRUE;
10953           }
10954         }
10955       }
10956     }
10957   }
10958
10959   RECURSION_LOOP_DETECTION_END();
10960
10961   return change_done_any;
10962 }
10963
10964 static boolean CheckElementChangeExt(int x, int y,
10965                                      int element,
10966                                      int trigger_element,
10967                                      int trigger_event,
10968                                      int trigger_player,
10969                                      int trigger_side)
10970 {
10971   boolean change_done = FALSE;
10972   int p;
10973
10974   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10975       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10976     return FALSE;
10977
10978   if (Tile[x][y] == EL_BLOCKED)
10979   {
10980     Blocked2Moving(x, y, &x, &y);
10981     element = Tile[x][y];
10982   }
10983
10984   // check if element has already changed or is about to change after moving
10985   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10986        Tile[x][y] != element) ||
10987
10988       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10989        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10990         ChangePage[x][y] != -1)))
10991     return FALSE;
10992
10993   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10994
10995   for (p = 0; p < element_info[element].num_change_pages; p++)
10996   {
10997     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10998
10999     /* check trigger element for all events where the element that is checked
11000        for changing interacts with a directly adjacent element -- this is
11001        different to element changes that affect other elements to change on the
11002        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11003     boolean check_trigger_element =
11004       (trigger_event == CE_TOUCHING_X ||
11005        trigger_event == CE_HITTING_X ||
11006        trigger_event == CE_HIT_BY_X ||
11007        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11008
11009     if (change->can_change_or_has_action &&
11010         change->has_event[trigger_event] &&
11011         change->trigger_side & trigger_side &&
11012         change->trigger_player & trigger_player &&
11013         (!check_trigger_element ||
11014          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11015     {
11016       change->actual_trigger_element = trigger_element;
11017       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11018       change->actual_trigger_player_bits = trigger_player;
11019       change->actual_trigger_side = trigger_side;
11020       change->actual_trigger_ce_value = CustomValue[x][y];
11021       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11022
11023       // special case: trigger element not at (x,y) position for some events
11024       if (check_trigger_element)
11025       {
11026         static struct
11027         {
11028           int dx, dy;
11029         } move_xy[] =
11030           {
11031             {  0,  0 },
11032             { -1,  0 },
11033             { +1,  0 },
11034             {  0,  0 },
11035             {  0, -1 },
11036             {  0,  0 }, { 0, 0 }, { 0, 0 },
11037             {  0, +1 }
11038           };
11039
11040         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11041         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11042
11043         change->actual_trigger_ce_value = CustomValue[xx][yy];
11044         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11045       }
11046
11047       if (change->can_change && !change_done)
11048       {
11049         ChangeDelay[x][y] = 1;
11050         ChangeEvent[x][y] = trigger_event;
11051
11052         HandleElementChange(x, y, p);
11053
11054         change_done = TRUE;
11055       }
11056       else if (change->has_action)
11057       {
11058         ExecuteCustomElementAction(x, y, element, p);
11059         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11060       }
11061     }
11062   }
11063
11064   RECURSION_LOOP_DETECTION_END();
11065
11066   return change_done;
11067 }
11068
11069 static void PlayPlayerSound(struct PlayerInfo *player)
11070 {
11071   int jx = player->jx, jy = player->jy;
11072   int sound_element = player->artwork_element;
11073   int last_action = player->last_action_waiting;
11074   int action = player->action_waiting;
11075
11076   if (player->is_waiting)
11077   {
11078     if (action != last_action)
11079       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11080     else
11081       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11082   }
11083   else
11084   {
11085     if (action != last_action)
11086       StopSound(element_info[sound_element].sound[last_action]);
11087
11088     if (last_action == ACTION_SLEEPING)
11089       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11090   }
11091 }
11092
11093 static void PlayAllPlayersSound(void)
11094 {
11095   int i;
11096
11097   for (i = 0; i < MAX_PLAYERS; i++)
11098     if (stored_player[i].active)
11099       PlayPlayerSound(&stored_player[i]);
11100 }
11101
11102 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11103 {
11104   boolean last_waiting = player->is_waiting;
11105   int move_dir = player->MovDir;
11106
11107   player->dir_waiting = move_dir;
11108   player->last_action_waiting = player->action_waiting;
11109
11110   if (is_waiting)
11111   {
11112     if (!last_waiting)          // not waiting -> waiting
11113     {
11114       player->is_waiting = TRUE;
11115
11116       player->frame_counter_bored =
11117         FrameCounter +
11118         game.player_boring_delay_fixed +
11119         GetSimpleRandom(game.player_boring_delay_random);
11120       player->frame_counter_sleeping =
11121         FrameCounter +
11122         game.player_sleeping_delay_fixed +
11123         GetSimpleRandom(game.player_sleeping_delay_random);
11124
11125       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11126     }
11127
11128     if (game.player_sleeping_delay_fixed +
11129         game.player_sleeping_delay_random > 0 &&
11130         player->anim_delay_counter == 0 &&
11131         player->post_delay_counter == 0 &&
11132         FrameCounter >= player->frame_counter_sleeping)
11133       player->is_sleeping = TRUE;
11134     else if (game.player_boring_delay_fixed +
11135              game.player_boring_delay_random > 0 &&
11136              FrameCounter >= player->frame_counter_bored)
11137       player->is_bored = TRUE;
11138
11139     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11140                               player->is_bored ? ACTION_BORING :
11141                               ACTION_WAITING);
11142
11143     if (player->is_sleeping && player->use_murphy)
11144     {
11145       // special case for sleeping Murphy when leaning against non-free tile
11146
11147       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11148           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11149            !IS_MOVING(player->jx - 1, player->jy)))
11150         move_dir = MV_LEFT;
11151       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11152                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11153                 !IS_MOVING(player->jx + 1, player->jy)))
11154         move_dir = MV_RIGHT;
11155       else
11156         player->is_sleeping = FALSE;
11157
11158       player->dir_waiting = move_dir;
11159     }
11160
11161     if (player->is_sleeping)
11162     {
11163       if (player->num_special_action_sleeping > 0)
11164       {
11165         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11166         {
11167           int last_special_action = player->special_action_sleeping;
11168           int num_special_action = player->num_special_action_sleeping;
11169           int special_action =
11170             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11171              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11172              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11173              last_special_action + 1 : ACTION_SLEEPING);
11174           int special_graphic =
11175             el_act_dir2img(player->artwork_element, special_action, move_dir);
11176
11177           player->anim_delay_counter =
11178             graphic_info[special_graphic].anim_delay_fixed +
11179             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11180           player->post_delay_counter =
11181             graphic_info[special_graphic].post_delay_fixed +
11182             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11183
11184           player->special_action_sleeping = special_action;
11185         }
11186
11187         if (player->anim_delay_counter > 0)
11188         {
11189           player->action_waiting = player->special_action_sleeping;
11190           player->anim_delay_counter--;
11191         }
11192         else if (player->post_delay_counter > 0)
11193         {
11194           player->post_delay_counter--;
11195         }
11196       }
11197     }
11198     else if (player->is_bored)
11199     {
11200       if (player->num_special_action_bored > 0)
11201       {
11202         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11203         {
11204           int special_action =
11205             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11206           int special_graphic =
11207             el_act_dir2img(player->artwork_element, special_action, move_dir);
11208
11209           player->anim_delay_counter =
11210             graphic_info[special_graphic].anim_delay_fixed +
11211             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11212           player->post_delay_counter =
11213             graphic_info[special_graphic].post_delay_fixed +
11214             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11215
11216           player->special_action_bored = special_action;
11217         }
11218
11219         if (player->anim_delay_counter > 0)
11220         {
11221           player->action_waiting = player->special_action_bored;
11222           player->anim_delay_counter--;
11223         }
11224         else if (player->post_delay_counter > 0)
11225         {
11226           player->post_delay_counter--;
11227         }
11228       }
11229     }
11230   }
11231   else if (last_waiting)        // waiting -> not waiting
11232   {
11233     player->is_waiting = FALSE;
11234     player->is_bored = FALSE;
11235     player->is_sleeping = FALSE;
11236
11237     player->frame_counter_bored = -1;
11238     player->frame_counter_sleeping = -1;
11239
11240     player->anim_delay_counter = 0;
11241     player->post_delay_counter = 0;
11242
11243     player->dir_waiting = player->MovDir;
11244     player->action_waiting = ACTION_DEFAULT;
11245
11246     player->special_action_bored = ACTION_DEFAULT;
11247     player->special_action_sleeping = ACTION_DEFAULT;
11248   }
11249 }
11250
11251 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11252 {
11253   if ((!player->is_moving  && player->was_moving) ||
11254       (player->MovPos == 0 && player->was_moving) ||
11255       (player->is_snapping && !player->was_snapping) ||
11256       (player->is_dropping && !player->was_dropping))
11257   {
11258     if (!CheckSaveEngineSnapshotToList())
11259       return;
11260
11261     player->was_moving = FALSE;
11262     player->was_snapping = TRUE;
11263     player->was_dropping = TRUE;
11264   }
11265   else
11266   {
11267     if (player->is_moving)
11268       player->was_moving = TRUE;
11269
11270     if (!player->is_snapping)
11271       player->was_snapping = FALSE;
11272
11273     if (!player->is_dropping)
11274       player->was_dropping = FALSE;
11275   }
11276
11277   static struct MouseActionInfo mouse_action_last = { 0 };
11278   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11279   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11280
11281   if (new_released)
11282     CheckSaveEngineSnapshotToList();
11283
11284   mouse_action_last = mouse_action;
11285 }
11286
11287 static void CheckSingleStepMode(struct PlayerInfo *player)
11288 {
11289   if (tape.single_step && tape.recording && !tape.pausing)
11290   {
11291     /* as it is called "single step mode", just return to pause mode when the
11292        player stopped moving after one tile (or never starts moving at all) */
11293     if (!player->is_moving &&
11294         !player->is_pushing &&
11295         !player->is_dropping_pressed &&
11296         !player->effective_mouse_action.button)
11297       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11298   }
11299
11300   CheckSaveEngineSnapshot(player);
11301 }
11302
11303 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11304 {
11305   int left      = player_action & JOY_LEFT;
11306   int right     = player_action & JOY_RIGHT;
11307   int up        = player_action & JOY_UP;
11308   int down      = player_action & JOY_DOWN;
11309   int button1   = player_action & JOY_BUTTON_1;
11310   int button2   = player_action & JOY_BUTTON_2;
11311   int dx        = (left ? -1 : right ? 1 : 0);
11312   int dy        = (up   ? -1 : down  ? 1 : 0);
11313
11314   if (!player->active || tape.pausing)
11315     return 0;
11316
11317   if (player_action)
11318   {
11319     if (button1)
11320       SnapField(player, dx, dy);
11321     else
11322     {
11323       if (button2)
11324         DropElement(player);
11325
11326       MovePlayer(player, dx, dy);
11327     }
11328
11329     CheckSingleStepMode(player);
11330
11331     SetPlayerWaiting(player, FALSE);
11332
11333     return player_action;
11334   }
11335   else
11336   {
11337     // no actions for this player (no input at player's configured device)
11338
11339     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11340     SnapField(player, 0, 0);
11341     CheckGravityMovementWhenNotMoving(player);
11342
11343     if (player->MovPos == 0)
11344       SetPlayerWaiting(player, TRUE);
11345
11346     if (player->MovPos == 0)    // needed for tape.playing
11347       player->is_moving = FALSE;
11348
11349     player->is_dropping = FALSE;
11350     player->is_dropping_pressed = FALSE;
11351     player->drop_pressed_delay = 0;
11352
11353     CheckSingleStepMode(player);
11354
11355     return 0;
11356   }
11357 }
11358
11359 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11360                                          byte *tape_action)
11361 {
11362   if (!tape.use_mouse_actions)
11363     return;
11364
11365   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11366   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11367   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11368 }
11369
11370 static void SetTapeActionFromMouseAction(byte *tape_action,
11371                                          struct MouseActionInfo *mouse_action)
11372 {
11373   if (!tape.use_mouse_actions)
11374     return;
11375
11376   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11377   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11378   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11379 }
11380
11381 static void CheckLevelSolved(void)
11382 {
11383   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11384   {
11385     if (game_em.level_solved &&
11386         !game_em.game_over)                             // game won
11387     {
11388       LevelSolved();
11389
11390       game_em.game_over = TRUE;
11391
11392       game.all_players_gone = TRUE;
11393     }
11394
11395     if (game_em.game_over)                              // game lost
11396       game.all_players_gone = TRUE;
11397   }
11398   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11399   {
11400     if (game_sp.level_solved &&
11401         !game_sp.game_over)                             // game won
11402     {
11403       LevelSolved();
11404
11405       game_sp.game_over = TRUE;
11406
11407       game.all_players_gone = TRUE;
11408     }
11409
11410     if (game_sp.game_over)                              // game lost
11411       game.all_players_gone = TRUE;
11412   }
11413   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11414   {
11415     if (game_mm.level_solved &&
11416         !game_mm.game_over)                             // game won
11417     {
11418       LevelSolved();
11419
11420       game_mm.game_over = TRUE;
11421
11422       game.all_players_gone = TRUE;
11423     }
11424
11425     if (game_mm.game_over)                              // game lost
11426       game.all_players_gone = TRUE;
11427   }
11428 }
11429
11430 static void CheckLevelTime(void)
11431 {
11432   int i;
11433
11434   if (TimeFrames >= FRAMES_PER_SECOND)
11435   {
11436     TimeFrames = 0;
11437     TapeTime++;
11438
11439     for (i = 0; i < MAX_PLAYERS; i++)
11440     {
11441       struct PlayerInfo *player = &stored_player[i];
11442
11443       if (SHIELD_ON(player))
11444       {
11445         player->shield_normal_time_left--;
11446
11447         if (player->shield_deadly_time_left > 0)
11448           player->shield_deadly_time_left--;
11449       }
11450     }
11451
11452     if (!game.LevelSolved && !level.use_step_counter)
11453     {
11454       TimePlayed++;
11455
11456       if (TimeLeft > 0)
11457       {
11458         TimeLeft--;
11459
11460         if (TimeLeft <= 10 && setup.time_limit)
11461           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11462
11463         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11464            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11465
11466         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11467
11468         if (!TimeLeft && setup.time_limit)
11469         {
11470           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11471             game_em.lev->killed_out_of_time = TRUE;
11472           else
11473             for (i = 0; i < MAX_PLAYERS; i++)
11474               KillPlayer(&stored_player[i]);
11475         }
11476       }
11477       else if (game.no_time_limit && !game.all_players_gone)
11478       {
11479         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11480       }
11481
11482       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11483     }
11484
11485     if (tape.recording || tape.playing)
11486       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11487   }
11488
11489   if (tape.recording || tape.playing)
11490     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11491
11492   UpdateAndDisplayGameControlValues();
11493 }
11494
11495 void AdvanceFrameAndPlayerCounters(int player_nr)
11496 {
11497   int i;
11498
11499   // advance frame counters (global frame counter and time frame counter)
11500   FrameCounter++;
11501   TimeFrames++;
11502
11503   // advance player counters (counters for move delay, move animation etc.)
11504   for (i = 0; i < MAX_PLAYERS; i++)
11505   {
11506     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11507     int move_delay_value = stored_player[i].move_delay_value;
11508     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11509
11510     if (!advance_player_counters)       // not all players may be affected
11511       continue;
11512
11513     if (move_frames == 0)       // less than one move per game frame
11514     {
11515       int stepsize = TILEX / move_delay_value;
11516       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11517       int count = (stored_player[i].is_moving ?
11518                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11519
11520       if (count % delay == 0)
11521         move_frames = 1;
11522     }
11523
11524     stored_player[i].Frame += move_frames;
11525
11526     if (stored_player[i].MovPos != 0)
11527       stored_player[i].StepFrame += move_frames;
11528
11529     if (stored_player[i].move_delay > 0)
11530       stored_player[i].move_delay--;
11531
11532     // due to bugs in previous versions, counter must count up, not down
11533     if (stored_player[i].push_delay != -1)
11534       stored_player[i].push_delay++;
11535
11536     if (stored_player[i].drop_delay > 0)
11537       stored_player[i].drop_delay--;
11538
11539     if (stored_player[i].is_dropping_pressed)
11540       stored_player[i].drop_pressed_delay++;
11541   }
11542 }
11543
11544 void StartGameActions(boolean init_network_game, boolean record_tape,
11545                       int random_seed)
11546 {
11547   unsigned int new_random_seed = InitRND(random_seed);
11548
11549   if (record_tape)
11550     TapeStartRecording(new_random_seed);
11551
11552   if (init_network_game)
11553   {
11554     SendToServer_LevelFile();
11555     SendToServer_StartPlaying();
11556
11557     return;
11558   }
11559
11560   InitGame();
11561 }
11562
11563 static void GameActionsExt(void)
11564 {
11565 #if 0
11566   static unsigned int game_frame_delay = 0;
11567 #endif
11568   unsigned int game_frame_delay_value;
11569   byte *recorded_player_action;
11570   byte summarized_player_action = 0;
11571   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11572   int i;
11573
11574   // detect endless loops, caused by custom element programming
11575   if (recursion_loop_detected && recursion_loop_depth == 0)
11576   {
11577     char *message = getStringCat3("Internal Error! Element ",
11578                                   EL_NAME(recursion_loop_element),
11579                                   " caused endless loop! Quit the game?");
11580
11581     Warn("element '%s' caused endless loop in game engine",
11582          EL_NAME(recursion_loop_element));
11583
11584     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11585
11586     recursion_loop_detected = FALSE;    // if game should be continued
11587
11588     free(message);
11589
11590     return;
11591   }
11592
11593   if (game.restart_level)
11594     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11595
11596   CheckLevelSolved();
11597
11598   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11599     GameWon();
11600
11601   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11602     TapeStop();
11603
11604   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11605     return;
11606
11607   game_frame_delay_value =
11608     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11609
11610   if (tape.playing && tape.warp_forward && !tape.pausing)
11611     game_frame_delay_value = 0;
11612
11613   SetVideoFrameDelay(game_frame_delay_value);
11614
11615   // (de)activate virtual buttons depending on current game status
11616   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11617   {
11618     if (game.all_players_gone)  // if no players there to be controlled anymore
11619       SetOverlayActive(FALSE);
11620     else if (!tape.playing)     // if game continues after tape stopped playing
11621       SetOverlayActive(TRUE);
11622   }
11623
11624 #if 0
11625 #if 0
11626   // ---------- main game synchronization point ----------
11627
11628   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11629
11630   Debug("game:playing:skip", "skip == %d", skip);
11631
11632 #else
11633   // ---------- main game synchronization point ----------
11634
11635   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11636 #endif
11637 #endif
11638
11639   if (network_playing && !network_player_action_received)
11640   {
11641     // try to get network player actions in time
11642
11643     // last chance to get network player actions without main loop delay
11644     HandleNetworking();
11645
11646     // game was quit by network peer
11647     if (game_status != GAME_MODE_PLAYING)
11648       return;
11649
11650     // check if network player actions still missing and game still running
11651     if (!network_player_action_received && !checkGameEnded())
11652       return;           // failed to get network player actions in time
11653
11654     // do not yet reset "network_player_action_received" (for tape.pausing)
11655   }
11656
11657   if (tape.pausing)
11658     return;
11659
11660   // at this point we know that we really continue executing the game
11661
11662   network_player_action_received = FALSE;
11663
11664   // when playing tape, read previously recorded player input from tape data
11665   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11666
11667   local_player->effective_mouse_action = local_player->mouse_action;
11668
11669   if (recorded_player_action != NULL)
11670     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11671                                  recorded_player_action);
11672
11673   // TapePlayAction() may return NULL when toggling to "pause before death"
11674   if (tape.pausing)
11675     return;
11676
11677   if (tape.set_centered_player)
11678   {
11679     game.centered_player_nr_next = tape.centered_player_nr_next;
11680     game.set_centered_player = TRUE;
11681   }
11682
11683   for (i = 0; i < MAX_PLAYERS; i++)
11684   {
11685     summarized_player_action |= stored_player[i].action;
11686
11687     if (!network_playing && (game.team_mode || tape.playing))
11688       stored_player[i].effective_action = stored_player[i].action;
11689   }
11690
11691   if (network_playing && !checkGameEnded())
11692     SendToServer_MovePlayer(summarized_player_action);
11693
11694   // summarize all actions at local players mapped input device position
11695   // (this allows using different input devices in single player mode)
11696   if (!network.enabled && !game.team_mode)
11697     stored_player[map_player_action[local_player->index_nr]].effective_action =
11698       summarized_player_action;
11699
11700   // summarize all actions at centered player in local team mode
11701   if (tape.recording &&
11702       setup.team_mode && !network.enabled &&
11703       setup.input_on_focus &&
11704       game.centered_player_nr != -1)
11705   {
11706     for (i = 0; i < MAX_PLAYERS; i++)
11707       stored_player[map_player_action[i]].effective_action =
11708         (i == game.centered_player_nr ? summarized_player_action : 0);
11709   }
11710
11711   if (recorded_player_action != NULL)
11712     for (i = 0; i < MAX_PLAYERS; i++)
11713       stored_player[i].effective_action = recorded_player_action[i];
11714
11715   for (i = 0; i < MAX_PLAYERS; i++)
11716   {
11717     tape_action[i] = stored_player[i].effective_action;
11718
11719     /* (this may happen in the RND game engine if a player was not present on
11720        the playfield on level start, but appeared later from a custom element */
11721     if (setup.team_mode &&
11722         tape.recording &&
11723         tape_action[i] &&
11724         !tape.player_participates[i])
11725       tape.player_participates[i] = TRUE;
11726   }
11727
11728   SetTapeActionFromMouseAction(tape_action,
11729                                &local_player->effective_mouse_action);
11730
11731   // only record actions from input devices, but not programmed actions
11732   if (tape.recording)
11733     TapeRecordAction(tape_action);
11734
11735   // remember if game was played (especially after tape stopped playing)
11736   if (!tape.playing && summarized_player_action)
11737     game.GamePlayed = TRUE;
11738
11739 #if USE_NEW_PLAYER_ASSIGNMENTS
11740   // !!! also map player actions in single player mode !!!
11741   // if (game.team_mode)
11742   if (1)
11743   {
11744     byte mapped_action[MAX_PLAYERS];
11745
11746 #if DEBUG_PLAYER_ACTIONS
11747     for (i = 0; i < MAX_PLAYERS; i++)
11748       DebugContinued("", "%d, ", stored_player[i].effective_action);
11749 #endif
11750
11751     for (i = 0; i < MAX_PLAYERS; i++)
11752       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11753
11754     for (i = 0; i < MAX_PLAYERS; i++)
11755       stored_player[i].effective_action = mapped_action[i];
11756
11757 #if DEBUG_PLAYER_ACTIONS
11758     DebugContinued("", "=> ");
11759     for (i = 0; i < MAX_PLAYERS; i++)
11760       DebugContinued("", "%d, ", stored_player[i].effective_action);
11761     DebugContinued("game:playing:player", "\n");
11762 #endif
11763   }
11764 #if DEBUG_PLAYER_ACTIONS
11765   else
11766   {
11767     for (i = 0; i < MAX_PLAYERS; i++)
11768       DebugContinued("", "%d, ", stored_player[i].effective_action);
11769     DebugContinued("game:playing:player", "\n");
11770   }
11771 #endif
11772 #endif
11773
11774   for (i = 0; i < MAX_PLAYERS; i++)
11775   {
11776     // allow engine snapshot in case of changed movement attempt
11777     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11778         (stored_player[i].effective_action & KEY_MOTION))
11779       game.snapshot.changed_action = TRUE;
11780
11781     // allow engine snapshot in case of snapping/dropping attempt
11782     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11783         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11784       game.snapshot.changed_action = TRUE;
11785
11786     game.snapshot.last_action[i] = stored_player[i].effective_action;
11787   }
11788
11789   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11790   {
11791     GameActions_EM_Main();
11792   }
11793   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11794   {
11795     GameActions_SP_Main();
11796   }
11797   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11798   {
11799     GameActions_MM_Main();
11800   }
11801   else
11802   {
11803     GameActions_RND_Main();
11804   }
11805
11806   BlitScreenToBitmap(backbuffer);
11807
11808   CheckLevelSolved();
11809   CheckLevelTime();
11810
11811   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11812
11813   if (global.show_frames_per_second)
11814   {
11815     static unsigned int fps_counter = 0;
11816     static int fps_frames = 0;
11817     unsigned int fps_delay_ms = Counter() - fps_counter;
11818
11819     fps_frames++;
11820
11821     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11822     {
11823       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11824
11825       fps_frames = 0;
11826       fps_counter = Counter();
11827
11828       // always draw FPS to screen after FPS value was updated
11829       redraw_mask |= REDRAW_FPS;
11830     }
11831
11832     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11833     if (GetDrawDeactivationMask() == REDRAW_NONE)
11834       redraw_mask |= REDRAW_FPS;
11835   }
11836 }
11837
11838 static void GameActions_CheckSaveEngineSnapshot(void)
11839 {
11840   if (!game.snapshot.save_snapshot)
11841     return;
11842
11843   // clear flag for saving snapshot _before_ saving snapshot
11844   game.snapshot.save_snapshot = FALSE;
11845
11846   SaveEngineSnapshotToList();
11847 }
11848
11849 void GameActions(void)
11850 {
11851   GameActionsExt();
11852
11853   GameActions_CheckSaveEngineSnapshot();
11854 }
11855
11856 void GameActions_EM_Main(void)
11857 {
11858   byte effective_action[MAX_PLAYERS];
11859   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11860   int i;
11861
11862   for (i = 0; i < MAX_PLAYERS; i++)
11863     effective_action[i] = stored_player[i].effective_action;
11864
11865   GameActions_EM(effective_action, warp_mode);
11866 }
11867
11868 void GameActions_SP_Main(void)
11869 {
11870   byte effective_action[MAX_PLAYERS];
11871   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11872   int i;
11873
11874   for (i = 0; i < MAX_PLAYERS; i++)
11875     effective_action[i] = stored_player[i].effective_action;
11876
11877   GameActions_SP(effective_action, warp_mode);
11878
11879   for (i = 0; i < MAX_PLAYERS; i++)
11880   {
11881     if (stored_player[i].force_dropping)
11882       stored_player[i].action |= KEY_BUTTON_DROP;
11883
11884     stored_player[i].force_dropping = FALSE;
11885   }
11886 }
11887
11888 void GameActions_MM_Main(void)
11889 {
11890   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11891
11892   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11893 }
11894
11895 void GameActions_RND_Main(void)
11896 {
11897   GameActions_RND();
11898 }
11899
11900 void GameActions_RND(void)
11901 {
11902   static struct MouseActionInfo mouse_action_last = { 0 };
11903   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11904   int magic_wall_x = 0, magic_wall_y = 0;
11905   int i, x, y, element, graphic, last_gfx_frame;
11906
11907   InitPlayfieldScanModeVars();
11908
11909   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11910   {
11911     SCAN_PLAYFIELD(x, y)
11912     {
11913       ChangeCount[x][y] = 0;
11914       ChangeEvent[x][y] = -1;
11915     }
11916   }
11917
11918   if (game.set_centered_player)
11919   {
11920     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11921
11922     // switching to "all players" only possible if all players fit to screen
11923     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11924     {
11925       game.centered_player_nr_next = game.centered_player_nr;
11926       game.set_centered_player = FALSE;
11927     }
11928
11929     // do not switch focus to non-existing (or non-active) player
11930     if (game.centered_player_nr_next >= 0 &&
11931         !stored_player[game.centered_player_nr_next].active)
11932     {
11933       game.centered_player_nr_next = game.centered_player_nr;
11934       game.set_centered_player = FALSE;
11935     }
11936   }
11937
11938   if (game.set_centered_player &&
11939       ScreenMovPos == 0)        // screen currently aligned at tile position
11940   {
11941     int sx, sy;
11942
11943     if (game.centered_player_nr_next == -1)
11944     {
11945       setScreenCenteredToAllPlayers(&sx, &sy);
11946     }
11947     else
11948     {
11949       sx = stored_player[game.centered_player_nr_next].jx;
11950       sy = stored_player[game.centered_player_nr_next].jy;
11951     }
11952
11953     game.centered_player_nr = game.centered_player_nr_next;
11954     game.set_centered_player = FALSE;
11955
11956     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11957     DrawGameDoorValues();
11958   }
11959
11960   for (i = 0; i < MAX_PLAYERS; i++)
11961   {
11962     int actual_player_action = stored_player[i].effective_action;
11963
11964 #if 1
11965     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11966        - rnd_equinox_tetrachloride 048
11967        - rnd_equinox_tetrachloride_ii 096
11968        - rnd_emanuel_schmieg 002
11969        - doctor_sloan_ww 001, 020
11970     */
11971     if (stored_player[i].MovPos == 0)
11972       CheckGravityMovement(&stored_player[i]);
11973 #endif
11974
11975     // overwrite programmed action with tape action
11976     if (stored_player[i].programmed_action)
11977       actual_player_action = stored_player[i].programmed_action;
11978
11979     PlayerActions(&stored_player[i], actual_player_action);
11980
11981     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11982   }
11983
11984   ScrollScreen(NULL, SCROLL_GO_ON);
11985
11986   /* for backwards compatibility, the following code emulates a fixed bug that
11987      occured when pushing elements (causing elements that just made their last
11988      pushing step to already (if possible) make their first falling step in the
11989      same game frame, which is bad); this code is also needed to use the famous
11990      "spring push bug" which is used in older levels and might be wanted to be
11991      used also in newer levels, but in this case the buggy pushing code is only
11992      affecting the "spring" element and no other elements */
11993
11994   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11995   {
11996     for (i = 0; i < MAX_PLAYERS; i++)
11997     {
11998       struct PlayerInfo *player = &stored_player[i];
11999       int x = player->jx;
12000       int y = player->jy;
12001
12002       if (player->active && player->is_pushing && player->is_moving &&
12003           IS_MOVING(x, y) &&
12004           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12005            Tile[x][y] == EL_SPRING))
12006       {
12007         ContinueMoving(x, y);
12008
12009         // continue moving after pushing (this is actually a bug)
12010         if (!IS_MOVING(x, y))
12011           Stop[x][y] = FALSE;
12012       }
12013     }
12014   }
12015
12016   SCAN_PLAYFIELD(x, y)
12017   {
12018     Last[x][y] = Tile[x][y];
12019
12020     ChangeCount[x][y] = 0;
12021     ChangeEvent[x][y] = -1;
12022
12023     // this must be handled before main playfield loop
12024     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12025     {
12026       MovDelay[x][y]--;
12027       if (MovDelay[x][y] <= 0)
12028         RemoveField(x, y);
12029     }
12030
12031     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12032     {
12033       MovDelay[x][y]--;
12034       if (MovDelay[x][y] <= 0)
12035       {
12036         RemoveField(x, y);
12037         TEST_DrawLevelField(x, y);
12038
12039         TestIfElementTouchesCustomElement(x, y);        // for empty space
12040       }
12041     }
12042
12043 #if DEBUG
12044     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12045     {
12046       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12047             x, y);
12048       Debug("game:playing:GameActions_RND", "This should never happen!");
12049
12050       ChangePage[x][y] = -1;
12051     }
12052 #endif
12053
12054     Stop[x][y] = FALSE;
12055     if (WasJustMoving[x][y] > 0)
12056       WasJustMoving[x][y]--;
12057     if (WasJustFalling[x][y] > 0)
12058       WasJustFalling[x][y]--;
12059     if (CheckCollision[x][y] > 0)
12060       CheckCollision[x][y]--;
12061     if (CheckImpact[x][y] > 0)
12062       CheckImpact[x][y]--;
12063
12064     GfxFrame[x][y]++;
12065
12066     /* reset finished pushing action (not done in ContinueMoving() to allow
12067        continuous pushing animation for elements with zero push delay) */
12068     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12069     {
12070       ResetGfxAnimation(x, y);
12071       TEST_DrawLevelField(x, y);
12072     }
12073
12074 #if DEBUG
12075     if (IS_BLOCKED(x, y))
12076     {
12077       int oldx, oldy;
12078
12079       Blocked2Moving(x, y, &oldx, &oldy);
12080       if (!IS_MOVING(oldx, oldy))
12081       {
12082         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12083         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12084         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12085         Debug("game:playing:GameActions_RND", "This should never happen!");
12086       }
12087     }
12088 #endif
12089   }
12090
12091   if (mouse_action.button)
12092   {
12093     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12094
12095     x = mouse_action.lx;
12096     y = mouse_action.ly;
12097     element = Tile[x][y];
12098
12099     if (new_button)
12100     {
12101       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12102       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12103     }
12104
12105     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12106     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12107   }
12108
12109   SCAN_PLAYFIELD(x, y)
12110   {
12111     element = Tile[x][y];
12112     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12113     last_gfx_frame = GfxFrame[x][y];
12114
12115     ResetGfxFrame(x, y);
12116
12117     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12118       DrawLevelGraphicAnimation(x, y, graphic);
12119
12120     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12121         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12122       ResetRandomAnimationValue(x, y);
12123
12124     SetRandomAnimationValue(x, y);
12125
12126     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12127
12128     if (IS_INACTIVE(element))
12129     {
12130       if (IS_ANIMATED(graphic))
12131         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12132
12133       continue;
12134     }
12135
12136     // this may take place after moving, so 'element' may have changed
12137     if (IS_CHANGING(x, y) &&
12138         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12139     {
12140       int page = element_info[element].event_page_nr[CE_DELAY];
12141
12142       HandleElementChange(x, y, page);
12143
12144       element = Tile[x][y];
12145       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12146     }
12147
12148     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12149     {
12150       StartMoving(x, y);
12151
12152       element = Tile[x][y];
12153       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12154
12155       if (IS_ANIMATED(graphic) &&
12156           !IS_MOVING(x, y) &&
12157           !Stop[x][y])
12158         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12159
12160       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12161         TEST_DrawTwinkleOnField(x, y);
12162     }
12163     else if (element == EL_ACID)
12164     {
12165       if (!Stop[x][y])
12166         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12167     }
12168     else if ((element == EL_EXIT_OPEN ||
12169               element == EL_EM_EXIT_OPEN ||
12170               element == EL_SP_EXIT_OPEN ||
12171               element == EL_STEEL_EXIT_OPEN ||
12172               element == EL_EM_STEEL_EXIT_OPEN ||
12173               element == EL_SP_TERMINAL ||
12174               element == EL_SP_TERMINAL_ACTIVE ||
12175               element == EL_EXTRA_TIME ||
12176               element == EL_SHIELD_NORMAL ||
12177               element == EL_SHIELD_DEADLY) &&
12178              IS_ANIMATED(graphic))
12179       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12180     else if (IS_MOVING(x, y))
12181       ContinueMoving(x, y);
12182     else if (IS_ACTIVE_BOMB(element))
12183       CheckDynamite(x, y);
12184     else if (element == EL_AMOEBA_GROWING)
12185       AmoebaGrowing(x, y);
12186     else if (element == EL_AMOEBA_SHRINKING)
12187       AmoebaShrinking(x, y);
12188
12189 #if !USE_NEW_AMOEBA_CODE
12190     else if (IS_AMOEBALIVE(element))
12191       AmoebaReproduce(x, y);
12192 #endif
12193
12194     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12195       Life(x, y);
12196     else if (element == EL_EXIT_CLOSED)
12197       CheckExit(x, y);
12198     else if (element == EL_EM_EXIT_CLOSED)
12199       CheckExitEM(x, y);
12200     else if (element == EL_STEEL_EXIT_CLOSED)
12201       CheckExitSteel(x, y);
12202     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12203       CheckExitSteelEM(x, y);
12204     else if (element == EL_SP_EXIT_CLOSED)
12205       CheckExitSP(x, y);
12206     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12207              element == EL_EXPANDABLE_STEELWALL_GROWING)
12208       MauerWaechst(x, y);
12209     else if (element == EL_EXPANDABLE_WALL ||
12210              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12211              element == EL_EXPANDABLE_WALL_VERTICAL ||
12212              element == EL_EXPANDABLE_WALL_ANY ||
12213              element == EL_BD_EXPANDABLE_WALL)
12214       MauerAbleger(x, y);
12215     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12216              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12217              element == EL_EXPANDABLE_STEELWALL_ANY)
12218       MauerAblegerStahl(x, y);
12219     else if (element == EL_FLAMES)
12220       CheckForDragon(x, y);
12221     else if (element == EL_EXPLOSION)
12222       ; // drawing of correct explosion animation is handled separately
12223     else if (element == EL_ELEMENT_SNAPPING ||
12224              element == EL_DIAGONAL_SHRINKING ||
12225              element == EL_DIAGONAL_GROWING)
12226     {
12227       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12228
12229       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12230     }
12231     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12232       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12233
12234     if (IS_BELT_ACTIVE(element))
12235       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12236
12237     if (game.magic_wall_active)
12238     {
12239       int jx = local_player->jx, jy = local_player->jy;
12240
12241       // play the element sound at the position nearest to the player
12242       if ((element == EL_MAGIC_WALL_FULL ||
12243            element == EL_MAGIC_WALL_ACTIVE ||
12244            element == EL_MAGIC_WALL_EMPTYING ||
12245            element == EL_BD_MAGIC_WALL_FULL ||
12246            element == EL_BD_MAGIC_WALL_ACTIVE ||
12247            element == EL_BD_MAGIC_WALL_EMPTYING ||
12248            element == EL_DC_MAGIC_WALL_FULL ||
12249            element == EL_DC_MAGIC_WALL_ACTIVE ||
12250            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12251           ABS(x - jx) + ABS(y - jy) <
12252           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12253       {
12254         magic_wall_x = x;
12255         magic_wall_y = y;
12256       }
12257     }
12258   }
12259
12260 #if USE_NEW_AMOEBA_CODE
12261   // new experimental amoeba growth stuff
12262   if (!(FrameCounter % 8))
12263   {
12264     static unsigned int random = 1684108901;
12265
12266     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12267     {
12268       x = RND(lev_fieldx);
12269       y = RND(lev_fieldy);
12270       element = Tile[x][y];
12271
12272       if (!IS_PLAYER(x,y) &&
12273           (element == EL_EMPTY ||
12274            CAN_GROW_INTO(element) ||
12275            element == EL_QUICKSAND_EMPTY ||
12276            element == EL_QUICKSAND_FAST_EMPTY ||
12277            element == EL_ACID_SPLASH_LEFT ||
12278            element == EL_ACID_SPLASH_RIGHT))
12279       {
12280         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12281             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12282             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12283             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12284           Tile[x][y] = EL_AMOEBA_DROP;
12285       }
12286
12287       random = random * 129 + 1;
12288     }
12289   }
12290 #endif
12291
12292   game.explosions_delayed = FALSE;
12293
12294   SCAN_PLAYFIELD(x, y)
12295   {
12296     element = Tile[x][y];
12297
12298     if (ExplodeField[x][y])
12299       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12300     else if (element == EL_EXPLOSION)
12301       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12302
12303     ExplodeField[x][y] = EX_TYPE_NONE;
12304   }
12305
12306   game.explosions_delayed = TRUE;
12307
12308   if (game.magic_wall_active)
12309   {
12310     if (!(game.magic_wall_time_left % 4))
12311     {
12312       int element = Tile[magic_wall_x][magic_wall_y];
12313
12314       if (element == EL_BD_MAGIC_WALL_FULL ||
12315           element == EL_BD_MAGIC_WALL_ACTIVE ||
12316           element == EL_BD_MAGIC_WALL_EMPTYING)
12317         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12318       else if (element == EL_DC_MAGIC_WALL_FULL ||
12319                element == EL_DC_MAGIC_WALL_ACTIVE ||
12320                element == EL_DC_MAGIC_WALL_EMPTYING)
12321         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12322       else
12323         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12324     }
12325
12326     if (game.magic_wall_time_left > 0)
12327     {
12328       game.magic_wall_time_left--;
12329
12330       if (!game.magic_wall_time_left)
12331       {
12332         SCAN_PLAYFIELD(x, y)
12333         {
12334           element = Tile[x][y];
12335
12336           if (element == EL_MAGIC_WALL_ACTIVE ||
12337               element == EL_MAGIC_WALL_FULL)
12338           {
12339             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12340             TEST_DrawLevelField(x, y);
12341           }
12342           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12343                    element == EL_BD_MAGIC_WALL_FULL)
12344           {
12345             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12346             TEST_DrawLevelField(x, y);
12347           }
12348           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12349                    element == EL_DC_MAGIC_WALL_FULL)
12350           {
12351             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12352             TEST_DrawLevelField(x, y);
12353           }
12354         }
12355
12356         game.magic_wall_active = FALSE;
12357       }
12358     }
12359   }
12360
12361   if (game.light_time_left > 0)
12362   {
12363     game.light_time_left--;
12364
12365     if (game.light_time_left == 0)
12366       RedrawAllLightSwitchesAndInvisibleElements();
12367   }
12368
12369   if (game.timegate_time_left > 0)
12370   {
12371     game.timegate_time_left--;
12372
12373     if (game.timegate_time_left == 0)
12374       CloseAllOpenTimegates();
12375   }
12376
12377   if (game.lenses_time_left > 0)
12378   {
12379     game.lenses_time_left--;
12380
12381     if (game.lenses_time_left == 0)
12382       RedrawAllInvisibleElementsForLenses();
12383   }
12384
12385   if (game.magnify_time_left > 0)
12386   {
12387     game.magnify_time_left--;
12388
12389     if (game.magnify_time_left == 0)
12390       RedrawAllInvisibleElementsForMagnifier();
12391   }
12392
12393   for (i = 0; i < MAX_PLAYERS; i++)
12394   {
12395     struct PlayerInfo *player = &stored_player[i];
12396
12397     if (SHIELD_ON(player))
12398     {
12399       if (player->shield_deadly_time_left)
12400         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12401       else if (player->shield_normal_time_left)
12402         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12403     }
12404   }
12405
12406 #if USE_DELAYED_GFX_REDRAW
12407   SCAN_PLAYFIELD(x, y)
12408   {
12409     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12410     {
12411       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12412          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12413
12414       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12415         DrawLevelField(x, y);
12416
12417       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12418         DrawLevelFieldCrumbled(x, y);
12419
12420       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12421         DrawLevelFieldCrumbledNeighbours(x, y);
12422
12423       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12424         DrawTwinkleOnField(x, y);
12425     }
12426
12427     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12428   }
12429 #endif
12430
12431   DrawAllPlayers();
12432   PlayAllPlayersSound();
12433
12434   for (i = 0; i < MAX_PLAYERS; i++)
12435   {
12436     struct PlayerInfo *player = &stored_player[i];
12437
12438     if (player->show_envelope != 0 && (!player->active ||
12439                                        player->MovPos == 0))
12440     {
12441       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12442
12443       player->show_envelope = 0;
12444     }
12445   }
12446
12447   // use random number generator in every frame to make it less predictable
12448   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12449     RND(1);
12450
12451   mouse_action_last = mouse_action;
12452 }
12453
12454 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12455 {
12456   int min_x = x, min_y = y, max_x = x, max_y = y;
12457   int i;
12458
12459   for (i = 0; i < MAX_PLAYERS; i++)
12460   {
12461     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12462
12463     if (!stored_player[i].active || &stored_player[i] == player)
12464       continue;
12465
12466     min_x = MIN(min_x, jx);
12467     min_y = MIN(min_y, jy);
12468     max_x = MAX(max_x, jx);
12469     max_y = MAX(max_y, jy);
12470   }
12471
12472   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12473 }
12474
12475 static boolean AllPlayersInVisibleScreen(void)
12476 {
12477   int i;
12478
12479   for (i = 0; i < MAX_PLAYERS; i++)
12480   {
12481     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12482
12483     if (!stored_player[i].active)
12484       continue;
12485
12486     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12487       return FALSE;
12488   }
12489
12490   return TRUE;
12491 }
12492
12493 void ScrollLevel(int dx, int dy)
12494 {
12495   int scroll_offset = 2 * TILEX_VAR;
12496   int x, y;
12497
12498   BlitBitmap(drawto_field, drawto_field,
12499              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12500              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12501              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12502              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12503              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12504              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12505
12506   if (dx != 0)
12507   {
12508     x = (dx == 1 ? BX1 : BX2);
12509     for (y = BY1; y <= BY2; y++)
12510       DrawScreenField(x, y);
12511   }
12512
12513   if (dy != 0)
12514   {
12515     y = (dy == 1 ? BY1 : BY2);
12516     for (x = BX1; x <= BX2; x++)
12517       DrawScreenField(x, y);
12518   }
12519
12520   redraw_mask |= REDRAW_FIELD;
12521 }
12522
12523 static boolean canFallDown(struct PlayerInfo *player)
12524 {
12525   int jx = player->jx, jy = player->jy;
12526
12527   return (IN_LEV_FIELD(jx, jy + 1) &&
12528           (IS_FREE(jx, jy + 1) ||
12529            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12530           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12531           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12532 }
12533
12534 static boolean canPassField(int x, int y, int move_dir)
12535 {
12536   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12537   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12538   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12539   int nextx = x + dx;
12540   int nexty = y + dy;
12541   int element = Tile[x][y];
12542
12543   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12544           !CAN_MOVE(element) &&
12545           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12546           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12547           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12548 }
12549
12550 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12551 {
12552   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12553   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12554   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12555   int newx = x + dx;
12556   int newy = y + dy;
12557
12558   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12559           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12560           (IS_DIGGABLE(Tile[newx][newy]) ||
12561            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12562            canPassField(newx, newy, move_dir)));
12563 }
12564
12565 static void CheckGravityMovement(struct PlayerInfo *player)
12566 {
12567   if (player->gravity && !player->programmed_action)
12568   {
12569     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12570     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12571     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12572     int jx = player->jx, jy = player->jy;
12573     boolean player_is_moving_to_valid_field =
12574       (!player_is_snapping &&
12575        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12576         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12577     boolean player_can_fall_down = canFallDown(player);
12578
12579     if (player_can_fall_down &&
12580         !player_is_moving_to_valid_field)
12581       player->programmed_action = MV_DOWN;
12582   }
12583 }
12584
12585 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12586 {
12587   return CheckGravityMovement(player);
12588
12589   if (player->gravity && !player->programmed_action)
12590   {
12591     int jx = player->jx, jy = player->jy;
12592     boolean field_under_player_is_free =
12593       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12594     boolean player_is_standing_on_valid_field =
12595       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12596        (IS_WALKABLE(Tile[jx][jy]) &&
12597         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12598
12599     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12600       player->programmed_action = MV_DOWN;
12601   }
12602 }
12603
12604 /*
12605   MovePlayerOneStep()
12606   -----------------------------------------------------------------------------
12607   dx, dy:               direction (non-diagonal) to try to move the player to
12608   real_dx, real_dy:     direction as read from input device (can be diagonal)
12609 */
12610
12611 boolean MovePlayerOneStep(struct PlayerInfo *player,
12612                           int dx, int dy, int real_dx, int real_dy)
12613 {
12614   int jx = player->jx, jy = player->jy;
12615   int new_jx = jx + dx, new_jy = jy + dy;
12616   int can_move;
12617   boolean player_can_move = !player->cannot_move;
12618
12619   if (!player->active || (!dx && !dy))
12620     return MP_NO_ACTION;
12621
12622   player->MovDir = (dx < 0 ? MV_LEFT :
12623                     dx > 0 ? MV_RIGHT :
12624                     dy < 0 ? MV_UP :
12625                     dy > 0 ? MV_DOWN :  MV_NONE);
12626
12627   if (!IN_LEV_FIELD(new_jx, new_jy))
12628     return MP_NO_ACTION;
12629
12630   if (!player_can_move)
12631   {
12632     if (player->MovPos == 0)
12633     {
12634       player->is_moving = FALSE;
12635       player->is_digging = FALSE;
12636       player->is_collecting = FALSE;
12637       player->is_snapping = FALSE;
12638       player->is_pushing = FALSE;
12639     }
12640   }
12641
12642   if (!network.enabled && game.centered_player_nr == -1 &&
12643       !AllPlayersInSight(player, new_jx, new_jy))
12644     return MP_NO_ACTION;
12645
12646   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12647   if (can_move != MP_MOVING)
12648     return can_move;
12649
12650   // check if DigField() has caused relocation of the player
12651   if (player->jx != jx || player->jy != jy)
12652     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12653
12654   StorePlayer[jx][jy] = 0;
12655   player->last_jx = jx;
12656   player->last_jy = jy;
12657   player->jx = new_jx;
12658   player->jy = new_jy;
12659   StorePlayer[new_jx][new_jy] = player->element_nr;
12660
12661   if (player->move_delay_value_next != -1)
12662   {
12663     player->move_delay_value = player->move_delay_value_next;
12664     player->move_delay_value_next = -1;
12665   }
12666
12667   player->MovPos =
12668     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12669
12670   player->step_counter++;
12671
12672   PlayerVisit[jx][jy] = FrameCounter;
12673
12674   player->is_moving = TRUE;
12675
12676 #if 1
12677   // should better be called in MovePlayer(), but this breaks some tapes
12678   ScrollPlayer(player, SCROLL_INIT);
12679 #endif
12680
12681   return MP_MOVING;
12682 }
12683
12684 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12685 {
12686   int jx = player->jx, jy = player->jy;
12687   int old_jx = jx, old_jy = jy;
12688   int moved = MP_NO_ACTION;
12689
12690   if (!player->active)
12691     return FALSE;
12692
12693   if (!dx && !dy)
12694   {
12695     if (player->MovPos == 0)
12696     {
12697       player->is_moving = FALSE;
12698       player->is_digging = FALSE;
12699       player->is_collecting = FALSE;
12700       player->is_snapping = FALSE;
12701       player->is_pushing = FALSE;
12702     }
12703
12704     return FALSE;
12705   }
12706
12707   if (player->move_delay > 0)
12708     return FALSE;
12709
12710   player->move_delay = -1;              // set to "uninitialized" value
12711
12712   // store if player is automatically moved to next field
12713   player->is_auto_moving = (player->programmed_action != MV_NONE);
12714
12715   // remove the last programmed player action
12716   player->programmed_action = 0;
12717
12718   if (player->MovPos)
12719   {
12720     // should only happen if pre-1.2 tape recordings are played
12721     // this is only for backward compatibility
12722
12723     int original_move_delay_value = player->move_delay_value;
12724
12725 #if DEBUG
12726     Debug("game:playing:MovePlayer",
12727           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12728           tape.counter);
12729 #endif
12730
12731     // scroll remaining steps with finest movement resolution
12732     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12733
12734     while (player->MovPos)
12735     {
12736       ScrollPlayer(player, SCROLL_GO_ON);
12737       ScrollScreen(NULL, SCROLL_GO_ON);
12738
12739       AdvanceFrameAndPlayerCounters(player->index_nr);
12740
12741       DrawAllPlayers();
12742       BackToFront_WithFrameDelay(0);
12743     }
12744
12745     player->move_delay_value = original_move_delay_value;
12746   }
12747
12748   player->is_active = FALSE;
12749
12750   if (player->last_move_dir & MV_HORIZONTAL)
12751   {
12752     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12753       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12754   }
12755   else
12756   {
12757     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12758       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12759   }
12760
12761   if (!moved && !player->is_active)
12762   {
12763     player->is_moving = FALSE;
12764     player->is_digging = FALSE;
12765     player->is_collecting = FALSE;
12766     player->is_snapping = FALSE;
12767     player->is_pushing = FALSE;
12768   }
12769
12770   jx = player->jx;
12771   jy = player->jy;
12772
12773   if (moved & MP_MOVING && !ScreenMovPos &&
12774       (player->index_nr == game.centered_player_nr ||
12775        game.centered_player_nr == -1))
12776   {
12777     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12778
12779     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12780     {
12781       // actual player has left the screen -- scroll in that direction
12782       if (jx != old_jx)         // player has moved horizontally
12783         scroll_x += (jx - old_jx);
12784       else                      // player has moved vertically
12785         scroll_y += (jy - old_jy);
12786     }
12787     else
12788     {
12789       int offset_raw = game.scroll_delay_value;
12790
12791       if (jx != old_jx)         // player has moved horizontally
12792       {
12793         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12794         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12795         int new_scroll_x = jx - MIDPOSX + offset_x;
12796
12797         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12798             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12799           scroll_x = new_scroll_x;
12800
12801         // don't scroll over playfield boundaries
12802         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12803
12804         // don't scroll more than one field at a time
12805         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12806
12807         // don't scroll against the player's moving direction
12808         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12809             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12810           scroll_x = old_scroll_x;
12811       }
12812       else                      // player has moved vertically
12813       {
12814         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12815         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12816         int new_scroll_y = jy - MIDPOSY + offset_y;
12817
12818         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12819             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12820           scroll_y = new_scroll_y;
12821
12822         // don't scroll over playfield boundaries
12823         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12824
12825         // don't scroll more than one field at a time
12826         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12827
12828         // don't scroll against the player's moving direction
12829         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12830             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12831           scroll_y = old_scroll_y;
12832       }
12833     }
12834
12835     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12836     {
12837       if (!network.enabled && game.centered_player_nr == -1 &&
12838           !AllPlayersInVisibleScreen())
12839       {
12840         scroll_x = old_scroll_x;
12841         scroll_y = old_scroll_y;
12842       }
12843       else
12844       {
12845         ScrollScreen(player, SCROLL_INIT);
12846         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12847       }
12848     }
12849   }
12850
12851   player->StepFrame = 0;
12852
12853   if (moved & MP_MOVING)
12854   {
12855     if (old_jx != jx && old_jy == jy)
12856       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12857     else if (old_jx == jx && old_jy != jy)
12858       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12859
12860     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12861
12862     player->last_move_dir = player->MovDir;
12863     player->is_moving = TRUE;
12864     player->is_snapping = FALSE;
12865     player->is_switching = FALSE;
12866     player->is_dropping = FALSE;
12867     player->is_dropping_pressed = FALSE;
12868     player->drop_pressed_delay = 0;
12869
12870 #if 0
12871     // should better be called here than above, but this breaks some tapes
12872     ScrollPlayer(player, SCROLL_INIT);
12873 #endif
12874   }
12875   else
12876   {
12877     CheckGravityMovementWhenNotMoving(player);
12878
12879     player->is_moving = FALSE;
12880
12881     /* at this point, the player is allowed to move, but cannot move right now
12882        (e.g. because of something blocking the way) -- ensure that the player
12883        is also allowed to move in the next frame (in old versions before 3.1.1,
12884        the player was forced to wait again for eight frames before next try) */
12885
12886     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12887       player->move_delay = 0;   // allow direct movement in the next frame
12888   }
12889
12890   if (player->move_delay == -1)         // not yet initialized by DigField()
12891     player->move_delay = player->move_delay_value;
12892
12893   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12894   {
12895     TestIfPlayerTouchesBadThing(jx, jy);
12896     TestIfPlayerTouchesCustomElement(jx, jy);
12897   }
12898
12899   if (!player->active)
12900     RemovePlayer(player);
12901
12902   return moved;
12903 }
12904
12905 void ScrollPlayer(struct PlayerInfo *player, int mode)
12906 {
12907   int jx = player->jx, jy = player->jy;
12908   int last_jx = player->last_jx, last_jy = player->last_jy;
12909   int move_stepsize = TILEX / player->move_delay_value;
12910
12911   if (!player->active)
12912     return;
12913
12914   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12915     return;
12916
12917   if (mode == SCROLL_INIT)
12918   {
12919     player->actual_frame_counter = FrameCounter;
12920     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12921
12922     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12923         Tile[last_jx][last_jy] == EL_EMPTY)
12924     {
12925       int last_field_block_delay = 0;   // start with no blocking at all
12926       int block_delay_adjustment = player->block_delay_adjustment;
12927
12928       // if player blocks last field, add delay for exactly one move
12929       if (player->block_last_field)
12930       {
12931         last_field_block_delay += player->move_delay_value;
12932
12933         // when blocking enabled, prevent moving up despite gravity
12934         if (player->gravity && player->MovDir == MV_UP)
12935           block_delay_adjustment = -1;
12936       }
12937
12938       // add block delay adjustment (also possible when not blocking)
12939       last_field_block_delay += block_delay_adjustment;
12940
12941       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12942       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12943     }
12944
12945     if (player->MovPos != 0)    // player has not yet reached destination
12946       return;
12947   }
12948   else if (!FrameReached(&player->actual_frame_counter, 1))
12949     return;
12950
12951   if (player->MovPos != 0)
12952   {
12953     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12954     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12955
12956     // before DrawPlayer() to draw correct player graphic for this case
12957     if (player->MovPos == 0)
12958       CheckGravityMovement(player);
12959   }
12960
12961   if (player->MovPos == 0)      // player reached destination field
12962   {
12963     if (player->move_delay_reset_counter > 0)
12964     {
12965       player->move_delay_reset_counter--;
12966
12967       if (player->move_delay_reset_counter == 0)
12968       {
12969         // continue with normal speed after quickly moving through gate
12970         HALVE_PLAYER_SPEED(player);
12971
12972         // be able to make the next move without delay
12973         player->move_delay = 0;
12974       }
12975     }
12976
12977     player->last_jx = jx;
12978     player->last_jy = jy;
12979
12980     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12981         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12982         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12983         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12984         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12985         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12986         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12987         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12988     {
12989       ExitPlayer(player);
12990
12991       if (game.players_still_needed == 0 &&
12992           (game.friends_still_needed == 0 ||
12993            IS_SP_ELEMENT(Tile[jx][jy])))
12994         LevelSolved();
12995     }
12996
12997     // this breaks one level: "machine", level 000
12998     {
12999       int move_direction = player->MovDir;
13000       int enter_side = MV_DIR_OPPOSITE(move_direction);
13001       int leave_side = move_direction;
13002       int old_jx = last_jx;
13003       int old_jy = last_jy;
13004       int old_element = Tile[old_jx][old_jy];
13005       int new_element = Tile[jx][jy];
13006
13007       if (IS_CUSTOM_ELEMENT(old_element))
13008         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13009                                    CE_LEFT_BY_PLAYER,
13010                                    player->index_bit, leave_side);
13011
13012       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13013                                           CE_PLAYER_LEAVES_X,
13014                                           player->index_bit, leave_side);
13015
13016       if (IS_CUSTOM_ELEMENT(new_element))
13017         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13018                                    player->index_bit, enter_side);
13019
13020       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13021                                           CE_PLAYER_ENTERS_X,
13022                                           player->index_bit, enter_side);
13023
13024       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13025                                         CE_MOVE_OF_X, move_direction);
13026     }
13027
13028     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13029     {
13030       TestIfPlayerTouchesBadThing(jx, jy);
13031       TestIfPlayerTouchesCustomElement(jx, jy);
13032
13033       /* needed because pushed element has not yet reached its destination,
13034          so it would trigger a change event at its previous field location */
13035       if (!player->is_pushing)
13036         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13037
13038       if (!player->active)
13039         RemovePlayer(player);
13040     }
13041
13042     if (!game.LevelSolved && level.use_step_counter)
13043     {
13044       int i;
13045
13046       TimePlayed++;
13047
13048       if (TimeLeft > 0)
13049       {
13050         TimeLeft--;
13051
13052         if (TimeLeft <= 10 && setup.time_limit)
13053           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13054
13055         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13056
13057         DisplayGameControlValues();
13058
13059         if (!TimeLeft && setup.time_limit)
13060           for (i = 0; i < MAX_PLAYERS; i++)
13061             KillPlayer(&stored_player[i]);
13062       }
13063       else if (game.no_time_limit && !game.all_players_gone)
13064       {
13065         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13066
13067         DisplayGameControlValues();
13068       }
13069     }
13070
13071     if (tape.single_step && tape.recording && !tape.pausing &&
13072         !player->programmed_action)
13073       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13074
13075     if (!player->programmed_action)
13076       CheckSaveEngineSnapshot(player);
13077   }
13078 }
13079
13080 void ScrollScreen(struct PlayerInfo *player, int mode)
13081 {
13082   static unsigned int screen_frame_counter = 0;
13083
13084   if (mode == SCROLL_INIT)
13085   {
13086     // set scrolling step size according to actual player's moving speed
13087     ScrollStepSize = TILEX / player->move_delay_value;
13088
13089     screen_frame_counter = FrameCounter;
13090     ScreenMovDir = player->MovDir;
13091     ScreenMovPos = player->MovPos;
13092     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13093     return;
13094   }
13095   else if (!FrameReached(&screen_frame_counter, 1))
13096     return;
13097
13098   if (ScreenMovPos)
13099   {
13100     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13101     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13102     redraw_mask |= REDRAW_FIELD;
13103   }
13104   else
13105     ScreenMovDir = MV_NONE;
13106 }
13107
13108 void TestIfPlayerTouchesCustomElement(int x, int y)
13109 {
13110   static int xy[4][2] =
13111   {
13112     { 0, -1 },
13113     { -1, 0 },
13114     { +1, 0 },
13115     { 0, +1 }
13116   };
13117   static int trigger_sides[4][2] =
13118   {
13119     // center side       border side
13120     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13121     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13122     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13123     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13124   };
13125   static int touch_dir[4] =
13126   {
13127     MV_LEFT | MV_RIGHT,
13128     MV_UP   | MV_DOWN,
13129     MV_UP   | MV_DOWN,
13130     MV_LEFT | MV_RIGHT
13131   };
13132   int center_element = Tile[x][y];      // should always be non-moving!
13133   int i;
13134
13135   for (i = 0; i < NUM_DIRECTIONS; i++)
13136   {
13137     int xx = x + xy[i][0];
13138     int yy = y + xy[i][1];
13139     int center_side = trigger_sides[i][0];
13140     int border_side = trigger_sides[i][1];
13141     int border_element;
13142
13143     if (!IN_LEV_FIELD(xx, yy))
13144       continue;
13145
13146     if (IS_PLAYER(x, y))                // player found at center element
13147     {
13148       struct PlayerInfo *player = PLAYERINFO(x, y);
13149
13150       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13151         border_element = Tile[xx][yy];          // may be moving!
13152       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13153         border_element = Tile[xx][yy];
13154       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13155         border_element = MovingOrBlocked2Element(xx, yy);
13156       else
13157         continue;               // center and border element do not touch
13158
13159       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13160                                  player->index_bit, border_side);
13161       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13162                                           CE_PLAYER_TOUCHES_X,
13163                                           player->index_bit, border_side);
13164
13165       {
13166         /* use player element that is initially defined in the level playfield,
13167            not the player element that corresponds to the runtime player number
13168            (example: a level that contains EL_PLAYER_3 as the only player would
13169            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13170         int player_element = PLAYERINFO(x, y)->initial_element;
13171
13172         CheckElementChangeBySide(xx, yy, border_element, player_element,
13173                                  CE_TOUCHING_X, border_side);
13174       }
13175     }
13176     else if (IS_PLAYER(xx, yy))         // player found at border element
13177     {
13178       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13179
13180       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13181       {
13182         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13183           continue;             // center and border element do not touch
13184       }
13185
13186       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13187                                  player->index_bit, center_side);
13188       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13189                                           CE_PLAYER_TOUCHES_X,
13190                                           player->index_bit, center_side);
13191
13192       {
13193         /* use player element that is initially defined in the level playfield,
13194            not the player element that corresponds to the runtime player number
13195            (example: a level that contains EL_PLAYER_3 as the only player would
13196            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13197         int player_element = PLAYERINFO(xx, yy)->initial_element;
13198
13199         CheckElementChangeBySide(x, y, center_element, player_element,
13200                                  CE_TOUCHING_X, center_side);
13201       }
13202
13203       break;
13204     }
13205   }
13206 }
13207
13208 void TestIfElementTouchesCustomElement(int x, int y)
13209 {
13210   static int xy[4][2] =
13211   {
13212     { 0, -1 },
13213     { -1, 0 },
13214     { +1, 0 },
13215     { 0, +1 }
13216   };
13217   static int trigger_sides[4][2] =
13218   {
13219     // center side      border side
13220     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13221     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13222     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13223     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13224   };
13225   static int touch_dir[4] =
13226   {
13227     MV_LEFT | MV_RIGHT,
13228     MV_UP   | MV_DOWN,
13229     MV_UP   | MV_DOWN,
13230     MV_LEFT | MV_RIGHT
13231   };
13232   boolean change_center_element = FALSE;
13233   int center_element = Tile[x][y];      // should always be non-moving!
13234   int border_element_old[NUM_DIRECTIONS];
13235   int i;
13236
13237   for (i = 0; i < NUM_DIRECTIONS; i++)
13238   {
13239     int xx = x + xy[i][0];
13240     int yy = y + xy[i][1];
13241     int border_element;
13242
13243     border_element_old[i] = -1;
13244
13245     if (!IN_LEV_FIELD(xx, yy))
13246       continue;
13247
13248     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13249       border_element = Tile[xx][yy];    // may be moving!
13250     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13251       border_element = Tile[xx][yy];
13252     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13253       border_element = MovingOrBlocked2Element(xx, yy);
13254     else
13255       continue;                 // center and border element do not touch
13256
13257     border_element_old[i] = border_element;
13258   }
13259
13260   for (i = 0; i < NUM_DIRECTIONS; i++)
13261   {
13262     int xx = x + xy[i][0];
13263     int yy = y + xy[i][1];
13264     int center_side = trigger_sides[i][0];
13265     int border_element = border_element_old[i];
13266
13267     if (border_element == -1)
13268       continue;
13269
13270     // check for change of border element
13271     CheckElementChangeBySide(xx, yy, border_element, center_element,
13272                              CE_TOUCHING_X, center_side);
13273
13274     // (center element cannot be player, so we dont have to check this here)
13275   }
13276
13277   for (i = 0; i < NUM_DIRECTIONS; i++)
13278   {
13279     int xx = x + xy[i][0];
13280     int yy = y + xy[i][1];
13281     int border_side = trigger_sides[i][1];
13282     int border_element = border_element_old[i];
13283
13284     if (border_element == -1)
13285       continue;
13286
13287     // check for change of center element (but change it only once)
13288     if (!change_center_element)
13289       change_center_element =
13290         CheckElementChangeBySide(x, y, center_element, border_element,
13291                                  CE_TOUCHING_X, border_side);
13292
13293     if (IS_PLAYER(xx, yy))
13294     {
13295       /* use player element that is initially defined in the level playfield,
13296          not the player element that corresponds to the runtime player number
13297          (example: a level that contains EL_PLAYER_3 as the only player would
13298          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13299       int player_element = PLAYERINFO(xx, yy)->initial_element;
13300
13301       CheckElementChangeBySide(x, y, center_element, player_element,
13302                                CE_TOUCHING_X, border_side);
13303     }
13304   }
13305 }
13306
13307 void TestIfElementHitsCustomElement(int x, int y, int direction)
13308 {
13309   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13310   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13311   int hitx = x + dx, hity = y + dy;
13312   int hitting_element = Tile[x][y];
13313   int touched_element;
13314
13315   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13316     return;
13317
13318   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13319                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13320
13321   if (IN_LEV_FIELD(hitx, hity))
13322   {
13323     int opposite_direction = MV_DIR_OPPOSITE(direction);
13324     int hitting_side = direction;
13325     int touched_side = opposite_direction;
13326     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13327                           MovDir[hitx][hity] != direction ||
13328                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13329
13330     object_hit = TRUE;
13331
13332     if (object_hit)
13333     {
13334       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13335                                CE_HITTING_X, touched_side);
13336
13337       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13338                                CE_HIT_BY_X, hitting_side);
13339
13340       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13341                                CE_HIT_BY_SOMETHING, opposite_direction);
13342
13343       if (IS_PLAYER(hitx, hity))
13344       {
13345         /* use player element that is initially defined in the level playfield,
13346            not the player element that corresponds to the runtime player number
13347            (example: a level that contains EL_PLAYER_3 as the only player would
13348            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13349         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13350
13351         CheckElementChangeBySide(x, y, hitting_element, player_element,
13352                                  CE_HITTING_X, touched_side);
13353       }
13354     }
13355   }
13356
13357   // "hitting something" is also true when hitting the playfield border
13358   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13359                            CE_HITTING_SOMETHING, direction);
13360 }
13361
13362 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13363 {
13364   int i, kill_x = -1, kill_y = -1;
13365
13366   int bad_element = -1;
13367   static int test_xy[4][2] =
13368   {
13369     { 0, -1 },
13370     { -1, 0 },
13371     { +1, 0 },
13372     { 0, +1 }
13373   };
13374   static int test_dir[4] =
13375   {
13376     MV_UP,
13377     MV_LEFT,
13378     MV_RIGHT,
13379     MV_DOWN
13380   };
13381
13382   for (i = 0; i < NUM_DIRECTIONS; i++)
13383   {
13384     int test_x, test_y, test_move_dir, test_element;
13385
13386     test_x = good_x + test_xy[i][0];
13387     test_y = good_y + test_xy[i][1];
13388
13389     if (!IN_LEV_FIELD(test_x, test_y))
13390       continue;
13391
13392     test_move_dir =
13393       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13394
13395     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13396
13397     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13398        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13399     */
13400     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13401         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13402     {
13403       kill_x = test_x;
13404       kill_y = test_y;
13405       bad_element = test_element;
13406
13407       break;
13408     }
13409   }
13410
13411   if (kill_x != -1 || kill_y != -1)
13412   {
13413     if (IS_PLAYER(good_x, good_y))
13414     {
13415       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13416
13417       if (player->shield_deadly_time_left > 0 &&
13418           !IS_INDESTRUCTIBLE(bad_element))
13419         Bang(kill_x, kill_y);
13420       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13421         KillPlayer(player);
13422     }
13423     else
13424       Bang(good_x, good_y);
13425   }
13426 }
13427
13428 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13429 {
13430   int i, kill_x = -1, kill_y = -1;
13431   int bad_element = Tile[bad_x][bad_y];
13432   static int test_xy[4][2] =
13433   {
13434     { 0, -1 },
13435     { -1, 0 },
13436     { +1, 0 },
13437     { 0, +1 }
13438   };
13439   static int touch_dir[4] =
13440   {
13441     MV_LEFT | MV_RIGHT,
13442     MV_UP   | MV_DOWN,
13443     MV_UP   | MV_DOWN,
13444     MV_LEFT | MV_RIGHT
13445   };
13446   static int test_dir[4] =
13447   {
13448     MV_UP,
13449     MV_LEFT,
13450     MV_RIGHT,
13451     MV_DOWN
13452   };
13453
13454   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13455     return;
13456
13457   for (i = 0; i < NUM_DIRECTIONS; i++)
13458   {
13459     int test_x, test_y, test_move_dir, test_element;
13460
13461     test_x = bad_x + test_xy[i][0];
13462     test_y = bad_y + test_xy[i][1];
13463
13464     if (!IN_LEV_FIELD(test_x, test_y))
13465       continue;
13466
13467     test_move_dir =
13468       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13469
13470     test_element = Tile[test_x][test_y];
13471
13472     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13473        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13474     */
13475     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13476         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13477     {
13478       // good thing is player or penguin that does not move away
13479       if (IS_PLAYER(test_x, test_y))
13480       {
13481         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13482
13483         if (bad_element == EL_ROBOT && player->is_moving)
13484           continue;     // robot does not kill player if he is moving
13485
13486         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13487         {
13488           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13489             continue;           // center and border element do not touch
13490         }
13491
13492         kill_x = test_x;
13493         kill_y = test_y;
13494
13495         break;
13496       }
13497       else if (test_element == EL_PENGUIN)
13498       {
13499         kill_x = test_x;
13500         kill_y = test_y;
13501
13502         break;
13503       }
13504     }
13505   }
13506
13507   if (kill_x != -1 || kill_y != -1)
13508   {
13509     if (IS_PLAYER(kill_x, kill_y))
13510     {
13511       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13512
13513       if (player->shield_deadly_time_left > 0 &&
13514           !IS_INDESTRUCTIBLE(bad_element))
13515         Bang(bad_x, bad_y);
13516       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13517         KillPlayer(player);
13518     }
13519     else
13520       Bang(kill_x, kill_y);
13521   }
13522 }
13523
13524 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13525 {
13526   int bad_element = Tile[bad_x][bad_y];
13527   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13528   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13529   int test_x = bad_x + dx, test_y = bad_y + dy;
13530   int test_move_dir, test_element;
13531   int kill_x = -1, kill_y = -1;
13532
13533   if (!IN_LEV_FIELD(test_x, test_y))
13534     return;
13535
13536   test_move_dir =
13537     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13538
13539   test_element = Tile[test_x][test_y];
13540
13541   if (test_move_dir != bad_move_dir)
13542   {
13543     // good thing can be player or penguin that does not move away
13544     if (IS_PLAYER(test_x, test_y))
13545     {
13546       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13547
13548       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13549          player as being hit when he is moving towards the bad thing, because
13550          the "get hit by" condition would be lost after the player stops) */
13551       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13552         return;         // player moves away from bad thing
13553
13554       kill_x = test_x;
13555       kill_y = test_y;
13556     }
13557     else if (test_element == EL_PENGUIN)
13558     {
13559       kill_x = test_x;
13560       kill_y = test_y;
13561     }
13562   }
13563
13564   if (kill_x != -1 || kill_y != -1)
13565   {
13566     if (IS_PLAYER(kill_x, kill_y))
13567     {
13568       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13569
13570       if (player->shield_deadly_time_left > 0 &&
13571           !IS_INDESTRUCTIBLE(bad_element))
13572         Bang(bad_x, bad_y);
13573       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13574         KillPlayer(player);
13575     }
13576     else
13577       Bang(kill_x, kill_y);
13578   }
13579 }
13580
13581 void TestIfPlayerTouchesBadThing(int x, int y)
13582 {
13583   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13584 }
13585
13586 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13587 {
13588   TestIfGoodThingHitsBadThing(x, y, move_dir);
13589 }
13590
13591 void TestIfBadThingTouchesPlayer(int x, int y)
13592 {
13593   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13594 }
13595
13596 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13597 {
13598   TestIfBadThingHitsGoodThing(x, y, move_dir);
13599 }
13600
13601 void TestIfFriendTouchesBadThing(int x, int y)
13602 {
13603   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13604 }
13605
13606 void TestIfBadThingTouchesFriend(int x, int y)
13607 {
13608   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13609 }
13610
13611 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13612 {
13613   int i, kill_x = bad_x, kill_y = bad_y;
13614   static int xy[4][2] =
13615   {
13616     { 0, -1 },
13617     { -1, 0 },
13618     { +1, 0 },
13619     { 0, +1 }
13620   };
13621
13622   for (i = 0; i < NUM_DIRECTIONS; i++)
13623   {
13624     int x, y, element;
13625
13626     x = bad_x + xy[i][0];
13627     y = bad_y + xy[i][1];
13628     if (!IN_LEV_FIELD(x, y))
13629       continue;
13630
13631     element = Tile[x][y];
13632     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13633         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13634     {
13635       kill_x = x;
13636       kill_y = y;
13637       break;
13638     }
13639   }
13640
13641   if (kill_x != bad_x || kill_y != bad_y)
13642     Bang(bad_x, bad_y);
13643 }
13644
13645 void KillPlayer(struct PlayerInfo *player)
13646 {
13647   int jx = player->jx, jy = player->jy;
13648
13649   if (!player->active)
13650     return;
13651
13652 #if 0
13653   Debug("game:playing:KillPlayer",
13654         "0: killed == %d, active == %d, reanimated == %d",
13655         player->killed, player->active, player->reanimated);
13656 #endif
13657
13658   /* the following code was introduced to prevent an infinite loop when calling
13659      -> Bang()
13660      -> CheckTriggeredElementChangeExt()
13661      -> ExecuteCustomElementAction()
13662      -> KillPlayer()
13663      -> (infinitely repeating the above sequence of function calls)
13664      which occurs when killing the player while having a CE with the setting
13665      "kill player X when explosion of <player X>"; the solution using a new
13666      field "player->killed" was chosen for backwards compatibility, although
13667      clever use of the fields "player->active" etc. would probably also work */
13668 #if 1
13669   if (player->killed)
13670     return;
13671 #endif
13672
13673   player->killed = TRUE;
13674
13675   // remove accessible field at the player's position
13676   Tile[jx][jy] = EL_EMPTY;
13677
13678   // deactivate shield (else Bang()/Explode() would not work right)
13679   player->shield_normal_time_left = 0;
13680   player->shield_deadly_time_left = 0;
13681
13682 #if 0
13683   Debug("game:playing:KillPlayer",
13684         "1: killed == %d, active == %d, reanimated == %d",
13685         player->killed, player->active, player->reanimated);
13686 #endif
13687
13688   Bang(jx, jy);
13689
13690 #if 0
13691   Debug("game:playing:KillPlayer",
13692         "2: killed == %d, active == %d, reanimated == %d",
13693         player->killed, player->active, player->reanimated);
13694 #endif
13695
13696   if (player->reanimated)       // killed player may have been reanimated
13697     player->killed = player->reanimated = FALSE;
13698   else
13699     BuryPlayer(player);
13700 }
13701
13702 static void KillPlayerUnlessEnemyProtected(int x, int y)
13703 {
13704   if (!PLAYER_ENEMY_PROTECTED(x, y))
13705     KillPlayer(PLAYERINFO(x, y));
13706 }
13707
13708 static void KillPlayerUnlessExplosionProtected(int x, int y)
13709 {
13710   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13711     KillPlayer(PLAYERINFO(x, y));
13712 }
13713
13714 void BuryPlayer(struct PlayerInfo *player)
13715 {
13716   int jx = player->jx, jy = player->jy;
13717
13718   if (!player->active)
13719     return;
13720
13721   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13722   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13723
13724   RemovePlayer(player);
13725
13726   player->buried = TRUE;
13727
13728   if (game.all_players_gone)
13729     game.GameOver = TRUE;
13730 }
13731
13732 void RemovePlayer(struct PlayerInfo *player)
13733 {
13734   int jx = player->jx, jy = player->jy;
13735   int i, found = FALSE;
13736
13737   player->present = FALSE;
13738   player->active = FALSE;
13739
13740   // required for some CE actions (even if the player is not active anymore)
13741   player->MovPos = 0;
13742
13743   if (!ExplodeField[jx][jy])
13744     StorePlayer[jx][jy] = 0;
13745
13746   if (player->is_moving)
13747     TEST_DrawLevelField(player->last_jx, player->last_jy);
13748
13749   for (i = 0; i < MAX_PLAYERS; i++)
13750     if (stored_player[i].active)
13751       found = TRUE;
13752
13753   if (!found)
13754   {
13755     game.all_players_gone = TRUE;
13756     game.GameOver = TRUE;
13757   }
13758
13759   game.exit_x = game.robot_wheel_x = jx;
13760   game.exit_y = game.robot_wheel_y = jy;
13761 }
13762
13763 void ExitPlayer(struct PlayerInfo *player)
13764 {
13765   DrawPlayer(player);   // needed here only to cleanup last field
13766   RemovePlayer(player);
13767
13768   if (game.players_still_needed > 0)
13769     game.players_still_needed--;
13770 }
13771
13772 static void setFieldForSnapping(int x, int y, int element, int direction)
13773 {
13774   struct ElementInfo *ei = &element_info[element];
13775   int direction_bit = MV_DIR_TO_BIT(direction);
13776   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13777   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13778                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13779
13780   Tile[x][y] = EL_ELEMENT_SNAPPING;
13781   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13782
13783   ResetGfxAnimation(x, y);
13784
13785   GfxElement[x][y] = element;
13786   GfxAction[x][y] = action;
13787   GfxDir[x][y] = direction;
13788   GfxFrame[x][y] = -1;
13789 }
13790
13791 /*
13792   =============================================================================
13793   checkDiagonalPushing()
13794   -----------------------------------------------------------------------------
13795   check if diagonal input device direction results in pushing of object
13796   (by checking if the alternative direction is walkable, diggable, ...)
13797   =============================================================================
13798 */
13799
13800 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13801                                     int x, int y, int real_dx, int real_dy)
13802 {
13803   int jx, jy, dx, dy, xx, yy;
13804
13805   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13806     return TRUE;
13807
13808   // diagonal direction: check alternative direction
13809   jx = player->jx;
13810   jy = player->jy;
13811   dx = x - jx;
13812   dy = y - jy;
13813   xx = jx + (dx == 0 ? real_dx : 0);
13814   yy = jy + (dy == 0 ? real_dy : 0);
13815
13816   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13817 }
13818
13819 /*
13820   =============================================================================
13821   DigField()
13822   -----------------------------------------------------------------------------
13823   x, y:                 field next to player (non-diagonal) to try to dig to
13824   real_dx, real_dy:     direction as read from input device (can be diagonal)
13825   =============================================================================
13826 */
13827
13828 static int DigField(struct PlayerInfo *player,
13829                     int oldx, int oldy, int x, int y,
13830                     int real_dx, int real_dy, int mode)
13831 {
13832   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13833   boolean player_was_pushing = player->is_pushing;
13834   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13835   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13836   int jx = oldx, jy = oldy;
13837   int dx = x - jx, dy = y - jy;
13838   int nextx = x + dx, nexty = y + dy;
13839   int move_direction = (dx == -1 ? MV_LEFT  :
13840                         dx == +1 ? MV_RIGHT :
13841                         dy == -1 ? MV_UP    :
13842                         dy == +1 ? MV_DOWN  : MV_NONE);
13843   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13844   int dig_side = MV_DIR_OPPOSITE(move_direction);
13845   int old_element = Tile[jx][jy];
13846   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13847   int collect_count;
13848
13849   if (is_player)                // function can also be called by EL_PENGUIN
13850   {
13851     if (player->MovPos == 0)
13852     {
13853       player->is_digging = FALSE;
13854       player->is_collecting = FALSE;
13855     }
13856
13857     if (player->MovPos == 0)    // last pushing move finished
13858       player->is_pushing = FALSE;
13859
13860     if (mode == DF_NO_PUSH)     // player just stopped pushing
13861     {
13862       player->is_switching = FALSE;
13863       player->push_delay = -1;
13864
13865       return MP_NO_ACTION;
13866     }
13867   }
13868
13869   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13870     old_element = Back[jx][jy];
13871
13872   // in case of element dropped at player position, check background
13873   else if (Back[jx][jy] != EL_EMPTY &&
13874            game.engine_version >= VERSION_IDENT(2,2,0,0))
13875     old_element = Back[jx][jy];
13876
13877   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13878     return MP_NO_ACTION;        // field has no opening in this direction
13879
13880   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13881     return MP_NO_ACTION;        // field has no opening in this direction
13882
13883   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13884   {
13885     SplashAcid(x, y);
13886
13887     Tile[jx][jy] = player->artwork_element;
13888     InitMovingField(jx, jy, MV_DOWN);
13889     Store[jx][jy] = EL_ACID;
13890     ContinueMoving(jx, jy);
13891     BuryPlayer(player);
13892
13893     return MP_DONT_RUN_INTO;
13894   }
13895
13896   if (player_can_move && DONT_RUN_INTO(element))
13897   {
13898     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13899
13900     return MP_DONT_RUN_INTO;
13901   }
13902
13903   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13904     return MP_NO_ACTION;
13905
13906   collect_count = element_info[element].collect_count_initial;
13907
13908   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13909     return MP_NO_ACTION;
13910
13911   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13912     player_can_move = player_can_move_or_snap;
13913
13914   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13915       game.engine_version >= VERSION_IDENT(2,2,0,0))
13916   {
13917     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13918                                player->index_bit, dig_side);
13919     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13920                                         player->index_bit, dig_side);
13921
13922     if (element == EL_DC_LANDMINE)
13923       Bang(x, y);
13924
13925     if (Tile[x][y] != element)          // field changed by snapping
13926       return MP_ACTION;
13927
13928     return MP_NO_ACTION;
13929   }
13930
13931   if (player->gravity && is_player && !player->is_auto_moving &&
13932       canFallDown(player) && move_direction != MV_DOWN &&
13933       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13934     return MP_NO_ACTION;        // player cannot walk here due to gravity
13935
13936   if (player_can_move &&
13937       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13938   {
13939     int sound_element = SND_ELEMENT(element);
13940     int sound_action = ACTION_WALKING;
13941
13942     if (IS_RND_GATE(element))
13943     {
13944       if (!player->key[RND_GATE_NR(element)])
13945         return MP_NO_ACTION;
13946     }
13947     else if (IS_RND_GATE_GRAY(element))
13948     {
13949       if (!player->key[RND_GATE_GRAY_NR(element)])
13950         return MP_NO_ACTION;
13951     }
13952     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13953     {
13954       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13955         return MP_NO_ACTION;
13956     }
13957     else if (element == EL_EXIT_OPEN ||
13958              element == EL_EM_EXIT_OPEN ||
13959              element == EL_EM_EXIT_OPENING ||
13960              element == EL_STEEL_EXIT_OPEN ||
13961              element == EL_EM_STEEL_EXIT_OPEN ||
13962              element == EL_EM_STEEL_EXIT_OPENING ||
13963              element == EL_SP_EXIT_OPEN ||
13964              element == EL_SP_EXIT_OPENING)
13965     {
13966       sound_action = ACTION_PASSING;    // player is passing exit
13967     }
13968     else if (element == EL_EMPTY)
13969     {
13970       sound_action = ACTION_MOVING;             // nothing to walk on
13971     }
13972
13973     // play sound from background or player, whatever is available
13974     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13975       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13976     else
13977       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13978   }
13979   else if (player_can_move &&
13980            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13981   {
13982     if (!ACCESS_FROM(element, opposite_direction))
13983       return MP_NO_ACTION;      // field not accessible from this direction
13984
13985     if (CAN_MOVE(element))      // only fixed elements can be passed!
13986       return MP_NO_ACTION;
13987
13988     if (IS_EM_GATE(element))
13989     {
13990       if (!player->key[EM_GATE_NR(element)])
13991         return MP_NO_ACTION;
13992     }
13993     else if (IS_EM_GATE_GRAY(element))
13994     {
13995       if (!player->key[EM_GATE_GRAY_NR(element)])
13996         return MP_NO_ACTION;
13997     }
13998     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13999     {
14000       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14001         return MP_NO_ACTION;
14002     }
14003     else if (IS_EMC_GATE(element))
14004     {
14005       if (!player->key[EMC_GATE_NR(element)])
14006         return MP_NO_ACTION;
14007     }
14008     else if (IS_EMC_GATE_GRAY(element))
14009     {
14010       if (!player->key[EMC_GATE_GRAY_NR(element)])
14011         return MP_NO_ACTION;
14012     }
14013     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14014     {
14015       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14016         return MP_NO_ACTION;
14017     }
14018     else if (element == EL_DC_GATE_WHITE ||
14019              element == EL_DC_GATE_WHITE_GRAY ||
14020              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14021     {
14022       if (player->num_white_keys == 0)
14023         return MP_NO_ACTION;
14024
14025       player->num_white_keys--;
14026     }
14027     else if (IS_SP_PORT(element))
14028     {
14029       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14030           element == EL_SP_GRAVITY_PORT_RIGHT ||
14031           element == EL_SP_GRAVITY_PORT_UP ||
14032           element == EL_SP_GRAVITY_PORT_DOWN)
14033         player->gravity = !player->gravity;
14034       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14035                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14036                element == EL_SP_GRAVITY_ON_PORT_UP ||
14037                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14038         player->gravity = TRUE;
14039       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14040                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14041                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14042                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14043         player->gravity = FALSE;
14044     }
14045
14046     // automatically move to the next field with double speed
14047     player->programmed_action = move_direction;
14048
14049     if (player->move_delay_reset_counter == 0)
14050     {
14051       player->move_delay_reset_counter = 2;     // two double speed steps
14052
14053       DOUBLE_PLAYER_SPEED(player);
14054     }
14055
14056     PlayLevelSoundAction(x, y, ACTION_PASSING);
14057   }
14058   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14059   {
14060     RemoveField(x, y);
14061
14062     if (mode != DF_SNAP)
14063     {
14064       GfxElement[x][y] = GFX_ELEMENT(element);
14065       player->is_digging = TRUE;
14066     }
14067
14068     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14069
14070     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14071                                         player->index_bit, dig_side);
14072
14073     // if digging triggered player relocation, finish digging tile
14074     if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14075       setFieldForSnapping(x, y, element, move_direction);
14076
14077     if (mode == DF_SNAP)
14078     {
14079       if (level.block_snap_field)
14080         setFieldForSnapping(x, y, element, move_direction);
14081       else
14082         TestIfElementTouchesCustomElement(x, y);        // for empty space
14083
14084       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14085                                           player->index_bit, dig_side);
14086     }
14087   }
14088   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14089   {
14090     RemoveField(x, y);
14091
14092     if (is_player && mode != DF_SNAP)
14093     {
14094       GfxElement[x][y] = element;
14095       player->is_collecting = TRUE;
14096     }
14097
14098     if (element == EL_SPEED_PILL)
14099     {
14100       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14101     }
14102     else if (element == EL_EXTRA_TIME && level.time > 0)
14103     {
14104       TimeLeft += level.extra_time;
14105
14106       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14107
14108       DisplayGameControlValues();
14109     }
14110     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14111     {
14112       player->shield_normal_time_left += level.shield_normal_time;
14113       if (element == EL_SHIELD_DEADLY)
14114         player->shield_deadly_time_left += level.shield_deadly_time;
14115     }
14116     else if (element == EL_DYNAMITE ||
14117              element == EL_EM_DYNAMITE ||
14118              element == EL_SP_DISK_RED)
14119     {
14120       if (player->inventory_size < MAX_INVENTORY_SIZE)
14121         player->inventory_element[player->inventory_size++] = element;
14122
14123       DrawGameDoorValues();
14124     }
14125     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14126     {
14127       player->dynabomb_count++;
14128       player->dynabombs_left++;
14129     }
14130     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14131     {
14132       player->dynabomb_size++;
14133     }
14134     else if (element == EL_DYNABOMB_INCREASE_POWER)
14135     {
14136       player->dynabomb_xl = TRUE;
14137     }
14138     else if (IS_KEY(element))
14139     {
14140       player->key[KEY_NR(element)] = TRUE;
14141
14142       DrawGameDoorValues();
14143     }
14144     else if (element == EL_DC_KEY_WHITE)
14145     {
14146       player->num_white_keys++;
14147
14148       // display white keys?
14149       // DrawGameDoorValues();
14150     }
14151     else if (IS_ENVELOPE(element))
14152     {
14153       player->show_envelope = element;
14154     }
14155     else if (element == EL_EMC_LENSES)
14156     {
14157       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14158
14159       RedrawAllInvisibleElementsForLenses();
14160     }
14161     else if (element == EL_EMC_MAGNIFIER)
14162     {
14163       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14164
14165       RedrawAllInvisibleElementsForMagnifier();
14166     }
14167     else if (IS_DROPPABLE(element) ||
14168              IS_THROWABLE(element))     // can be collected and dropped
14169     {
14170       int i;
14171
14172       if (collect_count == 0)
14173         player->inventory_infinite_element = element;
14174       else
14175         for (i = 0; i < collect_count; i++)
14176           if (player->inventory_size < MAX_INVENTORY_SIZE)
14177             player->inventory_element[player->inventory_size++] = element;
14178
14179       DrawGameDoorValues();
14180     }
14181     else if (collect_count > 0)
14182     {
14183       game.gems_still_needed -= collect_count;
14184       if (game.gems_still_needed < 0)
14185         game.gems_still_needed = 0;
14186
14187       game.snapshot.collected_item = TRUE;
14188
14189       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14190
14191       DisplayGameControlValues();
14192     }
14193
14194     RaiseScoreElement(element);
14195     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14196
14197     if (is_player)
14198     {
14199       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14200                                           player->index_bit, dig_side);
14201
14202       // if collecting triggered player relocation, finish collecting tile
14203       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14204         setFieldForSnapping(x, y, element, move_direction);
14205     }
14206
14207     if (mode == DF_SNAP)
14208     {
14209       if (level.block_snap_field)
14210         setFieldForSnapping(x, y, element, move_direction);
14211       else
14212         TestIfElementTouchesCustomElement(x, y);        // for empty space
14213
14214       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14215                                           player->index_bit, dig_side);
14216     }
14217   }
14218   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14219   {
14220     if (mode == DF_SNAP && element != EL_BD_ROCK)
14221       return MP_NO_ACTION;
14222
14223     if (CAN_FALL(element) && dy)
14224       return MP_NO_ACTION;
14225
14226     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14227         !(element == EL_SPRING && level.use_spring_bug))
14228       return MP_NO_ACTION;
14229
14230     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14231         ((move_direction & MV_VERTICAL &&
14232           ((element_info[element].move_pattern & MV_LEFT &&
14233             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14234            (element_info[element].move_pattern & MV_RIGHT &&
14235             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14236          (move_direction & MV_HORIZONTAL &&
14237           ((element_info[element].move_pattern & MV_UP &&
14238             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14239            (element_info[element].move_pattern & MV_DOWN &&
14240             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14241       return MP_NO_ACTION;
14242
14243     // do not push elements already moving away faster than player
14244     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14245         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14246       return MP_NO_ACTION;
14247
14248     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14249     {
14250       if (player->push_delay_value == -1 || !player_was_pushing)
14251         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14252     }
14253     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14254     {
14255       if (player->push_delay_value == -1)
14256         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14257     }
14258     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14259     {
14260       if (!player->is_pushing)
14261         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14262     }
14263
14264     player->is_pushing = TRUE;
14265     player->is_active = TRUE;
14266
14267     if (!(IN_LEV_FIELD(nextx, nexty) &&
14268           (IS_FREE(nextx, nexty) ||
14269            (IS_SB_ELEMENT(element) &&
14270             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14271            (IS_CUSTOM_ELEMENT(element) &&
14272             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14273       return MP_NO_ACTION;
14274
14275     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14276       return MP_NO_ACTION;
14277
14278     if (player->push_delay == -1)       // new pushing; restart delay
14279       player->push_delay = 0;
14280
14281     if (player->push_delay < player->push_delay_value &&
14282         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14283         element != EL_SPRING && element != EL_BALLOON)
14284     {
14285       // make sure that there is no move delay before next try to push
14286       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14287         player->move_delay = 0;
14288
14289       return MP_NO_ACTION;
14290     }
14291
14292     if (IS_CUSTOM_ELEMENT(element) &&
14293         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14294     {
14295       if (!DigFieldByCE(nextx, nexty, element))
14296         return MP_NO_ACTION;
14297     }
14298
14299     if (IS_SB_ELEMENT(element))
14300     {
14301       boolean sokoban_task_solved = FALSE;
14302
14303       if (element == EL_SOKOBAN_FIELD_FULL)
14304       {
14305         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14306
14307         IncrementSokobanFieldsNeeded();
14308         IncrementSokobanObjectsNeeded();
14309       }
14310
14311       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14312       {
14313         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14314
14315         DecrementSokobanFieldsNeeded();
14316         DecrementSokobanObjectsNeeded();
14317
14318         // sokoban object was pushed from empty field to sokoban field
14319         if (Back[x][y] == EL_EMPTY)
14320           sokoban_task_solved = TRUE;
14321       }
14322
14323       Tile[x][y] = EL_SOKOBAN_OBJECT;
14324
14325       if (Back[x][y] == Back[nextx][nexty])
14326         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14327       else if (Back[x][y] != 0)
14328         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14329                                     ACTION_EMPTYING);
14330       else
14331         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14332                                     ACTION_FILLING);
14333
14334       if (sokoban_task_solved &&
14335           game.sokoban_fields_still_needed == 0 &&
14336           game.sokoban_objects_still_needed == 0 &&
14337           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14338       {
14339         game.players_still_needed = 0;
14340
14341         LevelSolved();
14342
14343         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14344       }
14345     }
14346     else
14347       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14348
14349     InitMovingField(x, y, move_direction);
14350     GfxAction[x][y] = ACTION_PUSHING;
14351
14352     if (mode == DF_SNAP)
14353       ContinueMoving(x, y);
14354     else
14355       MovPos[x][y] = (dx != 0 ? dx : dy);
14356
14357     Pushed[x][y] = TRUE;
14358     Pushed[nextx][nexty] = TRUE;
14359
14360     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14361       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14362     else
14363       player->push_delay_value = -1;    // get new value later
14364
14365     // check for element change _after_ element has been pushed
14366     if (game.use_change_when_pushing_bug)
14367     {
14368       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14369                                  player->index_bit, dig_side);
14370       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14371                                           player->index_bit, dig_side);
14372     }
14373   }
14374   else if (IS_SWITCHABLE(element))
14375   {
14376     if (PLAYER_SWITCHING(player, x, y))
14377     {
14378       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14379                                           player->index_bit, dig_side);
14380
14381       return MP_ACTION;
14382     }
14383
14384     player->is_switching = TRUE;
14385     player->switch_x = x;
14386     player->switch_y = y;
14387
14388     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14389
14390     if (element == EL_ROBOT_WHEEL)
14391     {
14392       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14393
14394       game.robot_wheel_x = x;
14395       game.robot_wheel_y = y;
14396       game.robot_wheel_active = TRUE;
14397
14398       TEST_DrawLevelField(x, y);
14399     }
14400     else if (element == EL_SP_TERMINAL)
14401     {
14402       int xx, yy;
14403
14404       SCAN_PLAYFIELD(xx, yy)
14405       {
14406         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14407         {
14408           Bang(xx, yy);
14409         }
14410         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14411         {
14412           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14413
14414           ResetGfxAnimation(xx, yy);
14415           TEST_DrawLevelField(xx, yy);
14416         }
14417       }
14418     }
14419     else if (IS_BELT_SWITCH(element))
14420     {
14421       ToggleBeltSwitch(x, y);
14422     }
14423     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14424              element == EL_SWITCHGATE_SWITCH_DOWN ||
14425              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14426              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14427     {
14428       ToggleSwitchgateSwitch(x, y);
14429     }
14430     else if (element == EL_LIGHT_SWITCH ||
14431              element == EL_LIGHT_SWITCH_ACTIVE)
14432     {
14433       ToggleLightSwitch(x, y);
14434     }
14435     else if (element == EL_TIMEGATE_SWITCH ||
14436              element == EL_DC_TIMEGATE_SWITCH)
14437     {
14438       ActivateTimegateSwitch(x, y);
14439     }
14440     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14441              element == EL_BALLOON_SWITCH_RIGHT ||
14442              element == EL_BALLOON_SWITCH_UP    ||
14443              element == EL_BALLOON_SWITCH_DOWN  ||
14444              element == EL_BALLOON_SWITCH_NONE  ||
14445              element == EL_BALLOON_SWITCH_ANY)
14446     {
14447       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14448                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14449                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14450                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14451                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14452                              move_direction);
14453     }
14454     else if (element == EL_LAMP)
14455     {
14456       Tile[x][y] = EL_LAMP_ACTIVE;
14457       game.lights_still_needed--;
14458
14459       ResetGfxAnimation(x, y);
14460       TEST_DrawLevelField(x, y);
14461     }
14462     else if (element == EL_TIME_ORB_FULL)
14463     {
14464       Tile[x][y] = EL_TIME_ORB_EMPTY;
14465
14466       if (level.time > 0 || level.use_time_orb_bug)
14467       {
14468         TimeLeft += level.time_orb_time;
14469         game.no_time_limit = FALSE;
14470
14471         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14472
14473         DisplayGameControlValues();
14474       }
14475
14476       ResetGfxAnimation(x, y);
14477       TEST_DrawLevelField(x, y);
14478     }
14479     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14480              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14481     {
14482       int xx, yy;
14483
14484       game.ball_active = !game.ball_active;
14485
14486       SCAN_PLAYFIELD(xx, yy)
14487       {
14488         int e = Tile[xx][yy];
14489
14490         if (game.ball_active)
14491         {
14492           if (e == EL_EMC_MAGIC_BALL)
14493             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14494           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14495             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14496         }
14497         else
14498         {
14499           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14500             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14501           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14502             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14503         }
14504       }
14505     }
14506
14507     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14508                                         player->index_bit, dig_side);
14509
14510     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14511                                         player->index_bit, dig_side);
14512
14513     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14514                                         player->index_bit, dig_side);
14515
14516     return MP_ACTION;
14517   }
14518   else
14519   {
14520     if (!PLAYER_SWITCHING(player, x, y))
14521     {
14522       player->is_switching = TRUE;
14523       player->switch_x = x;
14524       player->switch_y = y;
14525
14526       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14527                                  player->index_bit, dig_side);
14528       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14529                                           player->index_bit, dig_side);
14530
14531       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14532                                  player->index_bit, dig_side);
14533       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14534                                           player->index_bit, dig_side);
14535     }
14536
14537     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14538                                player->index_bit, dig_side);
14539     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14540                                         player->index_bit, dig_side);
14541
14542     return MP_NO_ACTION;
14543   }
14544
14545   player->push_delay = -1;
14546
14547   if (is_player)                // function can also be called by EL_PENGUIN
14548   {
14549     if (Tile[x][y] != element)          // really digged/collected something
14550     {
14551       player->is_collecting = !player->is_digging;
14552       player->is_active = TRUE;
14553     }
14554   }
14555
14556   return MP_MOVING;
14557 }
14558
14559 static boolean DigFieldByCE(int x, int y, int digging_element)
14560 {
14561   int element = Tile[x][y];
14562
14563   if (!IS_FREE(x, y))
14564   {
14565     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14566                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14567                   ACTION_BREAKING);
14568
14569     // no element can dig solid indestructible elements
14570     if (IS_INDESTRUCTIBLE(element) &&
14571         !IS_DIGGABLE(element) &&
14572         !IS_COLLECTIBLE(element))
14573       return FALSE;
14574
14575     if (AmoebaNr[x][y] &&
14576         (element == EL_AMOEBA_FULL ||
14577          element == EL_BD_AMOEBA ||
14578          element == EL_AMOEBA_GROWING))
14579     {
14580       AmoebaCnt[AmoebaNr[x][y]]--;
14581       AmoebaCnt2[AmoebaNr[x][y]]--;
14582     }
14583
14584     if (IS_MOVING(x, y))
14585       RemoveMovingField(x, y);
14586     else
14587     {
14588       RemoveField(x, y);
14589       TEST_DrawLevelField(x, y);
14590     }
14591
14592     // if digged element was about to explode, prevent the explosion
14593     ExplodeField[x][y] = EX_TYPE_NONE;
14594
14595     PlayLevelSoundAction(x, y, action);
14596   }
14597
14598   Store[x][y] = EL_EMPTY;
14599
14600   // this makes it possible to leave the removed element again
14601   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14602     Store[x][y] = element;
14603
14604   return TRUE;
14605 }
14606
14607 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14608 {
14609   int jx = player->jx, jy = player->jy;
14610   int x = jx + dx, y = jy + dy;
14611   int snap_direction = (dx == -1 ? MV_LEFT  :
14612                         dx == +1 ? MV_RIGHT :
14613                         dy == -1 ? MV_UP    :
14614                         dy == +1 ? MV_DOWN  : MV_NONE);
14615   boolean can_continue_snapping = (level.continuous_snapping &&
14616                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14617
14618   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14619     return FALSE;
14620
14621   if (!player->active || !IN_LEV_FIELD(x, y))
14622     return FALSE;
14623
14624   if (dx && dy)
14625     return FALSE;
14626
14627   if (!dx && !dy)
14628   {
14629     if (player->MovPos == 0)
14630       player->is_pushing = FALSE;
14631
14632     player->is_snapping = FALSE;
14633
14634     if (player->MovPos == 0)
14635     {
14636       player->is_moving = FALSE;
14637       player->is_digging = FALSE;
14638       player->is_collecting = FALSE;
14639     }
14640
14641     return FALSE;
14642   }
14643
14644   // prevent snapping with already pressed snap key when not allowed
14645   if (player->is_snapping && !can_continue_snapping)
14646     return FALSE;
14647
14648   player->MovDir = snap_direction;
14649
14650   if (player->MovPos == 0)
14651   {
14652     player->is_moving = FALSE;
14653     player->is_digging = FALSE;
14654     player->is_collecting = FALSE;
14655   }
14656
14657   player->is_dropping = FALSE;
14658   player->is_dropping_pressed = FALSE;
14659   player->drop_pressed_delay = 0;
14660
14661   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14662     return FALSE;
14663
14664   player->is_snapping = TRUE;
14665   player->is_active = TRUE;
14666
14667   if (player->MovPos == 0)
14668   {
14669     player->is_moving = FALSE;
14670     player->is_digging = FALSE;
14671     player->is_collecting = FALSE;
14672   }
14673
14674   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14675     TEST_DrawLevelField(player->last_jx, player->last_jy);
14676
14677   TEST_DrawLevelField(x, y);
14678
14679   return TRUE;
14680 }
14681
14682 static boolean DropElement(struct PlayerInfo *player)
14683 {
14684   int old_element, new_element;
14685   int dropx = player->jx, dropy = player->jy;
14686   int drop_direction = player->MovDir;
14687   int drop_side = drop_direction;
14688   int drop_element = get_next_dropped_element(player);
14689
14690   /* do not drop an element on top of another element; when holding drop key
14691      pressed without moving, dropped element must move away before the next
14692      element can be dropped (this is especially important if the next element
14693      is dynamite, which can be placed on background for historical reasons) */
14694   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14695     return MP_ACTION;
14696
14697   if (IS_THROWABLE(drop_element))
14698   {
14699     dropx += GET_DX_FROM_DIR(drop_direction);
14700     dropy += GET_DY_FROM_DIR(drop_direction);
14701
14702     if (!IN_LEV_FIELD(dropx, dropy))
14703       return FALSE;
14704   }
14705
14706   old_element = Tile[dropx][dropy];     // old element at dropping position
14707   new_element = drop_element;           // default: no change when dropping
14708
14709   // check if player is active, not moving and ready to drop
14710   if (!player->active || player->MovPos || player->drop_delay > 0)
14711     return FALSE;
14712
14713   // check if player has anything that can be dropped
14714   if (new_element == EL_UNDEFINED)
14715     return FALSE;
14716
14717   // only set if player has anything that can be dropped
14718   player->is_dropping_pressed = TRUE;
14719
14720   // check if drop key was pressed long enough for EM style dynamite
14721   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14722     return FALSE;
14723
14724   // check if anything can be dropped at the current position
14725   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14726     return FALSE;
14727
14728   // collected custom elements can only be dropped on empty fields
14729   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14730     return FALSE;
14731
14732   if (old_element != EL_EMPTY)
14733     Back[dropx][dropy] = old_element;   // store old element on this field
14734
14735   ResetGfxAnimation(dropx, dropy);
14736   ResetRandomAnimationValue(dropx, dropy);
14737
14738   if (player->inventory_size > 0 ||
14739       player->inventory_infinite_element != EL_UNDEFINED)
14740   {
14741     if (player->inventory_size > 0)
14742     {
14743       player->inventory_size--;
14744
14745       DrawGameDoorValues();
14746
14747       if (new_element == EL_DYNAMITE)
14748         new_element = EL_DYNAMITE_ACTIVE;
14749       else if (new_element == EL_EM_DYNAMITE)
14750         new_element = EL_EM_DYNAMITE_ACTIVE;
14751       else if (new_element == EL_SP_DISK_RED)
14752         new_element = EL_SP_DISK_RED_ACTIVE;
14753     }
14754
14755     Tile[dropx][dropy] = new_element;
14756
14757     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14758       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14759                           el2img(Tile[dropx][dropy]), 0);
14760
14761     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14762
14763     // needed if previous element just changed to "empty" in the last frame
14764     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14765
14766     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14767                                player->index_bit, drop_side);
14768     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14769                                         CE_PLAYER_DROPS_X,
14770                                         player->index_bit, drop_side);
14771
14772     TestIfElementTouchesCustomElement(dropx, dropy);
14773   }
14774   else          // player is dropping a dyna bomb
14775   {
14776     player->dynabombs_left--;
14777
14778     Tile[dropx][dropy] = new_element;
14779
14780     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14781       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14782                           el2img(Tile[dropx][dropy]), 0);
14783
14784     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14785   }
14786
14787   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14788     InitField_WithBug1(dropx, dropy, FALSE);
14789
14790   new_element = Tile[dropx][dropy];     // element might have changed
14791
14792   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14793       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14794   {
14795     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14796       MovDir[dropx][dropy] = drop_direction;
14797
14798     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14799
14800     // do not cause impact style collision by dropping elements that can fall
14801     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14802   }
14803
14804   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14805   player->is_dropping = TRUE;
14806
14807   player->drop_pressed_delay = 0;
14808   player->is_dropping_pressed = FALSE;
14809
14810   player->drop_x = dropx;
14811   player->drop_y = dropy;
14812
14813   return TRUE;
14814 }
14815
14816 // ----------------------------------------------------------------------------
14817 // game sound playing functions
14818 // ----------------------------------------------------------------------------
14819
14820 static int *loop_sound_frame = NULL;
14821 static int *loop_sound_volume = NULL;
14822
14823 void InitPlayLevelSound(void)
14824 {
14825   int num_sounds = getSoundListSize();
14826
14827   checked_free(loop_sound_frame);
14828   checked_free(loop_sound_volume);
14829
14830   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14831   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14832 }
14833
14834 static void PlayLevelSound(int x, int y, int nr)
14835 {
14836   int sx = SCREENX(x), sy = SCREENY(y);
14837   int volume, stereo_position;
14838   int max_distance = 8;
14839   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14840
14841   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14842       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14843     return;
14844
14845   if (!IN_LEV_FIELD(x, y) ||
14846       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14847       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14848     return;
14849
14850   volume = SOUND_MAX_VOLUME;
14851
14852   if (!IN_SCR_FIELD(sx, sy))
14853   {
14854     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14855     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14856
14857     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14858   }
14859
14860   stereo_position = (SOUND_MAX_LEFT +
14861                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14862                      (SCR_FIELDX + 2 * max_distance));
14863
14864   if (IS_LOOP_SOUND(nr))
14865   {
14866     /* This assures that quieter loop sounds do not overwrite louder ones,
14867        while restarting sound volume comparison with each new game frame. */
14868
14869     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14870       return;
14871
14872     loop_sound_volume[nr] = volume;
14873     loop_sound_frame[nr] = FrameCounter;
14874   }
14875
14876   PlaySoundExt(nr, volume, stereo_position, type);
14877 }
14878
14879 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14880 {
14881   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14882                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14883                  y < LEVELY(BY1) ? LEVELY(BY1) :
14884                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14885                  sound_action);
14886 }
14887
14888 static void PlayLevelSoundAction(int x, int y, int action)
14889 {
14890   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14891 }
14892
14893 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14894 {
14895   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14896
14897   if (sound_effect != SND_UNDEFINED)
14898     PlayLevelSound(x, y, sound_effect);
14899 }
14900
14901 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14902                                               int action)
14903 {
14904   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14905
14906   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14907     PlayLevelSound(x, y, sound_effect);
14908 }
14909
14910 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14911 {
14912   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14913
14914   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14915     PlayLevelSound(x, y, sound_effect);
14916 }
14917
14918 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14919 {
14920   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14921
14922   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14923     StopSound(sound_effect);
14924 }
14925
14926 static int getLevelMusicNr(void)
14927 {
14928   if (levelset.music[level_nr] != MUS_UNDEFINED)
14929     return levelset.music[level_nr];            // from config file
14930   else
14931     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14932 }
14933
14934 static void FadeLevelSounds(void)
14935 {
14936   FadeSounds();
14937 }
14938
14939 static void FadeLevelMusic(void)
14940 {
14941   int music_nr = getLevelMusicNr();
14942   char *curr_music = getCurrentlyPlayingMusicFilename();
14943   char *next_music = getMusicInfoEntryFilename(music_nr);
14944
14945   if (!strEqual(curr_music, next_music))
14946     FadeMusic();
14947 }
14948
14949 void FadeLevelSoundsAndMusic(void)
14950 {
14951   FadeLevelSounds();
14952   FadeLevelMusic();
14953 }
14954
14955 static void PlayLevelMusic(void)
14956 {
14957   int music_nr = getLevelMusicNr();
14958   char *curr_music = getCurrentlyPlayingMusicFilename();
14959   char *next_music = getMusicInfoEntryFilename(music_nr);
14960
14961   if (!strEqual(curr_music, next_music))
14962     PlayMusicLoop(music_nr);
14963 }
14964
14965 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14966 {
14967   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14968   int offset = 0;
14969   int x = xx - offset;
14970   int y = yy - offset;
14971
14972   switch (sample)
14973   {
14974     case SOUND_blank:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14976       break;
14977
14978     case SOUND_roll:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14980       break;
14981
14982     case SOUND_stone:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14984       break;
14985
14986     case SOUND_nut:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14988       break;
14989
14990     case SOUND_crack:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14992       break;
14993
14994     case SOUND_bug:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14996       break;
14997
14998     case SOUND_tank:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15000       break;
15001
15002     case SOUND_android_clone:
15003       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15004       break;
15005
15006     case SOUND_android_move:
15007       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15008       break;
15009
15010     case SOUND_spring:
15011       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15012       break;
15013
15014     case SOUND_slurp:
15015       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15016       break;
15017
15018     case SOUND_eater:
15019       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15020       break;
15021
15022     case SOUND_eater_eat:
15023       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15024       break;
15025
15026     case SOUND_alien:
15027       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15028       break;
15029
15030     case SOUND_collect:
15031       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15032       break;
15033
15034     case SOUND_diamond:
15035       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15036       break;
15037
15038     case SOUND_squash:
15039       // !!! CHECK THIS !!!
15040 #if 1
15041       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15042 #else
15043       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15044 #endif
15045       break;
15046
15047     case SOUND_wonderfall:
15048       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15049       break;
15050
15051     case SOUND_drip:
15052       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15053       break;
15054
15055     case SOUND_push:
15056       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15057       break;
15058
15059     case SOUND_dirt:
15060       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15061       break;
15062
15063     case SOUND_acid:
15064       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15065       break;
15066
15067     case SOUND_ball:
15068       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15069       break;
15070
15071     case SOUND_slide:
15072       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15073       break;
15074
15075     case SOUND_wonder:
15076       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15077       break;
15078
15079     case SOUND_door:
15080       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15081       break;
15082
15083     case SOUND_exit_open:
15084       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15085       break;
15086
15087     case SOUND_exit_leave:
15088       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15089       break;
15090
15091     case SOUND_dynamite:
15092       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15093       break;
15094
15095     case SOUND_tick:
15096       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15097       break;
15098
15099     case SOUND_press:
15100       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15101       break;
15102
15103     case SOUND_wheel:
15104       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15105       break;
15106
15107     case SOUND_boom:
15108       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15109       break;
15110
15111     case SOUND_die:
15112       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15113       break;
15114
15115     case SOUND_time:
15116       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15117       break;
15118
15119     default:
15120       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15121       break;
15122   }
15123 }
15124
15125 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15126 {
15127   int element = map_element_SP_to_RND(element_sp);
15128   int action = map_action_SP_to_RND(action_sp);
15129   int offset = (setup.sp_show_border_elements ? 0 : 1);
15130   int x = xx - offset;
15131   int y = yy - offset;
15132
15133   PlayLevelSoundElementAction(x, y, element, action);
15134 }
15135
15136 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15137 {
15138   int element = map_element_MM_to_RND(element_mm);
15139   int action = map_action_MM_to_RND(action_mm);
15140   int offset = 0;
15141   int x = xx - offset;
15142   int y = yy - offset;
15143
15144   if (!IS_MM_ELEMENT(element))
15145     element = EL_MM_DEFAULT;
15146
15147   PlayLevelSoundElementAction(x, y, element, action);
15148 }
15149
15150 void PlaySound_MM(int sound_mm)
15151 {
15152   int sound = map_sound_MM_to_RND(sound_mm);
15153
15154   if (sound == SND_UNDEFINED)
15155     return;
15156
15157   PlaySound(sound);
15158 }
15159
15160 void PlaySoundLoop_MM(int sound_mm)
15161 {
15162   int sound = map_sound_MM_to_RND(sound_mm);
15163
15164   if (sound == SND_UNDEFINED)
15165     return;
15166
15167   PlaySoundLoop(sound);
15168 }
15169
15170 void StopSound_MM(int sound_mm)
15171 {
15172   int sound = map_sound_MM_to_RND(sound_mm);
15173
15174   if (sound == SND_UNDEFINED)
15175     return;
15176
15177   StopSound(sound);
15178 }
15179
15180 void RaiseScore(int value)
15181 {
15182   game.score += value;
15183
15184   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15185
15186   DisplayGameControlValues();
15187 }
15188
15189 void RaiseScoreElement(int element)
15190 {
15191   switch (element)
15192   {
15193     case EL_EMERALD:
15194     case EL_BD_DIAMOND:
15195     case EL_EMERALD_YELLOW:
15196     case EL_EMERALD_RED:
15197     case EL_EMERALD_PURPLE:
15198     case EL_SP_INFOTRON:
15199       RaiseScore(level.score[SC_EMERALD]);
15200       break;
15201     case EL_DIAMOND:
15202       RaiseScore(level.score[SC_DIAMOND]);
15203       break;
15204     case EL_CRYSTAL:
15205       RaiseScore(level.score[SC_CRYSTAL]);
15206       break;
15207     case EL_PEARL:
15208       RaiseScore(level.score[SC_PEARL]);
15209       break;
15210     case EL_BUG:
15211     case EL_BD_BUTTERFLY:
15212     case EL_SP_ELECTRON:
15213       RaiseScore(level.score[SC_BUG]);
15214       break;
15215     case EL_SPACESHIP:
15216     case EL_BD_FIREFLY:
15217     case EL_SP_SNIKSNAK:
15218       RaiseScore(level.score[SC_SPACESHIP]);
15219       break;
15220     case EL_YAMYAM:
15221     case EL_DARK_YAMYAM:
15222       RaiseScore(level.score[SC_YAMYAM]);
15223       break;
15224     case EL_ROBOT:
15225       RaiseScore(level.score[SC_ROBOT]);
15226       break;
15227     case EL_PACMAN:
15228       RaiseScore(level.score[SC_PACMAN]);
15229       break;
15230     case EL_NUT:
15231       RaiseScore(level.score[SC_NUT]);
15232       break;
15233     case EL_DYNAMITE:
15234     case EL_EM_DYNAMITE:
15235     case EL_SP_DISK_RED:
15236     case EL_DYNABOMB_INCREASE_NUMBER:
15237     case EL_DYNABOMB_INCREASE_SIZE:
15238     case EL_DYNABOMB_INCREASE_POWER:
15239       RaiseScore(level.score[SC_DYNAMITE]);
15240       break;
15241     case EL_SHIELD_NORMAL:
15242     case EL_SHIELD_DEADLY:
15243       RaiseScore(level.score[SC_SHIELD]);
15244       break;
15245     case EL_EXTRA_TIME:
15246       RaiseScore(level.extra_time_score);
15247       break;
15248     case EL_KEY_1:
15249     case EL_KEY_2:
15250     case EL_KEY_3:
15251     case EL_KEY_4:
15252     case EL_EM_KEY_1:
15253     case EL_EM_KEY_2:
15254     case EL_EM_KEY_3:
15255     case EL_EM_KEY_4:
15256     case EL_EMC_KEY_5:
15257     case EL_EMC_KEY_6:
15258     case EL_EMC_KEY_7:
15259     case EL_EMC_KEY_8:
15260     case EL_DC_KEY_WHITE:
15261       RaiseScore(level.score[SC_KEY]);
15262       break;
15263     default:
15264       RaiseScore(element_info[element].collect_score);
15265       break;
15266   }
15267 }
15268
15269 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15270 {
15271   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15272   {
15273     // closing door required in case of envelope style request dialogs
15274     if (!skip_request)
15275     {
15276       // prevent short reactivation of overlay buttons while closing door
15277       SetOverlayActive(FALSE);
15278
15279       CloseDoor(DOOR_CLOSE_1);
15280     }
15281
15282     if (network.enabled)
15283       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15284     else
15285     {
15286       if (quick_quit)
15287         FadeSkipNextFadeIn();
15288
15289       SetGameStatus(GAME_MODE_MAIN);
15290
15291       DrawMainMenu();
15292     }
15293   }
15294   else          // continue playing the game
15295   {
15296     if (tape.playing && tape.deactivate_display)
15297       TapeDeactivateDisplayOff(TRUE);
15298
15299     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15300
15301     if (tape.playing && tape.deactivate_display)
15302       TapeDeactivateDisplayOn();
15303   }
15304 }
15305
15306 void RequestQuitGame(boolean ask_if_really_quit)
15307 {
15308   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15309   boolean skip_request = game.all_players_gone || quick_quit;
15310
15311   RequestQuitGameExt(skip_request, quick_quit,
15312                      "Do you really want to quit the game?");
15313 }
15314
15315 void RequestRestartGame(char *message)
15316 {
15317   game.restart_game_message = NULL;
15318
15319   boolean has_started_game = hasStartedNetworkGame();
15320   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15321
15322   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15323   {
15324     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15325   }
15326   else
15327   {
15328     // needed in case of envelope request to close game panel
15329     CloseDoor(DOOR_CLOSE_1);
15330
15331     SetGameStatus(GAME_MODE_MAIN);
15332
15333     DrawMainMenu();
15334   }
15335 }
15336
15337 void CheckGameOver(void)
15338 {
15339   static boolean last_game_over = FALSE;
15340   static int game_over_delay = 0;
15341   int game_over_delay_value = 50;
15342   boolean game_over = checkGameFailed();
15343
15344   // do not handle game over if request dialog is already active
15345   if (game.request_active)
15346     return;
15347
15348   // do not ask to play again if game was never actually played
15349   if (!game.GamePlayed)
15350     return;
15351
15352   if (!game_over)
15353   {
15354     last_game_over = FALSE;
15355     game_over_delay = game_over_delay_value;
15356
15357     return;
15358   }
15359
15360   if (game_over_delay > 0)
15361   {
15362     game_over_delay--;
15363
15364     return;
15365   }
15366
15367   if (last_game_over != game_over)
15368     game.restart_game_message = (hasStartedNetworkGame() ?
15369                                  "Game over! Play it again?" :
15370                                  "Game over!");
15371
15372   last_game_over = game_over;
15373 }
15374
15375 boolean checkGameSolved(void)
15376 {
15377   // set for all game engines if level was solved
15378   return game.LevelSolved_GameEnd;
15379 }
15380
15381 boolean checkGameFailed(void)
15382 {
15383   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15384     return (game_em.game_over && !game_em.level_solved);
15385   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15386     return (game_sp.game_over && !game_sp.level_solved);
15387   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15388     return (game_mm.game_over && !game_mm.level_solved);
15389   else                          // GAME_ENGINE_TYPE_RND
15390     return (game.GameOver && !game.LevelSolved);
15391 }
15392
15393 boolean checkGameEnded(void)
15394 {
15395   return (checkGameSolved() || checkGameFailed());
15396 }
15397
15398
15399 // ----------------------------------------------------------------------------
15400 // random generator functions
15401 // ----------------------------------------------------------------------------
15402
15403 unsigned int InitEngineRandom_RND(int seed)
15404 {
15405   game.num_random_calls = 0;
15406
15407   return InitEngineRandom(seed);
15408 }
15409
15410 unsigned int RND(int max)
15411 {
15412   if (max > 0)
15413   {
15414     game.num_random_calls++;
15415
15416     return GetEngineRandom(max);
15417   }
15418
15419   return 0;
15420 }
15421
15422
15423 // ----------------------------------------------------------------------------
15424 // game engine snapshot handling functions
15425 // ----------------------------------------------------------------------------
15426
15427 struct EngineSnapshotInfo
15428 {
15429   // runtime values for custom element collect score
15430   int collect_score[NUM_CUSTOM_ELEMENTS];
15431
15432   // runtime values for group element choice position
15433   int choice_pos[NUM_GROUP_ELEMENTS];
15434
15435   // runtime values for belt position animations
15436   int belt_graphic[4][NUM_BELT_PARTS];
15437   int belt_anim_mode[4][NUM_BELT_PARTS];
15438 };
15439
15440 static struct EngineSnapshotInfo engine_snapshot_rnd;
15441 static char *snapshot_level_identifier = NULL;
15442 static int snapshot_level_nr = -1;
15443
15444 static void SaveEngineSnapshotValues_RND(void)
15445 {
15446   static int belt_base_active_element[4] =
15447   {
15448     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15449     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15450     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15451     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15452   };
15453   int i, j;
15454
15455   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15456   {
15457     int element = EL_CUSTOM_START + i;
15458
15459     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15460   }
15461
15462   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15463   {
15464     int element = EL_GROUP_START + i;
15465
15466     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15467   }
15468
15469   for (i = 0; i < 4; i++)
15470   {
15471     for (j = 0; j < NUM_BELT_PARTS; j++)
15472     {
15473       int element = belt_base_active_element[i] + j;
15474       int graphic = el2img(element);
15475       int anim_mode = graphic_info[graphic].anim_mode;
15476
15477       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15478       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15479     }
15480   }
15481 }
15482
15483 static void LoadEngineSnapshotValues_RND(void)
15484 {
15485   unsigned int num_random_calls = game.num_random_calls;
15486   int i, j;
15487
15488   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15489   {
15490     int element = EL_CUSTOM_START + i;
15491
15492     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15493   }
15494
15495   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15496   {
15497     int element = EL_GROUP_START + i;
15498
15499     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15500   }
15501
15502   for (i = 0; i < 4; i++)
15503   {
15504     for (j = 0; j < NUM_BELT_PARTS; j++)
15505     {
15506       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15507       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15508
15509       graphic_info[graphic].anim_mode = anim_mode;
15510     }
15511   }
15512
15513   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15514   {
15515     InitRND(tape.random_seed);
15516     for (i = 0; i < num_random_calls; i++)
15517       RND(1);
15518   }
15519
15520   if (game.num_random_calls != num_random_calls)
15521   {
15522     Error("number of random calls out of sync");
15523     Error("number of random calls should be %d", num_random_calls);
15524     Error("number of random calls is %d", game.num_random_calls);
15525
15526     Fail("this should not happen -- please debug");
15527   }
15528 }
15529
15530 void FreeEngineSnapshotSingle(void)
15531 {
15532   FreeSnapshotSingle();
15533
15534   setString(&snapshot_level_identifier, NULL);
15535   snapshot_level_nr = -1;
15536 }
15537
15538 void FreeEngineSnapshotList(void)
15539 {
15540   FreeSnapshotList();
15541 }
15542
15543 static ListNode *SaveEngineSnapshotBuffers(void)
15544 {
15545   ListNode *buffers = NULL;
15546
15547   // copy some special values to a structure better suited for the snapshot
15548
15549   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15550     SaveEngineSnapshotValues_RND();
15551   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15552     SaveEngineSnapshotValues_EM();
15553   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15554     SaveEngineSnapshotValues_SP(&buffers);
15555   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15556     SaveEngineSnapshotValues_MM(&buffers);
15557
15558   // save values stored in special snapshot structure
15559
15560   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15561     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15562   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15563     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15564   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15565     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15566   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15567     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15568
15569   // save further RND engine values
15570
15571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15572   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15574
15575   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15576   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15578   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15579   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15580
15581   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15583   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15584
15585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15586
15587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15589
15590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15596   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15598   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15600   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15601   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15602   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15603   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15605   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15607   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15608
15609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15610   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15611
15612   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15615
15616   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15617   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15618
15619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15620   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15621   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15622   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15623   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15624
15625   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15626   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15627
15628 #if 0
15629   ListNode *node = engine_snapshot_list_rnd;
15630   int num_bytes = 0;
15631
15632   while (node != NULL)
15633   {
15634     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15635
15636     node = node->next;
15637   }
15638
15639   Debug("game:playing:SaveEngineSnapshotBuffers",
15640         "size of engine snapshot: %d bytes", num_bytes);
15641 #endif
15642
15643   return buffers;
15644 }
15645
15646 void SaveEngineSnapshotSingle(void)
15647 {
15648   ListNode *buffers = SaveEngineSnapshotBuffers();
15649
15650   // finally save all snapshot buffers to single snapshot
15651   SaveSnapshotSingle(buffers);
15652
15653   // save level identification information
15654   setString(&snapshot_level_identifier, leveldir_current->identifier);
15655   snapshot_level_nr = level_nr;
15656 }
15657
15658 boolean CheckSaveEngineSnapshotToList(void)
15659 {
15660   boolean save_snapshot =
15661     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15662      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15663       game.snapshot.changed_action) ||
15664      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15665       game.snapshot.collected_item));
15666
15667   game.snapshot.changed_action = FALSE;
15668   game.snapshot.collected_item = FALSE;
15669   game.snapshot.save_snapshot = save_snapshot;
15670
15671   return save_snapshot;
15672 }
15673
15674 void SaveEngineSnapshotToList(void)
15675 {
15676   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15677       tape.quick_resume)
15678     return;
15679
15680   ListNode *buffers = SaveEngineSnapshotBuffers();
15681
15682   // finally save all snapshot buffers to snapshot list
15683   SaveSnapshotToList(buffers);
15684 }
15685
15686 void SaveEngineSnapshotToListInitial(void)
15687 {
15688   FreeEngineSnapshotList();
15689
15690   SaveEngineSnapshotToList();
15691 }
15692
15693 static void LoadEngineSnapshotValues(void)
15694 {
15695   // restore special values from snapshot structure
15696
15697   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15698     LoadEngineSnapshotValues_RND();
15699   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15700     LoadEngineSnapshotValues_EM();
15701   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15702     LoadEngineSnapshotValues_SP();
15703   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15704     LoadEngineSnapshotValues_MM();
15705 }
15706
15707 void LoadEngineSnapshotSingle(void)
15708 {
15709   LoadSnapshotSingle();
15710
15711   LoadEngineSnapshotValues();
15712 }
15713
15714 static void LoadEngineSnapshot_Undo(int steps)
15715 {
15716   LoadSnapshotFromList_Older(steps);
15717
15718   LoadEngineSnapshotValues();
15719 }
15720
15721 static void LoadEngineSnapshot_Redo(int steps)
15722 {
15723   LoadSnapshotFromList_Newer(steps);
15724
15725   LoadEngineSnapshotValues();
15726 }
15727
15728 boolean CheckEngineSnapshotSingle(void)
15729 {
15730   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15731           snapshot_level_nr == level_nr);
15732 }
15733
15734 boolean CheckEngineSnapshotList(void)
15735 {
15736   return CheckSnapshotList();
15737 }
15738
15739
15740 // ---------- new game button stuff -------------------------------------------
15741
15742 static struct
15743 {
15744   int graphic;
15745   struct XY *pos;
15746   int gadget_id;
15747   boolean *setup_value;
15748   boolean allowed_on_tape;
15749   boolean is_touch_button;
15750   char *infotext;
15751 } gamebutton_info[NUM_GAME_BUTTONS] =
15752 {
15753   {
15754     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15755     GAME_CTRL_ID_STOP,                          NULL,
15756     TRUE, FALSE,                                "stop game"
15757   },
15758   {
15759     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15760     GAME_CTRL_ID_PAUSE,                         NULL,
15761     TRUE, FALSE,                                "pause game"
15762   },
15763   {
15764     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15765     GAME_CTRL_ID_PLAY,                          NULL,
15766     TRUE, FALSE,                                "play game"
15767   },
15768   {
15769     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15770     GAME_CTRL_ID_UNDO,                          NULL,
15771     TRUE, FALSE,                                "undo step"
15772   },
15773   {
15774     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15775     GAME_CTRL_ID_REDO,                          NULL,
15776     TRUE, FALSE,                                "redo step"
15777   },
15778   {
15779     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15780     GAME_CTRL_ID_SAVE,                          NULL,
15781     TRUE, FALSE,                                "save game"
15782   },
15783   {
15784     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15785     GAME_CTRL_ID_PAUSE2,                        NULL,
15786     TRUE, FALSE,                                "pause game"
15787   },
15788   {
15789     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15790     GAME_CTRL_ID_LOAD,                          NULL,
15791     TRUE, FALSE,                                "load game"
15792   },
15793   {
15794     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15795     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15796     FALSE, FALSE,                               "stop game"
15797   },
15798   {
15799     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15800     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15801     FALSE, FALSE,                               "pause game"
15802   },
15803   {
15804     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15805     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15806     FALSE, FALSE,                               "play game"
15807   },
15808   {
15809     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15810     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15811     FALSE, TRUE,                                "stop game"
15812   },
15813   {
15814     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15815     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15816     FALSE, TRUE,                                "pause game"
15817   },
15818   {
15819     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15820     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15821     TRUE, FALSE,                                "background music on/off"
15822   },
15823   {
15824     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15825     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15826     TRUE, FALSE,                                "sound loops on/off"
15827   },
15828   {
15829     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15830     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15831     TRUE, FALSE,                                "normal sounds on/off"
15832   },
15833   {
15834     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15835     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15836     FALSE, FALSE,                               "background music on/off"
15837   },
15838   {
15839     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15840     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15841     FALSE, FALSE,                               "sound loops on/off"
15842   },
15843   {
15844     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15845     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15846     FALSE, FALSE,                               "normal sounds on/off"
15847   }
15848 };
15849
15850 void CreateGameButtons(void)
15851 {
15852   int i;
15853
15854   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15855   {
15856     int graphic = gamebutton_info[i].graphic;
15857     struct GraphicInfo *gfx = &graphic_info[graphic];
15858     struct XY *pos = gamebutton_info[i].pos;
15859     struct GadgetInfo *gi;
15860     int button_type;
15861     boolean checked;
15862     unsigned int event_mask;
15863     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15864     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15865     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15866     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15867     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15868     int gd_x   = gfx->src_x;
15869     int gd_y   = gfx->src_y;
15870     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15871     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15872     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15873     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15874     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15875     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15876     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15877     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15878     int id = i;
15879
15880     if (gfx->bitmap == NULL)
15881     {
15882       game_gadget[id] = NULL;
15883
15884       continue;
15885     }
15886
15887     if (id == GAME_CTRL_ID_STOP ||
15888         id == GAME_CTRL_ID_PANEL_STOP ||
15889         id == GAME_CTRL_ID_TOUCH_STOP ||
15890         id == GAME_CTRL_ID_PLAY ||
15891         id == GAME_CTRL_ID_PANEL_PLAY ||
15892         id == GAME_CTRL_ID_SAVE ||
15893         id == GAME_CTRL_ID_LOAD)
15894     {
15895       button_type = GD_TYPE_NORMAL_BUTTON;
15896       checked = FALSE;
15897       event_mask = GD_EVENT_RELEASED;
15898     }
15899     else if (id == GAME_CTRL_ID_UNDO ||
15900              id == GAME_CTRL_ID_REDO)
15901     {
15902       button_type = GD_TYPE_NORMAL_BUTTON;
15903       checked = FALSE;
15904       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15905     }
15906     else
15907     {
15908       button_type = GD_TYPE_CHECK_BUTTON;
15909       checked = (gamebutton_info[i].setup_value != NULL ?
15910                  *gamebutton_info[i].setup_value : FALSE);
15911       event_mask = GD_EVENT_PRESSED;
15912     }
15913
15914     gi = CreateGadget(GDI_CUSTOM_ID, id,
15915                       GDI_IMAGE_ID, graphic,
15916                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15917                       GDI_X, base_x + x,
15918                       GDI_Y, base_y + y,
15919                       GDI_WIDTH, gfx->width,
15920                       GDI_HEIGHT, gfx->height,
15921                       GDI_TYPE, button_type,
15922                       GDI_STATE, GD_BUTTON_UNPRESSED,
15923                       GDI_CHECKED, checked,
15924                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15925                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15926                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15927                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15928                       GDI_DIRECT_DRAW, FALSE,
15929                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15930                       GDI_EVENT_MASK, event_mask,
15931                       GDI_CALLBACK_ACTION, HandleGameButtons,
15932                       GDI_END);
15933
15934     if (gi == NULL)
15935       Fail("cannot create gadget");
15936
15937     game_gadget[id] = gi;
15938   }
15939 }
15940
15941 void FreeGameButtons(void)
15942 {
15943   int i;
15944
15945   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15946     FreeGadget(game_gadget[i]);
15947 }
15948
15949 static void UnmapGameButtonsAtSamePosition(int id)
15950 {
15951   int i;
15952
15953   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15954     if (i != id &&
15955         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15956         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15957       UnmapGadget(game_gadget[i]);
15958 }
15959
15960 static void UnmapGameButtonsAtSamePosition_All(void)
15961 {
15962   if (setup.show_snapshot_buttons)
15963   {
15964     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15965     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15966     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15967   }
15968   else
15969   {
15970     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15971     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15972     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15973
15974     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15975     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15976     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15977   }
15978 }
15979
15980 static void MapGameButtonsAtSamePosition(int id)
15981 {
15982   int i;
15983
15984   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15985     if (i != id &&
15986         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15987         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15988       MapGadget(game_gadget[i]);
15989
15990   UnmapGameButtonsAtSamePosition_All();
15991 }
15992
15993 void MapUndoRedoButtons(void)
15994 {
15995   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15996   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15997
15998   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15999   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16000 }
16001
16002 void UnmapUndoRedoButtons(void)
16003 {
16004   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16005   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16006
16007   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16008   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16009 }
16010
16011 void ModifyPauseButtons(void)
16012 {
16013   static int ids[] =
16014   {
16015     GAME_CTRL_ID_PAUSE,
16016     GAME_CTRL_ID_PAUSE2,
16017     GAME_CTRL_ID_PANEL_PAUSE,
16018     GAME_CTRL_ID_TOUCH_PAUSE,
16019     -1
16020   };
16021   int i;
16022
16023   for (i = 0; ids[i] > -1; i++)
16024     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16025 }
16026
16027 static void MapGameButtonsExt(boolean on_tape)
16028 {
16029   int i;
16030
16031   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16032     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16033         i != GAME_CTRL_ID_UNDO &&
16034         i != GAME_CTRL_ID_REDO)
16035       MapGadget(game_gadget[i]);
16036
16037   UnmapGameButtonsAtSamePosition_All();
16038
16039   RedrawGameButtons();
16040 }
16041
16042 static void UnmapGameButtonsExt(boolean on_tape)
16043 {
16044   int i;
16045
16046   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16047     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16048       UnmapGadget(game_gadget[i]);
16049 }
16050
16051 static void RedrawGameButtonsExt(boolean on_tape)
16052 {
16053   int i;
16054
16055   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16056     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16057       RedrawGadget(game_gadget[i]);
16058 }
16059
16060 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16061 {
16062   if (gi == NULL)
16063     return;
16064
16065   gi->checked = state;
16066 }
16067
16068 static void RedrawSoundButtonGadget(int id)
16069 {
16070   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16071              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16072              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16073              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16074              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16075              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16076              id);
16077
16078   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16079   RedrawGadget(game_gadget[id2]);
16080 }
16081
16082 void MapGameButtons(void)
16083 {
16084   MapGameButtonsExt(FALSE);
16085 }
16086
16087 void UnmapGameButtons(void)
16088 {
16089   UnmapGameButtonsExt(FALSE);
16090 }
16091
16092 void RedrawGameButtons(void)
16093 {
16094   RedrawGameButtonsExt(FALSE);
16095 }
16096
16097 void MapGameButtonsOnTape(void)
16098 {
16099   MapGameButtonsExt(TRUE);
16100 }
16101
16102 void UnmapGameButtonsOnTape(void)
16103 {
16104   UnmapGameButtonsExt(TRUE);
16105 }
16106
16107 void RedrawGameButtonsOnTape(void)
16108 {
16109   RedrawGameButtonsExt(TRUE);
16110 }
16111
16112 static void GameUndoRedoExt(void)
16113 {
16114   ClearPlayerAction();
16115
16116   tape.pausing = TRUE;
16117
16118   RedrawPlayfield();
16119   UpdateAndDisplayGameControlValues();
16120
16121   DrawCompleteVideoDisplay();
16122   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16123   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16124   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16125
16126   BackToFront();
16127 }
16128
16129 static void GameUndo(int steps)
16130 {
16131   if (!CheckEngineSnapshotList())
16132     return;
16133
16134   LoadEngineSnapshot_Undo(steps);
16135
16136   GameUndoRedoExt();
16137 }
16138
16139 static void GameRedo(int steps)
16140 {
16141   if (!CheckEngineSnapshotList())
16142     return;
16143
16144   LoadEngineSnapshot_Redo(steps);
16145
16146   GameUndoRedoExt();
16147 }
16148
16149 static void HandleGameButtonsExt(int id, int button)
16150 {
16151   static boolean game_undo_executed = FALSE;
16152   int steps = BUTTON_STEPSIZE(button);
16153   boolean handle_game_buttons =
16154     (game_status == GAME_MODE_PLAYING ||
16155      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16156
16157   if (!handle_game_buttons)
16158     return;
16159
16160   switch (id)
16161   {
16162     case GAME_CTRL_ID_STOP:
16163     case GAME_CTRL_ID_PANEL_STOP:
16164     case GAME_CTRL_ID_TOUCH_STOP:
16165       if (game_status == GAME_MODE_MAIN)
16166         break;
16167
16168       if (tape.playing)
16169         TapeStop();
16170       else
16171         RequestQuitGame(TRUE);
16172
16173       break;
16174
16175     case GAME_CTRL_ID_PAUSE:
16176     case GAME_CTRL_ID_PAUSE2:
16177     case GAME_CTRL_ID_PANEL_PAUSE:
16178     case GAME_CTRL_ID_TOUCH_PAUSE:
16179       if (network.enabled && game_status == GAME_MODE_PLAYING)
16180       {
16181         if (tape.pausing)
16182           SendToServer_ContinuePlaying();
16183         else
16184           SendToServer_PausePlaying();
16185       }
16186       else
16187         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16188
16189       game_undo_executed = FALSE;
16190
16191       break;
16192
16193     case GAME_CTRL_ID_PLAY:
16194     case GAME_CTRL_ID_PANEL_PLAY:
16195       if (game_status == GAME_MODE_MAIN)
16196       {
16197         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16198       }
16199       else if (tape.pausing)
16200       {
16201         if (network.enabled)
16202           SendToServer_ContinuePlaying();
16203         else
16204           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16205       }
16206       break;
16207
16208     case GAME_CTRL_ID_UNDO:
16209       // Important: When using "save snapshot when collecting an item" mode,
16210       // load last (current) snapshot for first "undo" after pressing "pause"
16211       // (else the last-but-one snapshot would be loaded, because the snapshot
16212       // pointer already points to the last snapshot when pressing "pause",
16213       // which is fine for "every step/move" mode, but not for "every collect")
16214       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16215           !game_undo_executed)
16216         steps--;
16217
16218       game_undo_executed = TRUE;
16219
16220       GameUndo(steps);
16221       break;
16222
16223     case GAME_CTRL_ID_REDO:
16224       GameRedo(steps);
16225       break;
16226
16227     case GAME_CTRL_ID_SAVE:
16228       TapeQuickSave();
16229       break;
16230
16231     case GAME_CTRL_ID_LOAD:
16232       TapeQuickLoad();
16233       break;
16234
16235     case SOUND_CTRL_ID_MUSIC:
16236     case SOUND_CTRL_ID_PANEL_MUSIC:
16237       if (setup.sound_music)
16238       { 
16239         setup.sound_music = FALSE;
16240
16241         FadeMusic();
16242       }
16243       else if (audio.music_available)
16244       { 
16245         setup.sound = setup.sound_music = TRUE;
16246
16247         SetAudioMode(setup.sound);
16248
16249         if (game_status == GAME_MODE_PLAYING)
16250           PlayLevelMusic();
16251       }
16252
16253       RedrawSoundButtonGadget(id);
16254
16255       break;
16256
16257     case SOUND_CTRL_ID_LOOPS:
16258     case SOUND_CTRL_ID_PANEL_LOOPS:
16259       if (setup.sound_loops)
16260         setup.sound_loops = FALSE;
16261       else if (audio.loops_available)
16262       {
16263         setup.sound = setup.sound_loops = TRUE;
16264
16265         SetAudioMode(setup.sound);
16266       }
16267
16268       RedrawSoundButtonGadget(id);
16269
16270       break;
16271
16272     case SOUND_CTRL_ID_SIMPLE:
16273     case SOUND_CTRL_ID_PANEL_SIMPLE:
16274       if (setup.sound_simple)
16275         setup.sound_simple = FALSE;
16276       else if (audio.sound_available)
16277       {
16278         setup.sound = setup.sound_simple = TRUE;
16279
16280         SetAudioMode(setup.sound);
16281       }
16282
16283       RedrawSoundButtonGadget(id);
16284
16285       break;
16286
16287     default:
16288       break;
16289   }
16290 }
16291
16292 static void HandleGameButtons(struct GadgetInfo *gi)
16293 {
16294   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16295 }
16296
16297 void HandleSoundButtonKeys(Key key)
16298 {
16299   if (key == setup.shortcut.sound_simple)
16300     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16301   else if (key == setup.shortcut.sound_loops)
16302     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16303   else if (key == setup.shortcut.sound_music)
16304     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16305 }