added setup option to disable counting score after the game
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static void TestFieldAfterSnapping(int, int, int, int, int);
1123
1124 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1125
1126 // for detection of endless loops, caused by custom element programming
1127 // (using maximal playfield width x 10 is just a rough approximation)
1128 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1129
1130 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1131 {                                                                       \
1132   if (recursion_loop_detected)                                          \
1133     return (rc);                                                        \
1134                                                                         \
1135   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1136   {                                                                     \
1137     recursion_loop_detected = TRUE;                                     \
1138     recursion_loop_element = (e);                                       \
1139   }                                                                     \
1140                                                                         \
1141   recursion_loop_depth++;                                               \
1142 }
1143
1144 #define RECURSION_LOOP_DETECTION_END()                                  \
1145 {                                                                       \
1146   recursion_loop_depth--;                                               \
1147 }
1148
1149 static int recursion_loop_depth;
1150 static boolean recursion_loop_detected;
1151 static boolean recursion_loop_element;
1152
1153 static int map_player_action[MAX_PLAYERS];
1154
1155
1156 // ----------------------------------------------------------------------------
1157 // definition of elements that automatically change to other elements after
1158 // a specified time, eventually calling a function when changing
1159 // ----------------------------------------------------------------------------
1160
1161 // forward declaration for changer functions
1162 static void InitBuggyBase(int, int);
1163 static void WarnBuggyBase(int, int);
1164
1165 static void InitTrap(int, int);
1166 static void ActivateTrap(int, int);
1167 static void ChangeActiveTrap(int, int);
1168
1169 static void InitRobotWheel(int, int);
1170 static void RunRobotWheel(int, int);
1171 static void StopRobotWheel(int, int);
1172
1173 static void InitTimegateWheel(int, int);
1174 static void RunTimegateWheel(int, int);
1175
1176 static void InitMagicBallDelay(int, int);
1177 static void ActivateMagicBall(int, int);
1178
1179 struct ChangingElementInfo
1180 {
1181   int element;
1182   int target_element;
1183   int change_delay;
1184   void (*pre_change_function)(int x, int y);
1185   void (*change_function)(int x, int y);
1186   void (*post_change_function)(int x, int y);
1187 };
1188
1189 static struct ChangingElementInfo change_delay_list[] =
1190 {
1191   {
1192     EL_NUT_BREAKING,
1193     EL_EMERALD,
1194     6,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_PEARL_BREAKING,
1201     EL_EMPTY,
1202     8,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EXIT_OPENING,
1209     EL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_CLOSING,
1217     EL_EXIT_CLOSED,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_STEEL_EXIT_OPENING,
1225     EL_STEEL_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_CLOSING,
1233     EL_STEEL_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EM_EXIT_OPENING,
1241     EL_EM_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_CLOSING,
1249     EL_EMPTY,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_OPENING,
1257     EL_EM_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_CLOSING,
1265     EL_STEELWALL,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_SP_EXIT_OPENING,
1273     EL_SP_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_CLOSING,
1281     EL_SP_EXIT_CLOSED,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SWITCHGATE_OPENING,
1289     EL_SWITCHGATE_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_CLOSING,
1297     EL_SWITCHGATE_CLOSED,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_TIMEGATE_OPENING,
1305     EL_TIMEGATE_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_CLOSING,
1313     EL_TIMEGATE_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319
1320   {
1321     EL_ACID_SPLASH_LEFT,
1322     EL_EMPTY,
1323     8,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_ACID_SPLASH_RIGHT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_SP_BUGGY_BASE,
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     0,
1340     InitBuggyBase,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE_ACTIVATING,
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVE,
1354     EL_SP_BUGGY_BASE,
1355     0,
1356     InitBuggyBase,
1357     WarnBuggyBase,
1358     NULL
1359   },
1360   {
1361     EL_TRAP,
1362     EL_TRAP_ACTIVE,
1363     0,
1364     InitTrap,
1365     NULL,
1366     ActivateTrap
1367   },
1368   {
1369     EL_TRAP_ACTIVE,
1370     EL_TRAP,
1371     31,
1372     NULL,
1373     ChangeActiveTrap,
1374     NULL
1375   },
1376   {
1377     EL_ROBOT_WHEEL_ACTIVE,
1378     EL_ROBOT_WHEEL,
1379     0,
1380     InitRobotWheel,
1381     RunRobotWheel,
1382     StopRobotWheel
1383   },
1384   {
1385     EL_TIMEGATE_SWITCH_ACTIVE,
1386     EL_TIMEGATE_SWITCH,
1387     0,
1388     InitTimegateWheel,
1389     RunTimegateWheel,
1390     NULL
1391   },
1392   {
1393     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394     EL_DC_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     EL_EMC_MAGIC_BALL_ACTIVE,
1403     0,
1404     InitMagicBallDelay,
1405     NULL,
1406     ActivateMagicBall
1407   },
1408   {
1409     EL_EMC_SPRING_BUMPER_ACTIVE,
1410     EL_EMC_SPRING_BUMPER,
1411     8,
1412     NULL,
1413     NULL,
1414     NULL
1415   },
1416   {
1417     EL_DIAGONAL_SHRINKING,
1418     EL_UNDEFINED,
1419     0,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_GROWING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL,
1431   },
1432
1433   {
1434     EL_UNDEFINED,
1435     EL_UNDEFINED,
1436     -1,
1437     NULL,
1438     NULL,
1439     NULL
1440   }
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int push_delay_fixed, push_delay_random;
1447 }
1448 push_delay_list[] =
1449 {
1450   { EL_SPRING,                  0, 0 },
1451   { EL_BALLOON,                 0, 0 },
1452
1453   { EL_SOKOBAN_OBJECT,          2, 0 },
1454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1455   { EL_SATELLITE,               2, 0 },
1456   { EL_SP_DISK_YELLOW,          2, 0 },
1457
1458   { EL_UNDEFINED,               0, 0 },
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int move_stepsize;
1465 }
1466 move_stepsize_list[] =
1467 {
1468   { EL_AMOEBA_DROP,             2 },
1469   { EL_AMOEBA_DROPPING,         2 },
1470   { EL_QUICKSAND_FILLING,       1 },
1471   { EL_QUICKSAND_EMPTYING,      1 },
1472   { EL_QUICKSAND_FAST_FILLING,  2 },
1473   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474   { EL_MAGIC_WALL_FILLING,      2 },
1475   { EL_MAGIC_WALL_EMPTYING,     2 },
1476   { EL_BD_MAGIC_WALL_FILLING,   2 },
1477   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1478   { EL_DC_MAGIC_WALL_FILLING,   2 },
1479   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1480
1481   { EL_UNDEFINED,               0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int count;
1488 }
1489 collect_count_list[] =
1490 {
1491   { EL_EMERALD,                 1 },
1492   { EL_BD_DIAMOND,              1 },
1493   { EL_EMERALD_YELLOW,          1 },
1494   { EL_EMERALD_RED,             1 },
1495   { EL_EMERALD_PURPLE,          1 },
1496   { EL_DIAMOND,                 3 },
1497   { EL_SP_INFOTRON,             1 },
1498   { EL_PEARL,                   5 },
1499   { EL_CRYSTAL,                 8 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int direction;
1508 }
1509 access_direction_list[] =
1510 {
1511   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1514   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1515   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1517   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1518   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1519   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1520   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1521   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1522
1523   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1524   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1525   { EL_SP_PORT_UP,                                                   MV_DOWN },
1526   { EL_SP_PORT_DOWN,                                         MV_UP           },
1527   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1528   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1529   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1531   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1532   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1533   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1534   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1535   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1536   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1537   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1538   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1539   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1540   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1541   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1542
1543   { EL_UNDEFINED,                       MV_NONE                              }
1544 };
1545
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547
1548 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1551                                  IS_JUST_CHANGING(x, y))
1552
1553 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1554
1555 // static variables for playfield scan mode (scanning forward or backward)
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1560
1561 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1562                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1563                                      (y) += playfield_scan_delta_y)     \
1564                                 for ((x) = playfield_scan_start_x;      \
1565                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1566                                      (x) += playfield_scan_delta_x)
1567
1568 #ifdef DEBUG
1569 void DEBUG_SetMaximumDynamite(void)
1570 {
1571   int i;
1572
1573   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575       local_player->inventory_element[local_player->inventory_size++] =
1576         EL_DYNAMITE;
1577 }
1578 #endif
1579
1580 static void InitPlayfieldScanModeVars(void)
1581 {
1582   if (game.use_reverse_scan_direction)
1583   {
1584     playfield_scan_start_x = lev_fieldx - 1;
1585     playfield_scan_start_y = lev_fieldy - 1;
1586
1587     playfield_scan_delta_x = -1;
1588     playfield_scan_delta_y = -1;
1589   }
1590   else
1591   {
1592     playfield_scan_start_x = 0;
1593     playfield_scan_start_y = 0;
1594
1595     playfield_scan_delta_x = 1;
1596     playfield_scan_delta_y = 1;
1597   }
1598 }
1599
1600 static void InitPlayfieldScanMode(int mode)
1601 {
1602   game.use_reverse_scan_direction =
1603     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604
1605   InitPlayfieldScanModeVars();
1606 }
1607
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1609 {
1610   move_stepsize =
1611     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612
1613   // make sure that stepsize value is always a power of 2
1614   move_stepsize = (1 << log_2(move_stepsize));
1615
1616   return TILEX / move_stepsize;
1617 }
1618
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620                                boolean init_game)
1621 {
1622   int player_nr = player->index_nr;
1623   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625
1626   // do no immediately change move delay -- the player might just be moving
1627   player->move_delay_value_next = move_delay;
1628
1629   // information if player can move must be set separately
1630   player->cannot_move = cannot_move;
1631
1632   if (init_game)
1633   {
1634     player->move_delay       = game.initial_move_delay[player_nr];
1635     player->move_delay_value = game.initial_move_delay_value[player_nr];
1636
1637     player->move_delay_value_next = -1;
1638
1639     player->move_delay_reset_counter = 0;
1640   }
1641 }
1642
1643 void GetPlayerConfig(void)
1644 {
1645   GameFrameDelay = setup.game_frame_delay;
1646
1647   if (!audio.sound_available)
1648     setup.sound_simple = FALSE;
1649
1650   if (!audio.loops_available)
1651     setup.sound_loops = FALSE;
1652
1653   if (!audio.music_available)
1654     setup.sound_music = FALSE;
1655
1656   if (!video.fullscreen_available)
1657     setup.fullscreen = FALSE;
1658
1659   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660
1661   SetAudioMode(setup.sound);
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void IncrementSokobanFieldsNeeded(void)
1691 {
1692   if (level.sb_fields_needed)
1693     game.sokoban_fields_still_needed++;
1694 }
1695
1696 static void IncrementSokobanObjectsNeeded(void)
1697 {
1698   if (level.sb_objects_needed)
1699     game.sokoban_objects_still_needed++;
1700 }
1701
1702 static void DecrementSokobanFieldsNeeded(void)
1703 {
1704   if (game.sokoban_fields_still_needed > 0)
1705     game.sokoban_fields_still_needed--;
1706 }
1707
1708 static void DecrementSokobanObjectsNeeded(void)
1709 {
1710   if (game.sokoban_objects_still_needed > 0)
1711     game.sokoban_objects_still_needed--;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Tile[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Tile[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     // ---------- initialize player's last field block delay ------------------
1751
1752     // always start with reliable default value (no adjustment needed)
1753     player->block_delay_adjustment = 0;
1754
1755     // special case 1: in Supaplex, Murphy blocks last field one more frame
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     // special case 2: in game engines before 3.1.1, blocking was different
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!network.enabled || player->connected_network)
1764     {
1765       player->active = TRUE;
1766
1767       // remove potentially duplicate players
1768       if (StorePlayer[jx][jy] == Tile[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Tile[x][y];
1772
1773 #if DEBUG_INIT_PLAYER
1774       Debug("game:init:player", "- player element %d activated",
1775             player->element_nr);
1776       Debug("game:init:player", "  (local player is %d and currently %s)",
1777             local_player->element_nr,
1778             local_player->active ? "active" : "not active");
1779     }
1780 #endif
1781
1782     Tile[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   // always check if player was just killed and should be reanimated
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Tile[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Tile[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1832         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Tile[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Tile[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Tile[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Tile[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Tile[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error("'game_panel_controls' structure corrupted at %d", i);
2153
2154       Fail("this should not happen -- please debug");
2155     }
2156
2157     // force update of game controls after initialization
2158     gpc->value = gpc->last_value = -1;
2159     gpc->frame = gpc->last_frame = -1;
2160     gpc->gfx_frame = -1;
2161
2162     // determine panel value width for later calculation of alignment
2163     if (type == TYPE_INTEGER || type == TYPE_STRING)
2164     {
2165       pos->width = pos->size * getFontWidth(pos->font);
2166       pos->height = getFontHeight(pos->font);
2167     }
2168     else if (type == TYPE_ELEMENT)
2169     {
2170       pos->width = pos->size;
2171       pos->height = pos->size;
2172     }
2173
2174     // fill structure for game panel draw order
2175     gpo->nr = gpc->nr;
2176     gpo->sort_priority = pos->sort_priority;
2177   }
2178
2179   // sort game panel controls according to sort_priority and control number
2180   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2182 }
2183
2184 static void UpdatePlayfieldElementCount(void)
2185 {
2186   boolean use_element_count = FALSE;
2187   int i, j, x, y;
2188
2189   // first check if it is needed at all to calculate playfield element count
2190   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192       use_element_count = TRUE;
2193
2194   if (!use_element_count)
2195     return;
2196
2197   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198     element_info[i].element_count = 0;
2199
2200   SCAN_PLAYFIELD(x, y)
2201   {
2202     element_info[Tile[x][y]].element_count++;
2203   }
2204
2205   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207       if (IS_IN_GROUP(j, i))
2208         element_info[EL_GROUP_START + i].element_count +=
2209           element_info[j].element_count;
2210 }
2211
2212 static void UpdateGameControlValues(void)
2213 {
2214   int i, k;
2215   int time = (game.LevelSolved ?
2216               game.LevelSolved_CountingTime :
2217               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218               game_em.lev->time :
2219               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2220               game_sp.time_played :
2221               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2222               game_mm.energy_left :
2223               game.no_time_limit ? TimePlayed : TimeLeft);
2224   int score = (game.LevelSolved ?
2225                game.LevelSolved_CountingScore :
2226                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227                game_em.lev->score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2229                game_sp.score :
2230                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2231                game_mm.score :
2232                game.score);
2233   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234               game_em.lev->gems_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2236               game_sp.infotrons_still_needed :
2237               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2238               game_mm.kettles_still_needed :
2239               game.gems_still_needed);
2240   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241                      game_em.lev->gems_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243                      game_sp.infotrons_still_needed > 0 :
2244                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2245                      game_mm.kettles_still_needed > 0 ||
2246                      game_mm.lights_still_needed > 0 :
2247                      game.gems_still_needed > 0 ||
2248                      game.sokoban_fields_still_needed > 0 ||
2249                      game.sokoban_objects_still_needed > 0 ||
2250                      game.lights_still_needed > 0);
2251   int health = (game.LevelSolved ?
2252                 game.LevelSolved_CountingHealth :
2253                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2254                 MM_HEALTH(game_mm.laser_overload_value) :
2255                 game.health);
2256   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2257
2258   UpdatePlayfieldElementCount();
2259
2260   // update game panel control values
2261
2262   // used instead of "level_nr" (for network games)
2263   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2264   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2265
2266   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2267   for (i = 0; i < MAX_NUM_KEYS; i++)
2268     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2269   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2270   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2271
2272   if (game.centered_player_nr == -1)
2273   {
2274     for (i = 0; i < MAX_PLAYERS; i++)
2275     {
2276       // only one player in Supaplex game engine
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2278         break;
2279
2280       for (k = 0; k < MAX_NUM_KEYS; k++)
2281       {
2282         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283         {
2284           if (game_em.ply[i]->keys & (1 << k))
2285             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286               get_key_element_from_nr(k);
2287         }
2288         else if (stored_player[i].key[k])
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         getPlayerInventorySize(i);
2295
2296       if (stored_player[i].num_white_keys > 0)
2297         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2298           EL_DC_KEY_WHITE;
2299
2300       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301         stored_player[i].num_white_keys;
2302     }
2303   }
2304   else
2305   {
2306     int player_nr = game.centered_player_nr;
2307
2308     for (k = 0; k < MAX_NUM_KEYS; k++)
2309     {
2310       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2311       {
2312         if (game_em.ply[player_nr]->keys & (1 << k))
2313           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314             get_key_element_from_nr(k);
2315       }
2316       else if (stored_player[player_nr].key[k])
2317         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2318           get_key_element_from_nr(k);
2319     }
2320
2321     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2322       getPlayerInventorySize(player_nr);
2323
2324     if (stored_player[player_nr].num_white_keys > 0)
2325       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2326
2327     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328       stored_player[player_nr].num_white_keys;
2329   }
2330
2331   // re-arrange keys on game panel, if needed or if defined by style settings
2332   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2333   {
2334     int nr = GAME_PANEL_KEY_1 + i;
2335     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2336     struct TextPosInfo *pos = gpc->pos;
2337
2338     // skip check if key is not in the player's inventory
2339     if (gpc->value == EL_EMPTY)
2340       continue;
2341
2342     // check if keys should be arranged on panel from left to right
2343     if (pos->style == STYLE_LEFTMOST_POSITION)
2344     {
2345       // check previous key positions (left from current key)
2346       for (k = 0; k < i; k++)
2347       {
2348         int nr_new = GAME_PANEL_KEY_1 + k;
2349
2350         if (game_panel_controls[nr_new].value == EL_EMPTY)
2351         {
2352           game_panel_controls[nr_new].value = gpc->value;
2353           gpc->value = EL_EMPTY;
2354
2355           break;
2356         }
2357       }
2358     }
2359
2360     // check if "undefined" keys can be placed at some other position
2361     if (pos->x == -1 && pos->y == -1)
2362     {
2363       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2364
2365       // 1st try: display key at the same position as normal or EM keys
2366       if (game_panel_controls[nr_new].value == EL_EMPTY)
2367       {
2368         game_panel_controls[nr_new].value = gpc->value;
2369       }
2370       else
2371       {
2372         // 2nd try: display key at the next free position in the key panel
2373         for (k = 0; k < STD_NUM_KEYS; k++)
2374         {
2375           nr_new = GAME_PANEL_KEY_1 + k;
2376
2377           if (game_panel_controls[nr_new].value == EL_EMPTY)
2378           {
2379             game_panel_controls[nr_new].value = gpc->value;
2380
2381             break;
2382           }
2383         }
2384       }
2385     }
2386   }
2387
2388   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2389   {
2390     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, i);
2392     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2393       get_inventory_element_from_pos(local_player, -i - 1);
2394   }
2395
2396   game_panel_controls[GAME_PANEL_SCORE].value = score;
2397   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2398
2399   game_panel_controls[GAME_PANEL_TIME].value = time;
2400
2401   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2402   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2403   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2404
2405   if (level.time == 0)
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2407   else
2408     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2409
2410   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2411   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2412
2413   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2414
2415   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2416     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2417      EL_EMPTY);
2418   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2419     local_player->shield_normal_time_left;
2420   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2421     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2422      EL_EMPTY);
2423   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2424     local_player->shield_deadly_time_left;
2425
2426   game_panel_controls[GAME_PANEL_EXIT].value =
2427     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2428
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2431   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2432     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2433      EL_EMC_MAGIC_BALL_SWITCH);
2434
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2436     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2437   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2438     game.light_time_left;
2439
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2441     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2442   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2443     game.timegate_time_left;
2444
2445   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2446     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2447
2448   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2449     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2451     game.lenses_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2454     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2455   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2456     game.magnify_time_left;
2457
2458   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2459     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2460      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2461      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2462      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2463      EL_BALLOON_SWITCH_NONE);
2464
2465   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2466     local_player->dynabomb_count;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2468     local_player->dynabomb_size;
2469   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2470     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2471
2472   game_panel_controls[GAME_PANEL_PENGUINS].value =
2473     game.friends_still_needed;
2474
2475   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2476     game.sokoban_objects_still_needed;
2477   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2478     game.sokoban_fields_still_needed;
2479
2480   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2481     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2482
2483   for (i = 0; i < NUM_BELTS; i++)
2484   {
2485     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2486       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2487        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2488     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2489       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2490   }
2491
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2493     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2494   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2495     game.magic_wall_time_left;
2496
2497   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2498     local_player->gravity;
2499
2500   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2501     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2502
2503   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2504     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2505       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2506        game.panel.element[i].id : EL_UNDEFINED);
2507
2508   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2509     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2510       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2511        element_info[game.panel.element_count[i].id].element_count : 0);
2512
2513   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2514     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2515       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2516        element_info[game.panel.ce_score[i].id].collect_score : 0);
2517
2518   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2519     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2520       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2521        element_info[game.panel.ce_score_element[i].id].collect_score :
2522        EL_UNDEFINED);
2523
2524   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2525   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2526   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2527
2528   // update game panel control frames
2529
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531   {
2532     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533
2534     if (gpc->type == TYPE_ELEMENT)
2535     {
2536       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2537       {
2538         int last_anim_random_frame = gfx.anim_random_frame;
2539         int element = gpc->value;
2540         int graphic = el2panelimg(element);
2541         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2542                                sync_random_frame : INIT_GFX_RANDOM());
2543
2544         if (gpc->value != gpc->last_value)
2545         {
2546           gpc->gfx_frame = 0;
2547           gpc->gfx_random = init_gfx_random;
2548         }
2549         else
2550         {
2551           gpc->gfx_frame++;
2552
2553           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2554               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2555             gpc->gfx_random = init_gfx_random;
2556         }
2557
2558         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2559           gfx.anim_random_frame = gpc->gfx_random;
2560
2561         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2562           gpc->gfx_frame = element_info[element].collect_score;
2563
2564         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = last_anim_random_frame;
2568       }
2569     }
2570     else if (gpc->type == TYPE_GRAPHIC)
2571     {
2572       if (gpc->graphic != IMG_UNDEFINED)
2573       {
2574         int last_anim_random_frame = gfx.anim_random_frame;
2575         int graphic = gpc->graphic;
2576         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2577                                sync_random_frame : INIT_GFX_RANDOM());
2578
2579         if (gpc->value != gpc->last_value)
2580         {
2581           gpc->gfx_frame = 0;
2582           gpc->gfx_random = init_gfx_random;
2583         }
2584         else
2585         {
2586           gpc->gfx_frame++;
2587
2588           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2589               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2590             gpc->gfx_random = init_gfx_random;
2591         }
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = gpc->gfx_random;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602   }
2603 }
2604
2605 static void DisplayGameControlValues(void)
2606 {
2607   boolean redraw_panel = FALSE;
2608   int i;
2609
2610   for (i = 0; game_panel_controls[i].nr != -1; i++)
2611   {
2612     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2613
2614     if (PANEL_DEACTIVATED(gpc->pos))
2615       continue;
2616
2617     if (gpc->value == gpc->last_value &&
2618         gpc->frame == gpc->last_frame)
2619       continue;
2620
2621     redraw_panel = TRUE;
2622   }
2623
2624   if (!redraw_panel)
2625     return;
2626
2627   // copy default game door content to main double buffer
2628
2629   // !!! CHECK AGAIN !!!
2630   SetPanelBackground();
2631   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2632   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2633
2634   // redraw game control buttons
2635   RedrawGameButtons();
2636
2637   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2638
2639   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2640   {
2641     int nr = game_panel_order[i].nr;
2642     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2643     struct TextPosInfo *pos = gpc->pos;
2644     int type = gpc->type;
2645     int value = gpc->value;
2646     int frame = gpc->frame;
2647     int size = pos->size;
2648     int font = pos->font;
2649     boolean draw_masked = pos->draw_masked;
2650     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2651
2652     if (PANEL_DEACTIVATED(pos))
2653       continue;
2654
2655     if (pos->class == get_hash_from_key("extra_panel_items") &&
2656         !setup.prefer_extra_panel_items)
2657       continue;
2658
2659     gpc->last_value = value;
2660     gpc->last_frame = frame;
2661
2662     if (type == TYPE_INTEGER)
2663     {
2664       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2665           nr == GAME_PANEL_TIME)
2666       {
2667         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2668
2669         if (use_dynamic_size)           // use dynamic number of digits
2670         {
2671           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2672           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2673           int size2 = size1 + 1;
2674           int font1 = pos->font;
2675           int font2 = pos->font_alt;
2676
2677           size = (value < value_change ? size1 : size2);
2678           font = (value < value_change ? font1 : font2);
2679         }
2680       }
2681
2682       // correct text size if "digits" is zero or less
2683       if (size <= 0)
2684         size = strlen(int2str(value, size));
2685
2686       // dynamically correct text alignment
2687       pos->width = size * getFontWidth(font);
2688
2689       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2690                   int2str(value, size), font, mask_mode);
2691     }
2692     else if (type == TYPE_ELEMENT)
2693     {
2694       int element, graphic;
2695       Bitmap *src_bitmap;
2696       int src_x, src_y;
2697       int width, height;
2698       int dst_x = PANEL_XPOS(pos);
2699       int dst_y = PANEL_YPOS(pos);
2700
2701       if (value != EL_UNDEFINED && value != EL_EMPTY)
2702       {
2703         element = value;
2704         graphic = el2panelimg(value);
2705
2706 #if 0
2707         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2708               element, EL_NAME(element), size);
2709 #endif
2710
2711         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2712           size = TILESIZE;
2713
2714         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2715                               &src_x, &src_y);
2716
2717         width  = graphic_info[graphic].width  * size / TILESIZE;
2718         height = graphic_info[graphic].height * size / TILESIZE;
2719
2720         if (draw_masked)
2721           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2722                            dst_x, dst_y);
2723         else
2724           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2725                      dst_x, dst_y);
2726       }
2727     }
2728     else if (type == TYPE_GRAPHIC)
2729     {
2730       int graphic        = gpc->graphic;
2731       int graphic_active = gpc->graphic_active;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2738                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2739
2740       if (graphic != IMG_UNDEFINED && !skip)
2741       {
2742         if (pos->style == STYLE_REVERSE)
2743           value = 100 - value;
2744
2745         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2746
2747         if (pos->direction & MV_HORIZONTAL)
2748         {
2749           width  = graphic_info[graphic_active].width * value / 100;
2750           height = graphic_info[graphic_active].height;
2751
2752           if (pos->direction == MV_LEFT)
2753           {
2754             src_x += graphic_info[graphic_active].width - width;
2755             dst_x += graphic_info[graphic_active].width - width;
2756           }
2757         }
2758         else
2759         {
2760           width  = graphic_info[graphic_active].width;
2761           height = graphic_info[graphic_active].height * value / 100;
2762
2763           if (pos->direction == MV_UP)
2764           {
2765             src_y += graphic_info[graphic_active].height - height;
2766             dst_y += graphic_info[graphic_active].height - height;
2767           }
2768         }
2769
2770         if (draw_masked)
2771           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2772                            dst_x, dst_y);
2773         else
2774           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2775                      dst_x, dst_y);
2776
2777         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2778
2779         if (pos->direction & MV_HORIZONTAL)
2780         {
2781           if (pos->direction == MV_RIGHT)
2782           {
2783             src_x += width;
2784             dst_x += width;
2785           }
2786           else
2787           {
2788             dst_x = PANEL_XPOS(pos);
2789           }
2790
2791           width = graphic_info[graphic].width - width;
2792         }
2793         else
2794         {
2795           if (pos->direction == MV_DOWN)
2796           {
2797             src_y += height;
2798             dst_y += height;
2799           }
2800           else
2801           {
2802             dst_y = PANEL_YPOS(pos);
2803           }
2804
2805           height = graphic_info[graphic].height - height;
2806         }
2807
2808         if (draw_masked)
2809           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2810                            dst_x, dst_y);
2811         else
2812           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2813                      dst_x, dst_y);
2814       }
2815     }
2816     else if (type == TYPE_STRING)
2817     {
2818       boolean active = (value != 0);
2819       char *state_normal = "off";
2820       char *state_active = "on";
2821       char *state = (active ? state_active : state_normal);
2822       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2823                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2824                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2825                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2826
2827       if (nr == GAME_PANEL_GRAVITY_STATE)
2828       {
2829         int font1 = pos->font;          // (used for normal state)
2830         int font2 = pos->font_alt;      // (used for active state)
2831
2832         font = (active ? font2 : font1);
2833       }
2834
2835       if (s != NULL)
2836       {
2837         char *s_cut;
2838
2839         if (size <= 0)
2840         {
2841           // don't truncate output if "chars" is zero or less
2842           size = strlen(s);
2843
2844           // dynamically correct text alignment
2845           pos->width = size * getFontWidth(font);
2846         }
2847
2848         s_cut = getStringCopyN(s, size);
2849
2850         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2851                     s_cut, font, mask_mode);
2852
2853         free(s_cut);
2854       }
2855     }
2856
2857     redraw_mask |= REDRAW_DOOR_1;
2858   }
2859
2860   SetGameStatus(GAME_MODE_PLAYING);
2861 }
2862
2863 void UpdateAndDisplayGameControlValues(void)
2864 {
2865   if (tape.deactivate_display)
2866     return;
2867
2868   UpdateGameControlValues();
2869   DisplayGameControlValues();
2870 }
2871
2872 void UpdateGameDoorValues(void)
2873 {
2874   UpdateGameControlValues();
2875 }
2876
2877 void DrawGameDoorValues(void)
2878 {
2879   DisplayGameControlValues();
2880 }
2881
2882
2883 // ============================================================================
2884 // InitGameEngine()
2885 // ----------------------------------------------------------------------------
2886 // initialize game engine due to level / tape version number
2887 // ============================================================================
2888
2889 static void InitGameEngine(void)
2890 {
2891   int i, j, k, l, x, y;
2892
2893   // set game engine from tape file when re-playing, else from level file
2894   game.engine_version = (tape.playing ? tape.engine_version :
2895                          level.game_version);
2896
2897   // set single or multi-player game mode (needed for re-playing tapes)
2898   game.team_mode = setup.team_mode;
2899
2900   if (tape.playing)
2901   {
2902     int num_players = 0;
2903
2904     for (i = 0; i < MAX_PLAYERS; i++)
2905       if (tape.player_participates[i])
2906         num_players++;
2907
2908     // multi-player tapes contain input data for more than one player
2909     game.team_mode = (num_players > 1);
2910   }
2911
2912 #if 0
2913   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2914         level.game_version);
2915   Debug("game:init:level", "          tape.file_version   == %06d",
2916         tape.file_version);
2917   Debug("game:init:level", "          tape.game_version   == %06d",
2918         tape.game_version);
2919   Debug("game:init:level", "          tape.engine_version == %06d",
2920         tape.engine_version);
2921   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2922         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2923 #endif
2924
2925   // --------------------------------------------------------------------------
2926   // set flags for bugs and changes according to active game engine version
2927   // --------------------------------------------------------------------------
2928
2929   /*
2930     Summary of bugfix:
2931     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2932
2933     Bug was introduced in version:
2934     2.0.1
2935
2936     Bug was fixed in version:
2937     4.2.0.0
2938
2939     Description:
2940     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2941     but the property "can fall" was missing, which caused some levels to be
2942     unsolvable. This was fixed in version 4.2.0.0.
2943
2944     Affected levels/tapes:
2945     An example for a tape that was fixed by this bugfix is tape 029 from the
2946     level set "rnd_sam_bateman".
2947     The wrong behaviour will still be used for all levels or tapes that were
2948     created/recorded with it. An example for this is tape 023 from the level
2949     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2950   */
2951
2952   boolean use_amoeba_dropping_cannot_fall_bug =
2953     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2954       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2955      (tape.playing &&
2956       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2957       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2958
2959   /*
2960     Summary of bugfix/change:
2961     Fixed move speed of elements entering or leaving magic wall.
2962
2963     Fixed/changed in version:
2964     2.0.1
2965
2966     Description:
2967     Before 2.0.1, move speed of elements entering or leaving magic wall was
2968     twice as fast as it is now.
2969     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2970
2971     Affected levels/tapes:
2972     The first condition is generally needed for all levels/tapes before version
2973     2.0.1, which might use the old behaviour before it was changed; known tapes
2974     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2975     The second condition is an exception from the above case and is needed for
2976     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2977     above, but before it was known that this change would break tapes like the
2978     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2979     although the engine version while recording maybe was before 2.0.1. There
2980     are a lot of tapes that are affected by this exception, like tape 006 from
2981     the level set "rnd_conor_mancone".
2982   */
2983
2984   boolean use_old_move_stepsize_for_magic_wall =
2985     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2986      !(tape.playing &&
2987        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2988        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2989
2990   /*
2991     Summary of bugfix/change:
2992     Fixed handling for custom elements that change when pushed by the player.
2993
2994     Fixed/changed in version:
2995     3.1.0
2996
2997     Description:
2998     Before 3.1.0, custom elements that "change when pushing" changed directly
2999     after the player started pushing them (until then handled in "DigField()").
3000     Since 3.1.0, these custom elements are not changed until the "pushing"
3001     move of the element is finished (now handled in "ContinueMoving()").
3002
3003     Affected levels/tapes:
3004     The first condition is generally needed for all levels/tapes before version
3005     3.1.0, which might use the old behaviour before it was changed; known tapes
3006     that are affected are some tapes from the level set "Walpurgis Gardens" by
3007     Jamie Cullen.
3008     The second condition is an exception from the above case and is needed for
3009     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3010     above (including some development versions of 3.1.0), but before it was
3011     known that this change would break tapes like the above and was fixed in
3012     3.1.1, so that the changed behaviour was active although the engine version
3013     while recording maybe was before 3.1.0. There is at least one tape that is
3014     affected by this exception, which is the tape for the one-level set "Bug
3015     Machine" by Juergen Bonhagen.
3016   */
3017
3018   game.use_change_when_pushing_bug =
3019     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3020      !(tape.playing &&
3021        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3022        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3023
3024   /*
3025     Summary of bugfix/change:
3026     Fixed handling for blocking the field the player leaves when moving.
3027
3028     Fixed/changed in version:
3029     3.1.1
3030
3031     Description:
3032     Before 3.1.1, when "block last field when moving" was enabled, the field
3033     the player is leaving when moving was blocked for the time of the move,
3034     and was directly unblocked afterwards. This resulted in the last field
3035     being blocked for exactly one less than the number of frames of one player
3036     move. Additionally, even when blocking was disabled, the last field was
3037     blocked for exactly one frame.
3038     Since 3.1.1, due to changes in player movement handling, the last field
3039     is not blocked at all when blocking is disabled. When blocking is enabled,
3040     the last field is blocked for exactly the number of frames of one player
3041     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3042     last field is blocked for exactly one more than the number of frames of
3043     one player move.
3044
3045     Affected levels/tapes:
3046     (!!! yet to be determined -- probably many !!!)
3047   */
3048
3049   game.use_block_last_field_bug =
3050     (game.engine_version < VERSION_IDENT(3,1,1,0));
3051
3052   /* various special flags and settings for native Emerald Mine game engine */
3053
3054   game_em.use_single_button =
3055     (game.engine_version > VERSION_IDENT(4,0,0,2));
3056
3057   game_em.use_snap_key_bug =
3058     (game.engine_version < VERSION_IDENT(4,0,1,0));
3059
3060   game_em.use_random_bug =
3061     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3062
3063   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3064
3065   game_em.use_old_explosions            = use_old_em_engine;
3066   game_em.use_old_android               = use_old_em_engine;
3067   game_em.use_old_push_elements         = use_old_em_engine;
3068   game_em.use_old_push_into_acid        = use_old_em_engine;
3069
3070   game_em.use_wrap_around               = !use_old_em_engine;
3071
3072   // --------------------------------------------------------------------------
3073
3074   // set maximal allowed number of custom element changes per game frame
3075   game.max_num_changes_per_frame = 1;
3076
3077   // default scan direction: scan playfield from top/left to bottom/right
3078   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3079
3080   // dynamically adjust element properties according to game engine version
3081   InitElementPropertiesEngine(game.engine_version);
3082
3083   // ---------- initialize special element properties -------------------------
3084
3085   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3086   if (use_amoeba_dropping_cannot_fall_bug)
3087     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3088
3089   // ---------- initialize player's initial move delay ------------------------
3090
3091   // dynamically adjust player properties according to level information
3092   for (i = 0; i < MAX_PLAYERS; i++)
3093     game.initial_move_delay_value[i] =
3094       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3095
3096   // dynamically adjust player properties according to game engine version
3097   for (i = 0; i < MAX_PLAYERS; i++)
3098     game.initial_move_delay[i] =
3099       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3100        game.initial_move_delay_value[i] : 0);
3101
3102   // ---------- initialize player's initial push delay ------------------------
3103
3104   // dynamically adjust player properties according to game engine version
3105   game.initial_push_delay_value =
3106     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3107
3108   // ---------- initialize changing elements ----------------------------------
3109
3110   // initialize changing elements information
3111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3112   {
3113     struct ElementInfo *ei = &element_info[i];
3114
3115     // this pointer might have been changed in the level editor
3116     ei->change = &ei->change_page[0];
3117
3118     if (!IS_CUSTOM_ELEMENT(i))
3119     {
3120       ei->change->target_element = EL_EMPTY_SPACE;
3121       ei->change->delay_fixed = 0;
3122       ei->change->delay_random = 0;
3123       ei->change->delay_frames = 1;
3124     }
3125
3126     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3127     {
3128       ei->has_change_event[j] = FALSE;
3129
3130       ei->event_page_nr[j] = 0;
3131       ei->event_page[j] = &ei->change_page[0];
3132     }
3133   }
3134
3135   // add changing elements from pre-defined list
3136   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3137   {
3138     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3139     struct ElementInfo *ei = &element_info[ch_delay->element];
3140
3141     ei->change->target_element       = ch_delay->target_element;
3142     ei->change->delay_fixed          = ch_delay->change_delay;
3143
3144     ei->change->pre_change_function  = ch_delay->pre_change_function;
3145     ei->change->change_function      = ch_delay->change_function;
3146     ei->change->post_change_function = ch_delay->post_change_function;
3147
3148     ei->change->can_change = TRUE;
3149     ei->change->can_change_or_has_action = TRUE;
3150
3151     ei->has_change_event[CE_DELAY] = TRUE;
3152
3153     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3154     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3155   }
3156
3157   // ---------- initialize internal run-time variables ------------------------
3158
3159   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3160   {
3161     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3162
3163     for (j = 0; j < ei->num_change_pages; j++)
3164     {
3165       ei->change_page[j].can_change_or_has_action =
3166         (ei->change_page[j].can_change |
3167          ei->change_page[j].has_action);
3168     }
3169   }
3170
3171   // add change events from custom element configuration
3172   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3173   {
3174     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3175
3176     for (j = 0; j < ei->num_change_pages; j++)
3177     {
3178       if (!ei->change_page[j].can_change_or_has_action)
3179         continue;
3180
3181       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3182       {
3183         // only add event page for the first page found with this event
3184         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3185         {
3186           ei->has_change_event[k] = TRUE;
3187
3188           ei->event_page_nr[k] = j;
3189           ei->event_page[k] = &ei->change_page[j];
3190         }
3191       }
3192     }
3193   }
3194
3195   // ---------- initialize reference elements in change conditions ------------
3196
3197   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3198   {
3199     int element = EL_CUSTOM_START + i;
3200     struct ElementInfo *ei = &element_info[element];
3201
3202     for (j = 0; j < ei->num_change_pages; j++)
3203     {
3204       int trigger_element = ei->change_page[j].initial_trigger_element;
3205
3206       if (trigger_element >= EL_PREV_CE_8 &&
3207           trigger_element <= EL_NEXT_CE_8)
3208         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3209
3210       ei->change_page[j].trigger_element = trigger_element;
3211     }
3212   }
3213
3214   // ---------- initialize run-time trigger player and element ----------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3219
3220     for (j = 0; j < ei->num_change_pages; j++)
3221     {
3222       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3223       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3224       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3225       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3226       ei->change_page[j].actual_trigger_ce_value = 0;
3227       ei->change_page[j].actual_trigger_ce_score = 0;
3228     }
3229   }
3230
3231   // ---------- initialize trigger events -------------------------------------
3232
3233   // initialize trigger events information
3234   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3235     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3236       trigger_events[i][j] = FALSE;
3237
3238   // add trigger events from element change event properties
3239   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3240   {
3241     struct ElementInfo *ei = &element_info[i];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       if (!ei->change_page[j].can_change_or_has_action)
3246         continue;
3247
3248       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3249       {
3250         int trigger_element = ei->change_page[j].trigger_element;
3251
3252         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3253         {
3254           if (ei->change_page[j].has_event[k])
3255           {
3256             if (IS_GROUP_ELEMENT(trigger_element))
3257             {
3258               struct ElementGroupInfo *group =
3259                 element_info[trigger_element].group;
3260
3261               for (l = 0; l < group->num_elements_resolved; l++)
3262                 trigger_events[group->element_resolved[l]][k] = TRUE;
3263             }
3264             else if (trigger_element == EL_ANY_ELEMENT)
3265               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3266                 trigger_events[l][k] = TRUE;
3267             else
3268               trigger_events[trigger_element][k] = TRUE;
3269           }
3270         }
3271       }
3272     }
3273   }
3274
3275   // ---------- initialize push delay -----------------------------------------
3276
3277   // initialize push delay values to default
3278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3279   {
3280     if (!IS_CUSTOM_ELEMENT(i))
3281     {
3282       // set default push delay values (corrected since version 3.0.7-1)
3283       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3284       {
3285         element_info[i].push_delay_fixed = 2;
3286         element_info[i].push_delay_random = 8;
3287       }
3288       else
3289       {
3290         element_info[i].push_delay_fixed = 8;
3291         element_info[i].push_delay_random = 8;
3292       }
3293     }
3294   }
3295
3296   // set push delay value for certain elements from pre-defined list
3297   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3298   {
3299     int e = push_delay_list[i].element;
3300
3301     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3302     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3303   }
3304
3305   // set push delay value for Supaplex elements for newer engine versions
3306   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3307   {
3308     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309     {
3310       if (IS_SP_ELEMENT(i))
3311       {
3312         // set SP push delay to just enough to push under a falling zonk
3313         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3314
3315         element_info[i].push_delay_fixed  = delay;
3316         element_info[i].push_delay_random = 0;
3317       }
3318     }
3319   }
3320
3321   // ---------- initialize move stepsize --------------------------------------
3322
3323   // initialize move stepsize values to default
3324   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3325     if (!IS_CUSTOM_ELEMENT(i))
3326       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3327
3328   // set move stepsize value for certain elements from pre-defined list
3329   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3330   {
3331     int e = move_stepsize_list[i].element;
3332
3333     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3334
3335     // set move stepsize value for certain elements for older engine versions
3336     if (use_old_move_stepsize_for_magic_wall)
3337     {
3338       if (e == EL_MAGIC_WALL_FILLING ||
3339           e == EL_MAGIC_WALL_EMPTYING ||
3340           e == EL_BD_MAGIC_WALL_FILLING ||
3341           e == EL_BD_MAGIC_WALL_EMPTYING)
3342         element_info[e].move_stepsize *= 2;
3343     }
3344   }
3345
3346   // ---------- initialize collect score --------------------------------------
3347
3348   // initialize collect score values for custom elements from initial value
3349   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     if (IS_CUSTOM_ELEMENT(i))
3351       element_info[i].collect_score = element_info[i].collect_score_initial;
3352
3353   // ---------- initialize collect count --------------------------------------
3354
3355   // initialize collect count values for non-custom elements
3356   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3357     if (!IS_CUSTOM_ELEMENT(i))
3358       element_info[i].collect_count_initial = 0;
3359
3360   // add collect count values for all elements from pre-defined list
3361   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3362     element_info[collect_count_list[i].element].collect_count_initial =
3363       collect_count_list[i].count;
3364
3365   // ---------- initialize access direction -----------------------------------
3366
3367   // initialize access direction values to default (access from every side)
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (!IS_CUSTOM_ELEMENT(i))
3370       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3371
3372   // set access direction value for certain elements from pre-defined list
3373   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3374     element_info[access_direction_list[i].element].access_direction =
3375       access_direction_list[i].direction;
3376
3377   // ---------- initialize explosion content ----------------------------------
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379   {
3380     if (IS_CUSTOM_ELEMENT(i))
3381       continue;
3382
3383     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3384     {
3385       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3386
3387       element_info[i].content.e[x][y] =
3388         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3389          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3390          i == EL_PLAYER_3 ? EL_EMERALD :
3391          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3392          i == EL_MOLE ? EL_EMERALD_RED :
3393          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3394          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3395          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3396          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3397          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3398          i == EL_WALL_EMERALD ? EL_EMERALD :
3399          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3400          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3401          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3402          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3403          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3404          i == EL_WALL_PEARL ? EL_PEARL :
3405          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3406          EL_EMPTY);
3407     }
3408   }
3409
3410   // ---------- initialize recursion detection --------------------------------
3411   recursion_loop_depth = 0;
3412   recursion_loop_detected = FALSE;
3413   recursion_loop_element = EL_UNDEFINED;
3414
3415   // ---------- initialize graphics engine ------------------------------------
3416   game.scroll_delay_value =
3417     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3418      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3419      !setup.forced_scroll_delay           ? 0 :
3420      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3421   game.scroll_delay_value =
3422     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3423
3424   // ---------- initialize game engine snapshots ------------------------------
3425   for (i = 0; i < MAX_PLAYERS; i++)
3426     game.snapshot.last_action[i] = 0;
3427   game.snapshot.changed_action = FALSE;
3428   game.snapshot.collected_item = FALSE;
3429   game.snapshot.mode =
3430     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3431      SNAPSHOT_MODE_EVERY_STEP :
3432      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3433      SNAPSHOT_MODE_EVERY_MOVE :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3435      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3436   game.snapshot.save_snapshot = FALSE;
3437
3438   // ---------- initialize level time for Supaplex engine ---------------------
3439   // Supaplex levels with time limit currently unsupported -- should be added
3440   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3441     level.time = 0;
3442
3443   // ---------- initialize flags for handling game actions --------------------
3444
3445   // set flags for game actions to default values
3446   game.use_key_actions = TRUE;
3447   game.use_mouse_actions = FALSE;
3448
3449   // when using Mirror Magic game engine, handle mouse events only
3450   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3451   {
3452     game.use_key_actions = FALSE;
3453     game.use_mouse_actions = TRUE;
3454   }
3455
3456   // check for custom elements with mouse click events
3457   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3458   {
3459     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3460     {
3461       int element = EL_CUSTOM_START + i;
3462
3463       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3464           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3465           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3466           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3467         game.use_mouse_actions = TRUE;
3468     }
3469   }
3470 }
3471
3472 static int get_num_special_action(int element, int action_first,
3473                                   int action_last)
3474 {
3475   int num_special_action = 0;
3476   int i, j;
3477
3478   for (i = action_first; i <= action_last; i++)
3479   {
3480     boolean found = FALSE;
3481
3482     for (j = 0; j < NUM_DIRECTIONS; j++)
3483       if (el_act_dir2img(element, i, j) !=
3484           el_act_dir2img(element, ACTION_DEFAULT, j))
3485         found = TRUE;
3486
3487     if (found)
3488       num_special_action++;
3489     else
3490       break;
3491   }
3492
3493   return num_special_action;
3494 }
3495
3496
3497 // ============================================================================
3498 // InitGame()
3499 // ----------------------------------------------------------------------------
3500 // initialize and start new game
3501 // ============================================================================
3502
3503 #if DEBUG_INIT_PLAYER
3504 static void DebugPrintPlayerStatus(char *message)
3505 {
3506   int i;
3507
3508   if (!options.debug)
3509     return;
3510
3511   Debug("game:init:player", "%s:", message);
3512
3513   for (i = 0; i < MAX_PLAYERS; i++)
3514   {
3515     struct PlayerInfo *player = &stored_player[i];
3516
3517     Debug("game:init:player",
3518           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3519           i + 1,
3520           player->present,
3521           player->connected,
3522           player->connected_locally,
3523           player->connected_network,
3524           player->active,
3525           (local_player == player ? " (local player)" : ""));
3526   }
3527 }
3528 #endif
3529
3530 void InitGame(void)
3531 {
3532   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3533   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3534   int fade_mask = REDRAW_FIELD;
3535
3536   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3537   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3538   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3539   int initial_move_dir = MV_DOWN;
3540   int i, j, x, y;
3541
3542   // required here to update video display before fading (FIX THIS)
3543   DrawMaskedBorder(REDRAW_DOOR_2);
3544
3545   if (!game.restart_level)
3546     CloseDoor(DOOR_CLOSE_1);
3547
3548   SetGameStatus(GAME_MODE_PLAYING);
3549
3550   if (level_editor_test_game)
3551     FadeSkipNextFadeOut();
3552   else
3553     FadeSetEnterScreen();
3554
3555   if (CheckFadeAll())
3556     fade_mask = REDRAW_ALL;
3557
3558   FadeLevelSoundsAndMusic();
3559
3560   ExpireSoundLoops(TRUE);
3561
3562   FadeOut(fade_mask);
3563
3564   if (level_editor_test_game)
3565     FadeSkipNextFadeIn();
3566
3567   // needed if different viewport properties defined for playing
3568   ChangeViewportPropertiesIfNeeded();
3569
3570   ClearField();
3571
3572   DrawCompleteVideoDisplay();
3573
3574   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3575
3576   InitGameEngine();
3577   InitGameControlValues();
3578
3579   if (tape.recording)
3580   {
3581     // initialize tape actions from game when recording tape
3582     tape.use_key_actions   = game.use_key_actions;
3583     tape.use_mouse_actions = game.use_mouse_actions;
3584
3585     // initialize visible playfield size when recording tape (for team mode)
3586     tape.scr_fieldx = SCR_FIELDX;
3587     tape.scr_fieldy = SCR_FIELDY;
3588   }
3589
3590   // don't play tapes over network
3591   network_playing = (network.enabled && !tape.playing);
3592
3593   for (i = 0; i < MAX_PLAYERS; i++)
3594   {
3595     struct PlayerInfo *player = &stored_player[i];
3596
3597     player->index_nr = i;
3598     player->index_bit = (1 << i);
3599     player->element_nr = EL_PLAYER_1 + i;
3600
3601     player->present = FALSE;
3602     player->active = FALSE;
3603     player->mapped = FALSE;
3604
3605     player->killed = FALSE;
3606     player->reanimated = FALSE;
3607     player->buried = FALSE;
3608
3609     player->action = 0;
3610     player->effective_action = 0;
3611     player->programmed_action = 0;
3612     player->snap_action = 0;
3613
3614     player->mouse_action.lx = 0;
3615     player->mouse_action.ly = 0;
3616     player->mouse_action.button = 0;
3617     player->mouse_action.button_hint = 0;
3618
3619     player->effective_mouse_action.lx = 0;
3620     player->effective_mouse_action.ly = 0;
3621     player->effective_mouse_action.button = 0;
3622     player->effective_mouse_action.button_hint = 0;
3623
3624     for (j = 0; j < MAX_NUM_KEYS; j++)
3625       player->key[j] = FALSE;
3626
3627     player->num_white_keys = 0;
3628
3629     player->dynabomb_count = 0;
3630     player->dynabomb_size = 1;
3631     player->dynabombs_left = 0;
3632     player->dynabomb_xl = FALSE;
3633
3634     player->MovDir = initial_move_dir;
3635     player->MovPos = 0;
3636     player->GfxPos = 0;
3637     player->GfxDir = initial_move_dir;
3638     player->GfxAction = ACTION_DEFAULT;
3639     player->Frame = 0;
3640     player->StepFrame = 0;
3641
3642     player->initial_element = player->element_nr;
3643     player->artwork_element =
3644       (level.use_artwork_element[i] ? level.artwork_element[i] :
3645        player->element_nr);
3646     player->use_murphy = FALSE;
3647
3648     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3649     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3650
3651     player->gravity = level.initial_player_gravity[i];
3652
3653     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3654
3655     player->actual_frame_counter = 0;
3656
3657     player->step_counter = 0;
3658
3659     player->last_move_dir = initial_move_dir;
3660
3661     player->is_active = FALSE;
3662
3663     player->is_waiting = FALSE;
3664     player->is_moving = FALSE;
3665     player->is_auto_moving = FALSE;
3666     player->is_digging = FALSE;
3667     player->is_snapping = FALSE;
3668     player->is_collecting = FALSE;
3669     player->is_pushing = FALSE;
3670     player->is_switching = FALSE;
3671     player->is_dropping = FALSE;
3672     player->is_dropping_pressed = FALSE;
3673
3674     player->is_bored = FALSE;
3675     player->is_sleeping = FALSE;
3676
3677     player->was_waiting = TRUE;
3678     player->was_moving = FALSE;
3679     player->was_snapping = FALSE;
3680     player->was_dropping = FALSE;
3681
3682     player->force_dropping = FALSE;
3683
3684     player->frame_counter_bored = -1;
3685     player->frame_counter_sleeping = -1;
3686
3687     player->anim_delay_counter = 0;
3688     player->post_delay_counter = 0;
3689
3690     player->dir_waiting = initial_move_dir;
3691     player->action_waiting = ACTION_DEFAULT;
3692     player->last_action_waiting = ACTION_DEFAULT;
3693     player->special_action_bored = ACTION_DEFAULT;
3694     player->special_action_sleeping = ACTION_DEFAULT;
3695
3696     player->switch_x = -1;
3697     player->switch_y = -1;
3698
3699     player->drop_x = -1;
3700     player->drop_y = -1;
3701
3702     player->show_envelope = 0;
3703
3704     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3705
3706     player->push_delay       = -1;      // initialized when pushing starts
3707     player->push_delay_value = game.initial_push_delay_value;
3708
3709     player->drop_delay = 0;
3710     player->drop_pressed_delay = 0;
3711
3712     player->last_jx = -1;
3713     player->last_jy = -1;
3714     player->jx = -1;
3715     player->jy = -1;
3716
3717     player->shield_normal_time_left = 0;
3718     player->shield_deadly_time_left = 0;
3719
3720     player->last_removed_element = EL_UNDEFINED;
3721
3722     player->inventory_infinite_element = EL_UNDEFINED;
3723     player->inventory_size = 0;
3724
3725     if (level.use_initial_inventory[i])
3726     {
3727       for (j = 0; j < level.initial_inventory_size[i]; j++)
3728       {
3729         int element = level.initial_inventory_content[i][j];
3730         int collect_count = element_info[element].collect_count_initial;
3731         int k;
3732
3733         if (!IS_CUSTOM_ELEMENT(element))
3734           collect_count = 1;
3735
3736         if (collect_count == 0)
3737           player->inventory_infinite_element = element;
3738         else
3739           for (k = 0; k < collect_count; k++)
3740             if (player->inventory_size < MAX_INVENTORY_SIZE)
3741               player->inventory_element[player->inventory_size++] = element;
3742       }
3743     }
3744
3745     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3746     SnapField(player, 0, 0);
3747
3748     map_player_action[i] = i;
3749   }
3750
3751   network_player_action_received = FALSE;
3752
3753   // initial null action
3754   if (network_playing)
3755     SendToServer_MovePlayer(MV_NONE);
3756
3757   FrameCounter = 0;
3758   TimeFrames = 0;
3759   TimePlayed = 0;
3760   TimeLeft = level.time;
3761   TapeTime = 0;
3762
3763   ScreenMovDir = MV_NONE;
3764   ScreenMovPos = 0;
3765   ScreenGfxPos = 0;
3766
3767   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3768
3769   game.robot_wheel_x = -1;
3770   game.robot_wheel_y = -1;
3771
3772   game.exit_x = -1;
3773   game.exit_y = -1;
3774
3775   game.all_players_gone = FALSE;
3776
3777   game.LevelSolved = FALSE;
3778   game.GameOver = FALSE;
3779
3780   game.GamePlayed = !tape.playing;
3781
3782   game.LevelSolved_GameWon = FALSE;
3783   game.LevelSolved_GameEnd = FALSE;
3784   game.LevelSolved_SaveTape = FALSE;
3785   game.LevelSolved_SaveScore = FALSE;
3786
3787   game.LevelSolved_CountingTime = 0;
3788   game.LevelSolved_CountingScore = 0;
3789   game.LevelSolved_CountingHealth = 0;
3790
3791   game.panel.active = TRUE;
3792
3793   game.no_time_limit = (level.time == 0);
3794
3795   game.yamyam_content_nr = 0;
3796   game.robot_wheel_active = FALSE;
3797   game.magic_wall_active = FALSE;
3798   game.magic_wall_time_left = 0;
3799   game.light_time_left = 0;
3800   game.timegate_time_left = 0;
3801   game.switchgate_pos = 0;
3802   game.wind_direction = level.wind_direction_initial;
3803
3804   game.score = 0;
3805   game.score_final = 0;
3806
3807   game.health = MAX_HEALTH;
3808   game.health_final = MAX_HEALTH;
3809
3810   game.gems_still_needed = level.gems_needed;
3811   game.sokoban_fields_still_needed = 0;
3812   game.sokoban_objects_still_needed = 0;
3813   game.lights_still_needed = 0;
3814   game.players_still_needed = 0;
3815   game.friends_still_needed = 0;
3816
3817   game.lenses_time_left = 0;
3818   game.magnify_time_left = 0;
3819
3820   game.ball_active = level.ball_active_initial;
3821   game.ball_content_nr = 0;
3822
3823   game.explosions_delayed = TRUE;
3824
3825   game.envelope_active = FALSE;
3826
3827   for (i = 0; i < NUM_BELTS; i++)
3828   {
3829     game.belt_dir[i] = MV_NONE;
3830     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3831   }
3832
3833   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3834     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3835
3836 #if DEBUG_INIT_PLAYER
3837   DebugPrintPlayerStatus("Player status at level initialization");
3838 #endif
3839
3840   SCAN_PLAYFIELD(x, y)
3841   {
3842     Tile[x][y] = Last[x][y] = level.field[x][y];
3843     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3844     ChangeDelay[x][y] = 0;
3845     ChangePage[x][y] = -1;
3846     CustomValue[x][y] = 0;              // initialized in InitField()
3847     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3848     AmoebaNr[x][y] = 0;
3849     WasJustMoving[x][y] = 0;
3850     WasJustFalling[x][y] = 0;
3851     CheckCollision[x][y] = 0;
3852     CheckImpact[x][y] = 0;
3853     Stop[x][y] = FALSE;
3854     Pushed[x][y] = FALSE;
3855
3856     ChangeCount[x][y] = 0;
3857     ChangeEvent[x][y] = -1;
3858
3859     ExplodePhase[x][y] = 0;
3860     ExplodeDelay[x][y] = 0;
3861     ExplodeField[x][y] = EX_TYPE_NONE;
3862
3863     RunnerVisit[x][y] = 0;
3864     PlayerVisit[x][y] = 0;
3865
3866     GfxFrame[x][y] = 0;
3867     GfxRandom[x][y] = INIT_GFX_RANDOM();
3868     GfxElement[x][y] = EL_UNDEFINED;
3869     GfxAction[x][y] = ACTION_DEFAULT;
3870     GfxDir[x][y] = MV_NONE;
3871     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3872   }
3873
3874   SCAN_PLAYFIELD(x, y)
3875   {
3876     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3877       emulate_bd = FALSE;
3878     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3879       emulate_sb = FALSE;
3880     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3881       emulate_sp = FALSE;
3882
3883     InitField(x, y, TRUE);
3884
3885     ResetGfxAnimation(x, y);
3886   }
3887
3888   InitBeltMovement();
3889
3890   for (i = 0; i < MAX_PLAYERS; i++)
3891   {
3892     struct PlayerInfo *player = &stored_player[i];
3893
3894     // set number of special actions for bored and sleeping animation
3895     player->num_special_action_bored =
3896       get_num_special_action(player->artwork_element,
3897                              ACTION_BORING_1, ACTION_BORING_LAST);
3898     player->num_special_action_sleeping =
3899       get_num_special_action(player->artwork_element,
3900                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3901   }
3902
3903   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3904                     emulate_sb ? EMU_SOKOBAN :
3905                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3906
3907   // initialize type of slippery elements
3908   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3909   {
3910     if (!IS_CUSTOM_ELEMENT(i))
3911     {
3912       // default: elements slip down either to the left or right randomly
3913       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3914
3915       // SP style elements prefer to slip down on the left side
3916       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3917         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3918
3919       // BD style elements prefer to slip down on the left side
3920       if (game.emulation == EMU_BOULDERDASH)
3921         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3922     }
3923   }
3924
3925   // initialize explosion and ignition delay
3926   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3927   {
3928     if (!IS_CUSTOM_ELEMENT(i))
3929     {
3930       int num_phase = 8;
3931       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3932                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3933                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3934       int last_phase = (num_phase + 1) * delay;
3935       int half_phase = (num_phase / 2) * delay;
3936
3937       element_info[i].explosion_delay = last_phase - 1;
3938       element_info[i].ignition_delay = half_phase;
3939
3940       if (i == EL_BLACK_ORB)
3941         element_info[i].ignition_delay = 1;
3942     }
3943   }
3944
3945   // correct non-moving belts to start moving left
3946   for (i = 0; i < NUM_BELTS; i++)
3947     if (game.belt_dir[i] == MV_NONE)
3948       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3949
3950 #if USE_NEW_PLAYER_ASSIGNMENTS
3951   // use preferred player also in local single-player mode
3952   if (!network.enabled && !game.team_mode)
3953   {
3954     int new_index_nr = setup.network_player_nr;
3955
3956     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3957     {
3958       for (i = 0; i < MAX_PLAYERS; i++)
3959         stored_player[i].connected_locally = FALSE;
3960
3961       stored_player[new_index_nr].connected_locally = TRUE;
3962     }
3963   }
3964
3965   for (i = 0; i < MAX_PLAYERS; i++)
3966   {
3967     stored_player[i].connected = FALSE;
3968
3969     // in network game mode, the local player might not be the first player
3970     if (stored_player[i].connected_locally)
3971       local_player = &stored_player[i];
3972   }
3973
3974   if (!network.enabled)
3975     local_player->connected = TRUE;
3976
3977   if (tape.playing)
3978   {
3979     for (i = 0; i < MAX_PLAYERS; i++)
3980       stored_player[i].connected = tape.player_participates[i];
3981   }
3982   else if (network.enabled)
3983   {
3984     // add team mode players connected over the network (needed for correct
3985     // assignment of player figures from level to locally playing players)
3986
3987     for (i = 0; i < MAX_PLAYERS; i++)
3988       if (stored_player[i].connected_network)
3989         stored_player[i].connected = TRUE;
3990   }
3991   else if (game.team_mode)
3992   {
3993     // try to guess locally connected team mode players (needed for correct
3994     // assignment of player figures from level to locally playing players)
3995
3996     for (i = 0; i < MAX_PLAYERS; i++)
3997       if (setup.input[i].use_joystick ||
3998           setup.input[i].key.left != KSYM_UNDEFINED)
3999         stored_player[i].connected = TRUE;
4000   }
4001
4002 #if DEBUG_INIT_PLAYER
4003   DebugPrintPlayerStatus("Player status after level initialization");
4004 #endif
4005
4006 #if DEBUG_INIT_PLAYER
4007   Debug("game:init:player", "Reassigning players ...");
4008 #endif
4009
4010   // check if any connected player was not found in playfield
4011   for (i = 0; i < MAX_PLAYERS; i++)
4012   {
4013     struct PlayerInfo *player = &stored_player[i];
4014
4015     if (player->connected && !player->present)
4016     {
4017       struct PlayerInfo *field_player = NULL;
4018
4019 #if DEBUG_INIT_PLAYER
4020       Debug("game:init:player",
4021             "- looking for field player for player %d ...", i + 1);
4022 #endif
4023
4024       // assign first free player found that is present in the playfield
4025
4026       // first try: look for unmapped playfield player that is not connected
4027       for (j = 0; j < MAX_PLAYERS; j++)
4028         if (field_player == NULL &&
4029             stored_player[j].present &&
4030             !stored_player[j].mapped &&
4031             !stored_player[j].connected)
4032           field_player = &stored_player[j];
4033
4034       // second try: look for *any* unmapped playfield player
4035       for (j = 0; j < MAX_PLAYERS; j++)
4036         if (field_player == NULL &&
4037             stored_player[j].present &&
4038             !stored_player[j].mapped)
4039           field_player = &stored_player[j];
4040
4041       if (field_player != NULL)
4042       {
4043         int jx = field_player->jx, jy = field_player->jy;
4044
4045 #if DEBUG_INIT_PLAYER
4046         Debug("game:init:player", "- found player %d",
4047               field_player->index_nr + 1);
4048 #endif
4049
4050         player->present = FALSE;
4051         player->active = FALSE;
4052
4053         field_player->present = TRUE;
4054         field_player->active = TRUE;
4055
4056         /*
4057         player->initial_element = field_player->initial_element;
4058         player->artwork_element = field_player->artwork_element;
4059
4060         player->block_last_field       = field_player->block_last_field;
4061         player->block_delay_adjustment = field_player->block_delay_adjustment;
4062         */
4063
4064         StorePlayer[jx][jy] = field_player->element_nr;
4065
4066         field_player->jx = field_player->last_jx = jx;
4067         field_player->jy = field_player->last_jy = jy;
4068
4069         if (local_player == player)
4070           local_player = field_player;
4071
4072         map_player_action[field_player->index_nr] = i;
4073
4074         field_player->mapped = TRUE;
4075
4076 #if DEBUG_INIT_PLAYER
4077         Debug("game:init:player", "- map_player_action[%d] == %d",
4078               field_player->index_nr + 1, i + 1);
4079 #endif
4080       }
4081     }
4082
4083     if (player->connected && player->present)
4084       player->mapped = TRUE;
4085   }
4086
4087 #if DEBUG_INIT_PLAYER
4088   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4089 #endif
4090
4091 #else
4092
4093   // check if any connected player was not found in playfield
4094   for (i = 0; i < MAX_PLAYERS; i++)
4095   {
4096     struct PlayerInfo *player = &stored_player[i];
4097
4098     if (player->connected && !player->present)
4099     {
4100       for (j = 0; j < MAX_PLAYERS; j++)
4101       {
4102         struct PlayerInfo *field_player = &stored_player[j];
4103         int jx = field_player->jx, jy = field_player->jy;
4104
4105         // assign first free player found that is present in the playfield
4106         if (field_player->present && !field_player->connected)
4107         {
4108           player->present = TRUE;
4109           player->active = TRUE;
4110
4111           field_player->present = FALSE;
4112           field_player->active = FALSE;
4113
4114           player->initial_element = field_player->initial_element;
4115           player->artwork_element = field_player->artwork_element;
4116
4117           player->block_last_field       = field_player->block_last_field;
4118           player->block_delay_adjustment = field_player->block_delay_adjustment;
4119
4120           StorePlayer[jx][jy] = player->element_nr;
4121
4122           player->jx = player->last_jx = jx;
4123           player->jy = player->last_jy = jy;
4124
4125           break;
4126         }
4127       }
4128     }
4129   }
4130 #endif
4131
4132 #if 0
4133   Debug("game:init:player", "local_player->present == %d",
4134         local_player->present);
4135 #endif
4136
4137   // set focus to local player for network games, else to all players
4138   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4139   game.centered_player_nr_next = game.centered_player_nr;
4140   game.set_centered_player = FALSE;
4141   game.set_centered_player_wrap = FALSE;
4142
4143   if (network_playing && tape.recording)
4144   {
4145     // store client dependent player focus when recording network games
4146     tape.centered_player_nr_next = game.centered_player_nr_next;
4147     tape.set_centered_player = TRUE;
4148   }
4149
4150   if (tape.playing)
4151   {
4152     // when playing a tape, eliminate all players who do not participate
4153
4154 #if USE_NEW_PLAYER_ASSIGNMENTS
4155
4156     if (!game.team_mode)
4157     {
4158       for (i = 0; i < MAX_PLAYERS; i++)
4159       {
4160         if (stored_player[i].active &&
4161             !tape.player_participates[map_player_action[i]])
4162         {
4163           struct PlayerInfo *player = &stored_player[i];
4164           int jx = player->jx, jy = player->jy;
4165
4166 #if DEBUG_INIT_PLAYER
4167           Debug("game:init:player", "Removing player %d at (%d, %d)",
4168                 i + 1, jx, jy);
4169 #endif
4170
4171           player->active = FALSE;
4172           StorePlayer[jx][jy] = 0;
4173           Tile[jx][jy] = EL_EMPTY;
4174         }
4175       }
4176     }
4177
4178 #else
4179
4180     for (i = 0; i < MAX_PLAYERS; i++)
4181     {
4182       if (stored_player[i].active &&
4183           !tape.player_participates[i])
4184       {
4185         struct PlayerInfo *player = &stored_player[i];
4186         int jx = player->jx, jy = player->jy;
4187
4188         player->active = FALSE;
4189         StorePlayer[jx][jy] = 0;
4190         Tile[jx][jy] = EL_EMPTY;
4191       }
4192     }
4193 #endif
4194   }
4195   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4196   {
4197     // when in single player mode, eliminate all but the local player
4198
4199     for (i = 0; i < MAX_PLAYERS; i++)
4200     {
4201       struct PlayerInfo *player = &stored_player[i];
4202
4203       if (player->active && player != local_player)
4204       {
4205         int jx = player->jx, jy = player->jy;
4206
4207         player->active = FALSE;
4208         player->present = FALSE;
4209
4210         StorePlayer[jx][jy] = 0;
4211         Tile[jx][jy] = EL_EMPTY;
4212       }
4213     }
4214   }
4215
4216   for (i = 0; i < MAX_PLAYERS; i++)
4217     if (stored_player[i].active)
4218       game.players_still_needed++;
4219
4220   if (level.solved_by_one_player)
4221     game.players_still_needed = 1;
4222
4223   // when recording the game, store which players take part in the game
4224   if (tape.recording)
4225   {
4226 #if USE_NEW_PLAYER_ASSIGNMENTS
4227     for (i = 0; i < MAX_PLAYERS; i++)
4228       if (stored_player[i].connected)
4229         tape.player_participates[i] = TRUE;
4230 #else
4231     for (i = 0; i < MAX_PLAYERS; i++)
4232       if (stored_player[i].active)
4233         tape.player_participates[i] = TRUE;
4234 #endif
4235   }
4236
4237 #if DEBUG_INIT_PLAYER
4238   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4239 #endif
4240
4241   if (BorderElement == EL_EMPTY)
4242   {
4243     SBX_Left = 0;
4244     SBX_Right = lev_fieldx - SCR_FIELDX;
4245     SBY_Upper = 0;
4246     SBY_Lower = lev_fieldy - SCR_FIELDY;
4247   }
4248   else
4249   {
4250     SBX_Left = -1;
4251     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4252     SBY_Upper = -1;
4253     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4254   }
4255
4256   if (full_lev_fieldx <= SCR_FIELDX)
4257     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4258   if (full_lev_fieldy <= SCR_FIELDY)
4259     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4260
4261   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4262     SBX_Left--;
4263   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4264     SBY_Upper--;
4265
4266   // if local player not found, look for custom element that might create
4267   // the player (make some assumptions about the right custom element)
4268   if (!local_player->present)
4269   {
4270     int start_x = 0, start_y = 0;
4271     int found_rating = 0;
4272     int found_element = EL_UNDEFINED;
4273     int player_nr = local_player->index_nr;
4274
4275     SCAN_PLAYFIELD(x, y)
4276     {
4277       int element = Tile[x][y];
4278       int content;
4279       int xx, yy;
4280       boolean is_player;
4281
4282       if (level.use_start_element[player_nr] &&
4283           level.start_element[player_nr] == element &&
4284           found_rating < 4)
4285       {
4286         start_x = x;
4287         start_y = y;
4288
4289         found_rating = 4;
4290         found_element = element;
4291       }
4292
4293       if (!IS_CUSTOM_ELEMENT(element))
4294         continue;
4295
4296       if (CAN_CHANGE(element))
4297       {
4298         for (i = 0; i < element_info[element].num_change_pages; i++)
4299         {
4300           // check for player created from custom element as single target
4301           content = element_info[element].change_page[i].target_element;
4302           is_player = ELEM_IS_PLAYER(content);
4303
4304           if (is_player && (found_rating < 3 ||
4305                             (found_rating == 3 && element < found_element)))
4306           {
4307             start_x = x;
4308             start_y = y;
4309
4310             found_rating = 3;
4311             found_element = element;
4312           }
4313         }
4314       }
4315
4316       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4317       {
4318         // check for player created from custom element as explosion content
4319         content = element_info[element].content.e[xx][yy];
4320         is_player = ELEM_IS_PLAYER(content);
4321
4322         if (is_player && (found_rating < 2 ||
4323                           (found_rating == 2 && element < found_element)))
4324         {
4325           start_x = x + xx - 1;
4326           start_y = y + yy - 1;
4327
4328           found_rating = 2;
4329           found_element = element;
4330         }
4331
4332         if (!CAN_CHANGE(element))
4333           continue;
4334
4335         for (i = 0; i < element_info[element].num_change_pages; i++)
4336         {
4337           // check for player created from custom element as extended target
4338           content =
4339             element_info[element].change_page[i].target_content.e[xx][yy];
4340
4341           is_player = ELEM_IS_PLAYER(content);
4342
4343           if (is_player && (found_rating < 1 ||
4344                             (found_rating == 1 && element < found_element)))
4345           {
4346             start_x = x + xx - 1;
4347             start_y = y + yy - 1;
4348
4349             found_rating = 1;
4350             found_element = element;
4351           }
4352         }
4353       }
4354     }
4355
4356     scroll_x = SCROLL_POSITION_X(start_x);
4357     scroll_y = SCROLL_POSITION_Y(start_y);
4358   }
4359   else
4360   {
4361     scroll_x = SCROLL_POSITION_X(local_player->jx);
4362     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4363   }
4364
4365   // !!! FIX THIS (START) !!!
4366   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4367   {
4368     InitGameEngine_EM();
4369   }
4370   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4371   {
4372     InitGameEngine_SP();
4373   }
4374   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4375   {
4376     InitGameEngine_MM();
4377   }
4378   else
4379   {
4380     DrawLevel(REDRAW_FIELD);
4381     DrawAllPlayers();
4382
4383     // after drawing the level, correct some elements
4384     if (game.timegate_time_left == 0)
4385       CloseAllOpenTimegates();
4386   }
4387
4388   // blit playfield from scroll buffer to normal back buffer for fading in
4389   BlitScreenToBitmap(backbuffer);
4390   // !!! FIX THIS (END) !!!
4391
4392   DrawMaskedBorder(fade_mask);
4393
4394   FadeIn(fade_mask);
4395
4396 #if 1
4397   // full screen redraw is required at this point in the following cases:
4398   // - special editor door undrawn when game was started from level editor
4399   // - drawing area (playfield) was changed and has to be removed completely
4400   redraw_mask = REDRAW_ALL;
4401   BackToFront();
4402 #endif
4403
4404   if (!game.restart_level)
4405   {
4406     // copy default game door content to main double buffer
4407
4408     // !!! CHECK AGAIN !!!
4409     SetPanelBackground();
4410     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4411     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4412   }
4413
4414   SetPanelBackground();
4415   SetDrawBackgroundMask(REDRAW_DOOR_1);
4416
4417   UpdateAndDisplayGameControlValues();
4418
4419   if (!game.restart_level)
4420   {
4421     UnmapGameButtons();
4422     UnmapTapeButtons();
4423
4424     FreeGameButtons();
4425     CreateGameButtons();
4426
4427     MapGameButtons();
4428     MapTapeButtons();
4429
4430     // copy actual game door content to door double buffer for OpenDoor()
4431     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4432
4433     OpenDoor(DOOR_OPEN_ALL);
4434
4435     KeyboardAutoRepeatOffUnlessAutoplay();
4436
4437 #if DEBUG_INIT_PLAYER
4438     DebugPrintPlayerStatus("Player status (final)");
4439 #endif
4440   }
4441
4442   UnmapAllGadgets();
4443
4444   MapGameButtons();
4445   MapTapeButtons();
4446
4447   if (!game.restart_level && !tape.playing)
4448   {
4449     LevelStats_incPlayed(level_nr);
4450
4451     SaveLevelSetup_SeriesInfo();
4452   }
4453
4454   game.restart_level = FALSE;
4455   game.restart_game_message = NULL;
4456
4457   game.request_active = FALSE;
4458   game.request_active_or_moving = FALSE;
4459
4460   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4461     InitGameActions_MM();
4462
4463   SaveEngineSnapshotToListInitial();
4464
4465   if (!game.restart_level)
4466   {
4467     PlaySound(SND_GAME_STARTING);
4468
4469     if (setup.sound_music)
4470       PlayLevelMusic();
4471   }
4472 }
4473
4474 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4475                         int actual_player_x, int actual_player_y)
4476 {
4477   // this is used for non-R'n'D game engines to update certain engine values
4478
4479   // needed to determine if sounds are played within the visible screen area
4480   scroll_x = actual_scroll_x;
4481   scroll_y = actual_scroll_y;
4482
4483   // needed to get player position for "follow finger" playing input method
4484   local_player->jx = actual_player_x;
4485   local_player->jy = actual_player_y;
4486 }
4487
4488 void InitMovDir(int x, int y)
4489 {
4490   int i, element = Tile[x][y];
4491   static int xy[4][2] =
4492   {
4493     {  0, +1 },
4494     { +1,  0 },
4495     {  0, -1 },
4496     { -1,  0 }
4497   };
4498   static int direction[3][4] =
4499   {
4500     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4501     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4502     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4503   };
4504
4505   switch (element)
4506   {
4507     case EL_BUG_RIGHT:
4508     case EL_BUG_UP:
4509     case EL_BUG_LEFT:
4510     case EL_BUG_DOWN:
4511       Tile[x][y] = EL_BUG;
4512       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4513       break;
4514
4515     case EL_SPACESHIP_RIGHT:
4516     case EL_SPACESHIP_UP:
4517     case EL_SPACESHIP_LEFT:
4518     case EL_SPACESHIP_DOWN:
4519       Tile[x][y] = EL_SPACESHIP;
4520       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4521       break;
4522
4523     case EL_BD_BUTTERFLY_RIGHT:
4524     case EL_BD_BUTTERFLY_UP:
4525     case EL_BD_BUTTERFLY_LEFT:
4526     case EL_BD_BUTTERFLY_DOWN:
4527       Tile[x][y] = EL_BD_BUTTERFLY;
4528       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4529       break;
4530
4531     case EL_BD_FIREFLY_RIGHT:
4532     case EL_BD_FIREFLY_UP:
4533     case EL_BD_FIREFLY_LEFT:
4534     case EL_BD_FIREFLY_DOWN:
4535       Tile[x][y] = EL_BD_FIREFLY;
4536       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4537       break;
4538
4539     case EL_PACMAN_RIGHT:
4540     case EL_PACMAN_UP:
4541     case EL_PACMAN_LEFT:
4542     case EL_PACMAN_DOWN:
4543       Tile[x][y] = EL_PACMAN;
4544       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4545       break;
4546
4547     case EL_YAMYAM_LEFT:
4548     case EL_YAMYAM_RIGHT:
4549     case EL_YAMYAM_UP:
4550     case EL_YAMYAM_DOWN:
4551       Tile[x][y] = EL_YAMYAM;
4552       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4553       break;
4554
4555     case EL_SP_SNIKSNAK:
4556       MovDir[x][y] = MV_UP;
4557       break;
4558
4559     case EL_SP_ELECTRON:
4560       MovDir[x][y] = MV_LEFT;
4561       break;
4562
4563     case EL_MOLE_LEFT:
4564     case EL_MOLE_RIGHT:
4565     case EL_MOLE_UP:
4566     case EL_MOLE_DOWN:
4567       Tile[x][y] = EL_MOLE;
4568       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4569       break;
4570
4571     case EL_SPRING_LEFT:
4572     case EL_SPRING_RIGHT:
4573       Tile[x][y] = EL_SPRING;
4574       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4575       break;
4576
4577     default:
4578       if (IS_CUSTOM_ELEMENT(element))
4579       {
4580         struct ElementInfo *ei = &element_info[element];
4581         int move_direction_initial = ei->move_direction_initial;
4582         int move_pattern = ei->move_pattern;
4583
4584         if (move_direction_initial == MV_START_PREVIOUS)
4585         {
4586           if (MovDir[x][y] != MV_NONE)
4587             return;
4588
4589           move_direction_initial = MV_START_AUTOMATIC;
4590         }
4591
4592         if (move_direction_initial == MV_START_RANDOM)
4593           MovDir[x][y] = 1 << RND(4);
4594         else if (move_direction_initial & MV_ANY_DIRECTION)
4595           MovDir[x][y] = move_direction_initial;
4596         else if (move_pattern == MV_ALL_DIRECTIONS ||
4597                  move_pattern == MV_TURNING_LEFT ||
4598                  move_pattern == MV_TURNING_RIGHT ||
4599                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4600                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4601                  move_pattern == MV_TURNING_RANDOM)
4602           MovDir[x][y] = 1 << RND(4);
4603         else if (move_pattern == MV_HORIZONTAL)
4604           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4605         else if (move_pattern == MV_VERTICAL)
4606           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4607         else if (move_pattern & MV_ANY_DIRECTION)
4608           MovDir[x][y] = element_info[element].move_pattern;
4609         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4610                  move_pattern == MV_ALONG_RIGHT_SIDE)
4611         {
4612           // use random direction as default start direction
4613           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4614             MovDir[x][y] = 1 << RND(4);
4615
4616           for (i = 0; i < NUM_DIRECTIONS; i++)
4617           {
4618             int x1 = x + xy[i][0];
4619             int y1 = y + xy[i][1];
4620
4621             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4622             {
4623               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4624                 MovDir[x][y] = direction[0][i];
4625               else
4626                 MovDir[x][y] = direction[1][i];
4627
4628               break;
4629             }
4630           }
4631         }                
4632       }
4633       else
4634       {
4635         MovDir[x][y] = 1 << RND(4);
4636
4637         if (element != EL_BUG &&
4638             element != EL_SPACESHIP &&
4639             element != EL_BD_BUTTERFLY &&
4640             element != EL_BD_FIREFLY)
4641           break;
4642
4643         for (i = 0; i < NUM_DIRECTIONS; i++)
4644         {
4645           int x1 = x + xy[i][0];
4646           int y1 = y + xy[i][1];
4647
4648           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4649           {
4650             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4651             {
4652               MovDir[x][y] = direction[0][i];
4653               break;
4654             }
4655             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4656                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4657             {
4658               MovDir[x][y] = direction[1][i];
4659               break;
4660             }
4661           }
4662         }
4663       }
4664       break;
4665   }
4666
4667   GfxDir[x][y] = MovDir[x][y];
4668 }
4669
4670 void InitAmoebaNr(int x, int y)
4671 {
4672   int i;
4673   int group_nr = AmoebaNeighbourNr(x, y);
4674
4675   if (group_nr == 0)
4676   {
4677     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4678     {
4679       if (AmoebaCnt[i] == 0)
4680       {
4681         group_nr = i;
4682         break;
4683       }
4684     }
4685   }
4686
4687   AmoebaNr[x][y] = group_nr;
4688   AmoebaCnt[group_nr]++;
4689   AmoebaCnt2[group_nr]++;
4690 }
4691
4692 static void LevelSolved(void)
4693 {
4694   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4695       game.players_still_needed > 0)
4696     return;
4697
4698   game.LevelSolved = TRUE;
4699   game.GameOver = TRUE;
4700
4701   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4702                       game_em.lev->score :
4703                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4704                       game_mm.score :
4705                       game.score);
4706   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4707                        MM_HEALTH(game_mm.laser_overload_value) :
4708                        game.health);
4709
4710   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4711   game.LevelSolved_CountingScore = game.score_final;
4712   game.LevelSolved_CountingHealth = game.health_final;
4713 }
4714
4715 void GameWon(void)
4716 {
4717   static int time_count_steps;
4718   static int time, time_final;
4719   static float score, score_final; // needed for time score < 10 for 10 seconds
4720   static int health, health_final;
4721   static int game_over_delay_1 = 0;
4722   static int game_over_delay_2 = 0;
4723   static int game_over_delay_3 = 0;
4724   int game_over_delay_value_1 = 50;
4725   int game_over_delay_value_2 = 25;
4726   int game_over_delay_value_3 = 50;
4727   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4728   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4729
4730   if (!game.LevelSolved_GameWon)
4731   {
4732     int i;
4733
4734     // do not start end game actions before the player stops moving (to exit)
4735     if (local_player->active && local_player->MovPos)
4736       return;
4737
4738     game.LevelSolved_GameWon = TRUE;
4739     game.LevelSolved_SaveTape = tape.recording;
4740     game.LevelSolved_SaveScore = !tape.playing;
4741
4742     if (!tape.playing)
4743     {
4744       LevelStats_incSolved(level_nr);
4745
4746       SaveLevelSetup_SeriesInfo();
4747     }
4748
4749     if (tape.auto_play)         // tape might already be stopped here
4750       tape.auto_play_level_solved = TRUE;
4751
4752     TapeStop();
4753
4754     game_over_delay_1 = 0;
4755     game_over_delay_2 = 0;
4756     game_over_delay_3 = game_over_delay_value_3;
4757
4758     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4759     score = score_final = game.score_final;
4760     health = health_final = game.health_final;
4761
4762     if (time_score > 0)
4763     {
4764       int time_frames = 0;
4765
4766       if (TimeLeft > 0)
4767       {
4768         time_final = 0;
4769         time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4770       }
4771       else if (game.no_time_limit && TimePlayed < 999)
4772       {
4773         time_final = 999;
4774         time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
4775       }
4776
4777       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4778
4779       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4780
4781       game_over_delay_1 = game_over_delay_value_1;
4782
4783       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4784       {
4785         health_final = 0;
4786         score_final += health * time_score;
4787
4788         game_over_delay_2 = game_over_delay_value_2;
4789       }
4790
4791       game.score_final = score_final;
4792       game.health_final = health_final;
4793     }
4794
4795     if (level_editor_test_game || !setup.count_score_after_game)
4796     {
4797       time = time_final;
4798       score = score_final;
4799
4800       game.LevelSolved_CountingTime = time;
4801       game.LevelSolved_CountingScore = score;
4802
4803       game_panel_controls[GAME_PANEL_TIME].value = time;
4804       game_panel_controls[GAME_PANEL_SCORE].value = score;
4805
4806       DisplayGameControlValues();
4807     }
4808
4809     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4810     {
4811       // check if last player has left the level
4812       if (game.exit_x >= 0 &&
4813           game.exit_y >= 0)
4814       {
4815         int x = game.exit_x;
4816         int y = game.exit_y;
4817         int element = Tile[x][y];
4818
4819         // close exit door after last player
4820         if ((game.all_players_gone &&
4821              (element == EL_EXIT_OPEN ||
4822               element == EL_SP_EXIT_OPEN ||
4823               element == EL_STEEL_EXIT_OPEN)) ||
4824             element == EL_EM_EXIT_OPEN ||
4825             element == EL_EM_STEEL_EXIT_OPEN)
4826         {
4827
4828           Tile[x][y] =
4829             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4830              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4831              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4832              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4833              EL_EM_STEEL_EXIT_CLOSING);
4834
4835           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4836         }
4837
4838         // player disappears
4839         DrawLevelField(x, y);
4840       }
4841
4842       for (i = 0; i < MAX_PLAYERS; i++)
4843       {
4844         struct PlayerInfo *player = &stored_player[i];
4845
4846         if (player->present)
4847         {
4848           RemovePlayer(player);
4849
4850           // player disappears
4851           DrawLevelField(player->jx, player->jy);
4852         }
4853       }
4854     }
4855
4856     PlaySound(SND_GAME_WINNING);
4857   }
4858
4859   if (setup.count_score_after_game)
4860   {
4861     if (game_over_delay_1 > 0)
4862     {
4863       game_over_delay_1--;
4864
4865       return;
4866     }
4867
4868     if (time != time_final)
4869     {
4870       int time_to_go = ABS(time_final - time);
4871       int time_count_dir = (time < time_final ? +1 : -1);
4872
4873       if (time_to_go < time_count_steps)
4874         time_count_steps = 1;
4875
4876       time  += time_count_steps * time_count_dir;
4877       score += time_count_steps * time_score;
4878
4879       // set final score to correct rounding differences after counting score
4880       if (time == time_final)
4881         score = score_final;
4882
4883       game.LevelSolved_CountingTime = time;
4884       game.LevelSolved_CountingScore = score;
4885
4886       game_panel_controls[GAME_PANEL_TIME].value = time;
4887       game_panel_controls[GAME_PANEL_SCORE].value = score;
4888
4889       DisplayGameControlValues();
4890
4891       if (time == time_final)
4892         StopSound(SND_GAME_LEVELTIME_BONUS);
4893       else if (setup.sound_loops)
4894         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4895       else
4896         PlaySound(SND_GAME_LEVELTIME_BONUS);
4897
4898       return;
4899     }
4900
4901     if (game_over_delay_2 > 0)
4902     {
4903       game_over_delay_2--;
4904
4905       return;
4906     }
4907
4908     if (health != health_final)
4909     {
4910       int health_count_dir = (health < health_final ? +1 : -1);
4911
4912       health += health_count_dir;
4913       score  += time_score;
4914
4915       game.LevelSolved_CountingHealth = health;
4916       game.LevelSolved_CountingScore = score;
4917
4918       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4919       game_panel_controls[GAME_PANEL_SCORE].value = score;
4920
4921       DisplayGameControlValues();
4922
4923       if (health == health_final)
4924         StopSound(SND_GAME_LEVELTIME_BONUS);
4925       else if (setup.sound_loops)
4926         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4927       else
4928         PlaySound(SND_GAME_LEVELTIME_BONUS);
4929
4930       return;
4931     }
4932   }
4933
4934   game.panel.active = FALSE;
4935
4936   if (game_over_delay_3 > 0)
4937   {
4938     game_over_delay_3--;
4939
4940     return;
4941   }
4942
4943   GameEnd();
4944 }
4945
4946 void GameEnd(void)
4947 {
4948   // used instead of "level_nr" (needed for network games)
4949   int last_level_nr = levelset.level_nr;
4950   int hi_pos;
4951
4952   game.LevelSolved_GameEnd = TRUE;
4953
4954   if (game.LevelSolved_SaveTape)
4955   {
4956     // make sure that request dialog to save tape does not open door again
4957     if (!global.use_envelope_request)
4958       CloseDoor(DOOR_CLOSE_1);
4959
4960     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4961   }
4962
4963   // if no tape is to be saved, close both doors simultaneously
4964   CloseDoor(DOOR_CLOSE_ALL);
4965
4966   if (level_editor_test_game)
4967   {
4968     SetGameStatus(GAME_MODE_MAIN);
4969
4970     DrawMainMenu();
4971
4972     return;
4973   }
4974
4975   if (!game.LevelSolved_SaveScore)
4976   {
4977     SetGameStatus(GAME_MODE_MAIN);
4978
4979     DrawMainMenu();
4980
4981     return;
4982   }
4983
4984   if (level_nr == leveldir_current->handicap_level)
4985   {
4986     leveldir_current->handicap_level++;
4987
4988     SaveLevelSetup_SeriesInfo();
4989   }
4990
4991   if (setup.increment_levels &&
4992       level_nr < leveldir_current->last_level &&
4993       !network_playing)
4994   {
4995     level_nr++;         // advance to next level
4996     TapeErase();        // start with empty tape
4997
4998     if (setup.auto_play_next_level)
4999     {
5000       LoadLevel(level_nr);
5001
5002       SaveLevelSetup_SeriesInfo();
5003     }
5004   }
5005
5006   hi_pos = NewHiScore(last_level_nr);
5007
5008   if (hi_pos >= 0 && setup.show_scores_after_game)
5009   {
5010     SetGameStatus(GAME_MODE_SCORES);
5011
5012     DrawHallOfFame(last_level_nr, hi_pos);
5013   }
5014   else if (setup.auto_play_next_level && setup.increment_levels &&
5015            last_level_nr < leveldir_current->last_level &&
5016            !network_playing)
5017   {
5018     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5019   }
5020   else
5021   {
5022     SetGameStatus(GAME_MODE_MAIN);
5023
5024     DrawMainMenu();
5025   }
5026 }
5027
5028 int NewHiScore(int level_nr)
5029 {
5030   int k, l;
5031   int position = -1;
5032   boolean one_score_entry_per_name = !program.many_scores_per_name;
5033
5034   LoadScore(level_nr);
5035
5036   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5037       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5038     return -1;
5039
5040   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5041   {
5042     if (game.score_final > highscore[k].Score)
5043     {
5044       // player has made it to the hall of fame
5045
5046       if (k < MAX_SCORE_ENTRIES - 1)
5047       {
5048         int m = MAX_SCORE_ENTRIES - 1;
5049
5050         if (one_score_entry_per_name)
5051         {
5052           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5053             if (strEqual(setup.player_name, highscore[l].Name))
5054               m = l;
5055
5056           if (m == k)   // player's new highscore overwrites his old one
5057             goto put_into_list;
5058         }
5059
5060         for (l = m; l > k; l--)
5061         {
5062           strcpy(highscore[l].Name, highscore[l - 1].Name);
5063           highscore[l].Score = highscore[l - 1].Score;
5064         }
5065       }
5066
5067       put_into_list:
5068
5069       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5070       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5071       highscore[k].Score = game.score_final;
5072       position = k;
5073
5074       break;
5075     }
5076     else if (one_score_entry_per_name &&
5077              !strncmp(setup.player_name, highscore[k].Name,
5078                       MAX_PLAYER_NAME_LEN))
5079       break;    // player already there with a higher score
5080   }
5081
5082   if (position >= 0) 
5083     SaveScore(level_nr);
5084
5085   return position;
5086 }
5087
5088 static int getElementMoveStepsizeExt(int x, int y, int direction)
5089 {
5090   int element = Tile[x][y];
5091   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5092   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5093   int horiz_move = (dx != 0);
5094   int sign = (horiz_move ? dx : dy);
5095   int step = sign * element_info[element].move_stepsize;
5096
5097   // special values for move stepsize for spring and things on conveyor belt
5098   if (horiz_move)
5099   {
5100     if (CAN_FALL(element) &&
5101         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5102       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5103     else if (element == EL_SPRING)
5104       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5105   }
5106
5107   return step;
5108 }
5109
5110 static int getElementMoveStepsize(int x, int y)
5111 {
5112   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5113 }
5114
5115 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5116 {
5117   if (player->GfxAction != action || player->GfxDir != dir)
5118   {
5119     player->GfxAction = action;
5120     player->GfxDir = dir;
5121     player->Frame = 0;
5122     player->StepFrame = 0;
5123   }
5124 }
5125
5126 static void ResetGfxFrame(int x, int y)
5127 {
5128   // profiling showed that "autotest" spends 10~20% of its time in this function
5129   if (DrawingDeactivatedField())
5130     return;
5131
5132   int element = Tile[x][y];
5133   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5134
5135   if (graphic_info[graphic].anim_global_sync)
5136     GfxFrame[x][y] = FrameCounter;
5137   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5138     GfxFrame[x][y] = CustomValue[x][y];
5139   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5140     GfxFrame[x][y] = element_info[element].collect_score;
5141   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5142     GfxFrame[x][y] = ChangeDelay[x][y];
5143 }
5144
5145 static void ResetGfxAnimation(int x, int y)
5146 {
5147   GfxAction[x][y] = ACTION_DEFAULT;
5148   GfxDir[x][y] = MovDir[x][y];
5149   GfxFrame[x][y] = 0;
5150
5151   ResetGfxFrame(x, y);
5152 }
5153
5154 static void ResetRandomAnimationValue(int x, int y)
5155 {
5156   GfxRandom[x][y] = INIT_GFX_RANDOM();
5157 }
5158
5159 static void InitMovingField(int x, int y, int direction)
5160 {
5161   int element = Tile[x][y];
5162   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5163   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5164   int newx = x + dx;
5165   int newy = y + dy;
5166   boolean is_moving_before, is_moving_after;
5167
5168   // check if element was/is moving or being moved before/after mode change
5169   is_moving_before = (WasJustMoving[x][y] != 0);
5170   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5171
5172   // reset animation only for moving elements which change direction of moving
5173   // or which just started or stopped moving
5174   // (else CEs with property "can move" / "not moving" are reset each frame)
5175   if (is_moving_before != is_moving_after ||
5176       direction != MovDir[x][y])
5177     ResetGfxAnimation(x, y);
5178
5179   MovDir[x][y] = direction;
5180   GfxDir[x][y] = direction;
5181
5182   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5183                      direction == MV_DOWN && CAN_FALL(element) ?
5184                      ACTION_FALLING : ACTION_MOVING);
5185
5186   // this is needed for CEs with property "can move" / "not moving"
5187
5188   if (is_moving_after)
5189   {
5190     if (Tile[newx][newy] == EL_EMPTY)
5191       Tile[newx][newy] = EL_BLOCKED;
5192
5193     MovDir[newx][newy] = MovDir[x][y];
5194
5195     CustomValue[newx][newy] = CustomValue[x][y];
5196
5197     GfxFrame[newx][newy] = GfxFrame[x][y];
5198     GfxRandom[newx][newy] = GfxRandom[x][y];
5199     GfxAction[newx][newy] = GfxAction[x][y];
5200     GfxDir[newx][newy] = GfxDir[x][y];
5201   }
5202 }
5203
5204 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5205 {
5206   int direction = MovDir[x][y];
5207   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5208   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5209
5210   *goes_to_x = newx;
5211   *goes_to_y = newy;
5212 }
5213
5214 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5215 {
5216   int oldx = x, oldy = y;
5217   int direction = MovDir[x][y];
5218
5219   if (direction == MV_LEFT)
5220     oldx++;
5221   else if (direction == MV_RIGHT)
5222     oldx--;
5223   else if (direction == MV_UP)
5224     oldy++;
5225   else if (direction == MV_DOWN)
5226     oldy--;
5227
5228   *comes_from_x = oldx;
5229   *comes_from_y = oldy;
5230 }
5231
5232 static int MovingOrBlocked2Element(int x, int y)
5233 {
5234   int element = Tile[x][y];
5235
5236   if (element == EL_BLOCKED)
5237   {
5238     int oldx, oldy;
5239
5240     Blocked2Moving(x, y, &oldx, &oldy);
5241     return Tile[oldx][oldy];
5242   }
5243   else
5244     return element;
5245 }
5246
5247 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5248 {
5249   // like MovingOrBlocked2Element(), but if element is moving
5250   // and (x,y) is the field the moving element is just leaving,
5251   // return EL_BLOCKED instead of the element value
5252   int element = Tile[x][y];
5253
5254   if (IS_MOVING(x, y))
5255   {
5256     if (element == EL_BLOCKED)
5257     {
5258       int oldx, oldy;
5259
5260       Blocked2Moving(x, y, &oldx, &oldy);
5261       return Tile[oldx][oldy];
5262     }
5263     else
5264       return EL_BLOCKED;
5265   }
5266   else
5267     return element;
5268 }
5269
5270 static void RemoveField(int x, int y)
5271 {
5272   Tile[x][y] = EL_EMPTY;
5273
5274   MovPos[x][y] = 0;
5275   MovDir[x][y] = 0;
5276   MovDelay[x][y] = 0;
5277
5278   CustomValue[x][y] = 0;
5279
5280   AmoebaNr[x][y] = 0;
5281   ChangeDelay[x][y] = 0;
5282   ChangePage[x][y] = -1;
5283   Pushed[x][y] = FALSE;
5284
5285   GfxElement[x][y] = EL_UNDEFINED;
5286   GfxAction[x][y] = ACTION_DEFAULT;
5287   GfxDir[x][y] = MV_NONE;
5288 }
5289
5290 static void RemoveMovingField(int x, int y)
5291 {
5292   int oldx = x, oldy = y, newx = x, newy = y;
5293   int element = Tile[x][y];
5294   int next_element = EL_UNDEFINED;
5295
5296   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5297     return;
5298
5299   if (IS_MOVING(x, y))
5300   {
5301     Moving2Blocked(x, y, &newx, &newy);
5302
5303     if (Tile[newx][newy] != EL_BLOCKED)
5304     {
5305       // element is moving, but target field is not free (blocked), but
5306       // already occupied by something different (example: acid pool);
5307       // in this case, only remove the moving field, but not the target
5308
5309       RemoveField(oldx, oldy);
5310
5311       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5312
5313       TEST_DrawLevelField(oldx, oldy);
5314
5315       return;
5316     }
5317   }
5318   else if (element == EL_BLOCKED)
5319   {
5320     Blocked2Moving(x, y, &oldx, &oldy);
5321     if (!IS_MOVING(oldx, oldy))
5322       return;
5323   }
5324
5325   if (element == EL_BLOCKED &&
5326       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5327        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5328        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5329        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5330        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5331        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5332     next_element = get_next_element(Tile[oldx][oldy]);
5333
5334   RemoveField(oldx, oldy);
5335   RemoveField(newx, newy);
5336
5337   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5338
5339   if (next_element != EL_UNDEFINED)
5340     Tile[oldx][oldy] = next_element;
5341
5342   TEST_DrawLevelField(oldx, oldy);
5343   TEST_DrawLevelField(newx, newy);
5344 }
5345
5346 void DrawDynamite(int x, int y)
5347 {
5348   int sx = SCREENX(x), sy = SCREENY(y);
5349   int graphic = el2img(Tile[x][y]);
5350   int frame;
5351
5352   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5353     return;
5354
5355   if (IS_WALKABLE_INSIDE(Back[x][y]))
5356     return;
5357
5358   if (Back[x][y])
5359     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5360   else if (Store[x][y])
5361     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5362
5363   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5364
5365   if (Back[x][y] || Store[x][y])
5366     DrawGraphicThruMask(sx, sy, graphic, frame);
5367   else
5368     DrawGraphic(sx, sy, graphic, frame);
5369 }
5370
5371 static void CheckDynamite(int x, int y)
5372 {
5373   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5374   {
5375     MovDelay[x][y]--;
5376
5377     if (MovDelay[x][y] != 0)
5378     {
5379       DrawDynamite(x, y);
5380       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5381
5382       return;
5383     }
5384   }
5385
5386   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5387
5388   Bang(x, y);
5389 }
5390
5391 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5392 {
5393   boolean num_checked_players = 0;
5394   int i;
5395
5396   for (i = 0; i < MAX_PLAYERS; i++)
5397   {
5398     if (stored_player[i].active)
5399     {
5400       int sx = stored_player[i].jx;
5401       int sy = stored_player[i].jy;
5402
5403       if (num_checked_players == 0)
5404       {
5405         *sx1 = *sx2 = sx;
5406         *sy1 = *sy2 = sy;
5407       }
5408       else
5409       {
5410         *sx1 = MIN(*sx1, sx);
5411         *sy1 = MIN(*sy1, sy);
5412         *sx2 = MAX(*sx2, sx);
5413         *sy2 = MAX(*sy2, sy);
5414       }
5415
5416       num_checked_players++;
5417     }
5418   }
5419 }
5420
5421 static boolean checkIfAllPlayersFitToScreen_RND(void)
5422 {
5423   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5424
5425   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5426
5427   return (sx2 - sx1 < SCR_FIELDX &&
5428           sy2 - sy1 < SCR_FIELDY);
5429 }
5430
5431 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5432 {
5433   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5434
5435   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5436
5437   *sx = (sx1 + sx2) / 2;
5438   *sy = (sy1 + sy2) / 2;
5439 }
5440
5441 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5442                                boolean center_screen, boolean quick_relocation)
5443 {
5444   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5445   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5446   boolean no_delay = (tape.warp_forward);
5447   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5448   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5449   int new_scroll_x, new_scroll_y;
5450
5451   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5452   {
5453     // case 1: quick relocation inside visible screen (without scrolling)
5454
5455     RedrawPlayfield();
5456
5457     return;
5458   }
5459
5460   if (!level.shifted_relocation || center_screen)
5461   {
5462     // relocation _with_ centering of screen
5463
5464     new_scroll_x = SCROLL_POSITION_X(x);
5465     new_scroll_y = SCROLL_POSITION_Y(y);
5466   }
5467   else
5468   {
5469     // relocation _without_ centering of screen
5470
5471     int center_scroll_x = SCROLL_POSITION_X(old_x);
5472     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5473     int offset_x = x + (scroll_x - center_scroll_x);
5474     int offset_y = y + (scroll_y - center_scroll_y);
5475
5476     // for new screen position, apply previous offset to center position
5477     new_scroll_x = SCROLL_POSITION_X(offset_x);
5478     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5479   }
5480
5481   if (quick_relocation)
5482   {
5483     // case 2: quick relocation (redraw without visible scrolling)
5484
5485     scroll_x = new_scroll_x;
5486     scroll_y = new_scroll_y;
5487
5488     RedrawPlayfield();
5489
5490     return;
5491   }
5492
5493   // case 3: visible relocation (with scrolling to new position)
5494
5495   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5496
5497   SetVideoFrameDelay(wait_delay_value);
5498
5499   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5500   {
5501     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5502     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5503
5504     if (dx == 0 && dy == 0)             // no scrolling needed at all
5505       break;
5506
5507     scroll_x -= dx;
5508     scroll_y -= dy;
5509
5510     // set values for horizontal/vertical screen scrolling (half tile size)
5511     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5512     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5513     int pos_x = dx * TILEX / 2;
5514     int pos_y = dy * TILEY / 2;
5515     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5516     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5517
5518     ScrollLevel(dx, dy);
5519     DrawAllPlayers();
5520
5521     // scroll in two steps of half tile size to make things smoother
5522     BlitScreenToBitmapExt_RND(window, fx, fy);
5523
5524     // scroll second step to align at full tile size
5525     BlitScreenToBitmap(window);
5526   }
5527
5528   DrawAllPlayers();
5529   BackToFront();
5530
5531   SetVideoFrameDelay(frame_delay_value_old);
5532 }
5533
5534 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5535 {
5536   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5537   int player_nr = GET_PLAYER_NR(el_player);
5538   struct PlayerInfo *player = &stored_player[player_nr];
5539   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5540   boolean no_delay = (tape.warp_forward);
5541   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5542   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5543   int old_jx = player->jx;
5544   int old_jy = player->jy;
5545   int old_element = Tile[old_jx][old_jy];
5546   int element = Tile[jx][jy];
5547   boolean player_relocated = (old_jx != jx || old_jy != jy);
5548
5549   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5550   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5551   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5552   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5553   int leave_side_horiz = move_dir_horiz;
5554   int leave_side_vert  = move_dir_vert;
5555   int enter_side = enter_side_horiz | enter_side_vert;
5556   int leave_side = leave_side_horiz | leave_side_vert;
5557
5558   if (player->buried)           // do not reanimate dead player
5559     return;
5560
5561   if (!player_relocated)        // no need to relocate the player
5562     return;
5563
5564   if (IS_PLAYER(jx, jy))        // player already placed at new position
5565   {
5566     RemoveField(jx, jy);        // temporarily remove newly placed player
5567     DrawLevelField(jx, jy);
5568   }
5569
5570   if (player->present)
5571   {
5572     while (player->MovPos)
5573     {
5574       ScrollPlayer(player, SCROLL_GO_ON);
5575       ScrollScreen(NULL, SCROLL_GO_ON);
5576
5577       AdvanceFrameAndPlayerCounters(player->index_nr);
5578
5579       DrawPlayer(player);
5580
5581       BackToFront_WithFrameDelay(wait_delay_value);
5582     }
5583
5584     DrawPlayer(player);         // needed here only to cleanup last field
5585     DrawLevelField(player->jx, player->jy);     // remove player graphic
5586
5587     player->is_moving = FALSE;
5588   }
5589
5590   if (IS_CUSTOM_ELEMENT(old_element))
5591     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5592                                CE_LEFT_BY_PLAYER,
5593                                player->index_bit, leave_side);
5594
5595   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5596                                       CE_PLAYER_LEAVES_X,
5597                                       player->index_bit, leave_side);
5598
5599   Tile[jx][jy] = el_player;
5600   InitPlayerField(jx, jy, el_player, TRUE);
5601
5602   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5603      possible that the relocation target field did not contain a player element,
5604      but a walkable element, to which the new player was relocated -- in this
5605      case, restore that (already initialized!) element on the player field */
5606   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5607   {
5608     Tile[jx][jy] = element;     // restore previously existing element
5609   }
5610
5611   // only visually relocate centered player
5612   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5613                      FALSE, level.instant_relocation);
5614
5615   TestIfPlayerTouchesBadThing(jx, jy);
5616   TestIfPlayerTouchesCustomElement(jx, jy);
5617
5618   if (IS_CUSTOM_ELEMENT(element))
5619     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5620                                player->index_bit, enter_side);
5621
5622   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5623                                       player->index_bit, enter_side);
5624
5625   if (player->is_switching)
5626   {
5627     /* ensure that relocation while still switching an element does not cause
5628        a new element to be treated as also switched directly after relocation
5629        (this is important for teleporter switches that teleport the player to
5630        a place where another teleporter switch is in the same direction, which
5631        would then incorrectly be treated as immediately switched before the
5632        direction key that caused the switch was released) */
5633
5634     player->switch_x += jx - old_jx;
5635     player->switch_y += jy - old_jy;
5636   }
5637 }
5638
5639 static void Explode(int ex, int ey, int phase, int mode)
5640 {
5641   int x, y;
5642   int last_phase;
5643   int border_element;
5644
5645   // !!! eliminate this variable !!!
5646   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5647
5648   if (game.explosions_delayed)
5649   {
5650     ExplodeField[ex][ey] = mode;
5651     return;
5652   }
5653
5654   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5655   {
5656     int center_element = Tile[ex][ey];
5657     int artwork_element, explosion_element;     // set these values later
5658
5659     // remove things displayed in background while burning dynamite
5660     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5661       Back[ex][ey] = 0;
5662
5663     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5664     {
5665       // put moving element to center field (and let it explode there)
5666       center_element = MovingOrBlocked2Element(ex, ey);
5667       RemoveMovingField(ex, ey);
5668       Tile[ex][ey] = center_element;
5669     }
5670
5671     // now "center_element" is finally determined -- set related values now
5672     artwork_element = center_element;           // for custom player artwork
5673     explosion_element = center_element;         // for custom player artwork
5674
5675     if (IS_PLAYER(ex, ey))
5676     {
5677       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5678
5679       artwork_element = stored_player[player_nr].artwork_element;
5680
5681       if (level.use_explosion_element[player_nr])
5682       {
5683         explosion_element = level.explosion_element[player_nr];
5684         artwork_element = explosion_element;
5685       }
5686     }
5687
5688     if (mode == EX_TYPE_NORMAL ||
5689         mode == EX_TYPE_CENTER ||
5690         mode == EX_TYPE_CROSS)
5691       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5692
5693     last_phase = element_info[explosion_element].explosion_delay + 1;
5694
5695     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5696     {
5697       int xx = x - ex + 1;
5698       int yy = y - ey + 1;
5699       int element;
5700
5701       if (!IN_LEV_FIELD(x, y) ||
5702           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5703           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5704         continue;
5705
5706       element = Tile[x][y];
5707
5708       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5709       {
5710         element = MovingOrBlocked2Element(x, y);
5711
5712         if (!IS_EXPLOSION_PROOF(element))
5713           RemoveMovingField(x, y);
5714       }
5715
5716       // indestructible elements can only explode in center (but not flames)
5717       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5718                                            mode == EX_TYPE_BORDER)) ||
5719           element == EL_FLAMES)
5720         continue;
5721
5722       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5723          behaviour, for example when touching a yamyam that explodes to rocks
5724          with active deadly shield, a rock is created under the player !!! */
5725       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5726 #if 0
5727       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5728           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5729            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5730 #else
5731       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5732 #endif
5733       {
5734         if (IS_ACTIVE_BOMB(element))
5735         {
5736           // re-activate things under the bomb like gate or penguin
5737           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5738           Back[x][y] = 0;
5739         }
5740
5741         continue;
5742       }
5743
5744       // save walkable background elements while explosion on same tile
5745       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5746           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5747         Back[x][y] = element;
5748
5749       // ignite explodable elements reached by other explosion
5750       if (element == EL_EXPLOSION)
5751         element = Store2[x][y];
5752
5753       if (AmoebaNr[x][y] &&
5754           (element == EL_AMOEBA_FULL ||
5755            element == EL_BD_AMOEBA ||
5756            element == EL_AMOEBA_GROWING))
5757       {
5758         AmoebaCnt[AmoebaNr[x][y]]--;
5759         AmoebaCnt2[AmoebaNr[x][y]]--;
5760       }
5761
5762       RemoveField(x, y);
5763
5764       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5765       {
5766         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5767
5768         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5769
5770         if (PLAYERINFO(ex, ey)->use_murphy)
5771           Store[x][y] = EL_EMPTY;
5772       }
5773
5774       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5775       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5776       else if (ELEM_IS_PLAYER(center_element))
5777         Store[x][y] = EL_EMPTY;
5778       else if (center_element == EL_YAMYAM)
5779         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5780       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5781         Store[x][y] = element_info[center_element].content.e[xx][yy];
5782 #if 1
5783       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5784       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5785       // otherwise) -- FIX THIS !!!
5786       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5787         Store[x][y] = element_info[element].content.e[1][1];
5788 #else
5789       else if (!CAN_EXPLODE(element))
5790         Store[x][y] = element_info[element].content.e[1][1];
5791 #endif
5792       else
5793         Store[x][y] = EL_EMPTY;
5794
5795       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5796           center_element == EL_AMOEBA_TO_DIAMOND)
5797         Store2[x][y] = element;
5798
5799       Tile[x][y] = EL_EXPLOSION;
5800       GfxElement[x][y] = artwork_element;
5801
5802       ExplodePhase[x][y] = 1;
5803       ExplodeDelay[x][y] = last_phase;
5804
5805       Stop[x][y] = TRUE;
5806     }
5807
5808     if (center_element == EL_YAMYAM)
5809       game.yamyam_content_nr =
5810         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5811
5812     return;
5813   }
5814
5815   if (Stop[ex][ey])
5816     return;
5817
5818   x = ex;
5819   y = ey;
5820
5821   if (phase == 1)
5822     GfxFrame[x][y] = 0;         // restart explosion animation
5823
5824   last_phase = ExplodeDelay[x][y];
5825
5826   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5827
5828   // this can happen if the player leaves an explosion just in time
5829   if (GfxElement[x][y] == EL_UNDEFINED)
5830     GfxElement[x][y] = EL_EMPTY;
5831
5832   border_element = Store2[x][y];
5833   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5834     border_element = StorePlayer[x][y];
5835
5836   if (phase == element_info[border_element].ignition_delay ||
5837       phase == last_phase)
5838   {
5839     boolean border_explosion = FALSE;
5840
5841     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5842         !PLAYER_EXPLOSION_PROTECTED(x, y))
5843     {
5844       KillPlayerUnlessExplosionProtected(x, y);
5845       border_explosion = TRUE;
5846     }
5847     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5848     {
5849       Tile[x][y] = Store2[x][y];
5850       Store2[x][y] = 0;
5851       Bang(x, y);
5852       border_explosion = TRUE;
5853     }
5854     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5855     {
5856       AmoebaToDiamond(x, y);
5857       Store2[x][y] = 0;
5858       border_explosion = TRUE;
5859     }
5860
5861     // if an element just explodes due to another explosion (chain-reaction),
5862     // do not immediately end the new explosion when it was the last frame of
5863     // the explosion (as it would be done in the following "if"-statement!)
5864     if (border_explosion && phase == last_phase)
5865       return;
5866   }
5867
5868   if (phase == last_phase)
5869   {
5870     int element;
5871
5872     element = Tile[x][y] = Store[x][y];
5873     Store[x][y] = Store2[x][y] = 0;
5874     GfxElement[x][y] = EL_UNDEFINED;
5875
5876     // player can escape from explosions and might therefore be still alive
5877     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5878         element <= EL_PLAYER_IS_EXPLODING_4)
5879     {
5880       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5881       int explosion_element = EL_PLAYER_1 + player_nr;
5882       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5883       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5884
5885       if (level.use_explosion_element[player_nr])
5886         explosion_element = level.explosion_element[player_nr];
5887
5888       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5889                     element_info[explosion_element].content.e[xx][yy]);
5890     }
5891
5892     // restore probably existing indestructible background element
5893     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5894       element = Tile[x][y] = Back[x][y];
5895     Back[x][y] = 0;
5896
5897     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5898     GfxDir[x][y] = MV_NONE;
5899     ChangeDelay[x][y] = 0;
5900     ChangePage[x][y] = -1;
5901
5902     CustomValue[x][y] = 0;
5903
5904     InitField_WithBug2(x, y, FALSE);
5905
5906     TEST_DrawLevelField(x, y);
5907
5908     TestIfElementTouchesCustomElement(x, y);
5909
5910     if (GFX_CRUMBLED(element))
5911       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5912
5913     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5914       StorePlayer[x][y] = 0;
5915
5916     if (ELEM_IS_PLAYER(element))
5917       RelocatePlayer(x, y, element);
5918   }
5919   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5920   {
5921     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5922     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5923
5924     if (phase == delay)
5925       TEST_DrawLevelFieldCrumbled(x, y);
5926
5927     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5928     {
5929       DrawLevelElement(x, y, Back[x][y]);
5930       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5931     }
5932     else if (IS_WALKABLE_UNDER(Back[x][y]))
5933     {
5934       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5935       DrawLevelElementThruMask(x, y, Back[x][y]);
5936     }
5937     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5938       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5939   }
5940 }
5941
5942 static void DynaExplode(int ex, int ey)
5943 {
5944   int i, j;
5945   int dynabomb_element = Tile[ex][ey];
5946   int dynabomb_size = 1;
5947   boolean dynabomb_xl = FALSE;
5948   struct PlayerInfo *player;
5949   static int xy[4][2] =
5950   {
5951     { 0, -1 },
5952     { -1, 0 },
5953     { +1, 0 },
5954     { 0, +1 }
5955   };
5956
5957   if (IS_ACTIVE_BOMB(dynabomb_element))
5958   {
5959     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5960     dynabomb_size = player->dynabomb_size;
5961     dynabomb_xl = player->dynabomb_xl;
5962     player->dynabombs_left++;
5963   }
5964
5965   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5966
5967   for (i = 0; i < NUM_DIRECTIONS; i++)
5968   {
5969     for (j = 1; j <= dynabomb_size; j++)
5970     {
5971       int x = ex + j * xy[i][0];
5972       int y = ey + j * xy[i][1];
5973       int element;
5974
5975       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5976         break;
5977
5978       element = Tile[x][y];
5979
5980       // do not restart explosions of fields with active bombs
5981       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5982         continue;
5983
5984       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5985
5986       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5987           !IS_DIGGABLE(element) && !dynabomb_xl)
5988         break;
5989     }
5990   }
5991 }
5992
5993 void Bang(int x, int y)
5994 {
5995   int element = MovingOrBlocked2Element(x, y);
5996   int explosion_type = EX_TYPE_NORMAL;
5997
5998   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5999   {
6000     struct PlayerInfo *player = PLAYERINFO(x, y);
6001
6002     element = Tile[x][y] = player->initial_element;
6003
6004     if (level.use_explosion_element[player->index_nr])
6005     {
6006       int explosion_element = level.explosion_element[player->index_nr];
6007
6008       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6009         explosion_type = EX_TYPE_CROSS;
6010       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6011         explosion_type = EX_TYPE_CENTER;
6012     }
6013   }
6014
6015   switch (element)
6016   {
6017     case EL_BUG:
6018     case EL_SPACESHIP:
6019     case EL_BD_BUTTERFLY:
6020     case EL_BD_FIREFLY:
6021     case EL_YAMYAM:
6022     case EL_DARK_YAMYAM:
6023     case EL_ROBOT:
6024     case EL_PACMAN:
6025     case EL_MOLE:
6026       RaiseScoreElement(element);
6027       break;
6028
6029     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6030     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6031     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6032     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6033     case EL_DYNABOMB_INCREASE_NUMBER:
6034     case EL_DYNABOMB_INCREASE_SIZE:
6035     case EL_DYNABOMB_INCREASE_POWER:
6036       explosion_type = EX_TYPE_DYNA;
6037       break;
6038
6039     case EL_DC_LANDMINE:
6040       explosion_type = EX_TYPE_CENTER;
6041       break;
6042
6043     case EL_PENGUIN:
6044     case EL_LAMP:
6045     case EL_LAMP_ACTIVE:
6046     case EL_AMOEBA_TO_DIAMOND:
6047       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6048         explosion_type = EX_TYPE_CENTER;
6049       break;
6050
6051     default:
6052       if (element_info[element].explosion_type == EXPLODES_CROSS)
6053         explosion_type = EX_TYPE_CROSS;
6054       else if (element_info[element].explosion_type == EXPLODES_1X1)
6055         explosion_type = EX_TYPE_CENTER;
6056       break;
6057   }
6058
6059   if (explosion_type == EX_TYPE_DYNA)
6060     DynaExplode(x, y);
6061   else
6062     Explode(x, y, EX_PHASE_START, explosion_type);
6063
6064   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6065 }
6066
6067 static void SplashAcid(int x, int y)
6068 {
6069   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6070       (!IN_LEV_FIELD(x - 1, y - 2) ||
6071        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6072     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6073
6074   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6075       (!IN_LEV_FIELD(x + 1, y - 2) ||
6076        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6077     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6078
6079   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6080 }
6081
6082 static void InitBeltMovement(void)
6083 {
6084   static int belt_base_element[4] =
6085   {
6086     EL_CONVEYOR_BELT_1_LEFT,
6087     EL_CONVEYOR_BELT_2_LEFT,
6088     EL_CONVEYOR_BELT_3_LEFT,
6089     EL_CONVEYOR_BELT_4_LEFT
6090   };
6091   static int belt_base_active_element[4] =
6092   {
6093     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6094     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6095     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6096     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6097   };
6098
6099   int x, y, i, j;
6100
6101   // set frame order for belt animation graphic according to belt direction
6102   for (i = 0; i < NUM_BELTS; i++)
6103   {
6104     int belt_nr = i;
6105
6106     for (j = 0; j < NUM_BELT_PARTS; j++)
6107     {
6108       int element = belt_base_active_element[belt_nr] + j;
6109       int graphic_1 = el2img(element);
6110       int graphic_2 = el2panelimg(element);
6111
6112       if (game.belt_dir[i] == MV_LEFT)
6113       {
6114         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6115         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6116       }
6117       else
6118       {
6119         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6120         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6121       }
6122     }
6123   }
6124
6125   SCAN_PLAYFIELD(x, y)
6126   {
6127     int element = Tile[x][y];
6128
6129     for (i = 0; i < NUM_BELTS; i++)
6130     {
6131       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6132       {
6133         int e_belt_nr = getBeltNrFromBeltElement(element);
6134         int belt_nr = i;
6135
6136         if (e_belt_nr == belt_nr)
6137         {
6138           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6139
6140           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6141         }
6142       }
6143     }
6144   }
6145 }
6146
6147 static void ToggleBeltSwitch(int x, int y)
6148 {
6149   static int belt_base_element[4] =
6150   {
6151     EL_CONVEYOR_BELT_1_LEFT,
6152     EL_CONVEYOR_BELT_2_LEFT,
6153     EL_CONVEYOR_BELT_3_LEFT,
6154     EL_CONVEYOR_BELT_4_LEFT
6155   };
6156   static int belt_base_active_element[4] =
6157   {
6158     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6159     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6160     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6161     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6162   };
6163   static int belt_base_switch_element[4] =
6164   {
6165     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6166     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6167     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6168     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6169   };
6170   static int belt_move_dir[4] =
6171   {
6172     MV_LEFT,
6173     MV_NONE,
6174     MV_RIGHT,
6175     MV_NONE,
6176   };
6177
6178   int element = Tile[x][y];
6179   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6180   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6181   int belt_dir = belt_move_dir[belt_dir_nr];
6182   int xx, yy, i;
6183
6184   if (!IS_BELT_SWITCH(element))
6185     return;
6186
6187   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6188   game.belt_dir[belt_nr] = belt_dir;
6189
6190   if (belt_dir_nr == 3)
6191     belt_dir_nr = 1;
6192
6193   // set frame order for belt animation graphic according to belt direction
6194   for (i = 0; i < NUM_BELT_PARTS; i++)
6195   {
6196     int element = belt_base_active_element[belt_nr] + i;
6197     int graphic_1 = el2img(element);
6198     int graphic_2 = el2panelimg(element);
6199
6200     if (belt_dir == MV_LEFT)
6201     {
6202       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6203       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6204     }
6205     else
6206     {
6207       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6208       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6209     }
6210   }
6211
6212   SCAN_PLAYFIELD(xx, yy)
6213   {
6214     int element = Tile[xx][yy];
6215
6216     if (IS_BELT_SWITCH(element))
6217     {
6218       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6219
6220       if (e_belt_nr == belt_nr)
6221       {
6222         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6223         TEST_DrawLevelField(xx, yy);
6224       }
6225     }
6226     else if (IS_BELT(element) && belt_dir != MV_NONE)
6227     {
6228       int e_belt_nr = getBeltNrFromBeltElement(element);
6229
6230       if (e_belt_nr == belt_nr)
6231       {
6232         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6233
6234         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6235         TEST_DrawLevelField(xx, yy);
6236       }
6237     }
6238     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6239     {
6240       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6241
6242       if (e_belt_nr == belt_nr)
6243       {
6244         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6245
6246         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6247         TEST_DrawLevelField(xx, yy);
6248       }
6249     }
6250   }
6251 }
6252
6253 static void ToggleSwitchgateSwitch(int x, int y)
6254 {
6255   int xx, yy;
6256
6257   game.switchgate_pos = !game.switchgate_pos;
6258
6259   SCAN_PLAYFIELD(xx, yy)
6260   {
6261     int element = Tile[xx][yy];
6262
6263     if (element == EL_SWITCHGATE_SWITCH_UP)
6264     {
6265       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6266       TEST_DrawLevelField(xx, yy);
6267     }
6268     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6269     {
6270       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6271       TEST_DrawLevelField(xx, yy);
6272     }
6273     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6274     {
6275       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6276       TEST_DrawLevelField(xx, yy);
6277     }
6278     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6279     {
6280       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6281       TEST_DrawLevelField(xx, yy);
6282     }
6283     else if (element == EL_SWITCHGATE_OPEN ||
6284              element == EL_SWITCHGATE_OPENING)
6285     {
6286       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6287
6288       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6289     }
6290     else if (element == EL_SWITCHGATE_CLOSED ||
6291              element == EL_SWITCHGATE_CLOSING)
6292     {
6293       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6294
6295       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6296     }
6297   }
6298 }
6299
6300 static int getInvisibleActiveFromInvisibleElement(int element)
6301 {
6302   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6303           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6304           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6305           element);
6306 }
6307
6308 static int getInvisibleFromInvisibleActiveElement(int element)
6309 {
6310   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6311           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6312           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6313           element);
6314 }
6315
6316 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6317 {
6318   int x, y;
6319
6320   SCAN_PLAYFIELD(x, y)
6321   {
6322     int element = Tile[x][y];
6323
6324     if (element == EL_LIGHT_SWITCH &&
6325         game.light_time_left > 0)
6326     {
6327       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6328       TEST_DrawLevelField(x, y);
6329     }
6330     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6331              game.light_time_left == 0)
6332     {
6333       Tile[x][y] = EL_LIGHT_SWITCH;
6334       TEST_DrawLevelField(x, y);
6335     }
6336     else if (element == EL_EMC_DRIPPER &&
6337              game.light_time_left > 0)
6338     {
6339       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6340       TEST_DrawLevelField(x, y);
6341     }
6342     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6343              game.light_time_left == 0)
6344     {
6345       Tile[x][y] = EL_EMC_DRIPPER;
6346       TEST_DrawLevelField(x, y);
6347     }
6348     else if (element == EL_INVISIBLE_STEELWALL ||
6349              element == EL_INVISIBLE_WALL ||
6350              element == EL_INVISIBLE_SAND)
6351     {
6352       if (game.light_time_left > 0)
6353         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6354
6355       TEST_DrawLevelField(x, y);
6356
6357       // uncrumble neighbour fields, if needed
6358       if (element == EL_INVISIBLE_SAND)
6359         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6360     }
6361     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6362              element == EL_INVISIBLE_WALL_ACTIVE ||
6363              element == EL_INVISIBLE_SAND_ACTIVE)
6364     {
6365       if (game.light_time_left == 0)
6366         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6367
6368       TEST_DrawLevelField(x, y);
6369
6370       // re-crumble neighbour fields, if needed
6371       if (element == EL_INVISIBLE_SAND)
6372         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6373     }
6374   }
6375 }
6376
6377 static void RedrawAllInvisibleElementsForLenses(void)
6378 {
6379   int x, y;
6380
6381   SCAN_PLAYFIELD(x, y)
6382   {
6383     int element = Tile[x][y];
6384
6385     if (element == EL_EMC_DRIPPER &&
6386         game.lenses_time_left > 0)
6387     {
6388       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6389       TEST_DrawLevelField(x, y);
6390     }
6391     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6392              game.lenses_time_left == 0)
6393     {
6394       Tile[x][y] = EL_EMC_DRIPPER;
6395       TEST_DrawLevelField(x, y);
6396     }
6397     else if (element == EL_INVISIBLE_STEELWALL ||
6398              element == EL_INVISIBLE_WALL ||
6399              element == EL_INVISIBLE_SAND)
6400     {
6401       if (game.lenses_time_left > 0)
6402         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6403
6404       TEST_DrawLevelField(x, y);
6405
6406       // uncrumble neighbour fields, if needed
6407       if (element == EL_INVISIBLE_SAND)
6408         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6409     }
6410     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6411              element == EL_INVISIBLE_WALL_ACTIVE ||
6412              element == EL_INVISIBLE_SAND_ACTIVE)
6413     {
6414       if (game.lenses_time_left == 0)
6415         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6416
6417       TEST_DrawLevelField(x, y);
6418
6419       // re-crumble neighbour fields, if needed
6420       if (element == EL_INVISIBLE_SAND)
6421         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6422     }
6423   }
6424 }
6425
6426 static void RedrawAllInvisibleElementsForMagnifier(void)
6427 {
6428   int x, y;
6429
6430   SCAN_PLAYFIELD(x, y)
6431   {
6432     int element = Tile[x][y];
6433
6434     if (element == EL_EMC_FAKE_GRASS &&
6435         game.magnify_time_left > 0)
6436     {
6437       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6438       TEST_DrawLevelField(x, y);
6439     }
6440     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6441              game.magnify_time_left == 0)
6442     {
6443       Tile[x][y] = EL_EMC_FAKE_GRASS;
6444       TEST_DrawLevelField(x, y);
6445     }
6446     else if (IS_GATE_GRAY(element) &&
6447              game.magnify_time_left > 0)
6448     {
6449       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6450                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6451                     IS_EM_GATE_GRAY(element) ?
6452                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6453                     IS_EMC_GATE_GRAY(element) ?
6454                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6455                     IS_DC_GATE_GRAY(element) ?
6456                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6457                     element);
6458       TEST_DrawLevelField(x, y);
6459     }
6460     else if (IS_GATE_GRAY_ACTIVE(element) &&
6461              game.magnify_time_left == 0)
6462     {
6463       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6464                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6465                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6466                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6467                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6468                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6469                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6470                     EL_DC_GATE_WHITE_GRAY :
6471                     element);
6472       TEST_DrawLevelField(x, y);
6473     }
6474   }
6475 }
6476
6477 static void ToggleLightSwitch(int x, int y)
6478 {
6479   int element = Tile[x][y];
6480
6481   game.light_time_left =
6482     (element == EL_LIGHT_SWITCH ?
6483      level.time_light * FRAMES_PER_SECOND : 0);
6484
6485   RedrawAllLightSwitchesAndInvisibleElements();
6486 }
6487
6488 static void ActivateTimegateSwitch(int x, int y)
6489 {
6490   int xx, yy;
6491
6492   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6493
6494   SCAN_PLAYFIELD(xx, yy)
6495   {
6496     int element = Tile[xx][yy];
6497
6498     if (element == EL_TIMEGATE_CLOSED ||
6499         element == EL_TIMEGATE_CLOSING)
6500     {
6501       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6502       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6503     }
6504
6505     /*
6506     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6507     {
6508       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6509       TEST_DrawLevelField(xx, yy);
6510     }
6511     */
6512
6513   }
6514
6515   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6516                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6517 }
6518
6519 static void Impact(int x, int y)
6520 {
6521   boolean last_line = (y == lev_fieldy - 1);
6522   boolean object_hit = FALSE;
6523   boolean impact = (last_line || object_hit);
6524   int element = Tile[x][y];
6525   int smashed = EL_STEELWALL;
6526
6527   if (!last_line)       // check if element below was hit
6528   {
6529     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6530       return;
6531
6532     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6533                                          MovDir[x][y + 1] != MV_DOWN ||
6534                                          MovPos[x][y + 1] <= TILEY / 2));
6535
6536     // do not smash moving elements that left the smashed field in time
6537     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6538         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6539       object_hit = FALSE;
6540
6541 #if USE_QUICKSAND_IMPACT_BUGFIX
6542     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6543     {
6544       RemoveMovingField(x, y + 1);
6545       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6546       Tile[x][y + 2] = EL_ROCK;
6547       TEST_DrawLevelField(x, y + 2);
6548
6549       object_hit = TRUE;
6550     }
6551
6552     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6553     {
6554       RemoveMovingField(x, y + 1);
6555       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6556       Tile[x][y + 2] = EL_ROCK;
6557       TEST_DrawLevelField(x, y + 2);
6558
6559       object_hit = TRUE;
6560     }
6561 #endif
6562
6563     if (object_hit)
6564       smashed = MovingOrBlocked2Element(x, y + 1);
6565
6566     impact = (last_line || object_hit);
6567   }
6568
6569   if (!last_line && smashed == EL_ACID) // element falls into acid
6570   {
6571     SplashAcid(x, y + 1);
6572     return;
6573   }
6574
6575   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6576   // only reset graphic animation if graphic really changes after impact
6577   if (impact &&
6578       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6579   {
6580     ResetGfxAnimation(x, y);
6581     TEST_DrawLevelField(x, y);
6582   }
6583
6584   if (impact && CAN_EXPLODE_IMPACT(element))
6585   {
6586     Bang(x, y);
6587     return;
6588   }
6589   else if (impact && element == EL_PEARL &&
6590            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6591   {
6592     ResetGfxAnimation(x, y);
6593
6594     Tile[x][y] = EL_PEARL_BREAKING;
6595     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6596     return;
6597   }
6598   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6599   {
6600     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6601
6602     return;
6603   }
6604
6605   if (impact && element == EL_AMOEBA_DROP)
6606   {
6607     if (object_hit && IS_PLAYER(x, y + 1))
6608       KillPlayerUnlessEnemyProtected(x, y + 1);
6609     else if (object_hit && smashed == EL_PENGUIN)
6610       Bang(x, y + 1);
6611     else
6612     {
6613       Tile[x][y] = EL_AMOEBA_GROWING;
6614       Store[x][y] = EL_AMOEBA_WET;
6615
6616       ResetRandomAnimationValue(x, y);
6617     }
6618     return;
6619   }
6620
6621   if (object_hit)               // check which object was hit
6622   {
6623     if ((CAN_PASS_MAGIC_WALL(element) && 
6624          (smashed == EL_MAGIC_WALL ||
6625           smashed == EL_BD_MAGIC_WALL)) ||
6626         (CAN_PASS_DC_MAGIC_WALL(element) &&
6627          smashed == EL_DC_MAGIC_WALL))
6628     {
6629       int xx, yy;
6630       int activated_magic_wall =
6631         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6632          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6633          EL_DC_MAGIC_WALL_ACTIVE);
6634
6635       // activate magic wall / mill
6636       SCAN_PLAYFIELD(xx, yy)
6637       {
6638         if (Tile[xx][yy] == smashed)
6639           Tile[xx][yy] = activated_magic_wall;
6640       }
6641
6642       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6643       game.magic_wall_active = TRUE;
6644
6645       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6646                             SND_MAGIC_WALL_ACTIVATING :
6647                             smashed == EL_BD_MAGIC_WALL ?
6648                             SND_BD_MAGIC_WALL_ACTIVATING :
6649                             SND_DC_MAGIC_WALL_ACTIVATING));
6650     }
6651
6652     if (IS_PLAYER(x, y + 1))
6653     {
6654       if (CAN_SMASH_PLAYER(element))
6655       {
6656         KillPlayerUnlessEnemyProtected(x, y + 1);
6657         return;
6658       }
6659     }
6660     else if (smashed == EL_PENGUIN)
6661     {
6662       if (CAN_SMASH_PLAYER(element))
6663       {
6664         Bang(x, y + 1);
6665         return;
6666       }
6667     }
6668     else if (element == EL_BD_DIAMOND)
6669     {
6670       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6671       {
6672         Bang(x, y + 1);
6673         return;
6674       }
6675     }
6676     else if (((element == EL_SP_INFOTRON ||
6677                element == EL_SP_ZONK) &&
6678               (smashed == EL_SP_SNIKSNAK ||
6679                smashed == EL_SP_ELECTRON ||
6680                smashed == EL_SP_DISK_ORANGE)) ||
6681              (element == EL_SP_INFOTRON &&
6682               smashed == EL_SP_DISK_YELLOW))
6683     {
6684       Bang(x, y + 1);
6685       return;
6686     }
6687     else if (CAN_SMASH_EVERYTHING(element))
6688     {
6689       if (IS_CLASSIC_ENEMY(smashed) ||
6690           CAN_EXPLODE_SMASHED(smashed))
6691       {
6692         Bang(x, y + 1);
6693         return;
6694       }
6695       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6696       {
6697         if (smashed == EL_LAMP ||
6698             smashed == EL_LAMP_ACTIVE)
6699         {
6700           Bang(x, y + 1);
6701           return;
6702         }
6703         else if (smashed == EL_NUT)
6704         {
6705           Tile[x][y + 1] = EL_NUT_BREAKING;
6706           PlayLevelSound(x, y, SND_NUT_BREAKING);
6707           RaiseScoreElement(EL_NUT);
6708           return;
6709         }
6710         else if (smashed == EL_PEARL)
6711         {
6712           ResetGfxAnimation(x, y);
6713
6714           Tile[x][y + 1] = EL_PEARL_BREAKING;
6715           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6716           return;
6717         }
6718         else if (smashed == EL_DIAMOND)
6719         {
6720           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6721           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6722           return;
6723         }
6724         else if (IS_BELT_SWITCH(smashed))
6725         {
6726           ToggleBeltSwitch(x, y + 1);
6727         }
6728         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6729                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6730                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6731                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6732         {
6733           ToggleSwitchgateSwitch(x, y + 1);
6734         }
6735         else if (smashed == EL_LIGHT_SWITCH ||
6736                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6737         {
6738           ToggleLightSwitch(x, y + 1);
6739         }
6740         else
6741         {
6742           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6743
6744           CheckElementChangeBySide(x, y + 1, smashed, element,
6745                                    CE_SWITCHED, CH_SIDE_TOP);
6746           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6747                                             CH_SIDE_TOP);
6748         }
6749       }
6750       else
6751       {
6752         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6753       }
6754     }
6755   }
6756
6757   // play sound of magic wall / mill
6758   if (!last_line &&
6759       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6760        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6761        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6762   {
6763     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6764       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6765     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6766       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6767     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6768       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6769
6770     return;
6771   }
6772
6773   // play sound of object that hits the ground
6774   if (last_line || object_hit)
6775     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6776 }
6777
6778 static void TurnRoundExt(int x, int y)
6779 {
6780   static struct
6781   {
6782     int dx, dy;
6783   } move_xy[] =
6784   {
6785     {  0,  0 },
6786     { -1,  0 },
6787     { +1,  0 },
6788     {  0,  0 },
6789     {  0, -1 },
6790     {  0,  0 }, { 0, 0 }, { 0, 0 },
6791     {  0, +1 }
6792   };
6793   static struct
6794   {
6795     int left, right, back;
6796   } turn[] =
6797   {
6798     { 0,        0,              0        },
6799     { MV_DOWN,  MV_UP,          MV_RIGHT },
6800     { MV_UP,    MV_DOWN,        MV_LEFT  },
6801     { 0,        0,              0        },
6802     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6803     { 0,        0,              0        },
6804     { 0,        0,              0        },
6805     { 0,        0,              0        },
6806     { MV_RIGHT, MV_LEFT,        MV_UP    }
6807   };
6808
6809   int element = Tile[x][y];
6810   int move_pattern = element_info[element].move_pattern;
6811
6812   int old_move_dir = MovDir[x][y];
6813   int left_dir  = turn[old_move_dir].left;
6814   int right_dir = turn[old_move_dir].right;
6815   int back_dir  = turn[old_move_dir].back;
6816
6817   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6818   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6819   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6820   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6821
6822   int left_x  = x + left_dx,  left_y  = y + left_dy;
6823   int right_x = x + right_dx, right_y = y + right_dy;
6824   int move_x  = x + move_dx,  move_y  = y + move_dy;
6825
6826   int xx, yy;
6827
6828   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6829   {
6830     TestIfBadThingTouchesOtherBadThing(x, y);
6831
6832     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6833       MovDir[x][y] = right_dir;
6834     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6835       MovDir[x][y] = left_dir;
6836
6837     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6838       MovDelay[x][y] = 9;
6839     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6840       MovDelay[x][y] = 1;
6841   }
6842   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6843   {
6844     TestIfBadThingTouchesOtherBadThing(x, y);
6845
6846     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6847       MovDir[x][y] = left_dir;
6848     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6849       MovDir[x][y] = right_dir;
6850
6851     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6852       MovDelay[x][y] = 9;
6853     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6854       MovDelay[x][y] = 1;
6855   }
6856   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6857   {
6858     TestIfBadThingTouchesOtherBadThing(x, y);
6859
6860     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6861       MovDir[x][y] = left_dir;
6862     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6863       MovDir[x][y] = right_dir;
6864
6865     if (MovDir[x][y] != old_move_dir)
6866       MovDelay[x][y] = 9;
6867   }
6868   else if (element == EL_YAMYAM)
6869   {
6870     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6871     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6872
6873     if (can_turn_left && can_turn_right)
6874       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6875     else if (can_turn_left)
6876       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6877     else if (can_turn_right)
6878       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6879     else
6880       MovDir[x][y] = back_dir;
6881
6882     MovDelay[x][y] = 16 + 16 * RND(3);
6883   }
6884   else if (element == EL_DARK_YAMYAM)
6885   {
6886     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6887                                                          left_x, left_y);
6888     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6889                                                          right_x, right_y);
6890
6891     if (can_turn_left && can_turn_right)
6892       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6893     else if (can_turn_left)
6894       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6895     else if (can_turn_right)
6896       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6897     else
6898       MovDir[x][y] = back_dir;
6899
6900     MovDelay[x][y] = 16 + 16 * RND(3);
6901   }
6902   else if (element == EL_PACMAN)
6903   {
6904     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6905     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6906
6907     if (can_turn_left && can_turn_right)
6908       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6909     else if (can_turn_left)
6910       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6911     else if (can_turn_right)
6912       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6913     else
6914       MovDir[x][y] = back_dir;
6915
6916     MovDelay[x][y] = 6 + RND(40);
6917   }
6918   else if (element == EL_PIG)
6919   {
6920     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6921     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6922     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6923     boolean should_turn_left, should_turn_right, should_move_on;
6924     int rnd_value = 24;
6925     int rnd = RND(rnd_value);
6926
6927     should_turn_left = (can_turn_left &&
6928                         (!can_move_on ||
6929                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6930                                                    y + back_dy + left_dy)));
6931     should_turn_right = (can_turn_right &&
6932                          (!can_move_on ||
6933                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6934                                                     y + back_dy + right_dy)));
6935     should_move_on = (can_move_on &&
6936                       (!can_turn_left ||
6937                        !can_turn_right ||
6938                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6939                                                  y + move_dy + left_dy) ||
6940                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6941                                                  y + move_dy + right_dy)));
6942
6943     if (should_turn_left || should_turn_right || should_move_on)
6944     {
6945       if (should_turn_left && should_turn_right && should_move_on)
6946         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6947                         rnd < 2 * rnd_value / 3 ? right_dir :
6948                         old_move_dir);
6949       else if (should_turn_left && should_turn_right)
6950         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6951       else if (should_turn_left && should_move_on)
6952         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6953       else if (should_turn_right && should_move_on)
6954         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6955       else if (should_turn_left)
6956         MovDir[x][y] = left_dir;
6957       else if (should_turn_right)
6958         MovDir[x][y] = right_dir;
6959       else if (should_move_on)
6960         MovDir[x][y] = old_move_dir;
6961     }
6962     else if (can_move_on && rnd > rnd_value / 8)
6963       MovDir[x][y] = old_move_dir;
6964     else if (can_turn_left && can_turn_right)
6965       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6966     else if (can_turn_left && rnd > rnd_value / 8)
6967       MovDir[x][y] = left_dir;
6968     else if (can_turn_right && rnd > rnd_value/8)
6969       MovDir[x][y] = right_dir;
6970     else
6971       MovDir[x][y] = back_dir;
6972
6973     xx = x + move_xy[MovDir[x][y]].dx;
6974     yy = y + move_xy[MovDir[x][y]].dy;
6975
6976     if (!IN_LEV_FIELD(xx, yy) ||
6977         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6978       MovDir[x][y] = old_move_dir;
6979
6980     MovDelay[x][y] = 0;
6981   }
6982   else if (element == EL_DRAGON)
6983   {
6984     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6985     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6986     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6987     int rnd_value = 24;
6988     int rnd = RND(rnd_value);
6989
6990     if (can_move_on && rnd > rnd_value / 8)
6991       MovDir[x][y] = old_move_dir;
6992     else if (can_turn_left && can_turn_right)
6993       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6994     else if (can_turn_left && rnd > rnd_value / 8)
6995       MovDir[x][y] = left_dir;
6996     else if (can_turn_right && rnd > rnd_value / 8)
6997       MovDir[x][y] = right_dir;
6998     else
6999       MovDir[x][y] = back_dir;
7000
7001     xx = x + move_xy[MovDir[x][y]].dx;
7002     yy = y + move_xy[MovDir[x][y]].dy;
7003
7004     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7005       MovDir[x][y] = old_move_dir;
7006
7007     MovDelay[x][y] = 0;
7008   }
7009   else if (element == EL_MOLE)
7010   {
7011     boolean can_move_on =
7012       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7013                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7014                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7015     if (!can_move_on)
7016     {
7017       boolean can_turn_left =
7018         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7019                               IS_AMOEBOID(Tile[left_x][left_y])));
7020
7021       boolean can_turn_right =
7022         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7023                               IS_AMOEBOID(Tile[right_x][right_y])));
7024
7025       if (can_turn_left && can_turn_right)
7026         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7027       else if (can_turn_left)
7028         MovDir[x][y] = left_dir;
7029       else
7030         MovDir[x][y] = right_dir;
7031     }
7032
7033     if (MovDir[x][y] != old_move_dir)
7034       MovDelay[x][y] = 9;
7035   }
7036   else if (element == EL_BALLOON)
7037   {
7038     MovDir[x][y] = game.wind_direction;
7039     MovDelay[x][y] = 0;
7040   }
7041   else if (element == EL_SPRING)
7042   {
7043     if (MovDir[x][y] & MV_HORIZONTAL)
7044     {
7045       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7046           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7047       {
7048         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7049         ResetGfxAnimation(move_x, move_y);
7050         TEST_DrawLevelField(move_x, move_y);
7051
7052         MovDir[x][y] = back_dir;
7053       }
7054       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7055                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7056         MovDir[x][y] = MV_NONE;
7057     }
7058
7059     MovDelay[x][y] = 0;
7060   }
7061   else if (element == EL_ROBOT ||
7062            element == EL_SATELLITE ||
7063            element == EL_PENGUIN ||
7064            element == EL_EMC_ANDROID)
7065   {
7066     int attr_x = -1, attr_y = -1;
7067
7068     if (game.all_players_gone)
7069     {
7070       attr_x = game.exit_x;
7071       attr_y = game.exit_y;
7072     }
7073     else
7074     {
7075       int i;
7076
7077       for (i = 0; i < MAX_PLAYERS; i++)
7078       {
7079         struct PlayerInfo *player = &stored_player[i];
7080         int jx = player->jx, jy = player->jy;
7081
7082         if (!player->active)
7083           continue;
7084
7085         if (attr_x == -1 ||
7086             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7087         {
7088           attr_x = jx;
7089           attr_y = jy;
7090         }
7091       }
7092     }
7093
7094     if (element == EL_ROBOT &&
7095         game.robot_wheel_x >= 0 &&
7096         game.robot_wheel_y >= 0 &&
7097         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7098          game.engine_version < VERSION_IDENT(3,1,0,0)))
7099     {
7100       attr_x = game.robot_wheel_x;
7101       attr_y = game.robot_wheel_y;
7102     }
7103
7104     if (element == EL_PENGUIN)
7105     {
7106       int i;
7107       static int xy[4][2] =
7108       {
7109         { 0, -1 },
7110         { -1, 0 },
7111         { +1, 0 },
7112         { 0, +1 }
7113       };
7114
7115       for (i = 0; i < NUM_DIRECTIONS; i++)
7116       {
7117         int ex = x + xy[i][0];
7118         int ey = y + xy[i][1];
7119
7120         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7121                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7122                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7123                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7124         {
7125           attr_x = ex;
7126           attr_y = ey;
7127           break;
7128         }
7129       }
7130     }
7131
7132     MovDir[x][y] = MV_NONE;
7133     if (attr_x < x)
7134       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7135     else if (attr_x > x)
7136       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7137     if (attr_y < y)
7138       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7139     else if (attr_y > y)
7140       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7141
7142     if (element == EL_ROBOT)
7143     {
7144       int newx, newy;
7145
7146       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7147         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7148       Moving2Blocked(x, y, &newx, &newy);
7149
7150       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7151         MovDelay[x][y] = 8 + 8 * !RND(3);
7152       else
7153         MovDelay[x][y] = 16;
7154     }
7155     else if (element == EL_PENGUIN)
7156     {
7157       int newx, newy;
7158
7159       MovDelay[x][y] = 1;
7160
7161       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7162       {
7163         boolean first_horiz = RND(2);
7164         int new_move_dir = MovDir[x][y];
7165
7166         MovDir[x][y] =
7167           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7168         Moving2Blocked(x, y, &newx, &newy);
7169
7170         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7171           return;
7172
7173         MovDir[x][y] =
7174           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7175         Moving2Blocked(x, y, &newx, &newy);
7176
7177         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7178           return;
7179
7180         MovDir[x][y] = old_move_dir;
7181         return;
7182       }
7183     }
7184     else if (element == EL_SATELLITE)
7185     {
7186       int newx, newy;
7187
7188       MovDelay[x][y] = 1;
7189
7190       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7191       {
7192         boolean first_horiz = RND(2);
7193         int new_move_dir = MovDir[x][y];
7194
7195         MovDir[x][y] =
7196           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7197         Moving2Blocked(x, y, &newx, &newy);
7198
7199         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7200           return;
7201
7202         MovDir[x][y] =
7203           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7204         Moving2Blocked(x, y, &newx, &newy);
7205
7206         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7207           return;
7208
7209         MovDir[x][y] = old_move_dir;
7210         return;
7211       }
7212     }
7213     else if (element == EL_EMC_ANDROID)
7214     {
7215       static int check_pos[16] =
7216       {
7217         -1,             //  0 => (invalid)
7218         7,              //  1 => MV_LEFT
7219         3,              //  2 => MV_RIGHT
7220         -1,             //  3 => (invalid)
7221         1,              //  4 =>            MV_UP
7222         0,              //  5 => MV_LEFT  | MV_UP
7223         2,              //  6 => MV_RIGHT | MV_UP
7224         -1,             //  7 => (invalid)
7225         5,              //  8 =>            MV_DOWN
7226         6,              //  9 => MV_LEFT  | MV_DOWN
7227         4,              // 10 => MV_RIGHT | MV_DOWN
7228         -1,             // 11 => (invalid)
7229         -1,             // 12 => (invalid)
7230         -1,             // 13 => (invalid)
7231         -1,             // 14 => (invalid)
7232         -1,             // 15 => (invalid)
7233       };
7234       static struct
7235       {
7236         int dx, dy;
7237         int dir;
7238       } check_xy[8] =
7239       {
7240         { -1, -1,       MV_LEFT  | MV_UP   },
7241         {  0, -1,                  MV_UP   },
7242         { +1, -1,       MV_RIGHT | MV_UP   },
7243         { +1,  0,       MV_RIGHT           },
7244         { +1, +1,       MV_RIGHT | MV_DOWN },
7245         {  0, +1,                  MV_DOWN },
7246         { -1, +1,       MV_LEFT  | MV_DOWN },
7247         { -1,  0,       MV_LEFT            },
7248       };
7249       int start_pos, check_order;
7250       boolean can_clone = FALSE;
7251       int i;
7252
7253       // check if there is any free field around current position
7254       for (i = 0; i < 8; i++)
7255       {
7256         int newx = x + check_xy[i].dx;
7257         int newy = y + check_xy[i].dy;
7258
7259         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7260         {
7261           can_clone = TRUE;
7262
7263           break;
7264         }
7265       }
7266
7267       if (can_clone)            // randomly find an element to clone
7268       {
7269         can_clone = FALSE;
7270
7271         start_pos = check_pos[RND(8)];
7272         check_order = (RND(2) ? -1 : +1);
7273
7274         for (i = 0; i < 8; i++)
7275         {
7276           int pos_raw = start_pos + i * check_order;
7277           int pos = (pos_raw + 8) % 8;
7278           int newx = x + check_xy[pos].dx;
7279           int newy = y + check_xy[pos].dy;
7280
7281           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7282           {
7283             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7284             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7285
7286             Store[x][y] = Tile[newx][newy];
7287
7288             can_clone = TRUE;
7289
7290             break;
7291           }
7292         }
7293       }
7294
7295       if (can_clone)            // randomly find a direction to move
7296       {
7297         can_clone = FALSE;
7298
7299         start_pos = check_pos[RND(8)];
7300         check_order = (RND(2) ? -1 : +1);
7301
7302         for (i = 0; i < 8; i++)
7303         {
7304           int pos_raw = start_pos + i * check_order;
7305           int pos = (pos_raw + 8) % 8;
7306           int newx = x + check_xy[pos].dx;
7307           int newy = y + check_xy[pos].dy;
7308           int new_move_dir = check_xy[pos].dir;
7309
7310           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7311           {
7312             MovDir[x][y] = new_move_dir;
7313             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7314
7315             can_clone = TRUE;
7316
7317             break;
7318           }
7319         }
7320       }
7321
7322       if (can_clone)            // cloning and moving successful
7323         return;
7324
7325       // cannot clone -- try to move towards player
7326
7327       start_pos = check_pos[MovDir[x][y] & 0x0f];
7328       check_order = (RND(2) ? -1 : +1);
7329
7330       for (i = 0; i < 3; i++)
7331       {
7332         // first check start_pos, then previous/next or (next/previous) pos
7333         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7334         int pos = (pos_raw + 8) % 8;
7335         int newx = x + check_xy[pos].dx;
7336         int newy = y + check_xy[pos].dy;
7337         int new_move_dir = check_xy[pos].dir;
7338
7339         if (IS_PLAYER(newx, newy))
7340           break;
7341
7342         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7343         {
7344           MovDir[x][y] = new_move_dir;
7345           MovDelay[x][y] = level.android_move_time * 8 + 1;
7346
7347           break;
7348         }
7349       }
7350     }
7351   }
7352   else if (move_pattern == MV_TURNING_LEFT ||
7353            move_pattern == MV_TURNING_RIGHT ||
7354            move_pattern == MV_TURNING_LEFT_RIGHT ||
7355            move_pattern == MV_TURNING_RIGHT_LEFT ||
7356            move_pattern == MV_TURNING_RANDOM ||
7357            move_pattern == MV_ALL_DIRECTIONS)
7358   {
7359     boolean can_turn_left =
7360       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7361     boolean can_turn_right =
7362       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7363
7364     if (element_info[element].move_stepsize == 0)       // "not moving"
7365       return;
7366
7367     if (move_pattern == MV_TURNING_LEFT)
7368       MovDir[x][y] = left_dir;
7369     else if (move_pattern == MV_TURNING_RIGHT)
7370       MovDir[x][y] = right_dir;
7371     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7372       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7373     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7374       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7375     else if (move_pattern == MV_TURNING_RANDOM)
7376       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7377                       can_turn_right && !can_turn_left ? right_dir :
7378                       RND(2) ? left_dir : right_dir);
7379     else if (can_turn_left && can_turn_right)
7380       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7381     else if (can_turn_left)
7382       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7383     else if (can_turn_right)
7384       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7385     else
7386       MovDir[x][y] = back_dir;
7387
7388     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7389   }
7390   else if (move_pattern == MV_HORIZONTAL ||
7391            move_pattern == MV_VERTICAL)
7392   {
7393     if (move_pattern & old_move_dir)
7394       MovDir[x][y] = back_dir;
7395     else if (move_pattern == MV_HORIZONTAL)
7396       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7397     else if (move_pattern == MV_VERTICAL)
7398       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7399
7400     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7401   }
7402   else if (move_pattern & MV_ANY_DIRECTION)
7403   {
7404     MovDir[x][y] = move_pattern;
7405     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7406   }
7407   else if (move_pattern & MV_WIND_DIRECTION)
7408   {
7409     MovDir[x][y] = game.wind_direction;
7410     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7411   }
7412   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7413   {
7414     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7415       MovDir[x][y] = left_dir;
7416     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7417       MovDir[x][y] = right_dir;
7418
7419     if (MovDir[x][y] != old_move_dir)
7420       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7421   }
7422   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7423   {
7424     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7425       MovDir[x][y] = right_dir;
7426     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7427       MovDir[x][y] = left_dir;
7428
7429     if (MovDir[x][y] != old_move_dir)
7430       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7431   }
7432   else if (move_pattern == MV_TOWARDS_PLAYER ||
7433            move_pattern == MV_AWAY_FROM_PLAYER)
7434   {
7435     int attr_x = -1, attr_y = -1;
7436     int newx, newy;
7437     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7438
7439     if (game.all_players_gone)
7440     {
7441       attr_x = game.exit_x;
7442       attr_y = game.exit_y;
7443     }
7444     else
7445     {
7446       int i;
7447
7448       for (i = 0; i < MAX_PLAYERS; i++)
7449       {
7450         struct PlayerInfo *player = &stored_player[i];
7451         int jx = player->jx, jy = player->jy;
7452
7453         if (!player->active)
7454           continue;
7455
7456         if (attr_x == -1 ||
7457             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7458         {
7459           attr_x = jx;
7460           attr_y = jy;
7461         }
7462       }
7463     }
7464
7465     MovDir[x][y] = MV_NONE;
7466     if (attr_x < x)
7467       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7468     else if (attr_x > x)
7469       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7470     if (attr_y < y)
7471       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7472     else if (attr_y > y)
7473       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7474
7475     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7476
7477     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7478     {
7479       boolean first_horiz = RND(2);
7480       int new_move_dir = MovDir[x][y];
7481
7482       if (element_info[element].move_stepsize == 0)     // "not moving"
7483       {
7484         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7485         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7486
7487         return;
7488       }
7489
7490       MovDir[x][y] =
7491         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7492       Moving2Blocked(x, y, &newx, &newy);
7493
7494       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7495         return;
7496
7497       MovDir[x][y] =
7498         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7499       Moving2Blocked(x, y, &newx, &newy);
7500
7501       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7502         return;
7503
7504       MovDir[x][y] = old_move_dir;
7505     }
7506   }
7507   else if (move_pattern == MV_WHEN_PUSHED ||
7508            move_pattern == MV_WHEN_DROPPED)
7509   {
7510     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7511       MovDir[x][y] = MV_NONE;
7512
7513     MovDelay[x][y] = 0;
7514   }
7515   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7516   {
7517     static int test_xy[7][2] =
7518     {
7519       { 0, -1 },
7520       { -1, 0 },
7521       { +1, 0 },
7522       { 0, +1 },
7523       { 0, -1 },
7524       { -1, 0 },
7525       { +1, 0 },
7526     };
7527     static int test_dir[7] =
7528     {
7529       MV_UP,
7530       MV_LEFT,
7531       MV_RIGHT,
7532       MV_DOWN,
7533       MV_UP,
7534       MV_LEFT,
7535       MV_RIGHT,
7536     };
7537     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7538     int move_preference = -1000000;     // start with very low preference
7539     int new_move_dir = MV_NONE;
7540     int start_test = RND(4);
7541     int i;
7542
7543     for (i = 0; i < NUM_DIRECTIONS; i++)
7544     {
7545       int move_dir = test_dir[start_test + i];
7546       int move_dir_preference;
7547
7548       xx = x + test_xy[start_test + i][0];
7549       yy = y + test_xy[start_test + i][1];
7550
7551       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7552           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7553       {
7554         new_move_dir = move_dir;
7555
7556         break;
7557       }
7558
7559       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7560         continue;
7561
7562       move_dir_preference = -1 * RunnerVisit[xx][yy];
7563       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7564         move_dir_preference = PlayerVisit[xx][yy];
7565
7566       if (move_dir_preference > move_preference)
7567       {
7568         // prefer field that has not been visited for the longest time
7569         move_preference = move_dir_preference;
7570         new_move_dir = move_dir;
7571       }
7572       else if (move_dir_preference == move_preference &&
7573                move_dir == old_move_dir)
7574       {
7575         // prefer last direction when all directions are preferred equally
7576         move_preference = move_dir_preference;
7577         new_move_dir = move_dir;
7578       }
7579     }
7580
7581     MovDir[x][y] = new_move_dir;
7582     if (old_move_dir != new_move_dir)
7583       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7584   }
7585 }
7586
7587 static void TurnRound(int x, int y)
7588 {
7589   int direction = MovDir[x][y];
7590
7591   TurnRoundExt(x, y);
7592
7593   GfxDir[x][y] = MovDir[x][y];
7594
7595   if (direction != MovDir[x][y])
7596     GfxFrame[x][y] = 0;
7597
7598   if (MovDelay[x][y])
7599     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7600
7601   ResetGfxFrame(x, y);
7602 }
7603
7604 static boolean JustBeingPushed(int x, int y)
7605 {
7606   int i;
7607
7608   for (i = 0; i < MAX_PLAYERS; i++)
7609   {
7610     struct PlayerInfo *player = &stored_player[i];
7611
7612     if (player->active && player->is_pushing && player->MovPos)
7613     {
7614       int next_jx = player->jx + (player->jx - player->last_jx);
7615       int next_jy = player->jy + (player->jy - player->last_jy);
7616
7617       if (x == next_jx && y == next_jy)
7618         return TRUE;
7619     }
7620   }
7621
7622   return FALSE;
7623 }
7624
7625 static void StartMoving(int x, int y)
7626 {
7627   boolean started_moving = FALSE;       // some elements can fall _and_ move
7628   int element = Tile[x][y];
7629
7630   if (Stop[x][y])
7631     return;
7632
7633   if (MovDelay[x][y] == 0)
7634     GfxAction[x][y] = ACTION_DEFAULT;
7635
7636   if (CAN_FALL(element) && y < lev_fieldy - 1)
7637   {
7638     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7639         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7640       if (JustBeingPushed(x, y))
7641         return;
7642
7643     if (element == EL_QUICKSAND_FULL)
7644     {
7645       if (IS_FREE(x, y + 1))
7646       {
7647         InitMovingField(x, y, MV_DOWN);
7648         started_moving = TRUE;
7649
7650         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7651 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7652         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7653           Store[x][y] = EL_ROCK;
7654 #else
7655         Store[x][y] = EL_ROCK;
7656 #endif
7657
7658         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7659       }
7660       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7661       {
7662         if (!MovDelay[x][y])
7663         {
7664           MovDelay[x][y] = TILEY + 1;
7665
7666           ResetGfxAnimation(x, y);
7667           ResetGfxAnimation(x, y + 1);
7668         }
7669
7670         if (MovDelay[x][y])
7671         {
7672           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7673           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7674
7675           MovDelay[x][y]--;
7676           if (MovDelay[x][y])
7677             return;
7678         }
7679
7680         Tile[x][y] = EL_QUICKSAND_EMPTY;
7681         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7682         Store[x][y + 1] = Store[x][y];
7683         Store[x][y] = 0;
7684
7685         PlayLevelSoundAction(x, y, ACTION_FILLING);
7686       }
7687       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7688       {
7689         if (!MovDelay[x][y])
7690         {
7691           MovDelay[x][y] = TILEY + 1;
7692
7693           ResetGfxAnimation(x, y);
7694           ResetGfxAnimation(x, y + 1);
7695         }
7696
7697         if (MovDelay[x][y])
7698         {
7699           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7700           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7701
7702           MovDelay[x][y]--;
7703           if (MovDelay[x][y])
7704             return;
7705         }
7706
7707         Tile[x][y] = EL_QUICKSAND_EMPTY;
7708         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7709         Store[x][y + 1] = Store[x][y];
7710         Store[x][y] = 0;
7711
7712         PlayLevelSoundAction(x, y, ACTION_FILLING);
7713       }
7714     }
7715     else if (element == EL_QUICKSAND_FAST_FULL)
7716     {
7717       if (IS_FREE(x, y + 1))
7718       {
7719         InitMovingField(x, y, MV_DOWN);
7720         started_moving = TRUE;
7721
7722         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7723 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7724         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7725           Store[x][y] = EL_ROCK;
7726 #else
7727         Store[x][y] = EL_ROCK;
7728 #endif
7729
7730         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7731       }
7732       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7733       {
7734         if (!MovDelay[x][y])
7735         {
7736           MovDelay[x][y] = TILEY + 1;
7737
7738           ResetGfxAnimation(x, y);
7739           ResetGfxAnimation(x, y + 1);
7740         }
7741
7742         if (MovDelay[x][y])
7743         {
7744           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7745           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7746
7747           MovDelay[x][y]--;
7748           if (MovDelay[x][y])
7749             return;
7750         }
7751
7752         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7753         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7754         Store[x][y + 1] = Store[x][y];
7755         Store[x][y] = 0;
7756
7757         PlayLevelSoundAction(x, y, ACTION_FILLING);
7758       }
7759       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7760       {
7761         if (!MovDelay[x][y])
7762         {
7763           MovDelay[x][y] = TILEY + 1;
7764
7765           ResetGfxAnimation(x, y);
7766           ResetGfxAnimation(x, y + 1);
7767         }
7768
7769         if (MovDelay[x][y])
7770         {
7771           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7772           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7773
7774           MovDelay[x][y]--;
7775           if (MovDelay[x][y])
7776             return;
7777         }
7778
7779         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7780         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7781         Store[x][y + 1] = Store[x][y];
7782         Store[x][y] = 0;
7783
7784         PlayLevelSoundAction(x, y, ACTION_FILLING);
7785       }
7786     }
7787     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7788              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7789     {
7790       InitMovingField(x, y, MV_DOWN);
7791       started_moving = TRUE;
7792
7793       Tile[x][y] = EL_QUICKSAND_FILLING;
7794       Store[x][y] = element;
7795
7796       PlayLevelSoundAction(x, y, ACTION_FILLING);
7797     }
7798     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7799              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7800     {
7801       InitMovingField(x, y, MV_DOWN);
7802       started_moving = TRUE;
7803
7804       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7805       Store[x][y] = element;
7806
7807       PlayLevelSoundAction(x, y, ACTION_FILLING);
7808     }
7809     else if (element == EL_MAGIC_WALL_FULL)
7810     {
7811       if (IS_FREE(x, y + 1))
7812       {
7813         InitMovingField(x, y, MV_DOWN);
7814         started_moving = TRUE;
7815
7816         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7817         Store[x][y] = EL_CHANGED(Store[x][y]);
7818       }
7819       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7820       {
7821         if (!MovDelay[x][y])
7822           MovDelay[x][y] = TILEY / 4 + 1;
7823
7824         if (MovDelay[x][y])
7825         {
7826           MovDelay[x][y]--;
7827           if (MovDelay[x][y])
7828             return;
7829         }
7830
7831         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7832         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7833         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7834         Store[x][y] = 0;
7835       }
7836     }
7837     else if (element == EL_BD_MAGIC_WALL_FULL)
7838     {
7839       if (IS_FREE(x, y + 1))
7840       {
7841         InitMovingField(x, y, MV_DOWN);
7842         started_moving = TRUE;
7843
7844         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7845         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7846       }
7847       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7848       {
7849         if (!MovDelay[x][y])
7850           MovDelay[x][y] = TILEY / 4 + 1;
7851
7852         if (MovDelay[x][y])
7853         {
7854           MovDelay[x][y]--;
7855           if (MovDelay[x][y])
7856             return;
7857         }
7858
7859         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7860         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7861         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7862         Store[x][y] = 0;
7863       }
7864     }
7865     else if (element == EL_DC_MAGIC_WALL_FULL)
7866     {
7867       if (IS_FREE(x, y + 1))
7868       {
7869         InitMovingField(x, y, MV_DOWN);
7870         started_moving = TRUE;
7871
7872         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7873         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7874       }
7875       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7876       {
7877         if (!MovDelay[x][y])
7878           MovDelay[x][y] = TILEY / 4 + 1;
7879
7880         if (MovDelay[x][y])
7881         {
7882           MovDelay[x][y]--;
7883           if (MovDelay[x][y])
7884             return;
7885         }
7886
7887         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7888         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7889         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7890         Store[x][y] = 0;
7891       }
7892     }
7893     else if ((CAN_PASS_MAGIC_WALL(element) &&
7894               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7895                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7896              (CAN_PASS_DC_MAGIC_WALL(element) &&
7897               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7898
7899     {
7900       InitMovingField(x, y, MV_DOWN);
7901       started_moving = TRUE;
7902
7903       Tile[x][y] =
7904         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7905          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7906          EL_DC_MAGIC_WALL_FILLING);
7907       Store[x][y] = element;
7908     }
7909     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7910     {
7911       SplashAcid(x, y + 1);
7912
7913       InitMovingField(x, y, MV_DOWN);
7914       started_moving = TRUE;
7915
7916       Store[x][y] = EL_ACID;
7917     }
7918     else if (
7919              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7920               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7921              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7922               CAN_FALL(element) && WasJustFalling[x][y] &&
7923               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7924
7925              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7926               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7927               (Tile[x][y + 1] == EL_BLOCKED)))
7928     {
7929       /* this is needed for a special case not covered by calling "Impact()"
7930          from "ContinueMoving()": if an element moves to a tile directly below
7931          another element which was just falling on that tile (which was empty
7932          in the previous frame), the falling element above would just stop
7933          instead of smashing the element below (in previous version, the above
7934          element was just checked for "moving" instead of "falling", resulting
7935          in incorrect smashes caused by horizontal movement of the above
7936          element; also, the case of the player being the element to smash was
7937          simply not covered here... :-/ ) */
7938
7939       CheckCollision[x][y] = 0;
7940       CheckImpact[x][y] = 0;
7941
7942       Impact(x, y);
7943     }
7944     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7945     {
7946       if (MovDir[x][y] == MV_NONE)
7947       {
7948         InitMovingField(x, y, MV_DOWN);
7949         started_moving = TRUE;
7950       }
7951     }
7952     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7953     {
7954       if (WasJustFalling[x][y]) // prevent animation from being restarted
7955         MovDir[x][y] = MV_DOWN;
7956
7957       InitMovingField(x, y, MV_DOWN);
7958       started_moving = TRUE;
7959     }
7960     else if (element == EL_AMOEBA_DROP)
7961     {
7962       Tile[x][y] = EL_AMOEBA_GROWING;
7963       Store[x][y] = EL_AMOEBA_WET;
7964     }
7965     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7966               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7967              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7968              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7969     {
7970       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7971                                 (IS_FREE(x - 1, y + 1) ||
7972                                  Tile[x - 1][y + 1] == EL_ACID));
7973       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7974                                 (IS_FREE(x + 1, y + 1) ||
7975                                  Tile[x + 1][y + 1] == EL_ACID));
7976       boolean can_fall_any  = (can_fall_left || can_fall_right);
7977       boolean can_fall_both = (can_fall_left && can_fall_right);
7978       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7979
7980       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7981       {
7982         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7983           can_fall_right = FALSE;
7984         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7985           can_fall_left = FALSE;
7986         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7987           can_fall_right = FALSE;
7988         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7989           can_fall_left = FALSE;
7990
7991         can_fall_any  = (can_fall_left || can_fall_right);
7992         can_fall_both = FALSE;
7993       }
7994
7995       if (can_fall_both)
7996       {
7997         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7998           can_fall_right = FALSE;       // slip down on left side
7999         else
8000           can_fall_left = !(can_fall_right = RND(2));
8001
8002         can_fall_both = FALSE;
8003       }
8004
8005       if (can_fall_any)
8006       {
8007         // if not determined otherwise, prefer left side for slipping down
8008         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8009         started_moving = TRUE;
8010       }
8011     }
8012     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8013     {
8014       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8015       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8016       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8017       int belt_dir = game.belt_dir[belt_nr];
8018
8019       if ((belt_dir == MV_LEFT  && left_is_free) ||
8020           (belt_dir == MV_RIGHT && right_is_free))
8021       {
8022         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8023
8024         InitMovingField(x, y, belt_dir);
8025         started_moving = TRUE;
8026
8027         Pushed[x][y] = TRUE;
8028         Pushed[nextx][y] = TRUE;
8029
8030         GfxAction[x][y] = ACTION_DEFAULT;
8031       }
8032       else
8033       {
8034         MovDir[x][y] = 0;       // if element was moving, stop it
8035       }
8036     }
8037   }
8038
8039   // not "else if" because of elements that can fall and move (EL_SPRING)
8040   if (CAN_MOVE(element) && !started_moving)
8041   {
8042     int move_pattern = element_info[element].move_pattern;
8043     int newx, newy;
8044
8045     Moving2Blocked(x, y, &newx, &newy);
8046
8047     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8048       return;
8049
8050     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8051         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8052     {
8053       WasJustMoving[x][y] = 0;
8054       CheckCollision[x][y] = 0;
8055
8056       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8057
8058       if (Tile[x][y] != element)        // element has changed
8059         return;
8060     }
8061
8062     if (!MovDelay[x][y])        // start new movement phase
8063     {
8064       // all objects that can change their move direction after each step
8065       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8066
8067       if (element != EL_YAMYAM &&
8068           element != EL_DARK_YAMYAM &&
8069           element != EL_PACMAN &&
8070           !(move_pattern & MV_ANY_DIRECTION) &&
8071           move_pattern != MV_TURNING_LEFT &&
8072           move_pattern != MV_TURNING_RIGHT &&
8073           move_pattern != MV_TURNING_LEFT_RIGHT &&
8074           move_pattern != MV_TURNING_RIGHT_LEFT &&
8075           move_pattern != MV_TURNING_RANDOM)
8076       {
8077         TurnRound(x, y);
8078
8079         if (MovDelay[x][y] && (element == EL_BUG ||
8080                                element == EL_SPACESHIP ||
8081                                element == EL_SP_SNIKSNAK ||
8082                                element == EL_SP_ELECTRON ||
8083                                element == EL_MOLE))
8084           TEST_DrawLevelField(x, y);
8085       }
8086     }
8087
8088     if (MovDelay[x][y])         // wait some time before next movement
8089     {
8090       MovDelay[x][y]--;
8091
8092       if (element == EL_ROBOT ||
8093           element == EL_YAMYAM ||
8094           element == EL_DARK_YAMYAM)
8095       {
8096         DrawLevelElementAnimationIfNeeded(x, y, element);
8097         PlayLevelSoundAction(x, y, ACTION_WAITING);
8098       }
8099       else if (element == EL_SP_ELECTRON)
8100         DrawLevelElementAnimationIfNeeded(x, y, element);
8101       else if (element == EL_DRAGON)
8102       {
8103         int i;
8104         int dir = MovDir[x][y];
8105         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8106         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8107         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8108                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8109                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8110                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8111         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8112
8113         GfxAction[x][y] = ACTION_ATTACKING;
8114
8115         if (IS_PLAYER(x, y))
8116           DrawPlayerField(x, y);
8117         else
8118           TEST_DrawLevelField(x, y);
8119
8120         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8121
8122         for (i = 1; i <= 3; i++)
8123         {
8124           int xx = x + i * dx;
8125           int yy = y + i * dy;
8126           int sx = SCREENX(xx);
8127           int sy = SCREENY(yy);
8128           int flame_graphic = graphic + (i - 1);
8129
8130           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8131             break;
8132
8133           if (MovDelay[x][y])
8134           {
8135             int flamed = MovingOrBlocked2Element(xx, yy);
8136
8137             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8138               Bang(xx, yy);
8139             else
8140               RemoveMovingField(xx, yy);
8141
8142             ChangeDelay[xx][yy] = 0;
8143
8144             Tile[xx][yy] = EL_FLAMES;
8145
8146             if (IN_SCR_FIELD(sx, sy))
8147             {
8148               TEST_DrawLevelFieldCrumbled(xx, yy);
8149               DrawGraphic(sx, sy, flame_graphic, frame);
8150             }
8151           }
8152           else
8153           {
8154             if (Tile[xx][yy] == EL_FLAMES)
8155               Tile[xx][yy] = EL_EMPTY;
8156             TEST_DrawLevelField(xx, yy);
8157           }
8158         }
8159       }
8160
8161       if (MovDelay[x][y])       // element still has to wait some time
8162       {
8163         PlayLevelSoundAction(x, y, ACTION_WAITING);
8164
8165         return;
8166       }
8167     }
8168
8169     // now make next step
8170
8171     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8172
8173     if (DONT_COLLIDE_WITH(element) &&
8174         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8175         !PLAYER_ENEMY_PROTECTED(newx, newy))
8176     {
8177       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8178
8179       return;
8180     }
8181
8182     else if (CAN_MOVE_INTO_ACID(element) &&
8183              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8184              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8185              (MovDir[x][y] == MV_DOWN ||
8186               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8187     {
8188       SplashAcid(newx, newy);
8189       Store[x][y] = EL_ACID;
8190     }
8191     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8192     {
8193       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8194           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8195           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8196           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8197       {
8198         RemoveField(x, y);
8199         TEST_DrawLevelField(x, y);
8200
8201         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8202         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8203           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8204
8205         game.friends_still_needed--;
8206         if (!game.friends_still_needed &&
8207             !game.GameOver &&
8208             game.all_players_gone)
8209           LevelSolved();
8210
8211         return;
8212       }
8213       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8214       {
8215         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8216           TEST_DrawLevelField(newx, newy);
8217         else
8218           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8219       }
8220       else if (!IS_FREE(newx, newy))
8221       {
8222         GfxAction[x][y] = ACTION_WAITING;
8223
8224         if (IS_PLAYER(x, y))
8225           DrawPlayerField(x, y);
8226         else
8227           TEST_DrawLevelField(x, y);
8228
8229         return;
8230       }
8231     }
8232     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8233     {
8234       if (IS_FOOD_PIG(Tile[newx][newy]))
8235       {
8236         if (IS_MOVING(newx, newy))
8237           RemoveMovingField(newx, newy);
8238         else
8239         {
8240           Tile[newx][newy] = EL_EMPTY;
8241           TEST_DrawLevelField(newx, newy);
8242         }
8243
8244         PlayLevelSound(x, y, SND_PIG_DIGGING);
8245       }
8246       else if (!IS_FREE(newx, newy))
8247       {
8248         if (IS_PLAYER(x, y))
8249           DrawPlayerField(x, y);
8250         else
8251           TEST_DrawLevelField(x, y);
8252
8253         return;
8254       }
8255     }
8256     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8257     {
8258       if (Store[x][y] != EL_EMPTY)
8259       {
8260         boolean can_clone = FALSE;
8261         int xx, yy;
8262
8263         // check if element to clone is still there
8264         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8265         {
8266           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8267           {
8268             can_clone = TRUE;
8269
8270             break;
8271           }
8272         }
8273
8274         // cannot clone or target field not free anymore -- do not clone
8275         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8276           Store[x][y] = EL_EMPTY;
8277       }
8278
8279       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8280       {
8281         if (IS_MV_DIAGONAL(MovDir[x][y]))
8282         {
8283           int diagonal_move_dir = MovDir[x][y];
8284           int stored = Store[x][y];
8285           int change_delay = 8;
8286           int graphic;
8287
8288           // android is moving diagonally
8289
8290           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8291
8292           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8293           GfxElement[x][y] = EL_EMC_ANDROID;
8294           GfxAction[x][y] = ACTION_SHRINKING;
8295           GfxDir[x][y] = diagonal_move_dir;
8296           ChangeDelay[x][y] = change_delay;
8297
8298           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8299                                    GfxDir[x][y]);
8300
8301           DrawLevelGraphicAnimation(x, y, graphic);
8302           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8303
8304           if (Tile[newx][newy] == EL_ACID)
8305           {
8306             SplashAcid(newx, newy);
8307
8308             return;
8309           }
8310
8311           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8312
8313           Store[newx][newy] = EL_EMC_ANDROID;
8314           GfxElement[newx][newy] = EL_EMC_ANDROID;
8315           GfxAction[newx][newy] = ACTION_GROWING;
8316           GfxDir[newx][newy] = diagonal_move_dir;
8317           ChangeDelay[newx][newy] = change_delay;
8318
8319           graphic = el_act_dir2img(GfxElement[newx][newy],
8320                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8321
8322           DrawLevelGraphicAnimation(newx, newy, graphic);
8323           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8324
8325           return;
8326         }
8327         else
8328         {
8329           Tile[newx][newy] = EL_EMPTY;
8330           TEST_DrawLevelField(newx, newy);
8331
8332           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8333         }
8334       }
8335       else if (!IS_FREE(newx, newy))
8336       {
8337         return;
8338       }
8339     }
8340     else if (IS_CUSTOM_ELEMENT(element) &&
8341              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8342     {
8343       if (!DigFieldByCE(newx, newy, element))
8344         return;
8345
8346       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8347       {
8348         RunnerVisit[x][y] = FrameCounter;
8349         PlayerVisit[x][y] /= 8;         // expire player visit path
8350       }
8351     }
8352     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8353     {
8354       if (!IS_FREE(newx, newy))
8355       {
8356         if (IS_PLAYER(x, y))
8357           DrawPlayerField(x, y);
8358         else
8359           TEST_DrawLevelField(x, y);
8360
8361         return;
8362       }
8363       else
8364       {
8365         boolean wanna_flame = !RND(10);
8366         int dx = newx - x, dy = newy - y;
8367         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8368         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8369         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8370                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8371         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8372                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8373
8374         if ((wanna_flame ||
8375              IS_CLASSIC_ENEMY(element1) ||
8376              IS_CLASSIC_ENEMY(element2)) &&
8377             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8378             element1 != EL_FLAMES && element2 != EL_FLAMES)
8379         {
8380           ResetGfxAnimation(x, y);
8381           GfxAction[x][y] = ACTION_ATTACKING;
8382
8383           if (IS_PLAYER(x, y))
8384             DrawPlayerField(x, y);
8385           else
8386             TEST_DrawLevelField(x, y);
8387
8388           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8389
8390           MovDelay[x][y] = 50;
8391
8392           Tile[newx][newy] = EL_FLAMES;
8393           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8394             Tile[newx1][newy1] = EL_FLAMES;
8395           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8396             Tile[newx2][newy2] = EL_FLAMES;
8397
8398           return;
8399         }
8400       }
8401     }
8402     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8403              Tile[newx][newy] == EL_DIAMOND)
8404     {
8405       if (IS_MOVING(newx, newy))
8406         RemoveMovingField(newx, newy);
8407       else
8408       {
8409         Tile[newx][newy] = EL_EMPTY;
8410         TEST_DrawLevelField(newx, newy);
8411       }
8412
8413       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8414     }
8415     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8416              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8417     {
8418       if (AmoebaNr[newx][newy])
8419       {
8420         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8421         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8422             Tile[newx][newy] == EL_BD_AMOEBA)
8423           AmoebaCnt[AmoebaNr[newx][newy]]--;
8424       }
8425
8426       if (IS_MOVING(newx, newy))
8427       {
8428         RemoveMovingField(newx, newy);
8429       }
8430       else
8431       {
8432         Tile[newx][newy] = EL_EMPTY;
8433         TEST_DrawLevelField(newx, newy);
8434       }
8435
8436       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8437     }
8438     else if ((element == EL_PACMAN || element == EL_MOLE)
8439              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8440     {
8441       if (AmoebaNr[newx][newy])
8442       {
8443         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8444         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8445             Tile[newx][newy] == EL_BD_AMOEBA)
8446           AmoebaCnt[AmoebaNr[newx][newy]]--;
8447       }
8448
8449       if (element == EL_MOLE)
8450       {
8451         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8452         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8453
8454         ResetGfxAnimation(x, y);
8455         GfxAction[x][y] = ACTION_DIGGING;
8456         TEST_DrawLevelField(x, y);
8457
8458         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8459
8460         return;                         // wait for shrinking amoeba
8461       }
8462       else      // element == EL_PACMAN
8463       {
8464         Tile[newx][newy] = EL_EMPTY;
8465         TEST_DrawLevelField(newx, newy);
8466         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8467       }
8468     }
8469     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8470              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8471               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8472     {
8473       // wait for shrinking amoeba to completely disappear
8474       return;
8475     }
8476     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8477     {
8478       // object was running against a wall
8479
8480       TurnRound(x, y);
8481
8482       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8483         DrawLevelElementAnimation(x, y, element);
8484
8485       if (DONT_TOUCH(element))
8486         TestIfBadThingTouchesPlayer(x, y);
8487
8488       return;
8489     }
8490
8491     InitMovingField(x, y, MovDir[x][y]);
8492
8493     PlayLevelSoundAction(x, y, ACTION_MOVING);
8494   }
8495
8496   if (MovDir[x][y])
8497     ContinueMoving(x, y);
8498 }
8499
8500 void ContinueMoving(int x, int y)
8501 {
8502   int element = Tile[x][y];
8503   struct ElementInfo *ei = &element_info[element];
8504   int direction = MovDir[x][y];
8505   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8506   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8507   int newx = x + dx, newy = y + dy;
8508   int stored = Store[x][y];
8509   int stored_new = Store[newx][newy];
8510   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8511   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8512   boolean last_line = (newy == lev_fieldy - 1);
8513
8514   MovPos[x][y] += getElementMoveStepsize(x, y);
8515
8516   if (pushed_by_player) // special case: moving object pushed by player
8517     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8518
8519   if (ABS(MovPos[x][y]) < TILEX)
8520   {
8521     TEST_DrawLevelField(x, y);
8522
8523     return;     // element is still moving
8524   }
8525
8526   // element reached destination field
8527
8528   Tile[x][y] = EL_EMPTY;
8529   Tile[newx][newy] = element;
8530   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8531
8532   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8533   {
8534     element = Tile[newx][newy] = EL_ACID;
8535   }
8536   else if (element == EL_MOLE)
8537   {
8538     Tile[x][y] = EL_SAND;
8539
8540     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8541   }
8542   else if (element == EL_QUICKSAND_FILLING)
8543   {
8544     element = Tile[newx][newy] = get_next_element(element);
8545     Store[newx][newy] = Store[x][y];
8546   }
8547   else if (element == EL_QUICKSAND_EMPTYING)
8548   {
8549     Tile[x][y] = get_next_element(element);
8550     element = Tile[newx][newy] = Store[x][y];
8551   }
8552   else if (element == EL_QUICKSAND_FAST_FILLING)
8553   {
8554     element = Tile[newx][newy] = get_next_element(element);
8555     Store[newx][newy] = Store[x][y];
8556   }
8557   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8558   {
8559     Tile[x][y] = get_next_element(element);
8560     element = Tile[newx][newy] = Store[x][y];
8561   }
8562   else if (element == EL_MAGIC_WALL_FILLING)
8563   {
8564     element = Tile[newx][newy] = get_next_element(element);
8565     if (!game.magic_wall_active)
8566       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8567     Store[newx][newy] = Store[x][y];
8568   }
8569   else if (element == EL_MAGIC_WALL_EMPTYING)
8570   {
8571     Tile[x][y] = get_next_element(element);
8572     if (!game.magic_wall_active)
8573       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8574     element = Tile[newx][newy] = Store[x][y];
8575
8576     InitField(newx, newy, FALSE);
8577   }
8578   else if (element == EL_BD_MAGIC_WALL_FILLING)
8579   {
8580     element = Tile[newx][newy] = get_next_element(element);
8581     if (!game.magic_wall_active)
8582       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8583     Store[newx][newy] = Store[x][y];
8584   }
8585   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8586   {
8587     Tile[x][y] = get_next_element(element);
8588     if (!game.magic_wall_active)
8589       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8590     element = Tile[newx][newy] = Store[x][y];
8591
8592     InitField(newx, newy, FALSE);
8593   }
8594   else if (element == EL_DC_MAGIC_WALL_FILLING)
8595   {
8596     element = Tile[newx][newy] = get_next_element(element);
8597     if (!game.magic_wall_active)
8598       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8599     Store[newx][newy] = Store[x][y];
8600   }
8601   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8602   {
8603     Tile[x][y] = get_next_element(element);
8604     if (!game.magic_wall_active)
8605       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8606     element = Tile[newx][newy] = Store[x][y];
8607
8608     InitField(newx, newy, FALSE);
8609   }
8610   else if (element == EL_AMOEBA_DROPPING)
8611   {
8612     Tile[x][y] = get_next_element(element);
8613     element = Tile[newx][newy] = Store[x][y];
8614   }
8615   else if (element == EL_SOKOBAN_OBJECT)
8616   {
8617     if (Back[x][y])
8618       Tile[x][y] = Back[x][y];
8619
8620     if (Back[newx][newy])
8621       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8622
8623     Back[x][y] = Back[newx][newy] = 0;
8624   }
8625
8626   Store[x][y] = EL_EMPTY;
8627   MovPos[x][y] = 0;
8628   MovDir[x][y] = 0;
8629   MovDelay[x][y] = 0;
8630
8631   MovDelay[newx][newy] = 0;
8632
8633   if (CAN_CHANGE_OR_HAS_ACTION(element))
8634   {
8635     // copy element change control values to new field
8636     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8637     ChangePage[newx][newy]  = ChangePage[x][y];
8638     ChangeCount[newx][newy] = ChangeCount[x][y];
8639     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8640   }
8641
8642   CustomValue[newx][newy] = CustomValue[x][y];
8643
8644   ChangeDelay[x][y] = 0;
8645   ChangePage[x][y] = -1;
8646   ChangeCount[x][y] = 0;
8647   ChangeEvent[x][y] = -1;
8648
8649   CustomValue[x][y] = 0;
8650
8651   // copy animation control values to new field
8652   GfxFrame[newx][newy]  = GfxFrame[x][y];
8653   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8654   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8655   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8656
8657   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8658
8659   // some elements can leave other elements behind after moving
8660   if (ei->move_leave_element != EL_EMPTY &&
8661       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8662       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8663   {
8664     int move_leave_element = ei->move_leave_element;
8665
8666     // this makes it possible to leave the removed element again
8667     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8668       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8669
8670     Tile[x][y] = move_leave_element;
8671
8672     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8673       MovDir[x][y] = direction;
8674
8675     InitField(x, y, FALSE);
8676
8677     if (GFX_CRUMBLED(Tile[x][y]))
8678       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8679
8680     if (ELEM_IS_PLAYER(move_leave_element))
8681       RelocatePlayer(x, y, move_leave_element);
8682   }
8683
8684   // do this after checking for left-behind element
8685   ResetGfxAnimation(x, y);      // reset animation values for old field
8686
8687   if (!CAN_MOVE(element) ||
8688       (CAN_FALL(element) && direction == MV_DOWN &&
8689        (element == EL_SPRING ||
8690         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8691         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8692     GfxDir[x][y] = MovDir[newx][newy] = 0;
8693
8694   TEST_DrawLevelField(x, y);
8695   TEST_DrawLevelField(newx, newy);
8696
8697   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8698
8699   // prevent pushed element from moving on in pushed direction
8700   if (pushed_by_player && CAN_MOVE(element) &&
8701       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8702       !(element_info[element].move_pattern & direction))
8703     TurnRound(newx, newy);
8704
8705   // prevent elements on conveyor belt from moving on in last direction
8706   if (pushed_by_conveyor && CAN_FALL(element) &&
8707       direction & MV_HORIZONTAL)
8708     MovDir[newx][newy] = 0;
8709
8710   if (!pushed_by_player)
8711   {
8712     int nextx = newx + dx, nexty = newy + dy;
8713     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8714
8715     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8716
8717     if (CAN_FALL(element) && direction == MV_DOWN)
8718       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8719
8720     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8721       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8722
8723     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8724       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8725   }
8726
8727   if (DONT_TOUCH(element))      // object may be nasty to player or others
8728   {
8729     TestIfBadThingTouchesPlayer(newx, newy);
8730     TestIfBadThingTouchesFriend(newx, newy);
8731
8732     if (!IS_CUSTOM_ELEMENT(element))
8733       TestIfBadThingTouchesOtherBadThing(newx, newy);
8734   }
8735   else if (element == EL_PENGUIN)
8736     TestIfFriendTouchesBadThing(newx, newy);
8737
8738   if (DONT_GET_HIT_BY(element))
8739   {
8740     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8741   }
8742
8743   // give the player one last chance (one more frame) to move away
8744   if (CAN_FALL(element) && direction == MV_DOWN &&
8745       (last_line || (!IS_FREE(x, newy + 1) &&
8746                      (!IS_PLAYER(x, newy + 1) ||
8747                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8748     Impact(x, newy);
8749
8750   if (pushed_by_player && !game.use_change_when_pushing_bug)
8751   {
8752     int push_side = MV_DIR_OPPOSITE(direction);
8753     struct PlayerInfo *player = PLAYERINFO(x, y);
8754
8755     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8756                                player->index_bit, push_side);
8757     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8758                                         player->index_bit, push_side);
8759   }
8760
8761   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8762     MovDelay[newx][newy] = 1;
8763
8764   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8765
8766   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8767   TestIfElementHitsCustomElement(newx, newy, direction);
8768   TestIfPlayerTouchesCustomElement(newx, newy);
8769   TestIfElementTouchesCustomElement(newx, newy);
8770
8771   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8772       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8773     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8774                              MV_DIR_OPPOSITE(direction));
8775 }
8776
8777 int AmoebaNeighbourNr(int ax, int ay)
8778 {
8779   int i;
8780   int element = Tile[ax][ay];
8781   int group_nr = 0;
8782   static int xy[4][2] =
8783   {
8784     { 0, -1 },
8785     { -1, 0 },
8786     { +1, 0 },
8787     { 0, +1 }
8788   };
8789
8790   for (i = 0; i < NUM_DIRECTIONS; i++)
8791   {
8792     int x = ax + xy[i][0];
8793     int y = ay + xy[i][1];
8794
8795     if (!IN_LEV_FIELD(x, y))
8796       continue;
8797
8798     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8799       group_nr = AmoebaNr[x][y];
8800   }
8801
8802   return group_nr;
8803 }
8804
8805 static void AmoebaMerge(int ax, int ay)
8806 {
8807   int i, x, y, xx, yy;
8808   int new_group_nr = AmoebaNr[ax][ay];
8809   static int xy[4][2] =
8810   {
8811     { 0, -1 },
8812     { -1, 0 },
8813     { +1, 0 },
8814     { 0, +1 }
8815   };
8816
8817   if (new_group_nr == 0)
8818     return;
8819
8820   for (i = 0; i < NUM_DIRECTIONS; i++)
8821   {
8822     x = ax + xy[i][0];
8823     y = ay + xy[i][1];
8824
8825     if (!IN_LEV_FIELD(x, y))
8826       continue;
8827
8828     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8829          Tile[x][y] == EL_BD_AMOEBA ||
8830          Tile[x][y] == EL_AMOEBA_DEAD) &&
8831         AmoebaNr[x][y] != new_group_nr)
8832     {
8833       int old_group_nr = AmoebaNr[x][y];
8834
8835       if (old_group_nr == 0)
8836         return;
8837
8838       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8839       AmoebaCnt[old_group_nr] = 0;
8840       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8841       AmoebaCnt2[old_group_nr] = 0;
8842
8843       SCAN_PLAYFIELD(xx, yy)
8844       {
8845         if (AmoebaNr[xx][yy] == old_group_nr)
8846           AmoebaNr[xx][yy] = new_group_nr;
8847       }
8848     }
8849   }
8850 }
8851
8852 void AmoebaToDiamond(int ax, int ay)
8853 {
8854   int i, x, y;
8855
8856   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8857   {
8858     int group_nr = AmoebaNr[ax][ay];
8859
8860 #ifdef DEBUG
8861     if (group_nr == 0)
8862     {
8863       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8864       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8865
8866       return;
8867     }
8868 #endif
8869
8870     SCAN_PLAYFIELD(x, y)
8871     {
8872       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8873       {
8874         AmoebaNr[x][y] = 0;
8875         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8876       }
8877     }
8878
8879     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8880                             SND_AMOEBA_TURNING_TO_GEM :
8881                             SND_AMOEBA_TURNING_TO_ROCK));
8882     Bang(ax, ay);
8883   }
8884   else
8885   {
8886     static int xy[4][2] =
8887     {
8888       { 0, -1 },
8889       { -1, 0 },
8890       { +1, 0 },
8891       { 0, +1 }
8892     };
8893
8894     for (i = 0; i < NUM_DIRECTIONS; i++)
8895     {
8896       x = ax + xy[i][0];
8897       y = ay + xy[i][1];
8898
8899       if (!IN_LEV_FIELD(x, y))
8900         continue;
8901
8902       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8903       {
8904         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8905                               SND_AMOEBA_TURNING_TO_GEM :
8906                               SND_AMOEBA_TURNING_TO_ROCK));
8907         Bang(x, y);
8908       }
8909     }
8910   }
8911 }
8912
8913 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8914 {
8915   int x, y;
8916   int group_nr = AmoebaNr[ax][ay];
8917   boolean done = FALSE;
8918
8919 #ifdef DEBUG
8920   if (group_nr == 0)
8921   {
8922     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8923     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8924
8925     return;
8926   }
8927 #endif
8928
8929   SCAN_PLAYFIELD(x, y)
8930   {
8931     if (AmoebaNr[x][y] == group_nr &&
8932         (Tile[x][y] == EL_AMOEBA_DEAD ||
8933          Tile[x][y] == EL_BD_AMOEBA ||
8934          Tile[x][y] == EL_AMOEBA_GROWING))
8935     {
8936       AmoebaNr[x][y] = 0;
8937       Tile[x][y] = new_element;
8938       InitField(x, y, FALSE);
8939       TEST_DrawLevelField(x, y);
8940       done = TRUE;
8941     }
8942   }
8943
8944   if (done)
8945     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8946                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8947                             SND_BD_AMOEBA_TURNING_TO_GEM));
8948 }
8949
8950 static void AmoebaGrowing(int x, int y)
8951 {
8952   static unsigned int sound_delay = 0;
8953   static unsigned int sound_delay_value = 0;
8954
8955   if (!MovDelay[x][y])          // start new growing cycle
8956   {
8957     MovDelay[x][y] = 7;
8958
8959     if (DelayReached(&sound_delay, sound_delay_value))
8960     {
8961       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8962       sound_delay_value = 30;
8963     }
8964   }
8965
8966   if (MovDelay[x][y])           // wait some time before growing bigger
8967   {
8968     MovDelay[x][y]--;
8969     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8970     {
8971       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8972                                            6 - MovDelay[x][y]);
8973
8974       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8975     }
8976
8977     if (!MovDelay[x][y])
8978     {
8979       Tile[x][y] = Store[x][y];
8980       Store[x][y] = 0;
8981       TEST_DrawLevelField(x, y);
8982     }
8983   }
8984 }
8985
8986 static void AmoebaShrinking(int x, int y)
8987 {
8988   static unsigned int sound_delay = 0;
8989   static unsigned int sound_delay_value = 0;
8990
8991   if (!MovDelay[x][y])          // start new shrinking cycle
8992   {
8993     MovDelay[x][y] = 7;
8994
8995     if (DelayReached(&sound_delay, sound_delay_value))
8996       sound_delay_value = 30;
8997   }
8998
8999   if (MovDelay[x][y])           // wait some time before shrinking
9000   {
9001     MovDelay[x][y]--;
9002     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9003     {
9004       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9005                                            6 - MovDelay[x][y]);
9006
9007       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9008     }
9009
9010     if (!MovDelay[x][y])
9011     {
9012       Tile[x][y] = EL_EMPTY;
9013       TEST_DrawLevelField(x, y);
9014
9015       // don't let mole enter this field in this cycle;
9016       // (give priority to objects falling to this field from above)
9017       Stop[x][y] = TRUE;
9018     }
9019   }
9020 }
9021
9022 static void AmoebaReproduce(int ax, int ay)
9023 {
9024   int i;
9025   int element = Tile[ax][ay];
9026   int graphic = el2img(element);
9027   int newax = ax, neway = ay;
9028   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9029   static int xy[4][2] =
9030   {
9031     { 0, -1 },
9032     { -1, 0 },
9033     { +1, 0 },
9034     { 0, +1 }
9035   };
9036
9037   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9038   {
9039     Tile[ax][ay] = EL_AMOEBA_DEAD;
9040     TEST_DrawLevelField(ax, ay);
9041     return;
9042   }
9043
9044   if (IS_ANIMATED(graphic))
9045     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9046
9047   if (!MovDelay[ax][ay])        // start making new amoeba field
9048     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9049
9050   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9051   {
9052     MovDelay[ax][ay]--;
9053     if (MovDelay[ax][ay])
9054       return;
9055   }
9056
9057   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9058   {
9059     int start = RND(4);
9060     int x = ax + xy[start][0];
9061     int y = ay + xy[start][1];
9062
9063     if (!IN_LEV_FIELD(x, y))
9064       return;
9065
9066     if (IS_FREE(x, y) ||
9067         CAN_GROW_INTO(Tile[x][y]) ||
9068         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9069         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9070     {
9071       newax = x;
9072       neway = y;
9073     }
9074
9075     if (newax == ax && neway == ay)
9076       return;
9077   }
9078   else                          // normal or "filled" (BD style) amoeba
9079   {
9080     int start = RND(4);
9081     boolean waiting_for_player = FALSE;
9082
9083     for (i = 0; i < NUM_DIRECTIONS; i++)
9084     {
9085       int j = (start + i) % 4;
9086       int x = ax + xy[j][0];
9087       int y = ay + xy[j][1];
9088
9089       if (!IN_LEV_FIELD(x, y))
9090         continue;
9091
9092       if (IS_FREE(x, y) ||
9093           CAN_GROW_INTO(Tile[x][y]) ||
9094           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9095           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9096       {
9097         newax = x;
9098         neway = y;
9099         break;
9100       }
9101       else if (IS_PLAYER(x, y))
9102         waiting_for_player = TRUE;
9103     }
9104
9105     if (newax == ax && neway == ay)             // amoeba cannot grow
9106     {
9107       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9108       {
9109         Tile[ax][ay] = EL_AMOEBA_DEAD;
9110         TEST_DrawLevelField(ax, ay);
9111         AmoebaCnt[AmoebaNr[ax][ay]]--;
9112
9113         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9114         {
9115           if (element == EL_AMOEBA_FULL)
9116             AmoebaToDiamond(ax, ay);
9117           else if (element == EL_BD_AMOEBA)
9118             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9119         }
9120       }
9121       return;
9122     }
9123     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9124     {
9125       // amoeba gets larger by growing in some direction
9126
9127       int new_group_nr = AmoebaNr[ax][ay];
9128
9129 #ifdef DEBUG
9130   if (new_group_nr == 0)
9131   {
9132     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9133           newax, neway);
9134     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9135
9136     return;
9137   }
9138 #endif
9139
9140       AmoebaNr[newax][neway] = new_group_nr;
9141       AmoebaCnt[new_group_nr]++;
9142       AmoebaCnt2[new_group_nr]++;
9143
9144       // if amoeba touches other amoeba(s) after growing, unify them
9145       AmoebaMerge(newax, neway);
9146
9147       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9148       {
9149         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9150         return;
9151       }
9152     }
9153   }
9154
9155   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9156       (neway == lev_fieldy - 1 && newax != ax))
9157   {
9158     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9159     Store[newax][neway] = element;
9160   }
9161   else if (neway == ay || element == EL_EMC_DRIPPER)
9162   {
9163     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9164
9165     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9166   }
9167   else
9168   {
9169     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9170     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9171     Store[ax][ay] = EL_AMOEBA_DROP;
9172     ContinueMoving(ax, ay);
9173     return;
9174   }
9175
9176   TEST_DrawLevelField(newax, neway);
9177 }
9178
9179 static void Life(int ax, int ay)
9180 {
9181   int x1, y1, x2, y2;
9182   int life_time = 40;
9183   int element = Tile[ax][ay];
9184   int graphic = el2img(element);
9185   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9186                          level.biomaze);
9187   boolean changed = FALSE;
9188
9189   if (IS_ANIMATED(graphic))
9190     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9191
9192   if (Stop[ax][ay])
9193     return;
9194
9195   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9196     MovDelay[ax][ay] = life_time;
9197
9198   if (MovDelay[ax][ay])         // wait some time before next cycle
9199   {
9200     MovDelay[ax][ay]--;
9201     if (MovDelay[ax][ay])
9202       return;
9203   }
9204
9205   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9206   {
9207     int xx = ax+x1, yy = ay+y1;
9208     int old_element = Tile[xx][yy];
9209     int num_neighbours = 0;
9210
9211     if (!IN_LEV_FIELD(xx, yy))
9212       continue;
9213
9214     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9215     {
9216       int x = xx+x2, y = yy+y2;
9217
9218       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9219         continue;
9220
9221       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9222       boolean is_neighbour = FALSE;
9223
9224       if (level.use_life_bugs)
9225         is_neighbour =
9226           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9227            (IS_FREE(x, y)                             &&  Stop[x][y]));
9228       else
9229         is_neighbour =
9230           (Last[x][y] == element || is_player_cell);
9231
9232       if (is_neighbour)
9233         num_neighbours++;
9234     }
9235
9236     boolean is_free = FALSE;
9237
9238     if (level.use_life_bugs)
9239       is_free = (IS_FREE(xx, yy));
9240     else
9241       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9242
9243     if (xx == ax && yy == ay)           // field in the middle
9244     {
9245       if (num_neighbours < life_parameter[0] ||
9246           num_neighbours > life_parameter[1])
9247       {
9248         Tile[xx][yy] = EL_EMPTY;
9249         if (Tile[xx][yy] != old_element)
9250           TEST_DrawLevelField(xx, yy);
9251         Stop[xx][yy] = TRUE;
9252         changed = TRUE;
9253       }
9254     }
9255     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9256     {                                   // free border field
9257       if (num_neighbours >= life_parameter[2] &&
9258           num_neighbours <= life_parameter[3])
9259       {
9260         Tile[xx][yy] = element;
9261         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9262         if (Tile[xx][yy] != old_element)
9263           TEST_DrawLevelField(xx, yy);
9264         Stop[xx][yy] = TRUE;
9265         changed = TRUE;
9266       }
9267     }
9268   }
9269
9270   if (changed)
9271     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9272                    SND_GAME_OF_LIFE_GROWING);
9273 }
9274
9275 static void InitRobotWheel(int x, int y)
9276 {
9277   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9278 }
9279
9280 static void RunRobotWheel(int x, int y)
9281 {
9282   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9283 }
9284
9285 static void StopRobotWheel(int x, int y)
9286 {
9287   if (game.robot_wheel_x == x &&
9288       game.robot_wheel_y == y)
9289   {
9290     game.robot_wheel_x = -1;
9291     game.robot_wheel_y = -1;
9292     game.robot_wheel_active = FALSE;
9293   }
9294 }
9295
9296 static void InitTimegateWheel(int x, int y)
9297 {
9298   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9299 }
9300
9301 static void RunTimegateWheel(int x, int y)
9302 {
9303   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9304 }
9305
9306 static void InitMagicBallDelay(int x, int y)
9307 {
9308   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9309 }
9310
9311 static void ActivateMagicBall(int bx, int by)
9312 {
9313   int x, y;
9314
9315   if (level.ball_random)
9316   {
9317     int pos_border = RND(8);    // select one of the eight border elements
9318     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9319     int xx = pos_content % 3;
9320     int yy = pos_content / 3;
9321
9322     x = bx - 1 + xx;
9323     y = by - 1 + yy;
9324
9325     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9326       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9327   }
9328   else
9329   {
9330     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9331     {
9332       int xx = x - bx + 1;
9333       int yy = y - by + 1;
9334
9335       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9336         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9337     }
9338   }
9339
9340   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9341 }
9342
9343 static void CheckExit(int x, int y)
9344 {
9345   if (game.gems_still_needed > 0 ||
9346       game.sokoban_fields_still_needed > 0 ||
9347       game.sokoban_objects_still_needed > 0 ||
9348       game.lights_still_needed > 0)
9349   {
9350     int element = Tile[x][y];
9351     int graphic = el2img(element);
9352
9353     if (IS_ANIMATED(graphic))
9354       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9355
9356     return;
9357   }
9358
9359   // do not re-open exit door closed after last player
9360   if (game.all_players_gone)
9361     return;
9362
9363   Tile[x][y] = EL_EXIT_OPENING;
9364
9365   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9366 }
9367
9368 static void CheckExitEM(int x, int y)
9369 {
9370   if (game.gems_still_needed > 0 ||
9371       game.sokoban_fields_still_needed > 0 ||
9372       game.sokoban_objects_still_needed > 0 ||
9373       game.lights_still_needed > 0)
9374   {
9375     int element = Tile[x][y];
9376     int graphic = el2img(element);
9377
9378     if (IS_ANIMATED(graphic))
9379       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9380
9381     return;
9382   }
9383
9384   // do not re-open exit door closed after last player
9385   if (game.all_players_gone)
9386     return;
9387
9388   Tile[x][y] = EL_EM_EXIT_OPENING;
9389
9390   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9391 }
9392
9393 static void CheckExitSteel(int x, int y)
9394 {
9395   if (game.gems_still_needed > 0 ||
9396       game.sokoban_fields_still_needed > 0 ||
9397       game.sokoban_objects_still_needed > 0 ||
9398       game.lights_still_needed > 0)
9399   {
9400     int element = Tile[x][y];
9401     int graphic = el2img(element);
9402
9403     if (IS_ANIMATED(graphic))
9404       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9405
9406     return;
9407   }
9408
9409   // do not re-open exit door closed after last player
9410   if (game.all_players_gone)
9411     return;
9412
9413   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9414
9415   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9416 }
9417
9418 static void CheckExitSteelEM(int x, int y)
9419 {
9420   if (game.gems_still_needed > 0 ||
9421       game.sokoban_fields_still_needed > 0 ||
9422       game.sokoban_objects_still_needed > 0 ||
9423       game.lights_still_needed > 0)
9424   {
9425     int element = Tile[x][y];
9426     int graphic = el2img(element);
9427
9428     if (IS_ANIMATED(graphic))
9429       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9430
9431     return;
9432   }
9433
9434   // do not re-open exit door closed after last player
9435   if (game.all_players_gone)
9436     return;
9437
9438   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9439
9440   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9441 }
9442
9443 static void CheckExitSP(int x, int y)
9444 {
9445   if (game.gems_still_needed > 0)
9446   {
9447     int element = Tile[x][y];
9448     int graphic = el2img(element);
9449
9450     if (IS_ANIMATED(graphic))
9451       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9452
9453     return;
9454   }
9455
9456   // do not re-open exit door closed after last player
9457   if (game.all_players_gone)
9458     return;
9459
9460   Tile[x][y] = EL_SP_EXIT_OPENING;
9461
9462   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9463 }
9464
9465 static void CloseAllOpenTimegates(void)
9466 {
9467   int x, y;
9468
9469   SCAN_PLAYFIELD(x, y)
9470   {
9471     int element = Tile[x][y];
9472
9473     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9474     {
9475       Tile[x][y] = EL_TIMEGATE_CLOSING;
9476
9477       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9478     }
9479   }
9480 }
9481
9482 static void DrawTwinkleOnField(int x, int y)
9483 {
9484   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9485     return;
9486
9487   if (Tile[x][y] == EL_BD_DIAMOND)
9488     return;
9489
9490   if (MovDelay[x][y] == 0)      // next animation frame
9491     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9492
9493   if (MovDelay[x][y] != 0)      // wait some time before next frame
9494   {
9495     MovDelay[x][y]--;
9496
9497     DrawLevelElementAnimation(x, y, Tile[x][y]);
9498
9499     if (MovDelay[x][y] != 0)
9500     {
9501       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9502                                            10 - MovDelay[x][y]);
9503
9504       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9505     }
9506   }
9507 }
9508
9509 static void MauerWaechst(int x, int y)
9510 {
9511   int delay = 6;
9512
9513   if (!MovDelay[x][y])          // next animation frame
9514     MovDelay[x][y] = 3 * delay;
9515
9516   if (MovDelay[x][y])           // wait some time before next frame
9517   {
9518     MovDelay[x][y]--;
9519
9520     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9521     {
9522       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9523       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9524
9525       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9526     }
9527
9528     if (!MovDelay[x][y])
9529     {
9530       if (MovDir[x][y] == MV_LEFT)
9531       {
9532         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9533           TEST_DrawLevelField(x - 1, y);
9534       }
9535       else if (MovDir[x][y] == MV_RIGHT)
9536       {
9537         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9538           TEST_DrawLevelField(x + 1, y);
9539       }
9540       else if (MovDir[x][y] == MV_UP)
9541       {
9542         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9543           TEST_DrawLevelField(x, y - 1);
9544       }
9545       else
9546       {
9547         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9548           TEST_DrawLevelField(x, y + 1);
9549       }
9550
9551       Tile[x][y] = Store[x][y];
9552       Store[x][y] = 0;
9553       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9554       TEST_DrawLevelField(x, y);
9555     }
9556   }
9557 }
9558
9559 static void MauerAbleger(int ax, int ay)
9560 {
9561   int element = Tile[ax][ay];
9562   int graphic = el2img(element);
9563   boolean oben_frei = FALSE, unten_frei = FALSE;
9564   boolean links_frei = FALSE, rechts_frei = FALSE;
9565   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9566   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9567   boolean new_wall = FALSE;
9568
9569   if (IS_ANIMATED(graphic))
9570     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9571
9572   if (!MovDelay[ax][ay])        // start building new wall
9573     MovDelay[ax][ay] = 6;
9574
9575   if (MovDelay[ax][ay])         // wait some time before building new wall
9576   {
9577     MovDelay[ax][ay]--;
9578     if (MovDelay[ax][ay])
9579       return;
9580   }
9581
9582   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9583     oben_frei = TRUE;
9584   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9585     unten_frei = TRUE;
9586   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9587     links_frei = TRUE;
9588   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9589     rechts_frei = TRUE;
9590
9591   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9592       element == EL_EXPANDABLE_WALL_ANY)
9593   {
9594     if (oben_frei)
9595     {
9596       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9597       Store[ax][ay-1] = element;
9598       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9599       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9600         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9601                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9602       new_wall = TRUE;
9603     }
9604     if (unten_frei)
9605     {
9606       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9607       Store[ax][ay+1] = element;
9608       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9609       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9610         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9611                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9612       new_wall = TRUE;
9613     }
9614   }
9615
9616   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9617       element == EL_EXPANDABLE_WALL_ANY ||
9618       element == EL_EXPANDABLE_WALL ||
9619       element == EL_BD_EXPANDABLE_WALL)
9620   {
9621     if (links_frei)
9622     {
9623       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9624       Store[ax-1][ay] = element;
9625       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9626       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9627         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9628                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9629       new_wall = TRUE;
9630     }
9631
9632     if (rechts_frei)
9633     {
9634       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9635       Store[ax+1][ay] = element;
9636       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9637       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9638         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9639                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9640       new_wall = TRUE;
9641     }
9642   }
9643
9644   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9645     TEST_DrawLevelField(ax, ay);
9646
9647   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9648     oben_massiv = TRUE;
9649   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9650     unten_massiv = TRUE;
9651   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9652     links_massiv = TRUE;
9653   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9654     rechts_massiv = TRUE;
9655
9656   if (((oben_massiv && unten_massiv) ||
9657        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9658        element == EL_EXPANDABLE_WALL) &&
9659       ((links_massiv && rechts_massiv) ||
9660        element == EL_EXPANDABLE_WALL_VERTICAL))
9661     Tile[ax][ay] = EL_WALL;
9662
9663   if (new_wall)
9664     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9665 }
9666
9667 static void MauerAblegerStahl(int ax, int ay)
9668 {
9669   int element = Tile[ax][ay];
9670   int graphic = el2img(element);
9671   boolean oben_frei = FALSE, unten_frei = FALSE;
9672   boolean links_frei = FALSE, rechts_frei = FALSE;
9673   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9674   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9675   boolean new_wall = FALSE;
9676
9677   if (IS_ANIMATED(graphic))
9678     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9679
9680   if (!MovDelay[ax][ay])        // start building new wall
9681     MovDelay[ax][ay] = 6;
9682
9683   if (MovDelay[ax][ay])         // wait some time before building new wall
9684   {
9685     MovDelay[ax][ay]--;
9686     if (MovDelay[ax][ay])
9687       return;
9688   }
9689
9690   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9691     oben_frei = TRUE;
9692   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9693     unten_frei = TRUE;
9694   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9695     links_frei = TRUE;
9696   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9697     rechts_frei = TRUE;
9698
9699   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9700       element == EL_EXPANDABLE_STEELWALL_ANY)
9701   {
9702     if (oben_frei)
9703     {
9704       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9705       Store[ax][ay-1] = element;
9706       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9707       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9708         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9709                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9710       new_wall = TRUE;
9711     }
9712     if (unten_frei)
9713     {
9714       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9715       Store[ax][ay+1] = element;
9716       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9717       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9718         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9719                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9720       new_wall = TRUE;
9721     }
9722   }
9723
9724   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9725       element == EL_EXPANDABLE_STEELWALL_ANY)
9726   {
9727     if (links_frei)
9728     {
9729       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9730       Store[ax-1][ay] = element;
9731       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9732       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9733         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9734                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9735       new_wall = TRUE;
9736     }
9737
9738     if (rechts_frei)
9739     {
9740       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9741       Store[ax+1][ay] = element;
9742       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9743       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9744         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9745                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9746       new_wall = TRUE;
9747     }
9748   }
9749
9750   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9751     oben_massiv = TRUE;
9752   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9753     unten_massiv = TRUE;
9754   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9755     links_massiv = TRUE;
9756   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9757     rechts_massiv = TRUE;
9758
9759   if (((oben_massiv && unten_massiv) ||
9760        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9761       ((links_massiv && rechts_massiv) ||
9762        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9763     Tile[ax][ay] = EL_STEELWALL;
9764
9765   if (new_wall)
9766     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9767 }
9768
9769 static void CheckForDragon(int x, int y)
9770 {
9771   int i, j;
9772   boolean dragon_found = FALSE;
9773   static int xy[4][2] =
9774   {
9775     { 0, -1 },
9776     { -1, 0 },
9777     { +1, 0 },
9778     { 0, +1 }
9779   };
9780
9781   for (i = 0; i < NUM_DIRECTIONS; i++)
9782   {
9783     for (j = 0; j < 4; j++)
9784     {
9785       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9786
9787       if (IN_LEV_FIELD(xx, yy) &&
9788           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9789       {
9790         if (Tile[xx][yy] == EL_DRAGON)
9791           dragon_found = TRUE;
9792       }
9793       else
9794         break;
9795     }
9796   }
9797
9798   if (!dragon_found)
9799   {
9800     for (i = 0; i < NUM_DIRECTIONS; i++)
9801     {
9802       for (j = 0; j < 3; j++)
9803       {
9804         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9805   
9806         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9807         {
9808           Tile[xx][yy] = EL_EMPTY;
9809           TEST_DrawLevelField(xx, yy);
9810         }
9811         else
9812           break;
9813       }
9814     }
9815   }
9816 }
9817
9818 static void InitBuggyBase(int x, int y)
9819 {
9820   int element = Tile[x][y];
9821   int activating_delay = FRAMES_PER_SECOND / 4;
9822
9823   ChangeDelay[x][y] =
9824     (element == EL_SP_BUGGY_BASE ?
9825      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9826      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9827      activating_delay :
9828      element == EL_SP_BUGGY_BASE_ACTIVE ?
9829      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9830 }
9831
9832 static void WarnBuggyBase(int x, int y)
9833 {
9834   int i;
9835   static int xy[4][2] =
9836   {
9837     { 0, -1 },
9838     { -1, 0 },
9839     { +1, 0 },
9840     { 0, +1 }
9841   };
9842
9843   for (i = 0; i < NUM_DIRECTIONS; i++)
9844   {
9845     int xx = x + xy[i][0];
9846     int yy = y + xy[i][1];
9847
9848     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9849     {
9850       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9851
9852       break;
9853     }
9854   }
9855 }
9856
9857 static void InitTrap(int x, int y)
9858 {
9859   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9860 }
9861
9862 static void ActivateTrap(int x, int y)
9863 {
9864   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9865 }
9866
9867 static void ChangeActiveTrap(int x, int y)
9868 {
9869   int graphic = IMG_TRAP_ACTIVE;
9870
9871   // if new animation frame was drawn, correct crumbled sand border
9872   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9873     TEST_DrawLevelFieldCrumbled(x, y);
9874 }
9875
9876 static int getSpecialActionElement(int element, int number, int base_element)
9877 {
9878   return (element != EL_EMPTY ? element :
9879           number != -1 ? base_element + number - 1 :
9880           EL_EMPTY);
9881 }
9882
9883 static int getModifiedActionNumber(int value_old, int operator, int operand,
9884                                    int value_min, int value_max)
9885 {
9886   int value_new = (operator == CA_MODE_SET      ? operand :
9887                    operator == CA_MODE_ADD      ? value_old + operand :
9888                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9889                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9890                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9891                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9892                    value_old);
9893
9894   return (value_new < value_min ? value_min :
9895           value_new > value_max ? value_max :
9896           value_new);
9897 }
9898
9899 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9900 {
9901   struct ElementInfo *ei = &element_info[element];
9902   struct ElementChangeInfo *change = &ei->change_page[page];
9903   int target_element = change->target_element;
9904   int action_type = change->action_type;
9905   int action_mode = change->action_mode;
9906   int action_arg = change->action_arg;
9907   int action_element = change->action_element;
9908   int i;
9909
9910   if (!change->has_action)
9911     return;
9912
9913   // ---------- determine action paramater values -----------------------------
9914
9915   int level_time_value =
9916     (level.time > 0 ? TimeLeft :
9917      TimePlayed);
9918
9919   int action_arg_element_raw =
9920     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9921      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9922      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9923      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9924      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9925      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9926      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9927      EL_EMPTY);
9928   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9929
9930   int action_arg_direction =
9931     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9932      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9933      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9934      change->actual_trigger_side :
9935      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9936      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9937      MV_NONE);
9938
9939   int action_arg_number_min =
9940     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9941      CA_ARG_MIN);
9942
9943   int action_arg_number_max =
9944     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9945      action_type == CA_SET_LEVEL_GEMS ? 999 :
9946      action_type == CA_SET_LEVEL_TIME ? 9999 :
9947      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9948      action_type == CA_SET_CE_VALUE ? 9999 :
9949      action_type == CA_SET_CE_SCORE ? 9999 :
9950      CA_ARG_MAX);
9951
9952   int action_arg_number_reset =
9953     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9954      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9955      action_type == CA_SET_LEVEL_TIME ? level.time :
9956      action_type == CA_SET_LEVEL_SCORE ? 0 :
9957      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9958      action_type == CA_SET_CE_SCORE ? 0 :
9959      0);
9960
9961   int action_arg_number =
9962     (action_arg <= CA_ARG_MAX ? action_arg :
9963      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9964      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9965      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9966      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9967      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9968      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9969      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9970      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9971      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9972      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9973      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9974      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9975      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9976      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9977      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9978      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9979      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9980      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9981      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9982      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9983      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9984      -1);
9985
9986   int action_arg_number_old =
9987     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9988      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9989      action_type == CA_SET_LEVEL_SCORE ? game.score :
9990      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9991      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9992      0);
9993
9994   int action_arg_number_new =
9995     getModifiedActionNumber(action_arg_number_old,
9996                             action_mode, action_arg_number,
9997                             action_arg_number_min, action_arg_number_max);
9998
9999   int trigger_player_bits =
10000     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10001      change->actual_trigger_player_bits : change->trigger_player);
10002
10003   int action_arg_player_bits =
10004     (action_arg >= CA_ARG_PLAYER_1 &&
10005      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10006      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10007      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10008      PLAYER_BITS_ANY);
10009
10010   // ---------- execute action  -----------------------------------------------
10011
10012   switch (action_type)
10013   {
10014     case CA_NO_ACTION:
10015     {
10016       return;
10017     }
10018
10019     // ---------- level actions  ----------------------------------------------
10020
10021     case CA_RESTART_LEVEL:
10022     {
10023       game.restart_level = TRUE;
10024
10025       break;
10026     }
10027
10028     case CA_SHOW_ENVELOPE:
10029     {
10030       int element = getSpecialActionElement(action_arg_element,
10031                                             action_arg_number, EL_ENVELOPE_1);
10032
10033       if (IS_ENVELOPE(element))
10034         local_player->show_envelope = element;
10035
10036       break;
10037     }
10038
10039     case CA_SET_LEVEL_TIME:
10040     {
10041       if (level.time > 0)       // only modify limited time value
10042       {
10043         TimeLeft = action_arg_number_new;
10044
10045         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10046
10047         DisplayGameControlValues();
10048
10049         if (!TimeLeft && setup.time_limit)
10050           for (i = 0; i < MAX_PLAYERS; i++)
10051             KillPlayer(&stored_player[i]);
10052       }
10053
10054       break;
10055     }
10056
10057     case CA_SET_LEVEL_SCORE:
10058     {
10059       game.score = action_arg_number_new;
10060
10061       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10062
10063       DisplayGameControlValues();
10064
10065       break;
10066     }
10067
10068     case CA_SET_LEVEL_GEMS:
10069     {
10070       game.gems_still_needed = action_arg_number_new;
10071
10072       game.snapshot.collected_item = TRUE;
10073
10074       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10075
10076       DisplayGameControlValues();
10077
10078       break;
10079     }
10080
10081     case CA_SET_LEVEL_WIND:
10082     {
10083       game.wind_direction = action_arg_direction;
10084
10085       break;
10086     }
10087
10088     case CA_SET_LEVEL_RANDOM_SEED:
10089     {
10090       // ensure that setting a new random seed while playing is predictable
10091       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10092
10093       break;
10094     }
10095
10096     // ---------- player actions  ---------------------------------------------
10097
10098     case CA_MOVE_PLAYER:
10099     case CA_MOVE_PLAYER_NEW:
10100     {
10101       // automatically move to the next field in specified direction
10102       for (i = 0; i < MAX_PLAYERS; i++)
10103         if (trigger_player_bits & (1 << i))
10104           if (action_type == CA_MOVE_PLAYER ||
10105               stored_player[i].MovPos == 0)
10106             stored_player[i].programmed_action = action_arg_direction;
10107
10108       break;
10109     }
10110
10111     case CA_EXIT_PLAYER:
10112     {
10113       for (i = 0; i < MAX_PLAYERS; i++)
10114         if (action_arg_player_bits & (1 << i))
10115           ExitPlayer(&stored_player[i]);
10116
10117       if (game.players_still_needed == 0)
10118         LevelSolved();
10119
10120       break;
10121     }
10122
10123     case CA_KILL_PLAYER:
10124     {
10125       for (i = 0; i < MAX_PLAYERS; i++)
10126         if (action_arg_player_bits & (1 << i))
10127           KillPlayer(&stored_player[i]);
10128
10129       break;
10130     }
10131
10132     case CA_SET_PLAYER_KEYS:
10133     {
10134       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10135       int element = getSpecialActionElement(action_arg_element,
10136                                             action_arg_number, EL_KEY_1);
10137
10138       if (IS_KEY(element))
10139       {
10140         for (i = 0; i < MAX_PLAYERS; i++)
10141         {
10142           if (trigger_player_bits & (1 << i))
10143           {
10144             stored_player[i].key[KEY_NR(element)] = key_state;
10145
10146             DrawGameDoorValues();
10147           }
10148         }
10149       }
10150
10151       break;
10152     }
10153
10154     case CA_SET_PLAYER_SPEED:
10155     {
10156       for (i = 0; i < MAX_PLAYERS; i++)
10157       {
10158         if (trigger_player_bits & (1 << i))
10159         {
10160           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10161
10162           if (action_arg == CA_ARG_SPEED_FASTER &&
10163               stored_player[i].cannot_move)
10164           {
10165             action_arg_number = STEPSIZE_VERY_SLOW;
10166           }
10167           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10168                    action_arg == CA_ARG_SPEED_FASTER)
10169           {
10170             action_arg_number = 2;
10171             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10172                            CA_MODE_MULTIPLY);
10173           }
10174           else if (action_arg == CA_ARG_NUMBER_RESET)
10175           {
10176             action_arg_number = level.initial_player_stepsize[i];
10177           }
10178
10179           move_stepsize =
10180             getModifiedActionNumber(move_stepsize,
10181                                     action_mode,
10182                                     action_arg_number,
10183                                     action_arg_number_min,
10184                                     action_arg_number_max);
10185
10186           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10187         }
10188       }
10189
10190       break;
10191     }
10192
10193     case CA_SET_PLAYER_SHIELD:
10194     {
10195       for (i = 0; i < MAX_PLAYERS; i++)
10196       {
10197         if (trigger_player_bits & (1 << i))
10198         {
10199           if (action_arg == CA_ARG_SHIELD_OFF)
10200           {
10201             stored_player[i].shield_normal_time_left = 0;
10202             stored_player[i].shield_deadly_time_left = 0;
10203           }
10204           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10205           {
10206             stored_player[i].shield_normal_time_left = 999999;
10207           }
10208           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10209           {
10210             stored_player[i].shield_normal_time_left = 999999;
10211             stored_player[i].shield_deadly_time_left = 999999;
10212           }
10213         }
10214       }
10215
10216       break;
10217     }
10218
10219     case CA_SET_PLAYER_GRAVITY:
10220     {
10221       for (i = 0; i < MAX_PLAYERS; i++)
10222       {
10223         if (trigger_player_bits & (1 << i))
10224         {
10225           stored_player[i].gravity =
10226             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10227              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10228              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10229              stored_player[i].gravity);
10230         }
10231       }
10232
10233       break;
10234     }
10235
10236     case CA_SET_PLAYER_ARTWORK:
10237     {
10238       for (i = 0; i < MAX_PLAYERS; i++)
10239       {
10240         if (trigger_player_bits & (1 << i))
10241         {
10242           int artwork_element = action_arg_element;
10243
10244           if (action_arg == CA_ARG_ELEMENT_RESET)
10245             artwork_element =
10246               (level.use_artwork_element[i] ? level.artwork_element[i] :
10247                stored_player[i].element_nr);
10248
10249           if (stored_player[i].artwork_element != artwork_element)
10250             stored_player[i].Frame = 0;
10251
10252           stored_player[i].artwork_element = artwork_element;
10253
10254           SetPlayerWaiting(&stored_player[i], FALSE);
10255
10256           // set number of special actions for bored and sleeping animation
10257           stored_player[i].num_special_action_bored =
10258             get_num_special_action(artwork_element,
10259                                    ACTION_BORING_1, ACTION_BORING_LAST);
10260           stored_player[i].num_special_action_sleeping =
10261             get_num_special_action(artwork_element,
10262                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10263         }
10264       }
10265
10266       break;
10267     }
10268
10269     case CA_SET_PLAYER_INVENTORY:
10270     {
10271       for (i = 0; i < MAX_PLAYERS; i++)
10272       {
10273         struct PlayerInfo *player = &stored_player[i];
10274         int j, k;
10275
10276         if (trigger_player_bits & (1 << i))
10277         {
10278           int inventory_element = action_arg_element;
10279
10280           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10281               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10282               action_arg == CA_ARG_ELEMENT_ACTION)
10283           {
10284             int element = inventory_element;
10285             int collect_count = element_info[element].collect_count_initial;
10286
10287             if (!IS_CUSTOM_ELEMENT(element))
10288               collect_count = 1;
10289
10290             if (collect_count == 0)
10291               player->inventory_infinite_element = element;
10292             else
10293               for (k = 0; k < collect_count; k++)
10294                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10295                   player->inventory_element[player->inventory_size++] =
10296                     element;
10297           }
10298           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10299                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10300                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10301           {
10302             if (player->inventory_infinite_element != EL_UNDEFINED &&
10303                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10304                                      action_arg_element_raw))
10305               player->inventory_infinite_element = EL_UNDEFINED;
10306
10307             for (k = 0, j = 0; j < player->inventory_size; j++)
10308             {
10309               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10310                                         action_arg_element_raw))
10311                 player->inventory_element[k++] = player->inventory_element[j];
10312             }
10313
10314             player->inventory_size = k;
10315           }
10316           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10317           {
10318             if (player->inventory_size > 0)
10319             {
10320               for (j = 0; j < player->inventory_size - 1; j++)
10321                 player->inventory_element[j] = player->inventory_element[j + 1];
10322
10323               player->inventory_size--;
10324             }
10325           }
10326           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10327           {
10328             if (player->inventory_size > 0)
10329               player->inventory_size--;
10330           }
10331           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10332           {
10333             player->inventory_infinite_element = EL_UNDEFINED;
10334             player->inventory_size = 0;
10335           }
10336           else if (action_arg == CA_ARG_INVENTORY_RESET)
10337           {
10338             player->inventory_infinite_element = EL_UNDEFINED;
10339             player->inventory_size = 0;
10340
10341             if (level.use_initial_inventory[i])
10342             {
10343               for (j = 0; j < level.initial_inventory_size[i]; j++)
10344               {
10345                 int element = level.initial_inventory_content[i][j];
10346                 int collect_count = element_info[element].collect_count_initial;
10347
10348                 if (!IS_CUSTOM_ELEMENT(element))
10349                   collect_count = 1;
10350
10351                 if (collect_count == 0)
10352                   player->inventory_infinite_element = element;
10353                 else
10354                   for (k = 0; k < collect_count; k++)
10355                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10356                       player->inventory_element[player->inventory_size++] =
10357                         element;
10358               }
10359             }
10360           }
10361         }
10362       }
10363
10364       break;
10365     }
10366
10367     // ---------- CE actions  -------------------------------------------------
10368
10369     case CA_SET_CE_VALUE:
10370     {
10371       int last_ce_value = CustomValue[x][y];
10372
10373       CustomValue[x][y] = action_arg_number_new;
10374
10375       if (CustomValue[x][y] != last_ce_value)
10376       {
10377         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10378         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10379
10380         if (CustomValue[x][y] == 0)
10381         {
10382           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10383           ChangeCount[x][y] = 0;        // allow at least one more change
10384
10385           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10386           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10387         }
10388       }
10389
10390       break;
10391     }
10392
10393     case CA_SET_CE_SCORE:
10394     {
10395       int last_ce_score = ei->collect_score;
10396
10397       ei->collect_score = action_arg_number_new;
10398
10399       if (ei->collect_score != last_ce_score)
10400       {
10401         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10402         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10403
10404         if (ei->collect_score == 0)
10405         {
10406           int xx, yy;
10407
10408           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10409           ChangeCount[x][y] = 0;        // allow at least one more change
10410
10411           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10412           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10413
10414           /*
10415             This is a very special case that seems to be a mixture between
10416             CheckElementChange() and CheckTriggeredElementChange(): while
10417             the first one only affects single elements that are triggered
10418             directly, the second one affects multiple elements in the playfield
10419             that are triggered indirectly by another element. This is a third
10420             case: Changing the CE score always affects multiple identical CEs,
10421             so every affected CE must be checked, not only the single CE for
10422             which the CE score was changed in the first place (as every instance
10423             of that CE shares the same CE score, and therefore also can change)!
10424           */
10425           SCAN_PLAYFIELD(xx, yy)
10426           {
10427             if (Tile[xx][yy] == element)
10428               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10429                                  CE_SCORE_GETS_ZERO);
10430           }
10431         }
10432       }
10433
10434       break;
10435     }
10436
10437     case CA_SET_CE_ARTWORK:
10438     {
10439       int artwork_element = action_arg_element;
10440       boolean reset_frame = FALSE;
10441       int xx, yy;
10442
10443       if (action_arg == CA_ARG_ELEMENT_RESET)
10444         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10445                            element);
10446
10447       if (ei->gfx_element != artwork_element)
10448         reset_frame = TRUE;
10449
10450       ei->gfx_element = artwork_element;
10451
10452       SCAN_PLAYFIELD(xx, yy)
10453       {
10454         if (Tile[xx][yy] == element)
10455         {
10456           if (reset_frame)
10457           {
10458             ResetGfxAnimation(xx, yy);
10459             ResetRandomAnimationValue(xx, yy);
10460           }
10461
10462           TEST_DrawLevelField(xx, yy);
10463         }
10464       }
10465
10466       break;
10467     }
10468
10469     // ---------- engine actions  ---------------------------------------------
10470
10471     case CA_SET_ENGINE_SCAN_MODE:
10472     {
10473       InitPlayfieldScanMode(action_arg);
10474
10475       break;
10476     }
10477
10478     default:
10479       break;
10480   }
10481 }
10482
10483 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10484 {
10485   int old_element = Tile[x][y];
10486   int new_element = GetElementFromGroupElement(element);
10487   int previous_move_direction = MovDir[x][y];
10488   int last_ce_value = CustomValue[x][y];
10489   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10490   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10491   boolean add_player_onto_element = (new_element_is_player &&
10492                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10493                                      IS_WALKABLE(old_element));
10494
10495   if (!add_player_onto_element)
10496   {
10497     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10498       RemoveMovingField(x, y);
10499     else
10500       RemoveField(x, y);
10501
10502     Tile[x][y] = new_element;
10503
10504     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10505       MovDir[x][y] = previous_move_direction;
10506
10507     if (element_info[new_element].use_last_ce_value)
10508       CustomValue[x][y] = last_ce_value;
10509
10510     InitField_WithBug1(x, y, FALSE);
10511
10512     new_element = Tile[x][y];   // element may have changed
10513
10514     ResetGfxAnimation(x, y);
10515     ResetRandomAnimationValue(x, y);
10516
10517     TEST_DrawLevelField(x, y);
10518
10519     if (GFX_CRUMBLED(new_element))
10520       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10521   }
10522
10523   // check if element under the player changes from accessible to unaccessible
10524   // (needed for special case of dropping element which then changes)
10525   // (must be checked after creating new element for walkable group elements)
10526   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10527       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10528   {
10529     Bang(x, y);
10530
10531     return;
10532   }
10533
10534   // "ChangeCount" not set yet to allow "entered by player" change one time
10535   if (new_element_is_player)
10536     RelocatePlayer(x, y, new_element);
10537
10538   if (is_change)
10539     ChangeCount[x][y]++;        // count number of changes in the same frame
10540
10541   TestIfBadThingTouchesPlayer(x, y);
10542   TestIfPlayerTouchesCustomElement(x, y);
10543   TestIfElementTouchesCustomElement(x, y);
10544 }
10545
10546 static void CreateField(int x, int y, int element)
10547 {
10548   CreateFieldExt(x, y, element, FALSE);
10549 }
10550
10551 static void CreateElementFromChange(int x, int y, int element)
10552 {
10553   element = GET_VALID_RUNTIME_ELEMENT(element);
10554
10555   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10556   {
10557     int old_element = Tile[x][y];
10558
10559     // prevent changed element from moving in same engine frame
10560     // unless both old and new element can either fall or move
10561     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10562         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10563       Stop[x][y] = TRUE;
10564   }
10565
10566   CreateFieldExt(x, y, element, TRUE);
10567 }
10568
10569 static boolean ChangeElement(int x, int y, int element, int page)
10570 {
10571   struct ElementInfo *ei = &element_info[element];
10572   struct ElementChangeInfo *change = &ei->change_page[page];
10573   int ce_value = CustomValue[x][y];
10574   int ce_score = ei->collect_score;
10575   int target_element;
10576   int old_element = Tile[x][y];
10577
10578   // always use default change event to prevent running into a loop
10579   if (ChangeEvent[x][y] == -1)
10580     ChangeEvent[x][y] = CE_DELAY;
10581
10582   if (ChangeEvent[x][y] == CE_DELAY)
10583   {
10584     // reset actual trigger element, trigger player and action element
10585     change->actual_trigger_element = EL_EMPTY;
10586     change->actual_trigger_player = EL_EMPTY;
10587     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10588     change->actual_trigger_side = CH_SIDE_NONE;
10589     change->actual_trigger_ce_value = 0;
10590     change->actual_trigger_ce_score = 0;
10591   }
10592
10593   // do not change elements more than a specified maximum number of changes
10594   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10595     return FALSE;
10596
10597   ChangeCount[x][y]++;          // count number of changes in the same frame
10598
10599   if (change->explode)
10600   {
10601     Bang(x, y);
10602
10603     return TRUE;
10604   }
10605
10606   if (change->use_target_content)
10607   {
10608     boolean complete_replace = TRUE;
10609     boolean can_replace[3][3];
10610     int xx, yy;
10611
10612     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10613     {
10614       boolean is_empty;
10615       boolean is_walkable;
10616       boolean is_diggable;
10617       boolean is_collectible;
10618       boolean is_removable;
10619       boolean is_destructible;
10620       int ex = x + xx - 1;
10621       int ey = y + yy - 1;
10622       int content_element = change->target_content.e[xx][yy];
10623       int e;
10624
10625       can_replace[xx][yy] = TRUE;
10626
10627       if (ex == x && ey == y)   // do not check changing element itself
10628         continue;
10629
10630       if (content_element == EL_EMPTY_SPACE)
10631       {
10632         can_replace[xx][yy] = FALSE;    // do not replace border with space
10633
10634         continue;
10635       }
10636
10637       if (!IN_LEV_FIELD(ex, ey))
10638       {
10639         can_replace[xx][yy] = FALSE;
10640         complete_replace = FALSE;
10641
10642         continue;
10643       }
10644
10645       e = Tile[ex][ey];
10646
10647       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10648         e = MovingOrBlocked2Element(ex, ey);
10649
10650       is_empty = (IS_FREE(ex, ey) ||
10651                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10652
10653       is_walkable     = (is_empty || IS_WALKABLE(e));
10654       is_diggable     = (is_empty || IS_DIGGABLE(e));
10655       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10656       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10657       is_removable    = (is_diggable || is_collectible);
10658
10659       can_replace[xx][yy] =
10660         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10661           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10662           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10663           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10664           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10665           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10666          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10667
10668       if (!can_replace[xx][yy])
10669         complete_replace = FALSE;
10670     }
10671
10672     if (!change->only_if_complete || complete_replace)
10673     {
10674       boolean something_has_changed = FALSE;
10675
10676       if (change->only_if_complete && change->use_random_replace &&
10677           RND(100) < change->random_percentage)
10678         return FALSE;
10679
10680       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10681       {
10682         int ex = x + xx - 1;
10683         int ey = y + yy - 1;
10684         int content_element;
10685
10686         if (can_replace[xx][yy] && (!change->use_random_replace ||
10687                                     RND(100) < change->random_percentage))
10688         {
10689           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10690             RemoveMovingField(ex, ey);
10691
10692           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10693
10694           content_element = change->target_content.e[xx][yy];
10695           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10696                                               ce_value, ce_score);
10697
10698           CreateElementFromChange(ex, ey, target_element);
10699
10700           something_has_changed = TRUE;
10701
10702           // for symmetry reasons, freeze newly created border elements
10703           if (ex != x || ey != y)
10704             Stop[ex][ey] = TRUE;        // no more moving in this frame
10705         }
10706       }
10707
10708       if (something_has_changed)
10709       {
10710         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10711         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10712       }
10713     }
10714   }
10715   else
10716   {
10717     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10718                                         ce_value, ce_score);
10719
10720     if (element == EL_DIAGONAL_GROWING ||
10721         element == EL_DIAGONAL_SHRINKING)
10722     {
10723       target_element = Store[x][y];
10724
10725       Store[x][y] = EL_EMPTY;
10726     }
10727
10728     CreateElementFromChange(x, y, target_element);
10729
10730     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10731     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10732   }
10733
10734   // this uses direct change before indirect change
10735   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10736
10737   return TRUE;
10738 }
10739
10740 static void HandleElementChange(int x, int y, int page)
10741 {
10742   int element = MovingOrBlocked2Element(x, y);
10743   struct ElementInfo *ei = &element_info[element];
10744   struct ElementChangeInfo *change = &ei->change_page[page];
10745   boolean handle_action_before_change = FALSE;
10746
10747 #ifdef DEBUG
10748   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10749       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10750   {
10751     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10752           x, y, element, element_info[element].token_name);
10753     Debug("game:playing:HandleElementChange", "This should never happen!");
10754   }
10755 #endif
10756
10757   // this can happen with classic bombs on walkable, changing elements
10758   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10759   {
10760     return;
10761   }
10762
10763   if (ChangeDelay[x][y] == 0)           // initialize element change
10764   {
10765     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10766
10767     if (change->can_change)
10768     {
10769       // !!! not clear why graphic animation should be reset at all here !!!
10770       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10771       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10772
10773       /*
10774         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10775
10776         When using an animation frame delay of 1 (this only happens with
10777         "sp_zonk.moving.left/right" in the classic graphics), the default
10778         (non-moving) animation shows wrong animation frames (while the
10779         moving animation, like "sp_zonk.moving.left/right", is correct,
10780         so this graphical bug never shows up with the classic graphics).
10781         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10782         be drawn instead of the correct frames 0,1,2,3. This is caused by
10783         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10784         an element change: First when the change delay ("ChangeDelay[][]")
10785         counter has reached zero after decrementing, then a second time in
10786         the next frame (after "GfxFrame[][]" was already incremented) when
10787         "ChangeDelay[][]" is reset to the initial delay value again.
10788
10789         This causes frame 0 to be drawn twice, while the last frame won't
10790         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10791
10792         As some animations may already be cleverly designed around this bug
10793         (at least the "Snake Bite" snake tail animation does this), it cannot
10794         simply be fixed here without breaking such existing animations.
10795         Unfortunately, it cannot easily be detected if a graphics set was
10796         designed "before" or "after" the bug was fixed. As a workaround,
10797         a new graphics set option "game.graphics_engine_version" was added
10798         to be able to specify the game's major release version for which the
10799         graphics set was designed, which can then be used to decide if the
10800         bugfix should be used (version 4 and above) or not (version 3 or
10801         below, or if no version was specified at all, as with old sets).
10802
10803         (The wrong/fixed animation frames can be tested with the test level set
10804         "test_gfxframe" and level "000", which contains a specially prepared
10805         custom element at level position (x/y) == (11/9) which uses the zonk
10806         animation mentioned above. Using "game.graphics_engine_version: 4"
10807         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10808         This can also be seen from the debug output for this test element.)
10809       */
10810
10811       // when a custom element is about to change (for example by change delay),
10812       // do not reset graphic animation when the custom element is moving
10813       if (game.graphics_engine_version < 4 &&
10814           !IS_MOVING(x, y))
10815       {
10816         ResetGfxAnimation(x, y);
10817         ResetRandomAnimationValue(x, y);
10818       }
10819
10820       if (change->pre_change_function)
10821         change->pre_change_function(x, y);
10822     }
10823   }
10824
10825   ChangeDelay[x][y]--;
10826
10827   if (ChangeDelay[x][y] != 0)           // continue element change
10828   {
10829     if (change->can_change)
10830     {
10831       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10832
10833       if (IS_ANIMATED(graphic))
10834         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10835
10836       if (change->change_function)
10837         change->change_function(x, y);
10838     }
10839   }
10840   else                                  // finish element change
10841   {
10842     if (ChangePage[x][y] != -1)         // remember page from delayed change
10843     {
10844       page = ChangePage[x][y];
10845       ChangePage[x][y] = -1;
10846
10847       change = &ei->change_page[page];
10848     }
10849
10850     if (IS_MOVING(x, y))                // never change a running system ;-)
10851     {
10852       ChangeDelay[x][y] = 1;            // try change after next move step
10853       ChangePage[x][y] = page;          // remember page to use for change
10854
10855       return;
10856     }
10857
10858     // special case: set new level random seed before changing element
10859     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10860       handle_action_before_change = TRUE;
10861
10862     if (change->has_action && handle_action_before_change)
10863       ExecuteCustomElementAction(x, y, element, page);
10864
10865     if (change->can_change)
10866     {
10867       if (ChangeElement(x, y, element, page))
10868       {
10869         if (change->post_change_function)
10870           change->post_change_function(x, y);
10871       }
10872     }
10873
10874     if (change->has_action && !handle_action_before_change)
10875       ExecuteCustomElementAction(x, y, element, page);
10876   }
10877 }
10878
10879 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10880                                               int trigger_element,
10881                                               int trigger_event,
10882                                               int trigger_player,
10883                                               int trigger_side,
10884                                               int trigger_page)
10885 {
10886   boolean change_done_any = FALSE;
10887   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10888   int i;
10889
10890   if (!(trigger_events[trigger_element][trigger_event]))
10891     return FALSE;
10892
10893   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10894
10895   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10896   {
10897     int element = EL_CUSTOM_START + i;
10898     boolean change_done = FALSE;
10899     int p;
10900
10901     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10902         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10903       continue;
10904
10905     for (p = 0; p < element_info[element].num_change_pages; p++)
10906     {
10907       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10908
10909       if (change->can_change_or_has_action &&
10910           change->has_event[trigger_event] &&
10911           change->trigger_side & trigger_side &&
10912           change->trigger_player & trigger_player &&
10913           change->trigger_page & trigger_page_bits &&
10914           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10915       {
10916         change->actual_trigger_element = trigger_element;
10917         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10918         change->actual_trigger_player_bits = trigger_player;
10919         change->actual_trigger_side = trigger_side;
10920         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10921         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10922
10923         if ((change->can_change && !change_done) || change->has_action)
10924         {
10925           int x, y;
10926
10927           SCAN_PLAYFIELD(x, y)
10928           {
10929             if (Tile[x][y] == element)
10930             {
10931               if (change->can_change && !change_done)
10932               {
10933                 // if element already changed in this frame, not only prevent
10934                 // another element change (checked in ChangeElement()), but
10935                 // also prevent additional element actions for this element
10936
10937                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10938                     !level.use_action_after_change_bug)
10939                   continue;
10940
10941                 ChangeDelay[x][y] = 1;
10942                 ChangeEvent[x][y] = trigger_event;
10943
10944                 HandleElementChange(x, y, p);
10945               }
10946               else if (change->has_action)
10947               {
10948                 // if element already changed in this frame, not only prevent
10949                 // another element change (checked in ChangeElement()), but
10950                 // also prevent additional element actions for this element
10951
10952                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10953                     !level.use_action_after_change_bug)
10954                   continue;
10955
10956                 ExecuteCustomElementAction(x, y, element, p);
10957                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10958               }
10959             }
10960           }
10961
10962           if (change->can_change)
10963           {
10964             change_done = TRUE;
10965             change_done_any = TRUE;
10966           }
10967         }
10968       }
10969     }
10970   }
10971
10972   RECURSION_LOOP_DETECTION_END();
10973
10974   return change_done_any;
10975 }
10976
10977 static boolean CheckElementChangeExt(int x, int y,
10978                                      int element,
10979                                      int trigger_element,
10980                                      int trigger_event,
10981                                      int trigger_player,
10982                                      int trigger_side)
10983 {
10984   boolean change_done = FALSE;
10985   int p;
10986
10987   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10988       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10989     return FALSE;
10990
10991   if (Tile[x][y] == EL_BLOCKED)
10992   {
10993     Blocked2Moving(x, y, &x, &y);
10994     element = Tile[x][y];
10995   }
10996
10997   // check if element has already changed or is about to change after moving
10998   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10999        Tile[x][y] != element) ||
11000
11001       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11002        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11003         ChangePage[x][y] != -1)))
11004     return FALSE;
11005
11006   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11007
11008   for (p = 0; p < element_info[element].num_change_pages; p++)
11009   {
11010     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11011
11012     /* check trigger element for all events where the element that is checked
11013        for changing interacts with a directly adjacent element -- this is
11014        different to element changes that affect other elements to change on the
11015        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11016     boolean check_trigger_element =
11017       (trigger_event == CE_TOUCHING_X ||
11018        trigger_event == CE_HITTING_X ||
11019        trigger_event == CE_HIT_BY_X ||
11020        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11021
11022     if (change->can_change_or_has_action &&
11023         change->has_event[trigger_event] &&
11024         change->trigger_side & trigger_side &&
11025         change->trigger_player & trigger_player &&
11026         (!check_trigger_element ||
11027          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11028     {
11029       change->actual_trigger_element = trigger_element;
11030       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11031       change->actual_trigger_player_bits = trigger_player;
11032       change->actual_trigger_side = trigger_side;
11033       change->actual_trigger_ce_value = CustomValue[x][y];
11034       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11035
11036       // special case: trigger element not at (x,y) position for some events
11037       if (check_trigger_element)
11038       {
11039         static struct
11040         {
11041           int dx, dy;
11042         } move_xy[] =
11043           {
11044             {  0,  0 },
11045             { -1,  0 },
11046             { +1,  0 },
11047             {  0,  0 },
11048             {  0, -1 },
11049             {  0,  0 }, { 0, 0 }, { 0, 0 },
11050             {  0, +1 }
11051           };
11052
11053         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11054         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11055
11056         change->actual_trigger_ce_value = CustomValue[xx][yy];
11057         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11058       }
11059
11060       if (change->can_change && !change_done)
11061       {
11062         ChangeDelay[x][y] = 1;
11063         ChangeEvent[x][y] = trigger_event;
11064
11065         HandleElementChange(x, y, p);
11066
11067         change_done = TRUE;
11068       }
11069       else if (change->has_action)
11070       {
11071         ExecuteCustomElementAction(x, y, element, p);
11072         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11073       }
11074     }
11075   }
11076
11077   RECURSION_LOOP_DETECTION_END();
11078
11079   return change_done;
11080 }
11081
11082 static void PlayPlayerSound(struct PlayerInfo *player)
11083 {
11084   int jx = player->jx, jy = player->jy;
11085   int sound_element = player->artwork_element;
11086   int last_action = player->last_action_waiting;
11087   int action = player->action_waiting;
11088
11089   if (player->is_waiting)
11090   {
11091     if (action != last_action)
11092       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11093     else
11094       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11095   }
11096   else
11097   {
11098     if (action != last_action)
11099       StopSound(element_info[sound_element].sound[last_action]);
11100
11101     if (last_action == ACTION_SLEEPING)
11102       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11103   }
11104 }
11105
11106 static void PlayAllPlayersSound(void)
11107 {
11108   int i;
11109
11110   for (i = 0; i < MAX_PLAYERS; i++)
11111     if (stored_player[i].active)
11112       PlayPlayerSound(&stored_player[i]);
11113 }
11114
11115 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11116 {
11117   boolean last_waiting = player->is_waiting;
11118   int move_dir = player->MovDir;
11119
11120   player->dir_waiting = move_dir;
11121   player->last_action_waiting = player->action_waiting;
11122
11123   if (is_waiting)
11124   {
11125     if (!last_waiting)          // not waiting -> waiting
11126     {
11127       player->is_waiting = TRUE;
11128
11129       player->frame_counter_bored =
11130         FrameCounter +
11131         game.player_boring_delay_fixed +
11132         GetSimpleRandom(game.player_boring_delay_random);
11133       player->frame_counter_sleeping =
11134         FrameCounter +
11135         game.player_sleeping_delay_fixed +
11136         GetSimpleRandom(game.player_sleeping_delay_random);
11137
11138       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11139     }
11140
11141     if (game.player_sleeping_delay_fixed +
11142         game.player_sleeping_delay_random > 0 &&
11143         player->anim_delay_counter == 0 &&
11144         player->post_delay_counter == 0 &&
11145         FrameCounter >= player->frame_counter_sleeping)
11146       player->is_sleeping = TRUE;
11147     else if (game.player_boring_delay_fixed +
11148              game.player_boring_delay_random > 0 &&
11149              FrameCounter >= player->frame_counter_bored)
11150       player->is_bored = TRUE;
11151
11152     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11153                               player->is_bored ? ACTION_BORING :
11154                               ACTION_WAITING);
11155
11156     if (player->is_sleeping && player->use_murphy)
11157     {
11158       // special case for sleeping Murphy when leaning against non-free tile
11159
11160       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11161           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11162            !IS_MOVING(player->jx - 1, player->jy)))
11163         move_dir = MV_LEFT;
11164       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11165                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11166                 !IS_MOVING(player->jx + 1, player->jy)))
11167         move_dir = MV_RIGHT;
11168       else
11169         player->is_sleeping = FALSE;
11170
11171       player->dir_waiting = move_dir;
11172     }
11173
11174     if (player->is_sleeping)
11175     {
11176       if (player->num_special_action_sleeping > 0)
11177       {
11178         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11179         {
11180           int last_special_action = player->special_action_sleeping;
11181           int num_special_action = player->num_special_action_sleeping;
11182           int special_action =
11183             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11184              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11185              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11186              last_special_action + 1 : ACTION_SLEEPING);
11187           int special_graphic =
11188             el_act_dir2img(player->artwork_element, special_action, move_dir);
11189
11190           player->anim_delay_counter =
11191             graphic_info[special_graphic].anim_delay_fixed +
11192             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11193           player->post_delay_counter =
11194             graphic_info[special_graphic].post_delay_fixed +
11195             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11196
11197           player->special_action_sleeping = special_action;
11198         }
11199
11200         if (player->anim_delay_counter > 0)
11201         {
11202           player->action_waiting = player->special_action_sleeping;
11203           player->anim_delay_counter--;
11204         }
11205         else if (player->post_delay_counter > 0)
11206         {
11207           player->post_delay_counter--;
11208         }
11209       }
11210     }
11211     else if (player->is_bored)
11212     {
11213       if (player->num_special_action_bored > 0)
11214       {
11215         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11216         {
11217           int special_action =
11218             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11219           int special_graphic =
11220             el_act_dir2img(player->artwork_element, special_action, move_dir);
11221
11222           player->anim_delay_counter =
11223             graphic_info[special_graphic].anim_delay_fixed +
11224             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11225           player->post_delay_counter =
11226             graphic_info[special_graphic].post_delay_fixed +
11227             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11228
11229           player->special_action_bored = special_action;
11230         }
11231
11232         if (player->anim_delay_counter > 0)
11233         {
11234           player->action_waiting = player->special_action_bored;
11235           player->anim_delay_counter--;
11236         }
11237         else if (player->post_delay_counter > 0)
11238         {
11239           player->post_delay_counter--;
11240         }
11241       }
11242     }
11243   }
11244   else if (last_waiting)        // waiting -> not waiting
11245   {
11246     player->is_waiting = FALSE;
11247     player->is_bored = FALSE;
11248     player->is_sleeping = FALSE;
11249
11250     player->frame_counter_bored = -1;
11251     player->frame_counter_sleeping = -1;
11252
11253     player->anim_delay_counter = 0;
11254     player->post_delay_counter = 0;
11255
11256     player->dir_waiting = player->MovDir;
11257     player->action_waiting = ACTION_DEFAULT;
11258
11259     player->special_action_bored = ACTION_DEFAULT;
11260     player->special_action_sleeping = ACTION_DEFAULT;
11261   }
11262 }
11263
11264 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11265 {
11266   if ((!player->is_moving  && player->was_moving) ||
11267       (player->MovPos == 0 && player->was_moving) ||
11268       (player->is_snapping && !player->was_snapping) ||
11269       (player->is_dropping && !player->was_dropping))
11270   {
11271     if (!CheckSaveEngineSnapshotToList())
11272       return;
11273
11274     player->was_moving = FALSE;
11275     player->was_snapping = TRUE;
11276     player->was_dropping = TRUE;
11277   }
11278   else
11279   {
11280     if (player->is_moving)
11281       player->was_moving = TRUE;
11282
11283     if (!player->is_snapping)
11284       player->was_snapping = FALSE;
11285
11286     if (!player->is_dropping)
11287       player->was_dropping = FALSE;
11288   }
11289
11290   static struct MouseActionInfo mouse_action_last = { 0 };
11291   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11292   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11293
11294   if (new_released)
11295     CheckSaveEngineSnapshotToList();
11296
11297   mouse_action_last = mouse_action;
11298 }
11299
11300 static void CheckSingleStepMode(struct PlayerInfo *player)
11301 {
11302   if (tape.single_step && tape.recording && !tape.pausing)
11303   {
11304     // as it is called "single step mode", just return to pause mode when the
11305     // player stopped moving after one tile (or never starts moving at all)
11306     // (reverse logic needed here in case single step mode used in team mode)
11307     if (player->is_moving ||
11308         player->is_pushing ||
11309         player->is_dropping_pressed ||
11310         player->effective_mouse_action.button)
11311       game.enter_single_step_mode = FALSE;
11312   }
11313
11314   CheckSaveEngineSnapshot(player);
11315 }
11316
11317 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11318 {
11319   int left      = player_action & JOY_LEFT;
11320   int right     = player_action & JOY_RIGHT;
11321   int up        = player_action & JOY_UP;
11322   int down      = player_action & JOY_DOWN;
11323   int button1   = player_action & JOY_BUTTON_1;
11324   int button2   = player_action & JOY_BUTTON_2;
11325   int dx        = (left ? -1 : right ? 1 : 0);
11326   int dy        = (up   ? -1 : down  ? 1 : 0);
11327
11328   if (!player->active || tape.pausing)
11329     return 0;
11330
11331   if (player_action)
11332   {
11333     if (button1)
11334       SnapField(player, dx, dy);
11335     else
11336     {
11337       if (button2)
11338         DropElement(player);
11339
11340       MovePlayer(player, dx, dy);
11341     }
11342
11343     CheckSingleStepMode(player);
11344
11345     SetPlayerWaiting(player, FALSE);
11346
11347     return player_action;
11348   }
11349   else
11350   {
11351     // no actions for this player (no input at player's configured device)
11352
11353     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11354     SnapField(player, 0, 0);
11355     CheckGravityMovementWhenNotMoving(player);
11356
11357     if (player->MovPos == 0)
11358       SetPlayerWaiting(player, TRUE);
11359
11360     if (player->MovPos == 0)    // needed for tape.playing
11361       player->is_moving = FALSE;
11362
11363     player->is_dropping = FALSE;
11364     player->is_dropping_pressed = FALSE;
11365     player->drop_pressed_delay = 0;
11366
11367     CheckSingleStepMode(player);
11368
11369     return 0;
11370   }
11371 }
11372
11373 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11374                                          byte *tape_action)
11375 {
11376   if (!tape.use_mouse_actions)
11377     return;
11378
11379   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11380   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11381   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11382 }
11383
11384 static void SetTapeActionFromMouseAction(byte *tape_action,
11385                                          struct MouseActionInfo *mouse_action)
11386 {
11387   if (!tape.use_mouse_actions)
11388     return;
11389
11390   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11391   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11392   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11393 }
11394
11395 static void CheckLevelSolved(void)
11396 {
11397   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11398   {
11399     if (game_em.level_solved &&
11400         !game_em.game_over)                             // game won
11401     {
11402       LevelSolved();
11403
11404       game_em.game_over = TRUE;
11405
11406       game.all_players_gone = TRUE;
11407     }
11408
11409     if (game_em.game_over)                              // game lost
11410       game.all_players_gone = TRUE;
11411   }
11412   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11413   {
11414     if (game_sp.level_solved &&
11415         !game_sp.game_over)                             // game won
11416     {
11417       LevelSolved();
11418
11419       game_sp.game_over = TRUE;
11420
11421       game.all_players_gone = TRUE;
11422     }
11423
11424     if (game_sp.game_over)                              // game lost
11425       game.all_players_gone = TRUE;
11426   }
11427   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11428   {
11429     if (game_mm.level_solved &&
11430         !game_mm.game_over)                             // game won
11431     {
11432       LevelSolved();
11433
11434       game_mm.game_over = TRUE;
11435
11436       game.all_players_gone = TRUE;
11437     }
11438
11439     if (game_mm.game_over)                              // game lost
11440       game.all_players_gone = TRUE;
11441   }
11442 }
11443
11444 static void CheckLevelTime(void)
11445 {
11446   int i;
11447
11448   if (TimeFrames >= FRAMES_PER_SECOND)
11449   {
11450     TimeFrames = 0;
11451     TapeTime++;
11452
11453     for (i = 0; i < MAX_PLAYERS; i++)
11454     {
11455       struct PlayerInfo *player = &stored_player[i];
11456
11457       if (SHIELD_ON(player))
11458       {
11459         player->shield_normal_time_left--;
11460
11461         if (player->shield_deadly_time_left > 0)
11462           player->shield_deadly_time_left--;
11463       }
11464     }
11465
11466     if (!game.LevelSolved && !level.use_step_counter)
11467     {
11468       TimePlayed++;
11469
11470       if (TimeLeft > 0)
11471       {
11472         TimeLeft--;
11473
11474         if (TimeLeft <= 10 && setup.time_limit)
11475           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11476
11477         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11478            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11479
11480         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11481
11482         if (!TimeLeft && setup.time_limit)
11483         {
11484           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11485             game_em.lev->killed_out_of_time = TRUE;
11486           else
11487             for (i = 0; i < MAX_PLAYERS; i++)
11488               KillPlayer(&stored_player[i]);
11489         }
11490       }
11491       else if (game.no_time_limit && !game.all_players_gone)
11492       {
11493         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11494       }
11495
11496       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11497     }
11498
11499     if (tape.recording || tape.playing)
11500       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11501   }
11502
11503   if (tape.recording || tape.playing)
11504     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11505
11506   UpdateAndDisplayGameControlValues();
11507 }
11508
11509 void AdvanceFrameAndPlayerCounters(int player_nr)
11510 {
11511   int i;
11512
11513   // advance frame counters (global frame counter and time frame counter)
11514   FrameCounter++;
11515   TimeFrames++;
11516
11517   // advance player counters (counters for move delay, move animation etc.)
11518   for (i = 0; i < MAX_PLAYERS; i++)
11519   {
11520     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11521     int move_delay_value = stored_player[i].move_delay_value;
11522     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11523
11524     if (!advance_player_counters)       // not all players may be affected
11525       continue;
11526
11527     if (move_frames == 0)       // less than one move per game frame
11528     {
11529       int stepsize = TILEX / move_delay_value;
11530       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11531       int count = (stored_player[i].is_moving ?
11532                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11533
11534       if (count % delay == 0)
11535         move_frames = 1;
11536     }
11537
11538     stored_player[i].Frame += move_frames;
11539
11540     if (stored_player[i].MovPos != 0)
11541       stored_player[i].StepFrame += move_frames;
11542
11543     if (stored_player[i].move_delay > 0)
11544       stored_player[i].move_delay--;
11545
11546     // due to bugs in previous versions, counter must count up, not down
11547     if (stored_player[i].push_delay != -1)
11548       stored_player[i].push_delay++;
11549
11550     if (stored_player[i].drop_delay > 0)
11551       stored_player[i].drop_delay--;
11552
11553     if (stored_player[i].is_dropping_pressed)
11554       stored_player[i].drop_pressed_delay++;
11555   }
11556 }
11557
11558 void StartGameActions(boolean init_network_game, boolean record_tape,
11559                       int random_seed)
11560 {
11561   unsigned int new_random_seed = InitRND(random_seed);
11562
11563   if (record_tape)
11564     TapeStartRecording(new_random_seed);
11565
11566   if (init_network_game)
11567   {
11568     SendToServer_LevelFile();
11569     SendToServer_StartPlaying();
11570
11571     return;
11572   }
11573
11574   InitGame();
11575 }
11576
11577 static void GameActionsExt(void)
11578 {
11579 #if 0
11580   static unsigned int game_frame_delay = 0;
11581 #endif
11582   unsigned int game_frame_delay_value;
11583   byte *recorded_player_action;
11584   byte summarized_player_action = 0;
11585   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11586   int i;
11587
11588   // detect endless loops, caused by custom element programming
11589   if (recursion_loop_detected && recursion_loop_depth == 0)
11590   {
11591     char *message = getStringCat3("Internal Error! Element ",
11592                                   EL_NAME(recursion_loop_element),
11593                                   " caused endless loop! Quit the game?");
11594
11595     Warn("element '%s' caused endless loop in game engine",
11596          EL_NAME(recursion_loop_element));
11597
11598     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11599
11600     recursion_loop_detected = FALSE;    // if game should be continued
11601
11602     free(message);
11603
11604     return;
11605   }
11606
11607   if (game.restart_level)
11608     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11609
11610   CheckLevelSolved();
11611
11612   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11613     GameWon();
11614
11615   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11616     TapeStop();
11617
11618   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11619     return;
11620
11621   game_frame_delay_value =
11622     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11623
11624   if (tape.playing && tape.warp_forward && !tape.pausing)
11625     game_frame_delay_value = 0;
11626
11627   SetVideoFrameDelay(game_frame_delay_value);
11628
11629   // (de)activate virtual buttons depending on current game status
11630   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11631   {
11632     if (game.all_players_gone)  // if no players there to be controlled anymore
11633       SetOverlayActive(FALSE);
11634     else if (!tape.playing)     // if game continues after tape stopped playing
11635       SetOverlayActive(TRUE);
11636   }
11637
11638 #if 0
11639 #if 0
11640   // ---------- main game synchronization point ----------
11641
11642   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11643
11644   Debug("game:playing:skip", "skip == %d", skip);
11645
11646 #else
11647   // ---------- main game synchronization point ----------
11648
11649   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11650 #endif
11651 #endif
11652
11653   if (network_playing && !network_player_action_received)
11654   {
11655     // try to get network player actions in time
11656
11657     // last chance to get network player actions without main loop delay
11658     HandleNetworking();
11659
11660     // game was quit by network peer
11661     if (game_status != GAME_MODE_PLAYING)
11662       return;
11663
11664     // check if network player actions still missing and game still running
11665     if (!network_player_action_received && !checkGameEnded())
11666       return;           // failed to get network player actions in time
11667
11668     // do not yet reset "network_player_action_received" (for tape.pausing)
11669   }
11670
11671   if (tape.pausing)
11672     return;
11673
11674   // at this point we know that we really continue executing the game
11675
11676   network_player_action_received = FALSE;
11677
11678   // when playing tape, read previously recorded player input from tape data
11679   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11680
11681   local_player->effective_mouse_action = local_player->mouse_action;
11682
11683   if (recorded_player_action != NULL)
11684     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11685                                  recorded_player_action);
11686
11687   // TapePlayAction() may return NULL when toggling to "pause before death"
11688   if (tape.pausing)
11689     return;
11690
11691   if (tape.set_centered_player)
11692   {
11693     game.centered_player_nr_next = tape.centered_player_nr_next;
11694     game.set_centered_player = TRUE;
11695   }
11696
11697   for (i = 0; i < MAX_PLAYERS; i++)
11698   {
11699     summarized_player_action |= stored_player[i].action;
11700
11701     if (!network_playing && (game.team_mode || tape.playing))
11702       stored_player[i].effective_action = stored_player[i].action;
11703   }
11704
11705   if (network_playing && !checkGameEnded())
11706     SendToServer_MovePlayer(summarized_player_action);
11707
11708   // summarize all actions at local players mapped input device position
11709   // (this allows using different input devices in single player mode)
11710   if (!network.enabled && !game.team_mode)
11711     stored_player[map_player_action[local_player->index_nr]].effective_action =
11712       summarized_player_action;
11713
11714   // summarize all actions at centered player in local team mode
11715   if (tape.recording &&
11716       setup.team_mode && !network.enabled &&
11717       setup.input_on_focus &&
11718       game.centered_player_nr != -1)
11719   {
11720     for (i = 0; i < MAX_PLAYERS; i++)
11721       stored_player[map_player_action[i]].effective_action =
11722         (i == game.centered_player_nr ? summarized_player_action : 0);
11723   }
11724
11725   if (recorded_player_action != NULL)
11726     for (i = 0; i < MAX_PLAYERS; i++)
11727       stored_player[i].effective_action = recorded_player_action[i];
11728
11729   for (i = 0; i < MAX_PLAYERS; i++)
11730   {
11731     tape_action[i] = stored_player[i].effective_action;
11732
11733     /* (this may happen in the RND game engine if a player was not present on
11734        the playfield on level start, but appeared later from a custom element */
11735     if (setup.team_mode &&
11736         tape.recording &&
11737         tape_action[i] &&
11738         !tape.player_participates[i])
11739       tape.player_participates[i] = TRUE;
11740   }
11741
11742   SetTapeActionFromMouseAction(tape_action,
11743                                &local_player->effective_mouse_action);
11744
11745   // only record actions from input devices, but not programmed actions
11746   if (tape.recording)
11747     TapeRecordAction(tape_action);
11748
11749   // remember if game was played (especially after tape stopped playing)
11750   if (!tape.playing && summarized_player_action)
11751     game.GamePlayed = TRUE;
11752
11753 #if USE_NEW_PLAYER_ASSIGNMENTS
11754   // !!! also map player actions in single player mode !!!
11755   // if (game.team_mode)
11756   if (1)
11757   {
11758     byte mapped_action[MAX_PLAYERS];
11759
11760 #if DEBUG_PLAYER_ACTIONS
11761     for (i = 0; i < MAX_PLAYERS; i++)
11762       DebugContinued("", "%d, ", stored_player[i].effective_action);
11763 #endif
11764
11765     for (i = 0; i < MAX_PLAYERS; i++)
11766       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11767
11768     for (i = 0; i < MAX_PLAYERS; i++)
11769       stored_player[i].effective_action = mapped_action[i];
11770
11771 #if DEBUG_PLAYER_ACTIONS
11772     DebugContinued("", "=> ");
11773     for (i = 0; i < MAX_PLAYERS; i++)
11774       DebugContinued("", "%d, ", stored_player[i].effective_action);
11775     DebugContinued("game:playing:player", "\n");
11776 #endif
11777   }
11778 #if DEBUG_PLAYER_ACTIONS
11779   else
11780   {
11781     for (i = 0; i < MAX_PLAYERS; i++)
11782       DebugContinued("", "%d, ", stored_player[i].effective_action);
11783     DebugContinued("game:playing:player", "\n");
11784   }
11785 #endif
11786 #endif
11787
11788   for (i = 0; i < MAX_PLAYERS; i++)
11789   {
11790     // allow engine snapshot in case of changed movement attempt
11791     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11792         (stored_player[i].effective_action & KEY_MOTION))
11793       game.snapshot.changed_action = TRUE;
11794
11795     // allow engine snapshot in case of snapping/dropping attempt
11796     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11797         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11798       game.snapshot.changed_action = TRUE;
11799
11800     game.snapshot.last_action[i] = stored_player[i].effective_action;
11801   }
11802
11803   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11804   {
11805     GameActions_EM_Main();
11806   }
11807   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11808   {
11809     GameActions_SP_Main();
11810   }
11811   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11812   {
11813     GameActions_MM_Main();
11814   }
11815   else
11816   {
11817     GameActions_RND_Main();
11818   }
11819
11820   BlitScreenToBitmap(backbuffer);
11821
11822   CheckLevelSolved();
11823   CheckLevelTime();
11824
11825   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11826
11827   if (global.show_frames_per_second)
11828   {
11829     static unsigned int fps_counter = 0;
11830     static int fps_frames = 0;
11831     unsigned int fps_delay_ms = Counter() - fps_counter;
11832
11833     fps_frames++;
11834
11835     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11836     {
11837       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11838
11839       fps_frames = 0;
11840       fps_counter = Counter();
11841
11842       // always draw FPS to screen after FPS value was updated
11843       redraw_mask |= REDRAW_FPS;
11844     }
11845
11846     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11847     if (GetDrawDeactivationMask() == REDRAW_NONE)
11848       redraw_mask |= REDRAW_FPS;
11849   }
11850 }
11851
11852 static void GameActions_CheckSaveEngineSnapshot(void)
11853 {
11854   if (!game.snapshot.save_snapshot)
11855     return;
11856
11857   // clear flag for saving snapshot _before_ saving snapshot
11858   game.snapshot.save_snapshot = FALSE;
11859
11860   SaveEngineSnapshotToList();
11861 }
11862
11863 void GameActions(void)
11864 {
11865   GameActionsExt();
11866
11867   GameActions_CheckSaveEngineSnapshot();
11868 }
11869
11870 void GameActions_EM_Main(void)
11871 {
11872   byte effective_action[MAX_PLAYERS];
11873   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11874   int i;
11875
11876   for (i = 0; i < MAX_PLAYERS; i++)
11877     effective_action[i] = stored_player[i].effective_action;
11878
11879   GameActions_EM(effective_action, warp_mode);
11880 }
11881
11882 void GameActions_SP_Main(void)
11883 {
11884   byte effective_action[MAX_PLAYERS];
11885   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11886   int i;
11887
11888   for (i = 0; i < MAX_PLAYERS; i++)
11889     effective_action[i] = stored_player[i].effective_action;
11890
11891   GameActions_SP(effective_action, warp_mode);
11892
11893   for (i = 0; i < MAX_PLAYERS; i++)
11894   {
11895     if (stored_player[i].force_dropping)
11896       stored_player[i].action |= KEY_BUTTON_DROP;
11897
11898     stored_player[i].force_dropping = FALSE;
11899   }
11900 }
11901
11902 void GameActions_MM_Main(void)
11903 {
11904   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11905
11906   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11907 }
11908
11909 void GameActions_RND_Main(void)
11910 {
11911   GameActions_RND();
11912 }
11913
11914 void GameActions_RND(void)
11915 {
11916   static struct MouseActionInfo mouse_action_last = { 0 };
11917   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11918   int magic_wall_x = 0, magic_wall_y = 0;
11919   int i, x, y, element, graphic, last_gfx_frame;
11920
11921   InitPlayfieldScanModeVars();
11922
11923   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11924   {
11925     SCAN_PLAYFIELD(x, y)
11926     {
11927       ChangeCount[x][y] = 0;
11928       ChangeEvent[x][y] = -1;
11929     }
11930   }
11931
11932   if (game.set_centered_player)
11933   {
11934     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11935
11936     // switching to "all players" only possible if all players fit to screen
11937     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11938     {
11939       game.centered_player_nr_next = game.centered_player_nr;
11940       game.set_centered_player = FALSE;
11941     }
11942
11943     // do not switch focus to non-existing (or non-active) player
11944     if (game.centered_player_nr_next >= 0 &&
11945         !stored_player[game.centered_player_nr_next].active)
11946     {
11947       game.centered_player_nr_next = game.centered_player_nr;
11948       game.set_centered_player = FALSE;
11949     }
11950   }
11951
11952   if (game.set_centered_player &&
11953       ScreenMovPos == 0)        // screen currently aligned at tile position
11954   {
11955     int sx, sy;
11956
11957     if (game.centered_player_nr_next == -1)
11958     {
11959       setScreenCenteredToAllPlayers(&sx, &sy);
11960     }
11961     else
11962     {
11963       sx = stored_player[game.centered_player_nr_next].jx;
11964       sy = stored_player[game.centered_player_nr_next].jy;
11965     }
11966
11967     game.centered_player_nr = game.centered_player_nr_next;
11968     game.set_centered_player = FALSE;
11969
11970     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11971     DrawGameDoorValues();
11972   }
11973
11974   // check single step mode (set flag and clear again if any player is active)
11975   game.enter_single_step_mode =
11976     (tape.single_step && tape.recording && !tape.pausing);
11977
11978   for (i = 0; i < MAX_PLAYERS; i++)
11979   {
11980     int actual_player_action = stored_player[i].effective_action;
11981
11982 #if 1
11983     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11984        - rnd_equinox_tetrachloride 048
11985        - rnd_equinox_tetrachloride_ii 096
11986        - rnd_emanuel_schmieg 002
11987        - doctor_sloan_ww 001, 020
11988     */
11989     if (stored_player[i].MovPos == 0)
11990       CheckGravityMovement(&stored_player[i]);
11991 #endif
11992
11993     // overwrite programmed action with tape action
11994     if (stored_player[i].programmed_action)
11995       actual_player_action = stored_player[i].programmed_action;
11996
11997     PlayerActions(&stored_player[i], actual_player_action);
11998
11999     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12000   }
12001
12002   // single step pause mode may already have been toggled by "ScrollPlayer()"
12003   if (game.enter_single_step_mode && !tape.pausing)
12004     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12005
12006   ScrollScreen(NULL, SCROLL_GO_ON);
12007
12008   /* for backwards compatibility, the following code emulates a fixed bug that
12009      occured when pushing elements (causing elements that just made their last
12010      pushing step to already (if possible) make their first falling step in the
12011      same game frame, which is bad); this code is also needed to use the famous
12012      "spring push bug" which is used in older levels and might be wanted to be
12013      used also in newer levels, but in this case the buggy pushing code is only
12014      affecting the "spring" element and no other elements */
12015
12016   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12017   {
12018     for (i = 0; i < MAX_PLAYERS; i++)
12019     {
12020       struct PlayerInfo *player = &stored_player[i];
12021       int x = player->jx;
12022       int y = player->jy;
12023
12024       if (player->active && player->is_pushing && player->is_moving &&
12025           IS_MOVING(x, y) &&
12026           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12027            Tile[x][y] == EL_SPRING))
12028       {
12029         ContinueMoving(x, y);
12030
12031         // continue moving after pushing (this is actually a bug)
12032         if (!IS_MOVING(x, y))
12033           Stop[x][y] = FALSE;
12034       }
12035     }
12036   }
12037
12038   SCAN_PLAYFIELD(x, y)
12039   {
12040     Last[x][y] = Tile[x][y];
12041
12042     ChangeCount[x][y] = 0;
12043     ChangeEvent[x][y] = -1;
12044
12045     // this must be handled before main playfield loop
12046     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12047     {
12048       MovDelay[x][y]--;
12049       if (MovDelay[x][y] <= 0)
12050         RemoveField(x, y);
12051     }
12052
12053     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12054     {
12055       MovDelay[x][y]--;
12056       if (MovDelay[x][y] <= 0)
12057       {
12058         int element = Store[x][y];
12059         int move_direction = MovDir[x][y];
12060         int player_index_bit = Store2[x][y];
12061
12062         Store[x][y] = 0;
12063         Store2[x][y] = 0;
12064
12065         RemoveField(x, y);
12066         TEST_DrawLevelField(x, y);
12067
12068         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12069       }
12070     }
12071
12072 #if DEBUG
12073     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12074     {
12075       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12076             x, y);
12077       Debug("game:playing:GameActions_RND", "This should never happen!");
12078
12079       ChangePage[x][y] = -1;
12080     }
12081 #endif
12082
12083     Stop[x][y] = FALSE;
12084     if (WasJustMoving[x][y] > 0)
12085       WasJustMoving[x][y]--;
12086     if (WasJustFalling[x][y] > 0)
12087       WasJustFalling[x][y]--;
12088     if (CheckCollision[x][y] > 0)
12089       CheckCollision[x][y]--;
12090     if (CheckImpact[x][y] > 0)
12091       CheckImpact[x][y]--;
12092
12093     GfxFrame[x][y]++;
12094
12095     /* reset finished pushing action (not done in ContinueMoving() to allow
12096        continuous pushing animation for elements with zero push delay) */
12097     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12098     {
12099       ResetGfxAnimation(x, y);
12100       TEST_DrawLevelField(x, y);
12101     }
12102
12103 #if DEBUG
12104     if (IS_BLOCKED(x, y))
12105     {
12106       int oldx, oldy;
12107
12108       Blocked2Moving(x, y, &oldx, &oldy);
12109       if (!IS_MOVING(oldx, oldy))
12110       {
12111         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12112         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12113         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12114         Debug("game:playing:GameActions_RND", "This should never happen!");
12115       }
12116     }
12117 #endif
12118   }
12119
12120   if (mouse_action.button)
12121   {
12122     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12123
12124     x = mouse_action.lx;
12125     y = mouse_action.ly;
12126     element = Tile[x][y];
12127
12128     if (new_button)
12129     {
12130       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12131       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12132     }
12133
12134     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12135     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12136   }
12137
12138   SCAN_PLAYFIELD(x, y)
12139   {
12140     element = Tile[x][y];
12141     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12142     last_gfx_frame = GfxFrame[x][y];
12143
12144     ResetGfxFrame(x, y);
12145
12146     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12147       DrawLevelGraphicAnimation(x, y, graphic);
12148
12149     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12150         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12151       ResetRandomAnimationValue(x, y);
12152
12153     SetRandomAnimationValue(x, y);
12154
12155     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12156
12157     if (IS_INACTIVE(element))
12158     {
12159       if (IS_ANIMATED(graphic))
12160         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12161
12162       continue;
12163     }
12164
12165     // this may take place after moving, so 'element' may have changed
12166     if (IS_CHANGING(x, y) &&
12167         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12168     {
12169       int page = element_info[element].event_page_nr[CE_DELAY];
12170
12171       HandleElementChange(x, y, page);
12172
12173       element = Tile[x][y];
12174       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12175     }
12176
12177     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12178     {
12179       StartMoving(x, y);
12180
12181       element = Tile[x][y];
12182       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12183
12184       if (IS_ANIMATED(graphic) &&
12185           !IS_MOVING(x, y) &&
12186           !Stop[x][y])
12187         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12188
12189       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12190         TEST_DrawTwinkleOnField(x, y);
12191     }
12192     else if (element == EL_ACID)
12193     {
12194       if (!Stop[x][y])
12195         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12196     }
12197     else if ((element == EL_EXIT_OPEN ||
12198               element == EL_EM_EXIT_OPEN ||
12199               element == EL_SP_EXIT_OPEN ||
12200               element == EL_STEEL_EXIT_OPEN ||
12201               element == EL_EM_STEEL_EXIT_OPEN ||
12202               element == EL_SP_TERMINAL ||
12203               element == EL_SP_TERMINAL_ACTIVE ||
12204               element == EL_EXTRA_TIME ||
12205               element == EL_SHIELD_NORMAL ||
12206               element == EL_SHIELD_DEADLY) &&
12207              IS_ANIMATED(graphic))
12208       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12209     else if (IS_MOVING(x, y))
12210       ContinueMoving(x, y);
12211     else if (IS_ACTIVE_BOMB(element))
12212       CheckDynamite(x, y);
12213     else if (element == EL_AMOEBA_GROWING)
12214       AmoebaGrowing(x, y);
12215     else if (element == EL_AMOEBA_SHRINKING)
12216       AmoebaShrinking(x, y);
12217
12218 #if !USE_NEW_AMOEBA_CODE
12219     else if (IS_AMOEBALIVE(element))
12220       AmoebaReproduce(x, y);
12221 #endif
12222
12223     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12224       Life(x, y);
12225     else if (element == EL_EXIT_CLOSED)
12226       CheckExit(x, y);
12227     else if (element == EL_EM_EXIT_CLOSED)
12228       CheckExitEM(x, y);
12229     else if (element == EL_STEEL_EXIT_CLOSED)
12230       CheckExitSteel(x, y);
12231     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12232       CheckExitSteelEM(x, y);
12233     else if (element == EL_SP_EXIT_CLOSED)
12234       CheckExitSP(x, y);
12235     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12236              element == EL_EXPANDABLE_STEELWALL_GROWING)
12237       MauerWaechst(x, y);
12238     else if (element == EL_EXPANDABLE_WALL ||
12239              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12240              element == EL_EXPANDABLE_WALL_VERTICAL ||
12241              element == EL_EXPANDABLE_WALL_ANY ||
12242              element == EL_BD_EXPANDABLE_WALL)
12243       MauerAbleger(x, y);
12244     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12245              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12246              element == EL_EXPANDABLE_STEELWALL_ANY)
12247       MauerAblegerStahl(x, y);
12248     else if (element == EL_FLAMES)
12249       CheckForDragon(x, y);
12250     else if (element == EL_EXPLOSION)
12251       ; // drawing of correct explosion animation is handled separately
12252     else if (element == EL_ELEMENT_SNAPPING ||
12253              element == EL_DIAGONAL_SHRINKING ||
12254              element == EL_DIAGONAL_GROWING)
12255     {
12256       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12257
12258       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12259     }
12260     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12261       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12262
12263     if (IS_BELT_ACTIVE(element))
12264       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12265
12266     if (game.magic_wall_active)
12267     {
12268       int jx = local_player->jx, jy = local_player->jy;
12269
12270       // play the element sound at the position nearest to the player
12271       if ((element == EL_MAGIC_WALL_FULL ||
12272            element == EL_MAGIC_WALL_ACTIVE ||
12273            element == EL_MAGIC_WALL_EMPTYING ||
12274            element == EL_BD_MAGIC_WALL_FULL ||
12275            element == EL_BD_MAGIC_WALL_ACTIVE ||
12276            element == EL_BD_MAGIC_WALL_EMPTYING ||
12277            element == EL_DC_MAGIC_WALL_FULL ||
12278            element == EL_DC_MAGIC_WALL_ACTIVE ||
12279            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12280           ABS(x - jx) + ABS(y - jy) <
12281           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12282       {
12283         magic_wall_x = x;
12284         magic_wall_y = y;
12285       }
12286     }
12287   }
12288
12289 #if USE_NEW_AMOEBA_CODE
12290   // new experimental amoeba growth stuff
12291   if (!(FrameCounter % 8))
12292   {
12293     static unsigned int random = 1684108901;
12294
12295     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12296     {
12297       x = RND(lev_fieldx);
12298       y = RND(lev_fieldy);
12299       element = Tile[x][y];
12300
12301       if (!IS_PLAYER(x,y) &&
12302           (element == EL_EMPTY ||
12303            CAN_GROW_INTO(element) ||
12304            element == EL_QUICKSAND_EMPTY ||
12305            element == EL_QUICKSAND_FAST_EMPTY ||
12306            element == EL_ACID_SPLASH_LEFT ||
12307            element == EL_ACID_SPLASH_RIGHT))
12308       {
12309         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12310             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12311             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12312             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12313           Tile[x][y] = EL_AMOEBA_DROP;
12314       }
12315
12316       random = random * 129 + 1;
12317     }
12318   }
12319 #endif
12320
12321   game.explosions_delayed = FALSE;
12322
12323   SCAN_PLAYFIELD(x, y)
12324   {
12325     element = Tile[x][y];
12326
12327     if (ExplodeField[x][y])
12328       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12329     else if (element == EL_EXPLOSION)
12330       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12331
12332     ExplodeField[x][y] = EX_TYPE_NONE;
12333   }
12334
12335   game.explosions_delayed = TRUE;
12336
12337   if (game.magic_wall_active)
12338   {
12339     if (!(game.magic_wall_time_left % 4))
12340     {
12341       int element = Tile[magic_wall_x][magic_wall_y];
12342
12343       if (element == EL_BD_MAGIC_WALL_FULL ||
12344           element == EL_BD_MAGIC_WALL_ACTIVE ||
12345           element == EL_BD_MAGIC_WALL_EMPTYING)
12346         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12347       else if (element == EL_DC_MAGIC_WALL_FULL ||
12348                element == EL_DC_MAGIC_WALL_ACTIVE ||
12349                element == EL_DC_MAGIC_WALL_EMPTYING)
12350         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12351       else
12352         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12353     }
12354
12355     if (game.magic_wall_time_left > 0)
12356     {
12357       game.magic_wall_time_left--;
12358
12359       if (!game.magic_wall_time_left)
12360       {
12361         SCAN_PLAYFIELD(x, y)
12362         {
12363           element = Tile[x][y];
12364
12365           if (element == EL_MAGIC_WALL_ACTIVE ||
12366               element == EL_MAGIC_WALL_FULL)
12367           {
12368             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12369             TEST_DrawLevelField(x, y);
12370           }
12371           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12372                    element == EL_BD_MAGIC_WALL_FULL)
12373           {
12374             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12375             TEST_DrawLevelField(x, y);
12376           }
12377           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12378                    element == EL_DC_MAGIC_WALL_FULL)
12379           {
12380             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12381             TEST_DrawLevelField(x, y);
12382           }
12383         }
12384
12385         game.magic_wall_active = FALSE;
12386       }
12387     }
12388   }
12389
12390   if (game.light_time_left > 0)
12391   {
12392     game.light_time_left--;
12393
12394     if (game.light_time_left == 0)
12395       RedrawAllLightSwitchesAndInvisibleElements();
12396   }
12397
12398   if (game.timegate_time_left > 0)
12399   {
12400     game.timegate_time_left--;
12401
12402     if (game.timegate_time_left == 0)
12403       CloseAllOpenTimegates();
12404   }
12405
12406   if (game.lenses_time_left > 0)
12407   {
12408     game.lenses_time_left--;
12409
12410     if (game.lenses_time_left == 0)
12411       RedrawAllInvisibleElementsForLenses();
12412   }
12413
12414   if (game.magnify_time_left > 0)
12415   {
12416     game.magnify_time_left--;
12417
12418     if (game.magnify_time_left == 0)
12419       RedrawAllInvisibleElementsForMagnifier();
12420   }
12421
12422   for (i = 0; i < MAX_PLAYERS; i++)
12423   {
12424     struct PlayerInfo *player = &stored_player[i];
12425
12426     if (SHIELD_ON(player))
12427     {
12428       if (player->shield_deadly_time_left)
12429         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12430       else if (player->shield_normal_time_left)
12431         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12432     }
12433   }
12434
12435 #if USE_DELAYED_GFX_REDRAW
12436   SCAN_PLAYFIELD(x, y)
12437   {
12438     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12439     {
12440       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12441          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12442
12443       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12444         DrawLevelField(x, y);
12445
12446       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12447         DrawLevelFieldCrumbled(x, y);
12448
12449       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12450         DrawLevelFieldCrumbledNeighbours(x, y);
12451
12452       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12453         DrawTwinkleOnField(x, y);
12454     }
12455
12456     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12457   }
12458 #endif
12459
12460   DrawAllPlayers();
12461   PlayAllPlayersSound();
12462
12463   for (i = 0; i < MAX_PLAYERS; i++)
12464   {
12465     struct PlayerInfo *player = &stored_player[i];
12466
12467     if (player->show_envelope != 0 && (!player->active ||
12468                                        player->MovPos == 0))
12469     {
12470       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12471
12472       player->show_envelope = 0;
12473     }
12474   }
12475
12476   // use random number generator in every frame to make it less predictable
12477   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12478     RND(1);
12479
12480   mouse_action_last = mouse_action;
12481 }
12482
12483 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12484 {
12485   int min_x = x, min_y = y, max_x = x, max_y = y;
12486   int scr_fieldx = getScreenFieldSizeX();
12487   int scr_fieldy = getScreenFieldSizeY();
12488   int i;
12489
12490   for (i = 0; i < MAX_PLAYERS; i++)
12491   {
12492     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12493
12494     if (!stored_player[i].active || &stored_player[i] == player)
12495       continue;
12496
12497     min_x = MIN(min_x, jx);
12498     min_y = MIN(min_y, jy);
12499     max_x = MAX(max_x, jx);
12500     max_y = MAX(max_y, jy);
12501   }
12502
12503   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12504 }
12505
12506 static boolean AllPlayersInVisibleScreen(void)
12507 {
12508   int i;
12509
12510   for (i = 0; i < MAX_PLAYERS; i++)
12511   {
12512     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12513
12514     if (!stored_player[i].active)
12515       continue;
12516
12517     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12518       return FALSE;
12519   }
12520
12521   return TRUE;
12522 }
12523
12524 void ScrollLevel(int dx, int dy)
12525 {
12526   int scroll_offset = 2 * TILEX_VAR;
12527   int x, y;
12528
12529   BlitBitmap(drawto_field, drawto_field,
12530              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12531              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12532              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12533              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12534              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12535              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12536
12537   if (dx != 0)
12538   {
12539     x = (dx == 1 ? BX1 : BX2);
12540     for (y = BY1; y <= BY2; y++)
12541       DrawScreenField(x, y);
12542   }
12543
12544   if (dy != 0)
12545   {
12546     y = (dy == 1 ? BY1 : BY2);
12547     for (x = BX1; x <= BX2; x++)
12548       DrawScreenField(x, y);
12549   }
12550
12551   redraw_mask |= REDRAW_FIELD;
12552 }
12553
12554 static boolean canFallDown(struct PlayerInfo *player)
12555 {
12556   int jx = player->jx, jy = player->jy;
12557
12558   return (IN_LEV_FIELD(jx, jy + 1) &&
12559           (IS_FREE(jx, jy + 1) ||
12560            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12561           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12562           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12563 }
12564
12565 static boolean canPassField(int x, int y, int move_dir)
12566 {
12567   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12568   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12569   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12570   int nextx = x + dx;
12571   int nexty = y + dy;
12572   int element = Tile[x][y];
12573
12574   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12575           !CAN_MOVE(element) &&
12576           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12577           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12578           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12579 }
12580
12581 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12582 {
12583   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12584   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12585   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12586   int newx = x + dx;
12587   int newy = y + dy;
12588
12589   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12590           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12591           (IS_DIGGABLE(Tile[newx][newy]) ||
12592            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12593            canPassField(newx, newy, move_dir)));
12594 }
12595
12596 static void CheckGravityMovement(struct PlayerInfo *player)
12597 {
12598   if (player->gravity && !player->programmed_action)
12599   {
12600     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12601     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12602     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12603     int jx = player->jx, jy = player->jy;
12604     boolean player_is_moving_to_valid_field =
12605       (!player_is_snapping &&
12606        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12607         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12608     boolean player_can_fall_down = canFallDown(player);
12609
12610     if (player_can_fall_down &&
12611         !player_is_moving_to_valid_field)
12612       player->programmed_action = MV_DOWN;
12613   }
12614 }
12615
12616 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12617 {
12618   return CheckGravityMovement(player);
12619
12620   if (player->gravity && !player->programmed_action)
12621   {
12622     int jx = player->jx, jy = player->jy;
12623     boolean field_under_player_is_free =
12624       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12625     boolean player_is_standing_on_valid_field =
12626       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12627        (IS_WALKABLE(Tile[jx][jy]) &&
12628         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12629
12630     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12631       player->programmed_action = MV_DOWN;
12632   }
12633 }
12634
12635 /*
12636   MovePlayerOneStep()
12637   -----------------------------------------------------------------------------
12638   dx, dy:               direction (non-diagonal) to try to move the player to
12639   real_dx, real_dy:     direction as read from input device (can be diagonal)
12640 */
12641
12642 boolean MovePlayerOneStep(struct PlayerInfo *player,
12643                           int dx, int dy, int real_dx, int real_dy)
12644 {
12645   int jx = player->jx, jy = player->jy;
12646   int new_jx = jx + dx, new_jy = jy + dy;
12647   int can_move;
12648   boolean player_can_move = !player->cannot_move;
12649
12650   if (!player->active || (!dx && !dy))
12651     return MP_NO_ACTION;
12652
12653   player->MovDir = (dx < 0 ? MV_LEFT :
12654                     dx > 0 ? MV_RIGHT :
12655                     dy < 0 ? MV_UP :
12656                     dy > 0 ? MV_DOWN :  MV_NONE);
12657
12658   if (!IN_LEV_FIELD(new_jx, new_jy))
12659     return MP_NO_ACTION;
12660
12661   if (!player_can_move)
12662   {
12663     if (player->MovPos == 0)
12664     {
12665       player->is_moving = FALSE;
12666       player->is_digging = FALSE;
12667       player->is_collecting = FALSE;
12668       player->is_snapping = FALSE;
12669       player->is_pushing = FALSE;
12670     }
12671   }
12672
12673   if (!network.enabled && game.centered_player_nr == -1 &&
12674       !AllPlayersInSight(player, new_jx, new_jy))
12675     return MP_NO_ACTION;
12676
12677   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12678   if (can_move != MP_MOVING)
12679     return can_move;
12680
12681   // check if DigField() has caused relocation of the player
12682   if (player->jx != jx || player->jy != jy)
12683     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12684
12685   StorePlayer[jx][jy] = 0;
12686   player->last_jx = jx;
12687   player->last_jy = jy;
12688   player->jx = new_jx;
12689   player->jy = new_jy;
12690   StorePlayer[new_jx][new_jy] = player->element_nr;
12691
12692   if (player->move_delay_value_next != -1)
12693   {
12694     player->move_delay_value = player->move_delay_value_next;
12695     player->move_delay_value_next = -1;
12696   }
12697
12698   player->MovPos =
12699     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12700
12701   player->step_counter++;
12702
12703   PlayerVisit[jx][jy] = FrameCounter;
12704
12705   player->is_moving = TRUE;
12706
12707 #if 1
12708   // should better be called in MovePlayer(), but this breaks some tapes
12709   ScrollPlayer(player, SCROLL_INIT);
12710 #endif
12711
12712   return MP_MOVING;
12713 }
12714
12715 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12716 {
12717   int jx = player->jx, jy = player->jy;
12718   int old_jx = jx, old_jy = jy;
12719   int moved = MP_NO_ACTION;
12720
12721   if (!player->active)
12722     return FALSE;
12723
12724   if (!dx && !dy)
12725   {
12726     if (player->MovPos == 0)
12727     {
12728       player->is_moving = FALSE;
12729       player->is_digging = FALSE;
12730       player->is_collecting = FALSE;
12731       player->is_snapping = FALSE;
12732       player->is_pushing = FALSE;
12733     }
12734
12735     return FALSE;
12736   }
12737
12738   if (player->move_delay > 0)
12739     return FALSE;
12740
12741   player->move_delay = -1;              // set to "uninitialized" value
12742
12743   // store if player is automatically moved to next field
12744   player->is_auto_moving = (player->programmed_action != MV_NONE);
12745
12746   // remove the last programmed player action
12747   player->programmed_action = 0;
12748
12749   if (player->MovPos)
12750   {
12751     // should only happen if pre-1.2 tape recordings are played
12752     // this is only for backward compatibility
12753
12754     int original_move_delay_value = player->move_delay_value;
12755
12756 #if DEBUG
12757     Debug("game:playing:MovePlayer",
12758           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12759           tape.counter);
12760 #endif
12761
12762     // scroll remaining steps with finest movement resolution
12763     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12764
12765     while (player->MovPos)
12766     {
12767       ScrollPlayer(player, SCROLL_GO_ON);
12768       ScrollScreen(NULL, SCROLL_GO_ON);
12769
12770       AdvanceFrameAndPlayerCounters(player->index_nr);
12771
12772       DrawAllPlayers();
12773       BackToFront_WithFrameDelay(0);
12774     }
12775
12776     player->move_delay_value = original_move_delay_value;
12777   }
12778
12779   player->is_active = FALSE;
12780
12781   if (player->last_move_dir & MV_HORIZONTAL)
12782   {
12783     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12784       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12785   }
12786   else
12787   {
12788     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12789       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12790   }
12791
12792   if (!moved && !player->is_active)
12793   {
12794     player->is_moving = FALSE;
12795     player->is_digging = FALSE;
12796     player->is_collecting = FALSE;
12797     player->is_snapping = FALSE;
12798     player->is_pushing = FALSE;
12799   }
12800
12801   jx = player->jx;
12802   jy = player->jy;
12803
12804   if (moved & MP_MOVING && !ScreenMovPos &&
12805       (player->index_nr == game.centered_player_nr ||
12806        game.centered_player_nr == -1))
12807   {
12808     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12809
12810     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12811     {
12812       // actual player has left the screen -- scroll in that direction
12813       if (jx != old_jx)         // player has moved horizontally
12814         scroll_x += (jx - old_jx);
12815       else                      // player has moved vertically
12816         scroll_y += (jy - old_jy);
12817     }
12818     else
12819     {
12820       int offset_raw = game.scroll_delay_value;
12821
12822       if (jx != old_jx)         // player has moved horizontally
12823       {
12824         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12825         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12826         int new_scroll_x = jx - MIDPOSX + offset_x;
12827
12828         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12829             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12830           scroll_x = new_scroll_x;
12831
12832         // don't scroll over playfield boundaries
12833         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12834
12835         // don't scroll more than one field at a time
12836         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12837
12838         // don't scroll against the player's moving direction
12839         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12840             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12841           scroll_x = old_scroll_x;
12842       }
12843       else                      // player has moved vertically
12844       {
12845         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12846         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12847         int new_scroll_y = jy - MIDPOSY + offset_y;
12848
12849         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12850             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12851           scroll_y = new_scroll_y;
12852
12853         // don't scroll over playfield boundaries
12854         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12855
12856         // don't scroll more than one field at a time
12857         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12858
12859         // don't scroll against the player's moving direction
12860         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12861             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12862           scroll_y = old_scroll_y;
12863       }
12864     }
12865
12866     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12867     {
12868       if (!network.enabled && game.centered_player_nr == -1 &&
12869           !AllPlayersInVisibleScreen())
12870       {
12871         scroll_x = old_scroll_x;
12872         scroll_y = old_scroll_y;
12873       }
12874       else
12875       {
12876         ScrollScreen(player, SCROLL_INIT);
12877         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12878       }
12879     }
12880   }
12881
12882   player->StepFrame = 0;
12883
12884   if (moved & MP_MOVING)
12885   {
12886     if (old_jx != jx && old_jy == jy)
12887       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12888     else if (old_jx == jx && old_jy != jy)
12889       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12890
12891     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12892
12893     player->last_move_dir = player->MovDir;
12894     player->is_moving = TRUE;
12895     player->is_snapping = FALSE;
12896     player->is_switching = FALSE;
12897     player->is_dropping = FALSE;
12898     player->is_dropping_pressed = FALSE;
12899     player->drop_pressed_delay = 0;
12900
12901 #if 0
12902     // should better be called here than above, but this breaks some tapes
12903     ScrollPlayer(player, SCROLL_INIT);
12904 #endif
12905   }
12906   else
12907   {
12908     CheckGravityMovementWhenNotMoving(player);
12909
12910     player->is_moving = FALSE;
12911
12912     /* at this point, the player is allowed to move, but cannot move right now
12913        (e.g. because of something blocking the way) -- ensure that the player
12914        is also allowed to move in the next frame (in old versions before 3.1.1,
12915        the player was forced to wait again for eight frames before next try) */
12916
12917     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12918       player->move_delay = 0;   // allow direct movement in the next frame
12919   }
12920
12921   if (player->move_delay == -1)         // not yet initialized by DigField()
12922     player->move_delay = player->move_delay_value;
12923
12924   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12925   {
12926     TestIfPlayerTouchesBadThing(jx, jy);
12927     TestIfPlayerTouchesCustomElement(jx, jy);
12928   }
12929
12930   if (!player->active)
12931     RemovePlayer(player);
12932
12933   return moved;
12934 }
12935
12936 void ScrollPlayer(struct PlayerInfo *player, int mode)
12937 {
12938   int jx = player->jx, jy = player->jy;
12939   int last_jx = player->last_jx, last_jy = player->last_jy;
12940   int move_stepsize = TILEX / player->move_delay_value;
12941
12942   if (!player->active)
12943     return;
12944
12945   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12946     return;
12947
12948   if (mode == SCROLL_INIT)
12949   {
12950     player->actual_frame_counter = FrameCounter;
12951     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12952
12953     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12954         Tile[last_jx][last_jy] == EL_EMPTY)
12955     {
12956       int last_field_block_delay = 0;   // start with no blocking at all
12957       int block_delay_adjustment = player->block_delay_adjustment;
12958
12959       // if player blocks last field, add delay for exactly one move
12960       if (player->block_last_field)
12961       {
12962         last_field_block_delay += player->move_delay_value;
12963
12964         // when blocking enabled, prevent moving up despite gravity
12965         if (player->gravity && player->MovDir == MV_UP)
12966           block_delay_adjustment = -1;
12967       }
12968
12969       // add block delay adjustment (also possible when not blocking)
12970       last_field_block_delay += block_delay_adjustment;
12971
12972       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12973       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12974     }
12975
12976     if (player->MovPos != 0)    // player has not yet reached destination
12977       return;
12978   }
12979   else if (!FrameReached(&player->actual_frame_counter, 1))
12980     return;
12981
12982   if (player->MovPos != 0)
12983   {
12984     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12985     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12986
12987     // before DrawPlayer() to draw correct player graphic for this case
12988     if (player->MovPos == 0)
12989       CheckGravityMovement(player);
12990   }
12991
12992   if (player->MovPos == 0)      // player reached destination field
12993   {
12994     if (player->move_delay_reset_counter > 0)
12995     {
12996       player->move_delay_reset_counter--;
12997
12998       if (player->move_delay_reset_counter == 0)
12999       {
13000         // continue with normal speed after quickly moving through gate
13001         HALVE_PLAYER_SPEED(player);
13002
13003         // be able to make the next move without delay
13004         player->move_delay = 0;
13005       }
13006     }
13007
13008     player->last_jx = jx;
13009     player->last_jy = jy;
13010
13011     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13012         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13013         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13014         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13015         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13016         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13017         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13018         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13019     {
13020       ExitPlayer(player);
13021
13022       if (game.players_still_needed == 0 &&
13023           (game.friends_still_needed == 0 ||
13024            IS_SP_ELEMENT(Tile[jx][jy])))
13025         LevelSolved();
13026     }
13027
13028     // this breaks one level: "machine", level 000
13029     {
13030       int move_direction = player->MovDir;
13031       int enter_side = MV_DIR_OPPOSITE(move_direction);
13032       int leave_side = move_direction;
13033       int old_jx = last_jx;
13034       int old_jy = last_jy;
13035       int old_element = Tile[old_jx][old_jy];
13036       int new_element = Tile[jx][jy];
13037
13038       if (IS_CUSTOM_ELEMENT(old_element))
13039         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13040                                    CE_LEFT_BY_PLAYER,
13041                                    player->index_bit, leave_side);
13042
13043       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13044                                           CE_PLAYER_LEAVES_X,
13045                                           player->index_bit, leave_side);
13046
13047       if (IS_CUSTOM_ELEMENT(new_element))
13048         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13049                                    player->index_bit, enter_side);
13050
13051       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13052                                           CE_PLAYER_ENTERS_X,
13053                                           player->index_bit, enter_side);
13054
13055       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13056                                         CE_MOVE_OF_X, move_direction);
13057     }
13058
13059     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13060     {
13061       TestIfPlayerTouchesBadThing(jx, jy);
13062       TestIfPlayerTouchesCustomElement(jx, jy);
13063
13064       /* needed because pushed element has not yet reached its destination,
13065          so it would trigger a change event at its previous field location */
13066       if (!player->is_pushing)
13067         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13068
13069       if (level.finish_dig_collect &&
13070           (player->is_digging || player->is_collecting))
13071       {
13072         int last_element = player->last_removed_element;
13073         int move_direction = player->MovDir;
13074         int enter_side = MV_DIR_OPPOSITE(move_direction);
13075         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13076                             CE_PLAYER_COLLECTS_X);
13077
13078         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13079                                             player->index_bit, enter_side);
13080
13081         player->last_removed_element = EL_UNDEFINED;
13082       }
13083
13084       if (!player->active)
13085         RemovePlayer(player);
13086     }
13087
13088     if (!game.LevelSolved && level.use_step_counter)
13089     {
13090       int i;
13091
13092       TimePlayed++;
13093
13094       if (TimeLeft > 0)
13095       {
13096         TimeLeft--;
13097
13098         if (TimeLeft <= 10 && setup.time_limit)
13099           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13100
13101         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13102
13103         DisplayGameControlValues();
13104
13105         if (!TimeLeft && setup.time_limit)
13106           for (i = 0; i < MAX_PLAYERS; i++)
13107             KillPlayer(&stored_player[i]);
13108       }
13109       else if (game.no_time_limit && !game.all_players_gone)
13110       {
13111         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13112
13113         DisplayGameControlValues();
13114       }
13115     }
13116
13117     if (tape.single_step && tape.recording && !tape.pausing &&
13118         !player->programmed_action)
13119       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13120
13121     if (!player->programmed_action)
13122       CheckSaveEngineSnapshot(player);
13123   }
13124 }
13125
13126 void ScrollScreen(struct PlayerInfo *player, int mode)
13127 {
13128   static unsigned int screen_frame_counter = 0;
13129
13130   if (mode == SCROLL_INIT)
13131   {
13132     // set scrolling step size according to actual player's moving speed
13133     ScrollStepSize = TILEX / player->move_delay_value;
13134
13135     screen_frame_counter = FrameCounter;
13136     ScreenMovDir = player->MovDir;
13137     ScreenMovPos = player->MovPos;
13138     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13139     return;
13140   }
13141   else if (!FrameReached(&screen_frame_counter, 1))
13142     return;
13143
13144   if (ScreenMovPos)
13145   {
13146     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13147     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13148     redraw_mask |= REDRAW_FIELD;
13149   }
13150   else
13151     ScreenMovDir = MV_NONE;
13152 }
13153
13154 void TestIfPlayerTouchesCustomElement(int x, int y)
13155 {
13156   static int xy[4][2] =
13157   {
13158     { 0, -1 },
13159     { -1, 0 },
13160     { +1, 0 },
13161     { 0, +1 }
13162   };
13163   static int trigger_sides[4][2] =
13164   {
13165     // center side       border side
13166     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13167     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13168     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13169     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13170   };
13171   static int touch_dir[4] =
13172   {
13173     MV_LEFT | MV_RIGHT,
13174     MV_UP   | MV_DOWN,
13175     MV_UP   | MV_DOWN,
13176     MV_LEFT | MV_RIGHT
13177   };
13178   int center_element = Tile[x][y];      // should always be non-moving!
13179   int i;
13180
13181   for (i = 0; i < NUM_DIRECTIONS; i++)
13182   {
13183     int xx = x + xy[i][0];
13184     int yy = y + xy[i][1];
13185     int center_side = trigger_sides[i][0];
13186     int border_side = trigger_sides[i][1];
13187     int border_element;
13188
13189     if (!IN_LEV_FIELD(xx, yy))
13190       continue;
13191
13192     if (IS_PLAYER(x, y))                // player found at center element
13193     {
13194       struct PlayerInfo *player = PLAYERINFO(x, y);
13195
13196       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13197         border_element = Tile[xx][yy];          // may be moving!
13198       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13199         border_element = Tile[xx][yy];
13200       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13201         border_element = MovingOrBlocked2Element(xx, yy);
13202       else
13203         continue;               // center and border element do not touch
13204
13205       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13206                                  player->index_bit, border_side);
13207       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13208                                           CE_PLAYER_TOUCHES_X,
13209                                           player->index_bit, border_side);
13210
13211       {
13212         /* use player element that is initially defined in the level playfield,
13213            not the player element that corresponds to the runtime player number
13214            (example: a level that contains EL_PLAYER_3 as the only player would
13215            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13216         int player_element = PLAYERINFO(x, y)->initial_element;
13217
13218         CheckElementChangeBySide(xx, yy, border_element, player_element,
13219                                  CE_TOUCHING_X, border_side);
13220       }
13221     }
13222     else if (IS_PLAYER(xx, yy))         // player found at border element
13223     {
13224       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13225
13226       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13227       {
13228         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13229           continue;             // center and border element do not touch
13230       }
13231
13232       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13233                                  player->index_bit, center_side);
13234       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13235                                           CE_PLAYER_TOUCHES_X,
13236                                           player->index_bit, center_side);
13237
13238       {
13239         /* use player element that is initially defined in the level playfield,
13240            not the player element that corresponds to the runtime player number
13241            (example: a level that contains EL_PLAYER_3 as the only player would
13242            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13243         int player_element = PLAYERINFO(xx, yy)->initial_element;
13244
13245         CheckElementChangeBySide(x, y, center_element, player_element,
13246                                  CE_TOUCHING_X, center_side);
13247       }
13248
13249       break;
13250     }
13251   }
13252 }
13253
13254 void TestIfElementTouchesCustomElement(int x, int y)
13255 {
13256   static int xy[4][2] =
13257   {
13258     { 0, -1 },
13259     { -1, 0 },
13260     { +1, 0 },
13261     { 0, +1 }
13262   };
13263   static int trigger_sides[4][2] =
13264   {
13265     // center side      border side
13266     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13267     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13268     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13269     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13270   };
13271   static int touch_dir[4] =
13272   {
13273     MV_LEFT | MV_RIGHT,
13274     MV_UP   | MV_DOWN,
13275     MV_UP   | MV_DOWN,
13276     MV_LEFT | MV_RIGHT
13277   };
13278   boolean change_center_element = FALSE;
13279   int center_element = Tile[x][y];      // should always be non-moving!
13280   int border_element_old[NUM_DIRECTIONS];
13281   int i;
13282
13283   for (i = 0; i < NUM_DIRECTIONS; i++)
13284   {
13285     int xx = x + xy[i][0];
13286     int yy = y + xy[i][1];
13287     int border_element;
13288
13289     border_element_old[i] = -1;
13290
13291     if (!IN_LEV_FIELD(xx, yy))
13292       continue;
13293
13294     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13295       border_element = Tile[xx][yy];    // may be moving!
13296     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13297       border_element = Tile[xx][yy];
13298     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13299       border_element = MovingOrBlocked2Element(xx, yy);
13300     else
13301       continue;                 // center and border element do not touch
13302
13303     border_element_old[i] = border_element;
13304   }
13305
13306   for (i = 0; i < NUM_DIRECTIONS; i++)
13307   {
13308     int xx = x + xy[i][0];
13309     int yy = y + xy[i][1];
13310     int center_side = trigger_sides[i][0];
13311     int border_element = border_element_old[i];
13312
13313     if (border_element == -1)
13314       continue;
13315
13316     // check for change of border element
13317     CheckElementChangeBySide(xx, yy, border_element, center_element,
13318                              CE_TOUCHING_X, center_side);
13319
13320     // (center element cannot be player, so we dont have to check this here)
13321   }
13322
13323   for (i = 0; i < NUM_DIRECTIONS; i++)
13324   {
13325     int xx = x + xy[i][0];
13326     int yy = y + xy[i][1];
13327     int border_side = trigger_sides[i][1];
13328     int border_element = border_element_old[i];
13329
13330     if (border_element == -1)
13331       continue;
13332
13333     // check for change of center element (but change it only once)
13334     if (!change_center_element)
13335       change_center_element =
13336         CheckElementChangeBySide(x, y, center_element, border_element,
13337                                  CE_TOUCHING_X, border_side);
13338
13339     if (IS_PLAYER(xx, yy))
13340     {
13341       /* use player element that is initially defined in the level playfield,
13342          not the player element that corresponds to the runtime player number
13343          (example: a level that contains EL_PLAYER_3 as the only player would
13344          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13345       int player_element = PLAYERINFO(xx, yy)->initial_element;
13346
13347       CheckElementChangeBySide(x, y, center_element, player_element,
13348                                CE_TOUCHING_X, border_side);
13349     }
13350   }
13351 }
13352
13353 void TestIfElementHitsCustomElement(int x, int y, int direction)
13354 {
13355   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13356   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13357   int hitx = x + dx, hity = y + dy;
13358   int hitting_element = Tile[x][y];
13359   int touched_element;
13360
13361   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13362     return;
13363
13364   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13365                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13366
13367   if (IN_LEV_FIELD(hitx, hity))
13368   {
13369     int opposite_direction = MV_DIR_OPPOSITE(direction);
13370     int hitting_side = direction;
13371     int touched_side = opposite_direction;
13372     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13373                           MovDir[hitx][hity] != direction ||
13374                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13375
13376     object_hit = TRUE;
13377
13378     if (object_hit)
13379     {
13380       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13381                                CE_HITTING_X, touched_side);
13382
13383       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13384                                CE_HIT_BY_X, hitting_side);
13385
13386       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13387                                CE_HIT_BY_SOMETHING, opposite_direction);
13388
13389       if (IS_PLAYER(hitx, hity))
13390       {
13391         /* use player element that is initially defined in the level playfield,
13392            not the player element that corresponds to the runtime player number
13393            (example: a level that contains EL_PLAYER_3 as the only player would
13394            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13395         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13396
13397         CheckElementChangeBySide(x, y, hitting_element, player_element,
13398                                  CE_HITTING_X, touched_side);
13399       }
13400     }
13401   }
13402
13403   // "hitting something" is also true when hitting the playfield border
13404   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13405                            CE_HITTING_SOMETHING, direction);
13406 }
13407
13408 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13409 {
13410   int i, kill_x = -1, kill_y = -1;
13411
13412   int bad_element = -1;
13413   static int test_xy[4][2] =
13414   {
13415     { 0, -1 },
13416     { -1, 0 },
13417     { +1, 0 },
13418     { 0, +1 }
13419   };
13420   static int test_dir[4] =
13421   {
13422     MV_UP,
13423     MV_LEFT,
13424     MV_RIGHT,
13425     MV_DOWN
13426   };
13427
13428   for (i = 0; i < NUM_DIRECTIONS; i++)
13429   {
13430     int test_x, test_y, test_move_dir, test_element;
13431
13432     test_x = good_x + test_xy[i][0];
13433     test_y = good_y + test_xy[i][1];
13434
13435     if (!IN_LEV_FIELD(test_x, test_y))
13436       continue;
13437
13438     test_move_dir =
13439       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13440
13441     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13442
13443     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13444        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13445     */
13446     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13447         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13448     {
13449       kill_x = test_x;
13450       kill_y = test_y;
13451       bad_element = test_element;
13452
13453       break;
13454     }
13455   }
13456
13457   if (kill_x != -1 || kill_y != -1)
13458   {
13459     if (IS_PLAYER(good_x, good_y))
13460     {
13461       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13462
13463       if (player->shield_deadly_time_left > 0 &&
13464           !IS_INDESTRUCTIBLE(bad_element))
13465         Bang(kill_x, kill_y);
13466       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13467         KillPlayer(player);
13468     }
13469     else
13470       Bang(good_x, good_y);
13471   }
13472 }
13473
13474 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13475 {
13476   int i, kill_x = -1, kill_y = -1;
13477   int bad_element = Tile[bad_x][bad_y];
13478   static int test_xy[4][2] =
13479   {
13480     { 0, -1 },
13481     { -1, 0 },
13482     { +1, 0 },
13483     { 0, +1 }
13484   };
13485   static int touch_dir[4] =
13486   {
13487     MV_LEFT | MV_RIGHT,
13488     MV_UP   | MV_DOWN,
13489     MV_UP   | MV_DOWN,
13490     MV_LEFT | MV_RIGHT
13491   };
13492   static int test_dir[4] =
13493   {
13494     MV_UP,
13495     MV_LEFT,
13496     MV_RIGHT,
13497     MV_DOWN
13498   };
13499
13500   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13501     return;
13502
13503   for (i = 0; i < NUM_DIRECTIONS; i++)
13504   {
13505     int test_x, test_y, test_move_dir, test_element;
13506
13507     test_x = bad_x + test_xy[i][0];
13508     test_y = bad_y + test_xy[i][1];
13509
13510     if (!IN_LEV_FIELD(test_x, test_y))
13511       continue;
13512
13513     test_move_dir =
13514       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13515
13516     test_element = Tile[test_x][test_y];
13517
13518     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13519        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13520     */
13521     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13522         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13523     {
13524       // good thing is player or penguin that does not move away
13525       if (IS_PLAYER(test_x, test_y))
13526       {
13527         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13528
13529         if (bad_element == EL_ROBOT && player->is_moving)
13530           continue;     // robot does not kill player if he is moving
13531
13532         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13533         {
13534           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13535             continue;           // center and border element do not touch
13536         }
13537
13538         kill_x = test_x;
13539         kill_y = test_y;
13540
13541         break;
13542       }
13543       else if (test_element == EL_PENGUIN)
13544       {
13545         kill_x = test_x;
13546         kill_y = test_y;
13547
13548         break;
13549       }
13550     }
13551   }
13552
13553   if (kill_x != -1 || kill_y != -1)
13554   {
13555     if (IS_PLAYER(kill_x, kill_y))
13556     {
13557       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13558
13559       if (player->shield_deadly_time_left > 0 &&
13560           !IS_INDESTRUCTIBLE(bad_element))
13561         Bang(bad_x, bad_y);
13562       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13563         KillPlayer(player);
13564     }
13565     else
13566       Bang(kill_x, kill_y);
13567   }
13568 }
13569
13570 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13571 {
13572   int bad_element = Tile[bad_x][bad_y];
13573   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13574   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13575   int test_x = bad_x + dx, test_y = bad_y + dy;
13576   int test_move_dir, test_element;
13577   int kill_x = -1, kill_y = -1;
13578
13579   if (!IN_LEV_FIELD(test_x, test_y))
13580     return;
13581
13582   test_move_dir =
13583     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13584
13585   test_element = Tile[test_x][test_y];
13586
13587   if (test_move_dir != bad_move_dir)
13588   {
13589     // good thing can be player or penguin that does not move away
13590     if (IS_PLAYER(test_x, test_y))
13591     {
13592       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13593
13594       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13595          player as being hit when he is moving towards the bad thing, because
13596          the "get hit by" condition would be lost after the player stops) */
13597       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13598         return;         // player moves away from bad thing
13599
13600       kill_x = test_x;
13601       kill_y = test_y;
13602     }
13603     else if (test_element == EL_PENGUIN)
13604     {
13605       kill_x = test_x;
13606       kill_y = test_y;
13607     }
13608   }
13609
13610   if (kill_x != -1 || kill_y != -1)
13611   {
13612     if (IS_PLAYER(kill_x, kill_y))
13613     {
13614       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13615
13616       if (player->shield_deadly_time_left > 0 &&
13617           !IS_INDESTRUCTIBLE(bad_element))
13618         Bang(bad_x, bad_y);
13619       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13620         KillPlayer(player);
13621     }
13622     else
13623       Bang(kill_x, kill_y);
13624   }
13625 }
13626
13627 void TestIfPlayerTouchesBadThing(int x, int y)
13628 {
13629   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13630 }
13631
13632 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13633 {
13634   TestIfGoodThingHitsBadThing(x, y, move_dir);
13635 }
13636
13637 void TestIfBadThingTouchesPlayer(int x, int y)
13638 {
13639   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13640 }
13641
13642 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13643 {
13644   TestIfBadThingHitsGoodThing(x, y, move_dir);
13645 }
13646
13647 void TestIfFriendTouchesBadThing(int x, int y)
13648 {
13649   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13650 }
13651
13652 void TestIfBadThingTouchesFriend(int x, int y)
13653 {
13654   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13655 }
13656
13657 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13658 {
13659   int i, kill_x = bad_x, kill_y = bad_y;
13660   static int xy[4][2] =
13661   {
13662     { 0, -1 },
13663     { -1, 0 },
13664     { +1, 0 },
13665     { 0, +1 }
13666   };
13667
13668   for (i = 0; i < NUM_DIRECTIONS; i++)
13669   {
13670     int x, y, element;
13671
13672     x = bad_x + xy[i][0];
13673     y = bad_y + xy[i][1];
13674     if (!IN_LEV_FIELD(x, y))
13675       continue;
13676
13677     element = Tile[x][y];
13678     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13679         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13680     {
13681       kill_x = x;
13682       kill_y = y;
13683       break;
13684     }
13685   }
13686
13687   if (kill_x != bad_x || kill_y != bad_y)
13688     Bang(bad_x, bad_y);
13689 }
13690
13691 void KillPlayer(struct PlayerInfo *player)
13692 {
13693   int jx = player->jx, jy = player->jy;
13694
13695   if (!player->active)
13696     return;
13697
13698 #if 0
13699   Debug("game:playing:KillPlayer",
13700         "0: killed == %d, active == %d, reanimated == %d",
13701         player->killed, player->active, player->reanimated);
13702 #endif
13703
13704   /* the following code was introduced to prevent an infinite loop when calling
13705      -> Bang()
13706      -> CheckTriggeredElementChangeExt()
13707      -> ExecuteCustomElementAction()
13708      -> KillPlayer()
13709      -> (infinitely repeating the above sequence of function calls)
13710      which occurs when killing the player while having a CE with the setting
13711      "kill player X when explosion of <player X>"; the solution using a new
13712      field "player->killed" was chosen for backwards compatibility, although
13713      clever use of the fields "player->active" etc. would probably also work */
13714 #if 1
13715   if (player->killed)
13716     return;
13717 #endif
13718
13719   player->killed = TRUE;
13720
13721   // remove accessible field at the player's position
13722   Tile[jx][jy] = EL_EMPTY;
13723
13724   // deactivate shield (else Bang()/Explode() would not work right)
13725   player->shield_normal_time_left = 0;
13726   player->shield_deadly_time_left = 0;
13727
13728 #if 0
13729   Debug("game:playing:KillPlayer",
13730         "1: killed == %d, active == %d, reanimated == %d",
13731         player->killed, player->active, player->reanimated);
13732 #endif
13733
13734   Bang(jx, jy);
13735
13736 #if 0
13737   Debug("game:playing:KillPlayer",
13738         "2: killed == %d, active == %d, reanimated == %d",
13739         player->killed, player->active, player->reanimated);
13740 #endif
13741
13742   if (player->reanimated)       // killed player may have been reanimated
13743     player->killed = player->reanimated = FALSE;
13744   else
13745     BuryPlayer(player);
13746 }
13747
13748 static void KillPlayerUnlessEnemyProtected(int x, int y)
13749 {
13750   if (!PLAYER_ENEMY_PROTECTED(x, y))
13751     KillPlayer(PLAYERINFO(x, y));
13752 }
13753
13754 static void KillPlayerUnlessExplosionProtected(int x, int y)
13755 {
13756   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13757     KillPlayer(PLAYERINFO(x, y));
13758 }
13759
13760 void BuryPlayer(struct PlayerInfo *player)
13761 {
13762   int jx = player->jx, jy = player->jy;
13763
13764   if (!player->active)
13765     return;
13766
13767   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13768   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13769
13770   RemovePlayer(player);
13771
13772   player->buried = TRUE;
13773
13774   if (game.all_players_gone)
13775     game.GameOver = TRUE;
13776 }
13777
13778 void RemovePlayer(struct PlayerInfo *player)
13779 {
13780   int jx = player->jx, jy = player->jy;
13781   int i, found = FALSE;
13782
13783   player->present = FALSE;
13784   player->active = FALSE;
13785
13786   // required for some CE actions (even if the player is not active anymore)
13787   player->MovPos = 0;
13788
13789   if (!ExplodeField[jx][jy])
13790     StorePlayer[jx][jy] = 0;
13791
13792   if (player->is_moving)
13793     TEST_DrawLevelField(player->last_jx, player->last_jy);
13794
13795   for (i = 0; i < MAX_PLAYERS; i++)
13796     if (stored_player[i].active)
13797       found = TRUE;
13798
13799   if (!found)
13800   {
13801     game.all_players_gone = TRUE;
13802     game.GameOver = TRUE;
13803   }
13804
13805   game.exit_x = game.robot_wheel_x = jx;
13806   game.exit_y = game.robot_wheel_y = jy;
13807 }
13808
13809 void ExitPlayer(struct PlayerInfo *player)
13810 {
13811   DrawPlayer(player);   // needed here only to cleanup last field
13812   RemovePlayer(player);
13813
13814   if (game.players_still_needed > 0)
13815     game.players_still_needed--;
13816 }
13817
13818 static void SetFieldForSnapping(int x, int y, int element, int direction,
13819                                 int player_index_bit)
13820 {
13821   struct ElementInfo *ei = &element_info[element];
13822   int direction_bit = MV_DIR_TO_BIT(direction);
13823   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13824   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13825                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13826
13827   Tile[x][y] = EL_ELEMENT_SNAPPING;
13828   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13829   MovDir[x][y] = direction;
13830   Store[x][y] = element;
13831   Store2[x][y] = player_index_bit;
13832
13833   ResetGfxAnimation(x, y);
13834
13835   GfxElement[x][y] = element;
13836   GfxAction[x][y] = action;
13837   GfxDir[x][y] = direction;
13838   GfxFrame[x][y] = -1;
13839 }
13840
13841 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13842                                    int player_index_bit)
13843 {
13844   TestIfElementTouchesCustomElement(x, y);      // for empty space
13845
13846   if (level.finish_dig_collect)
13847   {
13848     int dig_side = MV_DIR_OPPOSITE(direction);
13849
13850     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13851                                         player_index_bit, dig_side);
13852   }
13853 }
13854
13855 /*
13856   =============================================================================
13857   checkDiagonalPushing()
13858   -----------------------------------------------------------------------------
13859   check if diagonal input device direction results in pushing of object
13860   (by checking if the alternative direction is walkable, diggable, ...)
13861   =============================================================================
13862 */
13863
13864 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13865                                     int x, int y, int real_dx, int real_dy)
13866 {
13867   int jx, jy, dx, dy, xx, yy;
13868
13869   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13870     return TRUE;
13871
13872   // diagonal direction: check alternative direction
13873   jx = player->jx;
13874   jy = player->jy;
13875   dx = x - jx;
13876   dy = y - jy;
13877   xx = jx + (dx == 0 ? real_dx : 0);
13878   yy = jy + (dy == 0 ? real_dy : 0);
13879
13880   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13881 }
13882
13883 /*
13884   =============================================================================
13885   DigField()
13886   -----------------------------------------------------------------------------
13887   x, y:                 field next to player (non-diagonal) to try to dig to
13888   real_dx, real_dy:     direction as read from input device (can be diagonal)
13889   =============================================================================
13890 */
13891
13892 static int DigField(struct PlayerInfo *player,
13893                     int oldx, int oldy, int x, int y,
13894                     int real_dx, int real_dy, int mode)
13895 {
13896   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13897   boolean player_was_pushing = player->is_pushing;
13898   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13899   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13900   int jx = oldx, jy = oldy;
13901   int dx = x - jx, dy = y - jy;
13902   int nextx = x + dx, nexty = y + dy;
13903   int move_direction = (dx == -1 ? MV_LEFT  :
13904                         dx == +1 ? MV_RIGHT :
13905                         dy == -1 ? MV_UP    :
13906                         dy == +1 ? MV_DOWN  : MV_NONE);
13907   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13908   int dig_side = MV_DIR_OPPOSITE(move_direction);
13909   int old_element = Tile[jx][jy];
13910   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13911   int collect_count;
13912
13913   if (is_player)                // function can also be called by EL_PENGUIN
13914   {
13915     if (player->MovPos == 0)
13916     {
13917       player->is_digging = FALSE;
13918       player->is_collecting = FALSE;
13919     }
13920
13921     if (player->MovPos == 0)    // last pushing move finished
13922       player->is_pushing = FALSE;
13923
13924     if (mode == DF_NO_PUSH)     // player just stopped pushing
13925     {
13926       player->is_switching = FALSE;
13927       player->push_delay = -1;
13928
13929       return MP_NO_ACTION;
13930     }
13931   }
13932
13933   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13934     old_element = Back[jx][jy];
13935
13936   // in case of element dropped at player position, check background
13937   else if (Back[jx][jy] != EL_EMPTY &&
13938            game.engine_version >= VERSION_IDENT(2,2,0,0))
13939     old_element = Back[jx][jy];
13940
13941   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13942     return MP_NO_ACTION;        // field has no opening in this direction
13943
13944   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13945     return MP_NO_ACTION;        // field has no opening in this direction
13946
13947   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13948   {
13949     SplashAcid(x, y);
13950
13951     Tile[jx][jy] = player->artwork_element;
13952     InitMovingField(jx, jy, MV_DOWN);
13953     Store[jx][jy] = EL_ACID;
13954     ContinueMoving(jx, jy);
13955     BuryPlayer(player);
13956
13957     return MP_DONT_RUN_INTO;
13958   }
13959
13960   if (player_can_move && DONT_RUN_INTO(element))
13961   {
13962     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13963
13964     return MP_DONT_RUN_INTO;
13965   }
13966
13967   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13968     return MP_NO_ACTION;
13969
13970   collect_count = element_info[element].collect_count_initial;
13971
13972   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13973     return MP_NO_ACTION;
13974
13975   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13976     player_can_move = player_can_move_or_snap;
13977
13978   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13979       game.engine_version >= VERSION_IDENT(2,2,0,0))
13980   {
13981     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13982                                player->index_bit, dig_side);
13983     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13984                                         player->index_bit, dig_side);
13985
13986     if (element == EL_DC_LANDMINE)
13987       Bang(x, y);
13988
13989     if (Tile[x][y] != element)          // field changed by snapping
13990       return MP_ACTION;
13991
13992     return MP_NO_ACTION;
13993   }
13994
13995   if (player->gravity && is_player && !player->is_auto_moving &&
13996       canFallDown(player) && move_direction != MV_DOWN &&
13997       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13998     return MP_NO_ACTION;        // player cannot walk here due to gravity
13999
14000   if (player_can_move &&
14001       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14002   {
14003     int sound_element = SND_ELEMENT(element);
14004     int sound_action = ACTION_WALKING;
14005
14006     if (IS_RND_GATE(element))
14007     {
14008       if (!player->key[RND_GATE_NR(element)])
14009         return MP_NO_ACTION;
14010     }
14011     else if (IS_RND_GATE_GRAY(element))
14012     {
14013       if (!player->key[RND_GATE_GRAY_NR(element)])
14014         return MP_NO_ACTION;
14015     }
14016     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14017     {
14018       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14019         return MP_NO_ACTION;
14020     }
14021     else if (element == EL_EXIT_OPEN ||
14022              element == EL_EM_EXIT_OPEN ||
14023              element == EL_EM_EXIT_OPENING ||
14024              element == EL_STEEL_EXIT_OPEN ||
14025              element == EL_EM_STEEL_EXIT_OPEN ||
14026              element == EL_EM_STEEL_EXIT_OPENING ||
14027              element == EL_SP_EXIT_OPEN ||
14028              element == EL_SP_EXIT_OPENING)
14029     {
14030       sound_action = ACTION_PASSING;    // player is passing exit
14031     }
14032     else if (element == EL_EMPTY)
14033     {
14034       sound_action = ACTION_MOVING;             // nothing to walk on
14035     }
14036
14037     // play sound from background or player, whatever is available
14038     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14039       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14040     else
14041       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14042   }
14043   else if (player_can_move &&
14044            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14045   {
14046     if (!ACCESS_FROM(element, opposite_direction))
14047       return MP_NO_ACTION;      // field not accessible from this direction
14048
14049     if (CAN_MOVE(element))      // only fixed elements can be passed!
14050       return MP_NO_ACTION;
14051
14052     if (IS_EM_GATE(element))
14053     {
14054       if (!player->key[EM_GATE_NR(element)])
14055         return MP_NO_ACTION;
14056     }
14057     else if (IS_EM_GATE_GRAY(element))
14058     {
14059       if (!player->key[EM_GATE_GRAY_NR(element)])
14060         return MP_NO_ACTION;
14061     }
14062     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14063     {
14064       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14065         return MP_NO_ACTION;
14066     }
14067     else if (IS_EMC_GATE(element))
14068     {
14069       if (!player->key[EMC_GATE_NR(element)])
14070         return MP_NO_ACTION;
14071     }
14072     else if (IS_EMC_GATE_GRAY(element))
14073     {
14074       if (!player->key[EMC_GATE_GRAY_NR(element)])
14075         return MP_NO_ACTION;
14076     }
14077     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14078     {
14079       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14080         return MP_NO_ACTION;
14081     }
14082     else if (element == EL_DC_GATE_WHITE ||
14083              element == EL_DC_GATE_WHITE_GRAY ||
14084              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14085     {
14086       if (player->num_white_keys == 0)
14087         return MP_NO_ACTION;
14088
14089       player->num_white_keys--;
14090     }
14091     else if (IS_SP_PORT(element))
14092     {
14093       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14094           element == EL_SP_GRAVITY_PORT_RIGHT ||
14095           element == EL_SP_GRAVITY_PORT_UP ||
14096           element == EL_SP_GRAVITY_PORT_DOWN)
14097         player->gravity = !player->gravity;
14098       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14099                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14100                element == EL_SP_GRAVITY_ON_PORT_UP ||
14101                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14102         player->gravity = TRUE;
14103       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14104                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14105                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14106                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14107         player->gravity = FALSE;
14108     }
14109
14110     // automatically move to the next field with double speed
14111     player->programmed_action = move_direction;
14112
14113     if (player->move_delay_reset_counter == 0)
14114     {
14115       player->move_delay_reset_counter = 2;     // two double speed steps
14116
14117       DOUBLE_PLAYER_SPEED(player);
14118     }
14119
14120     PlayLevelSoundAction(x, y, ACTION_PASSING);
14121   }
14122   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14123   {
14124     RemoveField(x, y);
14125
14126     if (mode != DF_SNAP)
14127     {
14128       GfxElement[x][y] = GFX_ELEMENT(element);
14129       player->is_digging = TRUE;
14130     }
14131
14132     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14133
14134     // use old behaviour for old levels (digging)
14135     if (!level.finish_dig_collect)
14136     {
14137       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14138                                           player->index_bit, dig_side);
14139
14140       // if digging triggered player relocation, finish digging tile
14141       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14142         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14143     }
14144
14145     if (mode == DF_SNAP)
14146     {
14147       if (level.block_snap_field)
14148         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14149       else
14150         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14151
14152       // use old behaviour for old levels (snapping)
14153       if (!level.finish_dig_collect)
14154         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14155                                             player->index_bit, dig_side);
14156     }
14157   }
14158   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14159   {
14160     RemoveField(x, y);
14161
14162     if (is_player && mode != DF_SNAP)
14163     {
14164       GfxElement[x][y] = element;
14165       player->is_collecting = TRUE;
14166     }
14167
14168     if (element == EL_SPEED_PILL)
14169     {
14170       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14171     }
14172     else if (element == EL_EXTRA_TIME && level.time > 0)
14173     {
14174       TimeLeft += level.extra_time;
14175
14176       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14177
14178       DisplayGameControlValues();
14179     }
14180     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14181     {
14182       player->shield_normal_time_left += level.shield_normal_time;
14183       if (element == EL_SHIELD_DEADLY)
14184         player->shield_deadly_time_left += level.shield_deadly_time;
14185     }
14186     else if (element == EL_DYNAMITE ||
14187              element == EL_EM_DYNAMITE ||
14188              element == EL_SP_DISK_RED)
14189     {
14190       if (player->inventory_size < MAX_INVENTORY_SIZE)
14191         player->inventory_element[player->inventory_size++] = element;
14192
14193       DrawGameDoorValues();
14194     }
14195     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14196     {
14197       player->dynabomb_count++;
14198       player->dynabombs_left++;
14199     }
14200     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14201     {
14202       player->dynabomb_size++;
14203     }
14204     else if (element == EL_DYNABOMB_INCREASE_POWER)
14205     {
14206       player->dynabomb_xl = TRUE;
14207     }
14208     else if (IS_KEY(element))
14209     {
14210       player->key[KEY_NR(element)] = TRUE;
14211
14212       DrawGameDoorValues();
14213     }
14214     else if (element == EL_DC_KEY_WHITE)
14215     {
14216       player->num_white_keys++;
14217
14218       // display white keys?
14219       // DrawGameDoorValues();
14220     }
14221     else if (IS_ENVELOPE(element))
14222     {
14223       player->show_envelope = element;
14224     }
14225     else if (element == EL_EMC_LENSES)
14226     {
14227       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14228
14229       RedrawAllInvisibleElementsForLenses();
14230     }
14231     else if (element == EL_EMC_MAGNIFIER)
14232     {
14233       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14234
14235       RedrawAllInvisibleElementsForMagnifier();
14236     }
14237     else if (IS_DROPPABLE(element) ||
14238              IS_THROWABLE(element))     // can be collected and dropped
14239     {
14240       int i;
14241
14242       if (collect_count == 0)
14243         player->inventory_infinite_element = element;
14244       else
14245         for (i = 0; i < collect_count; i++)
14246           if (player->inventory_size < MAX_INVENTORY_SIZE)
14247             player->inventory_element[player->inventory_size++] = element;
14248
14249       DrawGameDoorValues();
14250     }
14251     else if (collect_count > 0)
14252     {
14253       game.gems_still_needed -= collect_count;
14254       if (game.gems_still_needed < 0)
14255         game.gems_still_needed = 0;
14256
14257       game.snapshot.collected_item = TRUE;
14258
14259       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14260
14261       DisplayGameControlValues();
14262     }
14263
14264     RaiseScoreElement(element);
14265     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14266
14267     // use old behaviour for old levels (collecting)
14268     if (!level.finish_dig_collect && is_player)
14269     {
14270       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14271                                           player->index_bit, dig_side);
14272
14273       // if collecting triggered player relocation, finish collecting tile
14274       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14275         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14276     }
14277
14278     if (mode == DF_SNAP)
14279     {
14280       if (level.block_snap_field)
14281         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14282       else
14283         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14284
14285       // use old behaviour for old levels (snapping)
14286       if (!level.finish_dig_collect)
14287         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14288                                             player->index_bit, dig_side);
14289     }
14290   }
14291   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14292   {
14293     if (mode == DF_SNAP && element != EL_BD_ROCK)
14294       return MP_NO_ACTION;
14295
14296     if (CAN_FALL(element) && dy)
14297       return MP_NO_ACTION;
14298
14299     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14300         !(element == EL_SPRING && level.use_spring_bug))
14301       return MP_NO_ACTION;
14302
14303     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14304         ((move_direction & MV_VERTICAL &&
14305           ((element_info[element].move_pattern & MV_LEFT &&
14306             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14307            (element_info[element].move_pattern & MV_RIGHT &&
14308             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14309          (move_direction & MV_HORIZONTAL &&
14310           ((element_info[element].move_pattern & MV_UP &&
14311             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14312            (element_info[element].move_pattern & MV_DOWN &&
14313             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14314       return MP_NO_ACTION;
14315
14316     // do not push elements already moving away faster than player
14317     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14318         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14319       return MP_NO_ACTION;
14320
14321     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14322     {
14323       if (player->push_delay_value == -1 || !player_was_pushing)
14324         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14325     }
14326     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14327     {
14328       if (player->push_delay_value == -1)
14329         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14330     }
14331     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14332     {
14333       if (!player->is_pushing)
14334         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14335     }
14336
14337     player->is_pushing = TRUE;
14338     player->is_active = TRUE;
14339
14340     if (!(IN_LEV_FIELD(nextx, nexty) &&
14341           (IS_FREE(nextx, nexty) ||
14342            (IS_SB_ELEMENT(element) &&
14343             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14344            (IS_CUSTOM_ELEMENT(element) &&
14345             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14346       return MP_NO_ACTION;
14347
14348     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14349       return MP_NO_ACTION;
14350
14351     if (player->push_delay == -1)       // new pushing; restart delay
14352       player->push_delay = 0;
14353
14354     if (player->push_delay < player->push_delay_value &&
14355         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14356         element != EL_SPRING && element != EL_BALLOON)
14357     {
14358       // make sure that there is no move delay before next try to push
14359       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14360         player->move_delay = 0;
14361
14362       return MP_NO_ACTION;
14363     }
14364
14365     if (IS_CUSTOM_ELEMENT(element) &&
14366         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14367     {
14368       if (!DigFieldByCE(nextx, nexty, element))
14369         return MP_NO_ACTION;
14370     }
14371
14372     if (IS_SB_ELEMENT(element))
14373     {
14374       boolean sokoban_task_solved = FALSE;
14375
14376       if (element == EL_SOKOBAN_FIELD_FULL)
14377       {
14378         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14379
14380         IncrementSokobanFieldsNeeded();
14381         IncrementSokobanObjectsNeeded();
14382       }
14383
14384       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14385       {
14386         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14387
14388         DecrementSokobanFieldsNeeded();
14389         DecrementSokobanObjectsNeeded();
14390
14391         // sokoban object was pushed from empty field to sokoban field
14392         if (Back[x][y] == EL_EMPTY)
14393           sokoban_task_solved = TRUE;
14394       }
14395
14396       Tile[x][y] = EL_SOKOBAN_OBJECT;
14397
14398       if (Back[x][y] == Back[nextx][nexty])
14399         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14400       else if (Back[x][y] != 0)
14401         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14402                                     ACTION_EMPTYING);
14403       else
14404         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14405                                     ACTION_FILLING);
14406
14407       if (sokoban_task_solved &&
14408           game.sokoban_fields_still_needed == 0 &&
14409           game.sokoban_objects_still_needed == 0 &&
14410           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14411       {
14412         game.players_still_needed = 0;
14413
14414         LevelSolved();
14415
14416         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14417       }
14418     }
14419     else
14420       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14421
14422     InitMovingField(x, y, move_direction);
14423     GfxAction[x][y] = ACTION_PUSHING;
14424
14425     if (mode == DF_SNAP)
14426       ContinueMoving(x, y);
14427     else
14428       MovPos[x][y] = (dx != 0 ? dx : dy);
14429
14430     Pushed[x][y] = TRUE;
14431     Pushed[nextx][nexty] = TRUE;
14432
14433     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14434       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14435     else
14436       player->push_delay_value = -1;    // get new value later
14437
14438     // check for element change _after_ element has been pushed
14439     if (game.use_change_when_pushing_bug)
14440     {
14441       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14442                                  player->index_bit, dig_side);
14443       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14444                                           player->index_bit, dig_side);
14445     }
14446   }
14447   else if (IS_SWITCHABLE(element))
14448   {
14449     if (PLAYER_SWITCHING(player, x, y))
14450     {
14451       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14452                                           player->index_bit, dig_side);
14453
14454       return MP_ACTION;
14455     }
14456
14457     player->is_switching = TRUE;
14458     player->switch_x = x;
14459     player->switch_y = y;
14460
14461     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14462
14463     if (element == EL_ROBOT_WHEEL)
14464     {
14465       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14466
14467       game.robot_wheel_x = x;
14468       game.robot_wheel_y = y;
14469       game.robot_wheel_active = TRUE;
14470
14471       TEST_DrawLevelField(x, y);
14472     }
14473     else if (element == EL_SP_TERMINAL)
14474     {
14475       int xx, yy;
14476
14477       SCAN_PLAYFIELD(xx, yy)
14478       {
14479         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14480         {
14481           Bang(xx, yy);
14482         }
14483         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14484         {
14485           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14486
14487           ResetGfxAnimation(xx, yy);
14488           TEST_DrawLevelField(xx, yy);
14489         }
14490       }
14491     }
14492     else if (IS_BELT_SWITCH(element))
14493     {
14494       ToggleBeltSwitch(x, y);
14495     }
14496     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14497              element == EL_SWITCHGATE_SWITCH_DOWN ||
14498              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14499              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14500     {
14501       ToggleSwitchgateSwitch(x, y);
14502     }
14503     else if (element == EL_LIGHT_SWITCH ||
14504              element == EL_LIGHT_SWITCH_ACTIVE)
14505     {
14506       ToggleLightSwitch(x, y);
14507     }
14508     else if (element == EL_TIMEGATE_SWITCH ||
14509              element == EL_DC_TIMEGATE_SWITCH)
14510     {
14511       ActivateTimegateSwitch(x, y);
14512     }
14513     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14514              element == EL_BALLOON_SWITCH_RIGHT ||
14515              element == EL_BALLOON_SWITCH_UP    ||
14516              element == EL_BALLOON_SWITCH_DOWN  ||
14517              element == EL_BALLOON_SWITCH_NONE  ||
14518              element == EL_BALLOON_SWITCH_ANY)
14519     {
14520       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14521                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14522                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14523                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14524                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14525                              move_direction);
14526     }
14527     else if (element == EL_LAMP)
14528     {
14529       Tile[x][y] = EL_LAMP_ACTIVE;
14530       game.lights_still_needed--;
14531
14532       ResetGfxAnimation(x, y);
14533       TEST_DrawLevelField(x, y);
14534     }
14535     else if (element == EL_TIME_ORB_FULL)
14536     {
14537       Tile[x][y] = EL_TIME_ORB_EMPTY;
14538
14539       if (level.time > 0 || level.use_time_orb_bug)
14540       {
14541         TimeLeft += level.time_orb_time;
14542         game.no_time_limit = FALSE;
14543
14544         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14545
14546         DisplayGameControlValues();
14547       }
14548
14549       ResetGfxAnimation(x, y);
14550       TEST_DrawLevelField(x, y);
14551     }
14552     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14553              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14554     {
14555       int xx, yy;
14556
14557       game.ball_active = !game.ball_active;
14558
14559       SCAN_PLAYFIELD(xx, yy)
14560       {
14561         int e = Tile[xx][yy];
14562
14563         if (game.ball_active)
14564         {
14565           if (e == EL_EMC_MAGIC_BALL)
14566             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14567           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14568             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14569         }
14570         else
14571         {
14572           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14573             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14574           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14575             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14576         }
14577       }
14578     }
14579
14580     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14581                                         player->index_bit, dig_side);
14582
14583     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14584                                         player->index_bit, dig_side);
14585
14586     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14587                                         player->index_bit, dig_side);
14588
14589     return MP_ACTION;
14590   }
14591   else
14592   {
14593     if (!PLAYER_SWITCHING(player, x, y))
14594     {
14595       player->is_switching = TRUE;
14596       player->switch_x = x;
14597       player->switch_y = y;
14598
14599       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14600                                  player->index_bit, dig_side);
14601       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14602                                           player->index_bit, dig_side);
14603
14604       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14605                                  player->index_bit, dig_side);
14606       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14607                                           player->index_bit, dig_side);
14608     }
14609
14610     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14611                                player->index_bit, dig_side);
14612     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14613                                         player->index_bit, dig_side);
14614
14615     return MP_NO_ACTION;
14616   }
14617
14618   player->push_delay = -1;
14619
14620   if (is_player)                // function can also be called by EL_PENGUIN
14621   {
14622     if (Tile[x][y] != element)          // really digged/collected something
14623     {
14624       player->is_collecting = !player->is_digging;
14625       player->is_active = TRUE;
14626
14627       player->last_removed_element = element;
14628     }
14629   }
14630
14631   return MP_MOVING;
14632 }
14633
14634 static boolean DigFieldByCE(int x, int y, int digging_element)
14635 {
14636   int element = Tile[x][y];
14637
14638   if (!IS_FREE(x, y))
14639   {
14640     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14641                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14642                   ACTION_BREAKING);
14643
14644     // no element can dig solid indestructible elements
14645     if (IS_INDESTRUCTIBLE(element) &&
14646         !IS_DIGGABLE(element) &&
14647         !IS_COLLECTIBLE(element))
14648       return FALSE;
14649
14650     if (AmoebaNr[x][y] &&
14651         (element == EL_AMOEBA_FULL ||
14652          element == EL_BD_AMOEBA ||
14653          element == EL_AMOEBA_GROWING))
14654     {
14655       AmoebaCnt[AmoebaNr[x][y]]--;
14656       AmoebaCnt2[AmoebaNr[x][y]]--;
14657     }
14658
14659     if (IS_MOVING(x, y))
14660       RemoveMovingField(x, y);
14661     else
14662     {
14663       RemoveField(x, y);
14664       TEST_DrawLevelField(x, y);
14665     }
14666
14667     // if digged element was about to explode, prevent the explosion
14668     ExplodeField[x][y] = EX_TYPE_NONE;
14669
14670     PlayLevelSoundAction(x, y, action);
14671   }
14672
14673   Store[x][y] = EL_EMPTY;
14674
14675   // this makes it possible to leave the removed element again
14676   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14677     Store[x][y] = element;
14678
14679   return TRUE;
14680 }
14681
14682 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14683 {
14684   int jx = player->jx, jy = player->jy;
14685   int x = jx + dx, y = jy + dy;
14686   int snap_direction = (dx == -1 ? MV_LEFT  :
14687                         dx == +1 ? MV_RIGHT :
14688                         dy == -1 ? MV_UP    :
14689                         dy == +1 ? MV_DOWN  : MV_NONE);
14690   boolean can_continue_snapping = (level.continuous_snapping &&
14691                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14692
14693   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14694     return FALSE;
14695
14696   if (!player->active || !IN_LEV_FIELD(x, y))
14697     return FALSE;
14698
14699   if (dx && dy)
14700     return FALSE;
14701
14702   if (!dx && !dy)
14703   {
14704     if (player->MovPos == 0)
14705       player->is_pushing = FALSE;
14706
14707     player->is_snapping = FALSE;
14708
14709     if (player->MovPos == 0)
14710     {
14711       player->is_moving = FALSE;
14712       player->is_digging = FALSE;
14713       player->is_collecting = FALSE;
14714     }
14715
14716     return FALSE;
14717   }
14718
14719   // prevent snapping with already pressed snap key when not allowed
14720   if (player->is_snapping && !can_continue_snapping)
14721     return FALSE;
14722
14723   player->MovDir = snap_direction;
14724
14725   if (player->MovPos == 0)
14726   {
14727     player->is_moving = FALSE;
14728     player->is_digging = FALSE;
14729     player->is_collecting = FALSE;
14730   }
14731
14732   player->is_dropping = FALSE;
14733   player->is_dropping_pressed = FALSE;
14734   player->drop_pressed_delay = 0;
14735
14736   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14737     return FALSE;
14738
14739   player->is_snapping = TRUE;
14740   player->is_active = TRUE;
14741
14742   if (player->MovPos == 0)
14743   {
14744     player->is_moving = FALSE;
14745     player->is_digging = FALSE;
14746     player->is_collecting = FALSE;
14747   }
14748
14749   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14750     TEST_DrawLevelField(player->last_jx, player->last_jy);
14751
14752   TEST_DrawLevelField(x, y);
14753
14754   return TRUE;
14755 }
14756
14757 static boolean DropElement(struct PlayerInfo *player)
14758 {
14759   int old_element, new_element;
14760   int dropx = player->jx, dropy = player->jy;
14761   int drop_direction = player->MovDir;
14762   int drop_side = drop_direction;
14763   int drop_element = get_next_dropped_element(player);
14764
14765   /* do not drop an element on top of another element; when holding drop key
14766      pressed without moving, dropped element must move away before the next
14767      element can be dropped (this is especially important if the next element
14768      is dynamite, which can be placed on background for historical reasons) */
14769   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14770     return MP_ACTION;
14771
14772   if (IS_THROWABLE(drop_element))
14773   {
14774     dropx += GET_DX_FROM_DIR(drop_direction);
14775     dropy += GET_DY_FROM_DIR(drop_direction);
14776
14777     if (!IN_LEV_FIELD(dropx, dropy))
14778       return FALSE;
14779   }
14780
14781   old_element = Tile[dropx][dropy];     // old element at dropping position
14782   new_element = drop_element;           // default: no change when dropping
14783
14784   // check if player is active, not moving and ready to drop
14785   if (!player->active || player->MovPos || player->drop_delay > 0)
14786     return FALSE;
14787
14788   // check if player has anything that can be dropped
14789   if (new_element == EL_UNDEFINED)
14790     return FALSE;
14791
14792   // only set if player has anything that can be dropped
14793   player->is_dropping_pressed = TRUE;
14794
14795   // check if drop key was pressed long enough for EM style dynamite
14796   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14797     return FALSE;
14798
14799   // check if anything can be dropped at the current position
14800   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14801     return FALSE;
14802
14803   // collected custom elements can only be dropped on empty fields
14804   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14805     return FALSE;
14806
14807   if (old_element != EL_EMPTY)
14808     Back[dropx][dropy] = old_element;   // store old element on this field
14809
14810   ResetGfxAnimation(dropx, dropy);
14811   ResetRandomAnimationValue(dropx, dropy);
14812
14813   if (player->inventory_size > 0 ||
14814       player->inventory_infinite_element != EL_UNDEFINED)
14815   {
14816     if (player->inventory_size > 0)
14817     {
14818       player->inventory_size--;
14819
14820       DrawGameDoorValues();
14821
14822       if (new_element == EL_DYNAMITE)
14823         new_element = EL_DYNAMITE_ACTIVE;
14824       else if (new_element == EL_EM_DYNAMITE)
14825         new_element = EL_EM_DYNAMITE_ACTIVE;
14826       else if (new_element == EL_SP_DISK_RED)
14827         new_element = EL_SP_DISK_RED_ACTIVE;
14828     }
14829
14830     Tile[dropx][dropy] = new_element;
14831
14832     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14833       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14834                           el2img(Tile[dropx][dropy]), 0);
14835
14836     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14837
14838     // needed if previous element just changed to "empty" in the last frame
14839     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14840
14841     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14842                                player->index_bit, drop_side);
14843     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14844                                         CE_PLAYER_DROPS_X,
14845                                         player->index_bit, drop_side);
14846
14847     TestIfElementTouchesCustomElement(dropx, dropy);
14848   }
14849   else          // player is dropping a dyna bomb
14850   {
14851     player->dynabombs_left--;
14852
14853     Tile[dropx][dropy] = new_element;
14854
14855     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14856       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14857                           el2img(Tile[dropx][dropy]), 0);
14858
14859     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14860   }
14861
14862   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14863     InitField_WithBug1(dropx, dropy, FALSE);
14864
14865   new_element = Tile[dropx][dropy];     // element might have changed
14866
14867   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14868       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14869   {
14870     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14871       MovDir[dropx][dropy] = drop_direction;
14872
14873     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14874
14875     // do not cause impact style collision by dropping elements that can fall
14876     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14877   }
14878
14879   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14880   player->is_dropping = TRUE;
14881
14882   player->drop_pressed_delay = 0;
14883   player->is_dropping_pressed = FALSE;
14884
14885   player->drop_x = dropx;
14886   player->drop_y = dropy;
14887
14888   return TRUE;
14889 }
14890
14891 // ----------------------------------------------------------------------------
14892 // game sound playing functions
14893 // ----------------------------------------------------------------------------
14894
14895 static int *loop_sound_frame = NULL;
14896 static int *loop_sound_volume = NULL;
14897
14898 void InitPlayLevelSound(void)
14899 {
14900   int num_sounds = getSoundListSize();
14901
14902   checked_free(loop_sound_frame);
14903   checked_free(loop_sound_volume);
14904
14905   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14906   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14907 }
14908
14909 static void PlayLevelSound(int x, int y, int nr)
14910 {
14911   int sx = SCREENX(x), sy = SCREENY(y);
14912   int volume, stereo_position;
14913   int max_distance = 8;
14914   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14915
14916   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14917       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14918     return;
14919
14920   if (!IN_LEV_FIELD(x, y) ||
14921       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14922       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14923     return;
14924
14925   volume = SOUND_MAX_VOLUME;
14926
14927   if (!IN_SCR_FIELD(sx, sy))
14928   {
14929     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14930     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14931
14932     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14933   }
14934
14935   stereo_position = (SOUND_MAX_LEFT +
14936                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14937                      (SCR_FIELDX + 2 * max_distance));
14938
14939   if (IS_LOOP_SOUND(nr))
14940   {
14941     /* This assures that quieter loop sounds do not overwrite louder ones,
14942        while restarting sound volume comparison with each new game frame. */
14943
14944     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14945       return;
14946
14947     loop_sound_volume[nr] = volume;
14948     loop_sound_frame[nr] = FrameCounter;
14949   }
14950
14951   PlaySoundExt(nr, volume, stereo_position, type);
14952 }
14953
14954 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14955 {
14956   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14957                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14958                  y < LEVELY(BY1) ? LEVELY(BY1) :
14959                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14960                  sound_action);
14961 }
14962
14963 static void PlayLevelSoundAction(int x, int y, int action)
14964 {
14965   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14966 }
14967
14968 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14969 {
14970   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14971
14972   if (sound_effect != SND_UNDEFINED)
14973     PlayLevelSound(x, y, sound_effect);
14974 }
14975
14976 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14977                                               int action)
14978 {
14979   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14980
14981   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14982     PlayLevelSound(x, y, sound_effect);
14983 }
14984
14985 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14986 {
14987   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14988
14989   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14990     PlayLevelSound(x, y, sound_effect);
14991 }
14992
14993 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14994 {
14995   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14996
14997   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14998     StopSound(sound_effect);
14999 }
15000
15001 static int getLevelMusicNr(void)
15002 {
15003   if (levelset.music[level_nr] != MUS_UNDEFINED)
15004     return levelset.music[level_nr];            // from config file
15005   else
15006     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15007 }
15008
15009 static void FadeLevelSounds(void)
15010 {
15011   FadeSounds();
15012 }
15013
15014 static void FadeLevelMusic(void)
15015 {
15016   int music_nr = getLevelMusicNr();
15017   char *curr_music = getCurrentlyPlayingMusicFilename();
15018   char *next_music = getMusicInfoEntryFilename(music_nr);
15019
15020   if (!strEqual(curr_music, next_music))
15021     FadeMusic();
15022 }
15023
15024 void FadeLevelSoundsAndMusic(void)
15025 {
15026   FadeLevelSounds();
15027   FadeLevelMusic();
15028 }
15029
15030 static void PlayLevelMusic(void)
15031 {
15032   int music_nr = getLevelMusicNr();
15033   char *curr_music = getCurrentlyPlayingMusicFilename();
15034   char *next_music = getMusicInfoEntryFilename(music_nr);
15035
15036   if (!strEqual(curr_music, next_music))
15037     PlayMusicLoop(music_nr);
15038 }
15039
15040 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15041 {
15042   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15043   int offset = 0;
15044   int x = xx - offset;
15045   int y = yy - offset;
15046
15047   switch (sample)
15048   {
15049     case SOUND_blank:
15050       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15051       break;
15052
15053     case SOUND_roll:
15054       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15055       break;
15056
15057     case SOUND_stone:
15058       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15059       break;
15060
15061     case SOUND_nut:
15062       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15063       break;
15064
15065     case SOUND_crack:
15066       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15067       break;
15068
15069     case SOUND_bug:
15070       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15071       break;
15072
15073     case SOUND_tank:
15074       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15075       break;
15076
15077     case SOUND_android_clone:
15078       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15079       break;
15080
15081     case SOUND_android_move:
15082       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15083       break;
15084
15085     case SOUND_spring:
15086       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15087       break;
15088
15089     case SOUND_slurp:
15090       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15091       break;
15092
15093     case SOUND_eater:
15094       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15095       break;
15096
15097     case SOUND_eater_eat:
15098       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15099       break;
15100
15101     case SOUND_alien:
15102       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15103       break;
15104
15105     case SOUND_collect:
15106       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15107       break;
15108
15109     case SOUND_diamond:
15110       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15111       break;
15112
15113     case SOUND_squash:
15114       // !!! CHECK THIS !!!
15115 #if 1
15116       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15117 #else
15118       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15119 #endif
15120       break;
15121
15122     case SOUND_wonderfall:
15123       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15124       break;
15125
15126     case SOUND_drip:
15127       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15128       break;
15129
15130     case SOUND_push:
15131       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15132       break;
15133
15134     case SOUND_dirt:
15135       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15136       break;
15137
15138     case SOUND_acid:
15139       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15140       break;
15141
15142     case SOUND_ball:
15143       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15144       break;
15145
15146     case SOUND_slide:
15147       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15148       break;
15149
15150     case SOUND_wonder:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15152       break;
15153
15154     case SOUND_door:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15156       break;
15157
15158     case SOUND_exit_open:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15160       break;
15161
15162     case SOUND_exit_leave:
15163       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15164       break;
15165
15166     case SOUND_dynamite:
15167       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15168       break;
15169
15170     case SOUND_tick:
15171       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15172       break;
15173
15174     case SOUND_press:
15175       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15176       break;
15177
15178     case SOUND_wheel:
15179       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15180       break;
15181
15182     case SOUND_boom:
15183       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15184       break;
15185
15186     case SOUND_die:
15187       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15188       break;
15189
15190     case SOUND_time:
15191       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15192       break;
15193
15194     default:
15195       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15196       break;
15197   }
15198 }
15199
15200 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15201 {
15202   int element = map_element_SP_to_RND(element_sp);
15203   int action = map_action_SP_to_RND(action_sp);
15204   int offset = (setup.sp_show_border_elements ? 0 : 1);
15205   int x = xx - offset;
15206   int y = yy - offset;
15207
15208   PlayLevelSoundElementAction(x, y, element, action);
15209 }
15210
15211 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15212 {
15213   int element = map_element_MM_to_RND(element_mm);
15214   int action = map_action_MM_to_RND(action_mm);
15215   int offset = 0;
15216   int x = xx - offset;
15217   int y = yy - offset;
15218
15219   if (!IS_MM_ELEMENT(element))
15220     element = EL_MM_DEFAULT;
15221
15222   PlayLevelSoundElementAction(x, y, element, action);
15223 }
15224
15225 void PlaySound_MM(int sound_mm)
15226 {
15227   int sound = map_sound_MM_to_RND(sound_mm);
15228
15229   if (sound == SND_UNDEFINED)
15230     return;
15231
15232   PlaySound(sound);
15233 }
15234
15235 void PlaySoundLoop_MM(int sound_mm)
15236 {
15237   int sound = map_sound_MM_to_RND(sound_mm);
15238
15239   if (sound == SND_UNDEFINED)
15240     return;
15241
15242   PlaySoundLoop(sound);
15243 }
15244
15245 void StopSound_MM(int sound_mm)
15246 {
15247   int sound = map_sound_MM_to_RND(sound_mm);
15248
15249   if (sound == SND_UNDEFINED)
15250     return;
15251
15252   StopSound(sound);
15253 }
15254
15255 void RaiseScore(int value)
15256 {
15257   game.score += value;
15258
15259   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15260
15261   DisplayGameControlValues();
15262 }
15263
15264 void RaiseScoreElement(int element)
15265 {
15266   switch (element)
15267   {
15268     case EL_EMERALD:
15269     case EL_BD_DIAMOND:
15270     case EL_EMERALD_YELLOW:
15271     case EL_EMERALD_RED:
15272     case EL_EMERALD_PURPLE:
15273     case EL_SP_INFOTRON:
15274       RaiseScore(level.score[SC_EMERALD]);
15275       break;
15276     case EL_DIAMOND:
15277       RaiseScore(level.score[SC_DIAMOND]);
15278       break;
15279     case EL_CRYSTAL:
15280       RaiseScore(level.score[SC_CRYSTAL]);
15281       break;
15282     case EL_PEARL:
15283       RaiseScore(level.score[SC_PEARL]);
15284       break;
15285     case EL_BUG:
15286     case EL_BD_BUTTERFLY:
15287     case EL_SP_ELECTRON:
15288       RaiseScore(level.score[SC_BUG]);
15289       break;
15290     case EL_SPACESHIP:
15291     case EL_BD_FIREFLY:
15292     case EL_SP_SNIKSNAK:
15293       RaiseScore(level.score[SC_SPACESHIP]);
15294       break;
15295     case EL_YAMYAM:
15296     case EL_DARK_YAMYAM:
15297       RaiseScore(level.score[SC_YAMYAM]);
15298       break;
15299     case EL_ROBOT:
15300       RaiseScore(level.score[SC_ROBOT]);
15301       break;
15302     case EL_PACMAN:
15303       RaiseScore(level.score[SC_PACMAN]);
15304       break;
15305     case EL_NUT:
15306       RaiseScore(level.score[SC_NUT]);
15307       break;
15308     case EL_DYNAMITE:
15309     case EL_EM_DYNAMITE:
15310     case EL_SP_DISK_RED:
15311     case EL_DYNABOMB_INCREASE_NUMBER:
15312     case EL_DYNABOMB_INCREASE_SIZE:
15313     case EL_DYNABOMB_INCREASE_POWER:
15314       RaiseScore(level.score[SC_DYNAMITE]);
15315       break;
15316     case EL_SHIELD_NORMAL:
15317     case EL_SHIELD_DEADLY:
15318       RaiseScore(level.score[SC_SHIELD]);
15319       break;
15320     case EL_EXTRA_TIME:
15321       RaiseScore(level.extra_time_score);
15322       break;
15323     case EL_KEY_1:
15324     case EL_KEY_2:
15325     case EL_KEY_3:
15326     case EL_KEY_4:
15327     case EL_EM_KEY_1:
15328     case EL_EM_KEY_2:
15329     case EL_EM_KEY_3:
15330     case EL_EM_KEY_4:
15331     case EL_EMC_KEY_5:
15332     case EL_EMC_KEY_6:
15333     case EL_EMC_KEY_7:
15334     case EL_EMC_KEY_8:
15335     case EL_DC_KEY_WHITE:
15336       RaiseScore(level.score[SC_KEY]);
15337       break;
15338     default:
15339       RaiseScore(element_info[element].collect_score);
15340       break;
15341   }
15342 }
15343
15344 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15345 {
15346   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15347   {
15348     // closing door required in case of envelope style request dialogs
15349     if (!skip_request)
15350     {
15351       // prevent short reactivation of overlay buttons while closing door
15352       SetOverlayActive(FALSE);
15353
15354       CloseDoor(DOOR_CLOSE_1);
15355     }
15356
15357     if (network.enabled)
15358       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15359     else
15360     {
15361       if (quick_quit)
15362         FadeSkipNextFadeIn();
15363
15364       SetGameStatus(GAME_MODE_MAIN);
15365
15366       DrawMainMenu();
15367     }
15368   }
15369   else          // continue playing the game
15370   {
15371     if (tape.playing && tape.deactivate_display)
15372       TapeDeactivateDisplayOff(TRUE);
15373
15374     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15375
15376     if (tape.playing && tape.deactivate_display)
15377       TapeDeactivateDisplayOn();
15378   }
15379 }
15380
15381 void RequestQuitGame(boolean ask_if_really_quit)
15382 {
15383   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15384   boolean skip_request = game.all_players_gone || quick_quit;
15385
15386   RequestQuitGameExt(skip_request, quick_quit,
15387                      "Do you really want to quit the game?");
15388 }
15389
15390 void RequestRestartGame(char *message)
15391 {
15392   game.restart_game_message = NULL;
15393
15394   boolean has_started_game = hasStartedNetworkGame();
15395   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15396
15397   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15398   {
15399     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15400   }
15401   else
15402   {
15403     // needed in case of envelope request to close game panel
15404     CloseDoor(DOOR_CLOSE_1);
15405
15406     SetGameStatus(GAME_MODE_MAIN);
15407
15408     DrawMainMenu();
15409   }
15410 }
15411
15412 void CheckGameOver(void)
15413 {
15414   static boolean last_game_over = FALSE;
15415   static int game_over_delay = 0;
15416   int game_over_delay_value = 50;
15417   boolean game_over = checkGameFailed();
15418
15419   // do not handle game over if request dialog is already active
15420   if (game.request_active)
15421     return;
15422
15423   // do not ask to play again if game was never actually played
15424   if (!game.GamePlayed)
15425     return;
15426
15427   if (!game_over)
15428   {
15429     last_game_over = FALSE;
15430     game_over_delay = game_over_delay_value;
15431
15432     return;
15433   }
15434
15435   if (game_over_delay > 0)
15436   {
15437     game_over_delay--;
15438
15439     return;
15440   }
15441
15442   if (last_game_over != game_over)
15443     game.restart_game_message = (hasStartedNetworkGame() ?
15444                                  "Game over! Play it again?" :
15445                                  "Game over!");
15446
15447   last_game_over = game_over;
15448 }
15449
15450 boolean checkGameSolved(void)
15451 {
15452   // set for all game engines if level was solved
15453   return game.LevelSolved_GameEnd;
15454 }
15455
15456 boolean checkGameFailed(void)
15457 {
15458   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15459     return (game_em.game_over && !game_em.level_solved);
15460   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15461     return (game_sp.game_over && !game_sp.level_solved);
15462   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15463     return (game_mm.game_over && !game_mm.level_solved);
15464   else                          // GAME_ENGINE_TYPE_RND
15465     return (game.GameOver && !game.LevelSolved);
15466 }
15467
15468 boolean checkGameEnded(void)
15469 {
15470   return (checkGameSolved() || checkGameFailed());
15471 }
15472
15473
15474 // ----------------------------------------------------------------------------
15475 // random generator functions
15476 // ----------------------------------------------------------------------------
15477
15478 unsigned int InitEngineRandom_RND(int seed)
15479 {
15480   game.num_random_calls = 0;
15481
15482   return InitEngineRandom(seed);
15483 }
15484
15485 unsigned int RND(int max)
15486 {
15487   if (max > 0)
15488   {
15489     game.num_random_calls++;
15490
15491     return GetEngineRandom(max);
15492   }
15493
15494   return 0;
15495 }
15496
15497
15498 // ----------------------------------------------------------------------------
15499 // game engine snapshot handling functions
15500 // ----------------------------------------------------------------------------
15501
15502 struct EngineSnapshotInfo
15503 {
15504   // runtime values for custom element collect score
15505   int collect_score[NUM_CUSTOM_ELEMENTS];
15506
15507   // runtime values for group element choice position
15508   int choice_pos[NUM_GROUP_ELEMENTS];
15509
15510   // runtime values for belt position animations
15511   int belt_graphic[4][NUM_BELT_PARTS];
15512   int belt_anim_mode[4][NUM_BELT_PARTS];
15513 };
15514
15515 static struct EngineSnapshotInfo engine_snapshot_rnd;
15516 static char *snapshot_level_identifier = NULL;
15517 static int snapshot_level_nr = -1;
15518
15519 static void SaveEngineSnapshotValues_RND(void)
15520 {
15521   static int belt_base_active_element[4] =
15522   {
15523     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15524     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15525     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15526     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15527   };
15528   int i, j;
15529
15530   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15531   {
15532     int element = EL_CUSTOM_START + i;
15533
15534     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15535   }
15536
15537   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15538   {
15539     int element = EL_GROUP_START + i;
15540
15541     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15542   }
15543
15544   for (i = 0; i < 4; i++)
15545   {
15546     for (j = 0; j < NUM_BELT_PARTS; j++)
15547     {
15548       int element = belt_base_active_element[i] + j;
15549       int graphic = el2img(element);
15550       int anim_mode = graphic_info[graphic].anim_mode;
15551
15552       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15553       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15554     }
15555   }
15556 }
15557
15558 static void LoadEngineSnapshotValues_RND(void)
15559 {
15560   unsigned int num_random_calls = game.num_random_calls;
15561   int i, j;
15562
15563   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15564   {
15565     int element = EL_CUSTOM_START + i;
15566
15567     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15568   }
15569
15570   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15571   {
15572     int element = EL_GROUP_START + i;
15573
15574     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15575   }
15576
15577   for (i = 0; i < 4; i++)
15578   {
15579     for (j = 0; j < NUM_BELT_PARTS; j++)
15580     {
15581       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15582       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15583
15584       graphic_info[graphic].anim_mode = anim_mode;
15585     }
15586   }
15587
15588   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15589   {
15590     InitRND(tape.random_seed);
15591     for (i = 0; i < num_random_calls; i++)
15592       RND(1);
15593   }
15594
15595   if (game.num_random_calls != num_random_calls)
15596   {
15597     Error("number of random calls out of sync");
15598     Error("number of random calls should be %d", num_random_calls);
15599     Error("number of random calls is %d", game.num_random_calls);
15600
15601     Fail("this should not happen -- please debug");
15602   }
15603 }
15604
15605 void FreeEngineSnapshotSingle(void)
15606 {
15607   FreeSnapshotSingle();
15608
15609   setString(&snapshot_level_identifier, NULL);
15610   snapshot_level_nr = -1;
15611 }
15612
15613 void FreeEngineSnapshotList(void)
15614 {
15615   FreeSnapshotList();
15616 }
15617
15618 static ListNode *SaveEngineSnapshotBuffers(void)
15619 {
15620   ListNode *buffers = NULL;
15621
15622   // copy some special values to a structure better suited for the snapshot
15623
15624   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15625     SaveEngineSnapshotValues_RND();
15626   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15627     SaveEngineSnapshotValues_EM();
15628   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15629     SaveEngineSnapshotValues_SP(&buffers);
15630   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15631     SaveEngineSnapshotValues_MM(&buffers);
15632
15633   // save values stored in special snapshot structure
15634
15635   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15636     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15637   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15638     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15639   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15640     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15641   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15642     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15643
15644   // save further RND engine values
15645
15646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15648   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15649
15650   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15652   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15655
15656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15657   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15659
15660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15661
15662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15664
15665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15683
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15686
15687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15690
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15693
15694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15699
15700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15702
15703 #if 0
15704   ListNode *node = engine_snapshot_list_rnd;
15705   int num_bytes = 0;
15706
15707   while (node != NULL)
15708   {
15709     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15710
15711     node = node->next;
15712   }
15713
15714   Debug("game:playing:SaveEngineSnapshotBuffers",
15715         "size of engine snapshot: %d bytes", num_bytes);
15716 #endif
15717
15718   return buffers;
15719 }
15720
15721 void SaveEngineSnapshotSingle(void)
15722 {
15723   ListNode *buffers = SaveEngineSnapshotBuffers();
15724
15725   // finally save all snapshot buffers to single snapshot
15726   SaveSnapshotSingle(buffers);
15727
15728   // save level identification information
15729   setString(&snapshot_level_identifier, leveldir_current->identifier);
15730   snapshot_level_nr = level_nr;
15731 }
15732
15733 boolean CheckSaveEngineSnapshotToList(void)
15734 {
15735   boolean save_snapshot =
15736     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15737      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15738       game.snapshot.changed_action) ||
15739      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15740       game.snapshot.collected_item));
15741
15742   game.snapshot.changed_action = FALSE;
15743   game.snapshot.collected_item = FALSE;
15744   game.snapshot.save_snapshot = save_snapshot;
15745
15746   return save_snapshot;
15747 }
15748
15749 void SaveEngineSnapshotToList(void)
15750 {
15751   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15752       tape.quick_resume)
15753     return;
15754
15755   ListNode *buffers = SaveEngineSnapshotBuffers();
15756
15757   // finally save all snapshot buffers to snapshot list
15758   SaveSnapshotToList(buffers);
15759 }
15760
15761 void SaveEngineSnapshotToListInitial(void)
15762 {
15763   FreeEngineSnapshotList();
15764
15765   SaveEngineSnapshotToList();
15766 }
15767
15768 static void LoadEngineSnapshotValues(void)
15769 {
15770   // restore special values from snapshot structure
15771
15772   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15773     LoadEngineSnapshotValues_RND();
15774   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15775     LoadEngineSnapshotValues_EM();
15776   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15777     LoadEngineSnapshotValues_SP();
15778   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15779     LoadEngineSnapshotValues_MM();
15780 }
15781
15782 void LoadEngineSnapshotSingle(void)
15783 {
15784   LoadSnapshotSingle();
15785
15786   LoadEngineSnapshotValues();
15787 }
15788
15789 static void LoadEngineSnapshot_Undo(int steps)
15790 {
15791   LoadSnapshotFromList_Older(steps);
15792
15793   LoadEngineSnapshotValues();
15794 }
15795
15796 static void LoadEngineSnapshot_Redo(int steps)
15797 {
15798   LoadSnapshotFromList_Newer(steps);
15799
15800   LoadEngineSnapshotValues();
15801 }
15802
15803 boolean CheckEngineSnapshotSingle(void)
15804 {
15805   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15806           snapshot_level_nr == level_nr);
15807 }
15808
15809 boolean CheckEngineSnapshotList(void)
15810 {
15811   return CheckSnapshotList();
15812 }
15813
15814
15815 // ---------- new game button stuff -------------------------------------------
15816
15817 static struct
15818 {
15819   int graphic;
15820   struct XY *pos;
15821   int gadget_id;
15822   boolean *setup_value;
15823   boolean allowed_on_tape;
15824   boolean is_touch_button;
15825   char *infotext;
15826 } gamebutton_info[NUM_GAME_BUTTONS] =
15827 {
15828   {
15829     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15830     GAME_CTRL_ID_STOP,                          NULL,
15831     TRUE, FALSE,                                "stop game"
15832   },
15833   {
15834     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15835     GAME_CTRL_ID_PAUSE,                         NULL,
15836     TRUE, FALSE,                                "pause game"
15837   },
15838   {
15839     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15840     GAME_CTRL_ID_PLAY,                          NULL,
15841     TRUE, FALSE,                                "play game"
15842   },
15843   {
15844     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15845     GAME_CTRL_ID_UNDO,                          NULL,
15846     TRUE, FALSE,                                "undo step"
15847   },
15848   {
15849     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15850     GAME_CTRL_ID_REDO,                          NULL,
15851     TRUE, FALSE,                                "redo step"
15852   },
15853   {
15854     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15855     GAME_CTRL_ID_SAVE,                          NULL,
15856     TRUE, FALSE,                                "save game"
15857   },
15858   {
15859     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15860     GAME_CTRL_ID_PAUSE2,                        NULL,
15861     TRUE, FALSE,                                "pause game"
15862   },
15863   {
15864     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15865     GAME_CTRL_ID_LOAD,                          NULL,
15866     TRUE, FALSE,                                "load game"
15867   },
15868   {
15869     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15870     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15871     FALSE, FALSE,                               "stop game"
15872   },
15873   {
15874     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15875     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15876     FALSE, FALSE,                               "pause game"
15877   },
15878   {
15879     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15880     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15881     FALSE, FALSE,                               "play game"
15882   },
15883   {
15884     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15885     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15886     FALSE, TRUE,                                "stop game"
15887   },
15888   {
15889     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15890     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15891     FALSE, TRUE,                                "pause game"
15892   },
15893   {
15894     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15895     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15896     TRUE, FALSE,                                "background music on/off"
15897   },
15898   {
15899     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15900     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15901     TRUE, FALSE,                                "sound loops on/off"
15902   },
15903   {
15904     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15905     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15906     TRUE, FALSE,                                "normal sounds on/off"
15907   },
15908   {
15909     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15910     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15911     FALSE, FALSE,                               "background music on/off"
15912   },
15913   {
15914     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15915     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15916     FALSE, FALSE,                               "sound loops on/off"
15917   },
15918   {
15919     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15920     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15921     FALSE, FALSE,                               "normal sounds on/off"
15922   }
15923 };
15924
15925 void CreateGameButtons(void)
15926 {
15927   int i;
15928
15929   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15930   {
15931     int graphic = gamebutton_info[i].graphic;
15932     struct GraphicInfo *gfx = &graphic_info[graphic];
15933     struct XY *pos = gamebutton_info[i].pos;
15934     struct GadgetInfo *gi;
15935     int button_type;
15936     boolean checked;
15937     unsigned int event_mask;
15938     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15939     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15940     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15941     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15942     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15943     int gd_x   = gfx->src_x;
15944     int gd_y   = gfx->src_y;
15945     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15946     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15947     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15948     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15949     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15950     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15951     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15952     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15953     int id = i;
15954
15955     if (gfx->bitmap == NULL)
15956     {
15957       game_gadget[id] = NULL;
15958
15959       continue;
15960     }
15961
15962     if (id == GAME_CTRL_ID_STOP ||
15963         id == GAME_CTRL_ID_PANEL_STOP ||
15964         id == GAME_CTRL_ID_TOUCH_STOP ||
15965         id == GAME_CTRL_ID_PLAY ||
15966         id == GAME_CTRL_ID_PANEL_PLAY ||
15967         id == GAME_CTRL_ID_SAVE ||
15968         id == GAME_CTRL_ID_LOAD)
15969     {
15970       button_type = GD_TYPE_NORMAL_BUTTON;
15971       checked = FALSE;
15972       event_mask = GD_EVENT_RELEASED;
15973     }
15974     else if (id == GAME_CTRL_ID_UNDO ||
15975              id == GAME_CTRL_ID_REDO)
15976     {
15977       button_type = GD_TYPE_NORMAL_BUTTON;
15978       checked = FALSE;
15979       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15980     }
15981     else
15982     {
15983       button_type = GD_TYPE_CHECK_BUTTON;
15984       checked = (gamebutton_info[i].setup_value != NULL ?
15985                  *gamebutton_info[i].setup_value : FALSE);
15986       event_mask = GD_EVENT_PRESSED;
15987     }
15988
15989     gi = CreateGadget(GDI_CUSTOM_ID, id,
15990                       GDI_IMAGE_ID, graphic,
15991                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15992                       GDI_X, base_x + x,
15993                       GDI_Y, base_y + y,
15994                       GDI_WIDTH, gfx->width,
15995                       GDI_HEIGHT, gfx->height,
15996                       GDI_TYPE, button_type,
15997                       GDI_STATE, GD_BUTTON_UNPRESSED,
15998                       GDI_CHECKED, checked,
15999                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16000                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16001                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16002                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16003                       GDI_DIRECT_DRAW, FALSE,
16004                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16005                       GDI_EVENT_MASK, event_mask,
16006                       GDI_CALLBACK_ACTION, HandleGameButtons,
16007                       GDI_END);
16008
16009     if (gi == NULL)
16010       Fail("cannot create gadget");
16011
16012     game_gadget[id] = gi;
16013   }
16014 }
16015
16016 void FreeGameButtons(void)
16017 {
16018   int i;
16019
16020   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16021     FreeGadget(game_gadget[i]);
16022 }
16023
16024 static void UnmapGameButtonsAtSamePosition(int id)
16025 {
16026   int i;
16027
16028   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16029     if (i != id &&
16030         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16031         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16032       UnmapGadget(game_gadget[i]);
16033 }
16034
16035 static void UnmapGameButtonsAtSamePosition_All(void)
16036 {
16037   if (setup.show_snapshot_buttons)
16038   {
16039     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16041     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16042   }
16043   else
16044   {
16045     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16047     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16048
16049     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16050     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16051     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16052   }
16053 }
16054
16055 static void MapGameButtonsAtSamePosition(int id)
16056 {
16057   int i;
16058
16059   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16060     if (i != id &&
16061         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16062         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16063       MapGadget(game_gadget[i]);
16064
16065   UnmapGameButtonsAtSamePosition_All();
16066 }
16067
16068 void MapUndoRedoButtons(void)
16069 {
16070   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16071   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16072
16073   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16074   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16075 }
16076
16077 void UnmapUndoRedoButtons(void)
16078 {
16079   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16080   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16081
16082   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16083   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16084 }
16085
16086 void ModifyPauseButtons(void)
16087 {
16088   static int ids[] =
16089   {
16090     GAME_CTRL_ID_PAUSE,
16091     GAME_CTRL_ID_PAUSE2,
16092     GAME_CTRL_ID_PANEL_PAUSE,
16093     GAME_CTRL_ID_TOUCH_PAUSE,
16094     -1
16095   };
16096   int i;
16097
16098   for (i = 0; ids[i] > -1; i++)
16099     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16100 }
16101
16102 static void MapGameButtonsExt(boolean on_tape)
16103 {
16104   int i;
16105
16106   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16107     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16108         i != GAME_CTRL_ID_UNDO &&
16109         i != GAME_CTRL_ID_REDO)
16110       MapGadget(game_gadget[i]);
16111
16112   UnmapGameButtonsAtSamePosition_All();
16113
16114   RedrawGameButtons();
16115 }
16116
16117 static void UnmapGameButtonsExt(boolean on_tape)
16118 {
16119   int i;
16120
16121   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16122     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16123       UnmapGadget(game_gadget[i]);
16124 }
16125
16126 static void RedrawGameButtonsExt(boolean on_tape)
16127 {
16128   int i;
16129
16130   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16131     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16132       RedrawGadget(game_gadget[i]);
16133 }
16134
16135 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16136 {
16137   if (gi == NULL)
16138     return;
16139
16140   gi->checked = state;
16141 }
16142
16143 static void RedrawSoundButtonGadget(int id)
16144 {
16145   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16146              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16147              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16148              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16149              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16150              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16151              id);
16152
16153   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16154   RedrawGadget(game_gadget[id2]);
16155 }
16156
16157 void MapGameButtons(void)
16158 {
16159   MapGameButtonsExt(FALSE);
16160 }
16161
16162 void UnmapGameButtons(void)
16163 {
16164   UnmapGameButtonsExt(FALSE);
16165 }
16166
16167 void RedrawGameButtons(void)
16168 {
16169   RedrawGameButtonsExt(FALSE);
16170 }
16171
16172 void MapGameButtonsOnTape(void)
16173 {
16174   MapGameButtonsExt(TRUE);
16175 }
16176
16177 void UnmapGameButtonsOnTape(void)
16178 {
16179   UnmapGameButtonsExt(TRUE);
16180 }
16181
16182 void RedrawGameButtonsOnTape(void)
16183 {
16184   RedrawGameButtonsExt(TRUE);
16185 }
16186
16187 static void GameUndoRedoExt(void)
16188 {
16189   ClearPlayerAction();
16190
16191   tape.pausing = TRUE;
16192
16193   RedrawPlayfield();
16194   UpdateAndDisplayGameControlValues();
16195
16196   DrawCompleteVideoDisplay();
16197   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16198   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16199   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16200
16201   BackToFront();
16202 }
16203
16204 static void GameUndo(int steps)
16205 {
16206   if (!CheckEngineSnapshotList())
16207     return;
16208
16209   LoadEngineSnapshot_Undo(steps);
16210
16211   GameUndoRedoExt();
16212 }
16213
16214 static void GameRedo(int steps)
16215 {
16216   if (!CheckEngineSnapshotList())
16217     return;
16218
16219   LoadEngineSnapshot_Redo(steps);
16220
16221   GameUndoRedoExt();
16222 }
16223
16224 static void HandleGameButtonsExt(int id, int button)
16225 {
16226   static boolean game_undo_executed = FALSE;
16227   int steps = BUTTON_STEPSIZE(button);
16228   boolean handle_game_buttons =
16229     (game_status == GAME_MODE_PLAYING ||
16230      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16231
16232   if (!handle_game_buttons)
16233     return;
16234
16235   switch (id)
16236   {
16237     case GAME_CTRL_ID_STOP:
16238     case GAME_CTRL_ID_PANEL_STOP:
16239     case GAME_CTRL_ID_TOUCH_STOP:
16240       if (game_status == GAME_MODE_MAIN)
16241         break;
16242
16243       if (tape.playing)
16244         TapeStop();
16245       else
16246         RequestQuitGame(TRUE);
16247
16248       break;
16249
16250     case GAME_CTRL_ID_PAUSE:
16251     case GAME_CTRL_ID_PAUSE2:
16252     case GAME_CTRL_ID_PANEL_PAUSE:
16253     case GAME_CTRL_ID_TOUCH_PAUSE:
16254       if (network.enabled && game_status == GAME_MODE_PLAYING)
16255       {
16256         if (tape.pausing)
16257           SendToServer_ContinuePlaying();
16258         else
16259           SendToServer_PausePlaying();
16260       }
16261       else
16262         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16263
16264       game_undo_executed = FALSE;
16265
16266       break;
16267
16268     case GAME_CTRL_ID_PLAY:
16269     case GAME_CTRL_ID_PANEL_PLAY:
16270       if (game_status == GAME_MODE_MAIN)
16271       {
16272         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16273       }
16274       else if (tape.pausing)
16275       {
16276         if (network.enabled)
16277           SendToServer_ContinuePlaying();
16278         else
16279           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16280       }
16281       break;
16282
16283     case GAME_CTRL_ID_UNDO:
16284       // Important: When using "save snapshot when collecting an item" mode,
16285       // load last (current) snapshot for first "undo" after pressing "pause"
16286       // (else the last-but-one snapshot would be loaded, because the snapshot
16287       // pointer already points to the last snapshot when pressing "pause",
16288       // which is fine for "every step/move" mode, but not for "every collect")
16289       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16290           !game_undo_executed)
16291         steps--;
16292
16293       game_undo_executed = TRUE;
16294
16295       GameUndo(steps);
16296       break;
16297
16298     case GAME_CTRL_ID_REDO:
16299       GameRedo(steps);
16300       break;
16301
16302     case GAME_CTRL_ID_SAVE:
16303       TapeQuickSave();
16304       break;
16305
16306     case GAME_CTRL_ID_LOAD:
16307       TapeQuickLoad();
16308       break;
16309
16310     case SOUND_CTRL_ID_MUSIC:
16311     case SOUND_CTRL_ID_PANEL_MUSIC:
16312       if (setup.sound_music)
16313       { 
16314         setup.sound_music = FALSE;
16315
16316         FadeMusic();
16317       }
16318       else if (audio.music_available)
16319       { 
16320         setup.sound = setup.sound_music = TRUE;
16321
16322         SetAudioMode(setup.sound);
16323
16324         if (game_status == GAME_MODE_PLAYING)
16325           PlayLevelMusic();
16326       }
16327
16328       RedrawSoundButtonGadget(id);
16329
16330       break;
16331
16332     case SOUND_CTRL_ID_LOOPS:
16333     case SOUND_CTRL_ID_PANEL_LOOPS:
16334       if (setup.sound_loops)
16335         setup.sound_loops = FALSE;
16336       else if (audio.loops_available)
16337       {
16338         setup.sound = setup.sound_loops = TRUE;
16339
16340         SetAudioMode(setup.sound);
16341       }
16342
16343       RedrawSoundButtonGadget(id);
16344
16345       break;
16346
16347     case SOUND_CTRL_ID_SIMPLE:
16348     case SOUND_CTRL_ID_PANEL_SIMPLE:
16349       if (setup.sound_simple)
16350         setup.sound_simple = FALSE;
16351       else if (audio.sound_available)
16352       {
16353         setup.sound = setup.sound_simple = TRUE;
16354
16355         SetAudioMode(setup.sound);
16356       }
16357
16358       RedrawSoundButtonGadget(id);
16359
16360       break;
16361
16362     default:
16363       break;
16364   }
16365 }
16366
16367 static void HandleGameButtons(struct GadgetInfo *gi)
16368 {
16369   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16370 }
16371
16372 void HandleSoundButtonKeys(Key key)
16373 {
16374   if (key == setup.shortcut.sound_simple)
16375     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16376   else if (key == setup.shortcut.sound_loops)
16377     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16378   else if (key == setup.shortcut.sound_music)
16379     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16380 }